<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Prahlad Yeri</title>
 <link href="https://prahladyeri.github.io/atom.xml" rel="self"/>
 <link href="https://prahladyeri.github.io/"/>
 <updated>2026-04-22T03:05:31+00:00</updated>
 <id>https://prahladyeri.github.io</id>
 <author>
   <name>Prahlad Yeri</name>
   <email></email>
 </author>

 
 <entry>
   <title>Readymade template engine using the function constructor trick in JavaScript</title>
   <link href="https://prahladyeri.github.io/blog/2026/02/function-constructor-trick.html"/>
   <updated>2026-02-28T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2026/02/function-constructor-trick</id>
   <content type="html">&lt;p&gt;One of my favorite features in JavaScript is the backtick string interpolation technique:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Prahlad&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;greeting&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`Hello, &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The backtick string can be extraordinarily long and quite versatile, folks often use it like a mini-template engine. And what’s more, it’s built right into the core JS language and you get all the goodies without installing an extra package like handlebars or mustache or ejs.&lt;/p&gt;

&lt;p&gt;But what will you do if it’s not a literal string constant but something dynamically retrieved html file like this one:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;User: Prahlad&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Role: Engineer&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are many ways to handle this but my favorite is what is called the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;function constructor trick&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- part.html --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;User: ${this.name}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Role: ${this.role}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/**
 * Simple Template Compiler
 * @param {string} templateString - The raw HTML with ${this.prop} placeholders
 * @returns {Function} - A function that accepts a data object
 */&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;createTemplate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;templateString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// 1. Escape backticks in the source to prevent breaking the wrapper&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sanitized&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;templateString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/`/g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  
  &lt;span class=&quot;c1&quot;&gt;// 2. Return a function that uses .call() to bind &quot;this&quot; to the data&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`return (function() { 
    return &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sanitized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\`&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;; 
  }).call(data);`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Usage with Async/Await&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;renderUserCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/parts/part.html&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;createTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;userData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Prahlad&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Engineer&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;finalOutput&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;userData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;finalOutput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Failed to load or compile template:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The only thing you want to ensure is that the source is good (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/parts/part.html&lt;/code&gt; in this case). But otherwise, it’s a highly utilitarian and mind blowing technique which will enhance your JavaScript productivity by leaps and bounds.&lt;/p&gt;

&lt;h3 id=&quot;comparison-table&quot;&gt;Comparison Table&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Feature&lt;/th&gt;
      &lt;th&gt;Function Constructor Trick&lt;/th&gt;
      &lt;th&gt;Dedicated Engine (EJS/Mustache)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Footprint&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;0kb (Native JS)&lt;/td&gt;
      &lt;td&gt;10kb - 50kb+&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Logic&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Full JS Power&lt;/td&gt;
      &lt;td&gt;Often restricted (Logic-less)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Security&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;High Risk (Same as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eval&lt;/code&gt;)&lt;/td&gt;
      &lt;td&gt;Higher (Built-in escaping/sanitization)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Speed&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Extremely fast&lt;/td&gt;
      &lt;td&gt;Slightly slower due to parsing&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;⚠️ A Note on Security: Because this technique executes strings as code, only use it with templates you control (like your own server-side components). Never use this to render untrusted user input, as it could open the door to XSS attacks.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How AI can turn us into a society of p-zombies</title>
   <link href="https://prahladyeri.github.io/blog/2025/10/how-ai-can-turn-us-into-society-of-p-zombies.html"/>
   <updated>2025-10-25T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2025/10/how-ai-can-turn-us-into-society-of-p-zombies</id>
   <content type="html">&lt;p&gt;I’m probably one of the last generations of human writers on this planet who is actually involved in the end-to-end writing process. We’re already transitioning to that phase where the drafting and proof-reading for many articles is done by an LLM, and even other processes like editing and publishing are transitioning to full automation as we speak.&lt;/p&gt;

&lt;p&gt;For starters, what exactly is a &lt;em&gt;p-zombie&lt;/em&gt; or philosophical zombie? For those who don’t know, it’s a hypothetical machine (biological or otherwise) that looks and feels much like a human but there is no soul or consciousness inside. When you talk to it, you feel like you’re talking to a real human based on the responses and feedback but can’t be 100% sure that there is no chip or other kind of controller device inside. That “thing” is called a philosophical zombie.&lt;/p&gt;

&lt;p&gt;The challenge then is: &lt;em&gt;how do you know if the person you’re chatting with is a real human and not a p-zombie?&lt;/em&gt; The truth is that you can never be cent percent sure; this is no different than what is popularly called a &lt;em&gt;Turing Test&lt;/em&gt; in computer science where a turing machine or program like LLM is said to have passed the test when its response is indistinguishable from a human’s response. The p-zombie is the philosophy equivalent of it.&lt;/p&gt;

&lt;p&gt;The idea that we humans might become p-zombies one day is both terrifying and dystopian, and also unbelievable at first glance. However, consider for a moment &lt;em&gt;what&lt;/em&gt; exactly makes you a human: Is it the material body or shell composed of protons, neutrons and electrons? That’s partly true, our personality is half-material and half-spiritual. The material or visible half is one that “does stuff”, walks in the morning, drives the car, works on a computer or laptop, eats, drinks, codes, has sex. The other spiritual half is the &lt;em&gt;inner world&lt;/em&gt; composed of thoughts, feelings, emotions, intellect, rationality, memory, wonder, epiphanies, etc. - things we know exist but can’t be touched or seen in material spectrum. It is this other half self that we have slowly and gradually started delegating to artificial machines like LLMs.&lt;/p&gt;

&lt;p&gt;Presently, we are mostly delegating memory and logic components, tasks which we feel overwhelmed to handle ourselves - that’s the justification given anyway. But we have also started emotionally depending on chatbots; like this famous teenager &lt;a href=&quot;https://www.npr.org/sections/shots-health-news/2025/09/19/nx-s1-5545749/ai-chatbots-safety-openai-meta-characterai-teens-suicide&quot;&gt;Adam Raine who shared his deepest emotions and vulnerabilities with an LLM&lt;/a&gt; which by several accounts helped him end his life.&lt;/p&gt;

&lt;p&gt;Now who exactly do we hold accountable for Adam’s death? The LLM itself which lacks an intent or motive to do anything on its own? Or the makers of this LLM who built the algorithm with primarily commercial motives - drive engagement and eyeballs to their content? Or this society itself which has enabled and unleashed this monstrous thing called AI?&lt;/p&gt;

&lt;p&gt;Let’s ask an even more fundamental question: how exactly do you define your identity or being? It’s not your AADHAR or PAN or SSN number (as much as the authority wants to convince you it is!). Neither is it the physical body, it’s this other spiritual half composed of soul, consciousness, etc. that defines the real you. Whatever be your religious or spiritual belief, or even lack thereof, you can’t be in denial of this part of your own self for it defines who you are, it is your very identity if you will.&lt;/p&gt;

&lt;p&gt;Authoritarians and big businesses love p-zombies; the idea of controlling other humans to such extent that you don’t even need a remote control or whip - rather, they themselves surrender parts of their psyche bit by bit to a machine built by you - this would be a dream society for any authoritarian! Stalin and Mao would be dancing in their graves on the day when humans are totally dependent and controlled by a branded gizmo or LLM built for their assistance and daily tasks. That’s exactly why all the top bosses like VP/CEO/CTO/etc. are loving the idea of AI so much! They ask you to use LLM assistance not for code generation or unit tests, but to slowly and steadily get you addicted to this matrix where they become the controller and you the controlled.&lt;/p&gt;

&lt;p&gt;If this article sounds overly alarming, consider the rapid pace of technological growth and even rapid trajectory of advancements in AI. Only five years ago in 2020, it’d have been unthinkable to have a chatting assistant right on your smartphone that accomplishes so many things. It’s possible that there is a genuine use case for LLMs, maybe using it as just a reference tool (a glorified wikipedia if you will?) might increase productivity. But given the average nature of present day humans and the larger political climate we are in, and the trajectory the society is on, AI has more potential for harm than good.&lt;/p&gt;

&lt;p&gt;If you think you can convince me otherwise or have any point to share, feel free to leave a comment below.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I am a programmer, not a rubber-stamp that approves Copilot generated code</title>
   <link href="https://prahladyeri.github.io/blog/2025/10/i-am-a-programmer.html"/>
   <updated>2025-10-15T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2025/10/i-am-a-programmer</id>
   <content type="html">&lt;p&gt;Today morning, I came across this reddit post titled &lt;a href=&quot;https://www.reddit.com/r/cscareerquestions/comments/1o6vjv0/completely_losing_interest_in_the_career_due_to/&quot;&gt;Completely losing interest in the career due to AI and AI-pilled people&lt;/a&gt;. They describe how in a span of just two months, their corporate job went from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;I&apos;ll be here for life&quot;&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Time to switch careers?&quot;&lt;/code&gt;. And this post isn’t alone, there is a deep and dark pattern to it.&lt;/p&gt;

&lt;p&gt;When CTOs or project managers suggest programmers in their team to use AI assistance from copilot, chatgpt or other LLMs to improve productivity, it’s totally understandable. But once it’s no longer voluntary but is enforced as a policy, you start entering sinister territory. Worse, said usage is actually getting monitored and performance appraisals have now started depending on the AI usage instead of (or at least in addition to) traditional metrics like number of priority bugs raised, code reviews, Function Points Analysis, etc.&lt;/p&gt;

&lt;p&gt;If they’re really so confident on the LLM’s effectiveness, why not just keep it voluntary, why force it on people? The results will be there in the outcome of the shipped product for all to see. By forcing LLM usage upon programmers for implementation of every tiny little thing, are they trying to make us dependent on LLMs to such extent that programmers will be reduced to mere approvers of LLM generated code in the new scheme of things; mere rubber stamps, if you will, who just label the commits and annotate the tags as a formality?&lt;/p&gt;

&lt;p&gt;Needless to say, they’d still want you to take the responsibility. If bugs or tickets get raised on the shipped code, it’s &lt;strong&gt;you&lt;/strong&gt; who gets fired, not the copilot or chatgpt - though the larger narrative or news headlines next day would still be, “AI is eating jobs”!&lt;/p&gt;

&lt;p&gt;If the essence of programming shifts from creating to merely approving, we risk losing not just a profession, but a craft. What do you think is going on here, let me know your thoughts in comments.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unpopular Opinion: Implicit Usings are an Anti-Pattern</title>
   <link href="https://prahladyeri.github.io/blog/2025/10/implicit-usings-are-an-anti-pattern.html"/>
   <updated>2025-10-05T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2025/10/implicit-usings-are-an-anti-pattern</id>
   <content type="html">&lt;p&gt;As someone who has coded across Python, PHP, Java, and C#, I’ve learned to borrow patterns from each language to improve workflow and maintainability. One principle that has consistently stood the test of time is Python’s aphorism: &lt;strong&gt;“Explicit is better than implicit.”&lt;/strong&gt; It usually leads to robust, readable, and maintainable code.&lt;/p&gt;

&lt;p&gt;From this perspective, explicit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;using&lt;/code&gt; statements in C# have always been a good practice. They make it immediately clear which namespaces are being used, which is helpful both for current development and for future maintenance.&lt;/p&gt;

&lt;p&gt;.NET Core introduced &lt;strong&gt;implicit usings&lt;/strong&gt; by default, and while they aim to reduce boilerplate, I’ve found several downsides:&lt;/p&gt;

&lt;h3 id=&quot;1-readability-suffers&quot;&gt;1. Readability suffers&lt;/h3&gt;

&lt;p&gt;When you open a C# file in a text editor, you no longer have a clear overview of the namespaces it relies on. For example, compare a traditional explicit block:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/**
 * StringHelper.cs
 * 
 * @author Prahlad Yeri
 */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Collections.Generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Security.Cryptography&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…to a file where all these usings are implied. Without the explicit statements, you lose instant visibility into dependencies, making the code harder to reason about, especially months later.&lt;/p&gt;

&lt;h3 id=&quot;2-potential-for-ambiguity&quot;&gt;2. Potential for ambiguity&lt;/h3&gt;

&lt;p&gt;Consider calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Path.Combine()&lt;/code&gt;. Which &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Path&lt;/code&gt; does the compiler use—&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System.IO.Path&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iTextSharp.text.pdf.parser.Path&lt;/code&gt;? With explicit usings, you always know. Implicit usings rely on namespace resolution rules, and while the compiler will enforce correctness, it can still lead to maintainability headaches when multiple libraries define similar types.&lt;/p&gt;

&lt;h3 id=&quot;3-convenience-should-not-trump-maintainability&quot;&gt;3. Convenience should not trump maintainability&lt;/h3&gt;

&lt;p&gt;The motivation behind implicit usings—reducing boilerplate—is understandable. Modern tooling like Visual Studio, VS Code, Copilot, and ChatGPT already make adding usings almost effortless. Embedding this convenience into the language itself, however, can encourage less maintainable code.&lt;/p&gt;

&lt;h3 id=&quot;my-preferred-approach&quot;&gt;My preferred approach&lt;/h3&gt;

&lt;p&gt;Since moving to .NET Core, I habitually disable implicit usings in new projects. It might seem minor, but it gives me &lt;strong&gt;mental peace&lt;/strong&gt; and ensures that my code remains explicit, readable, and maintainable:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ImplicitUsings&amp;gt;&lt;/span&gt;disable&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ImplicitUsings&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Implicit usings may save a few keystrokes, but they come at the cost of clarity and maintainability. For developers who care about clean, understandable code, explicit usings remain the safer choice.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Create a C# Windows Desktop App in 9 Lines — No Visual Studio Needed</title>
   <link href="https://prahladyeri.github.io/blog/2025/09/hello-csharp-winforms-zero-deps.html"/>
   <updated>2025-09-20T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2025/09/hello-csharp-winforms-zero-deps</id>
   <content type="html">&lt;p&gt;If you’re new to C# and especially desktop development, here’s how easy it is to get started on a modern Windows 10/11 PC. This way of creating a Hello World C# program doesn’t involve installation of any heavy IDE like Visual Studio or the .NET 8/9 SDK.&lt;/p&gt;

&lt;p&gt;In fact, most recent Windows versions already come with .NET Framework 4.x preinstalled, including the classic C# compiler at a location like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C:\Windows\Microsoft.NET\Framework\v4.0.30319
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The exact folder may differ depending on your system but it usually contains &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;csc.exe&lt;/code&gt;, the built-in compiler that can compile simple console or desktop apps. You can add this folder to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PATH&lt;/code&gt; environment variable (temporarily or permanently) so that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;csc&lt;/code&gt; is available from the command line:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;set PATH=C:\Windows\Microsoft.NET\Framework\v4.0.30319;%PATH%
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We will be using this classic, old-school style of programming (similar to writing C programs with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcc&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;turboc&lt;/code&gt;) — a great way to get acquainted with a new language.&lt;/p&gt;

&lt;h3 id=&quot;step-1-create-the-source-file&quot;&gt;Step 1: Create the source file&lt;/h3&gt;

&lt;p&gt;Make a new folder anywhere on your drive. Inside it, create a file named Program.cs using Notepad, VS Code, or any editor you like:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Windows.Forms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;Form&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frm&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;frm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;frm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShowDialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;	
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In just nine lines of C# code, without using any IDE, you now have a working “Hello World” desktop app with no additional dependencies.&lt;/p&gt;

&lt;h3 id=&quot;step-2-compile-and-run&quot;&gt;Step 2: Compile and run&lt;/h3&gt;

&lt;p&gt;Open Command Prompt in your project folder and run:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;csc Program.cs
Program
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first line tells the C# compiler (csc.exe) to build the app into Program.exe. The second one runs it. You can also double-click Program.exe in File Explorer.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/csharp-hello.png&quot; alt=&quot;Screenshot of Hello World WinForms app&quot; /&gt;&lt;/p&gt;

&lt;p&gt;By default, a console window appears behind the form. To build a GUI-only app without the console, recompile with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/target:winexe&lt;/code&gt; parameter:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;csc /target:winexe Program.cs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;whats-next&quot;&gt;What’s next?&lt;/h3&gt;

&lt;p&gt;In upcoming posts, we’ll add more features such as menus, buttons, event handlers, and even database access with ADO.NET. But for now, enjoy this milestone: you’ve built your very first Windows desktop app in C#!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The art and science of effectively maintaining changelogs</title>
   <link href="https://prahladyeri.github.io/blog/2025/06/effectively-maintaining-changelogs.html"/>
   <updated>2025-06-05T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2025/06/effectively-maintaining-changelogs</id>
   <content type="html">&lt;p&gt;Even in 2025, maintaining changelogs isn’t an exact science. There is no general consensus or even tooling around how your changelog should look in order to conform to some norms or standards, because there aren’t any. After some trial-error and pondering, this is what I came up with for my upcoming FocusBeam project:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Focus Beam Changelog&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;## [Unreleased]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;### Added&lt;/span&gt;
- &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;x] Dashboard and Timesheet view
- &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;x] About View
- &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;x] Settings View
- &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;x] Project creation and editing
- &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;x] Task creation and editing
- &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;x] Enable Timesheet button on dashboard
- &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; Show Total Hours worked on the project on Dashboard View
- &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; Logged hours computation
- &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; Notes
- &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; Mind Maps
- &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; MCQ
- &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; Export/Reports
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This format looks somewhat better than the older version that I still retain for some existing projects:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;R1.1

- Pending CRM Reports.
- Check post script.
+ Clicking on logos go to home page.
+ Chart: width on various screens.
* Chart: Remove decimal from &quot;Total Life (Hrs)&quot;
* report: pdf: line items: add gap between fields.
* report: space below header.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, the mnemonic prefix is dash (-) to define a pending task, a plus (+) means work is in progress, and asterisk (*) means it’s complete. Now, if you’re an automation nerd and decided to opt for &lt;a href=&quot;/blog/2019/06/how-to-enforce-conventional-commit-messages-using-git-hooks.html&quot;&gt;conventional commit message spec&lt;/a&gt;, it’s possible to maybe construct the changelog automatically by following this spec while committing the code itself!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- feat: pending CRM Reports
- chore: check post script
+ fix: clicking on logos go to home page
+ feat: chart: width on various screens
* feat: chart: Remove decimal from &quot;Total Life (Hrs)&quot;
* fix: report: pdf: line items: add gap between fields
* fix: report: space below header
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This assumes that you’ve followed the conventional spec while committing your code to git source control. For example, “feat: pending CRM Reports” for implementing this particular feature.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Stoicism vs Nihilism: The Power of Choice</title>
   <link href="https://prahladyeri.github.io/blog/2025/04/stoicism-vs-nihilism-the-power-of-choice.html"/>
   <updated>2025-04-21T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2025/04/stoicism-vs-nihilism-the-power-of-choice</id>
   <content type="html">&lt;p&gt;In a world increasingly shaped by existential uncertainty, many young minds find themselves drawn toward nihilism. It’s not difficult to see why: the vastness of the universe, the randomness of evolution, and the seeming insignificance of human action can make it tempting to believe that nothing truly matters. If everything is bound to perish — civilizations, memories, even the stars — then what’s the point of striving, caring, or even existing with intention?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Comfortable Trap of Nihilism&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nihilism, at its core, is the belief that life lacks objective meaning, purpose, or intrinsic value. It’s intellectually seductive — it relieves you of the burden of responsibility. If nothing matters, then nothing is required of you. There’s no guilt, no accountability, no expectations. It gives you an escape hatch from effort, from failure, from trying.&lt;/p&gt;

&lt;p&gt;But here’s the catch: while nihilism may seem freeing in theory, it’s paralyzing in practice. Believing in nothing doesn’t solve your debts. It doesn’t fix your relationships. It doesn’t make you healthier, wealthier, wiser, or happier. It doesn’t get you out of bed when you need to. In fact, it may give you just enough philosophical justification to avoid doing any of those things. The danger is that you begin to think your despair is wisdom — that giving up is somehow a sign of deeper understanding.&lt;/p&gt;

&lt;p&gt;Even if there’s truth in the cosmic indifference of nature or the arbitrary evolution of life, it doesn’t help your day-to-day reality. You’re still here, in a body that ages, in a society that demands effort, surrounded by people who affect you. Life keeps going, whether you assign it meaning or not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enter Stoicism: A Path of Ownership&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Stoicism, on the other hand, offers a radically different — yet strikingly empowering — worldview. It doesn’t pretend the universe cares about you. In fact, it agrees with nihilism on that front: the universe is indifferent. But where it differs is in its response to that indifference. Stoicism says: yes, the cosmos doesn’t care — &lt;em&gt;so you must&lt;/em&gt;. You are the agent. You are the author.&lt;/p&gt;

&lt;p&gt;The Stoics teach that while we cannot control the external world, we can always control our response. This is the essence of Epictetus’ “dichotomy of control.” You don’t choose your genetics, your upbringing, or the economy — but you do choose your attitude, your discipline, your actions, your virtue. That is where your power lies.&lt;/p&gt;

&lt;p&gt;Instead of being crushed by the weight of life’s randomness, Stoicism invites you to stand tall amidst it. It doesn’t demand belief in fairy tales or divine destiny. It simply asks you to recognize what is within your control and take full responsibility for it. Even the act of showing up, of trying, of choosing to live by principles — that is a form of rebellion against the void.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;From Meaningless to Meaning-Maker&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A nihilist sees the lack of inherent meaning and stops. A Stoic sees the same and says, “Then I will create meaning through how I live.” In this way, Stoicism is a practical antidote to the paralysis of nihilism. It doesn’t ask you to believe in meaning — it challenges you to &lt;em&gt;embody&lt;/em&gt; it.&lt;/p&gt;

&lt;p&gt;Marcus Aurelius, a Stoic emperor who faced plagues, war, and personal loss, wrote in his meditations: &lt;em&gt;“You have power over your mind — not outside events. Realize this, and you will find strength.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you’re feeling lost, defeated, or overwhelmed by the void, perhaps the answer isn’t to lean further into nothingness — but to begin, step by step, to reclaim your role as a responsible actor in the stage of life.&lt;/p&gt;

&lt;p&gt;You may not control the script, but you can still choose how you perform it.&lt;/p&gt;

&lt;p&gt;Choose with intention. Choose with courage. Choose with virtue.&lt;/p&gt;

&lt;p&gt;That’s the Stoic way.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>When Gandhi met Satoshi</title>
   <link href="https://prahladyeri.github.io/blog/2025/04/when-gandhi-met-satoshi.html"/>
   <updated>2025-04-20T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2025/04/when-gandhi-met-satoshi</id>
   <content type="html">&lt;p&gt;&lt;em&gt;A fictional encounter between two radically different revolutionaries… or maybe not so different after all.&lt;/em&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;Scene:&lt;/strong&gt;&lt;br /&gt;
A quiet room beyond time, filled with gentle candlelight. Mahatma Gandhi sits cross-legged on a simple mat, spinning a symbolic charkha (spinning wheel). A soft whirring sound is heard — it’s Satoshi Nakamoto, hooded and calm, typing quietly on an open-source laptop.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;gandhi-smiling-gently&quot;&gt;&lt;strong&gt;Gandhi (smiling gently):&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;I see you’re building something… abstract. Not cloth this time, but code?&lt;/p&gt;

&lt;h3 id=&quot;satoshi-nodding&quot;&gt;&lt;strong&gt;Satoshi (nodding):&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Yes. It’s called Bitcoin. A currency without rulers. A decentralized network where no single man holds the power.&lt;/p&gt;

&lt;h3 id=&quot;gandhi&quot;&gt;&lt;strong&gt;Gandhi:&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Ah! Decentralization. You speak of &lt;em&gt;Swaraj&lt;/em&gt; — self-rule. But tell me, does this tool free people, or enslave them in new greed?&lt;/p&gt;

&lt;h3 id=&quot;satoshi&quot;&gt;&lt;strong&gt;Satoshi:&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;That depends on the user. I built it so that no central bank, no emperor of finance, can erode the common man’s savings.&lt;/p&gt;

&lt;h3 id=&quot;gandhi-1&quot;&gt;&lt;strong&gt;Gandhi:&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Hmm. That is noble. But in my land, I spun khadi — not to gain wealth, but to empower the weaver. Can your coin clothe the poor, feed the farmer?&lt;/p&gt;

&lt;h3 id=&quot;satoshi-1&quot;&gt;&lt;strong&gt;Satoshi:&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Not directly. But it can return power to local communities — if used wisely. It can fund clean water, create transparency in charity, help refugees carry value without paper.&lt;/p&gt;

&lt;h3 id=&quot;gandhi-thoughtfully&quot;&gt;&lt;strong&gt;Gandhi (thoughtfully):&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Technology must serve the &lt;em&gt;last man&lt;/em&gt;. The poorest. If your invention becomes the toy of the rich… it has failed its Dharma.&lt;/p&gt;

&lt;h3 id=&quot;satoshi-softly&quot;&gt;&lt;strong&gt;Satoshi (softly):&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;I agree. That’s why I vanished. Power corrupts. I wished to spark something, not to rule over it.&lt;/p&gt;

&lt;h3 id=&quot;gandhi-smiling&quot;&gt;&lt;strong&gt;Gandhi (smiling):&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Then we share a path, my friend. The world will judge our ideas — not by their &lt;em&gt;cleverness&lt;/em&gt;, but by the &lt;em&gt;compassion&lt;/em&gt; they inspire.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;[A pause.]&lt;/strong&gt;&lt;br /&gt;
The spinning wheel turns. The screen glows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gandhi leans forward:&lt;/strong&gt;&lt;br /&gt;
You must promise me, Satoshi — build for the village, not just the venture capitalist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Satoshi (grinning faintly):&lt;/strong&gt;&lt;br /&gt;
Only if you promise to try a Raspberry Pi.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;[Fade out.]&lt;/strong&gt;&lt;br /&gt;
The revolution continues — in code, in conscience, and in khadi.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to revive a Micromax Canvas Lapbook L1161 in 2025</title>
   <link href="https://prahladyeri.github.io/blog/2025/01/how-to-revive-micromax-l1161-in-2025.html"/>
   <updated>2025-01-27T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2025/01/how-to-revive-micromax-l1161-in-2025</id>
   <content type="html">&lt;p&gt;This little masterpiece of a Notebook that came with Atom baytrail processor was released in circa 2016 and was once the favorite of many a college students and tech enthusiasts. It has helped me a lot in my freelancing journey and learning process. I consider it a notable artifact of our times and hold it in the same nostalgic regard as the ASUS Eee PC and other such iconic devices. Back then, it barely cost INR 10K - you can’t even get a decent smartphone for that price today!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/micromax-canvas-lapbook-l1161.jpg&quot; alt=&quot;micromax-canvas-lapbook-l1161&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Sadly, like all good things in life, its glory days came to an end. Micromax eventually succumbed to global competition from big tech conglomerates and its laptop division became defunct. As a result, customers of this laptop were left stranded, unable to download official device drivers anymore. For those who wanted to format their disk and do a clean install of Windows, this presented a major problem.&lt;/p&gt;

&lt;p&gt;Fortunately, I had downloaded the L1161 drivers from their official web portal while the company was still functional, you can &lt;a href=&quot;https://drive.google.com/file/d/1iC1qoPaa7rNZohaSI5qmlIQK2EpCVahO/view?usp=drive_link&quot;&gt;find them here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, there is a caveat: x86 builds of Windows 10 released after 2019 are no longer compatible with the original Realtek audio driver. To resolve this, you’ll need to fetch the latest audio driver from &lt;a href=&quot;https://driverpack.io/en/laptops/micromax/canvas-lapbook-l1161/sound?os=windows-10-x86&quot;&gt;driverpack.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Together with the drivers in the original zip file and this updated sound driver, your Canvas laptop should be ready and kicking, even on the latest Windows 10 builds! If you’re unfamiliar with how to install device drivers manually from INF files, you can refer to &lt;a href=&quot;https://stackoverflow.com/q/22496847/849365&quot;&gt;this stackoverflow link&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;a-note-on-linux-compatibility&quot;&gt;A Note on Linux Compatibility&lt;/h2&gt;

&lt;p&gt;Don’t bother attempting to install Linux on this machine - it’s a fuitile effort. The unique and arcane configuration of this device (32bit UEFI, 64bit architecture) makes it near impossible for any Linux distro to boot seamlessly without &lt;a href=&quot;https://superuser.com/a/1872717/148445&quot;&gt;extreme workarounds&lt;/a&gt;. Even if you miraculously get past the GRUB screen, open-source drivers like i915 are unlikely to support this unusual architecture.&lt;/p&gt;

&lt;p&gt;The only operating system that can reliably run on this laptop is Windows 10 32-bit Home Edition—the one that originally came pre-installed.&lt;/p&gt;

&lt;h2 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h2&gt;

&lt;p&gt;The Micromax Canvas Lapbook L1161 may have been forgotten by its manufacturer, but with the right drivers and a bit of effort, it can still serve as a functional device in 2025. All the best in your electronic endeavors, and happy computing!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The ultimate guide to boosting productivity with the Pomodoro Technique</title>
   <link href="https://prahladyeri.github.io/blog/2024/12/boost-productivity-pomodoro-technique.html"/>
   <updated>2024-12-08T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/12/boost-productivity-pomodoro-technique</id>
   <content type="html">&lt;p&gt;In a world buzzing with distractions, maintaining focus and productivity can feel like trying to catch a greased pig. Whether it’s the relentless ping of notifications, the lure of social media, or the temptation to procrastinate, staying on task requires a solid strategy. One technique that has stood the test of time for its simplicity and effectiveness is the Pomodoro Technique.&lt;/p&gt;

&lt;p&gt;This guide explores everything you need to know about the Pomodoro Technique—what it is, how it works, its benefits, and tips for making it work for you.&lt;/p&gt;

&lt;h3 id=&quot;what-is-the-pomodoro-technique&quot;&gt;What is the Pomodoro Technique?&lt;/h3&gt;

&lt;p&gt;The Pomodoro Technique is a time management method developed by Francesco Cirillo in the late 1980s. Named after the tomato-shaped timer Cirillo used during university, the technique is deceptively simple yet powerful.&lt;/p&gt;

&lt;p&gt;At its core, the method involves breaking your work into focused intervals, typically 25 minutes long, known as “Pomodoros”, separated by short breaks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Basic steps of the Pomodoro Technique:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Choose a task&lt;/strong&gt; you want to work on.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Set a timer&lt;/strong&gt; for 25 minutes.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Work on the task&lt;/strong&gt; until the timer rings.&lt;/li&gt;
  &lt;li&gt;Take a &lt;strong&gt;5-minute break&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Repeat the cycle four times, then take a &lt;strong&gt;longer break&lt;/strong&gt; (15-30 minutes).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/pomodoro-productivity.webp&quot; alt=&quot;pomodoro-productivity&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;why-does-the-pomodoro-technique-work&quot;&gt;Why does the Pomodoro Technique work?&lt;/h3&gt;

&lt;p&gt;The effectiveness of the Pomodoro Technique lies in its ability to combat two common productivity killers: &lt;strong&gt;procrastination&lt;/strong&gt; and &lt;strong&gt;mental fatigue&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Focus through time constraints:&lt;/strong&gt;&lt;br /&gt;
Knowing you only need to focus for 25 minutes helps overcome the dread of a long task. It’s easier to say, “I can do this for 25 minutes,” than to face hours of work.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Frequent breaks prevent burnout:&lt;/strong&gt;&lt;br /&gt;
The short breaks after each Pomodoro refresh your mind and keep fatigue at bay.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Immediate feedback on time use:&lt;/strong&gt;&lt;br /&gt;
Tracking Pomodoros helps you understand how long tasks really take, leading to better planning.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Reduces perfectionism:&lt;/strong&gt;&lt;br /&gt;
By imposing time limits, the technique encourages starting and progressing, rather than getting stuck striving for perfection.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;setting-up-for-success&quot;&gt;Setting up for success&lt;/h3&gt;

&lt;p&gt;Here’s how to maximize the Pomodoro Technique’s potential:&lt;/p&gt;

&lt;h4 id=&quot;1-choose-the-right-tools&quot;&gt;1. &lt;strong&gt;Choose the right tools&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;While you can use any timer, specialized Pomodoro apps make the process smoother. Here are some options:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;&lt;strong&gt;App&lt;/strong&gt;&lt;/th&gt;
      &lt;th&gt;&lt;strong&gt;Platform&lt;/strong&gt;&lt;/th&gt;
      &lt;th&gt;&lt;strong&gt;Features&lt;/strong&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Focus Booster&lt;/td&gt;
      &lt;td&gt;Web, Desktop&lt;/td&gt;
      &lt;td&gt;Simple UI, Pomodoro analytics&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Forest&lt;/td&gt;
      &lt;td&gt;Mobile, Web&lt;/td&gt;
      &lt;td&gt;Gamified productivity, tree planting&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Tomato Timer&lt;/td&gt;
      &lt;td&gt;Web&lt;/td&gt;
      &lt;td&gt;No downloads, minimalist interface&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h4 id=&quot;2-plan-your-tasks&quot;&gt;2. &lt;strong&gt;Plan your tasks&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Before starting, list your tasks for the day and estimate the number of Pomodoros each will require. For example:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;&lt;strong&gt;Task&lt;/strong&gt;&lt;/th&gt;
      &lt;th&gt;&lt;strong&gt;Estimated Pomodoros&lt;/strong&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Write blog draft&lt;/td&gt;
      &lt;td&gt;4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Respond to emails&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Review project presentation&lt;/td&gt;
      &lt;td&gt;3&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h4 id=&quot;3-eliminate-distractions&quot;&gt;3. &lt;strong&gt;Eliminate distractions&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Create a conducive environment by:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Turning off notifications.&lt;/li&gt;
  &lt;li&gt;Letting coworkers or family know you’re in focus mode.&lt;/li&gt;
  &lt;li&gt;Using focus tools like website blockers (e.g., StayFocusd, Freedom).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;advanced-tips-for-seasoned-users&quot;&gt;Advanced tips for seasoned users&lt;/h3&gt;

&lt;p&gt;Once you’ve mastered the basics, consider these strategies to level up:&lt;/p&gt;

&lt;h4 id=&quot;1-customize-pomodoro-lengths&quot;&gt;1. &lt;strong&gt;Customize Pomodoro lengths&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Not all tasks are equal. Some may require extended focus. Experiment with longer Pomodoros (e.g., 45 minutes) for deep work or shorter intervals for admin tasks.&lt;/p&gt;

&lt;h4 id=&quot;2-batch-similar-tasks&quot;&gt;2. &lt;strong&gt;Batch similar tasks&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Group small tasks together within one Pomodoro. For instance, use one session for returning calls, replying to emails, or organizing files.&lt;/p&gt;

&lt;h4 id=&quot;3-analyze-your-productivity-data&quot;&gt;3. &lt;strong&gt;Analyze your productivity data&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Many apps provide insights into how you spend your time. Use this data to identify patterns, such as when you’re most productive, and schedule challenging tasks accordingly.&lt;/p&gt;

&lt;h3 id=&quot;potential-challenges-and-solutions&quot;&gt;Potential challenges and solutions&lt;/h3&gt;

&lt;p&gt;Even the best methods can encounter hiccups. Here’s how to address common issues with the Pomodoro Technique:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;&lt;strong&gt;Challenge&lt;/strong&gt;&lt;/th&gt;
      &lt;th&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Difficulty staying focused for 25 minutes&lt;/td&gt;
      &lt;td&gt;Break the session into smaller intervals (e.g., 15 minutes).&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Tasks too large to complete in one session&lt;/td&gt;
      &lt;td&gt;Break the task into smaller, actionable steps.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Feeling constrained by the timer&lt;/td&gt;
      &lt;td&gt;View it as a guide rather than a strict rule.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;real-world-success-stories&quot;&gt;Real-world success stories&lt;/h3&gt;

&lt;p&gt;Many professionals have embraced the Pomodoro Technique, from writers and developers to students and entrepreneurs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Case in point:&lt;/strong&gt; Sarah, a freelance graphic designer, found herself overwhelmed with client deadlines. By adopting the Pomodoro Technique, she not only met her deadlines but also discovered time for personal projects.&lt;/p&gt;

&lt;p&gt;Her secret? Using her breaks to do something creative but unrelated to work, like doodling or listening to music.&lt;/p&gt;

&lt;h3 id=&quot;beyond-productivity-unexpected-benefits&quot;&gt;Beyond productivity: Unexpected benefits&lt;/h3&gt;

&lt;p&gt;The Pomodoro Technique doesn’t just make you more productive; it also:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Enhances mindfulness by promoting single-tasking.&lt;/li&gt;
  &lt;li&gt;Reduces stress by fostering a sense of achievement after every session.&lt;/li&gt;
  &lt;li&gt;Improves time estimation skills over time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h3&gt;

&lt;p&gt;The Pomodoro Technique is more than just a time management hack; it’s a mindset shift. It teaches you to value focus, take mindful breaks, and respect your energy limits.&lt;/p&gt;

&lt;p&gt;As the old adage goes, “A journey of a thousand miles begins with a single step.” With the Pomodoro Technique, that single step is a manageable 25-minute sprint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sources:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Cirillo, Francesco. &lt;em&gt;The Pomodoro Technique&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;Various studies on productivity and time management from research journals.&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Master PDF digital signing with eMudhra and Proxkey in .NET: step-by-step guide</title>
   <link href="https://prahladyeri.github.io/blog/2024/12/digitally-sign-pdf-dotnet.html"/>
   <updated>2024-12-02T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/12/digitally-sign-pdf-dotnet</id>
   <content type="html">&lt;p&gt;One of my recent projects involved coding a simple .NET app that signs PDFs in bulk using Proxkey digital signature. These purchased CA verified digital signatures, like eMudhra and Proxkey, typically come with their own custom apps which can digitally sign PDFs. You can even use software like Adobe Reader for this purpose. However, the limitation is that you can only sign one PDF at a time.&lt;/p&gt;

&lt;p&gt;If you want to send multiple digitally signed bills to customers via email, for instance, you’ll need to write a custom program in .NET or Java. In this article, we’ll explore how to achieve this using .NET in Visual Studio.&lt;/p&gt;

&lt;h2 id=&quot;preparing-the-environment&quot;&gt;Preparing the environment.&lt;/h2&gt;

&lt;p&gt;We will use two open-source libraries to handle the cryptographic functionality:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/iTextSharp/5.5.13.3&quot;&gt;itextsharp&lt;/a&gt;: You may use the latest version, but I chose this specific version for compatibility with my Visual Studio setup.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/BouncyCastle/1.8.9&quot;&gt;BouncyCastle&lt;/a&gt;: The same applies to the BouncyCastle library.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both libraries work together to retrieve the CA certificate, private key, and other cryptographic files from the thumb drive and then digitally sign each PDF.&lt;/p&gt;

&lt;h2 id=&quot;setting-up-the-project&quot;&gt;Setting up the Project&lt;/h2&gt;

&lt;p&gt;I created a simple form with a text box for the file path, a browse button, and a few options for the type of digital signature, such as “Proxkey” or “Self-signed.” While self-signed certificates work well for testing and ad-hoc signing, they are not typically recommended for professional or commercial transactions, such as sending bills or purchase orders.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/digitally-sign-pdf-dotnet/form.jpg&quot; alt=&quot;form&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When the user clicks the “Sign PDF” button, the app passes three parameters to the corresponding method:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;The source PDF path.&lt;/li&gt;
  &lt;li&gt;The desired destination path.&lt;/li&gt;
  &lt;li&gt;(Optionally) the password for the self-signed PFX certificate, if applicable.&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;optProxKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Checked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Signer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SignPdfProxkey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourcePdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;signedPdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;optSelfSigned&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Checked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Signer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SignPdfSelfSign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourcePdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;signedPdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;digisign&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;signing-the-pdf-with-proxkey&quot;&gt;Signing the PDF with Proxkey&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Signer&lt;/code&gt; class contains the code for Proxkey, but it should also work with other CA-provided digital signatures like eMudhra. Once you have the certificate thumbprint, fetch the corresponding certificate from the Windows certificate store (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X509Store&lt;/code&gt; object).&lt;/p&gt;

&lt;p&gt;The private key is then accessed using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RSACryptoServiceProvider&lt;/code&gt; object. The Proxkey or eMudhra software will automatically prompt you for the PIN, granting the program access to the private key certificate.&lt;/p&gt;

&lt;p&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appearance.SetVisibleSignature()&lt;/code&gt; to print the visible (aesthetic) signature on the PDF. You can adjust variables like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rectWidth&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rectHeight&lt;/code&gt; to define the placement.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SignPdfProxkey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inputPdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outputPdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thumbprint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getThumbPrint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thumbprint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Thumbprint is not accessible. Ensure ProxKey middleware is installed and the token is connected.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;// Access the certificate from the Windows Certificate Store&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;X509Store&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;X509Store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StoreName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;My&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StoreLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpenFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReadOnly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;X509Certificate2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cert&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X509Certificate2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Certificates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thumbprint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thumbprint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thumbprint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;cert&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cert&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Certificate not found.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;c1&quot;&gt;// Authenticate with the USB token using the PIN and access the private key&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;RSACryptoServiceProvider&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rsa&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PrivateKey&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RSACryptoServiceProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rsa&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Private key is not accessible. Ensure ProxKey middleware is installed, the token is connected, and the correct PIN is used.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;c1&quot;&gt;// Perform a test operation to ensure the private key is accessible&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testData&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x02&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x03&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;rsa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SignData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SHA256CryptoServiceProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CryptographicException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Failed to authenticate with the USB token. Ensure the correct PIN is entered.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;c1&quot;&gt;// Sign the PDF with iTextSharp&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileStream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inputFileStream&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inputPdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileStream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outputFileStream&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;outputPdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;PdfReader&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PdfReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inputFileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;PdfStamper&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stamper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PdfStamper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outputFileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;&apos;\0&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

		&lt;span class=&quot;c1&quot;&gt;// Create the signature appearance&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;PdfSignatureAppearance&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appearance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stamper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SignatureAppearance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;appearance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reason&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Document digitally signed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;appearance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Vadodara&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;// Define the signature rectangle size&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;iTextSharp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rectangle&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pageSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPageSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Use 1 to get the size of the first page&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;// Define the width and height of the page&lt;/span&gt;
		&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pageWidth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pageSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pageHeight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pageSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rectWidth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;144f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rectHeight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;32f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;// Calculate the position for the bottom-right corner&lt;/span&gt;
		&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pageWidth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rectWidth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;36&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 36 is some margin from the right edge&lt;/span&gt;
		&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bottom&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;276f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 36 is the margin from the bottom edge&lt;/span&gt;

		&lt;span class=&quot;n&quot;&gt;appearance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetVisibleSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iTextSharp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rectWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bottom&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rectHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Signature&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

		&lt;span class=&quot;c1&quot;&gt;// Convert the .NET certificate to a BouncyCastle certificate&lt;/span&gt;
		&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bcCert&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DotNetUtilities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromX509Certificate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;IExternalSignature&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;externalSignature&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PrivateKeySignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rsa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SHA-256&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

		&lt;span class=&quot;c1&quot;&gt;// Sign the document&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;MakeSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SignDetached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;appearance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;externalSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bcCert&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CryptoStandard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CMS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;getting-the-thumb-print&quot;&gt;Getting the thumb print&lt;/h2&gt;

&lt;p&gt;This helper function fetches the first available thumbprint with a private key from the certificate store.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getThumbPrint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;X509Store&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;X509Store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StoreName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;My&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StoreLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpenFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReadOnly&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpenFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpenExistingOnly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X509Certificate2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cert&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Certificates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HasPrivateKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;c1&quot;&gt;//cmbSignatures.Items.Add(cert.FriendlyName + &quot; - &quot; + cert.Thumbprint);&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thumbprint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;self-signed-certificate&quot;&gt;Self-signed certificate&lt;/h2&gt;

&lt;p&gt;While self-signed certificates are suitable for testing, they are not recommended for professional use. Here’s a basic implementation for self-signing:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SignPdfSelfSign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inputPdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outputPdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;certificate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;X509Certificate2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;cert\certificate.pfx&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X509KeyStorageFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exportable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X509KeyStorageFlags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PersistKeySet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; 
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;certificate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HasPrivateKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;No private key found in the certificate.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;// Convert .NET X509Certificate2 to BouncyCastle X509Certificate&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BouncyCastle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X509&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X509CertificateParser&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;X509CertificateParser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BouncyCastle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X509&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X509Certificate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bcCert&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadCertificate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;certificate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RawData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;//PdfReader reader = new PdfReader(inputPdf);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PdfReader&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PdfReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inputPdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileStream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;outputPdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;totalPages&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NumberOfPages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastPageIndex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;totalPages&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;n&quot;&gt;PdfStamper&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stamper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PdfStamper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;&apos;\0&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

			&lt;span class=&quot;c1&quot;&gt;// Get the signature appearance&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;PdfSignatureAppearance&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appearance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stamper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SignatureAppearance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;appearance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reason&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Document digitally signed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;appearance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Vadodara&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;c1&quot;&gt;// Define the signature rectangle size&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;iTextSharp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rectangle&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pageSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPageSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Use 1 to get the size of the first page&lt;/span&gt;
			&lt;span class=&quot;c1&quot;&gt;// Define the width and height of the page&lt;/span&gt;
			&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pageWidth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pageSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pageHeight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pageSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rectWidth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;144f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rectHeight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;32f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;c1&quot;&gt;// Calculate the position for the bottom-right corner&lt;/span&gt;
			&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pageWidth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rectWidth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;36&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 36 is some margin from the right edge&lt;/span&gt;
			&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bottom&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;276f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 36 is the margin from the bottom edge&lt;/span&gt;

			&lt;span class=&quot;c1&quot;&gt;//appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(36, 748, 144, 780), lastPageIndex, &quot;SignatureField&quot;);&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;appearance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetVisibleSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iTextSharp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rectWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bottom&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rectHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SignatureField&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;


			&lt;span class=&quot;c1&quot;&gt;// Extract the private key using BouncyCastle&lt;/span&gt;
			&lt;span class=&quot;c1&quot;&gt;// Extract the private key using BouncyCastle&lt;/span&gt;
			&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;privateKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetPrivateKeyFromCertificate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;certificate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
			&lt;span class=&quot;c1&quot;&gt;//Org.BouncyCastle.Crypto.AsymmetricKeyParameter privateKey = DotNetUtilities.GetKeyPair(certificate.PrivateKey).Private;&lt;/span&gt;

			&lt;span class=&quot;c1&quot;&gt;// Create the external signature container&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;IExternalSignature&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pks&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CustomExternalSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;certificate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DigestAlgorithms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
			&lt;span class=&quot;c1&quot;&gt;//IExternalSignature pks = new X509Certificate2Signature(certificate, &quot;SHA-256&quot;);&lt;/span&gt;
			&lt;span class=&quot;c1&quot;&gt;//var chain = new X509Certificate[] { certificate };  // You can fill this with the chain of certificates if needed&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;ICollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BouncyCastle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X509&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X509Certificate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chain&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BouncyCastle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X509&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X509Certificate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bcCert&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
			&lt;span class=&quot;c1&quot;&gt;//ICollection&amp;lt;Org.BouncyCastle.X509.X509Certificate&amp;gt; chainCollection = chain;&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;MakeSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SignDetached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;appearance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CryptoStandard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CMS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In order for this to work, you must create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cert&lt;/code&gt; subdirectory within the application directory which contains all certificate files needed for self-signing. You can create these files using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openssl&lt;/code&gt; program as follows:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;openssl genrsa &lt;span class=&quot;nt&quot;&gt;-out&lt;/span&gt; private.key 2048
openssl req &lt;span class=&quot;nt&quot;&gt;-new&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-key&lt;/span&gt; private.key &lt;span class=&quot;nt&quot;&gt;-out&lt;/span&gt; request.csr &lt;span class=&quot;nt&quot;&gt;-subj&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/C=IN/ST=GUJ/L=Vadodara/O=ACME/OU=Unit/CN=ACME&quot;&lt;/span&gt;
openssl x509 &lt;span class=&quot;nt&quot;&gt;-req&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-days&lt;/span&gt; 365 &lt;span class=&quot;nt&quot;&gt;-in&lt;/span&gt; request.csr &lt;span class=&quot;nt&quot;&gt;-signkey&lt;/span&gt; private.key &lt;span class=&quot;nt&quot;&gt;-out&lt;/span&gt; certificate.crt  &lt;span class=&quot;nt&quot;&gt;-addext&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;subjectAltName=DNS:acme.example.com&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-addext&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;basicConstraints=CA:FALSE&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-addext&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;keyUsage=critical,digitalSignature,nonRepudiation,keyEncipherment&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-addext&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;extendedKeyUsage=codeSigning&quot;&lt;/span&gt;
openssl pkcs12 &lt;span class=&quot;nt&quot;&gt;-export&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-out&lt;/span&gt; certificate.pfx &lt;span class=&quot;nt&quot;&gt;-inkey&lt;/span&gt; private.key &lt;span class=&quot;nt&quot;&gt;-in&lt;/span&gt; certificate.crt &lt;span class=&quot;nt&quot;&gt;-password&lt;/span&gt; pass:digisign
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Remember to replace ACME with your organization name and Vadodara with your region name. You can also replace the password “digisign” with something else - in which case you’ll have to change the C# code above correspondingly.&lt;/p&gt;

&lt;h2 id=&quot;notes-on-legal-and-regional-compliance&quot;&gt;Notes on legal and regional compliance&lt;/h2&gt;

&lt;p&gt;Using self-signed certificates may not hold up in legal disputes. In jurisdictions like Europe, CA-provided signatures are often mandated. In India, the IT Act of 2000 recommends but doesn’t mandate them—this may change in the future.&lt;/p&gt;

&lt;p&gt;Happy coding and let me know how it goes in the comments below.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Building a secure and efficient MQTT-HTTP gateway with Node.js</title>
   <link href="https://prahladyeri.github.io/blog/2024/12/mqtt-http-gateway-using-nodejs.html"/>
   <updated>2024-12-01T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/12/mqtt-http-gateway-using-nodejs</id>
   <content type="html">&lt;p&gt;One of my recent projects involved creating a Node.js script that acts as a gateway or middleware, capturing messages from an MQTT broker and relaying (streaming) them on the HTTP side via GET requests. Similarly, it should also handle POST requests from the HTTP side and publish them to the corresponding broker on the respective topic.&lt;/p&gt;

&lt;p&gt;Why use this approach?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Security:&lt;/strong&gt; Avoid exposing MQTT broker credentials or certificates to browsers.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Efficiency:&lt;/strong&gt; Browsers require WebSocket for MQTT communication, adding overhead compared to native MQTTS.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Flexibility:&lt;/strong&gt; Centralized middleware simplifies custom message processing, improving maintainability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/mqtt-http-gateway-node.webp&quot; alt=&quot;mqtt-http-gateway-node&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;preparing-the-nodejs-environment&quot;&gt;Preparing the Node.js environment.&lt;/h2&gt;

&lt;p&gt;We only need two node components viz. &lt;a href=&quot;https://www.npmjs.com/package/mqtt&quot;&gt;mqtt&lt;/a&gt; and &lt;a href=&quot;https://www.npmjs.com/package/express&quot;&gt;express&lt;/a&gt;. The former is the core library used to interact with MQTT broker and latter is the micro-framework used to interact with HTTP GET/POST requests.&lt;/p&gt;

&lt;p&gt;It’s not necessary to stick to version 4 of the mqtt.js but I found it more stable and compatible with my Node.js setup.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;mqtt@4 express
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;optional-cors-package&quot;&gt;Optional: CORS package&lt;/h3&gt;

&lt;p&gt;You might also need the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cors&lt;/code&gt; package if you want to customize Cross-Origin Resource Sharing (CORS) settings and enable access to the gateway for multiple users, such as REST API clients. I needed this in my case.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;cors
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;setting-up-your-mqtt-brokers&quot;&gt;Setting up your MQTT brokers&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brokers&lt;/code&gt; object contains configurations for each MQTT broker such as the URL and options (e.g., authentication). Each broker is also assigned a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lastMessage&lt;/code&gt; object to store the latest MQTT message for each topic.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mqtt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;mqtt&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;express&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;express&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cors&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;cors&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// Optional: Import CORS package&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fs&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;os&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;


&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;brokers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;brokers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
	&lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;mqtts://example.us-east-1.amazonaws.com:8883&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;na&quot;&gt;lastMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt;
	&lt;span class=&quot;na&quot;&gt;devices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt;
	&lt;span class=&quot;na&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;na&quot;&gt;ca&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;readFileSync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;certs/ca.pem&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
		&lt;span class=&quot;na&quot;&gt;cert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;readFileSync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;certs/cert.crt&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;readFileSync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;certs/key.key&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;na&quot;&gt;protocolId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MQTT&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;na&quot;&gt;protocolVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;na&quot;&gt;keepalive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;//wsOptions: {timeout: 30000, path: &apos;/mqtt&apos;},&lt;/span&gt;
		&lt;span class=&quot;na&quot;&gt;clientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;mqttjs_&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;substr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;rejectUnauthorized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;na&quot;&gt;clean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;secureProtocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;TLSv1_2_method&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Explicitly use TLSv1.2&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;brokers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;mosquitto&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
	&lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;mqtt://test.mosquitto.org&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:{},&lt;/span&gt;
	&lt;span class=&quot;na&quot;&gt;clientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;mqttjs_&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;substr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;na&quot;&gt;lastMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;initializing-middleware-for-http-requests&quot;&gt;Initializing middleware for HTTP requests&lt;/h2&gt;

&lt;p&gt;Middleware ensures proper handling of requests by validating input and enabling CORS.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Initialize HTTP Server&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;express&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;express&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Parse JSON payloads&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;origin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// Allow requests from any origin&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;establishing-mqtt-connections&quot;&gt;Establishing MQTT connections&lt;/h2&gt;

&lt;p&gt;I’ve kept the subscription topic to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#&lt;/code&gt; wildcard i.e. subscribe to all topics, you may or may not want to do that. The “connect” event of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mqttClient&lt;/code&gt; handles the connection where you can subscribe to the topics. Correspondingly, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;message&lt;/code&gt; event handles the incoming events from each broker. The dummy &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt; block here can be used to add any custom processing logic you may have. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lastMessage&lt;/code&gt; variable will anyway store the message for the corresponding topic.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;error&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;auth&lt;/code&gt; event handlers are just dummy blocks that you can expand upon if you want to. In the end, each client object is stored to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mqttClients&lt;/code&gt; array object from which it can later be retrieved.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Store MQTT client objects for each broker&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mqttClients&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;subTopics&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Function to connect to each broker&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;brokers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;broker&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;brokers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
	&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;initializing broker:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;broker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mqttClient&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mqtt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;broker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;broker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;mqttClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`Connected to broker: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;broker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;nx&quot;&gt;mqttClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;subTopics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`Subscription error for &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`Subscribed to topic &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;subTopics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; on &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;mqttClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;] Received message on topic: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	  &lt;span class=&quot;nx&quot;&gt;broker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;lastMessage&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	  &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//custom logic&lt;/span&gt;
		  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;custom_broker&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
			&lt;span class=&quot;c1&quot;&gt;//@todo: handle custom logic&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
	
	&lt;span class=&quot;nx&quot;&gt;mqttClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`Error with broker &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
	
	&lt;span class=&quot;nx&quot;&gt;mqttClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;auth&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;packet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Authenticating with certificate...&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

	  &lt;span class=&quot;c1&quot;&gt;// Check the certificate properties and perform the authentication logic here.&lt;/span&gt;
	  &lt;span class=&quot;c1&quot;&gt;// Call cb() with an error if authentication fails or null if it succeeds.&lt;/span&gt;
	  &lt;span class=&quot;nx&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;	
	
	&lt;span class=&quot;nx&quot;&gt;mqttClients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mqttClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;bridging-mqtt-with-express-api-routes&quot;&gt;Bridging MQTT with Express API routes&lt;/h2&gt;

&lt;p&gt;On the HTTP side, I’ve created just two routes viz. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;events&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;publish&lt;/code&gt;. The former is used to fetch messages from a broker and latter to publish to them.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/events&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;brokerKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;topic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;// Build the SQL query dynamically based on provided parameters&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;brokerKey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Invalid request. Provide brokerkey and topic.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;nx&quot;&gt;broker&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;brokers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;brokerKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;brokerKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//custom logic&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;custom_broker&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
			&lt;span class=&quot;c1&quot;&gt;//@todo: handle custom logic&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;nl&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
			&lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;broker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;lastMessage&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// HTTP POST: Publish messages back to MQTT&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/publish&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;brokerKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Destructure input fields&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Validate the input&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;brokerKey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;topic&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Invalid request. Provide broker key, topic, and message.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Retrieve the MQTT client for the specified broker&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mqttClient&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mqttClients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;brokerKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mqttClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Invalid broker specified.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Publish the message&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;mqttClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`Publish error on &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;brokerKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Failed to publish message.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`Message published successfully to &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;brokerKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is also pretty simple and straightforward. As earlier, I’ve created a dummy switch block for custom handling. The default is to just pick the latest message for that particular broker and topic (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;broker[&apos;lastMessage&apos;][topic]&lt;/code&gt;) and return it using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;res.json()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Same holds true for publish. It accepts three parameters called brokerKey, topic and message, then correspondingly publishes the message on the broker. You may want to add custom logic here including authentication logic based on API keys, etc.&lt;/p&gt;

&lt;h2 id=&quot;starting-the-express-server&quot;&gt;Starting the Express server&lt;/h2&gt;

&lt;p&gt;The final step is to start the Express server. Use a custom port, or fallback to a default if not provided.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PORT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PORT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PORT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`Server is running on http://localhost:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PORT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This setup provides a reliable foundation for creating an MQTT-HTTP gateway. You can further enhance it with persistent storage, advanced authentication, or message processing workflows. Let me know how this implementation works for you—or share your own ideas for improvement—in the comments below. Happy coding!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Building a simple customer management system in PHP with MySQL</title>
   <link href="https://prahladyeri.github.io/blog/2024/11/simple-customer-management-system-php-mysql.html"/>
   <updated>2024-11-20T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/11/simple-customer-management-system-php-mysql</id>
   <content type="html">&lt;p&gt;Creating a customer management system (CMS) is a great way for beginners to learn PHP and MySQL. This hands-on project will guide you through the process of building a simple CMS from scratch, covering database design, CRUD operations, and form handling.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;what-is-a-customer-management-system&quot;&gt;What is a customer management system?&lt;/h2&gt;

&lt;p&gt;A customer management system is a tool used to store and manage customer information. It allows businesses to efficiently track customer details, such as names, email addresses, and phone numbers, using basic CRUD (Create, Read, Update, Delete) operations.&lt;/p&gt;

&lt;p&gt;In this tutorial, you’ll learn how to:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Design a database for storing customer information.&lt;/li&gt;
  &lt;li&gt;Build a user-friendly web interface for managing customers.&lt;/li&gt;
  &lt;li&gt;Implement CRUD functionality with PHP and MySQL.&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow this tutorial, you need:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;A local server environment like &lt;strong&gt;XAMPP&lt;/strong&gt; or &lt;strong&gt;WAMP&lt;/strong&gt; (for Windows) or &lt;strong&gt;MAMP&lt;/strong&gt; (for macOS).&lt;/li&gt;
  &lt;li&gt;Basic knowledge of HTML, PHP, and SQL.&lt;/li&gt;
  &lt;li&gt;A text editor such as VS Code, Sublime Text, or Notepad++.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/simple-customer-management-system-php-mysql.webp&quot; alt=&quot;simple-customer-management-system-php-mysql&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;step-1-setting-up-the-project&quot;&gt;Step 1: Setting up the project&lt;/h2&gt;

&lt;h3 id=&quot;11-install-and-configure-your-server&quot;&gt;1.1 Install and configure your server&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Download and install &lt;strong&gt;XAMPP&lt;/strong&gt; or your preferred local server.&lt;/li&gt;
  &lt;li&gt;Start &lt;strong&gt;Apache&lt;/strong&gt; and &lt;strong&gt;MySQL&lt;/strong&gt; from the control panel.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;12-create-the-project-folder&quot;&gt;1.2 Create the project folder&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Navigate to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;htdocs&lt;/code&gt; directory in XAMPP and create a folder named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;customer_management&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;step-2-designing-the-database&quot;&gt;Step 2: Designing the database&lt;/h2&gt;

&lt;h3 id=&quot;21-create-the-database&quot;&gt;2.1 Create the database&lt;/h3&gt;
&lt;ol&gt;
  &lt;li&gt;Open &lt;strong&gt;phpMyAdmin&lt;/strong&gt; by navigating to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost/phpmyadmin&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Click &lt;strong&gt;New&lt;/strong&gt;, and name the database &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;customer_db&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;22-create-the-customers-table&quot;&gt;2.2 Create the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;customers&lt;/code&gt; table&lt;/h3&gt;
&lt;p&gt;Run the following SQL query to create the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;customers&lt;/code&gt; table:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;customers&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AUTO_INCREMENT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;UNIQUE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;phone&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;created_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;TIMESTAMP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CURRENT_TIMESTAMP&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This table has the following columns:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt;: A unique identifier for each customer.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt;: The customer’s name.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;email&lt;/code&gt;: The customer’s email address.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;phone&lt;/code&gt;: The customer’s phone number.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;created_at&lt;/code&gt;: A timestamp of when the customer was added.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;step-3-building-the-front-end&quot;&gt;Step 3: Building the front-end&lt;/h2&gt;

&lt;h3 id=&quot;31-creating-the-html-layout&quot;&gt;3.1 Creating the HTML layout&lt;/h3&gt;
&lt;p&gt;Create a file named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.php&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;customer_management&lt;/code&gt; folder:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;lang=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;en&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;charset=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;viewport&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;width=device-width, initial-scale=1.0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Customer Management&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;style.css&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;container&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Customer Management System&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;add_customer.php&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;btn&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Add New Customer&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;table&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;thead&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;ID&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Name&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Email&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Phone&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Actions&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/thead&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;tbody&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Dynamic content will go here --&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;step-4-writing-the-back-end-logic&quot;&gt;Step 4: Writing the back-end logic&lt;/h2&gt;

&lt;h3 id=&quot;41-database-connection&quot;&gt;4.1 Database connection&lt;/h3&gt;
&lt;p&gt;Create a file named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db.php&lt;/code&gt; to handle database connectivity:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$host&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;localhost&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;root&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$password&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$database&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;customer_db&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;mysqli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$database&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connect_error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;die&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Connection failed: &quot;&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connect_error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;42-fetch-and-display-customers&quot;&gt;4.2 Fetch and display customers&lt;/h3&gt;
&lt;p&gt;Update &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.php&lt;/code&gt; to fetch customer data dynamically:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;db.php&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;SELECT * FROM customers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;tbody&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_rows&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$row&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch_assoc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;email&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;phone&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;edit_customer.php?id=&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;btn-edit&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Edit&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;delete_customer.php?id=&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;btn-delete&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Delete&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endwhile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;colspan=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;5&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;No customers found.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endif&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;43-adding-a-customer&quot;&gt;4.3 Adding a customer&lt;/h3&gt;
&lt;ol&gt;
  &lt;li&gt;Create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add_customer.php&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;db.php&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;REQUEST_METHOD&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;POST&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;email&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$phone&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;phone&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;INSERT INTO customers (name, email, phone) VALUES (&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&apos;, &apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&apos;, &apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$phone&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&apos;)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Location: index.php&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Error: &quot;&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;

&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;lang=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;en&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Add Customer&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;POST&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;placeholder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Name&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;email&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;email&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;placeholder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Email&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;phone&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;placeholder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Phone&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Add Customer&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;step-5-styling-the-app&quot;&gt;Step 5: Styling the app&lt;/h2&gt;

&lt;p&gt;Create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;style.css&lt;/code&gt; file for basic styling:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Arial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sans-serif&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#f9f9f9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#333&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;800px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#fff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#ddd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;border-collapse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;collapse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;margin-top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;th&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;td&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#ddd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.btn&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inline-block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;white&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#007bff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;text-decoration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;step-6-adding-update-and-delete-functionality&quot;&gt;Step 6: Adding update and delete functionality&lt;/h2&gt;

&lt;h3 id=&quot;update&quot;&gt;Update&lt;/h3&gt;
&lt;p&gt;Create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;edit_customer.php&lt;/code&gt; to edit customer details using a pre-filled form.&lt;/p&gt;

&lt;h3 id=&quot;delete&quot;&gt;Delete&lt;/h3&gt;
&lt;p&gt;Create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delete_customer.php&lt;/code&gt; to handle deleting a customer by ID.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;By following these steps, you’ve built a fully functional customer management system using PHP and MySQL. This project taught you how to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Design a database.&lt;/li&gt;
  &lt;li&gt;Implement CRUD operations.&lt;/li&gt;
  &lt;li&gt;Create a dynamic web interface.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can now extend this project by adding features like search, sorting, or user authentication.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/&quot;&gt;PHP Documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://dev.mysql.com/doc/&quot;&gt;MySQL Official Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Unlocking Text from Embedded-Font PDFs: A pytesseract OCR Tutorial</title>
   <link href="https://prahladyeri.github.io/blog/2024/11/unlocking-text-from-embedded-font-pdf-pytesseract.html"/>
   <updated>2024-11-11T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/11/unlocking-text-from-embedded-font-pdf-pytesseract</id>
   <content type="html">&lt;p&gt;Extracting text from a PDF is usually straightforward when it’s in English and doesn’t have embedded fonts. However, once those assumptions are removed, it becomes challenging to use basic python libraries like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdfminer&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdfplumber&lt;/code&gt;. Last month, I was tasked with extracting text from a Gujarati-language PDF and importing data fields such as name, address, city, etc., into JSON format.&lt;/p&gt;

&lt;p&gt;If the font is embedded in the PDF itself, simple copy-pasting won’t work, and using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdfplumber&lt;/code&gt; will return unreadable junk text. Therefore, I had to convert each PDF page to an image and then apply OCR using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pytesseract&lt;/code&gt; library to “scan” the page instead of just reading it. This tutorial will show you how to do just that.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/unlocking-text-from-embedded-font-pdf-pytesseract.webp&quot; alt=&quot;unlocking-text-from-embedded-font-pdf-pytesseract&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;things-you-will-need&quot;&gt;Things you will need&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdfplumber&lt;/code&gt; (Python library)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdf2image&lt;/code&gt; (Python library)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pytesseract&lt;/code&gt; (Python library)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://tesseract-ocr.github.io/tessdoc/Downloads.html&quot;&gt;tesseract-ocr&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can install the Python libraries using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt; commands as shown below. For Tesseract-OCR, download and install the software from the official site. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pytesseract&lt;/code&gt; is just a wrapper around the tesseract software.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;pdfplumber
pip &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;pdf2image
pip &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;pytesseract
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;converting-the-pdf-page-to-an-image&quot;&gt;Converting the PDF page to an image&lt;/h2&gt;

&lt;p&gt;The first step is to convert your PDF page to an image. This &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extract_text_from_pdf()&lt;/code&gt; function does exactly that-you pass the PDF path and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;page_num&lt;/code&gt; (zero indexed) as parameters. Note that I’m converting the page to black and white first for clarity, this is optional.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Extract text from a specific page of a PDF
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;extract_text_from_pdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pdf_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;page_num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Use pdfplumber to open the PDF
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;pdf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pdfplumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pdf_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;extracting page &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;page_num&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;..&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;page&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;page_num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;images&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;convert_from_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pdf_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first_page&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;page_num&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_page&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;page_num&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;images&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;# Convert to black and white
&lt;/span&gt;	&lt;span class=&quot;n&quot;&gt;bw_image&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;convert_to_bw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;# Save the B&amp;amp;W image for debugging (optional)
&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;#bw_image.save(&quot;bw_page.png&quot;)
&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;# Perform OCR on the B&amp;amp;W image
&lt;/span&gt;	&lt;span class=&quot;n&quot;&gt;e_text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ocr_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bw_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;out.txt&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;w&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;utf-8&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;#print(&quot;output written to file.&quot;)
&lt;/span&gt;	&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;process_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;page_num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Error occurred:&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;done..&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	
&lt;span class=&quot;c1&quot;&gt;# Convert image to black and white
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;convert_to_bw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Convert to grayscale
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;gray&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;L&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Apply threshold to convert to pure black and white
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;bw&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;128&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;1&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bw&lt;/span&gt;
	
&lt;span class=&quot;c1&quot;&gt;# Perform OCR using Tesseract on a given image
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ocr_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Perform OCR
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;custom_config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;--oem 3 --psm 6 -l guj+eng&apos;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pytesseract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image_to_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;custom_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# --psm 6 treats the image as a block of text
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Error during OCR: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;

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

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ocr_image()&lt;/code&gt; function uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pytesseract&lt;/code&gt; to extract text from the image through OCR. The technical parameters like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--oem&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--psm&lt;/code&gt; control how the image is processed, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-l guj+eng&lt;/code&gt; parameter sets the languages to be read. Since this PDF contained occasional English text, I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;guj+eng&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;processing-the-text&quot;&gt;Processing the text&lt;/h2&gt;

&lt;p&gt;Once you’ve imported the text using OCR, you can parse it in the format you want. This works similarly to other PDF libraries like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdfplumber&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pypdf2&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;nums&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;0&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;૧&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;૨&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;૩&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;૪&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;૫&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;૬&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;૭&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;૮&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;૯&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;process_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;page_num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;last_surname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;last_kramank&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;processing page &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;page_num&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;..&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;splitlines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;|&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;[&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;]&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;word&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos; &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;new_rec&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nums&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;new_rec&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;
        
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_rec&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# numbered line
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;records&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;last_surname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;kramank&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;last_kramank&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;full_name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos; &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;surname&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;pdf_page_num&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;page_num&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;registered_by&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;village_vatan&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;village_mosal&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;વર્ષ&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;dob&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos; વર્ષ&apos;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;-&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;dob&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;warning: no date&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;marital_status&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;extra_fields&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;::&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;blood_group&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_surname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# new member in existing family
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;records&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;kramank&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_kramank&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;surname&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_surname&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;full_name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos; &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;pdf_page_num&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;page_num&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;registered_by&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;village_vatan&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;village_mosal&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;વર્ષ&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# date exists
&lt;/span&gt;                &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;dob&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos; વર્ષ&apos;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;-&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;dob&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;warning: no date&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;marital_status&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;extra_fields&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;::&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;blood_group&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# continuation lines
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;)&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;મો.ઃ&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;extra_fields&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos; &apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;::&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:])&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;records&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
    &lt;span class=&quot;n&quot;&gt;jstr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dumps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;records&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;guj.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;w&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;utf-8&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jstr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;written page &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;page_num&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; to json..&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Every PDF has its own nuances that must be accounted for. In this case, a new serial number (like 0૧ or 0૨) in the first field signaled a new group when the subsequent field (surname) changed.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pytesseract&lt;/code&gt; is a testament to the evolution and advancement in IT technology. About a decade ago, reading or parsing a PDF image using OCR in a non-English language on a modestly configured PC or laptop would have been nearly impossible. This is truly progress! Happy coding, and let me know how it goes in the comments below.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/46140485/849365&quot;&gt;Tesseract installation in windows&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/37745519/849365&quot;&gt;Use pytesseract OCR to recognize text from an image&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/65572698/849365&quot;&gt;How to configure pytesseract to support text detection for non English language in windows 10?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>The indie blogger’s guide to seamless commenting: meet giscus</title>
   <link href="https://prahladyeri.github.io/blog/2024/11/indie-bloggers-guide-to-seamless-computing.html"/>
   <updated>2024-11-09T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/11/indie-bloggers-guide-to-seamless-computing</id>
   <content type="html">&lt;p&gt;Indie bloggers are on the constant lookout for tools that simplify their work while keeping costs low and user experience seamless. One of the most overlooked aspects of running a blog is finding the right comment hosting system. Enter &lt;strong&gt;giscus&lt;/strong&gt;, a modern solution that stands out among its peers for being lightweight, privacy-friendly, and free to use. Here’s why it’s the best comment hosting system for the indie blogger living in today’s digital age.&lt;/p&gt;

&lt;h2 id=&quot;benefits-of-static-hosting&quot;&gt;Benefits of static hosting&lt;/h2&gt;

&lt;h3 id=&quot;no-hassles-of-website-maintenance&quot;&gt;No hassles of website maintenance&lt;/h3&gt;

&lt;p&gt;Indie bloggers love static websites for good reason. Unlike traditional dynamic websites, static sites come with zero server-side headaches. Once you’ve deployed your content, you don’t need to worry about database management, server updates, or unexpected downtime caused by backend failures. Platforms like &lt;strong&gt;Hugo&lt;/strong&gt;, &lt;strong&gt;Jekyll&lt;/strong&gt;, and &lt;strong&gt;Gatsby&lt;/strong&gt; make it easier than ever to generate and maintain static sites. Add a robust version control system like &lt;strong&gt;GitHub&lt;/strong&gt;, and your content stays safe with every update neatly tracked.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/indie-bloggers-guide-to-seamless-computing.webp&quot; alt=&quot;indie-bloggers-guide-to-seamless-computing&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;no-costly-backend-bills-to-pay&quot;&gt;No costly backend bills to pay&lt;/h3&gt;

&lt;p&gt;Keeping a dynamic website up and running can be an expensive affair. Backend servers and database management come with their own set of recurring costs, from hosting fees to maintenance bills. These expenses can quickly add up, especially for indie bloggers who are not monetizing their content or are just starting out. Static site generators, paired with free hosting options, offer a refreshing alternative that doesn’t put a dent in your wallet.&lt;/p&gt;

&lt;h3 id=&quot;free-platforms-for-the-pleb&quot;&gt;Free platforms for the pleb&lt;/h3&gt;

&lt;p&gt;Gone are the days when hosting a blog meant shelling out money for a server. Platforms like &lt;strong&gt;GitHub Pages&lt;/strong&gt;, &lt;strong&gt;Netlify&lt;/strong&gt;, and &lt;strong&gt;Vercel&lt;/strong&gt; offer free static site hosting that’s perfect for indie bloggers. These platforms come with seamless deployment pipelines, allowing you to push updates to your blog in a matter of seconds. And since static hosting doesn’t require server-side processing, the pages load lightning fast, ensuring a smooth user experience for readers.&lt;/p&gt;

&lt;h2 id=&quot;the-horrors-of-privacy-invasive-third-party-systems-like-disqus&quot;&gt;The horrors of privacy-invasive third-party systems like Disqus&lt;/h2&gt;

&lt;p&gt;In the past, the go-to solution for adding comments to a blog was &lt;strong&gt;Disqus&lt;/strong&gt;. While popular, Disqus became synonymous with intrusive ads, data tracking, and privacy concerns. Here are some of the pain points many bloggers and users have faced:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Tracking and privacy issues&lt;/strong&gt;: Disqus is notorious for collecting user data and tracking user behavior across the web. For bloggers who value their readers’ privacy, this is a significant red flag.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Ads and monetization&lt;/strong&gt;: Disqus’s free version often injects ads into your blog’s comment section, cluttering the user experience and taking away from the essence of the content.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Slow load times&lt;/strong&gt;: Disqus can slow down the loading speed of your site due to its heavy scripts and external calls, which negatively impacts SEO and user satisfaction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The modern blogger who champions simplicity and transparency needs an alternative that respects user privacy while being lightweight and functional.&lt;/p&gt;

&lt;h2 id=&quot;the-horrors-of-coding-and-maintaining-your-own-commenting-system&quot;&gt;The horrors of coding and maintaining your own commenting system&lt;/h2&gt;

&lt;p&gt;Some bloggers may consider building their own commenting system to avoid the pitfalls of third-party options. While this sounds like an appealing DIY project, it comes with its own set of nightmares:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Complexity&lt;/strong&gt;: Developing a custom commenting system requires backend expertise, database integration, and server management. This can be overwhelming, especially for bloggers focused more on writing than on coding.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Security risks&lt;/strong&gt;: Handling user-generated content means dealing with potential security threats like SQL injection and spam bots. Keeping the system secure requires ongoing updates and vigilance.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Maintenance&lt;/strong&gt;: A custom-built system needs consistent maintenance. Any change in server configuration or software updates can disrupt the system and create additional work for the blogger.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the indie blogger who doesn’t want to spend their time coding or worrying about security flaws, a more straightforward option is essential.&lt;/p&gt;

&lt;h2 id=&quot;enter-the-middle-ground-why-giscus-is-the-best-of-both-worlds&quot;&gt;Enter the middle ground: why giscus is the best of both worlds&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://giscus.app/&quot;&gt;&lt;strong&gt;giscus&lt;/strong&gt;&lt;/a&gt; offers a perfect balance between ease of use, privacy, and functionality. Here’s why it’s the ideal comment hosting system for the indie blogger:&lt;/p&gt;

&lt;h3 id=&quot;fully-integrated-with-github&quot;&gt;Fully integrated with GitHub&lt;/h3&gt;

&lt;p&gt;giscus uses GitHub Discussions as its engine, so comments are stored as discussion threads in your repository. This means:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Transparent data management&lt;/strong&gt;: All comment data is stored on GitHub, a platform that millions of developers trust.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Easy moderation&lt;/strong&gt;: You can manage, edit, or delete comments directly from GitHub, leveraging the familiar interface and robust features.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Open-source spirit&lt;/strong&gt;: giscus itself is open-source, which aligns with the values of transparency and collaboration many indie bloggers uphold.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;lightweight-and-fast&quot;&gt;Lightweight and fast&lt;/h3&gt;

&lt;p&gt;Unlike heavyweight comment systems, giscus is built with performance in mind. It doesn’t add bloat to your site, ensuring that your pages load quickly and smoothly. This is especially important for maintaining good SEO and providing a seamless experience for mobile readers.&lt;/p&gt;

&lt;h3 id=&quot;privacy-focused&quot;&gt;Privacy-focused&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;giscus&lt;/strong&gt; does not track users or display ads, making it an excellent choice for bloggers who want to maintain a privacy-friendly site. Since comments are facilitated through GitHub accounts, users already familiar with GitHub’s policies can engage confidently.&lt;/p&gt;

&lt;h3 id=&quot;simple-installation&quot;&gt;Simple installation&lt;/h3&gt;

&lt;p&gt;Setting up giscus is a breeze. Here’s how to get started:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Create a GitHub repository&lt;/strong&gt; or use an existing one to store the discussions.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Visit the giscus website&lt;/strong&gt; and generate the code snippet by selecting your repository and preferred discussion category.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Copy the provided code&lt;/strong&gt; and paste it into your site’s codebase where you want the comment section to appear.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Deploy your site&lt;/strong&gt;, and voilà! You have a fully functional, GitHub-based commenting system.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;user-friendly-for-commenters&quot;&gt;User-friendly for commenters&lt;/h3&gt;

&lt;p&gt;Engagement through giscus requires users to log in with their GitHub account, which appeals to a tech-savvy audience that values simplicity and privacy. For users already on GitHub, commenting feels seamless, and they can subscribe to discussions without having to create yet another account.&lt;/p&gt;

&lt;h3 id=&quot;community-driven-enhancements&quot;&gt;Community-driven enhancements&lt;/h3&gt;

&lt;p&gt;Because giscus is an open-source project, it evolves based on user feedback. Developers can contribute improvements, making it a community-driven tool that adapts over time.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Choosing the right comment system is critical for any blogger, especially those who want a balance between ease of use, privacy, and functionality. While legacy systems like Disqus have paved the way for commenting but introduced privacy issues, and coding your own system is often impractical, &lt;strong&gt;giscus&lt;/strong&gt; shines as the modern, lightweight solution that respects privacy and integrates seamlessly with static sites. It’s time for indie bloggers to make the shift and experiment with giscus—the best comment hosting system for present times.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The emotional toll of Agile: burnout in the name of agility</title>
   <link href="https://prahladyeri.github.io/blog/2024/11/the-emotional-toll-of-agile.html"/>
   <updated>2024-11-08T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/11/the-emotional-toll-of-agile</id>
   <content type="html">&lt;p&gt;There is no doubt that over the last few years, Agile methodologies have gained prominence as a means to foster flexibility, innovation, and responsiveness to change. However, beneath the surface of this seemingly effective approach lies a troubling reality: the emotional toll it can take on teams. This article delves into the impact of Agile practices on team morale and well-being, particularly regarding constant iterations and pressure to deliver, exploring how the very principles intended to enhance productivity can inadvertently lead to burnout.&lt;/p&gt;

&lt;h2 id=&quot;understanding-agile-a-double-edged-sword&quot;&gt;Understanding Agile: a double-edged sword&lt;/h2&gt;

&lt;p&gt;Agile practices prioritize iterative development, continuous feedback, and collaborative teamwork. On paper, these elements promote a dynamic work environment that encourages creativity and adaptability. Teams are empowered to experiment, pivot, and respond swiftly to customer needs. However, the relentless cycle of iterations, coupled with the expectation of constant delivery, can create a high-pressure atmosphere that strains team morale and emotional well-being.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/the-emotional-toll-of-agile.png&quot; alt=&quot;the-emotional-toll-of-agile&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;the-constant-cycle-of-iteration&quot;&gt;The constant cycle of iteration&lt;/h3&gt;

&lt;p&gt;At the heart of Agile is the concept of iterative cycles—sprints that require teams to deliver incremental value within short timeframes. While this method enables quick responses to changing requirements, it also sets a relentless pace. Teams may find themselves caught in a continuous loop of planning, execution, review, and adjustment, leaving little room for reflection or recovery.&lt;/p&gt;

&lt;p&gt;The emotional toll of this relentless cycle can manifest in several ways:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Increased Stress Levels&lt;/strong&gt;: The pressure to meet tight deadlines and deliver high-quality work can lead to elevated stress levels among team members. The expectation of constant output can make individuals feel overwhelmed and anxious, particularly when faced with competing priorities.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Reduced Job Satisfaction&lt;/strong&gt;: Continuous iterations may result in diminished job satisfaction. When team members feel they are perpetually “on the clock,” they may lose sight of the intrinsic value of their work, leading to disengagement and a decline in overall morale.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Erosion of Work-Life Balance&lt;/strong&gt;: Agile’s focus on responsiveness can blur the lines between work and personal life. Team members may feel compelled to extend their work hours to keep up with demands, ultimately jeopardizing their work-life balance and well-being.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;the-pressure-to-deliver&quot;&gt;The pressure to deliver&lt;/h3&gt;

&lt;p&gt;The Agile philosophy encourages teams to prioritize customer needs and deliver value quickly. While this is essential for success, it can also create a culture of urgency that places undue pressure on team members. The fear of falling behind or disappointing stakeholders can lead to a toxic cycle of overwork and burnout.&lt;/p&gt;

&lt;h4 id=&quot;impacts-of-pressure-on-team-morale&quot;&gt;Impacts of pressure on team morale&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Fear of Failure&lt;/strong&gt;: In an Agile environment, the fear of failure can be amplified by the fast-paced nature of work. Team members may become hesitant to take risks or voice concerns, fearing that any setback will reflect poorly on their performance.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Team Fragmentation&lt;/strong&gt;: The pressure to deliver can lead to competition among team members rather than collaboration. When individuals are primarily focused on their own deliverables, the sense of teamwork and camaraderie may erode, resulting in a fragmented team dynamic.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Burnout&lt;/strong&gt;: Perhaps the most concerning outcome of sustained pressure in an Agile context is burnout. Prolonged exposure to high-stress conditions can lead to physical and emotional exhaustion, cynicism, and a sense of inefficacy. This not only affects individual team members but can also have cascading effects on overall team productivity and performance.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;strategies-for-mitigating-the-emotional-toll&quot;&gt;Strategies for mitigating the emotional toll&lt;/h2&gt;

&lt;p&gt;Recognizing the potential for burnout in Agile teams is crucial for fostering a healthy work environment. Here are some strategies organizations can implement to mitigate the emotional toll:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Promote a Culture of Psychological Safety&lt;/strong&gt;: Encouraging open communication and creating a safe space for team members to voice concerns can help alleviate the fear of failure. When individuals feel valued and heard, they are more likely to take risks and contribute meaningfully to the team.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Encourage Regular Breaks and Downtime&lt;/strong&gt;: Organizations should emphasize the importance of work-life balance and encourage team members to take regular breaks. This can help individuals recharge and return to work with renewed energy and focus.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Set Realistic Expectations&lt;/strong&gt;: While the Agile framework encourages responsiveness, organizations should strive to set realistic expectations for deliverables. Avoiding unrealistic deadlines and recognizing the limits of team capacity can help prevent burnout.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Foster Team Cohesion&lt;/strong&gt;: Building strong interpersonal relationships among team members can enhance collaboration and support. Team-building activities, both in-person and virtual, can foster a sense of belonging and unity.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Conduct Regular Retrospectives&lt;/strong&gt;: Regular retrospectives are a cornerstone of Agile practices. However, these sessions should focus not only on project outcomes but also on team well-being. Discussing emotional challenges and collectively brainstorming solutions can foster a supportive team environment.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;While Agile methodologies offer numerous benefits for organizations, it is crucial to recognize the emotional toll they can exact on teams. The pressure to deliver, combined with the relentless cycle of iterations, can lead to burnout and negatively impact team morale. By prioritizing psychological safety, work-life balance, and realistic expectations, organizations can harness the full potential of Agile while safeguarding the well-being of their teams.&lt;/p&gt;

&lt;p&gt;Ultimately, the goal should be to create a work environment where agility is not just about speed and efficiency but also about fostering a culture of well-being and collaboration—where teams can thrive without sacrificing their emotional health in the name of agility.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;references&quot;&gt;References&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Beck, K., et al. (2001). &lt;em&gt;Manifesto for Agile Software Development&lt;/em&gt;. Agile Alliance.&lt;/li&gt;
  &lt;li&gt;Goleman, D. (2013). &lt;em&gt;Focus: The Hidden Driver of Excellence&lt;/em&gt;. HarperCollins.&lt;/li&gt;
  &lt;li&gt;Maslach, C., &amp;amp; Leiter, M. P. (2016). &lt;em&gt;Burnout at Work: A Psychological Perspective&lt;/em&gt;. Psychology Press.&lt;/li&gt;
  &lt;li&gt;Sutherland, J., &amp;amp; Schwaber, K. (2017). &lt;em&gt;The Scrum Guide: The Definitive Guide to Scrum: The Rules of the Game&lt;/em&gt;. Scrum.org.&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Working with JSON data in PHP and MySQL: storing and retrieving complex structures</title>
   <link href="https://prahladyeri.github.io/blog/2024/11/working-with-json-data-in-php-mysql.html"/>
   <updated>2024-11-04T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/11/working-with-json-data-in-php-mysql</id>
   <content type="html">&lt;p&gt;In the ever-evolving world of web development, the ability to manage complex data structures efficiently is vital. JSON (JavaScript Object Notation) has become a standard for data exchange due to its lightweight format and ease of use. If you’re working with PHP and MySQL, understanding how to store and retrieve JSON data can greatly enhance the flexibility and capabilities of your applications. This guide will dive into using JSON columns in MySQL, interacting with them using PHP, and optimizing these processes.&lt;/p&gt;

&lt;h3 id=&quot;why-use-json-in-mysql&quot;&gt;Why use JSON in MySQL?&lt;/h3&gt;

&lt;p&gt;MySQL has supported JSON data types since version 5.7. This native support provides the ability to store complex, nested data structures without converting them to string formats. Here’s why JSON is beneficial in MySQL:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Ease of data modeling&lt;/strong&gt;: JSON columns allow you to store arrays, objects, and other nested data structures without the need for additional tables or complex joins.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Native functions for manipulation&lt;/strong&gt;: MySQL provides several JSON functions (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSON_EXTRACT&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSON_CONTAINS&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSON_SET&lt;/code&gt;, etc.) to query and manipulate data within JSON columns.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Performance improvements&lt;/strong&gt;: JSON data is stored in a format that allows efficient parsing and retrieval, enhancing query performance compared to traditional string parsing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/working-with-json-data-in-php-mysql.webp&quot; alt=&quot;working-with-json-data-in-php-mysql&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;creating-a-json-column-in-mysql&quot;&gt;Creating a JSON column in MySQL&lt;/h3&gt;

&lt;p&gt;To create a table with a JSON column, the syntax is straightforward:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AUTO_INCREMENT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JSON&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;attributes&lt;/code&gt; column can store nested structures like:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;preferences&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;theme&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;dark&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;notifications&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;history&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2024-11-01&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2024-11-02&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;storing-json-data-using-php&quot;&gt;Storing JSON data using PHP&lt;/h3&gt;

&lt;p&gt;PHP makes it simple to work with JSON through functions like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;json_encode()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;json_decode()&lt;/code&gt;. When inserting data into a JSON column, you need to ensure the data is properly formatted. Here’s a practical example of how to insert data:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Sample user data&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$userAttributes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;preferences&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;theme&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;dark&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;notifications&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;history&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2024-11-01&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;2024-11-02&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Convert the array to JSON&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$jsonData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;json_encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$userAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Database connection (PDO)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$pdo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PDO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql:host=localhost;dbname=testdb&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;username&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;password&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$pdo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PDO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ATTR_ERRMODE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PDO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ERRMODE_EXCEPTION&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Insert JSON data&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$pdo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INSERT INTO user_data (name, attributes) VALUES (:name, :attributes)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;name&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;John Doe&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;attributes&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$jsonData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;retrieving-and-parsing-json-data&quot;&gt;Retrieving and parsing JSON data&lt;/h3&gt;

&lt;p&gt;Fetching data from a JSON column in MySQL and decoding it in PHP is equally straightforward. Let’s retrieve and parse the stored data:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Fetching data&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$pdo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SELECT attributes FROM user_data WHERE name = &apos;John Doe&apos;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PDO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;FETCH_ASSOC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$attributes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;json_decode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;User Theme: &apos;&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;preferences&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;theme&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;json_decode()&lt;/code&gt; function converts the JSON string into a PHP array when the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; parameter is passed. This makes it easy to work with nested data directly.&lt;/p&gt;

&lt;h3 id=&quot;searching-and-filtering-json-data-in-mysql&quot;&gt;Searching and filtering JSON data in MySQL&lt;/h3&gt;

&lt;p&gt;MySQL’s JSON functions allow you to perform searches and filter data based on specific JSON keys and values. For instance, you can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSON_EXTRACT&lt;/code&gt; to access data or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSON_CONTAINS&lt;/code&gt; for searching.&lt;/p&gt;

&lt;h4 id=&quot;example-retrieving-users-who-have-a-dark-theme-preference&quot;&gt;Example: Retrieving users who have a dark theme preference&lt;/h4&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_data&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JSON_EXTRACT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;$.preferences.theme&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;dark&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;updating-json-data-in-mysql&quot;&gt;Updating JSON data in MySQL&lt;/h3&gt;

&lt;p&gt;Modifying JSON data directly in MySQL without replacing the entire content is a powerful feature. You can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSON_SET&lt;/code&gt; function to update specific keys.&lt;/p&gt;

&lt;h4 id=&quot;example-updating-a-users-notification-setting&quot;&gt;Example: Updating a user’s notification setting&lt;/h4&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;UPDATE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_data&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JSON_SET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;$.preferences.notifications&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;John Doe&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;best-practices-for-working-with-json-in-php-and-mysql&quot;&gt;Best practices for working with JSON in PHP and MySQL&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Validation&lt;/strong&gt;: Always validate JSON data before storing or processing it. Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;json_last_error()&lt;/code&gt; to check for any encoding/decoding issues in PHP.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Indexing&lt;/strong&gt;: If you frequently query specific keys in your JSON data, consider using &lt;strong&gt;generated columns&lt;/strong&gt; to index them for faster lookups.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: Sanitize any data before inserting it into your database to prevent SQL injection attacks.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Data structure considerations&lt;/strong&gt;: JSON is ideal for semi-structured data but not for scenarios that require complex relational queries. Use traditional relational tables for such cases.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;advanced-techniques-using-generated-columns-for-indexing&quot;&gt;Advanced techniques: using generated columns for indexing&lt;/h3&gt;

&lt;p&gt;Generated columns can make querying JSON data more efficient by creating an indexable column derived from the JSON content.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_data&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;theme&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JSON_UNQUOTE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JSON_EXTRACT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;$.preferences.theme&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;STORED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;theme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This allows you to query the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;theme&lt;/code&gt; column directly, improving performance:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_data&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;theme&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;dark&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;Managing complex JSON structures in PHP and MySQL has become simpler with native JSON support. Whether you need to store user preferences, log histories, or any other semi-structured data, the combination of MySQL’s JSON data type and PHP’s JSON functions provides powerful and flexible solutions. Always remember to validate, secure, and optimize your data handling for the best performance and reliability.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;Sources&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/&quot;&gt;PHP Documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/json.html&quot;&gt;MySQL 8.0 Reference Manual&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Handling large datasets in PHP: best practices for database management</title>
   <link href="https://prahladyeri.github.io/blog/2024/11/handling-large-datasets-in-php.html"/>
   <updated>2024-11-03T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/11/handling-large-datasets-in-php</id>
   <content type="html">&lt;p&gt;When dealing with vast amounts of data in PHP, the challenges are not just technical but strategic. Efficiently managing large datasets ensures that web applications remain fast, responsive, and user-friendly. In this guide, we’ll explore essential practices such as pagination, batch processing, and crafting efficient SQL queries.&lt;/p&gt;

&lt;h3 id=&quot;why-handling-large-datasets-is-challenging&quot;&gt;Why handling large datasets is challenging&lt;/h3&gt;
&lt;p&gt;PHP’s strength lies in its versatility and ease of use. However, as datasets grow into millions of records, memory limitations and performance bottlenecks become significant concerns. Loading too much data at once can lead to high memory usage, sluggish response times, and even server crashes.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/handling-large-datasets-in-php.webp&quot; alt=&quot;handling-large-datasets-in-php&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;best-practices-for-managing-large-datasets&quot;&gt;Best practices for managing large datasets&lt;/h3&gt;

&lt;h4 id=&quot;1-use-pagination&quot;&gt;1. Use pagination&lt;/h4&gt;
&lt;p&gt;Pagination breaks down large result sets into smaller, more manageable pieces. By fetching only a subset of records per request, you optimize performance and provide a better user experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example: Implementing pagination with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LIMIT&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OFFSET&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$page&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;page&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;page&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$records_per_page&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$offset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$page&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$records_per_page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;SELECT * FROM large_table LIMIT &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$records_per_page&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; OFFSET &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$offset&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$pdo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Advantages&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Reduces memory usage by limiting data retrieval.&lt;/li&gt;
  &lt;li&gt;Improves page load times and user navigation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;2-implement-batch-processing&quot;&gt;2. Implement batch processing&lt;/h4&gt;
&lt;p&gt;Batch processing handles data in smaller, sequential chunks, making large-scale operations more efficient and less resource-intensive. This technique is essential for data imports, exports, and complex data manipulations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example: Batch processing with a loop&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$batch_size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$total_records&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Assume we know this beforehand&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$total_records&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$batch_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;SELECT * FROM large_table LIMIT &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$batch_size&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; OFFSET &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$data_chunk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$pdo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Process data_chunk here&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Applications&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Data migrations.&lt;/li&gt;
  &lt;li&gt;Processing log files or data transformations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;3-optimize-sql-queries&quot;&gt;3. Optimize SQL queries&lt;/h4&gt;
&lt;p&gt;Writing efficient SQL queries is paramount when managing large datasets. Poorly optimized queries can drastically affect performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key tips for query optimization&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Use indexes&lt;/strong&gt;: Ensure that columns frequently used in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WHERE&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JOIN&lt;/code&gt;, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ORDER BY&lt;/code&gt; clauses are indexed.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Avoid &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT *&lt;/code&gt;&lt;/strong&gt;: Fetch only the columns you need to reduce data retrieval size.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Use LIMIT&lt;/strong&gt;: Always limit the number of rows returned when fetching large data.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Leverage caching&lt;/strong&gt;: Use PHP’s built-in caching mechanisms or third-party solutions like Redis to minimize database hits.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example: Optimized query&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;SELECT id, name, email FROM users WHERE status = &apos;active&apos; ORDER BY created_at DESC LIMIT 50&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;4-use-phps-pdo-with-buffered-queries&quot;&gt;4. Use PHP’s PDO with buffered queries&lt;/h4&gt;
&lt;p&gt;Buffered queries load the complete result set into memory, allowing you to loop over data without blocking other queries. However, be cautious with this method for extremely large datasets as it can still consume significant memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example: Using buffered queries&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$pdo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SELECT * FROM large_table&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$row&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PDO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;FETCH_ASSOC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Process each row&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;memory-management-techniques&quot;&gt;Memory management techniques&lt;/h3&gt;

&lt;p&gt;PHP’s memory can become a bottleneck when handling large data. Here’s how to manage it efficiently:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Increase PHP memory limit&lt;/strong&gt;: Temporarily increase &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;memory_limit&lt;/code&gt; in your script.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Unset variables&lt;/strong&gt;: Free up memory by unsetting variables after use.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Garbage collection&lt;/strong&gt;: Manually trigger garbage collection to reclaim memory.
    &lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;unset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$data_chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;gc_collect_cycles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;leveraging-external-tools-and-databases&quot;&gt;Leveraging external tools and databases&lt;/h3&gt;
&lt;p&gt;For highly scalable applications, integrating PHP with robust databases and tools is essential.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Use databases optimized for big data&lt;/strong&gt;: Consider PostgreSQL or NoSQL databases like MongoDB when traditional RDBMS can’t handle your data efficiently.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Database sharding&lt;/strong&gt;: Split large databases into smaller, manageable chunks across multiple servers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;comparative-table-php-data-management-strategies&quot;&gt;Comparative table: PHP data management strategies&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Method&lt;/th&gt;
      &lt;th&gt;Use Case&lt;/th&gt;
      &lt;th&gt;Pros&lt;/th&gt;
      &lt;th&gt;Cons&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Pagination&lt;/td&gt;
      &lt;td&gt;Displaying user data, search results&lt;/td&gt;
      &lt;td&gt;Lightweight, user-friendly&lt;/td&gt;
      &lt;td&gt;Limited view at a time&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Batch processing&lt;/td&gt;
      &lt;td&gt;Bulk imports/exports&lt;/td&gt;
      &lt;td&gt;Less resource-intensive&lt;/td&gt;
      &lt;td&gt;Higher code complexity&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Buffered queries&lt;/td&gt;
      &lt;td&gt;Continuous data retrieval&lt;/td&gt;
      &lt;td&gt;Low memory consumption&lt;/td&gt;
      &lt;td&gt;Can block other processes&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Indexing &amp;amp; optimized SQL&lt;/td&gt;
      &lt;td&gt;Data retrieval, sorting&lt;/td&gt;
      &lt;td&gt;Faster query execution&lt;/td&gt;
      &lt;td&gt;Can slow down inserts/updates&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;NoSQL databases&lt;/td&gt;
      &lt;td&gt;Real-time analytics, distributed systems&lt;/td&gt;
      &lt;td&gt;High scalability, flexibility&lt;/td&gt;
      &lt;td&gt;Learning curve, consistency trade-offs&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h3&gt;
&lt;p&gt;Handling large datasets in PHP is about balancing memory, processing speed, and efficient code. Implementing best practices like pagination, batch processing, and query optimization can greatly enhance application performance. As datasets continue to grow, staying updated on new PHP tools and methodologies will help you maintain efficiency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Citations&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;General best practices discussed in “High Performance MySQL” by O’Reilly Media.&lt;/li&gt;
  &lt;li&gt;PHP official documentation: &lt;a href=&quot;https://www.php.net&quot;&gt;PHP.net&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Escaping the pseudo-innovation loop: why we need real progress, not just syntactic sugar</title>
   <link href="https://prahladyeri.github.io/blog/2024/11/escaping-the-pseudo-innovation-loop.html"/>
   <updated>2024-11-02T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/11/escaping-the-pseudo-innovation-loop</id>
   <content type="html">&lt;p&gt;In the rapidly evolving world of technology, we often celebrate every new tool, library, or framework as a breakthrough. Headlines scream about the “next big thing” in coding, and developers rush to adopt the latest trend, eager to stay ahead of the curve. But amidst this frenzied pace, an unsettling pattern has emerged: the cycle of pseudo-innovation. These are changes that add flash and novelty but contribute little more than superficial improvements—like syntactic sugar—to existing systems.&lt;/p&gt;

&lt;h2 id=&quot;what-is-pseudo-innovation&quot;&gt;What is pseudo-innovation?&lt;/h2&gt;

&lt;p&gt;Pseudo-innovation refers to the type of technological change that, while marketed as a leap forward, essentially reworks existing ideas without substantial benefits. Think of it as the difference between repainting a room and remodeling an entire house. The former looks fresh but ultimately doesn’t change the structural function, while the latter transforms how the space is used.&lt;/p&gt;

&lt;p&gt;Syntactic sugar, a term well-known in the developer community, encapsulates this idea perfectly. These are language features that make code easier to read or write but don’t fundamentally alter its behavior or capabilities. While these can be useful in moderation, when overused, they create a loop where progress appears to happen but, in reality, remains stagnant.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/escaping-the-pseudo-innovation-loop.webp&quot; alt=&quot;escaping-the-pseudo-innovation-loop&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;examples-of-pseudo-innovation&quot;&gt;Examples of pseudo-innovation&lt;/h3&gt;

&lt;p&gt;One of the clearest manifestations of pseudo-innovation can be seen in the world of JavaScript frameworks. Not a year goes by without developers hearing about a new must-try tool that promises to revolutionize web development. For instance, when AngularJS emerged, it introduced a fresh way to build dynamic web applications. Yet soon after, the community saw an avalanche of alternatives like React, Vue, Svelte, and others. Each promised better syntax, a more streamlined approach, or a novel rendering technique, but how much of this is truly innovative?&lt;/p&gt;

&lt;p&gt;Sure, React’s introduction of the virtual DOM was a significant technical shift, but the frameworks that followed largely focused on syntactical improvements or minor adjustments to developer experience rather than changing the capabilities of web applications themselves. The constant cycle of new libraries forces developers to spend more time learning the latest trends than creating substantive projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table: Examples of pseudo-innovation in web development&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Innovation Cycle&lt;/th&gt;
      &lt;th&gt;Examples of Syntactic Sugar&lt;/th&gt;
      &lt;th&gt;Resulting Effect&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;JavaScript frameworks&lt;/td&gt;
      &lt;td&gt;Hooks in React, Composition API in Vue&lt;/td&gt;
      &lt;td&gt;Makes code more readable, but doesn’t fundamentally change application capability&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CSS advancements&lt;/td&gt;
      &lt;td&gt;New syntaxes like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@container&lt;/code&gt; queries&lt;/td&gt;
      &lt;td&gt;Easier, but doesn’t revolutionize layout design&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Language updates&lt;/td&gt;
      &lt;td&gt;Null coalescing in JavaScript (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;??&lt;/code&gt; operator)&lt;/td&gt;
      &lt;td&gt;Improves clarity but is not game-changing&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;the-hidden-cost-of-pseudo-innovation&quot;&gt;The hidden cost of pseudo-innovation&lt;/h3&gt;

&lt;p&gt;The danger of pseudo-innovation is not just wasted time but a shift in focus. Developers become locked in a cycle of “learn, adapt, repeat,” prioritizing mastery over a new tool rather than addressing more meaningful challenges. When all efforts go toward following trends, less attention is given to tech that can solve real-world issues like climate change, educational inequality, or healthcare inefficiencies.&lt;/p&gt;

&lt;p&gt;The obsession with being on the bleeding edge often stifles deep, long-term thinking. Why invest in groundbreaking environmental technologies when it’s easier to iterate on yet another JavaScript library? Why look for ways to develop inclusive educational platforms when most developers are busy debating over whether TypeScript is better than JavaScript?&lt;/p&gt;

&lt;h2 id=&quot;what-real-innovation-looks-like&quot;&gt;What real innovation looks like&lt;/h2&gt;

&lt;p&gt;True innovation changes the rules of the game. It’s not just about improving aesthetics or making something slightly easier to write. Instead, it’s about addressing fundamental problems and creating value that goes beyond a better developer experience.&lt;/p&gt;

&lt;h3 id=&quot;examples-of-real-innovation-in-action&quot;&gt;Examples of real innovation in action&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Healthcare solutions&lt;/strong&gt;: Real innovation can be found in AI tools that analyze medical scans to detect diseases like cancer early. This doesn’t just help specialists be more efficient—it saves lives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Environmental tech&lt;/strong&gt;: Breakthroughs in renewable energy, like more efficient solar panels or advancements in energy storage, represent genuine progress that impacts our global carbon footprint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Education technology&lt;/strong&gt;: Platforms like Khan Academy and Coursera have democratized learning, making education more accessible worldwide. Tools that leverage AI to provide personalized learning experiences have moved the needle in how education is perceived and accessed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Comparison table: Real innovation vs. pseudo-innovation&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Criteria&lt;/th&gt;
      &lt;th&gt;Pseudo-innovation&lt;/th&gt;
      &lt;th&gt;Real innovation&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Scope&lt;/td&gt;
      &lt;td&gt;Cosmetic or syntactic changes&lt;/td&gt;
      &lt;td&gt;Fundamental shifts or new capabilities&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Impact&lt;/td&gt;
      &lt;td&gt;Limited to developer productivity&lt;/td&gt;
      &lt;td&gt;Broad societal and industry impact&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Sustainability&lt;/td&gt;
      &lt;td&gt;Often results in churn&lt;/td&gt;
      &lt;td&gt;Long-lasting, deeply rooted progress&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Example&lt;/td&gt;
      &lt;td&gt;A new framework with different syntax&lt;/td&gt;
      &lt;td&gt;AI-powered diagnostic tools in healthcare&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;avoiding-the-pseudo-innovation-loop&quot;&gt;Avoiding the pseudo-innovation loop&lt;/h2&gt;

&lt;p&gt;Breaking free from the cycle of pseudo-innovation requires a change in perspective:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Focus on value, not novelty&lt;/strong&gt;: The allure of shiny new tools is strong, but it’s crucial to ask, “What value does this add?” before investing time and resources.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Invest in long-term learning&lt;/strong&gt;: Instead of cycling through the latest framework, developers can benefit more from mastering core concepts that transcend individual technologies—data structures, algorithms, and architectural patterns.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Encourage value-driven projects&lt;/strong&gt;: As a tech community, we should celebrate innovations that have tangible benefits. Projects that improve efficiency, inclusivity, or sustainability deserve more spotlight than those that simply repackage old ideas with a modern twist.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In an industry obsessed with being “cutting edge,” it’s all too easy to confuse movement with progress. Syntactic sugar and pseudo-innovation may look appealing, but they often trap developers and companies in a loop where true impact is overlooked. By shifting our focus to meaningful, value-driven progress, we can ensure that our collective tech efforts go beyond incremental changes and contribute to solutions that matter. The future of technology shouldn’t just be easier to write—it should be transformative.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Optimizing PHP and MySQL for high-traffic websites</title>
   <link href="https://prahladyeri.github.io/blog/2024/11/optimizing-php-mysql-for-high-traffic-sites.html"/>
   <updated>2024-11-01T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/11/optimizing-php-mysql-for-high-traffic-sites</id>
   <content type="html">&lt;p&gt;Managing a high-traffic website effectively requires a robust and well-optimized backend to handle the surge of concurrent requests, minimize server load, and ensure a seamless user experience. PHP and MySQL, being a popular tech stack, offer various strategies and best practices to optimize performance. This guide explores key caching strategies, indexing techniques, query optimizations, and performance monitoring tools to help you scale PHP and MySQL efficiently.&lt;/p&gt;

&lt;h4 id=&quot;1-implementing-caching-strategies&quot;&gt;1. Implementing caching strategies&lt;/h4&gt;

&lt;p&gt;Caching is one of the most effective ways to improve the performance of a PHP-MySQL web application. By storing frequently accessed data in a faster, intermediary storage layer, you can reduce the load on your database and accelerate response times.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Types of caching to consider:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Opcode caching&lt;/strong&gt;: PHP can compile code to bytecode, which can be cached with tools like &lt;strong&gt;OPcache&lt;/strong&gt;. This reduces the need for PHP to recompile scripts on every request, resulting in faster execution.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Data caching&lt;/strong&gt;: Tools like &lt;strong&gt;Redis&lt;/strong&gt; and &lt;strong&gt;Memcached&lt;/strong&gt; store frequently accessed database query results. This approach is highly beneficial for read-heavy applications.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Page caching&lt;/strong&gt;: Cache entire HTML pages for static content using systems like &lt;strong&gt;Varnish&lt;/strong&gt; or built-in mechanisms in frameworks like &lt;strong&gt;Laravel&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Code example for Redis integration&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$redis&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$redis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;127.0.0.1&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6379&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$cacheKey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;user_data&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$redis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$cacheKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$userData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$redis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$cacheKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$userData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetchUserDataFromDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Assume this fetches data from MySQL&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$redis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$cacheKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$userData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Cache data for 1 hour&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/optimizing-php-mysql-for-high-traffic-sites.webp&quot; alt=&quot;optimizing-php-mysql-for-high-traffic-sites&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;2-using-efficient-indexing-techniques&quot;&gt;2. Using efficient indexing techniques&lt;/h4&gt;

&lt;p&gt;Indexes in MySQL can significantly enhance the speed of queries by allowing the database engine to find rows more quickly. However, improper or excessive indexing can lead to increased storage and maintenance overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best practices for indexing&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Use composite indexes&lt;/strong&gt; when filtering queries with multiple columns. Ensure that columns with higher selectivity come first.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Avoid over-indexing&lt;/strong&gt;, as maintaining too many indexes can slow down &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UPDATE&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE&lt;/code&gt; operations.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Analyze slow queries&lt;/strong&gt; using MySQL’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EXPLAIN&lt;/code&gt; command to determine how your queries interact with indexes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example of creating a composite index&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idx_user_activity&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_logs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;3-optimizing-sql-queries&quot;&gt;3. Optimizing SQL queries&lt;/h4&gt;

&lt;p&gt;Inefficient SQL queries can lead to significant performance bottlenecks, especially under high traffic. Here’s how you can optimize your SQL:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Select only necessary columns&lt;/strong&gt;: Avoid using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT *&lt;/code&gt; as it fetches unnecessary data.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LIMIT&lt;/code&gt; for pagination&lt;/strong&gt;: When handling large datasets, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LIMIT&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OFFSET&lt;/code&gt; to retrieve data in smaller chunks.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Avoid subqueries when possible&lt;/strong&gt;: Replace subqueries with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JOIN&lt;/code&gt; operations for better performance.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Normalize wisely&lt;/strong&gt;: While normalization helps reduce redundancy, excessive normalization can lead to complex queries. Balance normalization with denormalization where necessary for read-heavy operations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Optimized query example&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action_type&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_logs&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;101&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;2024-01-01&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;4-configuring-mysql-for-performance&quot;&gt;4. Configuring MySQL for performance&lt;/h4&gt;

&lt;p&gt;Tuning MySQL settings to match your server’s resources and usage patterns can improve overall performance:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Adjust the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;innodb_buffer_pool_size&lt;/code&gt;&lt;/strong&gt; to fit most of your database in memory. This is crucial for InnoDB storage engines.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Increase &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;query_cache_size&lt;/code&gt;&lt;/strong&gt; for scenarios with many identical queries, although caching engines like Redis are more effective.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;slow_query_log&lt;/code&gt;&lt;/strong&gt; to identify and optimize slow queries. This log helps pinpoint inefficient queries that take longer than a specified threshold.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Basic MySQL tuning snippet&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[mysqld]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;innodb_buffer_pool_size&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2G&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;query_cache_size&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;64M&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;slow_query_log&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;slow_query_log_file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/var/log/mysql-slow.log&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;long_query_time&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;5-using-php-profiling-and-monitoring-tools&quot;&gt;5. Using PHP profiling and monitoring tools&lt;/h4&gt;

&lt;p&gt;Profiling and monitoring can help you identify bottlenecks in your PHP scripts and database interactions. Use these tools to track performance and make data-driven optimizations:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Xdebug&lt;/strong&gt;: A PHP extension for in-depth profiling, showing function calls and execution time.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;New Relic&lt;/strong&gt;: Monitors server performance, application errors, and transaction traces in real-time.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Blackfire.io&lt;/strong&gt;: Provides performance insights and recommendations for PHP scripts, ideal for identifying performance issues in complex applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Snippet for Xdebug setup&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[xdebug]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;xdebug.mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;profile&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;xdebug.output_dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/var/log/xdebug&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;6-load-balancing-and-database-sharding&quot;&gt;6. Load balancing and database sharding&lt;/h4&gt;

&lt;p&gt;For very high-traffic sites, scaling vertically may not be sufficient. Instead, use load balancing and database sharding to distribute traffic and reduce load on individual servers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Load balancing&lt;/strong&gt;: Distribute traffic across multiple PHP servers using tools like &lt;strong&gt;NGINX&lt;/strong&gt; or &lt;strong&gt;HAProxy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Database sharding&lt;/strong&gt;: Partition your database into smaller, more manageable pieces based on logical data separation (e.g., sharding by user region).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example of basic load balancing with NGINX&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;upstream&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;php_servers&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;php1.example.com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;php2.example.com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://php_servers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;Optimizing PHP and MySQL for high-traffic websites involves a multifaceted approach, from implementing effective caching and indexing to refining SQL queries and tuning server configurations. With the right strategies and tools, your application can handle large-scale traffic efficiently, ensuring a seamless experience for your users.&lt;/p&gt;

&lt;p&gt;Here is the rewritten citation in a Wikipedia-style format:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;MySQL Documentation Team, Oracle Corporation. &lt;em&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/optimization.html&quot;&gt;MySQL Documentation: Optimization Guidelines&lt;/a&gt;&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;PHP.net contributors. &lt;em&gt;&lt;a href=&quot;https://www.php.net/manual/en/features.gc.performance-considerations.php&quot;&gt;PHP Performance Best Practices&lt;/a&gt;&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;Schwartz, Baron; Zaitsev, Peter; Tkachenko, Vadim. &lt;em&gt;High Performance MySQL, 3rd Edition&lt;/em&gt;. O’Reilly Media, 2012.&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>From "My Computer" to "This PC": the evolution of language in a brand-centric consumer culture</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/my-computer-to-this-pc-evolution-of-brand-centric-language.html"/>
   <updated>2024-10-31T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/my-computer-to-this-pc-evolution-of-brand-centric-language</id>
   <content type="html">&lt;p&gt;Language is a living, breathing phenomenon, evolving with society’s changing values, technological advancements, and cultural shifts. The transition from “My Computer” to “This PC” serves as a fascinating example of how linguistics have shifted toward branding and consumerism, rather than focusing on individual identity. This article dives deep into this evolution, exploring how language has transformed to reflect a more brand-centric and consumer-focused mindset, and the profound impact this shift has on our collective behavior and identity.&lt;/p&gt;

&lt;h2 id=&quot;the-personalization-of-language-a-historical-overview&quot;&gt;The personalization of language: a historical overview&lt;/h2&gt;

&lt;p&gt;Historically, language has been a powerful medium of expression, fostering connections among individuals. Words like “My Home” and “My Car” evoke personal significance, creating a sense of ownership and emotional attachment. These terms reinforce individuality and human connections, allowing people to express their experiences and identity.&lt;/p&gt;

&lt;h3 id=&quot;the-dawn-of-technology-and-consumer-culture&quot;&gt;The dawn of technology and consumer culture&lt;/h3&gt;

&lt;p&gt;However, with the rise of technology and a consumer-driven culture, a notable shift occurred. Terms that once embodied personal ownership began to reflect a standardized, commodified view of our interactions with products. For instance, the phrase “My Computer” once denoted a personal relationship with a device, but it gradually evolved into “This PC,” stripping away the personal touch in favor of a more generic term.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table 1: The Shift in Language Over Time&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Original Phrase&lt;/th&gt;
      &lt;th&gt;Evolved Phrase&lt;/th&gt;
      &lt;th&gt;Implication&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;My Computer&lt;/td&gt;
      &lt;td&gt;This PC&lt;/td&gt;
      &lt;td&gt;Shift from personal identity to product focus&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;My Phone&lt;/td&gt;
      &lt;td&gt;This Device&lt;/td&gt;
      &lt;td&gt;Move from personal connection to generic utility&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;My Profile&lt;/td&gt;
      &lt;td&gt;User Account&lt;/td&gt;
      &lt;td&gt;Commodification of personal online presence&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;My Music&lt;/td&gt;
      &lt;td&gt;Media Library&lt;/td&gt;
      &lt;td&gt;Transition from personal enjoyment to a curated collection&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;the-rise-of-brand-centric-language&quot;&gt;The rise of brand-centric language&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/my-computer-to-this-pc-evolution-of-brand-centric-language-2.png&quot; alt=&quot;my-computer-to-this-pc-evolution-of-brand-centric-language&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;language-as-branding-examining-this-pc&quot;&gt;Language as branding: examining “This PC”&lt;/h3&gt;

&lt;p&gt;The phrase “This PC” is more than just a change in terminology; it reflects a broader trend toward brand-centric language. The shift from “My” to “This” emphasizes the product rather than the individual user. This linguistic evolution aligns with the rise of branding as a powerful force that shapes consumer perception and behavior.&lt;/p&gt;

&lt;h4 id=&quot;implications-for-consumer-identity&quot;&gt;Implications for consumer identity&lt;/h4&gt;

&lt;p&gt;This transition highlights how language is tailored to appeal to a collective consumer identity rather than personal ownership. Companies like Microsoft promote this brand-centric terminology, fostering uniformity among users. Consequently, this shift diminishes individual differentiation while reinforcing brand loyalty.&lt;/p&gt;

&lt;h3 id=&quot;case-studies-in-brand-centric-language-evolution&quot;&gt;Case studies in brand-centric language evolution&lt;/h3&gt;

&lt;h4 id=&quot;1-my-phone-to-this-device&quot;&gt;1. “My Phone” to “This Device”&lt;/h4&gt;

&lt;p&gt;Similar to the transition in computer terminology, smartphones have also undergone a linguistic evolution. The phrase “My Phone” once evoked personal connection, but “This Device” implies a more generic, product-focused view. This change encourages users to see their phones as mere tools for productivity rather than extensions of their identity.&lt;/p&gt;

&lt;h4 id=&quot;2-my-profile-to-user-account&quot;&gt;2. “My Profile” to “User Account”&lt;/h4&gt;

&lt;p&gt;In the context of social media and online platforms, the terminology has shifted from “My Profile” to “User Account.” This evolution signifies a move toward a transactional view of online presence, highlighting the commodification of personal data. By prioritizing brand-centric language, platforms focus on users’ roles within a larger digital ecosystem rather than their individuality.&lt;/p&gt;

&lt;h4 id=&quot;3-my-music-to-media-library&quot;&gt;3. “My Music” to “Media Library”&lt;/h4&gt;

&lt;p&gt;The phrase “My Music” has transformed into “Media Library,” encapsulating the change from personal interests to a broader category of consumable media. This shift signifies the transition from personal enjoyment to a curated collection of commercially available content, reinforcing the brand’s role in defining user experiences.&lt;/p&gt;

&lt;h2 id=&quot;the-impact-on-collective-mindset-and-behavior&quot;&gt;The impact on collective mindset and behavior&lt;/h2&gt;

&lt;h3 id=&quot;shaping-consumer-identity&quot;&gt;Shaping consumer identity&lt;/h3&gt;

&lt;p&gt;Language plays a crucial role in shaping our collective identity as consumers. The prevalence of brand-centric terminology fosters a mindset focused on consumerism, where products and brands define individuals more than personal experiences or values. As consumers increasingly identify with brands rather than personal narratives, the language they use reflects this shift.&lt;/p&gt;

&lt;h4 id=&quot;identity-formation&quot;&gt;Identity formation&lt;/h4&gt;

&lt;p&gt;This linguistic evolution influences how individuals perceive themselves and their relationships with products. Phrases emphasizing brand identity can diminish personal agency, reinforcing a mindset that values material possessions over intrinsic human qualities.&lt;/p&gt;

&lt;h3 id=&quot;the-role-of-marketing-and-advertising&quot;&gt;The role of marketing and advertising&lt;/h3&gt;

&lt;p&gt;Marketing strategies wield significant influence over the language we use, reinforcing brand-centric narratives that guide individuals to adopt consumerist identities. Advertisements often employ language that promotes brand loyalty, subtly shifting our perceptions of self and identity.&lt;/p&gt;

&lt;h4 id=&quot;example-join-the-movement&quot;&gt;Example: “Join the movement”&lt;/h4&gt;

&lt;p&gt;This phrase, frequently used in marketing campaigns, positions consumers as part of a larger community, suggesting that purchasing a product equates to participating in a significant cultural shift. This linguistic framing encourages consumers to see themselves not as individuals but as part of a collective identity shaped by brand affiliation.&lt;/p&gt;

&lt;h2 id=&quot;the-effects-on-our-behavior-and-culture&quot;&gt;The effects on our behavior and culture&lt;/h2&gt;

&lt;h3 id=&quot;consumer-behavior-and-decision-making&quot;&gt;Consumer behavior and decision-making&lt;/h3&gt;

&lt;p&gt;The evolution of language into a brand-centric approach has profound implications for consumer behavior. Language shapes our thoughts and perceptions, influencing how we make purchasing decisions. The more we engage with brand-centric language, the more likely we are to prioritize brand loyalty over personal preferences.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Emotional triggers&lt;/strong&gt;: Marketers understand the power of language in eliciting emotional responses. Phrases like “limited edition,” “exclusive access,” or “unbeatable value” create urgency and exclusivity, motivating consumers to act quickly. This psychological manipulation showcases how language can drive consumer behavior, often at the expense of personal agency.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;cultural-implications&quot;&gt;Cultural implications&lt;/h3&gt;

&lt;p&gt;The linguistic shift toward brand-centric language also impacts culture on a broader scale. As language reflects societal values, the prevalence of brand-centric terminology reinforces consumerism as a dominant cultural narrative. This transformation can lead to a society that prioritizes material possessions and brand affiliations over personal connections and experiences.&lt;/p&gt;

&lt;h4 id=&quot;the-dangers-of-consumer-centric-language&quot;&gt;The dangers of consumer-centric language&lt;/h4&gt;

&lt;p&gt;As our language becomes increasingly focused on branding and consumerism, we risk losing sight of our individuality and shared human experiences. The more we engage with a language that promotes products over people, the more we distance ourselves from authentic connections.&lt;/p&gt;

&lt;h3 id=&quot;the-pushback-reclaiming-personal-language&quot;&gt;The pushback: reclaiming personal language&lt;/h3&gt;

&lt;p&gt;In a world dominated by brand-centric language, there is a growing need to reclaim personal language that emphasizes individuality and authentic connection. Individuals and communities can challenge the prevailing narratives by prioritizing language that reflects personal values and experiences.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Advocacy for personal expression&lt;/strong&gt;: Initiatives promoting self-expression and individuality can encourage people to reclaim their identities. Encouraging language that resonates with personal narratives can foster a sense of belonging and connection in an increasingly commercialized world.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;conclusion-the-future-of-language-and-identity&quot;&gt;Conclusion: the future of language and identity&lt;/h2&gt;

&lt;p&gt;The evolution of language from personal ownership to brand-centric terminology illustrates a significant shift in how we perceive ourselves and our relationships with the world. As language becomes more consumerism-centric, it is crucial to reflect on its impact on collective mindset, behavior, and identity.&lt;/p&gt;

&lt;p&gt;Reclaiming personal language can empower individuals to reconnect with their identities beyond brands. By fostering a more people-centric approach to language, we can create a cultural environment that values individual experiences and narratives over commodified identities. The evolution of language serves as a powerful reminder of the importance of communication in shaping our perceptions of self, society, and the brands we engage with.&lt;/p&gt;

&lt;p&gt;In this age of consumerism, it is vital to recognize the role of linguistics in shaping our collective consciousness and strive for a linguistic landscape that celebrates individuality alongside the brands that shape our world.&lt;/p&gt;

&lt;h3 id=&quot;references&quot;&gt;References&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;Crystal, D. (2008). A Dictionary of Linguistics and Phonetics. Wiley-Blackwell.&lt;/li&gt;
  &lt;li&gt;McLuhan, M. (1964). Understanding Media: The Extensions of Man. McGraw-Hill.&lt;/li&gt;
  &lt;li&gt;Postman, N. (1993). Technopoly: The Surrender of Culture to Technology. Knopf.&lt;/li&gt;
&lt;/ol&gt;
</content>
 </entry>
 
 <entry>
   <title>Creating a dynamic airline ticket booking system with PHP and MySQL</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/airline-ticket-booking-system-php-mysql.html"/>
   <updated>2024-10-31T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/airline-ticket-booking-system-php-mysql</id>
   <content type="html">&lt;p&gt;In this in-depth guide, we’ll cover how to create a dynamic airline ticket booking system using PHP and MySQL. This system will allow users to search flights, view availability, book tickets, and manage bookings. We’ll also highlight best practices for building efficient reservation systems, including user authentication and secure data handling.&lt;/p&gt;

&lt;h3 id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h3&gt;
&lt;ol&gt;
  &lt;li&gt;Introduction to the airline booking system&lt;/li&gt;
  &lt;li&gt;Project setup and prerequisites&lt;/li&gt;
  &lt;li&gt;Database structure and design&lt;/li&gt;
  &lt;li&gt;User authentication module&lt;/li&gt;
  &lt;li&gt;Flight search and availability check&lt;/li&gt;
  &lt;li&gt;Ticket booking and reservation management&lt;/li&gt;
  &lt;li&gt;Handling payments (optional integration)&lt;/li&gt;
  &lt;li&gt;Additional resources and open-source projects&lt;/li&gt;
  &lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/airline-ticket-booking-system-php-mysql.webp&quot; alt=&quot;airline-ticket-booking-system-php-mysql&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;1-introduction-to-the-airline-booking-system&quot;&gt;1. Introduction to the airline booking system&lt;/h3&gt;

&lt;p&gt;An airline ticket booking system is a web application that enables users to check flight availability, book seats, and view their bookings. This type of system can be useful for travel agencies, airlines, and developers seeking to improve their skills with a real-world PHP project.&lt;/p&gt;

&lt;p&gt;The primary components of the system will include:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;User registration and login&lt;/li&gt;
  &lt;li&gt;Flight database and search functionality&lt;/li&gt;
  &lt;li&gt;Booking management and ticket reservation&lt;/li&gt;
  &lt;li&gt;Optional payment gateway integration for a complete e-commerce experience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide assumes a basic understanding of PHP, MySQL, and HTML.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;2-project-setup-and-prerequisites&quot;&gt;2. Project setup and prerequisites&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Environment setup:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;PHP&lt;/strong&gt;: Version 7.4 or higher.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;MySQL&lt;/strong&gt;: For storing user and flight data.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Apache or Nginx server&lt;/strong&gt;: To serve the application locally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ensure that PHP, MySQL, and your preferred server are installed and configured correctly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project Structure:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;airline-booking-system/
│
├── index.php                 # Home page for searching flights
├── register.php              # User registration
├── login.php                 # User login
├── flights.php               # Search flights page
├── book.php                  # Booking and reservation page
├── manage_booking.php        # Manage bookings for users
├── assets/                   # Folder for CSS, JavaScript
│   └── styles.css            # Basic styling for the application
└── db/
    └── database.sql          # SQL file to create necessary tables
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;3-database-structure-and-design&quot;&gt;3. Database structure and design&lt;/h3&gt;

&lt;p&gt;Designing an effective database schema is crucial for managing flights, reservations, and user data. Here is a simple schema that includes three main tables: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;users&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flights&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bookings&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;-- Table to store user data&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;users&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AUTO_INCREMENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;UNIQUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;created_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;TIMESTAMP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CURRENT_TIMESTAMP&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;-- Table to store flight information&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flights&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AUTO_INCREMENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;flight_number&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;UNIQUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;origin&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;departure_time&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;DATETIME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;arrival_time&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;DATETIME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;seats_available&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;DECIMAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;-- Table to store booking information&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bookings&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AUTO_INCREMENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;flight_id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;booking_date&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;TIMESTAMP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CURRENT_TIMESTAMP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;ENUM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;confirmed&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;pending&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;cancelled&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;REFERENCES&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flight_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;REFERENCES&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flights&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;users&lt;/code&gt;&lt;/strong&gt;: Stores user data, including password hashes for secure authentication.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flights&lt;/code&gt;&lt;/strong&gt;: Stores information about flights, availability, and pricing.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bookings&lt;/code&gt;&lt;/strong&gt;: Links users to flights, allowing them to book and manage reservations.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;4-user-authentication-module&quot;&gt;4. User authentication module&lt;/h3&gt;

&lt;p&gt;To manage user registration and login, we’ll implement basic authentication. Start by creating a registration form (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;register.php&lt;/code&gt;) that collects user information and stores hashed passwords.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// register.php - User registration&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;REQUEST_METHOD&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;POST&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;db/connection.php&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;email&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$password&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;password_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;password&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PASSWORD_BCRYPT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;INSERT INTO users (name, email, password) VALUES (?, ?, ?)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind_param&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;sss&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For login (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;login.php&lt;/code&gt;), verify the email and password against stored values, starting a session if credentials are valid.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// login.php - User login&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;session_start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;REQUEST_METHOD&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;POST&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;db/connection.php&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;email&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$password&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;password&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;SELECT * FROM users WHERE email = ?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind_param&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch_assoc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;password_verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;password&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$_SESSION&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;user_id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Location: index.php&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Invalid email or password.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;5-flight-search-and-availability-check&quot;&gt;5. Flight search and availability check&lt;/h3&gt;

&lt;p&gt;On the main page (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.php&lt;/code&gt;), allow users to search for flights by origin, destination, and date. Use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flights&lt;/code&gt; table to retrieve results matching the user’s criteria.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// index.php - Search for flights&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;REQUEST_METHOD&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;POST&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;db/connection.php&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$origin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;origin&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$destination&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;destination&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;date&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;SELECT * FROM flights WHERE origin = ? AND destination = ? AND DATE(departure_time) = ?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind_param&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;sss&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$origin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$flights&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flights.php&lt;/code&gt; (Search Flights Page) file is intended to allow users to search for flights by specifying criteria like origin, destination, and date, and then display available flights based on those inputs. Here’s how it could be implemented:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- flights.php --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Search Flights&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Search for Flights&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;POST&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;flights.php&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;label&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;for=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;origin&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Origin:&lt;span class=&quot;nt&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;origin&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;label&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;for=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;destination&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Destination:&lt;span class=&quot;nt&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;destination&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;label&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;for=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Date:&lt;span class=&quot;nt&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;date&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;date&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Search&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;REQUEST_METHOD&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;POST&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;db/connection.php&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$origin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;origin&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$destination&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;destination&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;date&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Query to search flights&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;SELECT * FROM flights WHERE origin = ? AND destination = ? AND DATE(departure_time) = ?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind_param&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;sss&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$origin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;h2&amp;gt;Available Flights&amp;lt;/h2&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_rows&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;table&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Flight Number&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Departure&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Arrival&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Seats&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Price&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$flight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch_assoc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;tr&amp;gt;
                    &amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$flight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;flight_number&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;lt;/td&amp;gt;
                    &amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$flight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;departure_time&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;lt;/td&amp;gt;
                    &amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$flight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;arrival_time&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;lt;/td&amp;gt;
                    &amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$flight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;seats_available&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;lt;/td&amp;gt;
                    &amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$flight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;price&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;lt;/td&amp;gt;
                    &amp;lt;td&amp;gt;&amp;lt;a href=&apos;book.php?flight_id=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$flight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&apos;&amp;gt;Book Now&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
                &amp;lt;/tr&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;/table&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;No flights found.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;6-ticket-booking-and-reservation-management&quot;&gt;6. Ticket booking and reservation management&lt;/h3&gt;

&lt;p&gt;On the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;book.php&lt;/code&gt; page, allow users to book a flight, reducing the number of available seats. Update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bookings&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flights&lt;/code&gt; tables to reflect the reservation.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// book.php - Book a flight&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;REQUEST_METHOD&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;POST&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;db/connection.php&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$flight_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;flight_id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$user_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_SESSION&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;user_id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Check seat availability&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;SELECT seats_available FROM flights WHERE id = ?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind_param&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;i&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$flight_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$flight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch_assoc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$flight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;seats_available&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Update seats and create booking&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$update_seats&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;UPDATE flights SET seats_available = seats_available - 1 WHERE id = ?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$update_seats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind_param&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;i&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$flight_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;nv&quot;&gt;$create_booking&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;INSERT INTO bookings (user_id, flight_id, status) VALUES (?, ?, &apos;confirmed&apos;)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$create_booking&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind_param&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ii&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$flight_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Booking confirmed!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;No seats available.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;manage_booking.php&lt;/code&gt; file allows users to view and manage their existing bookings, such as viewing current reservations, canceling them, or checking status. Here’s a basic implementation for managing bookings:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- manage_booking.php --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;session_start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_SESSION&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;user_id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Location: login.php&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;db/connection.php&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$user_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_SESSION&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;user_id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Fetch bookings for the logged-in user&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;SELECT bookings.id, flights.flight_number, flights.origin, flights.destination, flights.departure_time, bookings.status
          FROM bookings
          JOIN flights ON bookings.flight_id = flights.id
          WHERE bookings.user_id = ?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind_param&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;i&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;

&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Manage Bookings&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Your Bookings&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_rows&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;table&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Flight Number&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Origin&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Destination&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Departure&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Status&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Actions&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$booking&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch_assoc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$booking&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;flight_number&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$booking&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;origin&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$booking&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;destination&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$booking&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;departure_time&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;ucfirst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$booking&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;status&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$booking&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;status&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;confirmed&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;POST&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cancel_booking.php&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;hidden&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;booking_id&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$booking&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Cancel&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endif&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endwhile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;No bookings found.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endif&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Similarly, the &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cancel_booking.php&lt;/code&gt;&lt;/strong&gt; could handle booking cancellation logic, updating the booking status in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bookings&lt;/code&gt; table when a user requests to cancel. The implementation of this file is left to the user as a home-work exercise.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;7-handling-payments-optional-integration&quot;&gt;7. Handling payments (optional integration)&lt;/h3&gt;

&lt;p&gt;For a full-featured booking system, consider adding a payment module. Popular options include PayPal, Stripe, and Razorpay. Many offer PHP SDKs for easy integration. Implementing payment handling will require a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;payments&lt;/code&gt; table and modifications to the booking process.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;8-additional-resources-and-open-source-projects&quot;&gt;8. Additional resources and open-source projects&lt;/h3&gt;

&lt;p&gt;For further learning and to expand your project, consider checking out these open-source repositories and resources:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/username/repo&quot;&gt;PHP-MySQL Flight Booking System on GitHub&lt;/a&gt; &lt;em&gt;(Add actual relevant repositories here)&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Official PHP documentation on MySQLi and PDO&lt;/li&gt;
  &lt;li&gt;Tutorials on handling authentication and authorization in PHP&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;9-conclusion&quot;&gt;9. Conclusion&lt;/h3&gt;

&lt;p&gt;Creating a dynamic airline booking system in PHP and MySQL is a rewarding project that offers insights into both backend development and relational database management. By following this guide, you should now have a working ticket reservation system, from database design to user authentication and booking management.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The myth of one-size-fits-all: why standard ERP systems often fail</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/myth-of-one-size-fits-all-erp.html"/>
   <updated>2024-10-30T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/myth-of-one-size-fits-all-erp</id>
   <content type="html">&lt;p&gt;Enterprise Resource Planning (ERP) systems are the backbone of countless organizations, promising seamless integration of business processes, improved efficiency, and centralized data management. Yet, the glitter of ERP solutions sometimes conceals a harsh reality: the promise of “one-size-fits-all” is often a myth. While ERP systems claim to be universal solutions, the truth is they frequently struggle to fit the unique needs of specific industries or individual businesses. Let’s explore why these supposedly standardized ERP systems often fail and the costly customizations many companies must endure to make them work.&lt;/p&gt;

&lt;h3 id=&quot;the-allure-of-the-standardized-erp&quot;&gt;The allure of the standardized ERP&lt;/h3&gt;

&lt;p&gt;For many organizations, the idea of a standardized ERP system is tempting. It offers a tidy package of integrated modules to manage everything from accounting and inventory to sales and human resources. Theoretically, it’s supposed to eliminate data silos, automate tasks, and enhance decision-making by providing real-time insights. Companies are attracted to the idea of a universal, off-the-shelf solution that reduces complexity and is easy to implement.&lt;/p&gt;

&lt;p&gt;However, this narrative overlooks a crucial aspect of business: no two organizations are alike. Even companies within the same industry can vary significantly in their operations, workflows, and requirements. And here lies the primary flaw of the standardized ERP approach.&lt;/p&gt;

&lt;h3 id=&quot;why-a-generic-erp-often-falls-short&quot;&gt;Why a generic ERP often falls short&lt;/h3&gt;

&lt;p&gt;When businesses buy a standardized ERP solution, they are essentially purchasing a mold—a pre-designed framework that presumes to know what a “typical” company needs. However, reality paints a different picture. Each organization has its own legacy processes, market demands, customer expectations, and unique value propositions. These are just a few of the reasons why a generic, one-size-fits-all ERP often falls short:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Industry-specific needs&lt;/strong&gt;: Every industry has distinct regulations, standards, and processes. A retail chain’s ERP must prioritize inventory management and customer relationships, while a manufacturing company might need a heavy focus on supply chain management and production tracking. Standardized ERPs struggle to account for these differences, leading to a mismatch between what the software offers and what the company needs.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Unique workflows and processes&lt;/strong&gt;: Businesses often build competitive advantages around specific workflows. For example, a company might have a unique sales process that distinguishes it from competitors. Standard ERP systems are designed for generic workflows and may not align with these unique practices, leading to inefficient workarounds or forced changes in business processes.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Cultural and geographical differences&lt;/strong&gt;: Organizations operating globally face diverse requirements regarding taxation, language, legal compliance, and cultural preferences. A standardized ERP may overlook these regional nuances, leading to operational and compliance issues in different branches.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Scalability and flexibility&lt;/strong&gt;: Businesses evolve over time, and their ERP systems need to scale and adapt to changing demands. Standard ERP systems often have rigid structures that limit scalability or require extensive reconfigurations, making them less flexible for organizations in growth phases or those pivoting in their strategies.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/myth-of-one-size-fits-all-erp.webp&quot; alt=&quot;myth-of-one-size-fits-all-erp&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;the-high-cost-of-customization&quot;&gt;The high cost of customization&lt;/h3&gt;

&lt;p&gt;When a company realizes its standardized ERP isn’t meeting its needs, the next step is customization. While customizations are a necessary evil to achieve a better fit, they often come with hidden costs and complexities.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Time and budget overruns&lt;/strong&gt;: Customizing an ERP system can turn into a long and expensive process. Modifying the core functions, tweaking workflows, and integrating new modules all require additional time and budget, which can escalate beyond initial expectations. According to research by Panorama Consulting, more than half of ERP implementations end up over budget due to unforeseen costs—often due to extensive customizations.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Increased maintenance complexity&lt;/strong&gt;: Customizations can complicate the software’s architecture. Future updates, patches, or integrations with third-party systems can become cumbersome, requiring businesses to continually rework custom code or manually test new features. The complexity only grows as the ERP becomes more deeply embedded in the company’s ecosystem.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Loss of vendor support&lt;/strong&gt;: Many ERP vendors hesitate to fully support highly customized systems. Custom code might not be compatible with standard updates or improvements, leaving businesses in a vulnerable position when bugs or system errors arise. This lack of support can lead to more downtime, decreased productivity, and higher costs for ongoing maintenance.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Compromised user experience&lt;/strong&gt;: Customizations add complexity not just in maintenance but also in usage. Employees accustomed to a certain workflow or process may find it challenging to adapt to new, custom-designed ERP modules that don’t fit seamlessly into their established routines.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;real-world-examples-where-standardized-erps-failed&quot;&gt;Real-world examples: Where standardized ERPs failed&lt;/h3&gt;

&lt;p&gt;There are numerous high-profile examples of standardized ERP implementations gone awry. A well-known instance is that of &lt;strong&gt;Hershey’s ERP failure in 1999&lt;/strong&gt;. The candy giant attempted to implement an ERP system to streamline its supply chain and order processes. However, due to customization issues, it ended up with massive problems in its distribution, leading to a loss of $100 million in sales during the Halloween season—its most crucial sales period.&lt;/p&gt;

&lt;p&gt;Another example is &lt;strong&gt;Lidl’s SAP disaster&lt;/strong&gt;, where the German retailer invested seven years and €500 million in a new SAP-based ERP system, only to abandon the project because the system couldn’t handle Lidl’s custom pricing structure. Despite SAP being a powerful system, it was too rigid to accommodate Lidl’s unique pricing and inventory practices.&lt;/p&gt;

&lt;h3 id=&quot;the-fallacy-of-one-size-fits-all-why-industry-specific-erps-may-be-the-solution&quot;&gt;The fallacy of one-size-fits-all: Why industry-specific ERPs may be the solution&lt;/h3&gt;

&lt;p&gt;Many ERP vendors are beginning to recognize that their standardized solutions are insufficient for companies in highly specialized industries. As a result, industry-specific ERP systems are gaining traction. These systems offer pre-built modules tailored to the requirements of certain verticals, such as healthcare, retail, construction, or manufacturing.&lt;/p&gt;

&lt;p&gt;For example, &lt;strong&gt;Epicor&lt;/strong&gt; offers ERP solutions with modules specifically designed for manufacturing companies, addressing their needs in areas like supply chain management, job scheduling, and production quality control. Meanwhile, &lt;strong&gt;Cerner&lt;/strong&gt; focuses on ERP systems for the healthcare industry, with features that prioritize patient records, billing, and compliance with healthcare regulations.&lt;/p&gt;

&lt;h3 id=&quot;making-the-right-erp-choice&quot;&gt;Making the right ERP choice&lt;/h3&gt;

&lt;p&gt;Before diving headfirst into ERP implementation, it’s crucial for businesses to conduct a thorough needs assessment and evaluate their unique processes. Here are some recommendations:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Understand your key requirements&lt;/strong&gt;: Identify which business processes are critical and how the ERP should support them. List the specific features that your organization cannot function without.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Prioritize industry-specific solutions&lt;/strong&gt;: Explore ERP systems tailored to your industry. Such solutions typically come with built-in features for specialized processes and compliance requirements.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Plan for flexibility&lt;/strong&gt;: Choose an ERP that allows you to configure without significant code changes. This ensures smoother future updates and reduces ongoing costs.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Involve stakeholders from the start&lt;/strong&gt;: Ensure that key employees, particularly those who will use the ERP daily, are part of the decision-making process. Their insights into existing workflows can help avoid implementation headaches down the road.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;The allure of a one-size-fits-all ERP is strong, but it’s crucial to remember that businesses are as diverse as the industries they serve. The promise of a universal ERP solution often ignores the complexities of real-world operations, leading to customization headaches, blown budgets, and user dissatisfaction. By carefully assessing your needs, prioritizing industry-specific solutions, and staying wary of rigid systems, you can avoid the common pitfalls of generic ERP systems and ensure your organization’s success.&lt;/p&gt;

&lt;p&gt;When it comes to ERP solutions, remember: if the shoe doesn’t fit, it’s not your foot that needs changing—it’s the shoe. The myth of one-size-fits-all is just that—a myth. So, find the ERP that fits your organization, not the other way around.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;References&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Panorama Consulting Solutions, “2023 ERP Report: Key Findings on ERP Implementations.”&lt;/li&gt;
  &lt;li&gt;ERP Focus, “ERP Failures: Real Life Stories.”&lt;/li&gt;
  &lt;li&gt;Gartner, “Critical Capabilities for ERP in 2024.”&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>The illusion of simplicity: how OOP can overcomplicate simple problems</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/the-illusion-of-simplicity.html"/>
   <updated>2024-10-29T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/the-illusion-of-simplicity</id>
   <content type="html">&lt;p&gt;Object-oriented programming (OOP) has been hailed as the savior of the software world, promising more manageable codebases and scalable applications. From encapsulation to inheritance, the paradigm offers a toolkit that is designed to make developers’ lives easier. Yet, over the years, OOP has gained its fair share of critics who argue that the principles it upholds can often lead to overcomplication—especially when applied to simple problems. So, is OOP truly the answer to every software challenge, or is it an illusion of simplicity, cloaking unnecessary complexity beneath a facade of best practices?&lt;/p&gt;

&lt;p&gt;In this deep dive, we’ll explore why the abstraction and encapsulation principles of OOP might be overkill for simple problems and when sticking to basic solutions or alternative paradigms can lead to more effective and maintainable code.&lt;/p&gt;

&lt;h2 id=&quot;the-promise-of-object-oriented-programming-simplicity-through-abstraction&quot;&gt;The promise of object-oriented programming: simplicity through abstraction&lt;/h2&gt;

&lt;p&gt;The core philosophy of OOP is to model software as a collection of objects that interact with each other. It’s akin to representing the real world in code. Principles like inheritance, polymorphism, and encapsulation provide a way to make code modular, reusable, and maintainable. In theory, this sounds like an absolute win.&lt;/p&gt;

&lt;p&gt;Consider an example of a basic &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Car&lt;/code&gt; class:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Car&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;speed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Car&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;speed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;color&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;speed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;speed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;drive&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The car is driving at &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;speed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; km/h.&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;repaint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newColor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;color&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newColor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On the surface, it looks quite simple and intuitive. If you want to create a car, you instantiate an object with a color and speed, and you can make it drive or repaint it with a method. It’s elegant—until complexity starts creeping in.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/the-illusion-of-simplicity.webp&quot; alt=&quot;the-illusion-of-simplicity&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;when-simple-tasks-become-complex&quot;&gt;When simple tasks become complex&lt;/h2&gt;

&lt;p&gt;The illusion of simplicity in OOP begins to unravel when basic requirements evolve. Let’s say we need to support a new vehicle type: a truck. Following OOP principles, you may introduce a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Truck&lt;/code&gt; class that inherits from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Car&lt;/code&gt;, or you might abstract both into a common superclass called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vehicle&lt;/code&gt;. It looks like a small addition, but such minor changes snowball over time.&lt;/p&gt;

&lt;h3 id=&quot;layers-of-abstraction-how-complexity-multiplies&quot;&gt;Layers of abstraction: How complexity multiplies&lt;/h3&gt;

&lt;p&gt;In larger OOP-based systems, creating classes for every concept or behavior quickly results in a deep inheritance hierarchy. A simple problem, like managing vehicles, might require dozens of classes and interfaces due to enforced abstraction.&lt;/p&gt;

&lt;p&gt;For instance, to accommodate more types like electric cars and motorcycles, you might structure a hierarchy like this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Vehicle&lt;/strong&gt; (superclass)
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Car&lt;/strong&gt; (inherits from Vehicle)
        &lt;ul&gt;
          &lt;li&gt;&lt;strong&gt;ElectricCar&lt;/strong&gt; (inherits from Car)&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Truck&lt;/strong&gt; (inherits from Vehicle)&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Motorcycle&lt;/strong&gt; (inherits from Vehicle)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you now need to add a behavior like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;calculateMaintenanceCost()&lt;/code&gt;, you could end up writing it in multiple places or creating an additional abstraction to handle it.&lt;/p&gt;

&lt;h3 id=&quot;encapsulation-and-its-trade-offs&quot;&gt;Encapsulation and its trade-offs&lt;/h3&gt;

&lt;p&gt;Encapsulation is another tenet of OOP that often contributes to this complexity. While encapsulation helps in hiding internal states and only exposing necessary details, enforcing this for simple problems can create a tangled mess of getters, setters, and boilerplate methods.&lt;/p&gt;

&lt;p&gt;For instance, if the internal state of an object is simple, having strict getters and setters can lead to code that is difficult to refactor. It also creates an illusion that these methods are somehow necessary for safety or maintainability when the underlying data could be as simple as a couple of fields.&lt;/p&gt;

&lt;p&gt;This proliferation of unnecessary boilerplate has even led to entire frameworks (like JavaBeans or Lombok in Java) existing solely to reduce the drudgery of writing these repetitive pieces of code. And at that point, you need to ask: Are we truly simplifying things?&lt;/p&gt;

&lt;h2 id=&quot;why-oop-may-not-be-the-best-fit-for-simple-problems&quot;&gt;Why OOP may not be the best fit for simple problems&lt;/h2&gt;

&lt;h3 id=&quot;misalignment-with-the-problem-domain&quot;&gt;Misalignment with the problem domain&lt;/h3&gt;

&lt;p&gt;In many cases, using an OOP approach to solve a simple problem can feel like over-engineering. If all you need is a script that calculates the sum of integers from 1 to 1000, writing a class named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IntegerSummationService&lt;/code&gt; with multiple methods for “abstraction” is absurdly unnecessary.&lt;/p&gt;

&lt;h3 id=&quot;functional-paradigms-less-fluff-more-focus&quot;&gt;Functional paradigms: Less fluff, more focus&lt;/h3&gt;

&lt;p&gt;Functional programming (FP) has gained traction as an alternative to OOP precisely because of its emphasis on simplicity and immutability. FP offers the flexibility of defining isolated functions, leading to more straightforward code for many straightforward problems.&lt;/p&gt;

&lt;p&gt;For instance, consider how a vehicle maintenance cost might be handled in a functional style:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;calculate_maintenance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vehicle_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vehicle_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;car&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vehicle_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;truck&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;No need for a sprawling class hierarchy; the logic is contained within a small, easy-to-understand function.&lt;/p&gt;

&lt;h3 id=&quot;procedural-simplicity-for-simple-needs&quot;&gt;Procedural simplicity for simple needs&lt;/h3&gt;

&lt;p&gt;Sometimes, basic procedural programming—writing simple scripts and functions—can be more than adequate. For tasks like file reading, processing CSVs, or generating reports, there’s often no tangible benefit to wrapping the logic in classes or objects. The code remains more readable and easier to maintain without the OOP overhead.&lt;/p&gt;

&lt;h2 id=&quot;the-pitfalls-of-enforced-best-practices&quot;&gt;The pitfalls of enforced best practices&lt;/h2&gt;

&lt;p&gt;The notion that OOP is the &lt;em&gt;only&lt;/em&gt; way to organize code is a fallacy that often leads developers to overthink basic solutions. A classic example of this can be seen in frameworks that enforce design patterns, such as Model-View-Controller (MVC). While MVC is great for complex applications, forcing it on a simple CRUD application can lead to unnecessary classes, interfaces, and abstract factories.&lt;/p&gt;

&lt;p&gt;It’s like forcing every home cook to set up a professional kitchen just to make a bowl of cereal.&lt;/p&gt;

&lt;h3 id=&quot;an-anecdote-the-battle-with-classes-and-interfaces&quot;&gt;An anecdote: The battle with classes and interfaces&lt;/h3&gt;

&lt;p&gt;Here’s a story that many developers can relate to. I once worked on a project where a senior developer insisted that every single domain object needed an accompanying factory class. For example, to instantiate an order, we had to go through an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderFactory&lt;/code&gt;. This was justified as a best practice, and when I inquired about what it achieved, the answer was always the same: &lt;em&gt;“It’s good design.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What it ended up achieving was hundreds of lines of boilerplate code and confused junior developers who spent more time understanding factories than solving actual business problems.&lt;/p&gt;

&lt;h2 id=&quot;when-oop-works-and-when-it-doesnt&quot;&gt;When OOP works, and when it doesn’t&lt;/h2&gt;

&lt;p&gt;Of course, there are situations where OOP shines. For large-scale systems with numerous entities and relationships—such as banking systems, e-commerce platforms, or content management systems—OOP can help manage the complexity effectively. The benefits of encapsulation, polymorphism, and inheritance pay off in these scenarios.&lt;/p&gt;

&lt;p&gt;However, using the same hammer for every nail isn’t always the right strategy. For smaller projects or simple tasks, other paradigms like functional programming, scripting, or procedural code can be more straightforward, easier to maintain, and require less boilerplate.&lt;/p&gt;

&lt;h2 id=&quot;the-key-takeaway-balance-over-zealotry&quot;&gt;The key takeaway: balance over zealotry&lt;/h2&gt;

&lt;p&gt;The takeaway here is not to discard OOP altogether. It’s a valuable tool, but one that needs to be wielded judiciously. Treating it as a panacea can lead to an illusion of simplicity that hides layers of unnecessary complexity. As developers, we must remain adaptable, choosing the right paradigm based on the problem at hand rather than blindly adhering to OOP principles.&lt;/p&gt;

&lt;p&gt;The next time you’re about to design a new system or refactor an existing one, ask yourself: &lt;em&gt;Is OOP truly the simplest solution?&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Purpose-driven technology: infusing utilitarianism back into innovation</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/purpose-driven-technology.html"/>
   <updated>2024-10-28T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/purpose-driven-technology</id>
   <content type="html">&lt;p&gt;In an age dominated by rapid technological advancements, it’s easy to forget the original purpose behind the creation of technology: to enhance human potential, address pressing societal challenges, and drive progress. As we forge ahead, however, there’s a growing concern that innovation is increasingly motivated by novelty rather than utility. This article explores the notion of purpose-driven technology and the importance of reinstating utilitarian principles in our approach to innovation.&lt;/p&gt;

&lt;h2 id=&quot;the-roots-of-technology-utilitarianism-in-action&quot;&gt;The roots of technology: utilitarianism in action&lt;/h2&gt;

&lt;p&gt;From the wheel to the internet, technology has historically been rooted in utilitarianism—the philosophy that emphasizes actions that produce the greatest benefit for the most people. Early inventors and engineers approached problems with a mindset focused on solving specific issues: agricultural tools improved food production, transportation systems connected communities, and medical advancements enhanced healthcare. These innovations were not mere artifacts of creativity; they were designed to meet real-world needs, improving lives and advancing society.&lt;/p&gt;

&lt;p&gt;However, as technology evolved, the landscape shifted. The advent of the digital age ushered in a new era of innovation characterized by rapid development cycles, the proliferation of frameworks, libraries, and APIs, and an unrelenting push to create the next big thing. In this environment, it’s increasingly common to see technologies developed not for their utility, but simply for the sake of innovation. This shift raises crucial questions: Have we lost sight of the utilitarian goals that once guided technological progress? Are we prioritizing the creation of shiny new tools over their actual application in solving real-world problems?&lt;/p&gt;

&lt;h2 id=&quot;the-drift-towards-trivial-innovation&quot;&gt;The drift towards trivial innovation&lt;/h2&gt;

&lt;p&gt;In recent years, numerous examples highlight how technology has drifted away from its utilitarian roots.&lt;/p&gt;

&lt;h3 id=&quot;1-framework-fatigue&quot;&gt;1. &lt;strong&gt;Framework fatigue&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Consider the world of web development, where new frameworks emerge at breakneck speed. Developers often find themselves in a constant cycle of learning and adopting the latest technologies—be it Angular, React, or Vue.js. While these frameworks offer unique features and functionalities, the relentless pursuit of the “next best thing” can lead to a fragmented ecosystem where projects become overly complex, hindering collaboration and efficiency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table 1: Comparison of Popular JavaScript Frameworks&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Framework&lt;/th&gt;
      &lt;th&gt;Year Released&lt;/th&gt;
      &lt;th&gt;Main Purpose&lt;/th&gt;
      &lt;th&gt;Notable Features&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Angular&lt;/td&gt;
      &lt;td&gt;2010&lt;/td&gt;
      &lt;td&gt;Single-page applications&lt;/td&gt;
      &lt;td&gt;Two-way data binding, dependency injection&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;React&lt;/td&gt;
      &lt;td&gt;2013&lt;/td&gt;
      &lt;td&gt;UI component development&lt;/td&gt;
      &lt;td&gt;Virtual DOM, component-based architecture&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Vue.js&lt;/td&gt;
      &lt;td&gt;2014&lt;/td&gt;
      &lt;td&gt;Flexible and incremental adoption&lt;/td&gt;
      &lt;td&gt;Reactive data binding, easy integration&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;As a result, many developers find themselves using these frameworks without fully understanding their real-world applications, leading to projects that prioritize technical sophistication over tangible benefits.&lt;/p&gt;

&lt;h3 id=&quot;2-the-app-overload&quot;&gt;2. &lt;strong&gt;The app overload&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;The app ecosystem presents another glaring example of trivial innovation. While the explosion of mobile applications has transformed how we interact with technology, it has also led to an overwhelming number of apps that often lack clear utility. Many apps are developed merely to jump on trends or capitalize on user behavior rather than genuinely addressing users’ needs.&lt;/p&gt;

&lt;p&gt;For instance, countless “lifestyle” apps provide minimal value, often overlapping in functionality. Users are bombarded with choices, yet many struggle to find applications that truly enhance their daily lives. This saturation can lead to app fatigue, where the novelty of new apps quickly fades, leaving users disenchanted.&lt;/p&gt;

&lt;h2 id=&quot;realigning-technology-with-utilitarian-goals&quot;&gt;Realigning technology with utilitarian goals&lt;/h2&gt;

&lt;p&gt;To combat this trend, it’s essential to realign technology development with utilitarian principles, ensuring that innovation is driven by genuine human needs rather than superficial trends. Here are some strategies to foster purpose-driven technology:&lt;/p&gt;

&lt;h3 id=&quot;1-focus-on-problem-solving&quot;&gt;1. &lt;strong&gt;Focus on problem-solving&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Technology should be developed with a clear understanding of the problems it aims to solve. Developers and engineers need to engage with end-users to identify their pain points and collaborate on solutions that directly address those issues. By prioritizing utility over novelty, we can create products that make a meaningful impact on people’s lives.&lt;/p&gt;

&lt;h3 id=&quot;2-adopt-a-minimalist-approach&quot;&gt;2. &lt;strong&gt;Adopt a minimalist approach&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;In a world rife with complexity, embracing simplicity can be a powerful tool for innovation. By stripping away unnecessary features and focusing on core functionalities, developers can create more intuitive and effective solutions. This minimalist approach can lead to a better user experience and greater satisfaction.&lt;/p&gt;

&lt;h3 id=&quot;3-encourage-interdisciplinary-collaboration&quot;&gt;3. &lt;strong&gt;Encourage interdisciplinary collaboration&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;The most successful innovations often arise from collaboration across disciplines. By fostering partnerships between technologists, social scientists, and domain experts, we can gain a more holistic understanding of the challenges facing society and create solutions that are both effective and relevant. Interdisciplinary teams can bridge the gap between technical capabilities and real-world applications, ensuring that innovations are rooted in utility.&lt;/p&gt;

&lt;h3 id=&quot;4-emphasize-sustainability-and-social-impact&quot;&gt;4. &lt;strong&gt;Emphasize sustainability and social impact&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;As we develop new technologies, it’s crucial to consider their long-term impact on society and the environment. Prioritizing sustainability can guide innovation toward solutions that benefit not only the present but also future generations. By focusing on social impact, developers can create technologies that empower communities, enhance quality of life, and address pressing global issues, such as climate change, poverty, and inequality.&lt;/p&gt;

&lt;h2 id=&quot;conclusion-a-call-for-purpose-driven-innovation&quot;&gt;Conclusion: A call for purpose-driven innovation&lt;/h2&gt;

&lt;p&gt;The time has come to reinvigorate the utilitarian principles that once drove technological innovation. By shifting our focus back to problem-solving and genuine human welfare, we can cultivate a landscape of technology that enriches lives rather than merely filling the market with superficial trends.&lt;/p&gt;

&lt;p&gt;Purpose-driven technology has the potential to transform society for the better, advancing human potential and addressing the challenges we face today. As developers, engineers, and innovators, we must ask ourselves: Are we creating technology that truly serves a purpose? By aligning our efforts with the needs of society, we can ensure that innovation remains a powerful force for good, paving the way for a brighter, more sustainable future.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Understanding the limitations of Agile: when flexibility becomes chaos</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/understanding-limitations-of-agile.html"/>
   <updated>2024-10-27T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/understanding-limitations-of-agile</id>
   <content type="html">&lt;p&gt;Agile methodology, lauded for its adaptability and responsiveness, has revolutionized the way software development teams operate. However, beneath the surface of its acclaimed flexibility lies a paradox: the very principles that enable Agile’s success can also give rise to chaos when not properly managed. This article delves into the limitations of Agile, examining how its inherent flexibility can sometimes lead to confusion and misalignment, ultimately jeopardizing project outcomes.&lt;/p&gt;

&lt;h2 id=&quot;the-agile-promise-flexibility-and-responsiveness&quot;&gt;The Agile promise: flexibility and responsiveness&lt;/h2&gt;

&lt;p&gt;At its core, Agile is designed to embrace change. The Agile Manifesto emphasizes collaboration, customer feedback, and iterative progress, allowing teams to pivot as requirements evolve. This promise of flexibility is undoubtedly appealing in today’s fast-paced, ever-changing technological landscape. However, this flexibility can become a double-edged sword.&lt;/p&gt;

&lt;h3 id=&quot;the-dangers-of-unchecked-flexibility&quot;&gt;The dangers of unchecked flexibility&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Scope creep and shifting priorities&lt;/strong&gt;&lt;br /&gt;
In a bid to remain responsive to customer needs, Agile teams may fall victim to scope creep. With every iteration, stakeholders might introduce new features or modify existing ones based on feedback, resulting in a project that deviates significantly from its original objectives. This continual shifting of priorities can lead to confusion among team members, as they struggle to align their efforts with an ever-changing roadmap.&lt;/p&gt;

    &lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Imagine a development team working on a project management tool. Initially, the goal was to develop a user-friendly interface for task tracking. However, after multiple feedback sessions, stakeholders keep adding features like time tracking, project budgeting, and collaboration tools. Each new request stretches the team’s resources thin, leading to a product that is bloated and lacks cohesion.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Miscommunication and role ambiguity&lt;/strong&gt;&lt;br /&gt;
Agile’s emphasis on self-organizing teams can inadvertently create role ambiguity. When team members have the freedom to take on various tasks, lines between responsibilities can blur. This lack of clarity may lead to miscommunication, misunderstandings, and ultimately a fragmented approach to project execution.&lt;/p&gt;

    &lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; In a Scrum team, if there is no clear definition of roles, developers might assume that testers will also handle bug fixes, while testers expect developers to fix the issues they identify. This overlap can result in critical bugs slipping through the cracks, causing frustration for both team members and stakeholders.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Reduced focus on long-term goals&lt;/strong&gt;&lt;br /&gt;
Agile promotes short-term iterations and frequent releases, which can lead teams to prioritize immediate gains over long-term objectives. While delivering incremental value is essential, losing sight of the bigger picture can hinder a project’s success. Teams may become so engrossed in daily stand-ups and sprint reviews that they forget to align their work with the overall vision.&lt;/p&gt;

    &lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; A marketing automation platform may release updates every two weeks, focusing on user interface enhancements based on user feedback. However, without a strategic plan for integrating these features into a cohesive marketing strategy, the product may fail to meet user expectations in the long run, resulting in declining user engagement.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/understanding-limitations-of-agile.webp&quot; alt=&quot;understanding-limitations-of-agile&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;when-agile-turns-into-chaos&quot;&gt;When Agile turns into chaos&lt;/h3&gt;

&lt;p&gt;The cumulative effect of these pitfalls can lead to chaos within an Agile framework. Here are some scenarios where flexibility transforms into disorder:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Overwhelmed teams&lt;/strong&gt;&lt;br /&gt;
As the demands on an Agile team increase, members may feel overwhelmed by the influx of changes and requests. The pressure to adapt quickly can lead to burnout, low morale, and diminished productivity. When team members are constantly firefighting to keep up with shifting priorities, the quality of their work may suffer.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Stakeholder frustration&lt;/strong&gt;&lt;br /&gt;
Stakeholders expect a clear, consistent communication flow and reliable progress updates. When an Agile team struggles to keep everyone aligned due to changing requirements, stakeholders may become frustrated and lose confidence in the team’s ability to deliver results. This erodes trust and can hinder collaboration.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Compromised product quality&lt;/strong&gt;&lt;br /&gt;
The focus on flexibility can sometimes lead to corners being cut in terms of quality assurance. As deadlines loom and pressure mounts, teams may prioritize speed over thorough testing. This can result in a product that, while completed quickly, is riddled with bugs and usability issues.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;finding-balance-establishing-boundaries-within-agile&quot;&gt;Finding balance: establishing boundaries within Agile&lt;/h3&gt;

&lt;p&gt;To mitigate the chaos that can arise from Agile’s inherent flexibility, organizations must establish clear boundaries and practices:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Define a clear project scope&lt;/strong&gt;&lt;br /&gt;
While flexibility is essential, establishing a baseline project scope at the outset can help maintain focus. This scope should be agreed upon by all stakeholders, with an emphasis on core features that align with user needs. Regular reviews can allow for adjustments while keeping the overall objectives intact.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Clarify roles and responsibilities&lt;/strong&gt;&lt;br /&gt;
Ensuring that every team member understands their role within the Agile framework is crucial for effective communication and collaboration. Regularly revisiting role definitions during retrospectives can help keep everyone accountable and aligned.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Prioritize quality assurance&lt;/strong&gt;&lt;br /&gt;
Integrating robust testing practices within the Agile process can help uphold product quality. Implementing automated testing and involving quality assurance from the beginning can catch issues early and maintain high standards.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Foster open communication&lt;/strong&gt;&lt;br /&gt;
Encouraging transparent and open communication among team members and stakeholders can help mitigate misunderstandings. Utilizing collaboration tools can facilitate information sharing and keep everyone on the same page.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;Agile methodologies hold immense potential for enhancing project outcomes through flexibility and responsiveness. However, without careful management, this very flexibility can spiral into chaos, resulting in confusion, burnout, and compromised product quality. By understanding the limitations of Agile and establishing clear boundaries, organizations can harness its power while avoiding the pitfalls that threaten to derail their projects. In the end, the goal should be to strike a balance between agility and structure, ensuring that teams remain aligned, focused, and productive.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Think async: unleashing PHP's hidden performance power</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/think-async-php-guide.html"/>
   <updated>2024-10-25T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/think-async-php-guide</id>
   <content type="html">&lt;p&gt;Asynchronous programming is an often misunderstood yet crucial concept in modern software development. While traditionally associated with JavaScript or Python, asynchronous programming is just as powerful and relevant in PHP. In this guide, we’ll explore the foundations of asynchronous programming, why it matters, and how PHP developers can use it to build faster and more efficient applications.&lt;/p&gt;

&lt;h2 id=&quot;understanding-synchronous-vs-asynchronous-programming&quot;&gt;Understanding synchronous vs. asynchronous programming&lt;/h2&gt;

&lt;p&gt;Before we dive into the nuts and bolts, let’s define two core concepts: synchronous and asynchronous programming.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Synchronous programming&lt;/strong&gt; is how PHP (and many languages) typically handle code execution. Here, each line of code is executed one after the other. If a particular line involves a time-consuming operation, such as fetching data from a database or making an API call, the entire script waits until that operation completes. Think of it as a line at a coffee shop where customers are served one by one, and nobody else can be served until the current customer is done.&lt;/p&gt;

&lt;p&gt;On the other hand, &lt;strong&gt;asynchronous programming&lt;/strong&gt; allows multiple tasks to run simultaneously or at least seem to run concurrently. When you make an asynchronous call, it doesn’t wait for the result immediately. Instead, it moves on to execute other parts of your program and returns to the original task once it’s complete. It’s like the coffee shop employing multiple baristas so that customers can order and collect their drinks in parallel.&lt;/p&gt;

&lt;h2 id=&quot;why-should-php-developers-care-about-asynchronous-programming&quot;&gt;Why should PHP developers care about asynchronous programming?&lt;/h2&gt;

&lt;p&gt;Asynchronous programming offers significant advantages for PHP applications that need to handle multiple I/O-bound tasks, such as API requests, file uploads, or database queries. Here are some scenarios where adopting asynchronous methods in PHP can supercharge your application:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;improving web scraping efficiency&lt;/strong&gt;: If your PHP app needs to scrape data from multiple sources, waiting for each HTTP request to complete before starting the next can slow things down. Asynchronous processing lets your script send multiple HTTP requests simultaneously and handle the responses as they arrive, speeding up the scraping process dramatically.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;handling multiple API requests&lt;/strong&gt;: Applications that depend on several external APIs, like aggregating social media feeds, can benefit from non-blocking requests. Instead of waiting for each API call to return, PHP can make all the requests asynchronously, improving the overall response time.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;optimizing real-time applications&lt;/strong&gt;: Think about online gaming, real-time chats, or collaborative tools where latency can ruin the user experience. Asynchronous programming allows PHP developers to handle multiple events and user inputs at once, providing a much smoother and more responsive user experience.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;background task processing&lt;/strong&gt;: If you have tasks such as sending email confirmations, processing images, or performing background data analysis, asynchronous programming is ideal. You don’t want these background operations to slow down the main script or web request.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;exploring-async-libraries-and-tools-in-php&quot;&gt;Exploring async libraries and tools in PHP&lt;/h2&gt;

&lt;p&gt;Historically, PHP hasn’t been the go-to language for asynchronous programming. The language’s synchronous nature made it more suited for traditional, blocking operations. However, the PHP community has developed several libraries and tools to support asynchronous programming. Let’s dive into some of the most notable ones:&lt;/p&gt;

&lt;h3 id=&quot;1-swoole&quot;&gt;1. Swoole&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Swoole&lt;/strong&gt; is a high-performance coroutine-based PHP extension that brings asynchronous I/O and concurrent programming capabilities to PHP. It allows developers to build event-driven, real-time applications using native PHP code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Coroutines&lt;/strong&gt;: Enable multiple tasks to run concurrently, reducing the complexity of managing asynchronous code.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Asynchronous I/O&lt;/strong&gt;: Supports non-blocking HTTP clients, database queries, file operations, and more.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;WebSockets&lt;/strong&gt;: Ideal for building real-time applications like live chats and dashboards.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:
Here’s a simple example of creating an asynchronous HTTP server using Swoole:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Swoole\Http\Server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;127.0.0.1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9501&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;request&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Hello, Swoole!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With Swoole, you can build efficient servers that can handle multiple requests simultaneously without blocking.&lt;/p&gt;

&lt;h3 id=&quot;2-reactphp&quot;&gt;2. ReactPHP&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;ReactPHP&lt;/strong&gt; is another popular asynchronous PHP library. It’s event-driven and non-blocking, making it well-suited for I/O-intensive applications. ReactPHP can help create long-running applications like chat servers, API integrations, or even real-time analytics tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Non-blocking I/O&lt;/strong&gt;: ReactPHP allows PHP scripts to handle many I/O operations concurrently, like HTTP requests and WebSocket connections.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Event loop&lt;/strong&gt;: Uses an event-driven model that works similarly to Node.js, enabling responsive and real-time applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:
Here’s how to create an asynchronous HTTP server using ReactPHP:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;React\Http\Server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Psr\Http\Message\ServerRequestInterface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;React\Http\Message\Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;React\EventLoop\Factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;vendor/autoload.php&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$loop&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Factory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;ServerRequestInterface&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Content-Type&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;text/plain&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Hello, ReactPHP!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$socket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;React\Socket\Server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;127.0.0.1:8080&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$loop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$socket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$loop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With ReactPHP, the event-driven nature of your code allows it to handle requests concurrently.&lt;/p&gt;

&lt;h3 id=&quot;3-amp&quot;&gt;3. Amp&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Amp&lt;/strong&gt; is another promising library that introduces asynchronous programming using coroutines. It’s lightweight and designed to make writing asynchronous code in PHP simple and intuitive. Amp supports HTTP servers, MySQL queries, and more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Promise-based concurrency&lt;/strong&gt;: Offers an easy way to work with async tasks.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Coroutines&lt;/strong&gt;: Use generators to write non-blocking code.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Rich ecosystem&lt;/strong&gt;: Provides multiple packages, including for HTTP servers, database queries, and WebSocket servers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:
An example of using Amp for making asynchronous HTTP requests:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Amp\Loop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Amp\Http\Client\Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Amp\Http\Client\HttpClientBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;vendor/autoload.php&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;Loop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HttpClientBuilder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;buildDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$request&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://example.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;nv&quot;&gt;$response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getBody&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Amp’s promise-based approach and support for coroutines make it a great choice for PHP developers looking to add async capabilities to their applications.&lt;/p&gt;

&lt;h2 id=&quot;comparison-of-async-libraries&quot;&gt;Comparison of async libraries&lt;/h2&gt;

&lt;p&gt;Here’s a brief comparison of the three prominent libraries:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Feature&lt;/th&gt;
      &lt;th&gt;Swoole&lt;/th&gt;
      &lt;th&gt;ReactPHP&lt;/th&gt;
      &lt;th&gt;Amp&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Programming Model&lt;/td&gt;
      &lt;td&gt;Coroutines, async I/O&lt;/td&gt;
      &lt;td&gt;Event-driven, non-blocking&lt;/td&gt;
      &lt;td&gt;Promise-based, coroutines&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Real-time Apps&lt;/td&gt;
      &lt;td&gt;Great for real-time&lt;/td&gt;
      &lt;td&gt;Well-suited&lt;/td&gt;
      &lt;td&gt;Suitable for real-time&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Ecosystem Support&lt;/td&gt;
      &lt;td&gt;WebSockets, HTTP&lt;/td&gt;
      &lt;td&gt;HTTP, WebSocket, TCP&lt;/td&gt;
      &lt;td&gt;HTTP, MySQL, Redis&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Learning Curve&lt;/td&gt;
      &lt;td&gt;Medium&lt;/td&gt;
      &lt;td&gt;Low to medium&lt;/td&gt;
      &lt;td&gt;Medium&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;when-to-choose-asynchronous-php&quot;&gt;When to choose asynchronous PHP&lt;/h2&gt;

&lt;p&gt;While asynchronous programming offers considerable advantages, it’s not always the best solution for every PHP project. Here are some scenarios where asynchronous PHP shines:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;API-heavy applications&lt;/strong&gt;: When your application needs to consume multiple APIs simultaneously, asynchronous PHP reduces the total wait time.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;real-time applications&lt;/strong&gt;: Chat applications, gaming servers, or live analytics dashboards benefit significantly from non-blocking PHP code.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;file processing&lt;/strong&gt;: Large file uploads, image resizing, or PDF processing can be handled in the background while the rest of the application remains responsive.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Conversely, for simpler websites or applications that are mainly read-heavy (like blogs or news websites), the complexity of async PHP may not offer enough benefits to justify the additional learning curve.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Asynchronous programming might seem daunting to PHP developers initially, but with tools like Swoole, ReactPHP, and Amp, PHP’s asynchronous capabilities are more accessible than ever. Embracing async methods in your PHP projects can enhance performance, responsiveness, and scalability, making your applications more competitive in today’s fast-paced digital world.&lt;/p&gt;

&lt;p&gt;By leveraging these techniques, PHP developers can expand their horizons and dive into new realms of concurrent programming, creating dynamic and highly performant applications. So, why not try out asynchronous PHP in your next project?&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;sources&quot;&gt;Sources:&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.swoole.co.uk/docs&quot;&gt;Swoole Documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://reactphp.org/&quot;&gt;ReactPHP Documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://amphp.org/&quot;&gt;Amp Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Framework overload: when convenience dulls innovation in software development</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/framework-overload.html"/>
   <updated>2024-10-24T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/framework-overload</id>
   <content type="html">&lt;p&gt;Web and software developers are more empowered today than ever. But there’s a growing problem: the increasing reliance on frameworks, libraries, and pre-packaged solutions. Frameworks like Laravel, React, Angular, and Django have become household names, not just for their ability to streamline development but also for their role in reshaping how developers approach problem-solving. While these frameworks undoubtedly improve efficiency and scalability, their dominance is also inadvertently curbing creativity and encouraging cookie-cutter solutions.&lt;/p&gt;

&lt;h3 id=&quot;the-allure-of-frameworks-convenience-over-comprehension&quot;&gt;The allure of frameworks: convenience over comprehension&lt;/h3&gt;

&lt;p&gt;Let’s face it, frameworks are alluring. They offer pre-built structures, built-in security measures, and optimized performance. Why reinvent the wheel when there’s a highly polished, thoroughly tested wheel already available? This mindset is where the problem begins. For many developers, understanding how a framework functions under the hood or grasping the nuances of core languages like PHP, Python, or JavaScript has taken a backseat. Instead, the race is on to master the framework, not necessarily the foundational technology.&lt;/p&gt;

&lt;p&gt;For example, when we work with Laravel for PHP, it abstracts much of the nitty-gritty of database management using its Eloquent ORM. While this is fantastic for developers looking to build applications quickly, it can lead to a situation where developers aren’t familiar with basic SQL concepts, such as joins, indexing, or optimization strategies. The abstraction, while convenient, becomes a crutch, limiting a deeper understanding of the language and the database systems in play.&lt;/p&gt;

&lt;p&gt;A popular anecdote among experienced programmers is encountering job candidates who excel at following documentation to the letter but falter when presented with unique or unconventional problems. In such cases, over-reliance on frameworks has effectively dulled their problem-solving abilities.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/framework-overload.webp&quot; alt=&quot;framework-overload&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;the-trap-of-tool-centric-learning&quot;&gt;The trap of tool-centric learning&lt;/h3&gt;

&lt;p&gt;When you focus on mastering frameworks, you end up chasing trends. In recent years, there’s been a rush to learn new tools rather than mastering the underlying principles of coding and problem-solving. This approach runs contrary to the spirit of innovation. A competent craftsman isn’t defined by their tools but by their skill, creativity, and understanding of materials.&lt;/p&gt;

&lt;p&gt;Consider the rise of front-end frameworks like React. While these frameworks have revolutionized the web, many developers jump straight into JSX and component states without having a firm grasp of vanilla JavaScript. They are fluent in implementing lifecycle methods and hooks but are left scrambling when faced with simple tasks outside the React ecosystem.&lt;/p&gt;

&lt;p&gt;The same applies to mobile development, where the advent of Flutter and React Native has driven many developers away from native mobile development. While hybrid frameworks are an excellent choice for cross-platform deployment, they can lead developers to a shallow understanding of how the underlying operating system works.&lt;/p&gt;

&lt;h3 id=&quot;missing-the-forest-for-the-trees-losing-sight-of-the-bigger-picture&quot;&gt;Missing the forest for the trees: losing sight of the bigger picture&lt;/h3&gt;

&lt;p&gt;Let’s take the gaming industry as an example. With the prevalence of powerful game engines like Unity and Unreal, developers can create complex games relatively quickly. These engines offer incredible capabilities for rendering, physics, and AI. However, this convenience often leads to an over-reliance on built-in features rather than understanding core graphics programming or physics simulation.&lt;/p&gt;

&lt;p&gt;In web development, the complexity added by juggling multiple libraries can lead developers to fall into the trap of “over-engineering.” When a relatively simple problem is approached with overly complicated solutions, developers often lose sight of the core purpose and creativity behind their work. The technical focus becomes how to optimize their React code or fine-tune a Redux store, rather than solving the user’s actual problem.&lt;/p&gt;

&lt;p&gt;This is where the phrase “missing the forest for the trees” applies. Developers sometimes become so engrossed in the tools at their disposal that they fail to step back and evaluate whether these tools are necessary or the best fit for the job.&lt;/p&gt;

&lt;h3 id=&quot;frameworks-as-enablers-not-crutches-a-path-forward&quot;&gt;Frameworks as enablers, not crutches: a path forward&lt;/h3&gt;

&lt;p&gt;The solution isn’t to stop using frameworks altogether—they’re valuable and time-saving. However, a balanced approach is key. Here are some recommendations to prevent frameworks from dulling innovation:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Learn the basics thoroughly:&lt;/strong&gt; Commit to understanding core concepts in programming languages before jumping to frameworks. Know what happens under the hood. For example, a deep dive into PHP’s handling of HTTP requests or JavaScript’s prototype-based inheritance can be eye-opening.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Don’t skip problem-solving skills:&lt;/strong&gt; The best way to learn coding is not by following tutorials blindly, but by working on projects that challenge you to solve unique problems. Instead of relying solely on predefined solutions, challenge yourself to come up with your own, even if they are less polished or more cumbersome initially.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Use frameworks as scaffolding, not blueprints:&lt;/strong&gt; Think of frameworks as scaffolding—they provide the structure and support needed to erect a building. However, the design of the building itself should come from the architect (you). It’s essential to keep the focus on the problem and not allow frameworks to dictate every aspect of your solution.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Experiment beyond the framework:&lt;/strong&gt; Spend time creating projects that are not tied to any framework. For instance, try creating a simple REST API without Laravel or Spring. This forces you to engage with the raw HTTP protocol and routing mechanisms, broadening your understanding of how these frameworks streamline tasks.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Embrace minimalism:&lt;/strong&gt; Before you install yet another package, ask yourself if you genuinely need it. Consider the words of Steve Jobs: “Innovation is saying no to a thousand things.” Applying this minimalist philosophy to your tech stack can keep your projects simple and impactful.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;conclusion-towards-a-balanced-approach&quot;&gt;Conclusion: towards a balanced approach&lt;/h3&gt;

&lt;p&gt;In the end, frameworks are here to stay, and rightly so—they save time, reduce complexity, and make our lives easier. But it’s essential to remember that they are not the goal; they’re merely a means to an end. Developers should strive to build a solid understanding of fundamental concepts, focusing on solving problems creatively rather than fitting them into the constraints of a particular framework.&lt;/p&gt;

&lt;p&gt;We must remember that the tools we use should enhance our creativity and not limit it. By approaching frameworks with this balanced mindset, we can continue to innovate, solve complex problems, and create truly remarkable work.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Guarding the digital frontier: top open source security projects of 2024</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/guarding-the-digital-frontier.html"/>
   <updated>2024-10-20T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/guarding-the-digital-frontier</id>
   <content type="html">&lt;p&gt;As digital threats evolve at an unprecedented pace, robust cybersecurity measures have become more critical than ever. Open source security projects stand at the forefront of this battle, offering powerful tools that empower organizations to protect their data and infrastructure. These projects promote transparency, encourage collaboration, and often provide cost-effective alternatives to proprietary software. In 2024, the open source community continues to innovate, with numerous security projects making significant strides in safeguarding the digital frontier. This article will explore the leading open source security projects you should know about this year.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/guarding-the-digital-frontier.jpg&quot; alt=&quot;guarding-the-digital-frontier&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-importance-of-open-source-in-cybersecurity&quot;&gt;The importance of open source in cybersecurity&lt;/h2&gt;

&lt;p&gt;Open source security solutions have become indispensable in today’s cybersecurity landscape for several reasons:&lt;/p&gt;

&lt;h3 id=&quot;1-transparency&quot;&gt;1. &lt;strong&gt;Transparency&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The open source model allows anyone to inspect the code, making it easier to identify vulnerabilities. This transparency builds trust among users, as they can verify security measures themselves.&lt;/p&gt;

&lt;h3 id=&quot;2-community-collaboration&quot;&gt;2. &lt;strong&gt;Community collaboration&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The collaborative nature of open source allows developers and security experts worldwide to contribute, leading to more robust and constantly evolving solutions.&lt;/p&gt;

&lt;h3 id=&quot;3-cost-effectiveness&quot;&gt;3. &lt;strong&gt;Cost-effectiveness&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Many open source tools are free, making them accessible to organizations of all sizes. This is particularly beneficial for small businesses and startups with limited cybersecurity budgets.&lt;/p&gt;

&lt;h3 id=&quot;4-flexibility-and-customization&quot;&gt;4. &lt;strong&gt;Flexibility and customization&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Organizations can modify open source projects to suit their specific security needs, providing a tailored solution that proprietary software may not offer.&lt;/p&gt;

&lt;h3 id=&quot;5-rapid-innovation&quot;&gt;5. &lt;strong&gt;Rapid innovation&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;With contributions from a diverse pool of developers, open source security projects often adopt the latest security technologies and trends faster than proprietary alternatives.&lt;/p&gt;

&lt;h2 id=&quot;top-open-source-security-projects-of-2024&quot;&gt;Top open source security projects of 2024&lt;/h2&gt;

&lt;p&gt;Here are some of the most impactful open source security projects in 2024, helping organizations bolster their defenses against modern cyber threats:&lt;/p&gt;

&lt;h3 id=&quot;1-owasp-zap-zed-attack-proxy&quot;&gt;1. &lt;strong&gt;OWASP ZAP (Zed Attack Proxy)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: OWASP ZAP is an advanced tool for discovering security vulnerabilities in web applications. It is user-friendly and suitable for both beginners and experienced professionals.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why It Matters&lt;/strong&gt;: As web applications grow more complex, OWASP ZAP remains a popular choice for developers and security teams to identify and remediate vulnerabilities effectively.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;2-metasploit-framework&quot;&gt;2. &lt;strong&gt;Metasploit Framework&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: Metasploit is a powerful penetration testing framework used to find and exploit system vulnerabilities.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why It Matters&lt;/strong&gt;: Metasploit is widely relied upon by ethical hackers for simulating real-world attacks, helping organizations strengthen their cybersecurity defenses.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;3-suricata&quot;&gt;3. &lt;strong&gt;Suricata&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: Suricata is an open source network threat detection engine that supports intrusion detection, prevention, and security monitoring.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why It Matters&lt;/strong&gt;: Suricata’s ability to analyze network traffic in real-time provides essential protection against evolving cyber threats.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;4-clamav&quot;&gt;4. &lt;strong&gt;ClamAV&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: ClamAV is an antivirus engine that helps detect viruses, malware, and malicious threats.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why It Matters&lt;/strong&gt;: ClamAV is commonly used in environments that require consistent malware scanning, including mail servers and web applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;5-wireguard&quot;&gt;5. &lt;strong&gt;WireGuard&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: WireGuard is a modern VPN protocol designed for speed and simplicity, offering secure communication with minimal complexity.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why It Matters&lt;/strong&gt;: As remote work and online privacy concerns rise, WireGuard is gaining traction for its efficiency and strong encryption standards.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;6-openvas&quot;&gt;6. &lt;strong&gt;OpenVAS&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: OpenVAS (Open Vulnerability Assessment System) is a comprehensive vulnerability scanning tool that helps identify weaknesses in networks and systems.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why It Matters&lt;/strong&gt;: Regular vulnerability assessments with tools like OpenVAS are essential for staying ahead of emerging cyber threats.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;7-thehive&quot;&gt;7. &lt;strong&gt;TheHive&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: TheHive is an incident response platform that enables teams to manage security incidents collaboratively.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why It Matters&lt;/strong&gt;: In a landscape where cyber incidents can escalate quickly, TheHive provides the tools necessary for effective incident management.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;8-ossec&quot;&gt;8. &lt;strong&gt;OSSEC&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: OSSEC is a host-based intrusion detection system (HIDS) that monitors and analyzes logs, identifying anomalies and potential threats.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why It Matters&lt;/strong&gt;: OSSEC plays a key role in maintaining system security and compliance by offering insights into system behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;9-kali-linux&quot;&gt;9. &lt;strong&gt;Kali Linux&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: Kali Linux is a popular Debian-based distribution designed for penetration testing and ethical hacking, featuring pre-installed security tools.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why It Matters&lt;/strong&gt;: Kali Linux remains a favorite for security practitioners conducting network scans, exploit development, and web application testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;10-lynis&quot;&gt;10. &lt;strong&gt;Lynis&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: Lynis is a security auditing tool for Unix-based systems that performs in-depth scans and offers suggestions for improving security.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why It Matters&lt;/strong&gt;: Lynis helps organizations assess their security posture and implement best practices to mitigate potential risks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-to-get-involved-in-open-source-security-projects&quot;&gt;How to Get Involved in Open Source Security Projects&lt;/h2&gt;

&lt;p&gt;Contributing to open source security projects can boost your skills and positively impact the cybersecurity community. Here’s how to get started:&lt;/p&gt;

&lt;h3 id=&quot;1-choose-a-project&quot;&gt;1. &lt;strong&gt;Choose a project&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Find a project that aligns with your interests and skills. Many projects list beginner-friendly tasks under labels like “good first issue” or “help wanted.”&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;2-set-up-your-development-environment&quot;&gt;2. &lt;strong&gt;Set up your development environment&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Familiarize yourself with the project’s tools and technologies, such as Git and specific programming languages.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;3-follow-contribution-guidelines&quot;&gt;3. &lt;strong&gt;Follow contribution guidelines&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Each project has contribution guidelines. Understanding how to submit issues, pull requests, and code reviews is essential.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;4-start-small&quot;&gt;4. &lt;strong&gt;Start small&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Begin with manageable tasks, such as fixing documentation or small bugs, to build confidence and get familiar with the codebase.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;5-engage-with-the-community&quot;&gt;5. &lt;strong&gt;Engage with the community&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Participating in community discussions can help you gain valuable insights and establish relationships with fellow contributors.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;6-be-open-to-feedback&quot;&gt;6. &lt;strong&gt;Be open to feedback&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Contributions often come with feedback. Use it as an opportunity to learn and improve your skills.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Open source security projects are transforming how organizations defend against cyber threats. Leading tools like OWASP ZAP, Metasploit, and Suricata are paving the way for innovative, transparent, and collaborative cybersecurity solutions. As we navigate 2024’s complex cybersecurity landscape, contributing to these projects can help protect digital assets while enhancing your professional growth. Embrace the power of collaboration in the open source community, and together, let’s guard the digital frontier!&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;sources&quot;&gt;Sources:&lt;/h3&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://opensource.com/article/21/2/open-source-security&quot;&gt;opensource.com: Open source transparency in cybersecurity&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.zaproxy.org/getting-started/&quot;&gt;OWASP ZAP Project Page&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://portswigger.net/daily-swig/owasp-security-projects-showcased-at-all-day-devops-conference&quot;&gt;OWASP ZAP: A key tool for web app security&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.metasploit.com/&quot;&gt;Metasploit: A hacker’s toolkit&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://suricata.io/&quot;&gt;Suricata Project Page&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.clamav.net/&quot;&gt;ClamAV Project Page&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.wireguard.com/&quot;&gt;WireGuard Project Page&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cybersecurity-insiders.com/zcaler-threatlabz-2024-vpn-risk-report/&quot;&gt;WireGuard: Modern VPN solutions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.openvas.org/&quot;&gt;OpenVAS Project Page&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/security-assessment-openvas/&quot;&gt;GeeksforGeeks: OpenVAS and vulnerability assessments&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://strangebee.com/thehive/&quot;&gt;TheHive Project Page&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.ossec.net/&quot;&gt;OSSEC Project Page&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.kali.org/&quot;&gt;Kali Linux Project Page&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://cisofy.com/lynis/&quot;&gt;Lynis Project Page&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content>
 </entry>
 
 <entry>
   <title>Why forced updates in Snaps and Flatpaks are breaking Linux's core principles</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/forced-updates-snaps-flatpaks-linux-core-principles.html"/>
   <updated>2024-10-19T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/forced-updates-snaps-flatpaks-linux-core-principles</id>
   <content type="html">&lt;p&gt;Linux has long been celebrated for its commitment to user control and freedom. However, the rise of modern packaging systems like Snaps and Flatpaks, with their mandatory updates, poses a significant challenge to these core principles. This article delves into the implications of forced updates, highlighting how they disrupt user autonomy, compromise system stability, and contribute to the growing concern of vendor lock-in.&lt;/p&gt;

&lt;h2 id=&quot;user-control-vs-convenience-the-forced-updates-issue&quot;&gt;User control vs. convenience: The forced updates issue&lt;/h2&gt;

&lt;p&gt;Traditional Linux packaging systems, such as DEBs and RPMs, prioritize user control, enabling individuals to decide when and how to update their software. In contrast, Snaps and Flatpaks cater to convenience, especially for less technical users, by offering automated updates and dependency management. Yet, this shift raises critical questions about user autonomy.&lt;/p&gt;

&lt;p&gt;For many, forced updates can disrupt workflows, especially in environments where system stability is essential. The need to maintain consistent software versions becomes paramount, as unexpected updates may introduce bugs or break compatibility with existing applications.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/forced-updates-snaps-flatpaks.jpg&quot; alt=&quot;forced-updates-snaps-flatpaks.jpg&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-downsides-of-forced-updates-breaking-the-linux-philosophy&quot;&gt;The downsides of forced updates: breaking the ‘Linux Philosophy’&lt;/h2&gt;

&lt;p&gt;Linux distributions thrive on the “do one thing and do it well” philosophy. However, Snaps and Flatpaks bundle dependencies together, which, while reducing “dependency hell,” results in larger package sizes and unintended updates. The backlash against Snap’s background updates is telling; users often find themselves unprepared for changes that can occur without prior notice. This lack of transparency runs counter to the traditional Linux approach, where users are empowered to control their systems.&lt;/p&gt;

&lt;p&gt;In mission-critical environments—such as servers or development setups—unplanned updates can be particularly detrimental. Users may find themselves scrambling to revert to previous versions after a sudden break in functionality, illustrating the tension between convenience and control.&lt;/p&gt;

&lt;h2 id=&quot;security-vs-stability-striking-the-balance&quot;&gt;Security vs. Stability: Striking the balance&lt;/h2&gt;

&lt;p&gt;Proponents of forced updates argue that they enhance security by ensuring vulnerabilities are patched promptly. However, this perspective overlooks the stability concerns of users who prefer to test updates before implementation. For many, particularly those managing critical Linux systems, the desire to maintain operational integrity outweighs the perceived security benefits of automatic updates.&lt;/p&gt;

&lt;p&gt;A well-balanced approach would allow users to review updates, implement testing phases, and ensure compatibility before deploying changes. This flexibility fosters a more resilient system, prioritizing both security and user autonomy.&lt;/p&gt;

&lt;h2 id=&quot;bloat-and-performance-issues&quot;&gt;Bloat and performance issues&lt;/h2&gt;

&lt;p&gt;Another significant critique of Snaps and Flatpaks is their tendency to create bloat. By packaging all dependencies with each application, these systems lead to larger file sizes and decreased performance compared to traditional package managers. The forced update mechanism compounds this issue, as it continually downloads newer versions, consuming bandwidth and storage space, even when the current version meets the user’s needs.&lt;/p&gt;

&lt;p&gt;This inefficiency can be particularly frustrating for those who value a lean and responsive system. As the Linux community increasingly embraces minimalism, the bloat associated with modern packaging systems stands in stark contrast to these ideals.&lt;/p&gt;

&lt;h2 id=&quot;vendor-lock-in-concerns&quot;&gt;Vendor lock-in concerns&lt;/h2&gt;

&lt;p&gt;While Snaps are closely associated with Canonical, the parent company of Ubuntu, Flatpaks are tied to Red Hat and GNOME. This centralization raises alarms within the Linux community, which has historically championed decentralization and user choice. Forced updates can create a sense of dependency on specific companies’ methodologies, eroding the freedom that Linux users have come to expect.&lt;/p&gt;

&lt;h2 id=&quot;app-store-mentality-convenience-vs-freedom&quot;&gt;App store mentality: convenience vs. freedom&lt;/h2&gt;

&lt;p&gt;Snaps and Flatpaks aim to streamline the software distribution process, aligning Linux with the app store models familiar to users of macOS and Windows. While this approach simplifies access to applications, it also detracts from Linux’s foundational principles of user control and customization. The reality of forced updates can feel like a compromise of freedom in exchange for the illusion of convenience.&lt;/p&gt;

&lt;h2 id=&quot;potential-solutions-and-middle-ground&quot;&gt;Potential solutions and middle ground&lt;/h2&gt;

&lt;p&gt;To address these concerns, developers could consider implementing more granular controls over Snap and Flatpak updates. Options for users to opt-in or opt-out of forced updates could restore some autonomy, allowing for a more tailored experience. Additionally, offering Long-Term Support (LTS) versions of packages could provide users with stability while ensuring that security updates are still accessible when needed.&lt;/p&gt;

&lt;h2 id=&quot;alternative-package-management-systems&quot;&gt;Alternative package management systems&lt;/h2&gt;

&lt;p&gt;Other packaging solutions, such as AppImages, provide an appealing alternative by allowing for portable applications without enforced updates. Furthermore, traditional package management systems (like APT or RPM) continue to hold value, especially in scenarios where stability and control are paramount. By examining the pros and cons of these systems compared to newer methodologies, users can make informed decisions about the best approach for their needs.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;The debate surrounding forced updates in Snaps and Flatpaks resonates deeply with the Linux community. While these modern packaging systems aim to enhance convenience, they risk undermining the core principles of user control, stability, and decentralization that have defined Linux for decades. As discussions about the future of Linux packaging continue, striking a balance between attracting new users and preserving the values that long-time enthusiasts cherish will be crucial. In a landscape where user autonomy is increasingly challenged, it’s essential to advocate for solutions that empower users rather than constrain them.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Where to get your icons for web development: a comprehensive guide</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/where-to-get-icons-for-web-development.html"/>
   <updated>2024-10-18T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/where-to-get-icons-for-web-development</id>
   <content type="html">&lt;p&gt;Icons are an essential part of modern web development, providing visual cues that enhance user experience and making interfaces more intuitive and appealing. However, choosing the right set of icons involves considering both aesthetic appeal and licensing terms. In this article, we’ll explore three primary sources for web development icons: truly open-source icons, “with attribution” options, and proprietary ones. Let’s dive into these categories to help you make an informed decision.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;1-truly-open-source-icons&quot;&gt;1. &lt;strong&gt;Truly open-source icons&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;If you’re looking for full flexibility—where you can modify, distribute, and use icons without worrying about commercial restrictions—open-source icons are your best bet. These icon sets often come with permissive licenses, allowing for broad usage in both personal and commercial projects.&lt;/p&gt;

&lt;h3 id=&quot;adwaita-gnome-icons&quot;&gt;&lt;strong&gt;Adwaita (GNOME Icons)&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Adwaita&lt;/strong&gt; icon theme, used in the GNOME desktop environment, is an excellent option for developers who prioritize open-source freedom. The icon set follows the &lt;strong&gt;Creative Commons Attribution-ShareAlike 3.0 License (CC-BY-SA 3.0)&lt;/strong&gt;, which allows you to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Use the icons freely in any project, including commercial ones.&lt;/li&gt;
  &lt;li&gt;Modify the icons to suit your design needs.&lt;/li&gt;
  &lt;li&gt;Share them, provided that you attribute the original creators and maintain the same license for any derivative work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Where to Download&lt;/strong&gt;: You can download GNOME’s Adwaita icons from their &lt;a href=&quot;https://gitlab.gnome.org/GNOME/adwaita-icon-theme&quot;&gt;GitLab repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/where-to-get-icons-for-web-development.jpg&quot; alt=&quot;icons&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;material-design-icons&quot;&gt;&lt;strong&gt;Material Design Icons&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Material Design Icons&lt;/strong&gt; are inspired by Google’s Material Design guidelines and are widely used in web and app development. This set is licensed under the &lt;strong&gt;Apache License 2.0&lt;/strong&gt;, a permissive open-source license. You can use, modify, and distribute the icons freely, but you must include a copy of the license in any redistribution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where to Download&lt;/strong&gt;: You can find the full set of Material Design Icons on their &lt;a href=&quot;https://github.com/google/material-design-icons&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;font-awesome-open-source-icons&quot;&gt;&lt;strong&gt;Font Awesome (Open-Source Icons)&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Font Awesome&lt;/strong&gt; offers an extensive library of icons with both free and paid options. The free icons are licensed under &lt;strong&gt;Creative Commons Attribution 4.0&lt;/strong&gt; and can be used for both personal and commercial projects, provided attribution is given.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where to Download&lt;/strong&gt;: Visit &lt;a href=&quot;https://fontawesome.com/&quot;&gt;Font Awesome’s website&lt;/a&gt; to download the free icons.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;2-with-attribution-icon-resources&quot;&gt;2. &lt;strong&gt;“With Attribution” icon resources&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;If you’re open to giving credit to the original creators, “with attribution” icon sets offer a middle ground between fully open-source and proprietary. These platforms typically provide free icons but require proper attribution to be used legally. Here’s a look at some popular options in this category.&lt;/p&gt;

&lt;h3 id=&quot;flaticon&quot;&gt;&lt;strong&gt;Flaticon&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Flaticon&lt;/strong&gt; is one of the largest databases of free icons, offering millions of icons in various formats. You can use the free version of the icons as long as you provide attribution. The platform also offers a premium option, which removes the attribution requirement.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;License&lt;/strong&gt;: Free icons require attribution under a custom license.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Use Case&lt;/strong&gt;: Perfect for quick access to vast icon libraries, but you’ll need to credit Flaticon if you use their free icons in a commercial or personal project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Where to Download&lt;/strong&gt;: Visit &lt;a href=&quot;https://www.flaticon.com&quot;&gt;Flaticon&lt;/a&gt; to explore their collection.&lt;/p&gt;

&lt;h3 id=&quot;freepik&quot;&gt;&lt;strong&gt;Freepik&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Freepik&lt;/strong&gt; provides not only icons but also a wide range of design assets, including vectors, illustrations, and photos. Freepik’s free resources come with the same attribution requirement, while their premium subscription offers attribution-free usage.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;License&lt;/strong&gt;: Free resources are available under the &lt;strong&gt;Freepik License&lt;/strong&gt;, which requires attribution. Premium resources are licensed under more relaxed terms.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Use Case&lt;/strong&gt;: A great resource for comprehensive design needs beyond icons, but attribution is a must for free content.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Where to Download&lt;/strong&gt;: You can access Freepik’s collection at &lt;a href=&quot;https://www.freepik.com&quot;&gt;Freepik.com&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;icons8&quot;&gt;&lt;strong&gt;Icons8&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Icons8&lt;/strong&gt; offers a collection of free icons for use in web development. Like Flaticon and Freepik, their free icons require attribution, though premium users can avoid this requirement.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;License&lt;/strong&gt;: Free icons need attribution, but premium licenses remove this requirement.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Use Case&lt;/strong&gt;: Icons8 also provides tools for customizing and editing icons, making it useful for developers looking for more flexibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Where to Download&lt;/strong&gt;: Explore their library on &lt;a href=&quot;https://icons8.com&quot;&gt;Icons8’s website&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;3-proprietary-icon-resources&quot;&gt;3. &lt;strong&gt;Proprietary icon resources&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;For developers who need high-quality, professional icons and are willing to invest in premium licenses, proprietary options offer the most polished and diverse collections. These icons come with specific licensing terms that restrict redistribution but offer more freedom in commercial use without needing attribution.&lt;/p&gt;

&lt;h3 id=&quot;adobe-stock-icons&quot;&gt;&lt;strong&gt;Adobe Stock Icons&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Adobe Stock&lt;/strong&gt; offers a large variety of professionally designed icons as part of its stock asset library. These icons are royalty-free but come with strict licensing terms that allow use only in specific commercial projects, without redistribution rights.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;License&lt;/strong&gt;: Icons purchased from Adobe Stock are typically licensed under Adobe’s royalty-free stock licensing, allowing use in personal and commercial projects but prohibiting redistribution or modification beyond project needs.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Use Case&lt;/strong&gt;: Best suited for high-end commercial projects that require polished, unique iconography.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Where to Download&lt;/strong&gt;: You can browse Adobe Stock icons at &lt;a href=&quot;https://stock.adobe.com&quot;&gt;Adobe Stock’s official site&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;envato-elements&quot;&gt;&lt;strong&gt;Envato Elements&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Envato Elements&lt;/strong&gt; is another popular marketplace for premium design resources, including icons. It offers a subscription-based model where you get access to all design assets, including icons, under a commercial license.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;License&lt;/strong&gt;: The assets on Envato Elements are available under their own commercial license, which allows unlimited use in projects but does not allow redistribution of the icons.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Use Case&lt;/strong&gt;: Suitable for businesses and developers looking for exclusive, high-quality design assets for client work or branding purposes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Where to Download&lt;/strong&gt;: Visit &lt;a href=&quot;https://elements.envato.com&quot;&gt;Envato Elements&lt;/a&gt; for more information.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Choosing the right icon set depends on your project requirements and licensing preferences:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Truly Open-Source&lt;/strong&gt; icons like Adwaita and Material Design Icons offer the most flexibility for open-source projects, allowing full use and modification with permissive licenses.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;“With Attribution”&lt;/strong&gt; platforms like Flaticon and Freepik offer vast collections of free icons but require giving credit to the creators.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Proprietary Options&lt;/strong&gt; like Adobe Stock and Envato Elements are ideal for high-quality, professional-grade icons, but come with stricter usage rights and licensing fees.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By carefully selecting the right source for your icons, you can enhance your web development projects while adhering to legal and ethical guidelines.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;sources&quot;&gt;Sources&lt;/h3&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://gitlab.gnome.org/GNOME/adwaita-icon-theme&quot;&gt;GNOME Adwaita Icons GitLab&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/google/material-design-icons&quot;&gt;Material Design Icons GitHub&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://fontawesome.com&quot;&gt;Font Awesome&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.flaticon.com&quot;&gt;Flaticon&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.freepik.com&quot;&gt;Freepik&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://icons8.com&quot;&gt;Icons8&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stock.adobe.com&quot;&gt;Adobe Stock&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://elements.envato.com&quot;&gt;Envato Elements&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content>
 </entry>
 
 <entry>
   <title>Your go-to guide to open source forums: where developers collaborate and innovate</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/guide-to-foss-forums.html"/>
   <updated>2024-10-17T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/guide-to-foss-forums</id>
   <content type="html">&lt;p&gt;In the ever-evolving world of technology, open source software stands out as a beacon of collaboration, creativity, and community spirit. As developers dive into the realm of open source, forums play a crucial role in facilitating discussions, sharing knowledge, and fostering innovation. This guide will explore the significance of open source forums, the best platforms to engage with, and tips on how to make the most of these collaborative spaces.&lt;/p&gt;

&lt;h2 id=&quot;why-open-source-forums-matter&quot;&gt;Why Open Source Forums Matter&lt;/h2&gt;

&lt;p&gt;Open source forums serve as virtual meeting grounds where developers, contributors, and enthusiasts can come together to share ideas, troubleshoot problems, and collaborate on projects. Here are several reasons why these forums are vital to the open source ecosystem:&lt;/p&gt;

&lt;h3 id=&quot;1-community-support&quot;&gt;1. &lt;strong&gt;Community Support&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Forums offer a platform for users to seek help and advice. Whether you’re a beginner grappling with your first code or an experienced developer facing a complex issue, forums connect you with a supportive community  .&lt;/p&gt;

&lt;h3 id=&quot;2-knowledge-sharing&quot;&gt;2. &lt;strong&gt;Knowledge Sharing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Developers often share tutorials, documentation, and resources in forums, which helps others learn and grow. This culture of sharing is fundamental to the success of open source projects .&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/community.jpg&quot; alt=&quot;community&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;3-networking-opportunities&quot;&gt;3. &lt;strong&gt;Networking Opportunities&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Open source forums allow you to connect with like-minded individuals, potential collaborators, and industry professionals. These connections can lead to job opportunities, mentorship, and partnerships  .&lt;/p&gt;

&lt;h3 id=&quot;4-collaboration-and-innovation&quot;&gt;4. &lt;strong&gt;Collaboration and Innovation&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Many open source projects thrive on community contributions. Forums often serve as the starting point for collaboration, where ideas can be discussed and implemented, leading to innovative solutions  .&lt;/p&gt;

&lt;h2 id=&quot;top-open-source-forums-to-join&quot;&gt;Top Open Source Forums to Join&lt;/h2&gt;

&lt;p&gt;As you embark on your journey into the open source world, consider joining these well-established forums where developers collaborate and innovate:&lt;/p&gt;

&lt;h3 id=&quot;1-stack-overflow&quot;&gt;1. &lt;strong&gt;&lt;a href=&quot;https://stackoverflow.com&quot;&gt;Stack Overflow&lt;/a&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: Stack Overflow is one of the largest Q&amp;amp;A platforms for developers. It covers a vast range of programming languages and technologies, making it a go-to resource for troubleshooting and learning.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why Join?&lt;/strong&gt;: With a robust tagging system, you can find questions and answers related to specific open source projects, frameworks, and programming languages. Contributing to discussions enhances your reputation and visibility within the community  .&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;2-reddit&quot;&gt;2. &lt;strong&gt;&lt;a href=&quot;https://www.reddit.com&quot;&gt;Reddit&lt;/a&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: Reddit hosts a plethora of subreddits dedicated to open source software and development, such as r/opensource, r/Python, and r/programming.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why Join?&lt;/strong&gt;: Reddit’s informal nature encourages open discussions, idea sharing, and project promotion. Engaging in subreddit communities allows you to connect with fellow enthusiasts and learn from diverse perspectives .&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;3-github-discussions&quot;&gt;3. &lt;strong&gt;&lt;a href=&quot;https://github.com&quot;&gt;GitHub Discussions&lt;/a&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: GitHub Discussions is a feature integrated into GitHub repositories, enabling users to ask questions, share ideas, and discuss features related to specific projects.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why Join?&lt;/strong&gt;: This forum provides direct access to the project maintainers and contributors. It’s an ideal space for brainstorming and providing feedback on ongoing projects .&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;4-opensourcecom&quot;&gt;4. &lt;strong&gt;&lt;a href=&quot;https://opensource.com&quot;&gt;OpenSource.com&lt;/a&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: OpenSource.com is a platform dedicated to sharing stories, resources, and insights about open source. It features a vibrant community of writers and contributors.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why Join?&lt;/strong&gt;: By engaging with articles and participating in discussions, you can stay updated on open source trends and contribute your insights, fostering a richer understanding of the ecosystem .&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;5-the-linux-foundation-forums&quot;&gt;5. &lt;strong&gt;&lt;a href=&quot;https://forum.linuxfoundation.org&quot;&gt;The Linux Foundation Forums&lt;/a&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: The Linux Foundation hosts forums that cater specifically to Linux users and developers. These forums are a valuable resource for discussions on Linux-based projects and initiatives.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why Join?&lt;/strong&gt;: Being part of the Linux Foundation community provides access to valuable resources, training, and networking opportunities with industry leaders .&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;6-devto&quot;&gt;6. &lt;strong&gt;&lt;a href=&quot;https://dev.to&quot;&gt;Dev.to&lt;/a&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: Dev.to is a community of software developers who share articles, tutorials, and insights. It fosters a friendly and inclusive environment for learning and collaboration.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why Join?&lt;/strong&gt;: The platform encourages you to write and share your experiences, while also engaging with others through comments and discussions. This can lead to valuable feedback and new connections .&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;7-sourceforge-community-forums&quot;&gt;7. &lt;strong&gt;&lt;a href=&quot;https://sourceforge.net&quot;&gt;SourceForge Community Forums&lt;/a&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: SourceForge, one of the original platforms for hosting open source projects, offers community forums for discussing projects, features, and best practices.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why Join?&lt;/strong&gt;: Engaging in SourceForge forums allows you to connect with project maintainers and users, helping you learn more about specific tools and how to contribute effectively .&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;8-lobsters&quot;&gt;8. &lt;strong&gt;&lt;a href=&quot;https://lobste.rs&quot;&gt;Lobsters&lt;/a&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: Lobsters is a community-driven link aggregation site focused on software development and technology, including open-source projects.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why Join?&lt;/strong&gt;: Its smaller, more focused community allows for high-quality discussions on technical topics, including open-source software and development tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;9-freecodecamp-forum&quot;&gt;9. &lt;strong&gt;&lt;a href=&quot;https://forum.freecodecamp.org&quot;&gt;FreeCodeCamp Forum&lt;/a&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: FreeCodeCamp is a community-driven platform offering coding tutorials, certification courses, and a large forum for learning open-source development.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why Join?&lt;/strong&gt;: Ideal for beginners, FreeCodeCamp allows users to discuss their learning journeys, contribute to open-source projects, and seek guidance from experienced developers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;10-gitter&quot;&gt;10. &lt;strong&gt;&lt;a href=&quot;https://gitter.im&quot;&gt;Gitter&lt;/a&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: Gitter is an open-source chat platform designed for communities to discuss software development. It’s commonly used for real-time collaboration within GitHub projects.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why Join?&lt;/strong&gt;: Gitter offers focused discussions around specific open-source projects, helping users to engage directly with maintainers and contributors in a chat-like setting.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;11-geeksmint-forums&quot;&gt;11. &lt;strong&gt;&lt;a href=&quot;https://www.geeksmint.com&quot;&gt;Geeksmint Forums&lt;/a&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: Geeksmint is a platform focused on free and open-source software (FOSS) news and tutorials, and it offers community forums for in-depth discussions.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why Join?&lt;/strong&gt;: It’s an excellent resource for discovering lesser-known open-source tools and getting advice from a diverse and global FOSS community.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;12-discourse-meta&quot;&gt;12. &lt;strong&gt;&lt;a href=&quot;https://meta.discourse.org&quot;&gt;Discourse Meta&lt;/a&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: Discourse Meta is a community discussion platform built with Discourse. It’s commonly used by open-source developers to discuss feature requests, bug reports, and project enhancements.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why Join?&lt;/strong&gt;: Many open-source projects use Discourse as their primary discussion forum, so joining allows you to engage directly with various project communities.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;13-hacker-news&quot;&gt;13. &lt;strong&gt;&lt;a href=&quot;https://news.ycombinator.com&quot;&gt;Hacker News&lt;/a&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: Hacker News is a social news site focused on technology and startups. While not strictly a forum, it has a strong developer community and is known for insightful discussions about open-source trends and tools.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why Join?&lt;/strong&gt;: Participating in discussions on Hacker News can give you access to cutting-edge insights and opportunities to connect with thought leaders in the tech industry.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;14-opensuse-forums&quot;&gt;14. &lt;strong&gt;&lt;a href=&quot;https://forums.opensuse.org&quot;&gt;OpenSUSE Forums&lt;/a&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: OpenSUSE, a popular Linux distribution, offers a forum for users and developers to discuss its ecosystem and open-source software in general.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why Join?&lt;/strong&gt;: This community is ideal for Linux enthusiasts looking to engage with a dedicated group of open-source software users and developers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;15-mozilla-discourse&quot;&gt;15. &lt;strong&gt;&lt;a href=&quot;https://discourse.mozilla.org&quot;&gt;Mozilla Discourse&lt;/a&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: Mozilla, the organization behind Firefox, provides a Discourse forum for users and developers to discuss its various open-source projects, from browsers to privacy tools.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why Join?&lt;/strong&gt;: It’s an opportunity to directly influence one of the most prominent open-source organizations and engage with Mozilla’s developer community.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;16-apache-software-foundation-mailing-lists&quot;&gt;16. &lt;strong&gt;&lt;a href=&quot;https://lists.apache.org&quot;&gt;Apache Software Foundation Mailing Lists&lt;/a&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Overview&lt;/strong&gt;: The Apache Software Foundation offers mailing lists that function as forums for open-source projects under its umbrella, including Hadoop, Spark, and HTTP Server.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why Join?&lt;/strong&gt;: Mailing lists are an excellent way to stay up-to-date on project developments, seek help, and contribute to Apache’s many open-source initiatives.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-to-make-the-most-of-open-source-forums&quot;&gt;How to Make the Most of Open Source Forums&lt;/h2&gt;

&lt;p&gt;Participating in open source forums can be immensely rewarding, but it requires some effort to maximize your experience. Here are some tips to help you engage effectively:&lt;/p&gt;

&lt;h3 id=&quot;1-be-respectful-and-constructive&quot;&gt;1. &lt;strong&gt;Be Respectful and Constructive&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;When engaging in discussions, always maintain a respectful tone. Constructive criticism and positive feedback foster a healthy community atmosphere.&lt;/p&gt;

&lt;h3 id=&quot;2-ask-thoughtful-questions&quot;&gt;2. &lt;strong&gt;Ask Thoughtful Questions&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;If you’re seeking help, ensure your questions are clear and specific. Include relevant details, such as error messages and the context of your problem. This makes it easier for others to assist you.&lt;/p&gt;

&lt;h3 id=&quot;3-contribute-to-discussions&quot;&gt;3. &lt;strong&gt;Contribute to Discussions&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Don’t hesitate to share your knowledge or experiences. Contributing to discussions enhances your reputation and helps others in the community.&lt;/p&gt;

&lt;h3 id=&quot;4-follow-forum-etiquette&quot;&gt;4. &lt;strong&gt;Follow Forum Etiquette&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Familiarize yourself with each forum’s rules and guidelines. This ensures your contributions are well-received and align with community standards.&lt;/p&gt;

&lt;h3 id=&quot;5-stay-active-and-engaged&quot;&gt;5. &lt;strong&gt;Stay Active and Engaged&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Regularly participate in discussions, comment on articles, and follow up on questions you’ve asked. Staying active increases your visibility and fosters connections.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Open source forums are invaluable resources for developers at all skill levels. They provide a platform for collaboration, learning, and innovation that shapes the future of software development. By engaging with these communities, you’ll not only enhance your own skills but also contribute to a vibrant ecosystem that thrives on shared knowledge and collaboration.&lt;/p&gt;

&lt;p&gt;So, whether you’re troubleshooting an issue, seeking advice, or sharing your insights, dive into the world of open source forums. Join the conversation, connect with fellow developers, and become an integral part of the open source movement!&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;sources&quot;&gt;Sources:&lt;/h3&gt;
&lt;ol&gt;
  &lt;li&gt;Stack Overflow: &lt;a href=&quot;https://stackoverflow.com&quot;&gt;https://stackoverflow.com&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;OpenSource.com: &lt;a href=&quot;https://opensource.com&quot;&gt;https://opensource.com&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;The Linux Foundation: &lt;a href=&quot;https://linuxfoundation.org&quot;&gt;https://linuxfoundation.org&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Reddit: &lt;a href=&quot;https://www.reddit.com&quot;&gt;https://www.reddit.com&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;GitHub Discussions: &lt;a href=&quot;https://github.com&quot;&gt;https://github.com&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Dev.to: &lt;a href=&quot;https://dev.to&quot;&gt;https://dev.to&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;SourceForge: &lt;a href=&quot;https://sourceforge.net&quot;&gt;https://sourceforge.net&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Lobsters: &lt;a href=&quot;https://lobste.rs&quot;&gt;https://lobste.rs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;FreeCodeCamp Forum: &lt;a href=&quot;https://forum.freecodecamp.org&quot;&gt;https://forum.freecodecamp.org&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Gitter: &lt;a href=&quot;https://gitter.im&quot;&gt;https://gitter.im&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Geeksmint Forums: &lt;a href=&quot;https://www.geeksmint.com/&quot;&gt;https://www.geeksmint.com/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Discourse Meta: &lt;a href=&quot;https://meta.discourse.org&quot;&gt;https://meta.discourse.org&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Hacker News: &lt;a href=&quot;https://news.ycombinator.com&quot;&gt;https://news.ycombinator.com&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;OpenSUSE Forums: &lt;a href=&quot;https://forums.opensuse.org&quot;&gt;https://forums.opensuse.org&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Mozilla Discourse: &lt;a href=&quot;https://discourse.mozilla.org&quot;&gt;https://discourse.mozilla.org&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Apache Software Foundation Mailing Lists: &lt;a href=&quot;https://lists.apache.org&quot;&gt;https://lists.apache.org&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content>
 </entry>
 
 <entry>
   <title>Open source in education: empowering students and teachers</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/open-source-in-education-empowering-students-teachers.html"/>
   <updated>2024-10-16T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/open-source-in-education-empowering-students-teachers</id>
   <content type="html">&lt;p&gt;In the digital age, technology has become an indispensable tool in education. Open-source software, with its free, accessible, and collaborative nature, offers a powerful means to revolutionize teaching and learning. This article delves into the ways in which open-source solutions can empower both students and teachers, fostering a more equitable, innovative, and personalized educational experience.&lt;/p&gt;

&lt;h3 id=&quot;empowering-students-fostering-creativity-and-critical-thinking&quot;&gt;Empowering Students: Fostering Creativity and Critical Thinking&lt;/h3&gt;

&lt;p&gt;Open-source software provides students with a unique opportunity to engage with technology on a deeper level. By interacting with open-source projects, students can:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Develop Problem-Solving Skills:&lt;/strong&gt; Open-source software often requires users to troubleshoot issues and find solutions. This process helps students develop essential problem-solving skills that are invaluable in both academic and professional settings.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Cultivate Critical Thinking:&lt;/strong&gt; The open-source model encourages students to question and analyze technology. By understanding how software works, students can develop a critical perspective on its use and limitations.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Nurture Creativity:&lt;/strong&gt; Open-source platforms offer a space for students to experiment and innovate. They can contribute to existing projects, create their own, or customize existing software to suit their needs. This fosters creativity and encourages students to think outside the box.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Learn Collaborative Skills:&lt;/strong&gt; Open-source development often involves collaboration with others from around the world. This provides students with opportunities to learn how to work effectively in teams, communicate effectively, and respect diverse perspectives.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;empowering-teachers-enhancing-teaching-and-learning&quot;&gt;Empowering Teachers: Enhancing Teaching and Learning&lt;/h3&gt;

&lt;p&gt;Open-source software can also significantly enhance the teaching experience. By leveraging open-source tools, teachers can:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Create Personalized Learning Experiences:&lt;/strong&gt; Open-source software can be customized to meet the specific needs of individual students. Teachers can create differentiated assignments, provide targeted support, and offer personalized learning paths.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Access High-Quality Educational Resources:&lt;/strong&gt; A vast array of open-source educational resources, including textbooks, lesson plans, and multimedia content, are available online. This can help teachers save time and resources while ensuring that students have access to high-quality materials.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Foster a Culture of Innovation:&lt;/strong&gt; Open-source software encourages experimentation and innovation. Teachers can use open-source tools to develop new teaching methods, create engaging activities, and explore innovative approaches to learning.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Reduce Costs:&lt;/strong&gt; Open-source software is often free or low-cost, making it a more affordable option for schools and educators. This can help to reduce the financial burden on schools and ensure that all students have access to necessary technology.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/books.jpg&quot; alt=&quot;books&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;case-studies-successful-open-source-initiatives-in-education&quot;&gt;Case Studies: Successful Open-Source Initiatives in Education&lt;/h3&gt;

&lt;p&gt;Several successful open-source initiatives have demonstrated the transformative potential of open-source software in education. Some notable examples include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Khan Academy:&lt;/strong&gt; This popular online learning platform uses open-source technology to provide free, world-class education to anyone, anywhere. Khan Academy’s open-source approach has enabled it to reach millions of students around the globe and has inspired countless other educational initiatives.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sugar Labs:&lt;/strong&gt; Sugar Labs develops the Sugar interface, which is used in the XO laptop, a low-cost, rugged laptop designed for education. Sugar Labs’ open-source approach has made it possible to provide affordable computing devices and educational software to students in developing countries.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Mozilla Foundation:&lt;/strong&gt; Mozilla develops open-source software, such as Firefox, that is used by millions of students and educators worldwide. Mozilla’s commitment to open-source development has helped to create a more open and accessible internet, empowering users to learn and collaborate freely.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;challenges-and-opportunities&quot;&gt;Challenges and Opportunities&lt;/h3&gt;

&lt;p&gt;While open-source software offers numerous benefits, there are also challenges to consider. These include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Technical Support:&lt;/strong&gt; Ensuring adequate technical support for open-source software in educational settings can be a challenge, especially for schools with limited IT resources.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Data Privacy and Security:&lt;/strong&gt; Protecting student data and ensuring the security of open-source software is a critical concern. Schools must implement appropriate security measures to safeguard sensitive information.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Integration with Existing Systems:&lt;/strong&gt; Integrating open-source tools with existing school infrastructure can be time-consuming and complex. Schools may need to invest in professional services or develop custom solutions to ensure compatibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Despite these challenges, the potential benefits of open-source software in education far outweigh the drawbacks. By embracing open-source solutions, schools and educators can create more equitable, innovative, and personalized learning environments for students.&lt;/p&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;Open-source software has the power to transform education by empowering students and teachers. By providing access to free, customizable, and collaborative tools, open-source can foster creativity, critical thinking, and a lifelong love of learning. As technology continues to evolve, the role of open-source in education will only become more important. By embracing open-source, schools and educators can help to create a brighter future for students around the world.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.khanacademy.org/&quot;&gt;Khan Academy&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sugarlabs.org/&quot;&gt;Sugar Labs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.mozilla.org/&quot;&gt;Mozilla Foundation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://opensource.org/&quot;&gt;Open Source Initiative&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Creating magic with code: simple PHP programs for beginners</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/simple-php-programs-for-beginners.html"/>
   <updated>2024-10-15T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/simple-php-programs-for-beginners</id>
   <content type="html">&lt;p&gt;Welcome to the enchanting world of PHP! If you’ve ever dreamed of crafting your own web applications, managing databases, or simply adding a dash of interactivity to your website, you’ve stumbled upon the right place. In this article, we’ll explore some simple yet magical PHP programs that will serve as a fantastic introduction to this versatile scripting language. So, roll up your sleeves, and let’s dive in!&lt;/p&gt;

&lt;h2 id=&quot;what-is-php&quot;&gt;What is PHP?&lt;/h2&gt;

&lt;p&gt;PHP, which stands for &lt;strong&gt;Hypertext Preprocessor&lt;/strong&gt;, is a server-side scripting language designed primarily for web development. It allows you to create dynamic content that can interact with databases, handle forms, and manage sessions, making it an essential tool in a web developer’s toolkit.&lt;/p&gt;

&lt;p&gt;But wait—what makes PHP so appealing to beginners? Here are a few reasons:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Easy to Learn&lt;/strong&gt;: With its straightforward syntax, even novices can grasp the basics quickly.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Versatile&lt;/strong&gt;: PHP can be used for everything from simple websites to complex web applications.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Open Source&lt;/strong&gt;: Being free and widely supported means you’ll find plenty of resources and community help along the way.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;getting-started-with-php&quot;&gt;Getting Started with PHP&lt;/h3&gt;

&lt;p&gt;Before we jump into some cool programs, let’s ensure you have the essentials set up. You’ll need a local server environment to run your PHP code. &lt;strong&gt;XAMPP&lt;/strong&gt; and &lt;strong&gt;MAMP&lt;/strong&gt; are excellent options that allow you to create a local web server on your computer.&lt;/p&gt;

&lt;p&gt;Once installed, place your PHP files in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;htdocs&lt;/code&gt; directory (XAMPP) or the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;htdocs&lt;/code&gt; equivalent (MAMP) to run them. Now, let’s explore some beginner-friendly programs!&lt;/p&gt;

&lt;h2 id=&quot;1-hello-world&quot;&gt;1. Hello, World!&lt;/h2&gt;

&lt;p&gt;Let’s start with the classic &lt;strong&gt;Hello, World!&lt;/strong&gt; program. It’s a rite of passage for any programmer.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Hello, World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;explanation&quot;&gt;Explanation&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;?php ... ?&amp;gt;&lt;/code&gt;: This tag tells the server that the code inside is PHP.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo&lt;/code&gt;: This function outputs the text to the browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;what-you-learn&quot;&gt;What You Learn&lt;/h3&gt;

&lt;p&gt;This simple program introduces you to the PHP syntax and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo&lt;/code&gt; function.&lt;/p&gt;

&lt;h2 id=&quot;2-basic-calculator&quot;&gt;2. Basic Calculator&lt;/h2&gt;

&lt;p&gt;Next up, we’ll create a basic calculator that can perform addition, subtraction, multiplication, and division.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calculator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$num1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$num2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;add&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$num1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$num2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;subtract&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$num1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$num2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;multiply&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$num1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$num2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;divide&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$num2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$num1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$num2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Cannot divide by zero&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Invalid operation&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Example Usage&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;calculator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;add&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Outputs: 15&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;explanation-1&quot;&gt;Explanation&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Function&lt;/strong&gt;: The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;calculator&lt;/code&gt; function takes two numbers and an operation as parameters and returns the result based on the specified operation.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Switch Case&lt;/strong&gt;: This structure helps to execute different code blocks based on the value of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$operation&lt;/code&gt; variable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;what-you-learn-1&quot;&gt;What You Learn&lt;/h3&gt;

&lt;p&gt;You’ll gain insights into functions, control structures, and how to handle basic arithmetic operations in PHP.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/code-php-3.jpg&quot; alt=&quot;php code&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;3-form-handling&quot;&gt;3. Form Handling&lt;/h2&gt;

&lt;p&gt;Now, let’s create a simple web form that collects user input. We’ll build a contact form that captures the user’s name and email.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Contact Form&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;post&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    Name: &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;
    Email: &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;email&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;email&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Submit&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;

&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REQUEST_METHOD&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;htmlspecialchars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;htmlspecialchars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;email&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Thank you, &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;! Your email, &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, has been received.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;explanation-2&quot;&gt;Explanation&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Form&lt;/strong&gt;: The form uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST&lt;/code&gt; method to send data to the same script for processing.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;HTML Special Characters&lt;/strong&gt;: The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;htmlspecialchars&lt;/code&gt; function prevents XSS attacks by escaping special characters.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;what-you-learn-2&quot;&gt;What You Learn&lt;/h3&gt;

&lt;p&gt;This program introduces form handling, user input validation, and secure output in PHP.&lt;/p&gt;

&lt;h2 id=&quot;4-array-manipulation&quot;&gt;4. Array Manipulation&lt;/h2&gt;

&lt;p&gt;Let’s dive into arrays—a fundamental aspect of PHP programming. We’ll create a simple program that sorts an array of numbers.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$numbers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Sorted numbers: &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$numbers&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$number&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;explanation-3&quot;&gt;Explanation&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Array&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$numbers&lt;/code&gt; is an array that contains a list of integers.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sort Function&lt;/strong&gt;: The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sort&lt;/code&gt; function organizes the array in ascending order.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Foreach Loop&lt;/strong&gt;: This loop iterates over each element in the array.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;what-you-learn-3&quot;&gt;What You Learn&lt;/h3&gt;

&lt;p&gt;You’ll discover how to work with arrays, sort them, and loop through their elements.&lt;/p&gt;

&lt;h2 id=&quot;5-session-management&quot;&gt;5. Session Management&lt;/h2&gt;

&lt;p&gt;Sessions allow you to maintain user data across different pages. Let’s create a simple login system using sessions.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;session_start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REQUEST_METHOD&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$_SESSION&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;username&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;htmlspecialchars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;username&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Welcome, &quot;&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_SESSION&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;username&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;

&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Login&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;post&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    Username: &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;username&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Login&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;explanation-4&quot;&gt;Explanation&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Session Start&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;session_start()&lt;/code&gt; initializes a new session or resumes the existing one.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Storing Data&lt;/strong&gt;: User input is stored in the session, allowing you to access it on other pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;what-you-learn-4&quot;&gt;What You Learn&lt;/h3&gt;

&lt;p&gt;You’ll grasp the concept of sessions and how they help maintain state in web applications.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Congratulations! You’ve just scratched the surface of what PHP can do. From simple programs like a calculator to managing user sessions, these examples should give you a solid foundation in PHP.&lt;/p&gt;

&lt;p&gt;Remember, coding is like magic; the more you practice, the more powerful your spells (or programs) will become. Keep experimenting, building, and pushing the boundaries of your creativity. Who knows? One day, you might just conjure up the next big web application!&lt;/p&gt;

&lt;p&gt;Now, go forth and create some magic with code!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Python tutorial for beginners: learn the basics</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/python-tutorial-beginners.html"/>
   <updated>2024-10-14T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/python-tutorial-beginners</id>
   <content type="html">&lt;p&gt;Welcome to the wonderful world of Python programming! If you’re new to coding, buckle up because Python is one of the easiest, yet most powerful, languages out there. Whether you’re looking to automate tedious tasks, build web apps, or dive into data science, Python is your gateway to coding success.&lt;/p&gt;

&lt;p&gt;In this beginner’s guide, we’ll walk you through the essential building blocks of Python, making sure you’re ready to tackle more advanced projects in no time. Let’s get started!&lt;/p&gt;

&lt;h3 id=&quot;why-python&quot;&gt;Why Python?&lt;/h3&gt;

&lt;p&gt;Before we jump into the nuts and bolts, let’s talk about why Python is an excellent choice for beginners.&lt;/p&gt;

&lt;p&gt;Imagine coding as learning to drive. Python is like the automatic transmission of programming languages — smooth, easy, and efficient. You don’t need to worry about complicated syntax rules or puzzling error messages. Its straightforward design allows you to focus on learning how to &lt;em&gt;think&lt;/em&gt; like a programmer, not battling the language itself.&lt;/p&gt;

&lt;h3 id=&quot;installing-python-your-first-step&quot;&gt;Installing Python: Your First Step&lt;/h3&gt;

&lt;p&gt;First things first — you need to install Python on your machine. Go to &lt;a href=&quot;https://www.python.org&quot;&gt;python.org&lt;/a&gt; and download the latest version (Python 3 is recommended). Installation is a breeze, and you’ll have Python running on your machine within minutes. Once installed, open your terminal or command prompt and type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python --version&lt;/code&gt; to ensure everything’s set up.&lt;/p&gt;

&lt;h3 id=&quot;your-first-python-program&quot;&gt;Your First Python Program&lt;/h3&gt;

&lt;p&gt;Every programmer’s journey begins with the iconic “Hello, World!” program. It’s the software equivalent of a warm hug. Open your favorite code editor (you can use a simple text editor, or for a more professional feel, try IDEs like PyCharm or VS Code), and type:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello, World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Run your script, and you should see that familiar greeting pop up on your screen. Congratulations! You just wrote your first Python program. Now, let’s build on that momentum.&lt;/p&gt;

&lt;h3 id=&quot;variables-pythons-building-blocks&quot;&gt;Variables: Python’s Building Blocks&lt;/h3&gt;

&lt;p&gt;Variables are like little containers that hold data. In Python, creating a variable is as simple as assigning a value to a name:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;John&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;is_student&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;No need to declare the type of the variable (whether it’s a number, a string, or a boolean) — Python figures it out for you. It’s as if Python is a mind reader, but with code.&lt;/p&gt;

&lt;p&gt;You can manipulate these variables however you like:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;is&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;years old.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Python allows you to easily print out these values and even combine them in a readable format. This is one of the language’s best features — simplicity without sacrificing power.&lt;/p&gt;

&lt;h3 id=&quot;data-types-in-python&quot;&gt;Data Types in Python&lt;/h3&gt;

&lt;p&gt;Python uses several basic data types, each suited for a different kind of task. Here are the most common ones you’ll encounter:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Integers&lt;/strong&gt;: Whole numbers (e.g., 10, 42, -3)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Floats&lt;/strong&gt;: Numbers with decimal points (e.g., 3.14, 0.99)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Strings&lt;/strong&gt;: A sequence of characters (e.g., “Hello”, “Python”)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Booleans&lt;/strong&gt;: True or False values (e.g., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;True&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;False&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each data type has its unique properties, and as you grow more experienced, you’ll start recognizing when and how to use them effectively.&lt;/p&gt;

&lt;h3 id=&quot;lists-grouping-data-together&quot;&gt;Lists: Grouping Data Together&lt;/h3&gt;

&lt;p&gt;Lists are one of Python’s most versatile data structures. Think of a list as a collection of items (just like your shopping list) that you can easily manipulate:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;fruits&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;apple&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;banana&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cherry&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can access individual items using an index:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fruits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# This will print &quot;apple&quot;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And you can even add or remove items from the list:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;fruits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;orange&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Adds &quot;orange&quot; to the list
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fruits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;banana&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Removes &quot;banana&quot; from the list
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Lists can grow and shrink dynamically, and they’re perfect for scenarios where you need to store multiple values.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/code-python.jpg&quot; alt=&quot;code-python&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;control-flow-if-else-statements&quot;&gt;Control Flow: If-Else Statements&lt;/h3&gt;

&lt;p&gt;Now that we’ve got data, what if we want our program to make decisions? This is where conditional statements like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;else&lt;/code&gt; come into play.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;18&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;You’re an adult.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;You’re still a minor.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Python’s syntax for conditions is as readable as it gets. No need for excessive punctuation — just a clean, human-readable format that tells you exactly what’s going on.&lt;/p&gt;

&lt;h3 id=&quot;loops-repeating-actions&quot;&gt;Loops: Repeating Actions&lt;/h3&gt;

&lt;p&gt;Repetition is a key part of programming. Loops allow you to repeat certain actions without writing the same code over and over.&lt;/p&gt;

&lt;h4 id=&quot;for-loop&quot;&gt;For Loop&lt;/h4&gt;

&lt;p&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for&lt;/code&gt; loop lets you iterate over a sequence (like a list or a range of numbers):&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fruit&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fruits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fruit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will print each fruit in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fruits&lt;/code&gt; list, one by one.&lt;/p&gt;

&lt;h4 id=&quot;while-loop&quot;&gt;While Loop&lt;/h4&gt;

&lt;p&gt;If you want to repeat something as long as a condition is true, use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;while&lt;/code&gt; loop:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# This increases count by 1 each time
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This loop runs until the condition (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;count &amp;lt; 5&lt;/code&gt;) is no longer true. It’s perfect for situations where the number of iterations isn’t fixed ahead of time.&lt;/p&gt;

&lt;h3 id=&quot;functions-reusing-code&quot;&gt;Functions: Reusing Code&lt;/h3&gt;

&lt;p&gt;Functions are your way of bundling code into reusable blocks. Instead of writing the same code over and over, you can create a function and call it whenever needed:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;greet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello, &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;greet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Alice&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# This will print &quot;Hello, Alice&quot;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;greet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Bob&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# This will print &quot;Hello, Bob&quot;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Functions are the secret to writing clean, efficient code. They also make your programs easier to read and maintain.&lt;/p&gt;

&lt;h3 id=&quot;user-input-making-your-program-interactive&quot;&gt;User Input: Making Your Program Interactive&lt;/h3&gt;

&lt;p&gt;Why keep all the fun to yourself? Let’s make your program interactive by allowing users to input data.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Enter your name: &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello, &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This little script prompts the user for their name and responds with a personalized greeting. It’s a great way to make your programs feel dynamic and responsive.&lt;/p&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;Congratulations, you’ve just dipped your toes into the basics of Python programming! From writing your first lines of code to understanding variables, lists, loops, and functions, you’ve got a solid foundation to build on.&lt;/p&gt;

&lt;p&gt;The beauty of Python is that it grows with you. Whether you’re building simple scripts or diving into complex data science projects, Python’s intuitive design and versatility will keep you hooked. So keep practicing, keep experimenting, and soon enough, you’ll be writing programs that solve real-world problems.&lt;/p&gt;

&lt;p&gt;Welcome to the world of Python — happy coding!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>LibreOffice Calc for beginners: a complete guide to spreadsheets</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/libreoffice-calc-for-beginners.html"/>
   <updated>2024-10-13T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/libreoffice-calc-for-beginners</id>
   <content type="html">&lt;p&gt;Are you new to &lt;strong&gt;LibreOffice Calc&lt;/strong&gt; and eager to master spreadsheets? Don’t worry—you’re in the right place! Whether you’re a student, professional, or someone who just wants to organize their life better, LibreOffice Calc has got your back. It’s not just an alternative to Excel; it’s a powerful, open-source tool that can help you tackle everything from simple data tracking to complex calculations. Ready to dive in? Let’s break it down, step-by-step!&lt;/p&gt;

&lt;h3 id=&quot;what-is-libreoffice-calc&quot;&gt;What Is LibreOffice Calc?&lt;/h3&gt;

&lt;p&gt;LibreOffice Calc is part of the LibreOffice suite, a free and open-source software that offers everything you’d expect from an office suite: word processing, presentations, and, of course, spreadsheets. Calc is comparable to Excel, but what sets it apart is that it’s completely &lt;strong&gt;free&lt;/strong&gt; and backed by a strong community of developers from The Document Foundation.&lt;/p&gt;

&lt;p&gt;But don’t be fooled by its price tag—or lack thereof. LibreOffice Calc is a &lt;strong&gt;heavyweight&lt;/strong&gt; in the world of spreadsheets, and once you get a handle on its features, you’ll realize just how powerful it can be. Plus, it works on all platforms: &lt;strong&gt;Windows, macOS, Linux&lt;/strong&gt;, and even &lt;strong&gt;Android&lt;/strong&gt;!&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;getting-started-installing-and-setting-up-libreoffice-calc&quot;&gt;Getting Started: Installing and Setting Up LibreOffice Calc&lt;/h3&gt;

&lt;p&gt;If you haven’t installed LibreOffice Calc yet, don’t sweat it. Head over to &lt;a href=&quot;https://www.libreoffice.org/download/download/&quot;&gt;LibreOffice’s official site&lt;/a&gt; and download the latest version for your operating system. Once installed, simply open Calc from the LibreOffice suite, and you’re ready to roll.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Windows users&lt;/strong&gt;: Works seamlessly on Windows 10, 11, and even older versions like Windows 7.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Mac users&lt;/strong&gt;: LibreOffice Calc for Mac runs as smooth as butter, whether you’re on macOS Big Sur or Monterey.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Linux users&lt;/strong&gt;: If you’re a Linux enthusiast, you’re in luck—LibreOffice Calc integrates effortlessly with most Linux distributions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, with Calc installed and ready, let’s tackle some of the basics.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;navigating-the-interface&quot;&gt;Navigating the Interface&lt;/h3&gt;

&lt;p&gt;LibreOffice Calc’s interface is clean and user-friendly. You’ll find all the essential tools at the top, just like you would in Excel.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Toolbars and Menus&lt;/strong&gt;: These are packed with features, from basic formatting to complex data analysis tools. The most commonly used options are easily accessible.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Cells, Rows, and Columns&lt;/strong&gt;: Just like Excel, Calc’s main workspace consists of cells arranged in rows and columns. The rows are numbered, and the columns are labeled with letters.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Formula Bar&lt;/strong&gt;: This is where you enter your calculations and formulas. Click on a cell, type a formula, and hit Enter to calculate it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/work-desk.jpg&quot; alt=&quot;work-desk&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;creating-your-first-spreadsheet&quot;&gt;Creating Your First Spreadsheet&lt;/h3&gt;

&lt;p&gt;Let’s start small. Imagine you’re creating a simple budget tracker for personal use. Open up a new sheet and follow these steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Column Titles&lt;/strong&gt;: In Row 1, enter titles like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Date&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Description&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Category&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Amount&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Balance&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Data Entry&lt;/strong&gt;: Start entering your expenses in the respective columns. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2024-10-01 | Rent | Housing | 500&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Formulas&lt;/strong&gt;: To keep a running total of your balance, click on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Balance&lt;/code&gt; cell in the first row and type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=SUM(D2:D2)&lt;/code&gt;. Drag the formula down the column to apply it to all rows.&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;key-features-every-beginner-should-know&quot;&gt;Key Features Every Beginner Should Know&lt;/h3&gt;

&lt;p&gt;Here are some essential features to master as you get started with LibreOffice Calc:&lt;/p&gt;

&lt;h4 id=&quot;1-formulas-and-functions&quot;&gt;1. &lt;strong&gt;Formulas and Functions&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;This is where spreadsheets shine! LibreOffice Calc supports a variety of &lt;strong&gt;functions&lt;/strong&gt; like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SUM()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AVERAGE()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IF()&lt;/code&gt;, and more. If you’ve used Excel, you’ll find Calc’s formula system is almost identical.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Example&lt;/strong&gt;: If you want to calculate the total of a range of numbers, use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SUM()&lt;/code&gt; function. Just click on a cell and type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=SUM(A1:A10)&lt;/code&gt; to add up everything from A1 to A10.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Pro Tip&lt;/strong&gt;: You can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IF()&lt;/code&gt; functions to create conditional logic, like “If my balance drops below $100, highlight the cell in red.” That’s how you get smart with your data!&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;2-formatting-cells&quot;&gt;2. &lt;strong&gt;Formatting Cells&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Making your data readable is as important as entering it. Select a cell or range of cells, right-click, and choose &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Format Cells&lt;/code&gt;. You can:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Adjust number formats (currency, date, percentages, etc.).&lt;/li&gt;
  &lt;li&gt;Add borders, background colors, and custom fonts.&lt;/li&gt;
  &lt;li&gt;Use conditional formatting to automatically change cell colors based on values.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;3-sorting-and-filtering&quot;&gt;3. &lt;strong&gt;Sorting and Filtering&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Imagine you’re tracking a long list of transactions and want to see only those in a specific category, say “Housing.” Use &lt;strong&gt;AutoFilter&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Select your data range, go to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Data &amp;gt; AutoFilter&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Click the small drop-down arrow that appears in your column titles and filter by category.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Need to sort your data? Select the range, and head to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Data &amp;gt; Sort&lt;/code&gt;. You can sort by date, amount, or any other criteria.&lt;/p&gt;

&lt;h4 id=&quot;4-libreoffice-calc-on-the-cloud&quot;&gt;4. &lt;strong&gt;LibreOffice Calc on the Cloud&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Want to access Calc online? You can use &lt;strong&gt;LibreOffice Calc Online&lt;/strong&gt;, a cloud-based version that allows you to edit your spreadsheets from anywhere. No more worrying about carrying files between devices. Just log in and get to work.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;excel-to-libreoffice-calc-a-smooth-transition&quot;&gt;Excel to LibreOffice Calc: A Smooth Transition&lt;/h3&gt;

&lt;p&gt;If you’re transitioning from Excel, rest assured, most of your &lt;strong&gt;Excel files&lt;/strong&gt; (XLS, XLSX) can be opened in LibreOffice Calc without any issues. However, for more complex spreadsheets with macros or advanced formatting, you may need to do some minor tweaking. Here’s how you can open an Excel file in Calc:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Open LibreOffice Calc.&lt;/li&gt;
  &lt;li&gt;Go to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;File &amp;gt; Open&lt;/code&gt;, select the Excel file, and open it.&lt;/li&gt;
  &lt;li&gt;If the formatting seems off, try saving it as an ODS file (the native LibreOffice format) to maintain better compatibility.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can also convert &lt;strong&gt;LibreOffice Calc to Excel&lt;/strong&gt; if you’re collaborating with others who prefer Microsoft’s software. Just save your file as XLS or XLSX (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;File &amp;gt; Save As&lt;/code&gt;).&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;advanced-tips-for-when-youre-ready&quot;&gt;Advanced Tips for When You’re Ready&lt;/h3&gt;

&lt;p&gt;Once you’re comfortable with the basics, it’s time to level up! Here are a few &lt;strong&gt;advanced tricks&lt;/strong&gt; to explore:&lt;/p&gt;

&lt;h4 id=&quot;1-pivot-tables&quot;&gt;1. &lt;strong&gt;Pivot Tables&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Use pivot tables to summarize and analyze large datasets quickly. Go to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Data &amp;gt; Pivot Table&lt;/code&gt; and explore this feature to create custom reports based on your data.&lt;/p&gt;

&lt;h4 id=&quot;2-charts-and-graphs&quot;&gt;2. &lt;strong&gt;Charts and Graphs&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Turn your data into visual insights by creating charts. Select your data and click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Insert &amp;gt; Chart&lt;/code&gt;. Calc supports bar charts, line graphs, pie charts, and more.&lt;/p&gt;

&lt;h4 id=&quot;3-macros&quot;&gt;3. &lt;strong&gt;Macros&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;If you’re dealing with repetitive tasks, you can record &lt;strong&gt;macros&lt;/strong&gt; to automate them. This is similar to Excel’s VBA scripting but uses LibreOffice’s built-in Basic language.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;libreoffice-calc-on-mobile-and-portable-versions&quot;&gt;LibreOffice Calc on Mobile and Portable Versions&lt;/h3&gt;

&lt;p&gt;Need to access your spreadsheets on the go? LibreOffice Calc has &lt;strong&gt;Android&lt;/strong&gt; support, allowing you to open and edit spreadsheets from your mobile device. Alternatively, you can use the &lt;strong&gt;portable version&lt;/strong&gt; of LibreOffice Calc on a USB stick, so you can run it on any machine without installation.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;wrapping-it-up&quot;&gt;Wrapping It Up&lt;/h3&gt;

&lt;p&gt;And there you have it—your beginner’s guide to &lt;strong&gt;LibreOffice Calc&lt;/strong&gt;! From simple spreadsheets to advanced functions and data analysis, Calc offers a rich set of tools that rival paid alternatives like Excel. The best part? It’s free and open-source, so you can use it across platforms without worrying about licenses or subscriptions.&lt;/p&gt;

&lt;p&gt;Whether you’re just tracking your personal budget or managing business data, LibreOffice Calc is your go-to spreadsheet solution. Now, go ahead and &lt;strong&gt;crunch those numbers&lt;/strong&gt; like a pro!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Core PHP CRUD operations: a beginner's tutorial</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/php-crud-beginner-tutorial.html"/>
   <updated>2024-10-12T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/php-crud-beginner-tutorial</id>
   <content type="html">&lt;p&gt;Creating dynamic web applications often involves interacting with a database. One of the foundational elements of web development is the ability to perform CRUD operations: Create, Read, Update, and Delete. This tutorial will guide you through core PHP CRUD operations, helping you understand how to manage data effectively in your applications. Whether you’re building a simple blog or a complex web app, mastering these operations will be essential for your development journey.&lt;/p&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Before diving into CRUD operations, ensure you have a basic understanding of the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;HTML and CSS&lt;/strong&gt;: To create user interfaces.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;PHP&lt;/strong&gt;: Knowledge of core PHP concepts.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;MySQL&lt;/strong&gt;: Basic understanding of relational databases and SQL queries.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;XAMPP or MAMP&lt;/strong&gt;: A local server setup to run your PHP scripts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;setting-up-your-environment&quot;&gt;Setting Up Your Environment&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Install XAMPP or MAMP&lt;/strong&gt;: This will help you run PHP and MySQL on your local machine.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Create a Database&lt;/strong&gt;: Open phpMyAdmin and create a database named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crud_demo&lt;/code&gt;. Inside this database, create a table named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;users&lt;/code&gt; with the following structure:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;users&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AUTO_INCREMENT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;UNIQUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;created_at&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;TIMESTAMP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CURRENT_TIMESTAMP&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;users&lt;/code&gt; table will hold the data for our CRUD operations.&lt;/p&gt;

&lt;h2 id=&quot;creating-a-php-script-for-crud-operations&quot;&gt;Creating a PHP Script for CRUD Operations&lt;/h2&gt;

&lt;h3 id=&quot;1-database-connection&quot;&gt;1. Database Connection&lt;/h3&gt;

&lt;p&gt;Start by creating a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.php&lt;/code&gt; file to handle database connections.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$host&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;localhost&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$db_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;crud_demo&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$username&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;root&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Change if your MySQL username is different&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$password&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Change if you have set a password&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PDO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;mysql:host=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$host&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;;dbname=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$db_name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PDO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ATTR_ERRMODE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PDO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ERRMODE_EXCEPTION&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;PDOException&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Connection failed: &quot;&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;2-creating-data-insert&quot;&gt;2. Creating Data (Insert)&lt;/h3&gt;

&lt;p&gt;Next, create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create.php&lt;/code&gt; file to insert data into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;users&lt;/code&gt; table.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;config.php&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;REQUEST_METHOD&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;POST&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;email&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INSERT INTO users (name, email) VALUES (:name, :email)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bindParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;:name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bindParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;:email&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;New user created successfully.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Error creating user.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    Name: &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    Email: &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;email&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;email&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Create User&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/code-php-2.jpg&quot; alt=&quot;code-php&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;3-reading-data-select&quot;&gt;3. Reading Data (Select)&lt;/h3&gt;

&lt;p&gt;Create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read.php&lt;/code&gt; file to display all users in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;users&lt;/code&gt; table.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;config.php&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SELECT * FROM users&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$users&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetchAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PDO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;FETCH_ASSOC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;table&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;border=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;ID&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Name&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Email&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Actions&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$users&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;email&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;update.php?id=&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Edit&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;delete.php?id=&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Delete&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endforeach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;4-updating-data&quot;&gt;4. Updating Data&lt;/h3&gt;

&lt;p&gt;Now, create an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update.php&lt;/code&gt; file to update user information.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;config.php&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;REQUEST_METHOD&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;POST&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;email&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;UPDATE users SET name = :name, email = :email WHERE id = :id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bindParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;:name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bindParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;:email&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bindParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;:id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;User updated successfully.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Location: read.php&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Error updating user.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SELECT * FROM users WHERE id = :id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bindParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;:id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PDO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;FETCH_ASSOC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    Name: &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    Email: &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;email&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;email&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;email&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Update User&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;5-deleting-data&quot;&gt;5. Deleting Data&lt;/h3&gt;

&lt;p&gt;Finally, create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delete.php&lt;/code&gt; file to handle user deletions.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;config.php&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;DELETE FROM users WHERE id = :id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bindParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;:id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$stmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;User deleted successfully.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Location: read.php&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Error deleting user.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;understanding-crud-operations&quot;&gt;Understanding CRUD Operations&lt;/h2&gt;

&lt;h3 id=&quot;create&quot;&gt;Create&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Create&lt;/strong&gt; operation allows you to add new records to the database. In our example, we use an HTML form to gather user input and the PDO &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prepare&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;execute&lt;/code&gt; methods to insert data securely.&lt;/p&gt;

&lt;h3 id=&quot;read&quot;&gt;Read&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Read&lt;/strong&gt; operation fetches and displays records from the database. We use a SQL &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT&lt;/code&gt; query to retrieve user data, and we format it into an HTML table for easy viewing.&lt;/p&gt;

&lt;h3 id=&quot;update&quot;&gt;Update&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Update&lt;/strong&gt; operation modifies existing records based on user input. We fetch the current data for a specific user, present it in a form, and update the record using a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST&lt;/code&gt; request.&lt;/p&gt;

&lt;h3 id=&quot;delete&quot;&gt;Delete&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Delete&lt;/strong&gt; operation removes records from the database. By passing the user ID via a URL parameter, we can execute a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE&lt;/code&gt; statement to remove the specified user.&lt;/p&gt;

&lt;h2 id=&quot;best-practices-for-php-crud-operations&quot;&gt;Best Practices for PHP CRUD Operations&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Use Prepared Statements&lt;/strong&gt;: Always use prepared statements to protect against SQL injection.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Validate User Input&lt;/strong&gt;: Implement server-side validation for user input to ensure data integrity.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Implement Error Handling&lt;/strong&gt;: Use try-catch blocks to gracefully handle potential errors.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Organize Your Code&lt;/strong&gt;: Keep your code organized by separating concerns (e.g., database connections, business logic, and presentation).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Follow Security Best Practices&lt;/strong&gt;: Sanitize inputs, hash passwords, and secure your application against common vulnerabilities.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this tutorial, we’ve covered the essential core PHP CRUD operations, demonstrating how to create, read, update, and delete records in a MySQL database. By understanding these foundational operations, you’ll be well-equipped to build dynamic web applications.&lt;/p&gt;

&lt;p&gt;Remember that mastering CRUD operations is just the beginning. As you advance in your PHP journey, explore additional concepts such as AJAX integration, data validation, and security measures to enhance your applications further.&lt;/p&gt;

&lt;p&gt;With practice and patience, you’ll be able to implement complex functionalities and create robust web applications that meet various user needs. Happy coding!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Common PHP errors: solutions to frequently encountered issues</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/common-php-errors-solutions.html"/>
   <updated>2024-10-09T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/common-php-errors-solutions</id>
   <content type="html">&lt;p&gt;PHP is a powerful scripting language widely used for web development, but like any language, it’s easy to run into errors that can be frustrating to debug. While some errors are simple and easy to fix, others may be a little more complex. This article covers some of the most &lt;strong&gt;common PHP errors&lt;/strong&gt; and offers solutions to help you resolve them quickly.&lt;/p&gt;

&lt;h2 id=&quot;1-syntax-errors&quot;&gt;1. &lt;strong&gt;Syntax Errors&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;problem&quot;&gt;Problem:&lt;/h3&gt;
&lt;p&gt;A syntax error occurs when the PHP interpreter encounters code that doesn’t conform to the expected structure. These are the most basic types of errors and often result in the dreaded &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Parse error: syntax error, unexpected token&lt;/code&gt; message.&lt;/p&gt;

&lt;h3 id=&quot;common-causes&quot;&gt;Common Causes:&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Missing semicolons (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;;&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;Unmatched parentheses, curly braces, or brackets&lt;/li&gt;
  &lt;li&gt;Incorrect use of quotation marks&lt;/li&gt;
  &lt;li&gt;Misspelling keywords&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;example&quot;&gt;Example:&lt;/h3&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Hello World&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Missing semicolon&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;solution&quot;&gt;Solution:&lt;/h3&gt;
&lt;p&gt;Double-check your code for missing or extra punctuation. Make sure all your opening and closing parentheses, brackets, and quotes match.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Hello World&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Fixed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;2-undefined-variable-error&quot;&gt;2. &lt;strong&gt;Undefined Variable Error&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;problem-1&quot;&gt;Problem:&lt;/h3&gt;
&lt;p&gt;An “undefined variable” error occurs when you try to use a variable that has not been initialized. PHP will throw a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Notice: Undefined variable&lt;/code&gt; error in this case.&lt;/p&gt;

&lt;h3 id=&quot;example-1&quot;&gt;Example:&lt;/h3&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Undefined variable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;solution-1&quot;&gt;Solution:&lt;/h3&gt;
&lt;p&gt;Ensure that the variable is initialized before using it in your code. You can also suppress this notice by checking if the variable is set using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isset()&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;No username provided&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;3-fatal-error-call-to-undefined-function&quot;&gt;3. &lt;strong&gt;Fatal Error: Call to Undefined Function&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;problem-2&quot;&gt;Problem:&lt;/h3&gt;
&lt;p&gt;This error occurs when you attempt to call a function that hasn’t been defined. It could happen because you misspelled the function name or forgot to include the necessary file containing the function.&lt;/p&gt;

&lt;h3 id=&quot;example-2&quot;&gt;Example:&lt;/h3&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;myFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Undefined function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;solution-2&quot;&gt;Solution:&lt;/h3&gt;
&lt;p&gt;Ensure that the function is properly defined or included in your script. Also, check for typos in the function name.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;myFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;myFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Fixed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/code-php.jpeg&quot; alt=&quot;code-php&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;4-headers-already-sent&quot;&gt;4. &lt;strong&gt;Headers Already Sent&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;problem-3&quot;&gt;Problem:&lt;/h3&gt;
&lt;p&gt;This error occurs when PHP tries to modify headers (e.g., with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;header()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setcookie()&lt;/code&gt;) after output has already been sent to the browser. The error message typically looks like this: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Warning: Cannot modify header information - headers already sent by...&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;example-3&quot;&gt;Example:&lt;/h3&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Some output&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Location: /newpage.php&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Causes error because output was already sent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;solution-3&quot;&gt;Solution:&lt;/h3&gt;
&lt;p&gt;Ensure that no output (including whitespace or BOM) is sent before the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;header()&lt;/code&gt; function is called. If you need to redirect the user, make sure the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;header()&lt;/code&gt; is called before any output is generated.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Location: /newpage.php&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// This must appear before any echo or print statements&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;5-incorrect-permissions&quot;&gt;5. &lt;strong&gt;Incorrect Permissions&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;problem-4&quot;&gt;Problem:&lt;/h3&gt;
&lt;p&gt;Permission errors occur when your PHP script does not have the proper read or write permissions to access files or directories. You might see errors like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Warning: fopen(/path/to/file): failed to open stream: Permission denied&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;solution-4&quot;&gt;Solution:&lt;/h3&gt;
&lt;p&gt;Check the file and directory permissions. Typically, web server users should have read permissions for files and write permissions for directories where uploads or file manipulations occur. Use the following command to adjust permissions:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;755 /path/to/directory
&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;644 /path/to/file
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note: Be cautious when setting permissions, as overly permissive settings can pose security risks.&lt;/p&gt;

&lt;h2 id=&quot;6-memory-limit-exhausted&quot;&gt;6. &lt;strong&gt;Memory Limit Exhausted&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;problem-5&quot;&gt;Problem:&lt;/h3&gt;
&lt;p&gt;When PHP runs out of allocated memory, you’ll see a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fatal error: Allowed memory size of X bytes exhausted&lt;/code&gt; error. This happens when a script uses more memory than the limit set in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;php.ini&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;solution-5&quot;&gt;Solution:&lt;/h3&gt;
&lt;p&gt;You can increase the memory limit temporarily by adding the following line to your PHP script:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;ini_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;memory_limit&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;256M&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Adjust as needed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Alternatively, you can permanently increase the memory limit in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;php.ini&lt;/code&gt; file:&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;py&quot;&gt;memory_limit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;256M&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Make sure to optimize your code to reduce memory usage where possible.&lt;/p&gt;

&lt;h2 id=&quot;7-mysql-connection-error&quot;&gt;7. &lt;strong&gt;MySQL Connection Error&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;problem-6&quot;&gt;Problem:&lt;/h3&gt;
&lt;p&gt;Connecting to a MySQL database can sometimes fail, resulting in an error like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fatal error: Uncaught mysqli_sql_exception: Access denied for user &apos;username&apos;@&apos;localhost&apos;&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;common-causes-1&quot;&gt;Common Causes:&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Incorrect database credentials (hostname, username, password, database name)&lt;/li&gt;
  &lt;li&gt;The MySQL server is not running&lt;/li&gt;
  &lt;li&gt;Incorrect PHP MySQL extension (e.g., using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysql_connect()&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysqli_connect()&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;solution-6&quot;&gt;Solution:&lt;/h3&gt;
&lt;p&gt;Ensure that your credentials are correct and that the MySQL server is running. Also, make sure to use the appropriate connection function. Here’s a correct example using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysqli_connect()&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$mysqli&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;mysqli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;localhost&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;username&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;password&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;database&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$mysqli&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connect_error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;die&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Connection failed: &quot;&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$mysqli&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connect_error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;8-file-upload-errors&quot;&gt;8. &lt;strong&gt;File Upload Errors&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;problem-7&quot;&gt;Problem:&lt;/h3&gt;
&lt;p&gt;File uploads often fail due to improper settings or file size limitations. You may encounter errors like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UPLOAD_ERR_INI_SIZE&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UPLOAD_ERR_FORM_SIZE&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;solution-7&quot;&gt;Solution:&lt;/h3&gt;
&lt;p&gt;Check and adjust the following &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;php.ini&lt;/code&gt; settings as needed:&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;py&quot;&gt;file_uploads&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;On&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;upload_max_filesize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10M&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;post_max_size&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;12M&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Also, make sure your form tag has the correct &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enctype&lt;/code&gt; attribute:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;upload.php&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;post&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;enctype=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;multipart/form-data&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;file&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;file&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Upload&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;9-undefined-indexoffset&quot;&gt;9. &lt;strong&gt;Undefined Index/Offset&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;problem-8&quot;&gt;Problem:&lt;/h3&gt;
&lt;p&gt;This notice occurs when you try to access an array element that doesn’t exist, causing a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Notice: Undefined index&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Notice: Undefined offset&lt;/code&gt; error.&lt;/p&gt;

&lt;h3 id=&quot;example-4&quot;&gt;Example:&lt;/h3&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;username&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Undefined index if &apos;username&apos; is not in the form data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;solution-8&quot;&gt;Solution:&lt;/h3&gt;
&lt;p&gt;Always check if the array key exists before trying to access it. Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isset()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;array_key_exists()&lt;/code&gt; to prevent this error.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;username&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;username&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Username not provided.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;10-class-not-found&quot;&gt;10. &lt;strong&gt;Class Not Found&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;problem-9&quot;&gt;Problem:&lt;/h3&gt;
&lt;p&gt;PHP throws a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fatal error: Class &apos;ClassName&apos; not found&lt;/code&gt; error when you try to instantiate a class that hasn’t been defined or included properly.&lt;/p&gt;

&lt;h3 id=&quot;solution-9&quot;&gt;Solution:&lt;/h3&gt;
&lt;p&gt;Ensure that the file containing the class is included using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;require()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;include()&lt;/code&gt;. Alternatively, use PHP’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spl_autoload_register()&lt;/code&gt; function to automatically load class files.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;spl_autoload_register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$class_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$class_name&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;.php&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$object&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ClassName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;11-maximum-execution-time-exceeded&quot;&gt;11. &lt;strong&gt;Maximum Execution Time Exceeded&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;problem-10&quot;&gt;Problem:&lt;/h3&gt;
&lt;p&gt;If your PHP script takes too long to execute, you may encounter the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fatal error: Maximum execution time of X seconds exceeded&lt;/code&gt; error. This usually happens when working with large datasets or external API calls.&lt;/p&gt;

&lt;h3 id=&quot;solution-10&quot;&gt;Solution:&lt;/h3&gt;
&lt;p&gt;You can increase the maximum execution time temporarily with:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;set_time_limit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Extends to 300 seconds (5 minutes)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To set it globally, adjust the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max_execution_time&lt;/code&gt; directive in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;php.ini&lt;/code&gt; file:&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;py&quot;&gt;max_execution_time&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;300&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;PHP errors are inevitable, but knowing how to tackle the most common ones can save you a lot of debugging time. Whether it’s a syntax issue, database connection problem, or file permission error, understanding the root cause and solution is key to becoming a proficient PHP developer.&lt;/p&gt;

&lt;p&gt;By following the guidelines in this article, you should be able to identify and resolve these issues effectively. Keep your error reporting enabled during development to catch these errors early and ensure smoother coding!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unlocking speed: the power of indexing in database performance</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/role-of-indexing-in-db-performance-optimization.html"/>
   <updated>2024-10-08T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/role-of-indexing-in-db-performance-optimization</id>
   <content type="html">&lt;p&gt;In today’s data-driven world, the performance of databases is crucial for applications, businesses, and users alike. As databases grow in size and complexity, the need for efficient data retrieval becomes paramount. One of the most effective strategies for enhancing database performance is indexing. This article explores the role of indexing in database performance optimization and how it significantly improves query performance.&lt;/p&gt;

&lt;h2 id=&quot;understanding-indexing&quot;&gt;Understanding Indexing&lt;/h2&gt;

&lt;h3 id=&quot;what-is-an-index&quot;&gt;What is an Index?&lt;/h3&gt;

&lt;p&gt;An index in a database is a data structure that improves the speed of data retrieval operations on a database table. It functions similarly to an index in a book, allowing the database management system (DBMS) to quickly locate and access the desired records without having to scan every row in a table.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/code.jpg&quot; alt=&quot;source code&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;how-indexes-work&quot;&gt;How Indexes Work&lt;/h3&gt;

&lt;p&gt;Indexes work by creating a separate structure that holds the key values and pointers to the actual data rows. When a query is executed, the DBMS can use the index to quickly locate the rows that match the search criteria, minimizing the amount of data that needs to be scanned.&lt;/p&gt;

&lt;h2 id=&quot;types-of-indexes&quot;&gt;Types of Indexes&lt;/h2&gt;

&lt;p&gt;There are several types of indexes, each with its unique characteristics and use cases. The most common types include:&lt;/p&gt;

&lt;h3 id=&quot;1-b-tree-indexes&quot;&gt;1. &lt;strong&gt;B-Tree Indexes&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;B-Tree (Balanced Tree) indexes are the most widely used index type in relational databases. They store data in a hierarchical structure, enabling efficient retrieval of sorted data. B-Tree indexes are particularly effective for range queries and equality searches.&lt;/p&gt;

&lt;h3 id=&quot;2-hash-indexes&quot;&gt;2. &lt;strong&gt;Hash Indexes&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Hash indexes use a hash table to store data, mapping keys to their corresponding values. They provide extremely fast lookups for equality comparisons but are not suitable for range queries.&lt;/p&gt;

&lt;h3 id=&quot;3-bitmap-indexes&quot;&gt;3. &lt;strong&gt;Bitmap Indexes&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Bitmap indexes use bit arrays to represent the existence of values in a column. They are particularly useful for low-cardinality data (columns with a limited number of distinct values) and can significantly reduce storage requirements and improve performance for complex queries.&lt;/p&gt;

&lt;h3 id=&quot;4-full-text-indexes&quot;&gt;4. &lt;strong&gt;Full-Text Indexes&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Full-text indexes are designed for searching text within large volumes of unstructured data. They enable efficient searching and retrieval of documents based on keywords, phrases, and other criteria.&lt;/p&gt;

&lt;h2 id=&quot;the-benefits-of-indexing&quot;&gt;The Benefits of Indexing&lt;/h2&gt;

&lt;h3 id=&quot;1-faster-query-performance&quot;&gt;1. &lt;strong&gt;Faster Query Performance&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;The primary benefit of indexing is improved query performance. By using indexes, the DBMS can reduce the number of data pages it must read, leading to faster response times for queries. For example, a search that could take seconds without an index may take milliseconds with one.&lt;/p&gt;

&lt;h3 id=&quot;2-reduced-io-operations&quot;&gt;2. &lt;strong&gt;Reduced I/O Operations&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Indexes minimize the number of disk I/O operations required to retrieve data. By allowing the DBMS to quickly locate data pages, indexes significantly reduce the workload on the disk subsystem, leading to improved overall performance.&lt;/p&gt;

&lt;h3 id=&quot;3-enhanced-sorting-and-filtering&quot;&gt;3. &lt;strong&gt;Enhanced Sorting and Filtering&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Indexes enable efficient sorting and filtering of data. When executing queries that involve sorting or filtering, the DBMS can leverage indexes to access the necessary data more quickly than performing a full table scan.&lt;/p&gt;

&lt;h3 id=&quot;4-improved-join-performance&quot;&gt;4. &lt;strong&gt;Improved Join Performance&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Indexes can enhance the performance of join operations by allowing the DBMS to quickly locate matching rows in different tables. This is especially beneficial for large datasets where join operations can otherwise become a significant performance bottleneck.&lt;/p&gt;

&lt;h3 id=&quot;5-support-for-unique-constraints&quot;&gt;5. &lt;strong&gt;Support for Unique Constraints&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Indexes can enforce uniqueness constraints on a column or a combination of columns. This ensures that duplicate values are not allowed, helping maintain data integrity within the database.&lt;/p&gt;

&lt;h2 id=&quot;considerations-when-implementing-indexes&quot;&gt;Considerations When Implementing Indexes&lt;/h2&gt;

&lt;p&gt;While indexing offers numerous benefits, it’s essential to approach it with care. Over-indexing or improperly indexed tables can lead to several issues:&lt;/p&gt;

&lt;h3 id=&quot;1-increased-storage-requirements&quot;&gt;1. &lt;strong&gt;Increased Storage Requirements&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Indexes consume additional disk space. Each index created increases the amount of storage required for the database. It’s essential to balance the benefits of improved performance against the additional storage costs.&lt;/p&gt;

&lt;h3 id=&quot;2-slower-write-operations&quot;&gt;2. &lt;strong&gt;Slower Write Operations&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Indexes can slow down insert, update, and delete operations because the index needs to be maintained with every modification to the data. This is particularly important for applications with high write workloads, where the overhead of maintaining indexes can become significant.&lt;/p&gt;

&lt;h3 id=&quot;3-choosing-the-right-columns&quot;&gt;3. &lt;strong&gt;Choosing the Right Columns&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Not all columns benefit equally from indexing. Careful consideration must be given to which columns to index based on the types of queries being executed. Columns frequently used in WHERE clauses, JOIN conditions, and ORDER BY statements are good candidates for indexing.&lt;/p&gt;

&lt;h3 id=&quot;4-monitoring-and-maintenance&quot;&gt;4. &lt;strong&gt;Monitoring and Maintenance&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Indexes require regular monitoring and maintenance. As data changes, indexes can become fragmented, leading to reduced performance. Regularly rebuilding or reorganizing indexes can help maintain optimal performance.&lt;/p&gt;

&lt;h2 id=&quot;best-practices-for-indexing&quot;&gt;Best Practices for Indexing&lt;/h2&gt;

&lt;p&gt;To maximize the benefits of indexing while minimizing potential drawbacks, consider the following best practices:&lt;/p&gt;

&lt;h3 id=&quot;1-analyze-query-patterns&quot;&gt;1. &lt;strong&gt;Analyze Query Patterns&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Before creating indexes, analyze the query patterns in your application. Focus on optimizing queries that are executed frequently and have high execution times.&lt;/p&gt;

&lt;h3 id=&quot;2-use-composite-indexes&quot;&gt;2. &lt;strong&gt;Use Composite Indexes&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;When multiple columns are often used together in queries, consider creating composite indexes. These indexes can improve performance for queries that filter based on multiple columns.&lt;/p&gt;

&lt;h3 id=&quot;3-regularly-review-and-optimize-indexes&quot;&gt;3. &lt;strong&gt;Regularly Review and Optimize Indexes&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Periodically review your indexes to ensure they are still providing value. Remove unused or redundant indexes that may be consuming resources without delivering performance improvements.&lt;/p&gt;

&lt;h3 id=&quot;4-limit-the-number-of-indexes&quot;&gt;4. &lt;strong&gt;Limit the Number of Indexes&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Avoid over-indexing. Focus on creating indexes that will have a significant impact on query performance while keeping the total number of indexes manageable.&lt;/p&gt;

&lt;h3 id=&quot;5-monitor-performance-impact&quot;&gt;5. &lt;strong&gt;Monitor Performance Impact&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Continuously monitor the impact of indexes on database performance. Use database profiling tools to evaluate how indexes affect query performance and make adjustments as needed.&lt;/p&gt;

&lt;p&gt;Indexing is a powerful tool for optimizing database performance, significantly enhancing query speed and efficiency. By understanding the various types of indexes and their benefits, database administrators and developers can make informed decisions about when and how to implement indexing strategies. With careful planning and regular maintenance, indexing can lead to a more responsive and efficient database, ultimately improving the overall user experience.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>PHP libraries for PDF handling: evaluation and use case guide</title>
   <link href="https://prahladyeri.github.io/blog/2024/10/php-libraries-for-pdf-handling-evaluation-use-case-guide.html"/>
   <updated>2024-10-06T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/10/php-libraries-for-pdf-handling-evaluation-use-case-guide</id>
   <content type="html">&lt;p&gt;PDF generation and processing is a common requirement in many web applications, especially for generating invoices, reports, or documents dynamically. PHP provides various libraries to handle PDF creation, manipulation, and extraction. In this article, we will evaluate the most popular PHP libraries for PDF handling, breaking down their &lt;strong&gt;pros&lt;/strong&gt; and &lt;strong&gt;cons&lt;/strong&gt;, and advising when each library is best suited for specific use cases.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;1-fpdf&quot;&gt;1. &lt;a href=&quot;https://github.com/Setasign/FPDF&quot;&gt;&lt;strong&gt;FPDF&lt;/strong&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;FPDF&lt;/strong&gt; is one of the most basic and lightweight libraries for creating PDF files with PHP. It requires no external dependencies and allows developers to generate PDFs from scratch using a simple API.&lt;/p&gt;

&lt;h4 id=&quot;pros&quot;&gt;Pros:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Lightweight&lt;/strong&gt;: FPDF is minimalistic, making it easy to set up and use for basic PDF generation.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Free&lt;/strong&gt;: It’s open-source and free to use, suitable for small projects.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Simple API&lt;/strong&gt;: The syntax is intuitive and easy for developers to understand, making it ideal for beginners.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Customizability&lt;/strong&gt;: Gives control over text placement, fonts, images, and shapes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;cons&quot;&gt;Cons:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;No HTML/CSS support&lt;/strong&gt;: FPDF does not support converting HTML or CSS into PDF, making it harder to design complex layouts.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Limited Features&lt;/strong&gt;: While it’s good for basic tasks, it lacks advanced features like embedding fonts, handling complex table layouts, and supporting Unicode.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;best-use-cases&quot;&gt;Best Use Cases:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Generating simple invoices, tickets, or certificates.&lt;/li&gt;
  &lt;li&gt;Projects that don’t require complex layouts or HTML-to-PDF conversion.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;2-tcpdf&quot;&gt;2. &lt;a href=&quot;https://github.com/tecnickcom/TCPDF&quot;&gt;&lt;strong&gt;TCPDF&lt;/strong&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;TCPDF&lt;/strong&gt; is a robust, feature-rich library that builds on the simplicity of FPDF but provides more advanced capabilities like support for HTML, Unicode, and complex layouts.&lt;/p&gt;

&lt;h4 id=&quot;pros-1&quot;&gt;Pros:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Feature-Rich&lt;/strong&gt;: TCPDF supports HTML and CSS, including tables, images, and complex document structures.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Unicode Support&lt;/strong&gt;: It has native support for UTF-8, making it ideal for multilingual projects.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;No Dependencies&lt;/strong&gt;: Unlike other libraries that rely on external tools, TCPDF is standalone.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Barcodes and QR codes&lt;/strong&gt;: It has built-in support for generating barcodes and QR codes, a useful feature for product labels or tickets.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;cons-1&quot;&gt;Cons:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: TCPDF is slower and more memory-intensive than FPDF due to its extensive features.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Steep Learning Curve&lt;/strong&gt;: Its vast array of features can be overwhelming for new users.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Large Files&lt;/strong&gt;: PDF files generated by TCPDF tend to be larger compared to those generated by FPDF or other libraries.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;best-use-cases-1&quot;&gt;Best Use Cases:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Applications that require multilingual or RTL text support.&lt;/li&gt;
  &lt;li&gt;Generating complex documents with images, tables, and different fonts.&lt;/li&gt;
  &lt;li&gt;Projects that need barcodes or QR codes within PDFs.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;3-mpdf&quot;&gt;3. &lt;a href=&quot;https://libraries.io/packagist/mpdf%2Fmpdf&quot;&gt;&lt;strong&gt;mPDF&lt;/strong&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;mPDF&lt;/strong&gt; is based on FPDF and HTML2PDF and is designed for converting HTML content into PDFs. It provides a good balance between simplicity and features, offering an easy way to convert web pages into PDFs.&lt;/p&gt;

&lt;h4 id=&quot;pros-2&quot;&gt;Pros:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;HTML/CSS to PDF Conversion&lt;/strong&gt;: mPDF excels at converting complex HTML structures (with CSS) into PDF, making it ideal for web-based PDFs.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Unicode and RTL&lt;/strong&gt;: It supports multilingual content, including right-to-left (RTL) scripts like Arabic.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Embedded Fonts&lt;/strong&gt;: Supports embedding custom fonts within the PDF, adding to design flexibility.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Ease of Use&lt;/strong&gt;: It’s relatively easy to convert a web page into a PDF with minimal configuration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;cons-2&quot;&gt;Cons:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: While fast for small documents, mPDF can become slow with large or complex HTML layouts.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Memory Usage&lt;/strong&gt;: mPDF consumes more memory than some other libraries when handling large documents.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Limited Features&lt;/strong&gt;: While it works well with HTML/CSS, it lacks advanced PDF manipulation features (e.g., extracting text from PDFs).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;best-use-cases-2&quot;&gt;Best Use Cases:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Converting web pages (HTML + CSS) directly into PDF.&lt;/li&gt;
  &lt;li&gt;Projects that require custom font embedding and support for multiple languages.&lt;/li&gt;
  &lt;li&gt;Ideal for invoices, reports, or forms that are originally designed as web pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;4-snappy-wkhtmltopdf&quot;&gt;4. &lt;a href=&quot;https://libraries.io/packagist/knplabs%2Fknp-snappy&quot;&gt;&lt;strong&gt;Snappy (wkhtmltopdf)&lt;/strong&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Snappy&lt;/strong&gt; is a wrapper for the &lt;strong&gt;wkhtmltopdf&lt;/strong&gt; command-line tool that uses WebKit to render HTML as PDF. It’s known for producing high-quality, accurate PDFs from complex HTML/CSS structures.&lt;/p&gt;

&lt;h4 id=&quot;pros-3&quot;&gt;Pros:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;High-Quality Output&lt;/strong&gt;: Snappy (wkhtmltopdf) delivers precise and accurate rendering of complex layouts, including advanced CSS and JavaScript.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;JavaScript Support&lt;/strong&gt;: It can execute JavaScript, making it possible to include dynamic content (e.g., charts) in your PDF.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Speed&lt;/strong&gt;: wkhtmltopdf is relatively fast compared to libraries like TCPDF and mPDF for large documents.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Command-line Utility&lt;/strong&gt;: Allows for batch processing via the command line for server-side applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;cons-3&quot;&gt;Cons:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Requires External Tool&lt;/strong&gt;: It depends on the wkhtmltopdf tool, which must be installed on the server.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Limited PDF Manipulation&lt;/strong&gt;: Snappy is great for converting HTML to PDF, but lacks advanced features like merging or splitting PDFs.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Resource-Heavy&lt;/strong&gt;: It can be resource-intensive, especially when generating PDFs from very complex HTML layouts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;best-use-cases-3&quot;&gt;Best Use Cases:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Projects that need pixel-perfect HTML-to-PDF conversion.&lt;/li&gt;
  &lt;li&gt;Applications where you need to convert large reports or invoices from HTML to PDF quickly.&lt;/li&gt;
  &lt;li&gt;Dynamic web content (with JavaScript) that needs to be rendered as a PDF.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;5-pdfparser&quot;&gt;5. &lt;a href=&quot;https://libraries.io/packagist/smalot%2Fpdfparser&quot;&gt;&lt;strong&gt;PDFParser&lt;/strong&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;PDFParser&lt;/strong&gt; is a powerful library for parsing and extracting content from existing PDF files. It focuses on analyzing and extracting text, images, and metadata rather than creating new PDFs.&lt;/p&gt;

&lt;h4 id=&quot;pros-4&quot;&gt;Pros:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Text Extraction&lt;/strong&gt;: It efficiently extracts text, images, and metadata from existing PDFs.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Search and Indexing&lt;/strong&gt;: Useful for indexing and searching large collections of PDFs.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Built on TCPDF&lt;/strong&gt;: Leverages TCPDF’s capabilities for more complex parsing tasks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;cons-4&quot;&gt;Cons:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;No PDF Creation&lt;/strong&gt;: PDFParser is specifically for extracting content, not creating or manipulating PDFs.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Complex Documents&lt;/strong&gt;: While it works well with simple documents, it can struggle with highly complex or encrypted PDFs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;best-use-cases-4&quot;&gt;Best Use Cases:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Projects that involve PDF document indexing, searching, or text extraction.&lt;/li&gt;
  &lt;li&gt;PDF content analysis or metadata extraction tasks.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;6-setapdf&quot;&gt;6. &lt;a href=&quot;https://www.setasign.com/products/setapdf-core/details/&quot;&gt;&lt;strong&gt;SetaPDF&lt;/strong&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;SetaPDF&lt;/strong&gt; is a commercial-grade PHP library for professional-level PDF processing. It offers features like form filling, signing, and advanced manipulation of PDF files.&lt;/p&gt;

&lt;h4 id=&quot;pros-5&quot;&gt;Pros:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Enterprise Features&lt;/strong&gt;: Includes professional-level features like digital signatures, form filling, and annotations.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;High Performance&lt;/strong&gt;: Optimized for enterprise environments with high-volume PDF processing.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: Offers advanced encryption and security features for PDF files.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Rich API&lt;/strong&gt;: Comprehensive API for advanced PDF manipulation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;cons-5&quot;&gt;Cons:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Commercial License&lt;/strong&gt;: SetaPDF is a paid library, which might be a barrier for smaller projects or individual developers.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Complexity&lt;/strong&gt;: The advanced feature set requires a steep learning curve and in-depth understanding of PDFs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;best-use-cases-5&quot;&gt;Best Use Cases:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Enterprise applications that need secure, high-performance PDF generation, filling, or signing.&lt;/li&gt;
  &lt;li&gt;Projects involving sensitive documents like contracts, legal papers, or certifications.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;7-fpdi&quot;&gt;7. &lt;a href=&quot;https://github.com/Setasign/FPDI&quot;&gt;&lt;strong&gt;FPDI&lt;/strong&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;FPDI&lt;/strong&gt; is an extension to FPDF and TCPDF that allows you to import pages from existing PDFs and use them in newly generated PDFs. It’s perfect for modifying and merging PDFs.&lt;/p&gt;

&lt;h4 id=&quot;pros-6&quot;&gt;Pros:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;PDF Importing&lt;/strong&gt;: Allows you to import, reuse, and manipulate existing PDF pages.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Works with FPDF and TCPDF&lt;/strong&gt;: Combines well with either library to provide enhanced functionality.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Simple to Use&lt;/strong&gt;: If you’re already using FPDF or TCPDF, FPDI is easy to integrate.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;cons-6&quot;&gt;Cons:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;No PDF Creation&lt;/strong&gt;: FPDI is designed for manipulating and merging PDFs, not for creating new ones from scratch.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Limited Editing&lt;/strong&gt;: It’s great for importing and merging but limited for complex editing of PDF content.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;best-use-cases-6&quot;&gt;Best Use Cases:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Applications where merging multiple PDFs is required (e.g., combining invoices or reports).&lt;/li&gt;
  &lt;li&gt;Projects that need to modify existing PDFs, like adding a cover page or watermark.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;8-pdfmerger&quot;&gt;8. &lt;a href=&quot;https://github.com/myokyawhtun/PDFMerger&quot;&gt;&lt;strong&gt;PDFMerger&lt;/strong&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;PDFMerger&lt;/strong&gt; is a small, easy-to-use library for merging PDF files. It simplifies the process of combining multiple PDF files into one document.&lt;/p&gt;

&lt;h4 id=&quot;pros-7&quot;&gt;Pros:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Simplicity&lt;/strong&gt;: Very easy to use for merging PDFs.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Quick Integration&lt;/strong&gt;: You can quickly add this library to an existing project.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Lightweight&lt;/strong&gt;: Minimal dependencies and configuration needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;cons-7&quot;&gt;Cons:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Limited Functionality&lt;/strong&gt;: Focuses purely on merging; does not support any other PDF manipulation.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;No Editing&lt;/strong&gt;: You can merge PDFs but cannot modify their contents.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;best-use-cases-7&quot;&gt;Best Use Cases:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Projects that require merging multiple PDFs into a single file.&lt;/li&gt;
  &lt;li&gt;Use cases like bundling invoices, contracts, or reports.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;9-dompdf&quot;&gt;9. &lt;a href=&quot;https://github.com/dompdf/dompdf&quot;&gt;&lt;strong&gt;Dompdf&lt;/strong&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Dompdf&lt;/strong&gt; is one of the most popular PHP libraries for converting HTML and CSS to PDF. It’s a powerful library that uses a layout engine based on HTML5 and CSS3, allowing developers to generate PDFs from existing web pages or dynamically built HTML documents.&lt;/p&gt;

&lt;h4 id=&quot;pros-8&quot;&gt;Pros:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;HTML/CSS Support&lt;/strong&gt;: Dompdf supports most HTML5 tags and CSS3 styles, making it suitable for converting complex web pages into PDFs.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Ease of Use&lt;/strong&gt;: Very straightforward for developers familiar with HTML and CSS, as it allows you to render PDFs directly from existing templates.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Open Source&lt;/strong&gt;: Dompdf is open-source and free, with a large community for support.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Font Embedding&lt;/strong&gt;: Allows you to embed custom fonts within the PDF for more design flexibility.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Basic JavaScript Support&lt;/strong&gt;: Supports basic JavaScript but is somewhat limited compared to other libraries like Snappy (wkhtmltopdf).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;cons-8&quot;&gt;Cons:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Dompdf can be slow when dealing with large or complex HTML documents, especially those with many images or heavy CSS.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Limited Advanced Features&lt;/strong&gt;: Compared to libraries like TCPDF or mPDF, Dompdf lacks advanced features like encryption, digital signatures, or form fields.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Inconsistent CSS Rendering&lt;/strong&gt;: While it supports CSS3, it can struggle with some advanced CSS properties, especially for responsive design elements.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Memory-Intensive&lt;/strong&gt;: Processing large PDFs can lead to high memory usage and longer generation times.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;best-use-cases-8&quot;&gt;Best Use Cases:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Web-to-PDF Conversion&lt;/strong&gt;: Perfect for converting web pages into PDF, such as online invoices, reports, or newsletters that are built in HTML.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Small to Medium-Sized Projects&lt;/strong&gt;: Works well for web applications that generate medium-sized PDFs (e.g., e-commerce invoices, user manuals).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;PDFs with Custom Designs&lt;/strong&gt;: Ideal for generating PDFs with custom fonts, layouts, and styling, particularly if the source content is already styled with HTML/CSS.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;when-to-use-dompdf&quot;&gt;When to Use &lt;strong&gt;Dompdf&lt;/strong&gt;:&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;HTML-to-PDF Conversion&lt;/strong&gt;: Like &lt;strong&gt;mPDF&lt;/strong&gt;, &lt;strong&gt;Dompdf&lt;/strong&gt; is designed specifically for converting HTML and CSS to PDF, making it the go-to choice for web applications that need to render dynamic HTML pages into PDF format.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Simple Projects with Moderate HTML&lt;/strong&gt;: It’s ideal for medium-complexity documents that require basic to moderate HTML/CSS rendering. However, if your project involves large documents, high resolution images, or complex layouts, you may run into performance bottlenecks.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Projects with Embedded Fonts&lt;/strong&gt;: Dompdf’s ability to embed custom fonts makes it a good choice for documents where typography is essential, such as branded reports or certificates.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;In summary, &lt;strong&gt;Dompdf&lt;/strong&gt; is a great option for converting existing HTML content into PDF format when you have a simple or moderately complex design and don’t need advanced PDF features like encryption or form processing. However, for larger documents or projects that require more sophisticated PDF capabilities, you may want to consider alternatives like &lt;strong&gt;TCPDF&lt;/strong&gt;, &lt;strong&gt;mPDF&lt;/strong&gt;, or &lt;strong&gt;Snappy&lt;/strong&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;when-to-use-each-library&quot;&gt;When to Use Each Library&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;FPDF&lt;/strong&gt;: Use when you need a lightweight solution for creating basic PDF documents from scratch.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;TCPDF&lt;/strong&gt;: Ideal for generating complex, feature-rich PDFs with support for HTML, Unicode, and custom fonts.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;mPDF&lt;/strong&gt;: Best for converting web pages (HTML + CSS) into PDFs. Great for forms, invoices, and reports designed for the web.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Snappy (wkhtmltopdf)&lt;/strong&gt;: Go-to for pixel-perfect conversion of complex HTML/CSS to PDFs, especially for reports or invoices.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;PDFParser&lt;/strong&gt;: Suitable for extracting text and metadata from existing PDF files.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;SetaPDF&lt;/strong&gt;: The choice for enterprise-level projects requiring secure PDF handling, digital signatures, and form processing.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FPDI&lt;/strong&gt;: Use when you need to import or manipulate existing PDFs within new PDF files.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;PDFMerger&lt;/strong&gt;: Perfect for merging multiple PDFs into one document, ideal for report bundling.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Dompdf&lt;/strong&gt;: For converting HTML/CSS to PDF in small to medium-sized projects with simple to moderate layouts. It’s ideal for web applications needing dynamic PDFs with custom fonts and designs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each library has its strengths and weaknesses, and the choice depends heavily on the specific requirements of your project. For HTML-to-PDF conversion, &lt;strong&gt;mPDF&lt;/strong&gt; or &lt;strong&gt;Snappy&lt;/strong&gt; are excellent choices. If you’re dealing with secure enterprise documents, &lt;strong&gt;SetaPDF&lt;/strong&gt; is your best bet. For basic tasks, &lt;strong&gt;FPDF&lt;/strong&gt; offers a straightforward and lightweight solution.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Who exactly I am as a soul? - A crash course in Advaita Vedanta</title>
   <link href="https://prahladyeri.github.io/blog/2024/09/who-exactly-am-i-as-soul-crash-course-in-advait-vedanta.html"/>
   <updated>2024-09-30T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/09/who-exactly-am-i-as-soul-crash-course-in-advait-vedanta</id>
   <content type="html">&lt;p&gt;“Who am I?” is perhaps the most sought after question by humankind since time immemorial.&lt;/p&gt;

&lt;p&gt;Do you even think that you have a soul or consciousness as separate entity than the physical body and biological brain? If not, you may be an &lt;em&gt;absolute materialist&lt;/em&gt;. On the other hand, do you think you live in a transient world of Matrix or a long-term dream and the reality will be faced only when you go out of it and meet the creator? In this case, you’re an &lt;em&gt;absolute spiritualist&lt;/em&gt;. In Indian philosophy or &lt;em&gt;Darshan Shastras&lt;/em&gt;, these are the two polar opposites (material and spiritual), the former represented by &lt;em&gt;Charvaka&lt;/em&gt; and latter by &lt;em&gt;Advaita Vedanta&lt;/em&gt; schools of thoughts. Even Buddhism and Jainism generally lean towards spiritual than material.&lt;/p&gt;

&lt;p&gt;The ideal approach to life is to balance out these philosophies and not put all the money in any one stock! However, the spiritual and material tendencies of humans also depend a lot on circumstances. An ambitious and materialistically wealthy person in the youth phase of their life will put more stock in materialism as that is paying off right now. On the other hand, someone who has aged beyond 40-50s or someone who is in a declining phase and facing difficulties might incline more towards spirituality.&lt;/p&gt;

&lt;p&gt;Even if you can’t answer that question or ever thought about it, everyone does have a “working theory” of sorts about who they are in their minds. And that working theory determines literally everything in their life - their choice of food, clothes, lifestyle, attitude towards wealth, expected salary and even political inclination!&lt;/p&gt;

&lt;p&gt;As per Advaita theory, deep within the subconscious mind, there is an inner urge in every soul to know itself, it’s literally crying out “Who am I?”. The first cry of an infant when it lands on the Earth is reflective of that. Having failed to get an answer, the soul then attaches to whichever identity it finds around, the identities of relationships, career, caste, creed, religion, ideology, etc. Latching to these identities then becomes a substitute for seeking that timeless answer, in fact it becomes an objective in itself, driven by desires and emotions through life.&lt;/p&gt;

&lt;p&gt;The moment you answer that question, and actually realize what that answer is, &lt;em&gt;realization&lt;/em&gt; or &lt;em&gt;nirvana&lt;/em&gt; is achieved and you become free from the Matrix. But the answer itself isn’t the important part of the quest, the real deal is to be aware of these sheaths and layers of identities and destroy them. As long as the soul is trapped in the sheaths, it will be driven by its own emotions and face its karma or consequences thrown by the material world, as if on an autopilot. Even after death, the same story repeats in another part of the Matrix (maybe another planet on another galaxy, or even centuries apart on this same planet).&lt;/p&gt;

&lt;p&gt;The Gita describes three kinds of &lt;em&gt;tapas&lt;/em&gt; or depressions - &lt;em&gt;Adi bhautik&lt;/em&gt; (material/wealth related depressions), &lt;em&gt;Adi daivik&lt;/em&gt; (depressions arising out of fate or acts of God like pandemic, disease, accidents, etc.) and finally &lt;em&gt;Adhyatmic&lt;/em&gt; (spiritual depression). If a soul has achieved everything they could in terms of material wealth and they’re still unhappy or depressed, it only means they’re now ready to deal with that final or ultimate question in spirituality.&lt;/p&gt;

&lt;p&gt;Sri Adi Shankaracharya, the author of Advaita Vedanta, has composed a beautiful Sanskrit poem called &lt;a href=&quot;https://www.youtube.com/watch?v=LXtNqL2vxoA&quot;&gt;&lt;em&gt;Nirvana Shatakam&lt;/em&gt;&lt;/a&gt; which beautifully reflects on each one of these material sheaths or layers one by one. The soul declares and distances itself from each one of these sheaths like mind, intellect, ego, relationships, etc.&lt;/p&gt;

&lt;p&gt;In an attempt to answer the ultimate question, Adi Shankaracharya declares every now and then &lt;em&gt;“Shivoham Shivoham”&lt;/em&gt; (&lt;em&gt;I am Shiva or God&lt;/em&gt;) in that poem. This has been one of the most controversial aspects of advaita vedanta, along with his other famous phrase &lt;em&gt;“Aham brahmasmi”&lt;/em&gt; which also means “I am God”, and gets criticized a lot. Being born in a Dvaita tradition family, I’ve often heard this criticism by many - “How can one declare themselves as God?”. The Dvaita school asserts on the separation of &lt;em&gt;Jiva&lt;/em&gt; (soul), &lt;em&gt;Brahma&lt;/em&gt; (God) and &lt;em&gt;Jada&lt;/em&gt; (material), and advocates &lt;em&gt;bhakti&lt;/em&gt; instead as the path towards liberation.&lt;/p&gt;

&lt;p&gt;Indeed, declaring oneself as God seems foolhardy or even lunatic in a fully materialistic world, where “you” and “me” are considered two different souls. In fact, this is the world which we daily observe. But what if this was essentially a “Matrix” or transient world or one of a long-term dream as shown in the movie “&lt;em&gt;Inception&lt;/em&gt;”? In that case, every single soul is just a part of the “greater dream” along with all the matter and energy that is. And thus, every soul can be referenced as a God or creator since they’re inside the creator’s dream? This dream or matrix is called Maya. This idea isn’t new, that our world could be a Matrix like thing is also a basic tenet of &lt;a href=&quot;https://en.wikipedia.org/wiki/Gnosticism&quot;&gt;Gnosticism&lt;/a&gt; which was a collection of religious and philosophical ideas among early Christian and Jewish sects.&lt;/p&gt;

&lt;p&gt;Even Buddhism is pretty much based on the idea that the material world is transient and not substantial, the Mahayana branch goes even further and talks of a &lt;a href=&quot;https://en.wikipedia.org/wiki/Pure_Land_Buddhism&quot;&gt;Pure Land&lt;/a&gt;, the better version of this Matrix where folks wanting to achieve liberation can go after this life ends.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/who-exactly-am-i-as-soul.webp&quot; alt=&quot;who-exactly-am-i-as-soul&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Other factors that support a “fluxed” Matrix like reality:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;The Economic theory of &lt;a href=&quot;https://en.wikipedia.org/wiki/Marginal_utility#Law_of_diminishing_marginal_utility&quot;&gt;diminishing marginal utility&lt;/a&gt;&lt;/strong&gt;. Nothing in this world brings &lt;em&gt;yatharth&lt;/em&gt; or lasting happiness. Even the best of SUVs, the best brand of alcohol, the best gated community will give you dopamine only till a certain point. Post that, the dopamine resistance sets in and so does the &lt;em&gt;Adhyatmic&lt;/em&gt; tapa or Spiritual depression.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The Double Slit Experiment.&lt;/strong&gt; &lt;a href=&quot;https://en.wikipedia.org/wiki/Double-slit_experiment&quot;&gt;This experiment&lt;/a&gt; in Quantum Physics shows that particles suddenly become waves depending on whether they’re being observed or not. This isn’t a sign of a &lt;em&gt;yatharth&lt;/em&gt; or stable world, it’s a sign of a transient or matrix world.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Human life itself is unpredictable.&lt;/strong&gt; The built-in emotions like hope and greed keep driving the samsara or life but if you think about it, data shows that all of that zeal may not stand the test of time. We barely managed to escape a pandemic few years ago, India is just one political turmoil away or one pandemic away from massive destruction of civilization. This doesn’t mean we should stop doing our work, but the over zealousness of negative emotions like greed, jealousy, crony capitalism, etc. can be certainly contained.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Experiential or subjective truth counts.&lt;/strong&gt; Each time I listen to the &lt;a href=&quot;https://www.youtube.com/watch?v=LXtNqL2vxoA&quot;&gt;&lt;em&gt;Nirvana Shatakam&lt;/em&gt;&lt;/a&gt; poem and the responses being given to the soul’s yearning, I feel the veil of Matrix being lifted just a little bit. But this is just my own subjective experience. In either case, it’s a beautiful poem and I recommend listening to it at least once.&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Seven most aesthetically pleasing fonts for your web apps</title>
   <link href="https://prahladyeri.github.io/blog/2024/06/most-aesthetic-google-fonts.html"/>
   <updated>2024-06-15T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/06/most-aesthetic-google-fonts</id>
   <content type="html">&lt;p&gt;Without Google Fonts, our typography would be in limbo! Now, I’m neither a web designer nor have an eye for the pixel perfect and aesthetic design which a pro designer usually does. Instead, my usual workflow is to cobble together web components like bootstrap, jquery, codeigniter, etc. and try to build something useful out of it.&lt;/p&gt;

&lt;link rel=&quot;stylesheet&quot; href=&quot;https://fonts.googleapis.com/css?family=Rubik|Inter|Mulish|Roboto|Libre+Baskerville|Open+Sans|Lato&quot; /&gt;

&lt;style&gt;
.Rubik {	font-family: Rubik; }
.Inter {	font-family: Inter; }
.Mulish {	font-family: Mulish; }
.Roboto { 	font-family: Roboto; }
.Lato { 	font-family: Lato; }
.OpenSans { 	font-family: &quot;Open Sans&quot;; }
.LibreBaskerville { font-family: &quot;Libre Baskerville&quot;; }
&lt;/style&gt;

&lt;p&gt;As for fonts, I happen to use the open source Google Fonts of which there are a zillion varieties. But what I’ve found over the years is that a few of them outshine the rest when it comes to actual web and mobile applications (as opposed to other kind of websites such as static sites and blogs).&lt;/p&gt;

&lt;p class=&quot;Rubik&quot;&gt;&lt;b&gt;Rubik&lt;/b&gt; is one of my favorite fonts when it comes to app development. The combination of Rubik font and a comprehensive CSS framework like Bootstrap is quite powerful and formidable. You&apos;ll see many professional open source apps using this font in the wild. The popular software ERPNext used it extensively until very recently, only now they&apos;ve switched to Inter.&lt;/p&gt;

&lt;p class=&quot;Inter&quot;&gt;&lt;b&gt;Inter&lt;/b&gt; is yet another aesthetically pleasing font, especially for web applications. Its &lt;a href=&quot;https://fonts.google.com/specimen/Inter&quot;&gt;fonts page&lt;/a&gt; says, &lt;i&gt;&quot;Inter is a variable font family carefully crafted &amp;amp; designed for computer screens.&quot;&lt;/i&gt; I&apos;ve largely found this font quite readable on app screens.&lt;/p&gt;

&lt;p class=&quot;Mulish&quot;&gt;&lt;b&gt;Mulish&lt;/b&gt; is the Google&apos;s open source equivalent of Adobe&apos;s original Muli font. It looks quite elegant and aesthetic on web apps. I&apos;ve used the Mulish font in my open source side project called &lt;a href=&quot;https://github.com/prahladyeri/comment-monk&quot;&gt;Comment Monk&lt;/a&gt;, a self-hosted comments CMS for static blogs.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/cm/cm-dashboard.png&quot; alt=&quot;cm-dashboard&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;Roboto&quot;&gt;&lt;b&gt;Roboto&lt;/b&gt; is the most downloaded font on the internet. According to its &lt;a href=&quot;https://fonts.google.com/specimen/Roboto&quot;&gt;fonts page&lt;/a&gt;, &lt;i&gt;&quot;Roboto has a dual nature. It has a mechanical skeleton and the forms are largely geometric. At the same time, the font features friendly and open curves.&quot;&lt;/i&gt;.&lt;/p&gt;

&lt;p class=&quot;OpenSans&quot;&gt;&lt;b&gt;Open Sans&lt;/b&gt; is often touted as the &lt;i&gt;open source humanist sans-serif typeface&lt;/i&gt;. Designed by Steve Matteson under commission from Google and published in 2011, Open Sans is a successor to the other popular font called Droid Sans which was specifically crafted for android mobile devices.&lt;/p&gt;

&lt;p class=&quot;Lato&quot;&gt;&lt;b&gt;Lato&lt;/b&gt; is yet another humanist sans-serif typeface designed by Łukasz Dziedzic. Published in 2010, the name Lato is Polish translation for &quot;summer&quot; and indeed, one could almost feel that warmth in this font.&lt;/p&gt;

&lt;p class=&quot;LibreBaskerville&quot;&gt;&lt;b&gt;Libre Baskerville&lt;/b&gt; is yet another elegant web font. Based on the American Type Founder&apos;s Baskerville from 1941, Libre Baskerville is optimized for body text (approximately 16px) and looks beautiful on typical web apps as well.&lt;/p&gt;

&lt;p&gt;To include a font from Google Fonts in your app, just link to their stylesheet with the “family” querystring parameter set to whatever you want as I’ve done on this very page. If there are multiple, you can separate them using the filter character (|).&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://fonts.googleapis.com/css?family=Rubik|Inter|Mulish|Roboto|Libre+Baskerville|Open+Sans|Lato&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@todo: Create a placeholder app on github.io using bootstrap components to &lt;span class=&quot;nb&quot;&gt;test &lt;/span&gt;these fonts.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Introducing Comment Monk: simple comment hosting system for static blogs and websites</title>
   <link href="https://prahladyeri.github.io/blog/2024/06/intoducing-comment-monk.html"/>
   <updated>2024-06-07T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/06/intoducing-comment-monk</id>
   <content type="html">&lt;p&gt;I wanted to implement a comment hosting system for my static blog—something as simple as the basic WordPress.org commenting feature, with fields for the user’s name, website, etc. No complicated logins, sign-ups, or third-party platforms. The user reads your blog, posts a comment, and you approve it from the backend (or, alternatively, it gets auto-approved, and you receive an email notification). As simple as that!&lt;/p&gt;

&lt;p&gt;Since GitHub Pages doesn’t provide backend PHP scripting, I had to develop an entire backend app along with a frontend ECMAScript (JavaScript) script that could be plugged into a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt; block at the end of a blog post—typically the space reserved for comments. &lt;a href=&quot;https://github.com/prahladyeri/comment-monk/&quot;&gt;Comment-Monk&lt;/a&gt; is the result of that effort. I’ve made this app open-source and hosted it on GitHub so that it can be used by as many folks as possible.&lt;/p&gt;

&lt;p&gt;To use this app on your own static blog, simply download the repo and deploy it to a PHP web hosting service. It’s a lightweight script with an SQLite backend, intentionally kept small enough to be deployed on one of those affordable (even free) PHP hosting services.&lt;/p&gt;

&lt;p&gt;Once you start the app, you’ll be taken to the Install page, where you can register with your details and credentials, which you’ll use to log in as a superuser. It also asks for the website or domain of your blog where you’ll host the commenting system.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/cm/cm-login.png&quot; alt=&quot;cm-login&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once logged in and on the homepage, you can see this screen where you can view and manage comments. Right now, you can only view and delete comments, but more features are on the way in upcoming versions. You can also set your user preferences from the “Actions” menu in the top right.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/cm/cm-dashboard.png&quot; alt=&quot;cm-dashboard&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Most importantly, you can click on the “Client Snippet” button, which will guide you through implementing the frontend HTML code to embed the comments.&lt;/p&gt;

&lt;p&gt;Once you’ve done that, your static blog should look something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/cm/cm-client.png&quot; alt=&quot;cm-client&quot; /&gt;&lt;/p&gt;

&lt;p&gt;At the place where you added the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;script&lt;/code&gt; tag, you should be able to see a comment block displaying all existing comments along with a submission form for posting new ones.&lt;/p&gt;

&lt;p&gt;Your audience can read your content and post comments. The backend validates the URI (Uniform Resource Identifier), and if a registered domain is found, it creates an entry for that comment, which will be reflected on the administrator’s (YOUR) dashboard. I think this is about as simple as it can get!&lt;/p&gt;

&lt;p&gt;The comment block has a very basic and minimal design by default, but you can fully customize it by editing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/static/cm-client.css&lt;/code&gt; on the backend, which is used for styling.&lt;/p&gt;

&lt;p&gt;I hope you find this system useful for your static blog. If you encounter any issues, don’t hesitate to raise them on the &lt;a href=&quot;https://github.com/prahladyeri/comment-monk/&quot;&gt;GitHub tracker&lt;/a&gt;. Happy coding!&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;edit-2024-11-07&quot;&gt;&lt;strong&gt;Edit: 2024-11-07&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;While Comment-Monk was a great academic exercise, I realized through this experiment that hosting and maintaining your own custom solution isn’t as trivial for an indie tech blogger as it may seem. To be honest, it’s not even necessary. Between the complexity of maintaining a custom comment system and the ad-ridden, privacy-unfriendly platforms like Disqus, I found &lt;a href=&quot;https://giscus.app/&quot;&gt;Giscus&lt;/a&gt; to be a good middle ground. That’s why I made the switch.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to fix "silent error handling" of PDO errors after upgrading to PHP 8</title>
   <link href="https://prahladyeri.github.io/blog/2024/06/how-to-fix-silent-error-handling-pdo-php8.html"/>
   <updated>2024-06-07T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/06/how-to-fix-silent-error-handling-pdo-php8</id>
   <content type="html">&lt;p&gt;PHP 8.x is filled with hidden land mines which can suddenly trip the unaware PHP coder, especially when porting from legacy frameworks like CodeIgniter or CakePHP to PHP 8.x systems. One such issue is PDO’s default exception handling method.&lt;/p&gt;

&lt;p&gt;Prior to PHP 8, PDO’s default exception handling mode was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PDO::ERRMODE_SILENT&lt;/code&gt; which means our apps deployed on production never expected any actual PDO Exceptions, they instead handled database errors by looking up the error code after the fact with something like CodeIgniter’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$CI-&amp;gt;db-&amp;gt;error()&lt;/code&gt; function, for instance.&lt;/p&gt;

&lt;p&gt;But starting from PHP 8.0, the &lt;a href=&quot;https://www.php.net/manual/en/pdo.error-handling.php&quot;&gt;default exception handling mode for PDO is changed to PDO::ERRMODE_EXCEPTION&lt;/a&gt;. This means most PHP apps developed with the prior mindset can expect sudden or subtle bugs depending on how they were coded!&lt;/p&gt;

&lt;p&gt;If you usually keep PHP’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;display_errors&lt;/code&gt; INI setting to 0 to avoid unnecessary depreciation notices on your production, then you are in for a great nasty surprise on PHP 8.x! Because if any database error occurs, you will never come to know of it. Your usual error code based handling won’t work here as PDO uses the exception handling mode by default. Which means PDO still raises this exception, only it won’t be displayed anywhere due to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;display_errors&lt;/code&gt; INI setting. It will silently crash your script execution with no output and a 500 HTTP status code.&lt;/p&gt;

&lt;p&gt;In one of the companies where I contract, this is exactly what happened recently. The following CI3 code is highly dependent on error code based checking - which is done inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db_error()&lt;/code&gt; helper function. But the sad thing is that the interpreter will never reach that point as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$this-&amp;gt;db-&amp;gt;query&lt;/code&gt; will throw an exception and halt the process immediately.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;insert into schedule(emp_id, place_id, sch_date, notes) values (?,?,?,?)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;db_error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;status&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;failure&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;message&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;status&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;success&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;message&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Successfully inserted record.&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;json_encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I had to debug by placing multiple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;error_log()&lt;/code&gt; statements to find out what’s going on here! Needless to say, even though Exception is the recommended way of handling PDO hiccups, we can’t change the whole app and its workflow for that alone, the cost would be too prohibitive for larger PHP apps like these. What we did instead was updated CodeIgniter’s database configuration and changed the PDO’s error handling option back to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PDO::ERRMODE_SILENT&lt;/code&gt; instead:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# /application/config/database.php&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#..&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#..&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;default&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;options&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PDO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ATTR_ERRMODE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PDO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ERRMODE_SILENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you don’t want to do this but take the righteous path of using PDO Exceptions instead, then make sure you report and display ALL errors (or at least all barring depreciation notices):&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;//error_reporting(E_ALL);&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;error_reporting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;E_ALL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;E_NOTICE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;E_DEPRECATED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;ini_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;display_errors&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After that, you can handle PDO Exceptions in your code normally. But the important thing is that you’ll now be able to see the errors and know what’s going on. They won’t be treated silently like before.&lt;/p&gt;

&lt;p&gt;Happy Coding!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to install composer packages locally or offline</title>
   <link href="https://prahladyeri.github.io/blog/2024/06/how-to-install-composer-packages-locally-offline.html"/>
   <updated>2024-06-01T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/06/how-to-install-composer-packages-locally-offline</id>
   <content type="html">&lt;p&gt;The usual way of installing composer packages with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;composer require &amp;lt;foo&amp;gt;&lt;/code&gt; is the standard. But occasionally, we might want to install the packages from our local computer to save Internet bandwidth or some other reason.&lt;/p&gt;

&lt;p&gt;Unlike Python’s pip, PHP’s composer doesn’t offer us a straightforward path to install from an archived package file here by running something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install -i &amp;lt;package-file-name&amp;gt;&lt;/code&gt;. However, there are two paths or workarounds to achieve this as follows:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[1] The artifact way.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is &lt;a href=&quot;https://aaronsaray.com/2021/install-composer-package-from-local-zip/&quot;&gt;the more preferable way&lt;/a&gt;, especially if your goal is to store the composer package files (*.zip) on your storage drive for long term use. But do note that if you go this route, you will be responsible for installing any dependency packages your app may have.&lt;/p&gt;

&lt;p&gt;To do this, just edit your app’s composer.json file and add your storage path in the repositories section:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s2&quot;&gt;&quot;repositories&quot;&lt;/span&gt;: &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;artifact&quot;&lt;/span&gt;,
    &lt;span class=&quot;s2&quot;&gt;&quot;url&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;./packages&quot;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The ./packages could be any arbitrary local path on your computer. When you specify this path, composer will look in this path before trying to fetch the packages from &lt;a href=&quot;https://packagist.org&quot;&gt;https://packagist.org&lt;/a&gt; when you run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;composer require&lt;/code&gt; command. However, you can also disable the &lt;a href=&quot;https://getcomposer.org/doc/05-repositories.md#disabling-packagist-org&quot;&gt;packagist.org repository completely&lt;/a&gt; if you want.&lt;/p&gt;

&lt;p&gt;Now where to get the actual composer packages? If you have recently installed any package, you can find them in the composer cache directory which is usually at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\Users\&amp;lt;username&amp;gt;\AppData\Local\Composer\files&lt;/code&gt;. Otherwise, you can run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;composer require foo&lt;/code&gt; command so that the file is saved there and you can save it to your storage. In theory, simply packaging the parent directory of the repo source containing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;composer.json&lt;/code&gt; file should also work but I haven’t tried that.&lt;/p&gt;

&lt;p&gt;But remember that while installing, you must ensure to install dependencies first. For example, if foo package depends on bar, you must install bar first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[2] The “copy package” way&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This one is a bit &lt;a href=&quot;https://stackoverflow.com/a/60531553&quot;&gt;tricky and hackish&lt;/a&gt; way and only recommended for experienced coders. Sometimes, you will want to just “copy” the package directory from one project to another. As the linked post suggests, you can just edit your app’s composer.json, go to the autoload section and add the mapping to this newly copied package as follows:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;s2&quot;&gt;&quot;autoload&quot;&lt;/span&gt;: &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;psr-4&quot;&lt;/span&gt;: &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;App&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;src/&quot;&lt;/span&gt;,
      &lt;span class=&quot;s2&quot;&gt;&quot;Funtastic&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;FooBar&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;lib/foobar/src&quot;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After that, you can run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;composer dump-autoload&lt;/code&gt; so that composer will integrate this package and it becomes available in your app.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Thoughts on PHP routing strategies</title>
   <link href="https://prahladyeri.github.io/blog/2024/05/thoughts-on-php-routing.html"/>
   <updated>2024-05-29T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/05/thoughts-on-php-routing</id>
   <content type="html">&lt;p&gt;Routing is a tricky business in PHP simply because there is no one standard way of doing it. Starting from the most simple and benign (but inflexible) routing strategy of handling every incoming request in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.php&lt;/code&gt; script to the highly flexible (but arcane and opinionated) routing libraries of Symfony, Laravel and the likes, there are all kinds of options in between and it’s up to you, as a developer, what routing strategy to use.&lt;/p&gt;

&lt;p&gt;As a freelance programmer, while considering a routing strategy for the long term, it helps to remember two major deployment constraints on your production server:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Your client may want the PHP app to be installed to a specific subfolder like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://xyz.com/subfolder/&lt;/code&gt; instead of the root domain or subdomain.&lt;/li&gt;
  &lt;li&gt;Your client may not give you full permission to override the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.htaccess&lt;/code&gt; file, hence any strategy to rewrite all URL paths to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.php&lt;/code&gt; may not work.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Especially for the second point, the common or usual approach is to simply override your .htaccess like this:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;DirectoryIndex index.php
RewriteEngine On
RewriteBase /
RewriteCond %&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;REQUEST_FILENAME&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt;
RewriteCond %&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;REQUEST_FILENAME&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt;
RewriteRule ^&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;.&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;/index.php/&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;L]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This simple Apache rewrite rule will send all script requests to your index.php where your routing component can easily handle it with something like this:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$router&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Hello, World&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But what if you’re not allowed to override the .htaccess file? I imagine many modern PHP routing libraries will fall apart if that happens! But some frameworks like CodeIgniter provide useful functions to handle this situation. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;site_url()&lt;/code&gt; function is especially useful here. Using it, I can refer to a URL route irrespective of whether the request came by overriding the index.php or not. Your user may have typed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xyz.com/index.php/foo/bar&lt;/code&gt; instead of just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xyz.com/foo/bar&lt;/code&gt; but in both cases, the request will arrive at the foo controller’s bar method. And if you want to redirect to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo/bar&lt;/code&gt; URL, you can simply do it by using site_url() function:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Location: &apos;&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;site_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;foo/bar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead of hard-coding the URL or making any assumptions, this is a better approach as it will work irrespective of whether .htaccess was allowed to be overridden or not.&lt;/p&gt;

&lt;p&gt;Similarly, for the first issue of using a subfolder, there is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base_url()&lt;/code&gt; function in CodeIgniter. You configure this one time variable initially and use it throughout your code like this:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;?=base_url()?&amp;gt;static/css/app.css&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I don’t have to hard code any static path here and the web app becomes extremely portable. I’m sure other modern frameworks like Symfony and Laravel must be providing equivalent features and I’m yet to look at them. But overall, I find these OOP frameworks much too complex, which is almost like using nuclear energy to ignite a kerosene lamp! Maybe Big Tech and large IT firms who are into scaling might find them useful but for small freelancers and bloggers, I think the old school PHP frameworks still work the best. What do you think?&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Working with multi-queries in CodeIgniter</title>
   <link href="https://prahladyeri.github.io/blog/2024/05/working-with-multi-queries-in-ci3.html"/>
   <updated>2024-05-27T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/05/working-with-multi-queries-in-ci3</id>
   <content type="html">&lt;p&gt;Running multi-queries (a bunch of text containing arbitrary DML/DDL statements) is highly unreliable and not an exact science in CodeIgniter or even PHP for that matter. The Internet is filled with posts like &lt;a href=&quot;https://stackoverflow.com/questions/8999959/executing-multiple-queries-in-codeigniter-that-cannot-be-executed-one-by-one&quot;&gt;this&lt;/a&gt;, &lt;a href=&quot;https://stackoverflow.com/questions/51257526/how-to-run-multi-queries-at-once-in-codeigniter&quot;&gt;this&lt;/a&gt;, and &lt;a href=&quot;https://stackoverflow.com/questions/21869603/running-multiple-queries-in-model-in-codeigniter&quot;&gt;this&lt;/a&gt; but you can’t depend on these solutions in most situations due to the difference between how each database driver handles it.&lt;/p&gt;

&lt;p&gt;Most of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;query()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec()&lt;/code&gt; statements often don’t run at all due to the batch structure (begin and commit trans) not handled properly as per that driver’s liking. It may also happen that due to one error in a single SQL statement, the whole text is ignored and yet, there is no error prompt, the folks will think that multi-query executed successfully when, in fact, it didn’t. Hence, it is often so tempting to do something like this in CodeIgniter but doesn’t always work (especially with SQLITE databases):&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;file_get_contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;APPPATH&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/core/init.sql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The only approach that is guaranteed to work reliably here is to break your multi query into individual SQL chunks by splitting the semicolon and then run each individual statement like this:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$sqls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;explode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;array_pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$sqls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$sqls&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$statement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$statment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$statement&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;//echo $statement;&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$statement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;   
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will naturally rule out adding of any comments or whitespaces above or below the statements as you would in a script because that might cause an error. However, a simple and clean SQL script such as this one will work flawlessly:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;drop&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;exists&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;drop&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;exists&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;drop&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;exists&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;quantities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;settings&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;integer&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;primary&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varchar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varchar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;update_status&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;integer&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;primary&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;last_update&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;datetime&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prices&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;integer&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;primary&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;price&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;decimal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;price_dt&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;datetime&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;quantities&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;integer&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;primary&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;integer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;qty&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;decimal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;qty_dt&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;datetime&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Troubleshoot the 'Invalid Package' error on Android APK installation</title>
   <link href="https://prahladyeri.github.io/blog/2024/04/how-to-fix-the-dreaded-package-error-android.html"/>
   <updated>2024-04-10T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/04/how-to-fix-the-dreaded-package-error-android</id>
   <content type="html">&lt;p&gt;When you try to manually install an Android APK on your phone, you are typically stuck with an error similar to this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/android-package-error.jpg&quot; alt=&quot;screenshot showing Android package invalid error&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Stack Overflow is filled with posts like &lt;a href=&quot;https://stackoverflow.com/q/77749878/849365&quot;&gt;this&lt;/a&gt;, &lt;a href=&quot;https://stackoverflow.com/q/46973058/849365&quot;&gt;this&lt;/a&gt; and &lt;a href=&quot;https://stackoverflow.com/q/76145397/849365&quot;&gt;this one&lt;/a&gt; but apparently, there seems to be no consensus regarding the cause or even a generally accepted fix for this problem.&lt;/p&gt;

&lt;p&gt;The very obvious thing to first do is clean the project and rebuild the APK in Android Studio. Many answers suggest this and if it works for you then well and good. But if rebuilding the APK doesn’t resolve this problem, there are three other solutions you can try based on my practical experience with Android development so far:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;If you are signing the final APK with a key, it could be an issue depending on how you sign. Some older android versions may not support the newer V2 signature format, so it’s recommended to sign your APK using &lt;a href=&quot;https://stackoverflow.com/a/46973194/849365&quot;&gt;both V1 and V2 formats&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;I found another solution from &lt;a href=&quot;https://android.stackexchange.com/q/252577/38760&quot;&gt;this post&lt;/a&gt; and it often works in many situations. What values you’ve set for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;minSdkVersion&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;targetSdkVersion&lt;/code&gt; properties (typically in the app’s build.gradle file) are often the culprit here due to the way android works. Apparently, &lt;a href=&quot;https://www.xda-developers.com/android-14-block-outdated-apps/&quot;&gt;some newer phones don’t like APKs which support versions lower than M&lt;/a&gt;. In my case, I had kept &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;minSdkVersion&lt;/code&gt; to 19 (KitKat) and once I changed it to 23 (Marshmallow), this package error mysteriously disappeared! If your user base consists of older android versions, you’ll have to release multiple APKs to cater to that segment in this case.&lt;/li&gt;
  &lt;li&gt;Apparently, another way this error goes away is by setting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exported&lt;/code&gt; attribute for your main Activity in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AndroidManifest.xml&lt;/code&gt; like below:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;activity&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;android:name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.LoginActivity&quot;&lt;/span&gt;
	&lt;span class=&quot;na&quot;&gt;android:exported=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;intent-filter&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;action&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;android:name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;android.intent.action.MAIN&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;category&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;android:name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;android.intent.category.LAUNCHER&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;/intent-filter&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/activity&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Please let me know whether this resolves your APK package error through comments. Also let me know if you know some other way to fix this error.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Intro to DOMPDF - light and simple PHP library to generate PDF documents</title>
   <link href="https://prahladyeri.github.io/blog/2024/04/how-to-generate-pdf-php-dompdf.html"/>
   <updated>2024-04-05T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2024/04/how-to-generate-pdf-php-dompdf</id>
   <content type="html">&lt;p&gt;Generating PDF documents from your HTML output is a very common requirement and there are several open source libraries to accomplish this in PHP. I came across this need for my project recently and I evaluated many popular ones such as &lt;a href=&quot;https://github.com/tecnickcom/TCPDF&quot;&gt;TCPDF&lt;/a&gt;, &lt;a href=&quot;https://github.com/mpdf/mpdf&quot;&gt;mpdf&lt;/a&gt;, &lt;a href=&quot;https://github.com/Setasign/FPDF&quot;&gt;FPDF&lt;/a&gt;, etc. But the one that truly stood up to my evaluation in terms of efficiency (minimal footprint) and ease of implementation was &lt;a href=&quot;https://github.com/dompdf/dompdf&quot;&gt;DOMPDF&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;DOMPDF is a pure PHP component which can be seamlessly installed using composer:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;composer require dompdf&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will pull the dompdf component in your vendor folder, it occupies just about 7 MB of disk space which is great for the kind of work it does. Once you add this component with composer, you can start generating PDFs right away using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dompdf\Dompdf&lt;/code&gt; object:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://example.com/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Dompdf\Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;isRemoteEnabled&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;isHtml5ParserEnabled&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$dompdf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Dompdf\Dompdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//$dompdf-&amp;gt;setBasePath($url);&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$dompdf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;loadHtml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;file_get_contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$dompdf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setPaper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;a4&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;portrait&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//$dompdf-&amp;gt;setPaper( array(0,0,822,848), &apos;portrait&apos; );&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$dompdf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$output&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$dompdf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;file_put_contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;output.pdf&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//$dompdf-&amp;gt;stream();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Done&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Needless to say, an important caveat or limitation here is that like most other libraries, dompdf will not support any and every latest feature in the CSS specification. It is mostly a CSS 2.1 compliant library and supports a few CSS 3 properties. In practice, I’ve seen most simple HTML tables and paragraphs set with attributes like bg/fg colors, fonts, margins, etc. work flawlessly. Styling with Bootstrap 3.x works too but 4 and above still has some issues.&lt;/p&gt;

&lt;p&gt;In the above example, I’ve taken the HTML output of &lt;a href=&quot;https://example.com/&quot;&gt;example.com&lt;/a&gt; which has very basic styling to generate the PDF. &lt;a href=&quot;/uploads/dompdf-output.pdf&quot;&gt;Here is the generated PDF output&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this code, we first set the options for PDF generation using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dompdf\Options&lt;/code&gt; object. I’ve enabled &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isRemoteEnabled&lt;/code&gt; as my HTML content uses external CSS links. I’ve also enabled &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isHtml5ParserEnabled&lt;/code&gt; as I often need some HTML5 features. You can find more details on their &lt;a href=&quot;https://github.com/dompdf/dompdf/wiki&quot;&gt;wiki page&lt;/a&gt; about these options.&lt;/p&gt;

&lt;p&gt;After that, you create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dompdf\Dompdf&lt;/code&gt; object and load the HTML.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$dompdf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;loadHtml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;file_get_contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After that, you set any other options you need (such as paper size) and call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$dompdf-&amp;gt;render()&lt;/code&gt; method followed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$dompdf-&amp;gt;output()&lt;/code&gt; method. Lo and behold, your PDF document is now generated!&lt;/p&gt;

&lt;p&gt;You can either save the generated result using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file_put_contents()&lt;/code&gt; as I’ve done in this example or you can also optionally call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$dompdf-&amp;gt;stream()&lt;/code&gt; in which case the PDF will be output to the browser window directly.&lt;/p&gt;

&lt;p&gt;That’s it, so simple! Enjoy your pdf generation process using dompdf and if you face any issues, let me know using the discussion links. Happy Coding.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>We are not creating the "right" kind of AI</title>
   <link href="https://prahladyeri.github.io/blog/2023/11/we-are-not-creating-the-right-kind-of-ai.html"/>
   <updated>2023-11-30T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2023/11/we-are-not-creating-the-right-kind-of-ai</id>
   <content type="html">&lt;p&gt;Arts, crafts, literature, even things like computer programming have been the domain of humans since time immemorial. Machines were historically meant to do menial tasks where the human couldn’t afford the time or resources. The classic example here is the type-writer, a remarkable machine that helped humans draft letters in a standardized and mechanized form which could then be used for documenting information in an objective form.&lt;/p&gt;

&lt;p&gt;The printing press was an extension of that, helped humans create more durable and finished works, or &lt;em&gt;books&lt;/em&gt; out of the drafts which they had penned down, either type written or hand written. A plethora of other discoveries and inventions followed including the telegraph, telephone, gramophone, musical instruments, etc. right until the dawn of twenty first century when personal computing with advanced software became a very popular thing almost everywhere. All of them were &lt;em&gt;utilitarian&lt;/em&gt; inventions, in the sense that they served some purpose in advancing and enhancing the tribe of humans. Even mobiles and smart phones are extremely utilitarian (though they also come with a lot of cruft!)&lt;/p&gt;

&lt;p&gt;But AI is no such thing, there is nothing utilitarian at least in the current forms of AI which are highly discussed and talked about today. The only utility or purpose they seem to have is to get rid of workers and staff through automation, and this itself is often celebrated in social media as if an end in itself. But show me where is the utility in this, for the society, for the tribe?&lt;/p&gt;

&lt;p&gt;In that sense, the machine (AI) is an ideal servant but not such a good master. For starters, it’s a clumsy concept to begin with considering you’re coercing a binary logic machine to do tasks which often have very little to do with logic - crafts like singing, writing, teaching, etc. aren’t pure logic things, there is also a lot of nuance, chaos and heart involved.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/laptop-coffee-mobile.jpeg&quot; alt=&quot;laptop-coffee-mobile&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Another thing is that trying to take away creative jobs from humans and giving it to robots while the menial ones (like data entry, manual labor, household work, etc.) are still being done by humans is an insult to humanity itself. It’s as if the authoritarians want to tell the humans, “This is your worth at most, nothing more. You’re too lowly to do something creative or different or unique”.&lt;/p&gt;

&lt;p&gt;Human skills, inquisitiveness and thirst for knowledge and craft will eventually die off if all creative tasks are taken over by the machines. Consider the already degraded thirst for GK or General Knowledge ever since google search became a thing. There was a time once when kids used to watch the KBC Show (Indian adaptation of &lt;em&gt;Who wants to be a Millionaire&lt;/em&gt;) with tremendous awe and enthusiasm, but no longer today. Today, a simple google search will tell them the correct answer for the quiz question, why bother with discussing the actual answer, the lore and the facts behind it, etc! This is just one example of a creative skill which is rapidly degrading among humans and already taken over by Turing machines.&lt;/p&gt;

&lt;p&gt;Imagine a world where everything is “AI generated”, everything including books, songs, music, movies, etc. One can imagine the irony in humans consuming and enjoying such content - the very same humans who have the potential to create poetry, prose, songs, software and even artificial intelligence itself!&lt;/p&gt;

&lt;p&gt;I abhor to imagine the state of such a dystopian society which is so divided between the chosen few “haves” administering the AI, and the masses of “have nots” doing all the menial jobs just to sustain the “AI ecosystem”. Are we already seeing signs of such a dystopian society being created in front of our eyes? You answer.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unpopular opinion: desktop GUI is the most efficient and fulfilling way of human-computer interaction</title>
   <link href="https://prahladyeri.github.io/blog/2023/11/desktop-gui-is-most-efficient-way-of-human-pc-interaction.html"/>
   <updated>2023-11-17T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2023/11/desktop-gui-is-most-efficient-way-of-human-pc-interaction</id>
   <content type="html">&lt;p&gt;In many ways, the classical paradigm of the “WEB” as a mere portal or information resource was the most likable and valuable one in the utilitarian sense. These were the earliest days of the Internet when most of the actual Human-Computer interaction took place in the form of a Windows Desktop GUI and people went online to just “look up” some information they wanted.&lt;/p&gt;

&lt;p&gt;Then something changed in early 2000s as more and more of these sites started becoming “apps” and started doing the actual computing. It solved a real problem though as you no longer had to install these individual EXE/DLL/OCX files on every user’s machine and that ONE centralized place called “Web Server” was all that’s needed to be updated and maintained. Thus, some of the “fat clients” started offloading their work to the “thin clients” such as web-browsers more and more, and that was also a good place to be in.&lt;/p&gt;

&lt;p&gt;But the worst thing to happen from an end-user utilitarian perspective was perhaps the “Cloud computing” paradigm. At this point, the Gods of Internet decided that even the “thin clients” no longer need to exist and that ONE client called the “Web Browser” should take on all functions of the operating system as per the directions received from that ONE place called “Web Server”. Circa 2008-10 is where I put the start of this kind of development, almost a decade or so ago from now.&lt;/p&gt;

&lt;p&gt;That’s the trajectory we are roughly in right now as browsers, on their way to become a fully fledged OS, are getting stuffed with updates upon updates every week and shoved down the user’s throats. This approach has created several problems for everyone:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Too much bloat as one software must cater to everyone’s needs.&lt;/strong&gt; The “least common user” problem had always existed, no doubt, even with real OSs like Windows and even Office Software. But an OS has to just do the bare minimum of bringing up basic services like graphics, sound, background services, etc, they don’t have to worry about business logic or application layer. Web browsers trying to become OS, on the other hand, do have to worry about ensuring that each and every site loads correctly based on W3C standards and sadly, these standards are defined by what’s good for the enterprise sector than the ordinary or average end user.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Open invitation to authoritarian software like DRMs, Adware, blobs, etc.&lt;/strong&gt; DRMs and Adware have already made their entry into most browsers and though there are extensions like ublock-origin to block adware, there is also a sustained effort to fight them with all kinds of things like restricting their features through the upgraded manifest version itself, and things like coercing the user to disable those extensions in order to view the content.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Use of Internet as propaganda tool.&lt;/strong&gt; Because capitalism is very much built into our system, what is stopping those with lot’s of monies from using it to push propaganda through these ads or even just push propaganda in pure form? Users become sitting ducks to the subtle propaganda perhaps even without realizing it.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Inefficient and bloated JavaScript.&lt;/strong&gt; Instead of taking time-tested and efficient best practices of desktop development in C#/C++/Java and putting them in the browser, what they’re doing now is use the bloated JS/NPM paradigm to write even desktop apps using frameworks like Electron! In this paradigm, the desktop app is a whole bundle of universe containing the Node.js web server, the Blink rendering engine and a browser like chromium that renders the UX. There are many ways to achieve platform independence across different desktop kinds viz. Windows, Linux and Mac, but using a layered solution like Electron is the worst way to go about it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I don’t know where the future of web is going, how long this “cloud and data” related madness will continue but until it lasts, those who can see the problem here can revert back to using desktop apps as much as possible. Desktop apps are typically seen as intrusive and distrustful these days, thanks to clever marketing by the cloud folks, but more and more desktop apps are getting open sourced every day and if you think about it, an open source desktop app can be way more trustworthy than a website containing blobs of minified JavaScript who’s working nobody knows?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/portfolio/dotnet-ordering-app.png&quot; alt=&quot;dotnet ordering app&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Let’s start celebrating desktop apps like we used to earlier. They’re not “old” as is often said, but they are &lt;em&gt;classical&lt;/em&gt;. It’s the Internet that ages by leaps and bounds, not the real world and desktop apps are as close to that real world as one could get!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Managing drafts in Jekyll while writing blog posts</title>
   <link href="https://prahladyeri.github.io/blog/2023/11/jekyll-maintaining-drafts-while-writing-blog-posts.html"/>
   <updated>2023-11-12T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2023/11/jekyll-maintaining-drafts-while-writing-blog-posts</id>
   <content type="html">&lt;p&gt;The conventional wisdom of maintaining draft posts in Jekyll is to &lt;a href=&quot;https://jekyllrb.com/docs/posts/#drafts&quot;&gt;store them in _drafts directory&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Drafts are posts without a date in the filename. They’re posts you’re still working on and don’t want to publish yet. To get up and running with drafts, create a _drafts folder in your site’s root and create your first draft:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;├── _drafts
│   └── a-draft-post.md
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But I found this conventional approach a bit cumbersome to follow as a daily driver. For one, at each point of time when you decide that a draft such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a-draft-post.md&lt;/code&gt; is done version, you must copy it from _drafts to _posts and also rename it with the date part.&lt;/p&gt;

&lt;p&gt;One solution here is to write a one time bash script or something for your workflow which does that automatically when run, but if you don’t want to do that, there is a cleaner approach which I’ve found:&lt;/p&gt;

&lt;p&gt;The other way of declaring drafts in Jekyll is to save it in the _posts itself as usual, and simply set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;published&lt;/code&gt; attribute in the front-matter section to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;---
layout: post
title: &apos;[Jekyll] Maintaining drafts while writing blog posts&apos;
tags: jekyll tricks
published: false
---

Saved as _posts/2023-11-12-jekyll-maintaining-drafts-while-writing-blog-posts.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This way, all you have to do is set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;published&lt;/code&gt; attribute to true and push the changes to your github repository! Of course, the downside here is that you might forget to change the date at the time of doing that and your published post will end up being back-dated by a couple of days. Personally, I’m quite tolerant of that and the difference won’t be much as I don’t like to keep a post in “draft” status for a long time anyway.&lt;/p&gt;

&lt;p&gt;In any case, this is just one approach for handling draft posts in the Jekyll workflow, there could be others.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to allow Ping (ICMP) requests through the Windows Firewall</title>
   <link href="https://prahladyeri.github.io/blog/2023/11/how-to-allow-ping-requests-through-windows-firewall.html"/>
   <updated>2023-11-09T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2023/11/how-to-allow-ping-requests-through-windows-firewall</id>
   <content type="html">&lt;p&gt;The ping.exe is a great tool provided by the Windows operating system to check and ensure if we are connected to another computer located across the Internet or even our own local LAN through the ICMP (Internet Control Message Protocol).&lt;/p&gt;

&lt;p&gt;However, it often happens that even if the remote machine is online, the ping request fails due to firewall or other settings and throws a “General Failure” or similar error message:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/ping-icmp/ping-general-failure.png&quot; alt=&quot;ping icmp general-failure&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If your Internet is working fine otherwise and you’re able to browse the websites, the culprit in most cases is a Windows firewall rule which is blocking the outbound ping requests. So, head over to the Windows firewall settings through the Control Panel-&amp;gt;Administrative Tools or by simply running the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wf.msc&lt;/code&gt; tool in the run dialog. Open the outbound rules settings and you should be able to see the following three rules set for ICMPv4-Out echo requests as shown below. Make sure the “File and Printer Sharing (Echo Request - ICMPv4-Out)” rule is enabled for the currently active profile (Domain/Private/Public).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/ping-icmp/wf-outbound.png&quot; alt=&quot;windows firewall outbound icmp&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now, unless you’ve changed something drastically in these firewall settings, there is a good chance that that they should be enabled by default. If not, then simply enable them and your ping requests should start working after that:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/ping-icmp/ping-success.png&quot; alt=&quot;ping icmp success&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now, it might happen in some cases that enabling this firewall rule for ICMP still doesn’t work and you’re still getting the ping errors. This depends on whether the currently active profile for your network connection is Domain, Private or Public, and which remote machine you’re trying to access here. If you observe the firewall rules closely, you’ll see that only the Domain profile is allowed to make a ping request to remote websites such as google.com, other profiles are limited to ping only in the local subnet:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/ping-icmp/wf-detail.png&quot; alt=&quot;ping wf-detail&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Microsoft, in their great wisdom, thought that if your connection profile is Private or Public, you shouldn’t have any need to ping an Internet resource like google.com! In this case, you’ll have to either change your network connection profile to Domain through the control panel, or simply add a new outbound firewall rule that allows all outbound ICMP requests. If you choose to do the latter, you can then disable the built-in rules that you enabled earlier.&lt;/p&gt;

&lt;p&gt;Hopefully, your outbound ping requests should start working by now!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How I made my web pages load 10x faster</title>
   <link href="https://prahladyeri.github.io/blog/2023/06/how-i-made-my-web-pages-load-10x-faster.html"/>
   <updated>2023-06-07T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2023/06/how-i-made-my-web-pages-load-10x-faster</id>
   <content type="html">&lt;p&gt;Update: As of 17th June, 2024, I have also started using font-awesome and google-fonts which goes directly against my principle of “no-cruft”! And while the point of this article still stands in theory, I came to learn that there are pragmatic limits to how far you can stretch minimalism. At some point, utilitarianism starts overriding minimalism.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Update: As of 1st November, 2023, I have switched to the default Jekyll site generator from Pelican as I found the deployment and Github Pages workflow easier. However, the point of this article still stands.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;The most typical advice you get when it comes to improving site performance these days is purely technical things like using CDNs or other serving optimization, using X hosting mechanism instead of Y, going serverless, cache optimization, etc.&lt;/p&gt;

&lt;p&gt;I’m not talking about those things, I’m talking about the old school millennial way of optimizing things which is by reducing clutter and subscribing to the ideals of minimalism and common sense!&lt;/p&gt;

&lt;p&gt;The way our websites look and perform reflects our mindset in some ways, all that clutter and clunkiness that could be easily avoided still makes it to the web design for God knows what reason and keeps clogging our network bandwidth.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/lan-switch.webp&quot; alt=&quot;lan-switch&quot; /&gt;&lt;/p&gt;

&lt;p&gt;“It’s too bland and simple Prahlad, it doesn’t look professional enough”, they keep saying. Unfortunately, most of us have been trained to appreciate complex and clunky solutions in life instead of simple ones! This &lt;a href=&quot;https://web.archive.org/web/19990117033159/http://pages.ebay.com/aw/index.html&quot;&gt;simple and bland looking eBay website from late 1990s&lt;/a&gt;, for example, allowed you to do a bunch of useful things with way more efficiency than today’s equivalents. From allowing a user to register and login to adding items to a shopping cart to processing the payment by directing the user to a gateway and then bringing them back to the “thank you” page - more utility, less clunkiness.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/ebay_1999.webp&quot; alt=&quot;web page from 1990s&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Today’s apps also do the exact same thing but they require 10x more resources due to all the bloat and clunkiness across the development stack. We need a hundred different npm packages for transpilers, minifiers, compressors, etc. and still the end result is clunkiness in user experience. The websites of 1990s used to run on a server with 32 megabyte RAM chip and powered by an Intel 486 processor!&lt;/p&gt;

&lt;p&gt;An attitude of simplicity and minimalism is the urgent need of the hour in both our society and IT systems. Once you start questioning your confused and complicated stack with, “Do I really need this X layer or Y framework or Z library?”, you’ll find that the answer you’ll get is “NO” in most of the cases.&lt;/p&gt;

&lt;p&gt;A few years back when this minimalism thing stuck me, I decided that I will de-clutter my website as much as possible. The first thing I did was to get rid of the free shared hosting and migrated my site from a &lt;a href=&quot;https://prahladyeri.github.io/blog/2019/05/wordpress-to-pelican-in-24-hours.html&quot;&gt;wordpress.org instance to a statically hosted github pages site&lt;/a&gt;. Some time after that, somewhere around the pandemic period, I decided to get rid of my domain name too. Why waste INR 1,000 each year when github.io provides me a subdomain which is sufficient for almost every use case I can think of as a freelance programmer.&lt;/p&gt;

&lt;p&gt;This is the kind of minimalist attitude we must bring in our lives. Imagine if a corporate IT or MNC firm decides to use its resources with such efficiency, imagine the kind of savings it’d make. All those savings can be used to pay off their engineers who are getting laid off and retain them instead of spending on costly AWS services which sustain that clunkiness!&lt;/p&gt;

&lt;p&gt;If you think that even after switching from WP to a statically hosted site, was it even possible to bring even more minimalism to a website, then the answer is a resounding YES! Below are some measures which I took (and still taking as it’s a work in progress) for this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get rid of front-end frameworks like bootstrap and material-ui, write your own CSS/JS.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Whether it’s old school stacks like jquery/bootstrap or more modernish ones like react/material-ui, they all add some clutter and clunkiness to your site. People today have somehow got this toxic and pathological belief that each and every website or app must absolutely use these frameworks and libraries in order to look professional. I have nothing against these tools but it’s quite apparent that they aren’t needed for all use cases. A statically hosted blog such as this one is a perfect example.&lt;/p&gt;

&lt;p&gt;If you are building a large ecommerce site or something, it could be justified but even there, you should ask yourself why you need a tool before just implementing (consider the eBay site from 1990s example).&lt;/p&gt;

&lt;p&gt;Thus, bootstrap and jquery were the things I got rid of as I didn’t need them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use UNICODE emojis instead of icon libraries like font-awesome.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A clunky icon or image library doesn’t need to be downloaded each time if all you need is to display some symbols. The Unicode standard has evolved enough by now so that most &lt;a href=&quot;https://emojipedia.org/objects/&quot;&gt;symbols and emojis&lt;/a&gt; are part of the standard and available on all devices. As you can see on my &lt;a href=&quot;/about&quot;&gt;about page&lt;/a&gt;, I’ve used emojis like books 📗, tools 🛠, pointing finger 👉 and even hour-glass ⌛ to convey my point across. You don’t need any icon or image library for this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resize/Compress your images and resources.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Do you even need that hi-resolution and thousands of pixels wide image which occupies triple digit MBs on the hard disk? In most cases, you don’t. In most cases, you specifically control the “width” or “height” attribute of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements which usually need to be much smaller than their actual size. But the entire image is still downloaded to the client browser each time. Better idea is to just compress the image using a software like GIMP or Paintbrush by reducing its dimensions and that’s it.&lt;/p&gt;

&lt;p&gt;I haven’t gotten around minifying my CSS and JS yet because the site’s average load is hardly ~500KB as it is. But guess I’d be able to shave off some more on that once I implement this in the tool chain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Convert your PNGs and JPEGs to WEBPs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This thing is still a work in progress but I’ve started converting my site’s PNGs and JPEGs to WEBP format. On average, &lt;a href=&quot;https://developers.google.com/speed/webp&quot;&gt;WEBP lossless images are 26% smaller in size compared to PNG&lt;/a&gt;. These savings mean gigantic potential efficiency in terms of less bandwidth utilization and resource consumption over the lifetime of a website! It’s a wonder why we are still stuck using PNGs and JPEGs at all and the web hasn’t already moved to WEBPs entirely yet!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It’s all about minimalism attitude and mindset.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think why is it that sites like &lt;a href=&quot;https://news.ycombinator.com/news&quot;&gt;Hacker News&lt;/a&gt;, &lt;a href=&quot;https://tildes.net/&quot;&gt;Tildes&lt;/a&gt;, &lt;a href=&quot;https://lobste.rs/&quot;&gt;Lobsters&lt;/a&gt;, etc. are getting so popular among the nerds and intellectuals of this society. Think why is &lt;a href=&quot;https://old.reddit.com/&quot;&gt;Old Reddit&lt;/a&gt; still up and running despite them trying everything under the sun to publicize the new and clunky version when it came! When Wikipedia tried the clunkiness way some time back, the Internet soon start filling up with &lt;a href=&quot;https://www.howtogeek.com/866617/how-to-get-the-old-wikipedia-layout-back/&quot;&gt;help posts like these&lt;/a&gt; for switching back to the old Vector theme. The world needs to understand that minimalism is the future, there just aren’t enough resources in this world to sustain the clunkiness anymore.&lt;/p&gt;

&lt;p&gt;Internet bandwidth also isn’t an abundant resource. It tended to be cheap ever since the data revolution began about a decade ago but that may not be the case always. ISPs across the world are already tightening the screws on this, they are gradually increasing the data cost. Jio and Airtel recharges are much pricier today than they were a year ago. Once that starts happening, your cloud services will need to bring more efficiency and performance instead of more features in order to stay profitable, better start preparing for that day today itself instead of waiting for that day!&lt;/p&gt;

&lt;p&gt;And don’t fall for the wrongful narratives and agendas of those who keep justifying the clunkiness as part of their business model. &lt;a href=&quot;https://world.hey.com/dhh/they-re-rebuilding-the-death-star-of-complexity-4fb5d08d&quot;&gt;Merchants of complexity will always keep building their Death Stars&lt;/a&gt; because it’s their job as a business or corporate to think about their interest. But at the same time, it’s our job to not get distracted by it and stay minimal which is both in our and society’s best interest.&lt;/p&gt;

&lt;p&gt;In conclusion, this famous quote by MK Gandhi perhaps applies more in today’s world than it did in 1947:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;The world has enough for everyone’s needs, but not everyone’s greed.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content>
 </entry>
 
 <entry>
   <title>The power of minimalism: Why ecommerce and corporate websites need to de-clutter</title>
   <link href="https://prahladyeri.github.io/blog/2023/02/opinion-ecommerce-and-corporate-websites-need-to-adopt-some-minimalism-and-de-clutter.html"/>
   <updated>2023-02-01T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2023/02/opinion-ecommerce-and-corporate-websites-need-to-adopt-some-minimalism-and-de-clutter</id>
   <content type="html">&lt;p&gt;In the digital age, ecommerce and corporate websites often overwhelm users with cluttered designs and excessive elements. But as consumers gravitate toward simplicity, adopting a minimalist approach can not only improve user experience but also enhance site performance. Here’s why minimalism is the future of web design.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/grocery-cart.jpg&quot; alt=&quot;grocery-cart&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;why-minimalism-matters-for-web-design&quot;&gt;Why Minimalism Matters for Web Design&lt;/h2&gt;
&lt;p&gt;In the past, flashy designs and feature-packed websites were the norm. However, users today prefer clean, simple, and functional designs. Minimalism in web design focuses on eliminating unnecessary elements, creating a smoother and more enjoyable experience for your visitors.&lt;/p&gt;

&lt;h2 id=&quot;the-cost-of-cluttered-ecommerce-sites&quot;&gt;The Cost of Cluttered Ecommerce Sites&lt;/h2&gt;
&lt;p&gt;Cluttered ecommerce sites can cause user frustration, slower loading times, and even a higher bounce rate. Here’s why reducing clutter is essential for any business website.&lt;/p&gt;

&lt;h2 id=&quot;how-to-de-clutter-your-website-for-better-performance&quot;&gt;How to De-Clutter Your Website for Better Performance&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Use whitespace strategically&lt;/strong&gt; to guide users’ attention to important content.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Limit the number of colors and fonts&lt;/strong&gt; to create a cohesive visual experience.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Remove unnecessary scripts&lt;/strong&gt; and media files that slow down page load times.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Simplify navigation&lt;/strong&gt; by reducing the number of categories and menu items.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;inspiration-for-minimalist-web-design&quot;&gt;Inspiration for Minimalist Web Design&lt;/h2&gt;
&lt;p&gt;Looking for inspiration? Below are some well-known brands which embraced minimalism and reaped the benefits. These examples demonstrate how minimalism can enhance user experience by focusing on simplicity, functionality, and clarity. Whether it’s a search engine, ecommerce platform, or content-driven blog, minimalist design helps eliminate distractions and guides users to the most important parts of the website.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.google.com/&quot;&gt;Google Search&lt;/a&gt;&lt;/strong&gt; Google’s homepage is the epitome of minimalism. It’s simple, functional, and focuses on one thing: search. The absence of clutter makes the user experience seamless, and users know exactly what to do when they arrive on the page. A single search bar, minimal text, and lots of whitespace is all they need!&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.dropbox.com/&quot;&gt;Dropbox&lt;/a&gt;&lt;/strong&gt; Dropbox’s website emphasizes simplicity, focusing on clear calls to action (CTAs) and minimal design elements. Their homepage uses a simple layout with large, easy-to-read fonts and a consistent color scheme. A Straightforward navigation, clean typography, and no unnecessary design elements is what Dropbox users are looking for.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.apple.com/&quot;&gt;Apple&lt;/a&gt;&lt;/strong&gt;: Apple is known for its sleek, minimalist design philosophy across both its products and its website. The website is clean, with plenty of whitespace and a focus on product visuals. It uses large, high-quality images to showcase its devices without overwhelming users with unnecessary text or elements.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.medium.com/&quot;&gt;Medium&lt;/a&gt;&lt;/strong&gt;: Medium’s design is centered on readability and content-focused layouts. The platform uses a minimalistic interface that puts content first, allowing users to focus on reading without distractions. Its design makes effective use of whitespace and simple typography, which contributes to a clean, clutter-free experience.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.airbnb.com/&quot;&gt;Airbnb&lt;/a&gt;&lt;/strong&gt;: Airbnb balances minimalist design with high-quality visuals. The homepage features large, immersive images, yet the interface remains simple and user-friendly. Their focus on content clarity and ease of navigation ensures users can find what they need quickly. Minimalist Features like bold images, clean typography, and intuitive navigation is what makes Airbnb stand out.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.spotify.com/&quot;&gt;Spotify&lt;/a&gt;&lt;/strong&gt;: Spotify’s website leverages a minimalist interface, allowing its bold color choices and high-quality visuals to shine without overwhelming users. Navigation is simple, and calls to action are clear. Features like dark backgrounds with bright accents, minimal text, and prominent CTAs make Spotify website shine.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://zenhabits.net/&quot;&gt;Zen Habits&lt;/a&gt;&lt;/strong&gt;: Zen Habits is a blog centered around simplicity and mindfulness, and its design mirrors that philosophy. The site is purely text-based with minimal visual distractions, focusing entirely on content.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.everlane.com/&quot;&gt;Everlane&lt;/a&gt;&lt;/strong&gt;: As an ecommerce site, Everlane focuses on showcasing its products with high-quality images and minimalistic design. The clean interface allows users to browse effortlessly without being overwhelmed by too many elements.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Adopting a minimalist approach to your ecommerce or corporate website can improve user experience, reduce load times, and increase engagement. Start de-cluttering your site today and see how simplicity can lead to success!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>"API first" apps are the future</title>
   <link href="https://prahladyeri.github.io/blog/2023/01/api-first-apps-are-the-future.html"/>
   <updated>2023-01-31T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2023/01/api-first-apps-are-the-future</id>
   <content type="html">&lt;p&gt;Apps tend to become very complex beasts, especially when they start to scale. Part of the complexity is due to the fact that the users could be a diverse set of people with multitude of tastes and preferences which could all be conflicting.&lt;/p&gt;

&lt;p&gt;A classic example here is this recent &lt;a href=&quot;https://tildes.net/~talk/145a/unpopular_opinion_wikipedias_old_look_was_much_better_than_the_new_one&quot;&gt;tildes discussion thread&lt;/a&gt; I had started expressing my displeasure at the new Wikipedia design. As you can see from the discussion, it soon became quite heated with both for and against arguments with regard to the new design.&lt;/p&gt;

&lt;p&gt;The nature of life is such that you can’t fit all people into just one mold. There are those who prefer to read wider paragraphs and others who like shorter ones. The trend seems to be moving towards the latter if the new design is indeed based on mass preference.&lt;/p&gt;

&lt;p&gt;Many proponents of the new design will cite data and statistics to “prove” how shorter width is actually better for reading comprehension. But that’s not how life works, unfortunately. User preferences are based on their feelings. If you “feel” that the banana you’re eating is sweet then all the scientific data, research, etc. in the world stating that it’s bitter instead won’t change your mind.&lt;/p&gt;

&lt;p&gt;Thankfully, Wikipedia has other classic or old school themes which they’ve retained just like old Reddit, so this problem is solved. There is also the case that Wikipedia is based on the open source PHP software called &lt;a href=&quot;https://www.mediawiki.org/&quot;&gt;Mediawiki&lt;/a&gt; which can be accessible through an &lt;a href=&quot;https://www.mediawiki.org/wiki/API:Main_page&quot;&gt;API also&lt;/a&gt;. Those having an issue with the “official” font-end can thus use the API to fetch the wiki articles and display them in whatever format they want.&lt;/p&gt;

&lt;p&gt;The business wisdom here is that more and more Internet users are becoming “power users” now, they have strong preferences about UI and aesthetics, and they’re prepared to put some effort to improve that experience. The number of these power users is only going to increase on the Interwebs if this trend of the past continues.&lt;/p&gt;

&lt;p&gt;As a commercial app developer or firm, you can gain an edge here by offering an “API first” platform. What this means is that there is a clear separation in code between the user interface and backend logic. The interface is just a shell or front which calls the “headless” APIs on the backend to do almost everything the app needs like logging into the system, fetching the data related to business logic like customers, orders, invoices, etc. and much more. Another advantage here is that you can improve your font-end code without disturbing the back-end logic which is coded in the APIs.&lt;/p&gt;

&lt;p&gt;The user doesn’t need to care about how the data is actually fetched by the app. However, some of those users are going to be power users who may want a different interface or font-end features. They could be programmers too who can write a python script or something to suit their own taste provided an API exists. You, as an app developer, can cater to that need by bundling the APIs as a feature of your app and making the app itself “API first”.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Kiwixotherapy: A weird but working therapy for introverts suffering from sleeplessness</title>
   <link href="https://prahladyeri.github.io/blog/2023/01/kiwixotherapy-a-weird-but-working-therapy-for-introverts-suffering-from-sleeplessness.html"/>
   <updated>2023-01-14T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2023/01/kiwixotherapy-a-weird-but-working-therapy-for-introverts-suffering-from-sleeplessness</id>
   <content type="html">&lt;p&gt;If you’re an introvert who is suffering from both sleeplessness and also low self-esteem or impostor syndrome, I want to suggest you this therapy which I recently landed upon myself and seen many people recover from their insomnia or sleeplessness problem using it, and leading much happier and energized days now.&lt;/p&gt;

&lt;p&gt;You’ll benefit from this therapy provided:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;You’re an introvert who enjoys reading a lot.&lt;/li&gt;
  &lt;li&gt;Your insomnia is primarily due to Internet or social media addiction (continuously watching “unputdownable” shorts on Youtube, TikTok, etc. on phone).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The ideal advice here is to just put your phone somewhere far away from your reach and inaccessible so that the only activity you force yourself to do is do nothing but have a sound sleep.&lt;/p&gt;

&lt;p&gt;But that’s not always possible. For one, the amount of toll it might take on your will-power could be enormous (there is a reason they’re called “addictions”!). Secondly, I’ve realized over the last few days that the reason most people always want to stay online is that at some deep level, they want to have a constant sense of connectedness with the world or universe at large. If this connection is broken, they’re staring into the abyss or void where the gravity of reality brings all the miseries of the world.&lt;/p&gt;

&lt;p&gt;It’s almost like a red pill or blue pill thing, isn’t it! One way to gracefully come out of this illusory necessity you’ve created for yourself is to give your mind a substitute: An alternative “offline” version of that universal interconnectedness, one that doesn’t keep you awake (like youtube shorts) but one that’s reading material which will both enhance your knowledge and also gracefully make you fall asleep once you’re up for too long (like an encyclopedia article).&lt;/p&gt;

&lt;p&gt;You see, a short video is just too interesting to watch and grabs your full attention to the point where it’s almost impossible to fall asleep (until that point of forceful no return like 4-5 AM which is really the time of waking up, not falling asleep!). An article or book, however, isn’t that attention grabbing. On the contrary, reading actually makes it much easier to fall asleep. How long do you recall you ever kept reading a book with the night lamp on without feeling drowsy and falling asleep right away?&lt;/p&gt;

&lt;p&gt;And this is exactly where &lt;strong&gt;Kiwixotherapy&lt;/strong&gt; comes in! &lt;a href=&quot;https://www.kiwix.org/&quot;&gt;Kiwix&lt;/a&gt; is an open source app which is specifically developed to enable you to download and read Wikipedia (the popular encyclopedia) entirely offline! The app is available for all major platforms including Windows, Mac and Android on &lt;a href=&quot;https://www.kiwix.org/en/download/&quot;&gt;their home page&lt;/a&gt;. Plus you can also get it from &lt;a href=&quot;https://play.google.com/store/apps/details?id=org.kiwix.kiwixmobile&quot;&gt;Google Play&lt;/a&gt; and &lt;a href=&quot;https://f-droid.org/repository/browse/?fdid=org.kiwix.kiwixmobile&quot;&gt;F-Droid&lt;/a&gt; stores on mobile.&lt;/p&gt;

&lt;p&gt;All you have to do is do a one time download of a Wikipedia dump through the app - the full version is about 95GB but smaller dumps are also available for most popular 50K articles, etc. which come in more manageable sizes of around 5-10GB. After that, you must make a habit of forcing yourself to disconnect from the Internet at bed time. If you feel like not disconnecting, remind yourself that there is a whole universe of knowledge and wisdom just waiting to be explored on your mobile phone! And real knowledge too, not just some opinions or trolling posts by self-proclaimed wizards on social media!&lt;/p&gt;

&lt;p&gt;As you start reading the wikipedia articles while fighting your sleep, one of these two things will happen: either your knowledge will increase or you’ll fall asleep, and both these scenarios are in your interest or well-being. So, good luck and try this therapy today.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to handle long-polling of XHR requests in PHP</title>
   <link href="https://prahladyeri.github.io/blog/2023/01/php-long-polling-xhr.html"/>
   <updated>2023-01-02T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2023/01/php-long-polling-xhr</id>
   <content type="html">&lt;p&gt;A common need you often face in PHP scripting these days is writing a long-polling endpoint for things like sending notifications or other kinds of responses back to the client. While using something dedicated like node, cometd or websocket makes ideal sense for such things, there are use-cases when you want to accomplish this in PHP itself and don’t want to add any extra dependencies or components to your project.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/php-xhr-long-polling.png&quot; alt=&quot;php xhr long polling&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In this post, I’ll show you how to achieve this to a limited extent in PHP. But how scalable this solution will be will depend on the threading model and configuration of your web-server (apache/nginx). The basic problem with long-polling is that unlike short-polling, the client won’t exit immediately. The client keeps waiting for a response and this will inevitably block a thread on your web server. The same principle applies even when you go for other solutions like websocket, cometd, etc. but these libraries or components hide this “wait” implementation away from your code.&lt;/p&gt;

&lt;p&gt;A notable exception here is node.js. Due to its uniquely architectured single-threaded event based model, this problem isn’t faced. Anyway, coming back to PHP implementation, here is how I handled it in my code. Please note that it’s extremely important to call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;session_write_close()&lt;/code&gt; as early as possible in your code and it’s also important to override the default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max_execution_time&lt;/code&gt; php configuration at the beginning of your php script or framework configuration with something like this:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;ini_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;max_execution_time&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;300&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once you ensure this, this is how you can handle it on php side:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fetch_long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;session_write_close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;error_log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;LONG POLLING&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;error_log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;LONG POLLING LOOP STARTS...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;select * from notifications where id=1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;error_log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;NOW QUERYING...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;result_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;nb&quot;&gt;error_log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;GOT NOTIFS, ESCAPING...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;json_encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;nb&quot;&gt;error_log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;I KEEP POLLING ...&quot;&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;error_log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ESCAPING ANYWAY...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;json_encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Please note that once you call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;session_write_close()&lt;/code&gt;, you won’t be able to write to any session variables. However, you’ll still be able to read them. What I then do is create a blocking loop with a maximum limit of 300 seconds (you can increase it to as much as you want subject to what value you configured in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max_execution_time&lt;/code&gt; earlier). If a notification is fetched before that time, I simply return it and exit the function. If not, the loop just times out in the end and I return a blank array.&lt;/p&gt;

&lt;p&gt;Finally, this is how my AJAX call on front-end looks:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fetchNotifications&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ajax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
		&lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/home/fetch_long&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;GET&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;na&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;na&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;nx&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fetchNotifications&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
		&lt;span class=&quot;na&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;nx&quot;&gt;handleNotif&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
			&lt;span class=&quot;nx&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fetchNotifications&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ready&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
	&lt;span class=&quot;nx&quot;&gt;fetchNotifications&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once the client receives a response from server, it keeps calling it again and again ad-infinitum to keep fetching the notifications.&lt;/p&gt;

&lt;p&gt;The obvious limitation of this method is that a thread is blocked for every “waiting” request on the server side, so it isn’t ideal for scaling. In my case, I had only 30-40 users at most who used this app, so it wasn’t a big issue. Even then, I’ve added a pause of 3 seconds on client side before making the next request, just so as to give some breathing space to the server.&lt;/p&gt;

&lt;p&gt;Despite the limitation, the app is working decently on the apache server without any configuration tweaks required. And besides, this is an architectural limitation which will always be there as I said, even in case of third party solutions like cometd and websocket.&lt;/p&gt;

&lt;p&gt;Were you able to scale your PHP app with long polling by using this method? Please comment and let me know.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Common sense keyword research: the quickest way to find niche ideas for free</title>
   <link href="https://prahladyeri.github.io/blog/2022/12/common-sense-keyword-research.html"/>
   <updated>2022-12-08T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2022/12/common-sense-keyword-research</id>
   <content type="html">&lt;p&gt;Of late, I’ve found that a lot of unnecessary time and effort is being wasted on keyword-research activity by most new bloggers. Most bloggers do keyword research primarily for &lt;em&gt;finding a niche&lt;/em&gt; i.e. they try to find a (relatively low-competition) topic related to their own where opportunity to rank is there but competition is less.&lt;/p&gt;

&lt;p&gt;Inspired by many Youtubers, the usual way they go about this is by using the so called “keyword research tools” like semrush, ahrefs, etc, though there are some free ones too like Google’s keyword planner and ubersuggest. They then start bogging down into all kinds of number crunching and statistics like keyword volume, page and domain authority of competitive sites that rank for those words, then study their back links, etc.&lt;/p&gt;

&lt;p&gt;In this article, I’m going to show you that none of this is really needed for ranking a good content and if you consider the overall picture, is either an absolute waste of time or even actively harmful in some cases!&lt;/p&gt;

&lt;p&gt;To understand why this is a waste of time, let’s go back and understand the purpose of SEO in the first place. It may come as a surprise today but the dictum &lt;em&gt;Content is King&lt;/em&gt; still holds true even in 2022. Eventually, Google is trying to rank content that caters to the query maker, not the individual keywords.&lt;/p&gt;

&lt;p&gt;Keywords are just a means to an end, so bogging down to the levels of PA/DA/back links/etc. just defeats the purpose, be it for the blogger or the audience. If you’re confident that the content you’ve created is good enough (informative or provides a solution), the only kind of keyword research you’ll need is of the “common sense” kind which is, of course, entirely free.&lt;/p&gt;

&lt;p&gt;By extension, this common sense keyword research is needed only to ensure that you’re writing about a topic which is in demand and your content won’t get lost in those unfathomable depths of Internet that don’t rank at all! For however good a content might be, it won’t rank unless there is demand for that content and that’s where the common sense approach to keyword-research comes in.&lt;/p&gt;

&lt;p&gt;The common sense niche finding logic goes as follows. Taking my own case study as example, as a relatively new blogger and non-expert in SEO, it’d be a tad difficult for me to rank for something like “common sense keyword research”. Hence, I try to drill down or “niche down” my topic here by adding an “a” and putting the query into the Google search box. This gives me the following keyword suggestions:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/google_keyword_suggestions.png&quot; alt=&quot;google keyword suggestions&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Thus, you may continue postfixing “b”, “c”, “d”, etc. to your original or seed keyword to find more and more ideas which are in vogue presently. Google search suggestions is the common sense technique! It’s a simple and working design as Google is showing you these suggestions because people are actually searching for these words. Otherwise, Google would never have suggested them to you.&lt;/p&gt;

&lt;p&gt;None of the other technicalities like search volume, page and domain authority, back links count, etc. should matter to you as blogger or content creator. Once you make a note of these keywords, it should give you a good idea of where the general trend is going related to your topic. And thus, incorporating more of them in your future blog posts should increase your chances of ranking in Google searches, it’s that easy and that simple!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to handle multiple queries in SQLite using CodeIgniter</title>
   <link href="https://prahladyeri.github.io/blog/2022/11/php-sqlite-multi-queries.html"/>
   <updated>2022-11-14T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2022/11/php-sqlite-multi-queries</id>
   <content type="html">&lt;p&gt;My &lt;a href=&quot;/blog/2022/10/php-mysql-multi-queries.html&quot;&gt;earlier article&lt;/a&gt; dealt with multi-query issues of MySQL, this one is dedicated to SQLITE. Multi-queries are often discouraged to begin with but there are times when you find yourself using them. One typical use case is populating the database initially when it’s empty. You do this by running an SQL script which may include multiple queries for creating tables, views, stored procedures, etc. and a few insert queries to populate default records (such as an admin user).&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;file_get_contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;APPPATH&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;core\\install.sql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The above is what you typically do when it comes to Codeigniter framework but when I tried this (using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlite3&lt;/code&gt; database driver), I was shocked to know that the multi-query didn’t work at at all. Only the first SQL statement (among a soup of them separated by semi-colon) was working.&lt;/p&gt;

&lt;p&gt;So I googled it and came to know that it was &lt;a href=&quot;https://bugs.php.net/bug.php?id=28264&quot;&gt;an old bug&lt;/a&gt;. Apparently, they have now fixed it in later versions of PHP but only if you use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlite_exec()&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlite_query()&lt;/code&gt; function to run the query. Now, obviously our Codeigniter’s database abstraction layer was running the latter because I was still facing this same bug as of PHP 7.&lt;/p&gt;

&lt;p&gt;So I tried to scavenge and find out what exact function Codeigniter was running for sqlite3 driver by studying the file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;system\database\drivers\sqlite3\sqlite3_driver.php&lt;/code&gt;. And here I found out that the driver was running the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlite_exec()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlite_query()&lt;/code&gt; based on the very weirdest logic! Apparently, this driver tries to find out the first &lt;strong&gt;word&lt;/strong&gt; in your whole soup of SQL and only if it starts with one of these (DDL) statements, it ran the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlite_exec()&lt;/code&gt;, otherwise it ran the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlite_query()&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_write_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;preg_match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;/^\s*&quot;?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX|MERGE)\s/i&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;install.sql&lt;/code&gt; actually started with a bunch of comments explaining what the whole thing was about, I had to remove this comment block and ensure that it started with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATE&lt;/code&gt; statement, only then the multi-query worked with Codeigniter.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>From userscript to Chrome extension: say goodbye to Greasemonkey and Tampermonkey</title>
   <link href="https://prahladyeri.github.io/blog/2022/10/converting-userscripts-to-chrome-extensions.html"/>
   <updated>2022-10-14T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2022/10/converting-userscripts-to-chrome-extensions</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/userScripts&quot;&gt;Userscripts&lt;/a&gt; are a very useful and handy tool in the hands of power users. Just like customized garnishing, salt and pepper, etc. we put on our food before we eat, we can add custom javascript tweaks on the websites we visit.&lt;/p&gt;

&lt;p&gt;For example, you may want Reddit to automatically highlight the recently posted (unread) comments on a post or thread. Another example is that when you perform a Google search, you may want trusted sites highlighted specifically (based on a pre-filtered list if you have one).&lt;/p&gt;

&lt;p&gt;The classical or traditional way of running these userscripts is by using the “monkey” extensions, the most popular of them being &lt;a href=&quot;https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/&quot;&gt;Greasemonkey&lt;/a&gt; and &lt;a href=&quot;https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=en&quot;&gt;Tampermonkey&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These extensions allow you to write scripts that execute when you visit the particular sites for them. While this is a great way of using userscripts, the better and more efficient way is to always convert them into browser extensions.&lt;/p&gt;

&lt;p&gt;Extensions run natively in your browsers as you’re using one less execution layer. Besides, some extensions like Tampermonkey also update themselves too frequently (perhaps to keep up with the browser updates) and that might cause compatibility issues.&lt;/p&gt;

&lt;p&gt;While comprehensive extension development is beyond the scope of this blog post, the conversion from a userscript to a basic chrome extension is quite straightforward. All you have to create is a folder for your extension and create two files named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;manifest.json&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content.js&lt;/code&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content.js&lt;/code&gt; is where you’ll place the javascript code presently in your script file. Two important points to note here is that there should be no use of special &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GM_*&lt;/code&gt; functions and you must not use any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;return&lt;/code&gt; statement unless it’s inside a function.&lt;/p&gt;

&lt;p&gt;Here is a very basic &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;manifest.json&lt;/code&gt; example.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;manifest_version&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Reddit Plus&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Highlight unread comments and mark them in blue.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;icons&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;icon.png&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;48&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;icon.png&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;icon.png&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;content_scripts&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[&lt;/span&gt;
		&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;https://*.reddit.com/*&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
	  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
	  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;exclude_matches&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[&lt;/span&gt;
		&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;https://*.reddit.com/test/*&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
	  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
	  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;lib/jquery.min.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;content.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;browser_action&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;default_icon&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;icon.png&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;default_popup&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;popup.html&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;scripts&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;background.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
	  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;persistent&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
	&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;activeTab&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;https://ajax.googleapis.com/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My userscript also depends on jquery and instead of calling it through the CDN, I’ve included it as a separate script under “lib” folder as “lib/jquery.min.js”. Having an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;icon.png&lt;/code&gt; helps you distinguish the extension form others. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;background.js&lt;/code&gt; is required as an empty placeholder. So, there are 5 files in total (the last one being optional):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;manifest.json&lt;/li&gt;
  &lt;li&gt;content.js&lt;/li&gt;
  &lt;li&gt;icon.png&lt;/li&gt;
  &lt;li&gt;background.js&lt;/li&gt;
  &lt;li&gt;lib/jquery.min.js&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have this structure, just go to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chrome://extensions&lt;/code&gt; on your browser and switch on the “Developer Mode”. This will allow you to install unpacked extensions. Just click on the “Load Unpacked” and browse your extension folder.&lt;/p&gt;

&lt;p&gt;That’s it! You can now test your extension by visiting the site just like you did with userscripts and the monkey extensions. This was for chrome browser which I happen to use but extension development workflow for other browsers like firefox or edge shouldn’t be much different than this. Let me know how your userscript to extension conversion goes in the comments below.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to set default date to current date in MySQL</title>
   <link href="https://prahladyeri.github.io/blog/2022/10/mysql-setting-default-date-to-current-date.html"/>
   <updated>2022-10-13T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2022/10/mysql-setting-default-date-to-current-date</id>
   <content type="html">&lt;p&gt;The most typical way people set default values to mysql date fields is by using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CURRENT_TIMESTAMP&lt;/code&gt; constant as follows:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ledger_entries&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;auto_increment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;entry_date&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CURRENT_TIMESTAMP&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will obviously work if you need both date and time parts in the value. But in most cases (like entry_date here), we only need the date part and would happily like to save that extra space. Even if you don’t want to save extra space, you may want to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;date&lt;/code&gt; data type for widget compatibility on the front-end . In few such situations, we prefer to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;date&lt;/code&gt; mysql data type instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;datetime&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The obvious constraint here is that you can’t then use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CURRENT_TIMESTAMP&lt;/code&gt; as the default value to the newly added rows. Following isn’t allowed in MySQL as no pre-defined constant (except &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CURRENT_TIMESTAMP&lt;/code&gt;) is even permitted as a default value in the first place:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;entry_date datetime default CURRENT_DATE
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now what will you do in such situations? As stated in some stack overflow posts like &lt;a href=&quot;https://stackoverflow.com/q/20461030/849365&quot;&gt;this&lt;/a&gt;, &lt;a href=&quot;https://stackoverflow.com/q/64756590/849365&quot;&gt;this&lt;/a&gt; and &lt;a href=&quot;https://stackoverflow.com/q/36374335/849365&quot;&gt;this one&lt;/a&gt;, a new (2018) MySQL version does allow a default date like this but I don’t recommended this as the practice won’t be compatible with universal MySQL installations or versions:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CREATE TABLE INVOICE(
	INVOICEDATE DATE DEFAULT (CURRENT_DATE)
)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Another way to handle this is to write a trigger. When new rows are inserted to the table, you may want to set the value to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CURRENT_DATE&lt;/code&gt;. You may prefer this approach if you’re already writing a trigger but it’s a tad extra code and inefficiency to keep adding triggers just for this one need though.&lt;/p&gt;

&lt;p&gt;I felt the best way is to handle this at the application level, that’s what I preferred to do when I came across this problem recently.&lt;/p&gt;

&lt;p&gt;Happy coding and let me know your experience with mysql date/time values in comments below.&lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/20461030/849365&quot;&gt;https://stackoverflow.com/q/20461030/849365&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/64756590/849365&quot;&gt;https://stackoverflow.com/q/64756590/849365&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/36374335/849365&quot;&gt;https://stackoverflow.com/q/36374335/849365&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/168736/849365&quot;&gt;https://stackoverflow.com/q/168736/849365&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>How to execute multiple queries in MySQL using CodeIgniter</title>
   <link href="https://prahladyeri.github.io/blog/2022/10/php-mysql-multi-queries.html"/>
   <updated>2022-10-12T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2022/10/php-mysql-multi-queries</id>
   <content type="html">&lt;p&gt;Multi-queries are often discouraged with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysqli&lt;/code&gt; functions but there are times when you must use them. One obvious use case is initializing the database. One of the first things your app must do is determine if the database tables exist or not, and then run an initializing SQL script if they don’t. This script may include multiple queries for creating tables, views, stored procedures, etc. and a few insert queries to populate default records (such as an admin user).&lt;/p&gt;

&lt;p&gt;Running multiple queries with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysqli&lt;/code&gt; isn’t an exact science though! The &lt;a href=&quot;https://www.php.net/manual/en/mysqli.multi-query.php&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysqli_multi_query&lt;/code&gt;&lt;/a&gt; function is technically the way to do it but there are some quirks you must be aware of when using it.&lt;/p&gt;

&lt;p&gt;A major challenge here is error handling. The above function sends the individual queries (separated by semi-colons) to server one by one and stops executing the moment it faces an error in one of them. As stated in &lt;a href=&quot;https://stackoverflow.com/a/7867175/849365&quot;&gt;this stackoverflow answer&lt;/a&gt;, there is no official way to fetch errors in each and every one of your statements.&lt;/p&gt;

&lt;p&gt;What you must do, in fact, is keep calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysqli_next_result()&lt;/code&gt; again and again until each query result (or error) is fetched. This is how a proper implementation looks like (the code would probably be much shorter if it was implemented using some other technology like python or ADO.NET!):&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;error&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;post&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;file_get_contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;APPPATH&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;../init.sql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;mysqli_multi_query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;error&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;db_error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//handle error&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mysqli_more_results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;mysqli_next_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		 &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;error&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;db_error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//handle error&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;error&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;info&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;The system is successfully installed!&amp;lt;br&amp;gt;&amp;lt;a href=&quot;/auth/login&quot;&amp;gt;Click here&amp;lt;/a&amp;gt; to login!&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;installed&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Another quirk to be aware of with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysqli_multi_query&lt;/code&gt; is that you must ALWAYS fetch the results by calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysqli_next_result&lt;/code&gt; subsequently until &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysqli_more_results()&lt;/code&gt; returns true. Not doing so may introduce some inadvertent bugs in your code when you later try to fetch records through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysqli_query&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Happy coding and let me know through comments below how your implementation goes!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to approach and evaluate programming languages for a project</title>
   <link href="https://prahladyeri.github.io/blog/2022/09/how-to-approach-and-evaluate-programming-languages-for-a-project.html"/>
   <updated>2022-09-21T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2022/09/how-to-approach-and-evaluate-programming-languages-for-a-project</id>
   <content type="html">&lt;p&gt;While it’s a topic which has already invited hair splitting debates ad nauseam, it has also invited a lot of hype and there is a need to unclutter that hype and think about this topic objectively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Toolbox Approach&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This has been the classic or traditional approach when programming used to be much simpler in the earlier days! &lt;em&gt;“Right tool for the right job”&lt;/em&gt; used to be the motto once and you never thought of using a hammer when a chisel was needed. Around the early part of this century, languages began to consolidate and the result is that we only have a handful of highly popular ones used in the industry today and the rest are cast away into specialized domains.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Paradigm Approach&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The paradigm approach once was and still is popular today, in fact this is one way of thinking that contributes to the toolbox approach. Procedural languages like C and Rust are naturally built for system programming work or in situations where that last bit of computing performance is needed. The OOP paradigm in languages like C++ and C# helped build most of the desktop world citizens we use today such as operating systems, office suites, internet browsers, IDEs, etc. Python’s dynamic nature is similarly suited for data analysis work and PHP’s “all paradigms embracing” philosophy once made it a popular choice for web development. Same could be said about JavaScript to some extent, though it’s a language choice that’s more like thrust upon the web developers instead of being a natural choice! This is a highly subjective view, of course, all kinds of arguments can be made here depending on your own tastes and preferences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Ecosystem Approach&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is what I consider to be a modern approach of evaluating languages. Programming is no longer simple like it used to be once, today each major language has a comprehensive open source ecosystem built around it, and since these communities collaborate among themselves to not only build redistributable software packages but also help their users on coding forums, etc., they become a very important factor or consideration in taking the decision to use that language itself!&lt;/p&gt;

&lt;p&gt;Consider the strange case of PHP, for instance. It’s almost treated like a pariah in many programming circles, you’ll find very few courses having PHP in their curriculum and not one major &lt;a href=&quot;https://github.com/prahladyeri/CuratedLists/blob/master/lists/programming.md#coding-forums-weblogs-and-discussion-sites&quot;&gt;coding forum, blog or discussion site&lt;/a&gt; has a PHP topic included except Stack Overflow. But still the language is almost entirely powered by its community or ecosystem! It’s also a language that pioneered open source development and collaboration by involving contributors on massive scales with projects like Wordpress, Drupal, Joomla, Laravel, etc. which have all become ecosystems in their own right.&lt;/p&gt;

&lt;p&gt;A technology startup which is already hard-pressed in today’s economically stressing times will obviously want to leverage the power of open source software and free tools, and that’s where the ecosystem comes into picture! How friendly is the community on discussion forums and how large is the package ecosystem are typically the top questions in the minds of technology startup entrepreneurs.&lt;/p&gt;

&lt;p&gt;In this regard, Python, PHP and JavaScript are the major champion ecosystems and perhaps top the list of usage surveys, they’ve developed elaborate package management systems to serve their bases - PyPi, Composer and npm respectively. This trend has caught to some extent in the C# and Java world too with nuget and gradle/maven but theirs seem to cater to mostly the enterprise world, there doesn’t seem to be the same kind of buzz among the startups for these languages.&lt;/p&gt;

&lt;p&gt;Summarily, these are the three major approaches for evaluating a programming language. There are others too, a major one being local availability of professionals skilled in a language. But in the age of remote working, I’m hoping that factor should become less important as time progresses. And there are “contrarian coders” too who will invest in learning long obsolete languages like COBOL and FORTRAN hoping that the skewed supply/demand ratio for these skills arising due to the need for maintaining legacy systems will ensure they get hired with handsome perks. Indeed, that’s an approach or way of looking at things too!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to organize your research for blog posts in 2022: A comprehensive guide</title>
   <link href="https://prahladyeri.github.io/blog/2022/02/how-to-organize-your-research-for-blog-posts-in-2022-a-comprehensive-guide.html"/>
   <updated>2022-02-07T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2022/02/how-to-organize-your-research-for-blog-posts-in-2022-a-comprehensive-guide</id>
   <content type="html">&lt;p&gt;One of the most challenging aspects of blogging is how to perform research and more importantly, organizing your research. As a technology blogger, I face this problem too, so some days ago, I did some research on… guess what? Yep, on how to organize research for writing articles or blog posts!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/grocery-cart.jpg&quot; alt=&quot;grocery cart&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A natural question that arises here is why should bloggers organize their research in the first place? There are many benefits to it, the most obvious is that because your writing reflects your thinking or thought process itself, getting into a regular habit of organizing your research work will then force your mind to organize your thought patterns too. This will, in the longer term, make you a better thinking and more intellectual person, and also help your creativity as your creative mind will now have a much larger and more organized arsenal of knowledge to come up with more ideas more quickly.&lt;/p&gt;

&lt;p&gt;The bad news, though, is that organizing research isn’t an exact science, there is no one true way of doing it and every blogger evolves their own method over time. Having said that, as Katie Holmes &lt;a href=&quot;https://beryliveylibrary.wordpress.com/2018/02/13/organize-research/&quot;&gt;describes in this article&lt;/a&gt;, &lt;em&gt;“Librarians are known for a few things. Aside from loving cardigans and tea, we tend to be Amy Santiago levels of organized”&lt;/em&gt;. The article borrows some great ideas from librarians in this regard!&lt;/p&gt;

&lt;p&gt;Before straightaway diving into online research, let’s determine what kind of research we are doing first. Have you already decided on the exact topic and/or headline of your next article and doing research just for gathering content? Or you don’t yet know the topic and you’re just doing some “general” research around a broader topic to then find out what exactly to blog about? The latter is called preliminary research and you should ideally do it prior to performing the actual “heavy” research.&lt;/p&gt;

&lt;p&gt;In fact, preliminary research is something I happen to do almost always as that’s how I come up with ideas for my next blog post. Accordingly, you may organize your note taking process. What I personally do is I’ve created two folders called “research” and “drafts” for the preliminary and  actual kinds of research respectively. Inside the research folder, I have various topical subfolders like tech, health, lifestyle, etc. in which I store notes in the form of plain text files. The drafts folder is for my actual writing projects, in this folder go one file for each article I’m in the process of writing and these are in markdown format. The end result may look something like this:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;├───drafts
│   │   how-to-get-near-perfect-shave-in-less-than-5-rupees.md
│   │   how-to-understand-determinism-with-flowchart-analogy.md
│   │   how-to-maximize-happiness-with-dmu-philosophy.md
│   │   why-we-should-support-community-distros-like-mint.md
│	│
│	└───fiction
│
└───research
    ├───finance
    │       nifty.yaml
    │
    ├───health
    │       yoga.yaml
    │
    ├───horticulture
    │       organic-fertilizers.yaml
    │
    ├───lifestyle
    │       frugal-living.yaml
    │       yoga.yaml
    │
    ├───tech
    │       github.yaml
    │       linux.yaml
    │       microsoft.yaml
    │
    └───writing
            organization.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The markdown files are initially filled with chunks and subheadings, all organized into a rough outline until I finally call the research done and then start organizing the actual content of the draft. This way, I gradually come up with an article’s content bit by bit, as if picking up parts of a big jigsaw puzzle and joining them one by one. Trust me, doing all of this might sound very “boring” or monotonous initially but it’ll be real fun once you start getting the hang of it!&lt;/p&gt;

&lt;p&gt;Apart from this, you can also maintain something called &lt;em&gt;Concept Maps&lt;/em&gt; which Katie also describes in her article. It’s a form of organizing your brainstorming process. You start with a topic say &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python&lt;/code&gt;. Then you might link various areas of applications to it, say &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web development&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data analysis&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;machine learning&lt;/code&gt;, etc. Then you think of various frameworks and apps like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flask&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;django&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mezzanine&lt;/code&gt;, etc. So, you can have something like this:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Web Development
  ├───Flask
  ├───Django
  ├───Mezzanine
Machine Learning
  ├───NLTK  
  ├───TensorFlow
Data Analysis
  ├───Numpy
  ├───Pandas
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You don’t need any specific app or software to maintain this, you can even maintain them in something called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yaml&lt;/code&gt; files (*.yaml). These are plain text files with support for hierarchical text storage and can work wonders for tasks like this if you have a good text editor such as Notepad++ or Visual Studio Code.&lt;/p&gt;

&lt;p&gt;As for the preliminary research text files, I create one for each major topic. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;github&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;microsoft&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;windows&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linux&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt;, etc. (under technology). In each file, I’ll add a link or citation (for the source) and few bullet points for each below it (this is what Katie calls “cue cards” or “annotations”). For example, in case of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;github&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;1]:
	+ Microsoft acquires Github Inc.
	+ Speculations arise as to future of &lt;span class=&quot;nb&quot;&gt;source &lt;/span&gt;hosting facility.
	+ Competitor Gitlab pushes on advertising.
	+ Many developers thinking of migrating to Gitlab.

&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;1]: https://www.businessinsider.in/Microsoft-has-been-talking-about-buying-GitHub-a-startup-at-the-center-of-the-software-world-last-valued-at-2-billion/articleshow/64420905.cms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A simple organizational trick I follow here is that I append the bullet points in my own words with a plus sign (+) whereas a dash or minus sign indicates that the quote is lifted from the article as it is. So, “Microsoft acquires Github Inc.” above is my own note.&lt;/p&gt;

&lt;p&gt;Organizing information collected from various sources in this manner forces you to think like a journalist and believe me, you’ll come up with much more creative, original and refreshing content as a blogger, once you start practicing this strategy! It’s not necessary that you put just bullet points, you can have direct quotes in quotation marks, brief annotations or notes in your own language, etc.&lt;/p&gt;

&lt;p&gt;The most important thing is to write down ideas as they come to you. Say you’re reading this above source (the Business Insider article) and one blog post idea suddenly strikes you: &lt;em&gt;&lt;a href=&quot;https://prahladyeri.github.io/blog/2018/06/microsofts-github-acquisition-an-unbiased-perspective.html&quot;&gt;Microsoft’s Github Acquisition - A Perspective
&lt;/a&gt;&lt;/em&gt;. Then stop reading it and create a new markdown file for this article in the drafts folder, and start writing down things as they come to your mind. Ideally, you’ll also have the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;github&lt;/code&gt; file in the research folder open in a separate tab of your text editor to borrow sources and bullet points from there as and when needed. This is the most &lt;em&gt;organic&lt;/em&gt; way of coming up with articles and you can never go wrong with this method.&lt;/p&gt;

&lt;p&gt;Alternatively, the idea for the github article may arise sometime later. For example, after you have had a good night sleep and wake up with a fresh mind next day. But since you already have everything organized in your computer now, it’ll be much easier to just open up your preliminary research folder and skim through your notes and thus equipping yourself with a much better chance of coming up with a good quality article. If you hadn’t organized this, you’d have to go through your browser’s history, bookmarks, etc., and do a tremendous amount of head-scratching just to recall what good points you had read yesterday!&lt;/p&gt;

&lt;p&gt;Apart from this, it’ll also help you to read about the three types of sources as described in &lt;a href=&quot;https://habitsofatravellingarchaeologist.com/tips-for-organizing-sources-for-research-papers/&quot;&gt;this article&lt;/a&gt; by Smiti Nathan of the &lt;em&gt;Habits of a Travelling Archeologist&lt;/em&gt; blog. As bloggers, we mostly base our research on tertiary sources (like online news blogs, articles, Wikipedia, etc.) but it’s also important to be aware of primary and secondary sources in your field too if you want to establish some reputation as expert in your subject matter.&lt;/p&gt;

&lt;p&gt;The article also describes various ways of finding sources like getting to know your nearby librarians, &lt;a href=&quot;https://scholar.google.com/&quot;&gt;Google Scholar&lt;/a&gt;, locating literature reviews, checking out &lt;a href=&quot;https://www.academia.edu/&quot;&gt;Academia&lt;/a&gt; or &lt;a href=&quot;https://www.researchgate.net/&quot;&gt;ResearchGate&lt;/a&gt;, engaging with live sources (experts in various fields), etc.&lt;/p&gt;

&lt;p&gt;More inspiration for organizing writing content can be found in Mike Hanski’s article titled &lt;a href=&quot;https://inspirationfeed.com/5-writing-tips-how-to-organize-your-research/&quot;&gt;5 Writing Tips: How to Organize your Research&lt;/a&gt;. Hanski starts by describing the importance of quality research which is highly lacking in today’s blogosphere and then proceeds to give some important tips like Record Everything, Always Be Ready for Genius to Strike, etc.&lt;/p&gt;

&lt;p&gt;In conclusion, I feel that organizing research isn’t easy or straightforward, but if you’re motivated enough and prepared to dedicate it some time, you can do it successfully and evolve your own organization process over time.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Why I think "Sponsor Only" repositories introduced by Github is a terrible idea</title>
   <link href="https://prahladyeri.github.io/blog/2022/02/why-i-think-sponsor-only-repositories-introduced-by-github-is-a-terrible-idea.html"/>
   <updated>2022-02-03T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2022/02/why-i-think-sponsor-only-repositories-introduced-by-github-is-a-terrible-idea</id>
   <content type="html">&lt;p&gt;Today morning, I woke up to &lt;a href=&quot;https://twitter.com/TechCrunch/status/1488939271406948352&quot;&gt;this dull news&lt;/a&gt; that Github is soon going to introduce “Sponsor Only” feature, which means the ability for an open source project to hide its source code from everyone except the one who sponsors them.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/code-python.jpg&quot; alt=&quot;Code&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This incident shouldn’t be taken in isolation but as part of a larger trend. Few years ago, Medium also did a similar thing by allowing authors to monetize their articles. Quora followed suit too last year by prodding popular authors to sign up for their monetization program that involves putting their answers behind a walled garden.&lt;/p&gt;

&lt;p&gt;I don’t even know where to begin on this. As I replied to the TechCrunch news tweet, I don’t like this general direction the Internet is moving towards. There used to be a time when open source used to stand for something, it was all about the commons or the community. Commercial technology companies (and even society at large) used to look up to open source as a model of humanistic value to cherish and endeavor.&lt;/p&gt;

&lt;p&gt;This move is totally against the spirit of GPL/MIT/Apache licensed software which is all about openness and software freedom. In fact, both Stallmanists (FSF) and Open Source (OSI) camps are largely affected by this, so both should speak out vehemently against this. This is the time to forget your differences and unite against a common malady. Your definition of “freedom” and what constitutes it may differ, you both may have different ideologies, but here the very foundation of software freedom is being threatened with this “Sponsor Only” move.&lt;/p&gt;

&lt;p&gt;Now, I understand that putting food on the table should be a developer’s primary concern, and there is nothing wrong in monetizing their efforts. But I strongly disagree with the “walled garden” approach many are taking for it, the same could be done by using more ethical means like advertising, affiliate marketing, repaying the sponsor in other terms (like putting out a brief marketing video for them on youtube as it’s usually done).&lt;/p&gt;

&lt;p&gt;Meanwhile, I don’t understand the very confusing and convoluted concept of “sponsor” here who wants the product all for himself! The wordnet dictionary defines sponsor as &lt;em&gt;“Someone who supports or champions something”&lt;/em&gt;. If one wants to sponsor a FOSS project, it’s naturally assumed that they’re interested in the open source or freedom friendly nature of that project too. If not, they should just stop doing the farce of this “sponsorship” and be direct in stating that they want to acquire or buy the project (for that is what they’re effectively doing with this “Sponsor Only” move).&lt;/p&gt;

&lt;p&gt;I can understand an individual developer’s or author’s need for money and the need to put bread on their family table. But an institution like Quora or Github should rise above these things, they’re the custodians of the legacy of today’s Internet and all the content on it. Humans are fallible and corruptible, and that’s exactly why we have institutions, even commercial ones have a code of ethics or conduct apparently. Quora could have easily thought of better ways of paying off their content creators than coming up with a program that lures them with money in return to putting their posts behind a paywall. What kind of nonsensical approach is this? Everyone starts somewhere in this world, and everyone isn’t born with a silver spoon or the ability to pay for costly subscriptions. The pandemic has broken the financial backbone of many individuals as it is. This is the time to bring out the best of humanity inside us, not the worst.&lt;/p&gt;

&lt;p&gt;Imagine a world tomorrow where every single website or blog on the Internet is put behind a paywall, a world where money is the sole motivation to create content and the size of people’s pockets determines what gets published. Do you like to live in such a world? Free thinking individuals who don’t like to let that happen should speak out in loud voices against these moves, otherwise they’ll only keep getting worse and worse.&lt;/p&gt;

&lt;p&gt;I also understand that &lt;em&gt;Capitalism vs Socialism&lt;/em&gt; is a never ending debate and there are all shades of grey on both sides. But I have a strong feeling that some endeavors like Medicine, Education and Open Source should exist in a realm where not money, but work ethics and human values are a motivator for doing things. Otherwise the result could be disastrous for the society as a whole.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unlocking Creativity: Your Ultimate Guide to Overcoming Writer's Block</title>
   <link href="https://prahladyeri.github.io/blog/2022/01/dealing-with-writers-block-the-ultimate-checklist.html"/>
   <updated>2022-01-24T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2022/01/dealing-with-writers-block-the-ultimate-checklist</id>
   <content type="html">&lt;p&gt;Writer’s block is a common challenge that even the most seasoned writers face from time to time. Finding effective strategies to overcome it can feel overwhelming, but understanding the root causes is the first step. Drawing from various techniques, including the &lt;strong&gt;&lt;a href=&quot;/blog/2022/01/how-to-generate-a-never-ending-supply-of-blog-posts.html&quot;&gt;10 content-types method&lt;/a&gt;&lt;/strong&gt;, I’ve compiled a comprehensive checklist to help diagnose and conquer your writer’s block.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/laptop-coffee-mobile.jpeg&quot; alt=&quot;Laptop and Coffee&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Writer’s block can feel like a “wholesome evil.” Its elusive nature makes it difficult to pinpoint the exact cause. Are you struggling due to isolation during a lockdown, or is an underlying issue like depression stifling your creativity? This mystery often leads to diverse perspectives, as highlighted by my recent post in the &lt;strong&gt;Creative Writing&lt;/strong&gt; Facebook group, where &lt;a href=&quot;https://www.facebook.com/groups/wherecreativitylives/posts/10158713599531104/&quot;&gt;writers shared their unique insights&lt;/a&gt;. Here’s a checklist to help you identify the reasons behind your block, offering potential solutions to reignite your writing flow.&lt;/p&gt;

&lt;h3 id=&quot;1-procrastination&quot;&gt;1. Procrastination&lt;/h3&gt;
&lt;p&gt;Procrastination ranks high on the list of writer’s block causes. Many writers acknowledge it as a significant hurdle. This avoidance can stem from various factors, including perfectionism or anxiety. Recognizing this pattern is essential to overcoming it.&lt;/p&gt;

&lt;h3 id=&quot;2-lack-of-creative-ideas&quot;&gt;2. Lack of Creative Ideas&lt;/h3&gt;
&lt;p&gt;Sometimes, a dearth of fresh ideas can stall your writing. For non-fiction, researching your subject matter may help, while fiction writers might find inspiration through new experiences or breaking their daily routines.&lt;/p&gt;

&lt;h3 id=&quot;3-imposter-syndrome&quot;&gt;3. Imposter Syndrome&lt;/h3&gt;
&lt;p&gt;This phenomenon is particularly prevalent among young professionals, manifesting as an overwhelming need for perfection. It can lead to self-doubt and the belief that you don’t deserve your achievements. Acknowledge your accomplishments and remember that even the best writers start somewhere.&lt;/p&gt;

&lt;h3 id=&quot;4-need-for-perfection&quot;&gt;4. Need for Perfection&lt;/h3&gt;
&lt;p&gt;Perfectionism often hinders progress in creative endeavors. As writers, we can get stuck trying to perfect every sentence. Remind yourself that it’s okay to create a draft that’s not perfect. You can always edit later!&lt;/p&gt;

&lt;h3 id=&quot;5-busy-schedule&quot;&gt;5. Busy Schedule&lt;/h3&gt;
&lt;p&gt;Writers sometimes overlook the importance of dedicating time to the writing process. If you’re juggling numerous responsibilities, carving out time for writing can feel daunting. Reflect on your schedule and prioritize your writing time.&lt;/p&gt;

&lt;h3 id=&quot;6-distractions&quot;&gt;6. Distractions&lt;/h3&gt;
&lt;p&gt;External distractions can derail your focus. Whether it’s loud noises or digital notifications, finding a quiet space or using tools like earplugs can help you regain concentration. Practicing mindfulness meditation can also enhance your focus.&lt;/p&gt;

&lt;h3 id=&quot;7-depression-or-adhd&quot;&gt;7. Depression or ADHD&lt;/h3&gt;
&lt;p&gt;Depression can be a significant barrier to writing, yet it often goes unacknowledged. Recognizing its presence is vital to addressing it. Consider seeking professional help if you suspect this might be a factor.&lt;/p&gt;

&lt;h3 id=&quot;8-lack-of-confidence&quot;&gt;8. Lack of Confidence&lt;/h3&gt;
&lt;p&gt;Low self-confidence can stem from various issues, including depression and imposter syndrome. Reflect on your feelings and consider reaching out to fellow writers for support.&lt;/p&gt;

&lt;h3 id=&quot;9-ditz-factorcluttered-thoughts&quot;&gt;9. Ditz Factor/Cluttered Thoughts&lt;/h3&gt;
&lt;p&gt;A disorganized mind can inhibit creativity. Take time to brainstorm, outline your ideas, and organize your thoughts. Tools like &lt;a href=&quot;https://trends.google.com/trends/explore&quot;&gt;Google Trends&lt;/a&gt; and &lt;a href=&quot;https://answerthepublic.com/&quot;&gt;Answer The Public&lt;/a&gt; can be helpful.&lt;/p&gt;

&lt;h3 id=&quot;10-need-to-think-outside-the-box&quot;&gt;10. Need to Think “Outside the Box”&lt;/h3&gt;
&lt;p&gt;Creative thinking is essential for generating original ideas. Revisit this checklist regularly to stimulate your thought process and find what inspires you.&lt;/p&gt;

&lt;h3 id=&quot;11-lack-of-passion&quot;&gt;11. Lack of Passion&lt;/h3&gt;
&lt;p&gt;Consider whether you’re writing about a topic that truly interests you. If not, exploring new subjects may reignite your enthusiasm for writing.&lt;/p&gt;

&lt;h3 id=&quot;12-lack-of-enthusiasmenergy&quot;&gt;12. Lack of Enthusiasm/Energy&lt;/h3&gt;
&lt;p&gt;Sometimes, even when passionate about a subject, you might lack the energy to write. Identify the source of your fatigue and consider practices that boost your energy levels, such as exercise or short breaks.&lt;/p&gt;

&lt;h3 id=&quot;13-lack-of-discipline&quot;&gt;13. Lack of Discipline&lt;/h3&gt;
&lt;p&gt;Creative pursuits thrive on structure. Establish a routine that encourages regular writing, helping you push through periods of stagnation.&lt;/p&gt;

&lt;h3 id=&quot;14-creative-exhaustion&quot;&gt;14. Creative Exhaustion&lt;/h3&gt;
&lt;p&gt;Overextending yourself can lead to burnout. If you feel creatively drained, take a step back, recharge, and diversify your writing topics to renew your inspiration.&lt;/p&gt;

&lt;h3 id=&quot;15-failure-to-walk&quot;&gt;15. Failure to Walk&lt;/h3&gt;
&lt;p&gt;Taking a simple walk can refresh your mind and spark new ideas. Don’t underestimate the power of movement!&lt;/p&gt;

&lt;h3 id=&quot;16-other-mysterious-forces&quot;&gt;16. Other Mysterious Forces&lt;/h3&gt;
&lt;p&gt;If you’ve explored all the above factors and still face writer’s block, there might be other reasons at play. Experiment with different approaches to discover what works for you.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;By addressing these various factors and implementing targeted strategies, you can break free from writer’s block and unleash your creativity. Happy writing!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to generate a never ending supply of blog posts</title>
   <link href="https://prahladyeri.github.io/blog/2022/01/how-to-generate-a-never-ending-supply-of-blog-posts.html"/>
   <updated>2022-01-15T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2022/01/how-to-generate-a-never-ending-supply-of-blog-posts</id>
   <content type="html">&lt;p&gt;If you’re suffering from writer’s block or don’t know what to write about or how to research content for your next blog post, this guide will help you. There are just about 10 basic content-types or patterns around which weblog articles are written these days, and by creatively combining them with your subject matter expertise or your knowledge of hobbies, crafts or something else, you can potentially end up with a never ending or infinite supply of blog posts throughout the year!&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;#contentTypes&quot;&gt;The 10 basic content-types.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#interestsAndHobbies&quot;&gt;Your Interests, Hobbies and Subject Matter Expertise.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#combOneAndTwo&quot;&gt;Combine the 1 and 2!&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#staticAndDynamicPostIdeas&quot;&gt;Static and Dynamic Post Ideas.&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If this sounds very simple to you, that’s because it really is. Writing isn’t a rocket science, all you need is a little organization and you need to keep some time aside for it, and you need to keep practicing it until you gain perfection like any other art. Once you start getting into the “zone”, writing will feel as natural to you as photosynthesis!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/laptop-coffee-mobile.jpeg&quot; alt=&quot;laptop coffee&quot; /&gt;
&lt;!--img alt=&quot;python code&quot; src=&quot;/uploads/laptop-coffee-mobile.jpeg&quot; class=&quot;right-aligned&quot; --&gt;&lt;/p&gt;

&lt;p&gt;&lt;a name=&quot;contentTypes&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-10-content-types&quot;&gt;The 10 content-types&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Listicle:&lt;/strong&gt; A List formatted article or listicle is one of the most commonly found these days. The ones titled like “40+ Ways of Doing Something” fall in this category. These articles are relatively easy to write because you’re typically only playing the role of “information organizer” here. It’s totally fine if you describe each item in the list only briefly and provide links to other resources like Wikipedia, etc. where subject matter experts deal with that item in much depth or detail. Checklists and Cheat-Sheets are also types of a Listicle.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;How-To Guides:&lt;/strong&gt; A How-To guide (such as this one!) is also very common because these are quite informational and Googlers are always hunting for these! Blogging is still a relatively new and inexact science in the digital world, even the best SEO and Internet Marketing companies are always struggling for eyeballs by constantly improving content quality, gathering street cred on social media, creating backlinks, etc. While these techniques seem to work to a limited extent, it’s not an exact science and there is no guaranteeing that a given blog post is going to bring eyeballs for lifetime once written (despite all the SEO/SEM tactics).&lt;/p&gt;

    &lt;p&gt;Gone past are those days when people actually used to read the “user manuals” that came with their products, these days most of them simply Google that information and typically land up on a “How-To” article that describes that product. And it could well be yours if you’ve dealt with the subject thoroughly and it happens to be an in-demand topic or niche.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Infographics:&lt;/strong&gt; Infographics is the modern way of enhancing the content quality and improving the information value of an article. If you’re creative with graphics software like Inkscape or Gimp, you can design infographics which is nothing but a summary of what you want to say in picture format with words creatively sprinkled here and there!&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Comparison - X vs Y:&lt;/strong&gt; Comparisons are also as commonly looked up by Googlers as How-To Guides! I myself remember the countless times I’ve run searches like “PHP vs Python”, “Wordpress vs Drupal”, “Django vs Flask”, “WinForms vs WPF”, “MySQL vs PGSQL”, etc. You may be a programmer but the informational article you’re writing doesn’t necessarily have to be about programming. You may well decide to write a technical article pertaining to your subject matter too. But you may as well compare just anything you may come across or want to compare, even &lt;em&gt;Apples vs Oranges&lt;/em&gt; or &lt;em&gt;Democrats vs Republicans&lt;/em&gt;!&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Ultimate Review:&lt;/strong&gt; Review posts are also highly sought for these days for almost same reason as Comparison posts. Was a new product or service recently launched in your area of expertise? Or even a drastically changed new version of an existing product or software? If yes, write a post about it and it’ll be in high demand because a new product typically arrives with lots of bugs, issues, doubts, new ways of doing existing things, etc. and users are always looking for a helping hand there.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Ultimate Guide:&lt;/strong&gt; Ultimate guides are detailed write of a particular idea or process which people often find difficult or confusing because there is never one straightforward or easy way of doing things.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Case Study:&lt;/strong&gt; These are the ones titled, &lt;em&gt;XYZ Program: How to make 15K or More Each Month&lt;/em&gt;, or similar. It could be a Udemy course or an IT project or even an infrastructure project implemented somewhere or anything else of interest. People are always looking for case studies in order to study them and learn from them. Agile and Software Engineering is another area where constant improvement is needed, relatively new field, inexact science and all.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Personal Story:&lt;/strong&gt; Most personal stories these days feature along the lines of &lt;em&gt;How I moved from 1K to 10K in 18 Months&lt;/em&gt; but they don’t necessarily have to be. Making money online is just  one of the hot topics out there and frankly, it’s highly over-saturated! In fact, you’ll have a far better chance of ranking and getting page-views in that little niche or expertise area you might be having than here. A story could be about anything, it could be about the struggles of your life in childhood, school and college days, etc.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Informational Story (Industry News, World Events):&lt;/strong&gt; The blogger can occasionally wear the journalist’s shoes too by writing informational posts! Industry news relating your subject matter is something you’re most naturally equipped to write a lot about, but it doesn’t necessarily have to be. You can write an informational post about just any and every topic on earth from politics to weather. The dinner table conversations going on in your home (mostly about politics!) are an ideal thing you can convert into an informational article and publish on your blog.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a name=&quot;interestsAndHobbies&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;your-interests-hobbies-and-subject-matter-expertise&quot;&gt;Your Interests, Hobbies and Subject Matter Expertise&lt;/h2&gt;

&lt;p&gt;These can be almost anything but generally fall in the following broad categories:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Technology:&lt;/strong&gt; Whether you’re a programmer, tester, QA/QC analyst or designer, you’ll definitely have lot’s of things to write and share about your field and even workplace, a weblog is an ideal platform to do this. It doesn’t even have to be about IT, you could even be an Auto Engineer or Aeronotics Engineer or someone else. Each person has a unique story to tell and so do you, and the Internet is always all ears for that!&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Finance:&lt;/strong&gt; If you work in the finance industry as an accountant or book-keeper or even a clerk, you’ll still have a lot to share about the changing dynamics in your field, the computerization and use of open source software, changing policies of the government and due to effects of COVID, etc. Personal finance, and tips on saving money are also great topics to write on.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Politics:&lt;/strong&gt; Politics is almost like the bread and butter of netizens who read blogs, there are no other kind of articles that go viral in such a short time as those relating to politics! If you have a flair for writing creative and interesting matter on this topic, you’re definitely going to have a field day here!&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Food:&lt;/strong&gt; There are millions of food blogs out there already and yet, some new content turns up every single day! Some new delicious recipe or a variant of existing recipe or something different you tried out today? You can also blog a lot about topics relating to cuisine like health effects of various spices and vegetables, veganism and vegetarianism, etc.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a name=&quot;combOneAndTwo&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;combine-the-1-and-2&quot;&gt;Combine the 1 and 2!&lt;/h2&gt;

&lt;p&gt;Now, the real magic happens when you apply the basic content types to your knowledge or what you already happen to know! For example, do you shop a lot? You can then write a comparison post on say, &lt;em&gt;Walmart vs Tesco&lt;/em&gt; or &lt;em&gt;Big Bazaar vs Reliance Mart&lt;/em&gt;! If you’re a Python engineer, &lt;em&gt;Flask vs Django&lt;/em&gt; is the ideal post in that case. And how about &lt;em&gt;Veganism vs Vegetarianism&lt;/em&gt;! The possibilities of doing this are almost infinite!&lt;/p&gt;

&lt;p&gt;&lt;a name=&quot;staticAndDynamicPostIdeas&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;static-and-dynamic-post-ideas&quot;&gt;Static and Dynamic Post Ideas.&lt;/h2&gt;

&lt;p&gt;The above process of creatively combining your knowledge with basic content types will give you what is called &lt;em&gt;static posts&lt;/em&gt;. They’re called as such because the content is mostly static in nature (though their relevance will progressively get lower with time), and hence such posts can be planned in advance and organized using what is popularly called &lt;em&gt;blogging calendar&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Now, because of how the Internet works, you’re only allowed to share a couple of posts each day on social media platforms at most, so you must plan your content accordingly. Writer’s muse typically doesn’t work evenly, they come and go in leaps and bounds. But you can’t afford to don’t interact at all for a week and then dump all articles at once on Twitter, that will most likely flag you as a spammer! What bloggers typically do is they write their drafts in advance (leaps and bounds creativity!) and then schedule to publish them evenly.&lt;/p&gt;

&lt;p&gt;Apart from these static posts which you can plan in advance, you can also write a &lt;em&gt;dynamic post&lt;/em&gt; which can be written and published any time. Some big event took place in your field all of a sudden, yesterday? You’ll have a lot to say about that topic, so you can come up with a new post and publish it immediately.&lt;/p&gt;

&lt;p&gt;Your content strategy should involve publishing a mix of both static and dynamic posts frequently. Having only dynamic posts won’t give you much content at all because there will be only so much happening in your given field every day. On the other hand, having static posts only will make your blogging process feel more monotonous and less creative. So, having a mix of both is a good ideal.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Tackling a common CSS design problem: Footer placement at page or content bottom, whichever is lower</title>
   <link href="https://prahladyeri.github.io/blog/2021/04/tackling-a-common-css-design-problem-footer-placement-at-page-or-content-bottom-whichever-is-lower.html"/>
   <updated>2021-04-25T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2021/04/tackling-a-common-css-design-problem-footer-placement-at-page-or-content-bottom-whichever-is-lower</id>
   <content type="html">&lt;p&gt;This is one of those commonly occurring nags in web development which I’ve solved several times before but still have to scavenge the googles and stack-overflows each time I run into it. That’s why I’ve decided to document the simple solution to it in this brief article.&lt;/p&gt;

&lt;p&gt;What happens is that if you position your footer div and fix it at bottom of the page (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;position:fixed, bottom:0, width:100%&lt;/code&gt;), it will work great on shorter content pages (where you don’t have to scroll). But the problem is that on longer pages too, instead of moving to the bottom of the content, it will be stuck there at the viewport bottom like an idiot!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/stuck_viewport_bottom_footer.png&quot; alt=&quot;stuck viewport bottom footer&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The above situation can be seen in action in &lt;a href=&quot;https://jsfiddle.net/dwe8t6c5/10/show&quot;&gt;this fiddle&lt;/a&gt; where multiple “lorem ipsum” blocks (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;p&amp;gt;&lt;/code&gt; elements) are placed to simulate content growth. You’ll find that the footer will work flawlessly when the content is short (only 1-2 “lorem ipsum” blocks) but the footer gets stuck at the viewport bottom as you keep adding the blocks and they extend beyond the viewport height!&lt;/p&gt;

&lt;p&gt;On the other hand, instead of positioning your footer, if you just let it be (this is what about 90% of coders initially do), you have another problem. Your footer will now be placed correctly on longer content pages where you must scroll but on the shorter pages, they’ll be hanging in the middle of the page where your content ends as shown in &lt;a href=&quot;https://jsfiddle.net/dwe8t6c5/11/show&quot;&gt;this fiddle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/middle_of_page_footer.png&quot; alt=&quot;middle of page footer&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There could be multiple approaches to solve this problem. I personally prefer the old school method which is quite simple and easy to understand. Besides, it doesn’t require adding of any blank HTML element like “#offset” or “#placeholder” above your footer. All it requires is that all your HTML elements above the footer must be wrapped up inside one container div element. So, the body should be structured something like this:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HTML
..BODY
....div.container
......header1,
......article1,
........p,
......etc, etc.
....footer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then all you have to do is set your div.container’s minimum height to viewport’s height minus footer’s height. Assuming your footer’s height is 55px, all you have to do is:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;nl&quot;&gt;min-height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100vh&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;55px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can see a working demo of this in &lt;a href=&quot;https://jsfiddle.net/dwe8t6c5/14/show&quot;&gt;this fiddle&lt;/a&gt;. Even as you start adding more and more “lorem ipsum” paragraph elements, the footer will always be placed at the “right” place irrespective of other element’s positioning and content size! This is what you’d call a “properly placed footer”:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/properly_placed_footer.png&quot; alt=&quot;properly placed footer&quot; /&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>dateutil.relativedelta: A must have tool in your python kitty</title>
   <link href="https://prahladyeri.github.io/blog/2021/04/dateutilrelativedelta-a-must-have-tool-in-your-python-kitty.html"/>
   <updated>2021-04-25T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2021/04/dateutilrelativedelta-a-must-have-tool-in-your-python-kitty</id>
   <content type="html">&lt;p&gt;Making additions or subtractions to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;datetime&lt;/code&gt; variables is one of the most commonly encountered programming endeavors and that’s what the built-in &lt;a href=&quot;https://docs.python.org/3/library/datetime.html#datetime.timedelta&quot;&gt;datetime.datetime.timedelta&lt;/a&gt; object is for in python. Its very useful in adding hours or days to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;datetime&lt;/code&gt; variables as follows:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;datetime&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;tea_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timedelta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hours&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;next_week&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timedelta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weeks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;datetime.timedelta&lt;/code&gt; objects have one serious limitation. You can add hours, days or even weeks to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;datetime&lt;/code&gt; variable but not months! And there is a reason for that. As per the science of Horology (study/measurement of time), “months” isn’t a fixed quantity of time. There could be “long” or 31 day months like January/March, there could be 30 day months like April/June or even 28 day months like February.&lt;/p&gt;

&lt;p&gt;However, apart from this theoretical concept of month, we also have a practical concept which we commonly rely on for our usual tasks. For example, if today is 25th of April, a month from now would be 25th of May. This practical concept just considers the difference between current date and the same day of next month (25th in this case) as one month while disregarding the fact that the actual duration of this difference may not exactly be a fixed quantity like 30 days. It could be 30, 31 or even 28/29 depending on which month you are counting from and whether its a leap year.&lt;/p&gt;

&lt;p&gt;Computing this practical difference is also a very common programming endeavor but unfortunately, there is no easy way to do this in python using the built-in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;datetime&lt;/code&gt; package. But thankfully, the folks have invented this library called &lt;a href=&quot;https://github.com/dateutil/dateutil&quot;&gt;dateutil&lt;/a&gt; which does exactly that! Installing this package is pretty trivial and in most environments, you might be already having it as hundreds of popular python libraries rely on it.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip install python-dateutil
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After that, you can straightaway start using it to add or subtract months using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;relativedelta&lt;/code&gt; object:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;dateutil.relativedelta&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relativedelta&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;today&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hour&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;minute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;second&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;microsecond&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;epoch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relativedelta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;months&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;relativedelta&lt;/code&gt; is just one of the gems from the dateutil project, there are several others you can explore at &lt;a href=&quot;https://dateutil.readthedocs.io/en/latest/&quot;&gt;https://dateutil.readthedocs.io/en/latest/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy Coding!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The right way to serve static files when using django with gunicorn</title>
   <link href="https://prahladyeri.github.io/blog/2021/04/the-right-way-to-serve-static-files-when-using-django-with-gunicorn.html"/>
   <updated>2021-04-19T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2021/04/the-right-way-to-serve-static-files-when-using-django-with-gunicorn</id>
   <content type="html">&lt;p&gt;Yesterday, I learned during deployment that your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Django&lt;/code&gt; app when used in combination with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gunicorn&lt;/code&gt; will refuse to serve static files, do whatever you may. I looked up almost every Stack Overflow answer post on this topic including &lt;a href=&quot;https://stackoverflow.com/q/59857966/849365&quot;&gt;this&lt;/a&gt;, &lt;a href=&quot;https://stackoverflow.com/q/54972837/849365&quot;&gt;this&lt;/a&gt; and &lt;a href=&quot;https://stackoverflow.com/q/59857966/849365&quot;&gt;this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/code.jpg&quot; alt=&quot;python-source-code&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I meddled with almost every hopeful setting including &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STATICFILES_DIRS[]&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STATIC_ROOT&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STATIC_URL&lt;/code&gt; but to no avail. Its as if Django is designed to refuse serving of static files when using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gunicorn&lt;/code&gt; and that’s what I started to suspect after everything failed.&lt;/p&gt;

&lt;p&gt;And my suspicion was almost confirmed by &lt;a href=&quot;https://stackoverflow.com/q/20175243/849365&quot;&gt;this post&lt;/a&gt; which says that:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Gunicorn will only serve the dynamic content, i.e. the Django files&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But I know that’s not strictly true because I’ve used Gunicorn with Flask in the past and it serves static files of your Flask app without any issues at all!&lt;/p&gt;

&lt;p&gt;But then I thought that its better to handle static files using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx&lt;/code&gt; anyway and since I was already using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx&lt;/code&gt; as the front proxy on my server anyway, I thought of trying that post’s suggestion. As mentioned, I added a new location section to my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx&lt;/code&gt; configuration file as follows:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;location /static &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		autoindex on&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;alias&lt;/span&gt; /path/to/staticfiles&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And that’s exactly what worked! I bypassed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gunicorn&lt;/code&gt; entirely and static files are now being served directly by the front server and I think this is a more efficient setup than having &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gunicorn&lt;/code&gt; serve the static files.&lt;/p&gt;

&lt;p&gt;But why &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gunicorn/django&lt;/code&gt; refuse to serve static files directly still remain a mystery. I think the problem lies somewhere in Django and not gunicorn because as I said, I’ve seen gunicorn serve Flask static files before.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to enable twitter card validation for your blogspot blog</title>
   <link href="https://prahladyeri.github.io/blog/2021/01/how-to-set-your-blogspot-blog-for-twitter-card-validation.html"/>
   <updated>2021-01-18T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2021/01/how-to-set-your-blogspot-blog-for-twitter-card-validation</id>
   <content type="html">&lt;p&gt;When you publish a post on your blog, one of the first things you want to do is share it with the world, and &lt;strong&gt;Twitter/X&lt;/strong&gt; is one of the most popular platforms for that. However, if you’re using &lt;strong&gt;Google’s Blogspot&lt;/strong&gt; (also known as Blogger), your blog posts won’t automatically display as Twitter Cards when shared. Fortunately, you can easily configure your blog to show those eye-catching &lt;strong&gt;Twitter Cards&lt;/strong&gt; by following a few simple steps.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/twitter-card-blogger.png&quot; alt=&quot;twitter-card-blogger&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In this guide, we’ll walk you through the process of enabling Twitter Cards for your Blogspot blog so your posts display correctly when shared.&lt;/p&gt;

&lt;h2 id=&quot;why-twitter-cards-matter&quot;&gt;Why Twitter Cards Matter&lt;/h2&gt;

&lt;p&gt;Twitter Cards allow you to enhance tweets that link to your content by including images, summaries, and other metadata. This makes your tweets stand out and improves engagement with your audience. By default, Blogspot doesn’t provide the necessary meta tags for Twitter Cards, but with a small tweak, you can enable them.&lt;/p&gt;

&lt;p&gt;Follow these steps to ensure that your blog links expand into a Twitter Card when shared.&lt;/p&gt;

&lt;h2 id=&quot;steps-to-enable-twitter-cards-for-blogspot&quot;&gt;Steps to Enable Twitter Cards for Blogspot&lt;/h2&gt;

&lt;h3 id=&quot;1-access-your-blogs-html-template&quot;&gt;1. Access Your Blog’s HTML Template&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;In your blogger settings, visit &lt;strong&gt;Theme&lt;/strong&gt; &amp;gt; &lt;strong&gt;Down Arrow near Customize Button&lt;/strong&gt; &amp;gt; &lt;strong&gt;Edit HTML&lt;/strong&gt; in your Blogspot dashboard. This will open the HTML editor for your blog template.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;2-find-the-post-section&quot;&gt;2. Find the Post Section&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Click on the HTML editor. Then press &lt;strong&gt;Ctrl+F&lt;/strong&gt; to bring up the search bar, and search for the following tag:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;b:includable&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;post&apos;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;var=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;post&apos;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;You may need to search more than once, as some templates contain multiple instances. We’re interested in the &lt;strong&gt;last&lt;/strong&gt; occurrence of this tag.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;3-add-twitter-card-meta-tags&quot;&gt;3. Add Twitter Card Meta Tags&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Right below this tag, create a new line and paste the following code:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Twitter Card Tags --&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;@yourTwitterHandle&apos;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;twitter:site&apos;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;@yourTwitterHandle&apos;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;twitter:creator&apos;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;b:if&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;cond=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;data:post.firstImageUrl&apos;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
	   &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;summary_large_image&apos;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;twitter:card&apos;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- summary_large_image or any other your card types --&amp;gt;&lt;/span&gt;
	   &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;expr:content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;data:post.firstImageUrl&apos;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;twitter:image&apos;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt; 
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;b:elseif&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;cond=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;data:blog.postImageUrl&apos;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
	   &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;summary_large_image&apos;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;twitter:card&apos;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
	   &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;expr:content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;data:blog.postImageUrl&apos;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;twitter:image&apos;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt; 
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;b:elseif&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;cond=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;data:blog.postImageThumbnailUrl&apos;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
	   &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;summary&apos;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;twitter:card&apos;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
	   &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;expr:content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;data:blog.postImageThumbnailUrl&apos;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;twitter:image&apos;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt; 
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;b:else/&amp;gt;&lt;/span&gt;
	   &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;summary_large_image&apos;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;twitter:card&apos;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
	  &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;https://example.com/default-image.jpg&apos;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;twitter:image&apos;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;/b:if&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;4-customize-the-meta-tags&quot;&gt;4. Customize the Meta Tags&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Be sure to replace the following with your own details:
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@yourTwitterHandle&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;twitter:site&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;twitter:creator&lt;/code&gt; meta tags.&lt;/li&gt;
      &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;twitter:image&lt;/code&gt; URL in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;meta content=&apos;https://example.com/default-image.jpg&apos; name=&apos;twitter:image&apos;/&amp;gt;&lt;/code&gt; line, which is the default image for posts without images.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;5-save-and-test-your-template&quot;&gt;5. Save and Test Your Template&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Currently, the Twitter Card Validator may not be operational. Instead, you can test your blog’s Twitter Cards by sharing a link to your blog in a tweet and observing how it appears in the Twitter feed. Ensure that the link expands into the desired card format with the correct metadata and images.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;6-reapply-when-changing-themes&quot;&gt;6. Reapply When Changing Themes&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Keep in mind that if you change your blog theme in the future, you’ll need to repeat this process because new themes overwrite the HTML template entirely.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;7-share-your-blog-posts-on-twitter&quot;&gt;7. Share Your Blog Posts on Twitter&lt;/h3&gt;
&lt;p&gt;Once your blog is set up for Twitter Cards, you can confidently share your Blogspot links on Twitter, knowing they will appear in a more engaging format.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;By enabling Twitter Cards, you’ll make your Blogspot blog more appealing and clickable when shared on social media. Follow the steps outlined above to set up Twitter Cards, and remember to test using the Twitter Card Validator to ensure everything is working as expected. With these enhancements, your blog posts will have a more professional appearance and higher engagement potential when shared on Twitter.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The enduring value of Email: why it still matters even today</title>
   <link href="https://prahladyeri.github.io/blog/2020/12/lets-do-more-email.html"/>
   <updated>2020-12-14T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2020/12/lets-do-more-email</id>
   <content type="html">&lt;p&gt;Despite the rise of privacy advocates discouraging use of email, it remains a valuable communication tool with numerous benefits. Here’s why you should continue to embrace email in your digital communication strategy.&lt;/p&gt;

&lt;h2 id=&quot;the-nostalgic-value-of-written-communication&quot;&gt;The nostalgic value of written communication&lt;/h2&gt;

&lt;p&gt;When did you last see someone post a letter in an inland or envelope in the old-fashioned way? Believe it or not but the art of “letter writing” has become an almost extinct skill these days. Email is the last remaining vestige of actual written communication, allowing for thoughtful exchanges while preserving records in an open format.&lt;/p&gt;

&lt;h2 id=&quot;no-true-alternatives&quot;&gt;No true alternatives&lt;/h2&gt;

&lt;p&gt;A plethora of instant messaging (IM) apps have emerged over the years, including Skype, WhatsApp, Telegram and Discord. However, none can replace the functionality of email. It’s the only communication mode where the sender can respond with substantial content after taking a moment to think, rather than quickly “IMing” a few words through a mobile app.&lt;/p&gt;

&lt;h2 id=&quot;open-standards&quot;&gt;Open standards&lt;/h2&gt;

&lt;p&gt;Email is not controlled by any private sector entity; it operates on open standards based on well-established protocols like IMAP, SMTP and POP3. In contrast, proprietary platforms like WhatsApp and Telegram rely on non-standard technologies that restrict interoperability.&lt;/p&gt;

&lt;h2 id=&quot;accountability-through-records&quot;&gt;Accountability through records&lt;/h2&gt;

&lt;p&gt;The ability to retain records makes people more responsible in their tone and language. Unlike “disappearing WhatsApp messages”, emails can be archived and preserved for decades or even centuries, allowing for accountability and clarity in communication.&lt;/p&gt;

&lt;h2 id=&quot;enhanced-security-options&quot;&gt;Enhanced security options&lt;/h2&gt;

&lt;p&gt;Email can be secured with tools like VPN and plugins like Enigmail. Contrary to popular belief, email communication is not inherently insecure. Just as SSL/TLS enhances the security of HTTP, Enigmail can be used to digitally sign and encrypt outgoing emails, safeguarding sensitive information.&lt;/p&gt;

&lt;p&gt;As you navigate your digital communication landscape, consider the unique advantages email offers. It may be time to re-evaluate how you communicate and leverage the benefits of email in your professional and personal life.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>DIY Keyword research for small bloggers using absolutely free tools</title>
   <link href="https://prahladyeri.github.io/blog/2020/12/diy-keyword-research-for-small-bloggers-using-absolutely-free-tools.html"/>
   <updated>2020-12-06T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2020/12/diy-keyword-research-for-small-bloggers-using-absolutely-free-tools</id>
   <content type="html">&lt;p&gt;There are a plethora of keyword research tools available in the market such as &lt;a href=&quot;https://www.semrush.com/&quot;&gt;SEMRush&lt;/a&gt;, &lt;a href=&quot;https://ahrefs.com/dashboard&quot;&gt;ahrefs&lt;/a&gt;, &lt;a href=&quot;https://kwfinder.com/&quot;&gt;KWFinder&lt;/a&gt;, etc. and while a subscription to them makes sense if you are a small company or startup, it definitely doesn’t for small bloggers who work with a limited budget.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/laptop-coffee-mobile.jpeg&quot; alt=&quot;python code&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But even if you have an absolutely zero budget, you can still perform decent keyword research and get the exact same results that these popular web services offer. However, you won’t be served that information on a platter and you will have to work for it and setup your own workflow after sourcing inputs or raw material from various freely available tools. You must also know a little bit of scripting/programming and be prepared to write some scripts in a language like python.&lt;/p&gt;

&lt;p&gt;Each blogger or researcher may have a different workflow with respect to keyword research, mine involves the following two tools:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://ads.google.com/aw/keywordplanner/&quot;&gt;Google Keyword Planner&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://answerthepublic.com/&quot;&gt;AnswerThePublic.com&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both are excellent and robust freely available tools (though the latter gets you only a limited number of searches each day which is fine if you’re a small blogger who posts only few times a week).&lt;/p&gt;

&lt;p&gt;In general, writing any article involves the following steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Determination of the topic and/or niche you want to publish about.&lt;/li&gt;
  &lt;li&gt;Research and gather information or sources about it (like books, wikipedia, online blogs, etc.)&lt;/li&gt;
  &lt;li&gt;Actually write the article.&lt;/li&gt;
  &lt;li&gt;Give it a decent or captivating headline.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Performing keyword research beforehand will help you with almost all these steps extensively. Firstly, it will help you determine which topic or niche you must blog about so that people actually find your content through search engines. In other words, you shouldn’t end up writing an excellent piece about a topic which:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Nobody is interested in (low search volume).&lt;/li&gt;
  &lt;li&gt;Has so high competition that it’s very difficult to rank on the first page.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These are the two areas which you must actively avoid if you want to blog for visibility or getting web traffic. Of course, there are hobby bloggers who post just for the sake of fun or writing passion and that’s fine too.&lt;/p&gt;

&lt;p&gt;Coming to the process of niche determination using keyword research, it involves two tasks:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Find out which keywords related to our broader topic are currently in vogue or trending.&lt;/li&gt;
  &lt;li&gt;Find out which among those related keywords have a combination of good search volume and less ranking difficulty.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With Google Keyword Planner, both these tasks can be clubbed into one as it provides ways to both discover new keywords and getting search volume and forecasts for a given bunch of keywords (see below).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/google_keyword_planner_tool.png&quot; alt=&quot;Google Keyword Planner Tool&quot; /&gt;&lt;/p&gt;

&lt;p&gt;An alternative process of niche determination involves using ATP (answerthepublic.com) for the first task as it provides a richer set of related keywords which are questions or slightly longer phrases which have the potential for attracting long tail search volume. You then input these longer keywords provided by ATP to the second part of the Google Planner Tool (“Get search volume and forecasts”). But keep in mind that you can’t just lift the keywords from ATP and enter them directly to Google Keyword Planner tool!&lt;/p&gt;

&lt;p&gt;Here is where scripting comes into picture. The CSV data exported by ATP has to be sanitized to the proper format (extra columns removed) so that it can be then entered to the Google Keyword Planner Tool. Scripting is also needed to store the output CSV from Google Planner to a proper database. Personally, I prefer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlite&lt;/code&gt; but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysql&lt;/code&gt; is fine too. Google’s output CSV has almost everything you need, almost everything which the other subscription based services provide.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/google_keyword_planner_results.png&quot; alt=&quot;Google Keyword Planner Results&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But the only catch is that it’s a DIY. You need to write your own program or script which imports this output to your database tables and stores data in the respective fields. “Avg. monthly searches” is quite apparent, you can use a long integer column called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;searches&lt;/code&gt; to store this. “Competition” tells you qualitatively whether it’s high/medium/low for that keyword, and “Competition (Index Value)” does the same quantitatively. It’s an index value ranging from 0 to 100 and often referred to as KD (Keyword Difficulty) in the other popular tools.&lt;/p&gt;

&lt;p&gt;Once you’ve built your own workflow with automated scripts that involves importing and sanitizing data from ATP/Google, and storing it to a local database, you should be able to get what you want with a simple SQL query such as this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;select keyword from keywords where keyword like &apos;%flask%&apos; and searches &amp;gt;= 1000 and comp_idx &amp;lt; 30;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will return all keywords in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python flask&lt;/code&gt;	niche which has a good search volume (1000 or above) and less difficulty to rank (competition index is less than 30). You can then proceed to use those keywords for writing your article which will now have a greater visibility.&lt;/p&gt;

&lt;p&gt;A paid tool like SEMRush or ahrefs would have given you the exact same results as this SQL query but your solution is custom developed and more importantly, suited to your own work-flow. The only difference is that your database won’t be having billions keywords at the start but it will grow organically and gradually as you keep writing articles and keep researching new content.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to perform Microsoft OneDrive OAuth sign-in and authorization in a python web app</title>
   <link href="https://prahladyeri.github.io/blog/2020/11/how-to-perform-microsoft-onedrive-oauth-sign-in-and-authorization-in-a-python-web-app.html"/>
   <updated>2020-11-14T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2020/11/how-to-perform-microsoft-onedrive-oauth-sign-in-and-authorization-in-a-python-web-app</id>
   <content type="html">&lt;p&gt;The methods and examples given in this article are based on flask framework but they should apply to django or something similar too with a little tweak. Few weeks ago, I had landed myself on a project of similar nature and though I found several helpful articles and blog posts (such as &lt;a href=&quot;https://dev.to/jsnmtr/automating-files-upload-to-microsoft-onedrive-unexpected-challenges-and-a-success-story-2ini&quot;&gt;this one&lt;/a&gt;), none of them explained this process in a simple but comprehensive manner, so I’m writing one myself.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/code-python.jpg&quot; alt=&quot;python code&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The first and foremost step is to visit the &lt;a href=&quot;https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade&quot;&gt;Azure developer portal&lt;/a&gt; and register your app there. Just make sure that you select “All Microsoft account users” for supported account types and not just personal Microsoft accounts. This is needed in case you want your web app to perform OneDrive file transfers on behalf of the signed in user.&lt;/p&gt;

&lt;p&gt;Make note of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_id&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_secret&lt;/code&gt; values and store them somewhere as you’re going to need them while writing your app code.&lt;/p&gt;

&lt;p&gt;Its also important to add the following permissions by clicking the “API permissions” link, and then add a scope for them by visiting the “Expose an API” link on the developer console screen:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;&lt;strong&gt;API/Permissions&lt;/strong&gt;&lt;/th&gt;
      &lt;th&gt;&lt;strong&gt;Type&lt;/strong&gt;&lt;/th&gt;
      &lt;th&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Files.ReadWrite.All&lt;/td&gt;
      &lt;td&gt;Delegated&lt;/td&gt;
      &lt;td&gt;Have full access to all files user can access&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;offline_access&lt;/td&gt;
      &lt;td&gt;Delegated&lt;/td&gt;
      &lt;td&gt;Maintain access to data you have given it access to&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;User.Read&lt;/td&gt;
      &lt;td&gt;Delegated&lt;/td&gt;
      &lt;td&gt;Sign in and read user profile&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Also note that if you couldn’t set the supported account types to “All Microsoft account users” in the initial setup for some reason, you can later change it by updating the following XML setting after visiting the “Manifest” link on the app console:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;signInAudience&quot;: &quot;AzureADandPersonalMicrosoftAccount&quot;,
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, its also important to configure redirect URIs by visiting the “Authentication” screen. This is the URL endpoint where our signed-in user will be redirected to and our app receives the “code” request argument which is needed to complete the OAuth process. In my case, I configured two redirect URIs, one for testing and one for production respectively:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- http://localhost:5000/post_onedrive
- https://example-app.com/post_onedrive
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once the registered app is configured thus, we focus on our python app. Its important to provide the two URL endpoints here:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;First, where our signed in user visits (by clicking a link/button that says “Authorize OneDrive” or “Link OneDrive”), and thus starts the OAuth process:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;/link_onedrive&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methods&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;GET&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;link_onedrive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cursor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opendb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;settings&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;select * from settings&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fetchone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={client_id}&amp;amp;scope={scope}&amp;amp;response_type=code&amp;amp;redirect_uri={redirect_uri}&quot;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;#scopes = &quot;wl.basic onedrive.readwrite wl.offline_access&quot;
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;scopes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Files.ReadWrite.All offline_access&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;od_client_id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scopes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect_uri&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OD_REDIRECT_URI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;url: &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this case, I’ve already stored the app credentials like client_id, etc. in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlite&lt;/code&gt; table called settings from which I’m fetching them using a database connection. After that, its only a matter of calling the oauth URL with those specific parameters.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Second, where the OneDrive’s redirection is handled (/post_onedrive in above example):&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;/post_onedrive&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methods&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;GET&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;POST&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;post_onedrive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cursor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opendb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;settings&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;select * from settings&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fetchone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;One Drive: HERE HERE!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;now calling finish url&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://login.microsoftonline.com/common/oauth2/v2.0/token?client_id={client_id}&amp;amp;redirect_uri={redirect_uri}&amp;amp;client_secret={client_secret}&amp;amp;code={code}&amp;amp;grant_type=authorization_code&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;client_id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;od_client_id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;client_secret&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;od_client_secret&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;redirect_uri&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OD_REDIRECT_URI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;code&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;code&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;grant_type&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;authorization_code&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;client_id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect_uri&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;redirect_uri&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client_secret&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;client_secret&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;code&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Content-type&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;application/x-www-form-urlencoded&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;json response:&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;saving tokens&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;delete from user_creds where user_id=? and auth_type=&apos;onedrive&apos;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;insert into user_creds(user_id, auth_type, access_token, refresh_token) values (?,?,?,?)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;onedrive&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;access_token&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;refresh_token&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;update users set od_auth=&apos;y&apos; where id=?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;od_auth&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;y&apos;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;flash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Your One Drive account was linked successfully!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;index&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once OneDrive redirects me to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/post_onedrive&lt;/code&gt; endpoint, I just have to complete the process using the code (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;request.args[&apos;code&apos;]&lt;/code&gt;) parameter, so that I can get &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;access_token&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;refresh_token&lt;/code&gt; values. Once I get these values, I can store them as credentials against that user’s account (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user_creds&lt;/code&gt; sqlite table in this case). Once that is done, I (or my app) can access that user’s drive any time. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;access_token&lt;/code&gt; credential has a expiration limit of one hour but after that we can get a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;access_token&lt;/code&gt; using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;refresh_token&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;Here is a part of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cron.py&lt;/code&gt; background script which keeps accessing each user’s drive by using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;access_token&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;refresh_token&lt;/code&gt; received thus. In this case, the function is used to create a new folder on the user’s drive at a given path:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_folder_in_od&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;creds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dst_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# create folder in onedrive
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;write_log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;creating folder in onedrive: %s/%s&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dst_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Authorization&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;bearer &apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;creds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;access_token&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Content-Type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;application/json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;drive_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;https://graph.microsoft.com/v1.0/me/drive/root:%s:/children&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dst_path&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;folder&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;drive_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dumps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;write_log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;success!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, settings and creds correspond to sqlite rows in settings and user_creds table respectively, which store the app settings (client_id and client_secret) and user settings (access_token and refresh_token) respectively.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;drive_url&lt;/code&gt; variable consists of two parts: The Microsoft Graph site URL which provides the Drive API (graph.microsoft.com/v1.0) and the particular API endpoint for creating a new folder on the drive (/me/drive/root:&lt;path&gt;:/children). Similarly, there are OneDrive API endpoints for doing other things, they are thoroughly documented, [here](https://docs.microsoft.com/en-us/onedrive/developer/rest-api/resources/driveitem?view=odsp-graph-online) you can find various API endpoints for uploading, downloading, etc.&lt;/path&gt;&lt;/p&gt;

&lt;p&gt;In the next couple of days, I’ll try to put up a demo repo for this whole thing on Github. Until then, Happy Coding!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to clear individual site cookies on android firefox</title>
   <link href="https://prahladyeri.github.io/blog/2020/09/how-to-clear-individual-site-cookies-on-android-firefox.html"/>
   <updated>2020-09-05T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2020/09/how-to-clear-individual-site-cookies-on-android-firefox</id>
   <content type="html">&lt;p&gt;One might wonder why an entire blog post is needed for a simple matter of clearing cookies on a web browser in the 21st century. But believe it or not, such are the state of things when it comes to android browsing! Firefox is a great browser to have on Android because it gives you three features which are impossible to get even with Google’s Chrome:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Plugin Support&lt;/li&gt;
  &lt;li&gt;Ad Blocking&lt;/li&gt;
  &lt;li&gt;Data Privacy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s why over 3 million users have decided to stick with firefox despite the fact that its performance totally lacks in comparison to Chrome. Even so, there are some annoying bugs in firefox such as not being able to clear cookies for individual websites (at least as of version 68.11). This seemingly simple feature isn’t available out of the box on firefox and when I googled for it, I found two solutions:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;As this &lt;a href=&quot;https://android.stackexchange.com/a/120476/38760&quot;&gt;android stackexchange answer&lt;/a&gt; suggests, you can try extracting the below data file (where firefox stores cookies) to your PC, edit it and then put it back (you must be rooted though):&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; /data/data/org.mozilla.firefox/files/mozilla/{YOUR_PROFILE}/cookies.sqlite
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;But if you aren’t rooted or you don’t want to go through the above troubled exercise, I’ve found a much simpler solution which is to install &lt;a href=&quot;https://addons.mozilla.org/en-US/firefox/addon/a-cookie-manager&quot;&gt;this extension called Cookie Manager by Rob W&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since the android firefox browser supports extensions, I found it much easier to just install this extension, open it and type a domain such as *.reddit.com for which you want the cookies deleted, and press the “Search cookies” button:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/cookie_manager.png&quot; alt=&quot;cookie manager&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Then you just click the “Select all” button at the bottom followed by “Remove selected”, that’s it!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>SEO vs Content: What really drives traffic to your blog?</title>
   <link href="https://prahladyeri.github.io/blog/2020/08/is-it-good-seo-or-good-content-that-drives-traffic-to-your-blog-a-causal-analysis.html"/>
   <updated>2020-08-16T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2020/08/is-it-good-seo-or-good-content-that-drives-traffic-to-your-blog-a-causal-analysis</id>
   <content type="html">&lt;h2 id=&quot;the-content-vs-seo-debate&quot;&gt;The Content vs. SEO Debate&lt;/h2&gt;

&lt;p&gt;In the content publishing world, there has been a constant battle between the “Content is King” camp and the “SEO” camp! The first camp insists that it’s good content that essentially drives traffic to your blog or website. The second camp insists that it’s good Search Engine Optimization (SEO) techniques like keyword research and relevant meta tags that does it by standing up to the search engine algorithms that determine the page ranking for each link.&lt;/p&gt;

&lt;p&gt;The truth, obviously, lies somewhere in the middle. There used to be a time in the 1990s (and probably early 2000s) when you could easily fool the Google search algorithm into making your page rank on top by merely spamming it with good keywords. But that started changing as the algorithms got smarter, today they are able to separate the wheat from the chaff. Today, they are able to tell the difference between good content and bad, or do they?&lt;/p&gt;

&lt;p&gt;In a perfect world, SEO should work only to the extent of supply/demand of relevant information. In other words, to the extent that people searching for online booking of flight tickets is greater than people searching for flight simulator games, the content related to former must rank above for most queries related to “flights” and thus they should bag a larger number of “hits” overall than the latter.&lt;/p&gt;

&lt;h2 id=&quot;how-search-engines-have-evolved&quot;&gt;How Search Engines Have Evolved&lt;/h2&gt;

&lt;p&gt;But among all the “online flight booking” websites, how does the Google algorithm decide which ones to put on top and which ones at the bottom? How does it know that makemytrip.com and goibibo.com should rank higher than cleartrip.com and air.irctc.co.in for an Indian visitor? Or for that matter, how does it know that expedia.co.uk and skyscanner.net should be placed higher than momondo.co.uk or wegotravel.co.uk for a UK visitor?&lt;/p&gt;

&lt;p&gt;Now, this is where SEO and keyword research come into play which supposedly has some influence on that algorithm. SEO proponents like to think that their proposed measures (like filling the right meta tags, populating the page with right keywords, etc.) play a role in making makemytrip.com rank higher than cleartrip.com in Google searches. But can that really be true? Common sense suggests that search algorithms are a whole lot more complex beasts today than they once were and are influenced by just way too many things to create such a direct relationship.&lt;/p&gt;

&lt;h2 id=&quot;the-role-of-keywords-and-links&quot;&gt;The Role of Keywords and Links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Links (inbound and outbound)&lt;/strong&gt;. They play a large role in making the ranking decision. It could be the fact that makemytrip.com has a lot more links flowing in from highly reputable sites than air.irctc.co.in, so the algorithm is considering that in ranking. And of course, they should all be good (organic) links with no evidence of foul play! Now, trying to influence one’s own site’s ranking by throwing their links across various social media sites is an SEO technique called “link building” and is a slightly grey area. Do it too much and the search engines might ban your site or blog entirely in today’s age. Done correctly, link building provides a good initial boost of popularity, it’s a kind of marketing dude who goes across each home and tells good thinks about your product. But if the dude tries to be too intrusive or pushy, he might get blocked from the apartment. Thus, link building is more of a marketing trick than SEO technique in my opinion.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Keywords&lt;/strong&gt;. Well, they sometimes do work but I’d like to think of them as the effect of a well ranked page, not the cause. In retrospect, it explains how the page’s content got ranked above its peers by the ranking algorithm but trying to replicate that strategy on another site by attempting to use more of those keywords may not always be successful. Trying to reduce textual content to mere keywords is like trying to reduce the human mind and emotions to mere neurons and synapses in the brain! They do play a huge role in the thinking process, but we humans are definitely more than our brain cells. That’s the problem with many of today’s so called “SEO Experts”, they are pushing keyword research as some kind of a silver bullet to page ranking while ignoring everything else.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Content quality&lt;/strong&gt;. Ultimately, it’s good content that every googler is really searching for! It’s definitely not very easy to create good textual content and that’s because writing is not an easy craft. It takes a lot of effort to come up with good content periodically and this difficulty is what has given rise to the whole SEO industry! It’s my unpopular and controversial opinion that in an ideal world, SEO shouldn’t be needed at all (except to the extent of finding out what kind of content people are searching for (demand), so that you may write content relevant to that (supply)).&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;why-content-quality-matters-most&quot;&gt;Why Content Quality Matters Most&lt;/h2&gt;

&lt;p&gt;Again, trying to meet supply with demand is a common sense discipline, I wouldn’t call it SEO at all! But you don’t really need keyword research to tell you that do you? If you are an industry veteran who has some experience under your belt, don’t you already know what is the trendiest subject in your industry? As a php programmer, for example, you must know that Laravel and Symfony are far more trendy these days than CodeIgniter or CakePHP. That doesn’t mean that you shouldn’t write articles on CodeIgniter, by all means you should if that’s your area of expertise! But you must also be aware that those articles won’t be catching as many eyeballs as the ones on Laravel or Symfony.&lt;/p&gt;

&lt;p&gt;Same is the case in regard to Angular, React and Vue.js in the JavaScript world. You can probably rank much higher by writing on any of these frameworks instead of something like Backbone.js or Ember.js. All it takes is a little bit of reading and presence on social media to know that the former topics are in lot more demand than latter. Consequently, if you are a blogger who writes for adsense income or a freelance copywriter who sells content for a living, you’ll know better to write more on the former topics than the latter. On the other hand, if you are a hobbyist blogger who writes for self-fulfillment and your interest in Backbone/Ember, you will write about the latter subjects even if they are less in demand.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;The debate between the “Content is King” and “SEO” camps will probably continue for decades to come, but it all depends on who you are and what you hope to gain from SEO (or some of it’s aspects called &lt;em&gt;content marketing&lt;/em&gt;). If you are a blogger or writer, my advice for you is to focus on mastering the art of churning great content frequently than trying to master the art of SEO.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to use window.hashchange event to implement routing in vanilla javascript</title>
   <link href="https://prahladyeri.github.io/blog/2020/08/how-to-use-windowhashchange-event-to-implement-routing-in-vanilla-javascript.html"/>
   <updated>2020-08-06T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2020/08/how-to-use-windowhashchange-event-to-implement-routing-in-vanilla-javascript</id>
   <content type="html">&lt;p&gt;The popular wisdom when it comes to implement routing in your client side apps or single page apps (SPA) as they are called these days, is to just grab an “off-the-shelf” framework like angular or vue and start using it. Well, that’s the easy way out but you will never understand the nuts and bolts of how things like routing work at the low level. Besides, if routing is the only major requirement in your app, why riddle your app with unnecessary bloat of frameworks?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/code.jpg&quot; alt=&quot;source code&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you happen to agree with that wisdom, you can proceed further to learn how exactly to implement routing in vanilla javascript.&lt;/p&gt;

&lt;p&gt;When it comes to routing, your app has two main options:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;To use hash urls such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost/#login&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost/#register&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;To use fully fledged or “proper” urls like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost/login&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost/register&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As you can see, the major difference is that the former method uses hashes (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#&lt;/code&gt;) which are used to control routing on the client side whereas the latter method uses complete urls without the hashes. The latter method requires you to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;history.pushState&lt;/code&gt; API method and while these urls look quite sexy and elegant, they come with a cost: your backend web server must support routing up to multiple path fragments too. This means that you will need a “proper” web-server on the backend such as apache or nginx, you can’t do this with “simple” http servers such as the python’s built-in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http.server&lt;/code&gt; module or even static hosting facilities like github-pages and netlify. So, if you intend to host your app using github-pages or netlify, you must remember to use hash urls only!&lt;/p&gt;

&lt;p&gt;Considering that hash urls method is compatible with all kinds of backend servers, its a good practice to use them in all apps, irrespective of the backend used. Once again, popular wisdom may not agree with that and most “experts” may in fact advise you to do the opposite!&lt;/p&gt;

&lt;p&gt;Coming to the point, implementing hash urls is as easy as handling the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.hashchange()&lt;/code&gt; browser event. Using vanilla javascript, it can be as simple as this:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;hashchange&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#index&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
			&lt;span class=&quot;nx&quot;&gt;doIndexStuff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#login&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
			&lt;span class=&quot;nx&quot;&gt;doLoginStuff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#register&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
			&lt;span class=&quot;nx&quot;&gt;doRegisterStuff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
		&lt;span class=&quot;nl&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
			&lt;span class=&quot;nx&quot;&gt;handleDefaultCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You see how simple url routing is with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hashchange&lt;/code&gt;? You want to have advanced routing features like path fragments and parameters? No problemo! You can use simple string.split() method to take care of that:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//#user/prahlad?view=profile&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;parts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fragments&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// user/prahlad&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;paramParts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;nx&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;paramParts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pair&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;paramParts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
		&lt;span class=&quot;nx&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;
	&lt;span class=&quot;nx&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
	&lt;span class=&quot;nx&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This simple piece of code will parse your hash url and fill the successive path fragments (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user/prahlad&lt;/code&gt;) in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fragments&lt;/code&gt; array and the parameter pairs (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?view=profile&lt;/code&gt;) as key-value objects in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;params&lt;/code&gt; array. You can of course mix, match and customize this code as per your own scenario but this is all there is to routing in about 90% of use cases!&lt;/p&gt;

&lt;p&gt;Enjoy and Happy Coding!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Two years later, the reasons for Microsoft's Github acquisition still remain a mystery</title>
   <link href="https://prahladyeri.github.io/blog/2020/07/two-years-later-github-acquisition-still-a-mystery.html"/>
   <updated>2020-07-28T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2020/07/two-years-later-github-acquisition-still-a-mystery</id>
   <content type="html">&lt;p&gt;More than two years have passed since I shared my perspective on Microsoft’s Github acquisition &lt;a href=&quot;/blog/2018/06/microsofts-github-acquisition-an-unbiased-perspective.html&quot;&gt;in this blog post earlier&lt;/a&gt;, and the point still stands today. Why did Microsoft decide to acquire a startup company much smaller in size which had no viable and long term revenue/profit generation model?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/github.png&quot; alt=&quot;Github Logo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If Microsoft wanted their team to use Github in their daily work-flow, there was hardly a need to acquire the whole company in a multi billion dollar deal. Github offered free private repositories even back then (and even their premium-tier subscription should have cost peanuts for Microsoft to get it for their entire staff!).&lt;/p&gt;

&lt;p&gt;If Microsoft wanted to sell it to enterprise clients, again much better and creative ways to do that. Brand Microsoft is several times more valuable than Brand Github in the enterprise world anyway. And those are the type of clients who would have already invested heavily in Microsoft stack, so something like Microsoft’s Team Office would make a lot more sense for them than Github.&lt;/p&gt;

&lt;p&gt;What then could have been the reason? Its still unknown but that’s probably true about Microsoft’s &lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_mergers_and_acquisitions_by_Microsoft&quot;&gt;many other past acquisitions&lt;/a&gt; including Skype, Hotmail, Nokia and Linkedin. What exactly did Microsoft gain by each of these acquisitions? Arguably none in financial terms but perhaps that wasn’t their motive at all. Perhaps they want to make a mark on the sands of time by owning some of the most popular artifacts of our times? That could also explain Microsoft getting themselves a costly seat on the board of Linux Foundation too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some &lt;a href=&quot;https://old.reddit.com/r/linux/comments/hzbru6/two_years_later_the_reasons_for_microsofts_github/&quot;&gt;interesting perspectives from reddit&lt;/a&gt; about this:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A few years ago I was at a conference ’Dev ops days’ where Microsoft had a presence. The Microsoft speakers were part of Microsoft’s Unix/Linux team’s. MS speakers talked about MS investing millions into Open source as they see it as the future. Further they discussed their teams Git commits on open source projects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;!-- --&gt;
&lt;blockquote&gt;
  &lt;p&gt;STAMINA comes to mind. A lot of “hack” code ends up on GitHub and occasionally the bits and pieces that can be turned into malware. I wouldn’t be the least bit surprised if this is so they can use code as it appears to run through this and check code on the fly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;!-- --&gt;
&lt;blockquote&gt;
  &lt;p&gt;Listen to this : https://twitter.com/i/broadcasts/1OyKAYWPRrWKb Especially the bit where their AI is able to code. Any guesses how it learnt that?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;!-- --&gt;
&lt;blockquote&gt;
  &lt;p&gt;Seems a bit of a stretch to buy GitHub to train a coding AI, MS do have access internally to several decades worth of code, and of course it’s pretty simple to scrape code for free from all manner of open source projects and Github.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;!-- --&gt;
&lt;blockquote&gt;
  &lt;p&gt;A big part of the code of the projects at Github is freely accessible. Therefore, in my opinion, Microsoft would not have had to buy Github to train the AI.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;!-- --&gt;
&lt;blockquote&gt;
  &lt;p&gt;They are developing VS Code and MS Terminal on github with the github community. They wanted to secure the platform so no one could “take” it, or make it difficult for them. (Edit: it = the platform and access to the community)&lt;/p&gt;
&lt;/blockquote&gt;
</content>
 </entry>
 
 <entry>
   <title>Intro to message flashing: A handy way to send messages across page requests in Flask</title>
   <link href="https://prahladyeri.github.io/blog/2020/07/intro-to-message-flashing.html"/>
   <updated>2020-07-08T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2020/07/intro-to-message-flashing</id>
   <content type="html">&lt;p&gt;Message flashing is a very handy technique you must be aware of if you are a Flask developer. A common recurring pattern in web development is to send messages across web requests, especially in case of redirects when the route or controller that redirected wants to display some basic text message (such as “Login Successful!”) on the target page after redirection.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/code.jpg&quot; alt=&quot;source code&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Let’s look at this simple example app which asks for a username/password, then redirects user to a special page (“/homepage”) if the validation was successful.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;flask&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Flask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url_for&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Flask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/homepage&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;homepage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;You have been logged in!&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;br&amp;gt;Welcome to home page&quot;&lt;/span&gt;
    

&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methods&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;GET&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;POST&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;POST&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;username&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;admin&apos;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;password&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;admin&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;homepage&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Incorrect username/password.&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;GET&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;
&amp;lt;!doctype html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;form method=&quot;POST&quot;&amp;gt;
        &amp;lt;input name=&apos;username&apos; placeholder=&apos;Username&apos; required&amp;gt; &amp;lt;br&amp;gt;
        &amp;lt;input type=&apos;password&apos; name=&apos;password&apos; required&amp;gt; &amp;lt;br&amp;gt;
        &amp;lt;button type=&quot;submit&quot;&amp;gt;Login&amp;lt;/button&amp;gt; &amp;lt;br&amp;gt;
    &amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&quot;&quot;&quot;&lt;/span&gt;
	
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;__main__&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, there are two basic problems here. Firstly, the home page always displays “You have been logged in!” but what if I want to display it conditionally (i.e when the user has just performed a successful login) and not each time? Secondly, there is also the case that I may want to customize this message (with a user greeting, for example).&lt;/p&gt;

&lt;p&gt;Before knowing about the message flashing technique, I used to use the cumbersome technique of storing such custom values to the session object, then reading the session object in the corresponding target route (“/homepage”). But now, I’ve found a much simpler and easier way which is message flashing. All you have to do is add an import to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flask.flash&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flask.get_flashed_messages&lt;/code&gt; class methods in your code and call it just before a redirection:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;flask&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flash&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;flash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;You have been logged in!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;homepage&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can also add a second parameter to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flash&lt;/code&gt; call which is the category it is meant for (such as error, info, etc.). Once you flash a message, all you must do on the target route (“/homepage”) is fetch the last flashed message like this:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_flashed_messages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;br&amp;gt;Welcome to home page&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course, if you happen to use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jinja2&lt;/code&gt; template for displaying output then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_flashed_messages()&lt;/code&gt; is &lt;a href=&quot;https://flask.palletsprojects.com/en/0.12.x/patterns/flashing/&quot;&gt;directly callable in that too&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_flashed_messages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with_categories&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flashes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;li&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ category }}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;li&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endfor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ul&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endif&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endwith&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

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

&lt;p&gt;After playing with message flashing for some time, I came to know that it also uses the session object internally. For example, it won’t work if you omitted the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app.secret_key&lt;/code&gt; attribute before starting the app. As I said earlier, all of the above can be done using sessions too but message flashing is a more efficient way of doing it and requires less code. And the end, finding efficent ways of achieving things is what we programmers should be striving for, enjoy!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to use robocopy for taking regular backups on windows</title>
   <link href="https://prahladyeri.github.io/blog/2020/07/how-to-use-robocopy-windows.html"/>
   <updated>2020-07-08T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2020/07/how-to-use-robocopy-windows</id>
   <content type="html">&lt;p&gt;Having switched from linux to windows recently, I was looking for an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rsync&lt;/code&gt; alternative. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rsync&lt;/code&gt; is a popular tool in the linux world used for taking routine backups and also general file transfer. But since I use it mostly for backups and I’m mostly a command line dude, I was trying to find a windows equivalent which can do the same. That’s when I came across the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;robocopy&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/robot.jpg&quot; alt=&quot;robot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Despite its trivially sounding name, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;robocopy.exe&lt;/code&gt; is a versatile tool found in almost all windows installations at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\windows\system32&lt;/code&gt; and does a great job of taking incremental file backups. The best way to configure a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;robocopy&lt;/code&gt; backup is to create a simple batch file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backup.bat&lt;/code&gt; such as the one below:&lt;/p&gt;

&lt;div class=&quot;language-bat highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@echo &lt;span class=&quot;na&quot;&gt;off&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;exist&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;e:\source\&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;backup drive not found. make sure e:\ corresponds to backup usb drive&quot;&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;goto&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;:halt&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;Welcome&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;!&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;***&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;robocopy&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;c&lt;/span&gt;:\source &lt;span class=&quot;kd&quot;&gt;e&lt;/span&gt;:\source &lt;span class=&quot;na&quot;&gt;/E /PURGE /ndl /Z
&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;:halt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This example assumes that you have a directory located at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;c:\source&lt;/code&gt; and you want to backup that entire directory to another drive (for example, a USB pen-drive) at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;e:\source&lt;/code&gt;. If you have more directories to backup, you can add them before the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:halt&lt;/code&gt; statement like this:&lt;/p&gt;

&lt;div class=&quot;language-bat highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;robocopy&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;c&lt;/span&gt;:\docs &lt;span class=&quot;kd&quot;&gt;e&lt;/span&gt;:\docs &lt;span class=&quot;na&quot;&gt;/E /PURGE /ndl /Z
&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;robocopy&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;c&lt;/span&gt;:\music &lt;span class=&quot;kd&quot;&gt;e&lt;/span&gt;:\music &lt;span class=&quot;na&quot;&gt;/E /PURGE /ndl /Z
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, let’s look at what those parameters to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;robocopy&lt;/code&gt; command do. The source and destination folders are obvious, here is what follows:&lt;/p&gt;

&lt;div class=&quot;language-bat highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;/E		&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;Copy&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;subdirectories&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;similar&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;--recursive &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;rsync&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;/PURGE	&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;Delete&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;destination&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;files&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;that&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;no&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;longer&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;exist&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;--delete &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;rsync&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;option&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;/ndl	&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;Don&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;t log directory names while copying
/Z		Copy files in restartable mode
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;robocopy /?&lt;/code&gt; to see a full list of options available in this tool. If you want to schedule backups (like once a week or month), simply go to Task Scheduler from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Control Panel -&amp;gt; Administrative Tools&lt;/code&gt; and schedule the above script to run accordingly.&lt;/p&gt;

&lt;p&gt;Enjoy, your DIY backup solution for Windows is ready!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A crash course in python packaging</title>
   <link href="https://prahladyeri.github.io/blog/2020/07/a-crash-course-in-python-packaging.html"/>
   <updated>2020-07-02T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2020/07/a-crash-course-in-python-packaging</id>
   <content type="html">&lt;p&gt;This guide isn’t for the newbie who is just learning python programming (they are better off doing a “proper” reading of the &lt;a href=&quot;https://packaging.python.org/&quot;&gt;official docs&lt;/a&gt; instead). This is for the seasoned coder who dabbles in multiple technologies and needs a quick refresher on how to go about building a neat zip or tar.gz package from their existing python code, and optionally upload it to python package index (pypi).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/toolbox.jpg&quot; alt=&quot;toolbox&quot; /&gt;&lt;/p&gt;

&lt;p&gt;First of all, its necessary to encapsulate your individual python scripts into a python &lt;strong&gt;package&lt;/strong&gt;. Individual *.py scripts can’t be packaged on their own, you must add them to a directory which will represent your package. For example, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foobar&lt;/code&gt; package directory is as follows:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...foobar\
	...main.py
	...util.py
	...__init__.py
	...etc.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__init__.py&lt;/code&gt; is needed so that python treats your directory as a package. It can be either left totally blank or consist of helpful imports if you need them. The rest of your code also needs to be adjusted as every module is contained in a package now. So for example, the following main module won’t work because the imported module (util) resides in a package now.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import util

if __name__ == &quot;__main__&quot;:
	print(util.print_line())
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This code must be re-factored so that it imports the util module from the foobar package instead:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;from foobar import util
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course, this must be done in each module. When your package gets installed on the user’s machine, the scripts will be inside a directory such as following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C:\Users\&amp;lt;username&amp;gt;\AppData\Local\Programs\Python\Python36-32\Lib\site-packages\foobar\
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And the user may be running the scripts from any random directory such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\source&lt;/code&gt; and there won’t be a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;util.py&lt;/code&gt; on their current path obviously! Hence, the module must be referred to by its global package naming convention. Since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foobar&lt;/code&gt; package will be installed on the user’s machine, the above directory will be recognized by the python interpreter as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foobar&lt;/code&gt; package directory where it will find &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;util&lt;/code&gt; and other installed modules.&lt;/p&gt;

&lt;p&gt;Once you do that, you must create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup.py&lt;/code&gt; in the root directory of your source, so your directory structure becomes like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;setup.py
...foobar\
	...main.py
	...util.py
	...__init__.py
	...etc.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you manage multiple projects in your source directory, you can put this whole bunch of files in a “foobar_project” or similarly named directory for organization.&lt;/p&gt;

&lt;p&gt;You may add many other files at the root level such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MAINFEST.in&lt;/code&gt; which is only needed in case you want to add configuration or other files from different locations to your setup file. There could be other files too such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;README.md&lt;/code&gt; for your project’s description and your project’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LICENSE&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;The contents of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup.py&lt;/code&gt; should be as follows:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;foobar&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;setuptools&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;find_packages&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foobar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;license&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MIT&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Dummy package called foobar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;long_description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Dummy package called foobar, some longer description.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;long_description_content_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;text/markdown&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;https://github.com/prahladyeri/%s&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pkg_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;packages&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_packages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;include_package_data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;entry_points&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;s&quot;&gt;&quot;console_scripts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
			&lt;span class=&quot;s&quot;&gt;&quot;foobar = foobar.main:main&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;install_requires&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;requests&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;colorama&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;python_requires&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&amp;gt;= 3.4&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Prahlad Yeri&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;author_email&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;prahladyeri@yahoo.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;classifiers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
		&lt;span class=&quot;s&quot;&gt;&quot;Programming Language :: Python :: 3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;s&quot;&gt;&quot;License :: OSI Approved :: MIT License&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;s&quot;&gt;&quot;Operating System :: OS Independent&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup&lt;/code&gt; function takes some arguments which is basically the information needed to build and install your package. The most important argument is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt;, this is how your package will be identified by the pip installer and the python packaging index (pypi). For things like version number, license, description, etc., its a better idea to define those variables in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__init__.py&lt;/code&gt; itself, so that you can directly import them like this instead of hard coding:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;from foobar import __version__, __description__, __license__
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;An important parameter here is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;entry_points&lt;/code&gt;. You must define the command line script name here which your user will use to run your script. If its going to be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foobar&lt;/code&gt;, you must also specify which module’s function must be called as entry point. In this case, when user runs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foobar&lt;/code&gt;, the package foobar’s main module will be called and from that, the main() function will run:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	entry_points={
		&quot;console_scripts&quot;: [
			&quot;foobar = foobar.main:main&quot;,
		],
	},
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course, you must refactor your code so that it cannot simply start running from “&lt;strong&gt;main&lt;/strong&gt;” check but must be placed in a separate main function:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;def main():
	pass # &amp;lt;your code goes here
	
if __name__ == &quot;__main__&quot;:
	main()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can also pass command arguments arguments through this arrangement but its better to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;argparse&lt;/code&gt; module for that instead of parsing the CLI arguments manually. Once you’ve done this wiring of your code properly as described, you then have to just run the following command to test your build by going to the package root directory (where setup.py is located):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;python setup.py sdist
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sdist&lt;/code&gt; argument creates a source distribution (typically tar.gz file) under the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dist&lt;/code&gt; subdirectory. It will be of the format:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;foobar-&amp;lt;version&amp;gt;.tar.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, &lt;version&gt; is whatever you provide in the setup argument. You can test the installation of this build by running:&lt;/version&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip install dist\foobar-&amp;lt;version&amp;gt;.tar.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This must install your package on the local machine, it will have the same effect as if your package was installed by pip itself:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip install foobar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course, you cannot run the above command until you upload your foobar package to the python package index (pypi). If you don’t want to do that and just distribute zipped installation files to your users/clients, what you’ve learned until now should suffice. But if you want to upload your own package to pypi, then you must install a dependency library called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;twine&lt;/code&gt; for that:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip install twine
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now you are ready to upload your own package to python package index (pypi). For that, you must first register an account at &lt;a href=&quot;https://pypi.org&quot;&gt;python package index (pypi)&lt;/a&gt;. After that, its as easy as running the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;twine upload&lt;/code&gt; command!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;twine upload dist\foobar-&amp;lt;version&amp;gt;.tar.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The above command asks your pypi login credentials you just registered and then upload your package. Within a few minutes, your package will be available for the whole world to install through pip!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip install foobar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Seamlessly migrate disqus comments to WordPress: a step-by-step guide</title>
   <link href="https://prahladyeri.github.io/blog/2020/03/comments-migration-from-disqus-to-wordpress.html"/>
   <updated>2020-03-11T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2020/03/comments-migration-from-disqus-to-wordpress</id>
   <content type="html">&lt;p&gt;If you’re switching from a platform using Disqus to WordPress, migrating your comments can seem daunting. Disqus was once a popular choice for blog comments, but with WordPress’s built-in commenting system and more robust plugins, many are making the switch. In this guide, I’ll walk you through how to easily migrate your Disqus comments to WordPress using updated scripts and tips to ensure a smooth transition.&lt;/p&gt;

&lt;p&gt;This is an updated follow-up to my previous guide on &lt;a href=&quot;/blog/2017/09/demystifying-comments-migration-from-disqus-to-wordpress.html&quot;&gt;Importing Disqus Comments into WordPress&lt;/a&gt;. The process remains largely the same, but I’ve made tweaks and improvements to make it easier for first-time users. Let’s dive in!&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;why-migrate-from-disqus-to-wordpress&quot;&gt;Why Migrate from Disqus to WordPress?&lt;/h3&gt;

&lt;p&gt;Disqus was once a go-to for comment management, but its reliance on third-party services and recent issues like increased ads and slow performance have prompted many bloggers to look for alternatives. Migrating to WordPress’s native system means faster load times, more control over your data, and fewer dependencies on external services.&lt;/p&gt;

&lt;p&gt;If you’re ready to switch, follow this guide to migrate your comments efficiently.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;step-by-step-migrating-disqus-comments-to-wordpress&quot;&gt;Step-by-Step: Migrating Disqus Comments to WordPress&lt;/h3&gt;

&lt;p&gt;I’ve updated the migration scripts to reflect changes in both WordPress and Disqus. Below is a breakdown of the process, as well as the script updates that make it easier.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;1-adjusting-the-url-matching-logic&quot;&gt;1. Adjusting the URL Matching Logic&lt;/h4&gt;

&lt;p&gt;The previous migration process required exact URL matches between your Disqus and WordPress posts, which could cause issues. I’ve now optimized the script to compare only the URL paths (ignoring domains) using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parse_url&lt;/code&gt; function:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$cpath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;parse_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;PHP_URL_PATH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$tpath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;parse_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$t_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;PHP_URL_PATH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$cpath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$tpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;mf&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This change makes it easier to match URLs across different domains, streamlining the migration process.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/code-php.jpeg&quot; alt=&quot;php code&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;2-handling-comment-flooding-errors&quot;&gt;2. Handling Comment Flooding Errors&lt;/h4&gt;

&lt;p&gt;WordPress sometimes throws a &lt;em&gt;“You are posting comments too quickly”&lt;/em&gt; error when bulk importing comments. To mitigate this, I’ve added a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$sleep_interval&lt;/code&gt; parameter (default value is 2 seconds) to slow down the import:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$sleep_interval&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you encounter this error, you can increase the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$sleep_interval&lt;/code&gt; or temporarily disable the comment flood protection by adding the following line to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;functions.php&lt;/code&gt; file (remember to revert it after the migration):&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;add_filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;comment_flood_filter&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;__return_false&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;3-parsing-comment-author-names&quot;&gt;3. Parsing Comment Author Names&lt;/h4&gt;

&lt;p&gt;Sometimes the Disqus comment author’s name is stored in an array. The updated script accounts for this with a simple check:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;is_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$comment_author&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$comment_author&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This prevents issues when migrating comments from authors with complex names.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;4-passing-the-avoid-die-parameter-to-wp_new_comment&quot;&gt;4. Passing the Avoid Die Parameter to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wp_new_comment&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;In some cases, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wp_new_comment()&lt;/code&gt; function would stop execution if there were issues with a comment. By passing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$avoid_die&lt;/code&gt; parameter as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;, we ensure the script continues to run smoothly:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;wp_new_comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$comment_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This change helps prevent the script from stopping prematurely.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;updated-migration-scripts&quot;&gt;Updated Migration Scripts&lt;/h3&gt;

&lt;p&gt;The latest versions of the scripts can be found below. These updates ensure smoother migrations with fewer errors and better compatibility with different WordPress setups.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://gist.github.com/prahladyeri/e22e4e232416ff841be670601b396c62&quot;&gt;console.php&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://gist.github.com/prahladyeri/d1e19d8a6d0c7ff23fe3e15f9050b6d3&quot;&gt;disqus_parse.php&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Place these files inside a plugin directory such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/wp-content/plugins/your-plugin/&lt;/code&gt; and run them from there to begin the migration process.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h3&gt;

&lt;p&gt;Migrating comments from Disqus to WordPress might seem complex, but with these updated scripts and adjustments, you’ll have your comments fully migrated in no time. WordPress offers better flexibility, performance, and security when compared to third-party comment systems, and this switch is a great step toward streamlining your blog management.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Why you should never use the dict.get() method in python</title>
   <link href="https://prahladyeri.github.io/blog/2019/11/why-you-shouldnt-use-dict-get-method.html"/>
   <updated>2019-11-28T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2019/11/why-you-shouldnt-use-dict-get-method</id>
   <content type="html">&lt;p&gt;There is an increasing trend towards using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dict.get()&lt;/code&gt; method in Python, it’s very widely used in many tutorials and examples too. Consider the below example for instance:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;a&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;a&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This simple dictionary dd has only one key (‘a’) and its corresponding value is 1. The proper way of accessing this key is to refer it as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dd[&apos;a&apos;]&lt;/code&gt; but you need to be sure that the dictionary has that key otherwise it’ll throw an error! Hence, as a shortcut way, programmers have started using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dd.get()&lt;/code&gt; method instead. This method simply returns the key value if it’s present and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;None&lt;/code&gt; if there is no such key in the dictionary.&lt;/p&gt;

&lt;p&gt;Seems good enough, right? Well, don’t fall into that trap so soon! In the real world, you’ll come across a situation sooner or later when you’ll have to handle a dictionary key who’s value is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;None&lt;/code&gt;! What will you do then? Consider this example:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;a&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;a&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In this case, you’ll never know whether a key was present in the dictionary or one was there and its value was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;None&lt;/code&gt;. Knowing the difference could be crucial in some situations and not doing so might cause subtle bugs in your program which will be very hard to trace later. The best habit to access a dictionary is this:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;a&apos;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;a&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# do whatever
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# handle this
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or you can even simply refer its key directly if you are sure that the key exists:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;a&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# do whatever
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Similarly, there are two different ways to remove an item from dictionary but there is nothing wrong in using either of them (except of course that you need to be sure that the key is present and use an exception handler if you aren’t):&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;a&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;del&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;a&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>A complete guide to identifying your MTP device on Linux using bash</title>
   <link href="https://prahladyeri.github.io/blog/2019/10/how-to-identify-mtp-device-bash.html"/>
   <updated>2019-10-27T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2019/10/how-to-identify-mtp-device-bash</id>
   <content type="html">&lt;p&gt;In the good old days, USB file-transfer between your laptop and smart-phone used to happen through the much easier “mass storage” mode which works just as if you had inserted an external pen-drive. But these days, most smart-phones and tablets have shifted to the MTP (Media Transfer Protocol).&lt;/p&gt;

&lt;p&gt;Unlike a mass storage device, the MTP device doesn’t show up in the usual &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/media/&amp;lt;user&amp;gt;/&amp;lt;device-label&amp;gt;&lt;/code&gt; folder in a linux distro which makes backup automation or other file transfer tasks more difficult as you cannot simply hard-code this path and write bash scripts based on that.&lt;/p&gt;

&lt;p&gt;This isn’t possible in case of MTP because the label is dynamically determined using two important bits of information called &lt;em&gt;BusID&lt;/em&gt; and &lt;em&gt;DeviceID&lt;/em&gt;, and the file-system path works out to be something like this:&lt;/p&gt;

&lt;p&gt;/run/user/1000/gvfs/mtp:host=%5Busb%3A&lt;strong&gt;001&lt;/strong&gt;%2C&lt;strong&gt;010&lt;/strong&gt;%5D/Internal shared storage&lt;/p&gt;

&lt;p&gt;In this example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;001&lt;/code&gt; is the BusID whereas &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;010&lt;/code&gt; is the DeviceID. You can find these two values by running the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lsusb&lt;/code&gt; command before and after the MTP device is inserted:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/lsusb1.png&quot; alt=&quot;bash lsusb&quot; /&gt;&lt;/p&gt;

&lt;p&gt;However, these two numbers (BusID and DeviceID) are dynamically determined depending on when and where you inserted the MTP device. Hence, if you are writing an automation script for backup, etc., you need to calculate it using other two static bits of information which are always constant for your device, they are called &lt;em&gt;VendorID&lt;/em&gt; and &lt;em&gt;ProductID&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In this example (for my Redmi Y2 phone), they happen to be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2717:ff40&lt;/code&gt; (displayed right next to the BusID and DeviceID in above screen). Now that you have these two static bits of information which are always going to be in a given format (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VendorID:ProductID&lt;/code&gt;), you can pass it to a bash function to determine the path dynamically:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;get_mtp_path&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;lsusb|grep &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;bus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$str&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{split($0,a,&quot; &quot;); print a[2]}&apos;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;dvc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$str&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{split($0,a,&quot; &quot;); print a[4]}&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;s/://g&apos;&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;mtp:host=%5Busb%3A&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$bus&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;%2C&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$dvc&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;%5D&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;redmi_y2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2717:ff40&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;redmi_y2_path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;get_mtp_path &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$redmi_y2&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;folder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/run/user/1000/gvfs/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$redmi_y2_path&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In above script, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$redmi_y2&lt;/code&gt; variable is hard-coded which is then passed to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_mtp_path&lt;/code&gt; bash function to determine the actual path which is then stored in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$folder&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;Now that you have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$folder&lt;/code&gt;, you can do anything with it. For example, you can use rsync to backup your smart-phone pics in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DCIM&lt;/code&gt; folder to your laptop:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rsync &lt;span class=&quot;nt&quot;&gt;-vrt&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$folder&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/Internal storage/DCIM/&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;~/Pictures/&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Alternatively, you can run a normal backup of your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Documents&lt;/code&gt; folder to your smart-phone:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rsync &lt;span class=&quot;nt&quot;&gt;-vrt&lt;/span&gt;  &lt;span class=&quot;s2&quot;&gt;&quot;~/Documents/&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$folder&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/Internal storage/Documents/&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The possibilities of this are immense!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Python recipe: Combine multiple images into one PDF</title>
   <link href="https://prahladyeri.github.io/blog/2019/10/python-recipe-combine-images-pdf.html"/>
   <updated>2019-10-24T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2019/10/python-recipe-combine-images-pdf</id>
   <content type="html">&lt;p&gt;Many a times, you come across the need to combine multiple image files (.jpg, .png, etc.) into one single portable document format (.pdf). Maybe, you have a bunch of handwritten notes which you want to organize into one file. Doing that is very trivial if you know python. To being with, you must have the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fpdf&lt;/code&gt; library installed:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;pip&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fpdf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first thing you do is create the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FPDF()&lt;/code&gt; object and set the source directory where you have the images stored. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sdir&lt;/code&gt; is that variable. You also need to set two variables to capture the height and width of the images respectively.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;PIL&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;fpdf&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FPDF&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pdf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FPDF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sdir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;imageFolder/&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then comes the looping part. This makes things easier if you have the image files named serially in numeric order (IMG001.png, IMG002.png, etc.). Otherwise, you can adapt this code to manually or sequentially process them one after another by setting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fname&lt;/code&gt; variable:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;fname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sdir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;IMG%.3d.png&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;cover&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cover&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;pdf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FPDF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;pt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fname&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;File not found:&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;processed %d&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;output.pdf&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;F&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;done&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The output of the above process will be stored in a single “output.pdf” file. So, that’s pretty nice and easy, isn’t it? Here is the entire code.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;PIL&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;fpdf&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FPDF&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pdf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FPDF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sdir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;imageFolder/&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;fname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sdir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;IMG%.3d.png&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;cover&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cover&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;pdf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FPDF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;pt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fname&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;File not found:&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;processed %d&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;output.pdf&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;F&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;done&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>How to implement URL routing in vanilla JavaScript</title>
   <link href="https://prahladyeri.github.io/blog/2019/07/url-routing-pure-js.html"/>
   <updated>2019-07-18T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2019/07/url-routing-pure-js</id>
   <content type="html">&lt;p&gt;In this post, I’ll show you how to implement dynamic URL routing in vanilla JavaScript without using any heavy frameworks like angular, vue, react, ember, etc. in less than 30 lines of code!&lt;/p&gt;

&lt;p&gt;The important attributes you need to know are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.location&lt;/code&gt; and more specifically to our purpose, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.location.hash&lt;/code&gt;. This built-in property basically tells us what page URL or route we are presently in (ex: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html#something&lt;/code&gt;). This property is blank if there is no hash URL and you are on the main URL (ex: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html#&lt;/code&gt;). In that case, you may assume a default hash such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_index&lt;/code&gt; to maintain consistency:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;displayHash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;theHash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;theHash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;theHash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_index&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;elems&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#caption&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;elems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerText&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Current Hash: &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;theHash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This simple function displays the current hash in a heading element named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#caption&lt;/code&gt;. If you want this to fire each time the hash changes (user navigated to a different page in your app), you can do that using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hashchange&lt;/code&gt; window event:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;hashchange&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;hashchange event&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;displayHash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And finally, in order to display the heading initially when the user first loads the URL in their browser, you can call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;displayHash()&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DOMContentLoaded&lt;/code&gt; event call too:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DOMContentLoaded&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DOMContentLoaded event&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;displayHash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s all folks! URL Routing is so easy to implement without using any heavy and bloated frameworks at all. The demo for this example can be found at &lt;a href=&quot;https://prahladyeri.github.io/learn-js/url-routing&quot;&gt;prahladyeri.github.io/learn-js/url-routing&lt;/a&gt;. You can see that as you click the individual hyper-links, the heading label changes to display the current hash.&lt;/p&gt;

&lt;p&gt;The complete source code for this can be found at my github repo, &lt;a href=&quot;https://github.com/prahladyeri/learn-js/blob/master/url-routing/index.html&quot;&gt;prahladyeri/learnjs&lt;/a&gt; (its less than 30 lines ;-).&lt;/p&gt;

&lt;p&gt;I came across this solution through this &lt;a href=&quot;https://stackoverflow.com/a/41426078/849365&quot;&gt;StackOverflow post by Tulio Faria&lt;/a&gt; which uses jquery but I adapted it for vanilla JavaScript.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Agile Manifesto's shortcomings: addressing modern development needs</title>
   <link href="https://prahladyeri.github.io/blog/2019/07/agile-manifesto-is-problematic.html"/>
   <updated>2019-07-02T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2019/07/agile-manifesto-is-problematic</id>
   <content type="html">&lt;p&gt;The &lt;strong&gt;Agile Manifesto&lt;/strong&gt; has been a cornerstone of software development since its creation in 2001, promoting flexibility, collaboration, and the prioritization of working software over comprehensive documentation. However, as development environments and business needs have evolved, many professionals are beginning to recognize certain limitations in the Agile approach, particularly in how it addresses modern complexities and the scale of today’s projects.&lt;/p&gt;

&lt;p&gt;While Agile undoubtedly transformed the way teams approach software development, it may not be sufficient to address the multifaceted challenges of contemporary projects. In this article, we’ll explore some of the &lt;strong&gt;shortcomings of the Agile Manifesto&lt;/strong&gt; and discuss how to balance its principles with the demands of modern development.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;1-overemphasis-on-working-software-over-documentation&quot;&gt;1. &lt;strong&gt;Overemphasis on “Working Software” Over Documentation&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;One of the most widely recognized principles of Agile is prioritizing &lt;strong&gt;working software over comprehensive documentation&lt;/strong&gt;. While this can be effective in smaller teams or early-stage startups, it becomes problematic for &lt;strong&gt;larger, more complex projects&lt;/strong&gt;. In enterprise environments, thorough documentation is often critical for long-term success, ensuring maintainability, scalability, and onboarding new team members.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Why It Matters Today&lt;/strong&gt;: In highly regulated industries like &lt;strong&gt;finance&lt;/strong&gt; or &lt;strong&gt;healthcare&lt;/strong&gt;, documentation is essential for compliance and risk management. Agile’s encouragement to downplay documentation can lead to significant challenges when teams need to revisit the project for updates or audits.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;The Balance&lt;/strong&gt;: The modern development landscape needs to strike a balance. While working software is important, &lt;strong&gt;structured documentation&lt;/strong&gt; should not be neglected. A hybrid approach, like &lt;strong&gt;Agile with documentation-driven practices&lt;/strong&gt;, ensures that the software remains maintainable without sacrificing the flexibility that Agile promotes.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;2-undervaluing-processes-and-tools&quot;&gt;2. &lt;strong&gt;Undervaluing Processes and Tools&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Agile promotes &lt;strong&gt;individuals and interactions over processes and tools&lt;/strong&gt;. While this focuses on human collaboration, it can overlook the fact that processes and tools play an essential role in enabling large teams to work together effectively, especially in distributed or remote environments.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;The Issue&lt;/strong&gt;: In today’s global development landscape, where remote work is becoming the norm, tools like &lt;strong&gt;CI/CD pipelines, project management software, and version control systems&lt;/strong&gt; are critical for collaboration. Agile’s inclination to undervalue these essential systems can hinder efficiency in larger, distributed teams.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;The Modern Need&lt;/strong&gt;: Effective processes and tools are crucial for maintaining &lt;strong&gt;consistency, quality control, and scalability&lt;/strong&gt; in large-scale software projects. Modern development demands a combination of &lt;strong&gt;flexible human interaction&lt;/strong&gt; and &lt;strong&gt;robust technical infrastructure&lt;/strong&gt; to support agile methodologies without sacrificing structure.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;3-client-collaboration-vs-contract-negotiation&quot;&gt;3. &lt;strong&gt;Client Collaboration vs. Contract Negotiation&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;The Agile Manifesto values &lt;strong&gt;customer collaboration over contract negotiation&lt;/strong&gt;, emphasizing ongoing interaction with clients. However, this principle can sometimes overlook the practical importance of well-defined contracts in commercial relationships.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Reality in Business&lt;/strong&gt;: While collaboration is crucial for understanding client needs, clear contracts set expectations, define deliverables, and manage legal and financial risks. Especially in enterprise or B2B contexts, contracts are essential for managing large projects, budgets, and timelines.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;A Balanced Approach&lt;/strong&gt;: Agile works best when there’s a strong framework in place. Instead of dismissing contracts, modern teams should look at contracts as &lt;strong&gt;living documents&lt;/strong&gt; that can adapt to changing client needs, incorporating collaboration while maintaining the security of structured agreements.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;4-the-danger-of-over-flexibility-responding-to-change-without-a-plan&quot;&gt;4. &lt;strong&gt;The Danger of Over-Flexibility: Responding to Change Without a Plan&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Agile favors &lt;strong&gt;responding to change over following a plan&lt;/strong&gt;, encouraging adaptability in the face of shifting requirements. While this flexibility is valuable, especially in fast-paced environments, it can sometimes come at the cost of &lt;strong&gt;strategic foresight&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Why Planning is Still Crucial&lt;/strong&gt;: Many large-scale projects require a long-term vision and detailed roadmaps. Over-prioritizing changes without a solid plan can lead to &lt;strong&gt;scope creep&lt;/strong&gt;, misaligned goals, and wasted resources. Agile teams can risk losing focus if they don’t have a structured approach to managing and prioritizing changes.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Finding the Middle Ground&lt;/strong&gt;: Agile teams can maintain flexibility while also following a &lt;strong&gt;high-level roadmap&lt;/strong&gt;. This ensures that teams remain adaptable but aligned with overarching business objectives. A more &lt;strong&gt;strategic Agile&lt;/strong&gt; approach can blend responsiveness with structured planning.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;5-the-limits-of-agile-in-large-scale-and-regulated-industries&quot;&gt;5. &lt;strong&gt;The Limits of Agile in Large-Scale and Regulated Industries&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Agile works exceptionally well in &lt;strong&gt;small to medium-sized teams&lt;/strong&gt; or fast-paced startup environments where rapid iterations are key. However, in &lt;strong&gt;enterprise-level projects&lt;/strong&gt; or industries with heavy regulations (e.g., &lt;strong&gt;automotive, aerospace, or government projects&lt;/strong&gt;), Agile may fall short.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Why It’s Challenging&lt;/strong&gt;: Large organizations often require &lt;strong&gt;structured project management methodologies&lt;/strong&gt; like &lt;strong&gt;Waterfall&lt;/strong&gt; or &lt;strong&gt;Scaled Agile Framework (SAFe)&lt;/strong&gt; to manage dependencies, risks, and compliance requirements. Pure Agile may lack the &lt;strong&gt;rigor&lt;/strong&gt; needed to satisfy these demands, leading to poor alignment across teams or inconsistent results.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Modern Solutions&lt;/strong&gt;: For large organizations, adopting a &lt;strong&gt;hybrid approach&lt;/strong&gt;, combining Agile principles with more &lt;strong&gt;traditional methodologies&lt;/strong&gt; like Waterfall or Lean, can better address the complexities of enterprise-level projects while maintaining agility where needed.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;conclusion-adapting-agile-to-meet-modern-needs&quot;&gt;Conclusion: Adapting Agile to Meet Modern Needs&lt;/h3&gt;

&lt;p&gt;While the Agile Manifesto has had a profound impact on software development, its principles need to be adapted to meet the demands of &lt;strong&gt;modern development environments&lt;/strong&gt;. Instead of viewing Agile as an all-or-nothing solution, teams should focus on finding a balance between &lt;strong&gt;collaboration and process, flexibility and planning&lt;/strong&gt;, and working software and documentation.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;future of software development&lt;/strong&gt; lies in adapting frameworks like Agile to align with both &lt;strong&gt;technical advancements&lt;/strong&gt; and the &lt;strong&gt;strategic needs&lt;/strong&gt; of modern organizations. By integrating &lt;strong&gt;Agile principles with scalable processes&lt;/strong&gt;, teams can ensure they meet the demands of today’s dynamic development landscape while still delivering high-quality, maintainable, and secure software.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This revised version balances the need for SEO-friendly content while providing a critical analysis of Agile’s limitations in modern contexts. It addresses &lt;strong&gt;key concerns&lt;/strong&gt; while keeping the language focused, eliminating unnecessary opinionation, and integrating actionable insights.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Introducing gh_announce: Automate github release announcements on twitter with this python bot</title>
   <link href="https://prahladyeri.github.io/blog/2019/06/announcing-gh_announce-a-python-bot-that-posts-a-tweet-each-time-you-make-a-release-on-github.html"/>
   <updated>2019-06-27T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2019/06/announcing-gh_announce-a-python-bot-that-posts-a-tweet-each-time-you-make-a-release-on-github</id>
   <content type="html">&lt;p&gt;I happen to maintain a lot of python projects on github such as &lt;a href=&quot;https://github.com/prahladyeri/distroverify&quot;&gt;distroverify&lt;/a&gt; and &lt;a href=&quot;https://github.com/prahladyeri/vtscan&quot;&gt;vtscan&lt;/a&gt;. And each time I make a tagged release on Github, I have to make a status tweet like this to let people know:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/distroverify_tweet.png&quot; alt=&quot;distroverify tweet&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So today I thought why not automate this process by writing a python bot that:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Runs as a background cron on my computer.&lt;/li&gt;
  &lt;li&gt;Keeps checking my github release tags.&lt;/li&gt;
  &lt;li&gt;Post a new tweet in the above format for me whenever there is a new release.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result of this endeavor is &lt;a href=&quot;https://github.com/prahladyeri/gh_announce&quot;&gt;gh_announce&lt;/a&gt;. I used the popular &lt;a href=&quot;https://github.com/tweepy/tweepy&quot;&gt;tweepy&lt;/a&gt; library for the twitter api and my own written package &lt;a href=&quot;https://github.com/prahladyeri/cfgsaver/&quot;&gt;cfgsaver&lt;/a&gt; to handle the configuration data such as my github username, twitter api credentials, etc.&lt;/p&gt;

&lt;p&gt;Once that is done, its only a matter of cruising through the github user events api and check whether a new tagged release has come:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;check_activity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://api.github.com/users/%s/events&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;github_username&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;acts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Zero events found, is this the correct github repo I&apos;m looking at?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Run the program again with --config parameter to set the correct values&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;act&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;act&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;CreateEvent&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#latest tag
&lt;/span&gt;			&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;act&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;payload&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;ref_type&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;tag&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
				&lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;repo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;act&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;repo&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;repo_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://github.com/&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;tag_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;ref&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
			
			&lt;span class=&quot;n&quot;&gt;dt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parse_date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;act&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;created_at&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;delta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dt&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;days&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;days&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;hrs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seconds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3600&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;mins&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seconds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;
			
			&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;days&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#this push is more than two days old, so just ignore
&lt;/span&gt;				&lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;
				
			&lt;span class=&quot;n&quot;&gt;tweet&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
			&lt;span class=&quot;c1&quot;&gt;#check local config data to know whether we&apos;ve already tweeted for this release
&lt;/span&gt;			&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;pushes&apos;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;pushes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;act&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;tweet&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;pushes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;pushes&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
				&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;act&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pushes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
					&lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#do nothing
&lt;/span&gt;				&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
					&lt;span class=&quot;n&quot;&gt;tweet&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
					&lt;span class=&quot;n&quot;&gt;pushes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;act&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tweet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
				&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
					&lt;span class=&quot;n&quot;&gt;tw_announce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repo_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
				&lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
					&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Error occurred: &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;pushes&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pushes&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;cfgsaver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pkg_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, I’ve ignored the pushes which are more than two days old as they may already have been tweeted or discussed, and can be ignored on the first run. For making the actual tweet, I use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tweepy&lt;/code&gt; library’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OAuthHandler&lt;/code&gt; for authentication, then call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api.update_status()&lt;/code&gt; method to make the actual tweet.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tw_announce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repo_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repo_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;ss&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;I have&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Full_Name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Full_Name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Full_Name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; has&quot;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;ss&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; just released version %s of %s on Github. Check it out! %s&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repo_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repo_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;#print(&quot;TWEETING: &quot; + ss)
&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;twitter_consumer_api_key&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;twitter api credentials missing&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;auth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tweepy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OAuthHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;twitter_consumer_api_key&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; 
		&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;twitter_consumer_secret&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;auth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_access_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;twitter_access_token&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;twitter_access_token_secret&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;api&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tweepy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;API&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;successfully updated status for repo: %s, tag: %s&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repo_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You need to register your own app on &lt;a href=&quot;https://developer.twitter.com/apps&quot;&gt;https://developer.twitter.com/apps&lt;/a&gt; and get these four credentials if you intend to use the tweepy library for posting tweets on your behalf:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&apos;twitter_consumer_api_key&apos;,
&apos;twitter_consumer_secret&apos;,
&apos;twitter_access_token&apos;,
&apos;twitter_access_token_secret&apos;,
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The app prompts for doing this on the first run of course. The wording of the tweet is kept in a manner that takes care of whether the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Full_Name&lt;/code&gt; configuration setting is entered. If its there, the status starts with “&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Full_Name&lt;/code&gt; has just released version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tag&lt;/code&gt; on Github…”, otherwise as “I have just released version &lt;tag&gt; on Github...&quot;. So, you can leave `Full_Name` setting blank depending on how you want the status structure to be.&lt;/tag&gt;&lt;/p&gt;

&lt;p&gt;Because I love the open source community so much, I want to just give this away to you! &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh_announce&lt;/code&gt; is open source and MIT licensed, you can install it by simply running:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip install gh_announce
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Please let me know if you like this tool, it’ll encourage me to keep writing more such tools in future!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>listdir vs scandir vs glob - The one and preferably only way to do it</title>
   <link href="https://prahladyeri.github.io/blog/2019/06/listdir-vs-scandir-vs-glob-the-one-and-preferably-only-one-obvious-way-to-do-it.html"/>
   <updated>2019-06-26T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2019/06/listdir-vs-scandir-vs-glob-the-one-and-preferably-only-one-obvious-way-to-do-it</id>
   <content type="html">&lt;p&gt;You know, sometimes when I read those python aphorisms like “beautiful is better than ugly” I wonder whether the makers were being sarcastic or real, and I’m not kidding!&lt;/p&gt;

&lt;p&gt;Its not just about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;listdir&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scandir&lt;/code&gt;, a whole lot of things are ambiguous and you’ll find a lot of different ways to do those same things thus contributing to a non-standard and messy system of working. setuptools and distutils are the obvious examples, there are so many setuptools components that still internally use the old distutils packages that its astonishing. The easy_install is yet another way to do the same packaging dance!&lt;/p&gt;

&lt;p&gt;Sometimes, a functionality in shutil is broken and you need to import the alternative from good old distutils which is truly absurd and preposterous. Most recently, I came stumbled across just this:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;distutils.dir_util&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;copy_tree&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The shutil.copy_tree is broken (it doesn’t automatically create non-existent folders and there are some other irregularities), so I had to use the copy_tree from distutils.dir_util module.&lt;/p&gt;

&lt;p&gt;Here is another great example. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;distutils.core&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkg_resources&lt;/code&gt; and there is another package called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;site&lt;/code&gt; all doing basically one and the same thing. In this instance, I had to write a local version of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip --freeze&lt;/code&gt; that checked my local setup.py and generated the requirements.txt from that instead of globally installed modules. As you can see, there are perhaps a lot of confusing ways of implementing this same thing:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!/usr/bin/env python
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;distutils.core&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pkg_resources&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;__main__&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;setup&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;distutils&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;core&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run_setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;setup.py&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;ss&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;install_requires&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
		 &lt;span class=&quot;n&quot;&gt;vv&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pkg_resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_distribution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;
		 &lt;span class=&quot;n&quot;&gt;ss&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;%s==%s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;ss&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;requirements.txt&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;w&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Written to requirements.txt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So, please tell me again, is this for real or sarcasm!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren&apos;t special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
**There should be one-- and preferably only one --obvious way to do it.**
Although that way may not be obvious at first unless you&apos;re Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it&apos;s a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let&apos;s do more of those!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>CodeMirror - A simple and efficient code editor component for your web applications</title>
   <link href="https://prahladyeri.github.io/blog/2019/06/codemirror-a-simple-and-efficient-code-editor-component-for-your-web-applications.html"/>
   <updated>2019-06-25T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2019/06/codemirror-a-simple-and-efficient-code-editor-component-for-your-web-applications</id>
   <content type="html">&lt;p&gt;I’m a huge fan of simple things in life, things that achieve a lot with minimal efforts or configuration from the user’s end. And whenever I come across such simple things, I like to share it with everyone and that’s what I’m doing now.&lt;/p&gt;

&lt;p&gt;In my recent flask based web project, one requirement was to provide a code editor in the app itself. The web app has a main system and a subsystem, and the user wanted the subsystem part to be dynamically scripted so that she can change that part of the code later and customize it herself. The web-based editor looks something like this (except that it contains the actual code instead of this Hello World placeholder):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/CodeMirror_demo.png&quot; alt=&quot;CodeMirror Demo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This was possible due to &lt;a href=&quot;https://codemirror.net/&quot;&gt;CodeMirror&lt;/a&gt; which is an open source javascript library, they have a &lt;a href=&quot;https://github.com/codemirror/CodeMirror/&quot;&gt;github repository&lt;/a&gt; too. You don’t even have to download this library, its available on CDNJS, so you can simply link the stylesheet and two scripts in your html &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;head&lt;/code&gt; tag like this:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.47.0/codemirror.min.js&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.47.0/codemirror.min.css&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.47.0/mode/python/python.min.js&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first two resources are necessities (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;codemirror.min.js&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;codemirror.min.css&lt;/code&gt;), whereas the last one for mode (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python.min.js&lt;/code&gt;) depends on which language or mode you want the editor for. In my case, it was python but there are dozens of modes available for different languages like java, php, ruby, html, css, etc.&lt;/p&gt;

&lt;p&gt;The best thing about using this component is getting started itself! All you have to do is create a html &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;textarea&lt;/code&gt; (which I already had as a dumb code editor!) and you are good to go:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ready&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
	&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;adding codeMirror object&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;myCodeMirror&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;CodeMirror&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fromTextArea&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;txtScript&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	   &lt;span class=&quot;na&quot;&gt;lineNumbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;na&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;python&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;myCodeMirror&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;change&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;editor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;//console.log(editor.getValue());	&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;myCodeMirror&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;keydown&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;editor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;//do whatever you want&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CodeMirror.fromTextArea()&lt;/code&gt; is the important method which directly converts your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;textarea&lt;/code&gt; to a code editor, so simple and so effective! But note that after that, it totally makes your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;textarea&lt;/code&gt; element vanish (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;display: none&lt;/code&gt;) and you’ll have to use the CodeMirror object variable (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.myCodeMirror&lt;/code&gt; in this example) to read or write text to it:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;myCodeMirror&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//get value from editor&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;myCodeMirror&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//set value to editor&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This component also has tons of &lt;a href=&quot;https://codemirror.net/doc/manual.html#config&quot;&gt;configuration options&lt;/a&gt; like tabSize, theme, direction (ltr/rtl), lineNumbers, etc. I hope this editor component will help you if you ever come across a web project that requires it.&lt;/p&gt;

&lt;p&gt;Happy coding, code and prosper!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Building a convention for configuration saver and reader module in Python</title>
   <link href="https://prahladyeri.github.io/blog/2019/06/building-a-convention-for-configuration-saver-and-reader-module-in-python.html"/>
   <updated>2019-06-19T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2019/06/building-a-convention-for-configuration-saver-and-reader-module-in-python</id>
   <content type="html">&lt;p&gt;I maintain several &lt;a href=&quot;https://github.com/prahladyeri&quot;&gt;python projects&lt;/a&gt; on github and some of them like &lt;a href=&quot;https://github.com/prahladyeri/vtscan&quot;&gt;VTScan&lt;/a&gt; has a need for user configuration. Now python has a plethora of &lt;a href=&quot;https://martin-thoma.com/configuration-files-in-python/&quot;&gt;ways and standards&lt;/a&gt; for &lt;em&gt;parsing&lt;/em&gt; of configuration files like json, *.ini files, etc., but there is no standard about where to save them on the user’s machine post installation.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/code-unix.jpeg&quot; alt=&quot;code-unix&quot; /&gt;&lt;/p&gt;

&lt;p&gt;One method used by many coders is to save it to the “app path” or the location where your python package itself is installed. On windows machines, this happens to be something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C:\Users\&amp;lt;username&amp;gt;\AppData\Local\Programs\Python\Python36-32\Lib\distutils\&amp;lt;package_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This approach has several problems. First, its a location for storing programs and shouldn’t be mixed with data or configuration. Secondly, if your user uninstalls your program in future or even upgrades it (which is a very common scenario), this entire location will simply vanish along with your user’s configuration data! When you upgrade a package like this, pip first removes the old package and then starts to install the new one:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip install --upgrade package_name
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The linux standard way of storing config files for users is ~/.app-name and even ~/.config/app-name where the tilde (~) represents the user’s home directory (which typically expands to /home/username/). This is the way I like the most as its readable and accessible to both apps and humans! There is also the /etc/app-name location on linux but that’s for apps installed with root privileges but as typical python devs, we don’t need to worry about that. If your app requires root, most probably you won’t be using pip/pypi in the first place but choose a proper linux packaging system like apt/dnf/pacman instead.&lt;/p&gt;

&lt;p&gt;But storing your configuration files to that location through setup.py upon installation isn’t easy! In fact, you’ll have to import the install class from setuptools.command.install and subclass it to override the post installation process if you wish to go that route!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;from setuptools.command.install import install

class PostInstallCommand(install):
	&quot;&quot;&quot;Post-installation for installation mode.&quot;&quot;&quot;
	def run(self):
		install.run(self)
		fpath = os.path.join(self.install_lib, pkg_name)
		fpath = os.path.join(fpath, &quot;cfg.json&quot;)
		cfg_dir = os.path.join(os.path.expanduser(&quot;~&quot;), &quot;.config/%s&quot; % pkg_name)
		if not os.path.isdir(cfg_dir): os.makedirs(cfg_dir)
		tpath = os.path.join(cfg_dir, &quot;cfg.json&quot;)
		shutil.move(fpath, tpath)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However, you can create certain conventions like keeping a standard name (like cfg.json) for all your configurations and storing them to a standard location like ~/.config/app-name as the above code does.&lt;/p&gt;

&lt;p&gt;Another thing to take care is that the way to include this configuration file in your setup file is to include it in MANIFEST.in and that thing doesn’t read anything from outside the app’s source directory! You can’t assign it a path like ~/.config/pkg_name/cfg.json, so you’ll have to manually copy cfg.json to your source directory in order to build your package. Then you can set it &lt;a href=&quot;https://github.com/prahladyeri/gar-cron/blob/master/MANIFEST.in&quot;&gt;like this in MANIFEST.in&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;include gar_cron/cfg.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course, I can’t automate the whole thing and turn this into a fully “plug and play” library module because things like above are app specific and each dev has to do it for her setup process specifically. But other things I can do like saving and retrieving data from this standard location:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;def save(pkg_name, cfgobject, cfgpath=None):
	if cfgpath == None:
		cfgpath = os.path.expanduser(&quot;~/.config/%s&quot; % pkg_name)
	if not os.path.isdir(cfgpath):
		os.makedirs(cfgpath)
	cfgpath = os.path.join(cfgpath,  &quot;cfg.json&quot;)
	ss = json.dumps(cfgobject)
	open(cfgpath, &apos;w&apos;).write(ss)
	return True


def get(pkg_name , cfgpath=None):
	if cfgpath == None:
		cfgpath = os.path.expanduser(&quot;~/.config/%s&quot; % pkg_name)
	if not os.path.isdir(cfgpath):
		os.makedirs(cfgpath)
		return None
	cfgpath = os.path.join(cfgpath,  &quot;cfg.json&quot;)
	if not os.path.isfile(cfgpath):
		return None
	ss = open(cfgpath).read()
	return json.loads(ss)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first function allows you to save your config object (typically a python dict) to this standard location by serializing it to json and writing it to cfg.json. You can also override the cfgpath argument to store it in a non-standard location but you shouldn’t do that unless there is any specific reason.&lt;/p&gt;

&lt;p&gt;The second function similarly fetches your config data by reading and deserializing the cfg.json file. Finally, I’ve also written a get_from_cmd() function which is helpful in getting the config directly from the user through command line by passing it a predefined list of config keys:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;def get_from_cmd(pkg_name, keys):
	print(&quot;Configuration Saver version %s\n&quot; % cfgsaver.__version__)
	obj = {}
	for key in keys:
		try: 
			obj[key] = input(&quot;Enter %s: &quot; % key)
		except KeyboardInterrupt as ex:
			return None
	save(pkg_name, obj)
	return obj
	
config_keys = [&apos;github_username&apos;, &apos;alert_email&apos;]
config = cfgsaver.get_from_cmd(pkg_name, config_keys)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you want to use this whole functionality right away, I’ve written a library called &lt;a href=&quot;https://github.com/prahladyeri/cfgsaver&quot;&gt;cfgsaver&lt;/a&gt; which you can install and get started!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip install cfgsaver
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/prahladyeri/cfgsaver&quot;&gt;project README&lt;/a&gt; also has special instructions for the specific problems - setting up your MANIFEST.in and customizing the setup.py script. You may also fork or copy this library and create your own version that adapts to your own build process.&lt;/p&gt;

&lt;p&gt;Finally, you can also keep using a non-standard method of dealing with config files but that path is riddled with agony and headaches. Besides, if every dev starts following a standard way of saving config files, the world will be so much a better place to live, both for humans and apps!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to enforce conventional commit messages using git hooks</title>
   <link href="https://prahladyeri.github.io/blog/2019/06/how-to-enforce-conventional-commit-messages-using-git-hooks.html"/>
   <updated>2019-06-14T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2019/06/how-to-enforce-conventional-commit-messages-using-git-hooks</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://www.conventionalcommits.org/en/v1.0.0-beta.4/&quot;&gt;Conventional git commit messages&lt;/a&gt; are not just nice to have but great to have. In fact, once you get to know them, you’ll start feeling that they are essential in any serious programming project. Consider the difference between following two commit messages for instance:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git commit -m &quot;added social login feature for authentication using twitter&quot;
git commit -m &quot;feat(authentication): added social login using twitter&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The second one is not only more readable but it also follows standards which makes it easier to integrate with build tools such as Travis CI and Appveyor. Not just that, you can easily automate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CHANGELOG&lt;/code&gt; generation when your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git log&lt;/code&gt; looks like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt; git log --oneline
61c8ca9 (HEAD -&amp;gt; master) fix: navbar not responsive on mobile
479c48b test: prepared test cases for user authentication
a992020 chore: moved to semantic versioning
b818120 fix: button click even handler firing twice
c6e9a97 fix: login page css
dfdc715 feat(authentication): added social login using twitter
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All in all, I like conventional commit messages so much that I don’t want to keep it optional but make it the default way of life. How do I do it? The answer is simple: &lt;em&gt;git hooks&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You don’t need to be a git ninja or expert to work with hooks. Suffice it to know that inside your special git repository folder (named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.git&lt;/code&gt;), there are some other special folders:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;logs
hooks
objects
refs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The one we are interested in is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hooks&lt;/code&gt;. To get the hang of it, create a test project on your machine and initialize an empty git repository by running:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git init .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now visit the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.git&lt;/code&gt; folder just generated and navigate to the hooks folder. There will be a bunch of scripts with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.sample&lt;/code&gt; extension (which means they are all disabled). The one we are interested in is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;commit-msg&lt;/code&gt;. This is the hook that fires just before your commit is made and thus allows you to reject the commit by throwing an error if the message isn’t in proper format.&lt;/p&gt;

&lt;p&gt;Create a new script in this directory named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;commit-msg&lt;/code&gt; and add the below code (you’ll need python installed) to it using your favorite editor (mine is notepad++!):&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!/usr/bin/env python
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;# example:
&lt;/span&gt;	&lt;span class=&quot;c1&quot;&gt;# feat(apikey): added the ability to add api key to configuration
&lt;/span&gt;	&lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;(build|ci|docs|feat|fix|perf|refactor|style|test|chore|revert)(\([\w\-]+\))?:\s.*&apos;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;ss&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;r&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;conventional commit validation failed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;__main__&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Save the script and make it executable (by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod u+x commit-msg&lt;/code&gt; on linux, not required on windows). Now head back to your source code folder where you initialized the git repo and create a source file just for testing. Then, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git add&lt;/code&gt; and try to commit just for testing using a non-conventional message. If all goes well, it should fail like this!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt; git commit -m &quot;added a new feature for xyz&quot;
Traceback (most recent call last):
  File &quot;C:/Users/prahlad/Documents/scripts/check_commit.py&quot;, line 22, in &amp;lt;module&amp;gt;
	main()
  File &quot;C:/Users/prahlad/Documents/scripts/check_commit.py&quot;, line 19, in main
	if m == None: raise Exception(&quot;conventional commit validation failed&quot;)
Exception: conventional commit validation failed	
Now test with a valid commit message and it should work!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once you practice this a few times and get a hang over it, you’ll then want to make this behavior default for whatever projects you start using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git init&lt;/code&gt; in future, isn’t it? Sure, you can do that by creating a global git commit hook template but that will be a post for another day. First things first!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For those really interested in enforcing and automating this thing, I’ve just published a python package called &lt;a href=&quot;https://github.com/prahladyeri/enforce-git-message&quot;&gt;enforce-git-message&lt;/a&gt;. All you need to do is install that python package and it’ll automatically copy the above script to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.git-templates&lt;/code&gt; directory and also set the value for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git config init.templatedir&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip install enforce-git-message
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.conventionalcommits.org/en/v1.0.0-beta.4/&quot;&gt;https://www.conventionalcommits.org/en/v1.0.0-beta.4/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/3523534/849365&quot;&gt;https://stackoverflow.com/q/3523534/849365&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/37699261/849365&quot;&gt;https://stackoverflow.com/q/37699261/849365&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://coderwall.com/p/jp7d5q/create-a-global-git-commit-hook&quot;&gt;https://coderwall.com/p/jp7d5q/create-a-global-git-commit-hook&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Package signing in PIP - It works, in a roundabout sort of way</title>
   <link href="https://prahladyeri.github.io/blog/2019/06/package-signing-in-pip-it-works-in-a-roundabout-sort-of-way.html"/>
   <updated>2019-06-10T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2019/06/package-signing-in-pip-it-works-in-a-roundabout-sort-of-way</id>
   <content type="html">&lt;p&gt;A few days ago, I made &lt;a href=&quot;https://dev.to/prahladyeri/why-doesn-t-the-python-package-manager-pip-have-package-signing-13ll&quot;&gt;this DEV.to post&lt;/a&gt; about how Python’s PIP lacks GPG package signing. Well, it turns out that I’m wrong! It does have a package signing process after all, only its one of the most manual, archaic and cumbersome security practices I’ve seen till date.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;I came to know about this when I landed on &lt;a href=&quot;https://kushaldas.in/posts/pypi-and-gpg-signed-packages.html&quot;&gt;this blog post&lt;/a&gt; by a core python developer yesterday. To test package signing in the way described, I created a test package called &lt;a href=&quot;https://github.com/prahladyeri/siterank&quot;&gt;siterank&lt;/a&gt;, a small script to fetch alexa ranking of given websites.&lt;/p&gt;

&lt;p&gt;Firstly, its essential that you use only &lt;a href=&quot;https://github.com/pypa/twine&quot;&gt;twine&lt;/a&gt; to upload a signed package to PyPi because only twine has that feature. Secondly, their documentation seems to be outdated because some arguments don’t seem to work. For example, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--sign&lt;/code&gt; argument for specifying signed files explicitly didn’t work for me:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-s, --sign            Sign files to upload using GPG
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What worked was to upload the package file and the generated signature file (*.asc) in succession like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;twine upload siterank-0.2.tar.gz siterank-0.2.tar.gz.asc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Also note that you’ll nowhere see the uploaded signature file on your &lt;a href=&quot;https://pypi.org/project/siterank/#files&quot;&gt;package page on PyPi&lt;/a&gt;. But there are two different ways to verify the signature:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Firstly, you can use the &lt;a href=&quot;https://pypi.org/pypi/siterank/json&quot;&gt;PyPi JSON API&lt;/a&gt;. It contains all the uploaded versions in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSON&lt;/code&gt; format, notice that in the second package version, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has_sig&lt;/code&gt; attribute has been set to true!&lt;/li&gt;
  &lt;li&gt;The second way is to add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.asc&lt;/code&gt; extension to the link to your setup file, in my case it is:
 https://files.pythonhosted.org/packages/16/f9/1dfce544610b9dcbbfcb4095c8e143c6cfd54b4371ccedc3f73df0a99926/siterank-0.2.tar.gz.asc&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, someone who wants to verify if this package was indeed authored by me can pull this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.asc&lt;/code&gt; file and match it with my GPG public key (ID &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;E12979BA15FDE7FD&lt;/code&gt; - which can be also found by running ` gpg –search-keys prahladyeri@yahoo.com`).&lt;/p&gt;

&lt;p&gt;This roundabout way of verification is needless to mention, tedious and cumbersome. This process should be seamless and automated, and included in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt; work-flow itself like apt and dnf have done. The only probable issue is that millions of developers upload their packages to PyPi and everyone may not want to (or lack the knowledge of) signing using GPG keys. So, signing could be kept optional (as it is now) but verification option ought to be there for signed packages as it ensures security and integrity of packages.&lt;/p&gt;

&lt;p&gt;Another issue is that of adoption. I’ve noticed from that JSON API that several popular projects like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requests&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nltk&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flask&lt;/code&gt;, etc. haven’t signed their packages at all. Its important that more and more developers push signed packages and thus contribute in making PyPi a more secure environment to install and distribute packages.&lt;/p&gt;

&lt;p&gt;Security and privacy are perhaps one of the most highly discussed topics of our times. There are attempts by all kinds of people and corporations globally to compromise these by hiding as many things as possible from the plebeians. In light of this, security and privacy should be given the highest priority in open source projects. I hope the Python project understands these concerns and does something about it.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Wordpress to Pelican in 24 hours</title>
   <link href="https://prahladyeri.github.io/blog/2019/05/wordpress-to-pelican-in-24-hours.html"/>
   <updated>2019-05-21T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2019/05/wordpress-to-pelican-in-24-hours</id>
   <content type="html">&lt;p&gt;Today, I finished migration of my blog from a self-hosted Wordpress site to a statically hosted &lt;a href=&quot;https://pages.github.com/&quot;&gt;Github Pages&lt;/a&gt; site. For the static site generator, instead of choosing Jekyll which is a hot favorite of rubyists, I went for &lt;a href=&quot;https://github.com/getpelican/pelican/&quot;&gt;Pelican&lt;/a&gt; instead as I figured my Python skills might be somewhat useful in dealing with that.&lt;/p&gt;

&lt;p&gt;Having used Jekyll earlier, I felt that Pelican is pretty much the same thing. There is a configuration file in which you define your site parameters, a folder hierarchy for defining your posts and pages, and a bunch of templates (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pelican theme&lt;/code&gt;) to make some serious customizations.&lt;/p&gt;

&lt;p&gt;In this post, I’ll briefly go through the migration process:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Run the standard Wordpress import tool and backup all your posts and comments. It’s better if you disable comments on your site (or maybe even take it down entirely for maintenance until the migration is complete).&lt;/li&gt;
  &lt;li&gt;Copy the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uploads&lt;/code&gt; folder containing your images through FTP/SFTP.&lt;/li&gt;
  &lt;li&gt;Install Python on your machine if not already installed.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Install the Pelican and Markdown packages:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; pip install pelican markdown
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Install additional packages for importing the Wordpress XML:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; pip install BeautifulSoup4 lxml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pandoc&lt;/code&gt; as it’s needed for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;markdown-xml&lt;/code&gt; conversion during the Wordpress import. Instructions for installing it for your OS can be found &lt;a href=&quot;https://pandoc.org/&quot;&gt;here&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Create a new folder (such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prahladyeri.github.io&lt;/code&gt;) for your new blog writing/publishing.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Traverse to that folder through command line and run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pelican-quickstart&lt;/code&gt;. It’ll ask some basic questions like your blog name, title, etc. You can leave most to their defaults. Once done, it’ll create a folder structure like below:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; d:\source\prahladyeri.github.io
 ├───content                                            
 │   ├───pages                                          
 │   ├───uploads                                        
 │   │   ├───2016
 pelicanconf.py
 tasks.py
 MakeFile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tasks.py&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MakeFile&lt;/code&gt; are something you can ignore unless you want to automate site generation. Now you can copy the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uploads&lt;/code&gt; folder brought from Wordpress inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt; folder (it’s the place where your posts will now reside in either markdown (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*.md&lt;/code&gt;) or reStructured Text (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*.rst&lt;/code&gt;) formats).&lt;/li&gt;
  &lt;li&gt;Copy the XML file imported from Wordpress inside the above folder.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Now simply run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pelican-import&lt;/code&gt;:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pelican-import --wpfile -m markdown &amp;lt;your-file.xml&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;The above command converts all your Wordpress posts (not comments) into markdown format and copies it to the content folder. Your old site is now imported to the pelcian system and is ready for generation!&lt;/li&gt;
  &lt;li&gt;Make some changes to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pelicanconf.py&lt;/code&gt;. Set the attributes for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AUTHOR&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SITENAME&lt;/code&gt;. You may also want to set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PAGE_URL&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PAGE_URL_SAVE_AS&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ARTICLE_URL&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ARTICLE_URL_SAVE_AS&lt;/code&gt; to match your existing site’s URL pattern.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;To generate the new site, simply run:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pelican content
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;This will generate a folder called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;output&lt;/code&gt; which you can directly serve through github pages! You can also go to the output folder and test it locally by running this command and checking in your browser:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;python -m http.server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;From now on, you can compose your new blog posts by simply creating a markdown file (in this case, it’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wordpress-pelican-24-hrs.md&lt;/code&gt;, add content to it and simply run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pelican content&lt;/code&gt; to generate the site! The markdown content should start with header attributes which will tell pelican system about the title, date, category, tags, etc. Here is a brief sample excerpt from this very blog post:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Title: Wordpress to Pelican &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;24 hours
Date: 2019-05-21 00:58
Author: Prahlad Yeri
Category: PHP
Tags: Wordpress, PHP, Python
Status: published
Cover: uploads/cover.jpg

Today, I finished migration of my blog from a self-hosted Wordpress site to a statically hosted &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;Github Pages]&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;https://pages.github.com/&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; site. For the static site generator, instead of choosing Jekyll which is a hot favorite of rubyists, I went &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;Pelican]&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;https://github.com/getpelican/pelican/&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; instead as I figured my Python skills might be somewhat useful &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;dealing with that.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is the very simplest of use cases, of course. For other advanced things like changing the theme templates and CSS, migrating to &lt;a href=&quot;https://disqus.com&quot;&gt;disqus comments&lt;/a&gt;, etc., I’ll make another post later. You may also refer the &lt;a href=&quot;https://docs.getpelican.com/&quot;&gt;official pelican docs&lt;/a&gt; which has all these details and a lot of other useful information.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Growing intolerance and lack of compassion towards new developers on StackOverflow</title>
   <link href="https://prahladyeri.github.io/blog/2019/05/growing-intolerance-and-lack-of-compassion-towards-new-developers-on-stackoverflow.html"/>
   <updated>2019-05-16T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2019/05/growing-intolerance-and-lack-of-compassion-towards-new-developers-on-stackoverflow</id>
   <content type="html">&lt;p&gt;StackOverflow is the digital equivalent of the Holy See or the Holy Mecca when it comes to programming. It is that place where developers go visit when they don’t have answers to their coding problems in a hope that the Gods of StackOverflow will help them solve it.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;But lately, the Gods of StackOverflow seem to be exhibiting a lot of intolerance and impatience towards those who are still learning the craft of programming and new to it. There seems to be a growing belief or consensus that unless you already have a certain level of expertise, you don’t belong here in our chamber.&lt;/p&gt;

&lt;p&gt;Take the example of this dude today who had &lt;a href=&quot;https://stackoverflow.com/q/56167254/849365&quot;&gt;a very simple question about jinja2 templates&lt;/a&gt;, a topic related to flask, a python web framework.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2019/05/so_closed_incorrectly-1024x458.png&quot; alt=&quot;A question about jinja2, abruptly closed as a duplicate&quot; /&gt;{.wp-image-1132 .size-large width=”1024” height=”458”}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A question about jinja2 abruptly closed as a duplicate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I was about to answer this question when it was abruptly closed for being a duplicate. The person who closed this didn’t even consider that a beginner coder (considering the reputation score) may not even know what the capabilities of jinja2 were. How the heck was s/he supposed to know that the answer to this question was to be found outside the context of jinja2? Everyone on this planet isn’t a born expert in web development. The coder expected a solution in the context of jinja2 templates itself and the right way would have been to guide him/her and explain that this isn’t possible through jinja2 but front-end scripting is required here. Then the answering post could proceed to provide a few helpful example links from StackOverflow or someplace else.&lt;/p&gt;

&lt;p&gt;I want to ask those intolerant ones: consider that you yourself could have needed this hand-holding and guidance when you were beginning to code some years earlier? Just because coding has become so easy and second nature to you now, it doesn’t give you any right to behave with arrogance. Knowledge needs to be used with grace and caution, with power comes the responsibility of using that power.&lt;/p&gt;

&lt;p&gt;This kind of behavior is quite common from what I’ve been reading lately on social media too and my observation today just confirms it. I hope that more and more senior developers develop compassion towards others and treat this site as the temple of knowledge which it really is, not an egotistical chamber to shut down other programmers.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A dark day for consumer rights: Right to repair bill killed in Canada thanks to corporate lobbying</title>
   <link href="https://prahladyeri.github.io/blog/2019/05/a-dark-day-for-consumer-rights-right-to-repair-bill-killed-in-canada-thanks-to-corporate-lobbying.html"/>
   <updated>2019-05-03T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2019/05/a-dark-day-for-consumer-rights-right-to-repair-bill-killed-in-canada-thanks-to-corporate-lobbying</id>
   <content type="html">&lt;p&gt;When Linus Torvalds wrote the &lt;a href=&quot;https://en.wikipedia.org/wiki/Linux&quot;&gt;Linux Kernel&lt;/a&gt; in 1991 or Ian Murdock first released the &lt;a href=&quot;https://en.wikipedia.org/wiki/Debian&quot;&gt;Debian OS&lt;/a&gt; in 1993, talking about the “openness” of a PC or the “right to repair it” would have been a laughable affair! 8086 was an open standard then (just as standards should be) and anybody was free to write an operating system for it (such as Linux), or user land utilities (such as GNU) or even define a desktop standard (such as Xorg).&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/07/pexels-photo-261577-300x225.jpeg&quot; alt=&quot;random image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But in 2019, that’s no longer the case with standards. Looks like humanity is going backward in terms of progress as standards are becoming more and more closed and proprietary instead of becoming open and transparent. Companies like Apple are successful in not only creating entire dependencies of such closed standards but are fighting tooth and nail so that their closed standards get a stamp from the legislature too.&lt;/p&gt;

&lt;p&gt;The law makers of Ontario, Canada succumbed to this massive corporate lobbying today and &lt;a href=&quot;https://motherboard.vice.com/en_us/article/9kxayy/right-to-repair-bill-killed-after-big-tech-lobbying-in-ontario&quot;&gt;killed the “right to repair” bill before it could even see the lights of the day&lt;/a&gt;. This bill was asking for a very basic thing - the right to open your own device (which you’ve already paid for) and fix it for any problems. In order to empower a user to do this, the bill called for forcing tech companies like Apple to provide the following at a fair price:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Repair Manual&lt;/li&gt;
  &lt;li&gt;Official Parts&lt;/li&gt;
  &lt;li&gt;Diagnostic Tools&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is something very basic, something the Stallmans and Torvalds of the world have already been doing since 90s. Canada sent a very wrong signal to the tech world today, this would have been the first such pro-consumer law in the world, if passed. &lt;a href=&quot;https://motherboard.vice.com/en_us/article/evex8z/right-to-repair-advocates-are-hosting-youtube-town-halls-to-show-you-how-to-get-involved-in-the-movement&quot;&gt;About 20 American states are also considering a similar right to repair bill&lt;/a&gt; and this sets a very wrong precedent to those decision makers. Both USA and Canada are highly developed countries and the whole world looks up to them for framing their own tech laws, so this is a very bad thing that happened today. I hope the American states will act sanely and pass this bill otherwise any hope left for open standards and protocols for the future generations will be lost. Since innovation blossoms in openness and gets stifled in closed environments, this will sound a death knell to innovation too.&lt;/p&gt;

&lt;p&gt;It is now left to organizations like &lt;a href=&quot;https://www.eff.org/&quot;&gt;EFF&lt;/a&gt; and internet freedom activists like &lt;a href=&quot;https://stallman.org/&quot;&gt;Richard Stallman&lt;/a&gt; to protest this decision and ensure some sanity in the tech world which is getting increasingly crony capitalistic.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Foxy firefighters and the clash between individual and collective liberalism</title>
   <link href="https://prahladyeri.github.io/blog/2019/02/foxy-firefighters-and-the-clash-between-individual-and-collective-liberalism.html"/>
   <updated>2019-02-02T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2019/02/foxy-firefighters-and-the-clash-between-individual-and-collective-liberalism</id>
   <content type="html">&lt;p&gt;The biggest speculation in the tech industry these days concern net neutrality and the last week was no different. Whilst I’m happy that TRAI is clear on upholding the net neutrality in my own country, India, the clouds of doom could be clearly seen in USA as the FCC is trying hard to banish net neutrality and bring control of the Internet in the hands of a few large companies.&lt;/p&gt;

&lt;p&gt;This is quite worrying for Internet in general because the world looks up to the United States for laws governing the digital space, not India. What’s hilarious is that the FCC is resorting to unbelievable arguments like &lt;a href=&quot;https://arstechnica.com/tech-policy/2019/02/throttling-of-firefighters-hurts-fcc-case-as-it-defends-net-neutrality-repeal/&quot;&gt;broadband isn’t a telecommunication service&lt;/a&gt; in their desperate attempt to repeal net neutrality. I hope the US legal system is smart enough to discard such illogical arguments and the light of justice prevails in the country where Internet was first invented.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2019/02/laptop-1478822_1920-300x200.jpg&quot; alt=&quot;random laptop image&quot; /&gt;{.size-medium .wp-image-1087 .aligncenter width=”300” height=”200”}&lt;/p&gt;

&lt;p&gt;The positive thing though is that those fighting on the side of net neutrality are also strong in numbers and include &lt;a href=&quot;https://9to5mac.com/2019/02/01/save-net-neutrality/&quot;&gt;companies like Reddit, Mozilla, Vimeo, etc. along with 22 state attorneys&lt;/a&gt;. Also, unbelievably, in the middle of this protest against repeal of net neutrality are a &lt;a href=&quot;https://abcnews.go.com/beta-story-container/Business/firefighters-join-mozilla-net-neutrality-fight-citing-public/story?id=60750432&quot;&gt;bunch of fire fighters&lt;/a&gt; who also want net neutrality to prevail! Fire fighters of California are citing public safety as a reason to uphold net neutrality and logically so. According to the linked ABC news report, California witnessed the largest fire in its entire history and the firefighters encountered an unexpected danger - they couldn’t communicate with their team members as Verizon was deliberately throttling and slowing down Internet speed on their phones. This is just a small sample of what could happen across the world if net neutrality was repealed for good.&lt;/p&gt;

&lt;p&gt;In other news, &lt;a href=&quot;https://www.theverge.com/2019/1/29/18202880/facebook-research-enterprise-root-certificate-onavo-techcrunch&quot;&gt;Facebook seems to be offering teenagers 20/month in exchange for their phone activity data&lt;/a&gt;. While privacy advocates are all pitchforks on this happening and calling for Facebook to be made liable, this has a very large implication on the ever ongoing debate on &lt;strong&gt;“Individual Liberty vs Collective Liberty”&lt;/strong&gt;. For me, personally, it makes sense that digital privacy is a very important thing and online companies shouldn’t be encouraged by allowing to run schemes like these when studies have shown that they already collect a whole lot of user data by default (location tracking, analytics, diagnostic data, etc.). Any other ration dude who has some knowledge of Information Technology will also think similarly, I suppose. However, if I try to advocate this knowledge to others and call for laws preventing such individual sale of private data, we are venturing into the area of collective liberalism, and here comes the danger of a clash happening between collective and individual liberalism. The thing is that those teenagers (however misguided they are), have full liberty to trade their private data with facebook or whatever social media company they want, and preventing them from doing that might be against the ethics of individual liberalism, that’s just my few cents. In all probability, this debate could continue for years to come and so will the privacy debate, hopefully a solution will be found which is favorable and acceptable to everyone.&lt;/p&gt;

&lt;p&gt;Last but not the least, there are &lt;a href=&quot;https://www.theguardian.com/technology/2019/feb/01/facebook-mental-health-study-happiness-delete-account&quot;&gt;many studies like&lt;/a&gt; these sprouting up these days claiming that deleting Facebook or Twitter accounts bring happiness in people’s lives. However, I want someone to perform another study into the mystery of why do so many people decidedly stay unhappy by constantly plugging into the social media sites!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Docker trick: running containers efficiently using a bash script</title>
   <link href="https://prahladyeri.github.io/blog/2018/09/docker-trick-running-containers-efficiently-using-a-bash-script.html"/>
   <updated>2018-09-14T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/09/docker-trick-running-containers-efficiently-using-a-bash-script</id>
   <content type="html">&lt;p&gt;One of the basic problems with running a docker image is that its too easy to spew up multiple instances or containers of the same image. Consider running the following container for instance:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run -it -e AWS_ACCESS_KEY_ID=&quot;test&quot; -e AWS_SECRET_ACCESS_KEY=&quot;test&quot; \
--net=host &quot;prahladyeri/testimage:latest&quot; /bin/bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will start a fresh container instance from image &lt;strong&gt;prahladyeri/testimage:latest&lt;/strong&gt; and you may optionally pass environment variables (using -e switch), and the /bin/bash argument to manually trigger a command inside the container instead of running the default one. However, once you exit the container, you don’t have any control over that container &lt;em&gt;instance&lt;/em&gt;. Running the same “docker run” command as above is just going to spew another container instance for the same image, while the old one still stays in memory. Repeating this process multiple times is a wastage of invaluable memory and cores which isn’t the goal of using docker at all!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/09/docker-logo-300x232.png&quot; alt=&quot;docker-logo&quot; /&gt;{.alignnone .size-medium .wp-image-1073 width=”300” height=”232”}&lt;/p&gt;

&lt;p&gt;A more efficient way of handling things is to check the existence of a container instance already running for the image, and restart that one if required:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker ps -a -f &quot;ancestor=prahladyeri/testimage:latest&quot; -q -l
f7261ea890ce
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The above command returns the id of an already running container (if any), so using that you can just restart that same container without passing all the parameters again as follows:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker start -i f7261ea890ce
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course, you can automate this whole process by writing a single bash script called &lt;em&gt;start_container:&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;imgname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;prahladyeri/testimage:latest&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;imgid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;docker ps &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ancestor=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$imgname&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$imgid&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;creating new instance for &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$imgname&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  docker run &lt;span class=&quot;nt&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;test&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;test&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--net&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;host &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$imgname&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; /bin/bash
&lt;span class=&quot;k&quot;&gt;else
  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;instance found&quot;&lt;/span&gt;
  docker start &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;docker ps &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ancestor=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$imgname&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Just replace the $&lt;em&gt;imgname&lt;/em&gt; variable with that of your own image name, and pass any arguments you want (for example, &lt;strong&gt;&lt;em&gt;-e AWS_KEY_ID&lt;/em&gt;&lt;/strong&gt;) to the container on line 7. This script will start a new container only if none are found already, otherwise, it will just restart an existing one!&lt;/p&gt;

&lt;p&gt;Hope you find this useful.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Performing a little usability tweak on the XFCE window recycler in greybird theme</title>
   <link href="https://prahladyeri.github.io/blog/2018/09/performing-a-little-usability-tweak-on-the-xfce-window-recycler-in-greybird-theme.html"/>
   <updated>2018-09-11T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/09/performing-a-little-usability-tweak-on-the-xfce-window-recycler-in-greybird-theme</id>
   <content type="html">&lt;p&gt;Xubuntu is my favorite distro, hands down and the default Greybird theme is just wonderful! There used to be a time when I liked Ubuntu-MATE too, but not so much since they took the road to GTK+3! Coming back to the topic, XFCE works great but it has a small usability issue in the default Greybird theme which annoys most power users.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;ALT+TAB is the usual way to recycle window icons on any DE, and the “tab” key determines the currently focused window. Now, one of the Greybird theme developers decided in his profound wisdom that a mouse move over the cycling dialog can cause a window focus too. Since the mouse cursor occupies a central position most of the time, it automatically causes an item in the cycling dialog to be selected which is not intended at all! While this could be a great “feature” for those users who like juggling with their mouse while pressing the ALT+TAB keys, I hardly think any power user would like to do that. For most users, this could be easily a cause of annoyance or frustration since they have to move the mouse out of the dialog’s range to ensure that it doesn’t interfere with their selection again.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/09/alt-tab-cycling.png&quot; alt=&quot;XFCE Alt+Tab Cycling Dialog&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the blue background represents the selected/active window. Now, imagine intending to select the firefox window by pressing ALT+TAB, but terminal window gets selected instead since the mouse happens to be placed there! Astonishingly, however, I couldn’t find any discussions or issues raised regarding this anywhere on the internet except &lt;a href=&quot;https://forum.xfce.org/viewtopic.php?id=9585&quot;&gt;this one thread on XFCE forums&lt;/a&gt;. After making some tweaks to their code, I was able to come up with my own version which resolves this issue:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;include &quot;/usr/share/themes/Greybird/gtk-2.0/gtkrc&quot;

style &quot;xfwm-tabwin-tweak&quot;
{
 Xfwm4TabwinWidget::border-width = 1
 Xfwm4TabwinWidget::border-alpha = 0.9
 Xfwm4TabwinWidget::icon-size = 64
 Xfwm4TabwinWidget::listview-icon-size = 16
 Xfwm4TabwinWidget::preview-size = 512
 Xfwm4TabwinWidget::alpha = 0.9 #0.8
 Xfwm4TabwinWidget::border-radius = 5 #10

bg[NORMAL] = shade (0.45, @bg_color_dark) # widget background color
 bg[ACTIVE] = shade (0.65, @selected_bg_color)# when keyboard and mouse focus on the same item
 bg[PRELIGHT] = shade (0.45, @bg_color_dark) # color of item with mouse hovering on it, which we want to make it
 bg[SELECTED] = shade (0.65, @selected_bg_color) #color of selected item using keyboard

fg[NORMAL] = shade (0.8, &quot;#fff&quot;) #shade (0.8, @base_color)
 fg[ACTIVE] = &quot;#fff&quot; #@base_color # text color of item where our mouse and keyboard meet
 fg[PRELIGHT] = shade (0.8, &quot;#fff&quot;) #shade (0.8, @base_color)
 fg[SELECTED] = &quot;#fff&quot; #@base_color

engine &quot;murrine&quot; {
 roundness = 6
 }
}

widget &quot;xfwm4-tabwin*&quot; style &quot;xfwm-tabwin-tweak&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Save the above file as ~/.gtkrc-2.0 and change your theme from Greybird to something else and back again, and this issue will be resolved.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Linux DIY: Creating a multiboot USB drive with ISO images of multiple distros</title>
   <link href="https://prahladyeri.github.io/blog/2018/09/linux-diy-creating-a-multiboot-usb-drive-with-iso-images-of-multiple-distros.html"/>
   <updated>2018-09-11T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/09/linux-diy-creating-a-multiboot-usb-drive-with-iso-images-of-multiple-distros</id>
   <content type="html">&lt;p&gt;Though I’m not a distro-hopper exactly, I keep trying live versions of popular linux distros like &lt;strong&gt;debian&lt;/strong&gt; and &lt;strong&gt;ubuntu&lt;/strong&gt; every now and then, and while I generally use the “dd” command which works right out of the box, today I thought that instead of burning a new ISO image each time, why not create a single 16GB USB stick which can boot multiple distro images!&lt;!--more--&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dd if=xubuntu-18.04.1-desktop-amd64.iso of=/dev/sdb bs=1M
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This simple dd command does the job, but you are left with a single ISO image occupying the entire disk space which is an inefficiency if you have a larger drive that can hold multiple images. So, let’s learn how to reclaim all that space by creating a single USB which can boot multiple ISO images. Though the process is a bit more involved than the dd command, the reward is obviously worth it too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step-1: Format your USB drive to FAT32, there should be a single partition on the drive with boot flag enabled.&lt;/strong&gt; - Also, label the drive as MULTIBOOT or something for recognizing it easier. Though the linked article below uses the fdisk/mkfs.vfat commands, you can even use graphical tools like &lt;strong&gt;gparted&lt;/strong&gt; to make this process simpler.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/09/gparted.png&quot; alt=&quot;Gparted&quot; /&gt;{.alignnone .size-full .wp-image-1020 width=”774” height=”520”}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step-2: Install grub2 on the newly formatted drive, create a boilerplate grub.cfg file:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Close the gparted window if its open, otherwise the drive won’t be mounted.&lt;/li&gt;
  &lt;li&gt;Type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo grub-install --force --no-floppy --boot-directory=/media/prahlad/MULTIBOOT/boot /dev/sdx&lt;/code&gt; (replacing path with yours, &lt;strong&gt;x&lt;/strong&gt; with your actual USB device)&lt;/li&gt;
  &lt;li&gt;Type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd /media/prahlad/MULTIBOOT/boot/grub&lt;/code&gt; (or whatever your path is)&lt;/li&gt;
  &lt;li&gt;Type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wget http://pendrivelinux.com/downloads/multibootlinux/grub.cfg&lt;/code&gt; (to get the grub.cfg file)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step-3: Copy whatever ISOs you want into the root folder of your drive.&lt;/strong&gt; For the purposes of this example, let’s consider latest LTS releases of Ubuntu and Xubuntu (swap with any other flavors or debian too if you prefer):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cp ubuntu-18.04.1-desktop-amd64.iso /media/prahlad/MULTIBOOT/
cp xubuntu-18.04.1-desktop-amd64.iso /media/prahlad/MULTIBOOT/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step-4: Edit the grub.cfg you just downloaded, and configure entries for your ISOs accordingly.&lt;/strong&gt; Its important that you do this carefully:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# This grub.cfg file was created by Lance http://www.pendrivelinux.com
# Suggested Entries and the suggestor, if available, will also be noted.

set timeout=10
set default=0

menuentry &quot;Ubuntu Desktop ISO&quot; {
 loopback loop /ubuntu-18.04.1-desktop-amd64.iso
 linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=/ubuntu-18.04.1-desktop-amd64.iso noeject noprompt splash --
 initrd (loop)/casper/initrd.lz
}

menuentry &quot;Xubuntu Desktop ISO&quot; {
 loopback loop /xubuntu-18.04.1-desktop-amd64.iso
 linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=/xubuntu-18.04.1-desktop-amd64.iso noeject noprompt splash --
 initrd (loop)/casper/initrd
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, I’ve changed only the menu entries to keep things simple. The arguments are also self apparent. For instance, &lt;em&gt;loopback&lt;/em&gt; and &lt;em&gt;loop&lt;/em&gt; attributes are used because we aren’t booting directly from the USB, but mounting the virtual loop device (ISO file) instead.&lt;/p&gt;

&lt;p&gt;However, note that there could be subtle differences between distros, xubuntu uses &lt;strong&gt;&lt;em&gt;/casper/initrd&lt;/em&gt;&lt;/strong&gt; instead of &lt;strong&gt;&lt;em&gt;/casper/initrd.lz&lt;/em&gt;&lt;/strong&gt;, for instance. To know the right entry for your own distro, compare the “&lt;em&gt;/boot/grub/grub.cfg” file&lt;/em&gt; of your distro ISO to get a hint about what menu entry to add. If your ISO isn’t one of the ‘buntus or a debian, then it might get trickier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step-5: Boot from your USB drive and ensure that it works.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Make sure that you boot from your newly created USB drive, not from the hard drive as usual. If everything goes right, you should be able to see a grub boot screen with the two menus for Ubuntu and Xubuntu.&lt;/p&gt;

&lt;p&gt;Once this process is successful, adding new ISOs to your drive is only too easy. Just copy the brand new ISO to your drive’s root directory and just add the necessary entry in &lt;em&gt;/boot/grub/grub.cfg&lt;/em&gt;, that’s it!&lt;/p&gt;

&lt;p&gt;Finally, you’ll have to enable legacy boot option if not already done in your bios settings, this process won’t work with EFI/UEFI systems since the drive doesn’t have an EFI partition. I might do another post about creating a similar drive for EFI/UEFI setups in future, until then you can refer to &lt;a href=&quot;https://old.reddit.com/r/linux/comments/9es7j1/linux_diy_creating_a_multiboot_usb_drive_with_iso/e5sa5d8/&quot;&gt;this reddit post&lt;/a&gt; by &lt;a href=&quot;https://old.reddit.com/user/need2burn&quot;&gt;/u/need2burn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Best of luck!&lt;/p&gt;

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

&lt;p&gt;http://www.pendrivelinux.com/boot-multiple-iso-from-usb-via-grub2-using-linux/&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Windows recipe: How to quickly track your active network connections using resmon utility</title>
   <link href="https://prahladyeri.github.io/blog/2018/08/windows-recipe-how-to-quickly-track-your-active-network-connections-using-resmon-utility.html"/>
   <updated>2018-08-28T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/08/windows-recipe-how-to-quickly-track-your-active-network-connections-using-resmon-utility</id>
   <content type="html">&lt;p&gt;Windows 10 is a great operating system when its clean and well-maintained but once you start cluttering it with more and more apps, your Internet speed typically starts to slow down and it becomes increasingly difficult to find out which background app is consuming your bandwidth.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;One way of tackling this is to harden the windows firewall by restricting outgoing TCP connections too (incoming are already restricted by default). But this is quite cumbersome in the long run as you will have to whitelist each and every executable that needs to access the internet which can be a difficult and time consuming task.&lt;/p&gt;

&lt;p&gt;Another quick way of finding out which app is consuming your most bandwidth (whether on the background or foreground) is to fire up the &lt;strong&gt;Windows Resource Monitor&lt;/strong&gt; by running &lt;strong&gt;resmon.exe&lt;/strong&gt; which is a nice Windows utility program that comes by default. By clicking on the Network tab and expanding the Network Activity pane, you’ll come to know exactly how much bandwidth each executable is eating on your system!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/08/resmon.png&quot; alt=&quot;Resource Monitor&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resource Monitor (resmon.exe) running on Windows&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once you know the culprit EXE causing the bandwidth drain, you can either uninstall that app or block it in the windows firewall. All in all, resmon.exe is a great utility in the hands of the Windows power user who wants to take control of his/her operating system. I hope you find this utility useful.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Userscript recipe: Adding auto-refresh button to gmail classic</title>
   <link href="https://prahladyeri.github.io/blog/2018/08/userscript-recipe-adding-auto-refresh-button-to-gmail-classic.html"/>
   <updated>2018-08-28T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/08/userscript-recipe-adding-auto-refresh-button-to-gmail-classic</id>
   <content type="html">&lt;p&gt;Call me old, but I much prefer the older classic GMail interface to the modern bloated one. Not only is the classic version less bandwidth consuming, but also simplistic in nature with just the buttons and tools that we need.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;I somehow don’t like it when zillions of AJAX scripts do all kinds of magic in the background of the web-page. However, I still want the GMail tab to auto-refresh itself and check new mails while I’m working in another tab in my browser. If only Google provided a way to auto-refresh in the classic interface! Since there isn’t one, I &lt;a href=&quot;https://gist.github.com/prahladyeri/b66ba873106474520577ff744aa1ed46&quot;&gt;wrote a user-script&lt;/a&gt; to add this functionality:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/08/gmail_classic_final.png&quot; alt=&quot;GMail Classic&quot; /&gt;{.size-full .wp-image-1009 width=”1171” height=”561”}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GMail Classic&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can use any Script extension for Chrome/Firefox such as &lt;strong&gt;GreaseMonkey&lt;/strong&gt; or &lt;strong&gt;TamperMonkey&lt;/strong&gt; to add this userscript to your browser. I really hope you find this userscript useful!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Beware of Uber's vomit fraud when booking a cab next time</title>
   <link href="https://prahladyeri.github.io/blog/2018/07/beware-of-ubers-vomit-fraud-when-booking-a-cab-next-time.html"/>
   <updated>2018-07-24T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/07/beware-of-ubers-vomit-fraud-when-booking-a-cab-next-time</id>
   <content type="html">&lt;p&gt;According to some recent reports by top technology blogs, Uber drivers have been reportedly engaging in an activity called “vomit fraud”. Basically, Uber (and many other cab services too) allow their drivers to collect a fine of up to $150 if their customer creates a mess in the cab (such as puking).&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;However, we aren’t talking about the occasional real puke or vomit here, drivers are reportedly exploiting this feature to fraudulently claim a “cleaning fee” even when no such thing was done by a customer.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/07/bmw-car-driving-13781-300x200.jpg&quot; alt=&quot;bmw-car-driving&quot; /&gt;{.alignnone .wp-image-994 .size-medium width=”300” height=”200”}&lt;/p&gt;

&lt;p&gt;For instance, according to &lt;a href=&quot;https://gizmodo.com/ubers-vomit-fraud-sounds-worse-than-surge-pricing-1827797566&quot;&gt;this Gizmodo report&lt;/a&gt; a Miami resident was recently fined twice fraudulently for this. And whilst you might recover this fine by engaging with their customer care and maybe exchanging several emails with them, the process is almost infinitely tedious and there isn’t a guarantee that you may get your money back.&lt;/p&gt;

&lt;p&gt;To make things worse, as per this &lt;a href=&quot;https://mashable.com/2018/07/23/uber-vomit-fraud&quot;&gt;Mashable report&lt;/a&gt;, a tech savvy driver may also photoshop a fake picture of a puke and provide it as evidence thus making your chances of recovering the fine almost zero.&lt;/p&gt;

&lt;p&gt;The practice has become so rampant that the drivers are openly admitting doing this on social media platforms and treating it casually. From the linked Mashable report:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&amp;gt;&amp;gt; &lt;em&gt;“lol vomit fraud is the oldest trick in my book,” one Redditor — with &lt;a href=&quot;https://old.reddit.com/r/lyftdrivers/comments/8cdg8k/lyft_pickup_changed_rerouting/&quot;&gt;a history&lt;/a&gt; of &lt;a href=&quot;https://old.reddit.com/r/uberdrivers/comments/8b9olz/just_announced_rate_increases_for_drivers/&quot;&gt;ride-hail related&lt;/a&gt; posts — &lt;a href=&quot;https://old.reddit.com/r/uberdrivers/comments/910kt8/its_called_vomit_fraud_and_it_could_make_your/e2v96m7/&quot;&gt;wrote in r/uberdrivers&lt;/a&gt;. “I’ve successfully done it many times when [passengers] are rude, slam my doors, or make me wait too long. Makes them learn the hard way.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I don’t know what could be the solution to this other than some basic “street-smartness” by customers and maybe the built-in review system. If drivers who indulge in such fraudulent behavior are reported frequently and called upon by customers in their reviews, that should ideally purge the system of bad drivers in the long-term.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>XFCE panel indicator plugin background fix</title>
   <link href="https://prahladyeri.github.io/blog/2018/07/xfce-panel-indicator-plugin-background-fix.html"/>
   <updated>2018-07-21T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/07/xfce-panel-indicator-plugin-background-fix</id>
   <content type="html">&lt;p&gt;The indicator plugin in XFCE panel has been a very useful but also a very controversial plugin! The reason is that its the only plugin on the XFCE panel developed in GTK3 (whereas rest of the panel is developed in GTK2) and this causes some theming issues.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Specifically, only those themes will work for the panel who’s developers have taken care to style both the GTK2 and GTK3 components, otherwise it results in an odd background color around the indicator plugin which isn’t in sync with rest of the theme:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/07/xfce-indicator-plugin-background-issue.png&quot; alt=&quot;xfce-indicator-plugin-background-issue&quot; /&gt;{.size-full .wp-image-976 width=”380” height=”130”}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XFCE indicator-plugin background issue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://bugs.launchpad.net/bugs/1408979&quot;&gt;Bug #1408979&lt;/a&gt; has been reported on launchpad for this and even askubuntu is filled with &lt;a href=&quot;https://askubuntu.com/questions/663248/xubuntu-indicator-plugin-background&quot;&gt;a bunch of questions&lt;/a&gt; pertaining to this issue. I came across this while trying to port &lt;a href=&quot;https://prahladyeri.github.io/blog/2018/07/how-to-make-your-xubuntu-desktop-look-and-behave-like-ubuntu-mate.html&quot;&gt;ambiance looks to my xubuntu desktop&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The fix is really simple if your theme already has GTK3 support (but not for the indicator plugin specifically). GTK3 support typically exists for your theme if it has &lt;strong&gt;/gtk-3.0&lt;/strong&gt; folder in its root folder. All you have to do is open the &lt;strong&gt;/&amp;lt;your_theme&amp;gt;/gtk-3.0/gtk.css&lt;/strong&gt; and add this style patch to it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/*xfce panel workaround */

.xfce4-panel {
  background-color: @dark_bg_color; 
  font: normal;
}

.xfce4-panel .button {
  background-image: none;
  background-color: @dark_bg_color;
  border-radius: 0;
}

.xfce4-panel .button:active,
.xfce4-panel .button:checked {
  background-image: none;
  background-color: shade(@dark_bg_color, 1.20);
  border: none;
}

.xfce4-panel .button:hover,
.xfce4-panel .button:active:hover,
.xfce4-panel .button:checked:hover {
  background-color: shade(@dark_bg_color, 1.20);
  border: none;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Make sure to replace the &lt;strong&gt;\@dark_bg_color&lt;/strong&gt; with whatever your theme’s background color is. After the above change, just apply your theme using the theme switcher and you will see this problem magically fixed!&lt;/p&gt;

&lt;p&gt;Now in cases where your theme doesn’t have GTK3 support, it might get tricky. I faced this same issue with the &lt;a href=&quot;https://www.xfce-look.org/p/1016446/&quot;&gt;Ambiance XFCE theme&lt;/a&gt;, for instance. What I did as a workaround was copy the &lt;strong&gt;gtk-3.0&lt;/strong&gt; folder from the official Ambiance theme from &lt;strong&gt;ubuntu-mate-themes&lt;/strong&gt; package to the linked XFCE theme’s folder and then did the above fix. Of course, I could have used the official theme itself in the first place, but it doesn’t support xfwm4.&lt;/p&gt;

&lt;p&gt;I hope the above information helps you if you were looking for a fix to this exact same issue.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to make your Xubuntu 16.04 desktop look and behave like Ubuntu-MATE</title>
   <link href="https://prahladyeri.github.io/blog/2018/07/how-to-make-your-xubuntu-desktop-look-and-behave-like-ubuntu-mate.html"/>
   <updated>2018-07-18T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/07/how-to-make-your-xubuntu-desktop-look-and-behave-like-ubuntu-mate</id>
   <content type="html">&lt;p&gt;Xubuntu is one of the most popular among the “lighter” distros as it hardly consumes 200MB when idle and even older machines are able to run with acceptable performance. Ubuntu-MATE which is based on MATE Desktop (a GNOME-2 fork) is also a similar competing distro though its debatable whether its as light as Xubuntu in terms of resource consumption.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/07/ubuntu-mate_lookalike.png&quot; alt=&quot;Xubuntu Desktop which looks like Ubuntu-MATE&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Xubuntu Desktop which looks like Ubuntu-MATE&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Making a switch from Xubuntu to Ubuntu-MATE totally makes sense if you have any particular reason for it, but I’ve observed that many people make that switch only because they like the astonishing looks of Ubuntu-MATE which are admittedly and relatively better compared to XFCE. For whatever reason (such as familiarity) if you want to just stay on Xubuntu but still want to have the “look and feel” of Ubuntu-MATE, its very easy to achieve that, you just have to follow this brief process described in this article.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step-1: Download the Ambiance theme for XFCE from &lt;a href=&quot;https://www.xfce-look.org/p/1016446/&quot;&gt;here&lt;/a&gt; and extract them to your ~/.themes/ folder.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Whilst the official Ubuntu packages &lt;strong&gt;mate-themes&lt;/strong&gt; and &lt;strong&gt;ubuntu-mate-themes&lt;/strong&gt; also come with a version of Ambiance theme, the window title background didn’t turn dark when I applied it for some reason. You can still have those packages installed if you want, but the one in your ~/.themes/ folder will override any installed themes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step-2: Apply Ambiance GTK theme.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Head over to Appearance applet in your XFCE Settings Manager and switch the GTK theme to &lt;strong&gt;Ambiance&lt;/strong&gt; which you should now see after performing step-1:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/07/SM_appearance.png&quot; alt=&quot;Settings Manager-&amp;gt;Appearance&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Settings Manager-&amp;gt;Appearance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step-3: Apply Ambiance Window Manager theme.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The next step is to do the same for Window Manager theme. Head over to Window Manager applet and make the switch there too:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/07/SM_window_manager.png&quot; alt=&quot;Settings Manager-&amp;gt;Window Manager&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Settings Manager-&amp;gt;Window Manager&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step-4: Install Ubuntu-MATE icons.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is for adding those green MATE icons. Just run this command to install the official package:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo apt install ubuntu-mate-icon-themes
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Again open the Appearance applet but this time click the &lt;strong&gt;Icons&lt;/strong&gt; tab on the top, and just select &lt;strong&gt;Ambiance-MATE&lt;/strong&gt; from there:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/07/SM_appearance_icons.png&quot; alt=&quot;Settings Manager-&amp;gt;Appearance (Icons Tab)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Settings Manager-&amp;gt;Appearance (Icons Tab)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Optionally, you can also click the &lt;strong&gt;Fonts&lt;/strong&gt; tab and change the font to Ubuntu or one of its derivatives to get that Ubuntu-MATE feel!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step-5: Use the magic switch: XFCE Panel Switch.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;XFCE Panel Switch&lt;/strong&gt; is a wonderful tool that drastically alters the layout of your XFCE Desktop, just search for “XFCE Panel Switch” in your whisker menu or find it under &lt;strong&gt;Settings&lt;/strong&gt; in your regular menu. Start that tool and you’ll be able to switch to any desired desktop layout (choose “GNOME 2” for the looks of Ubuntu-MATE and click “Apply” icon which is the first in the bottom list of small icons):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/07/xfce-panel-switch.png&quot; alt=&quot;XFCE Panel Switch&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XFCE Panel Switch&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once you do that, you should be able to get a perfect Ubuntu-MATE look alike such as the one shown on the top screen. Finally and optionally, you can also install the official package &lt;strong&gt;ubuntu-mate-wallpapers&lt;/strong&gt; if you like their wallpapers too.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Backbone.Events+Promises+async/await is a great combination for building javascript apps</title>
   <link href="https://prahladyeri.github.io/blog/2018/07/backbone-events-promises-async_await-combination.html"/>
   <updated>2018-07-12T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/07/backbone-events-promises-async_await-combination</id>
   <content type="html">&lt;p&gt;At the risk of being a contrarian, I’d like to show in this article how exactly can the Backbone’s Events model be combined with the more modern constructs of Promises and async/await to build a killer app using JavaScript.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;First, let’s try to understand why do we need these constructs for asynchronous programming when JavaScript itself is a mostly asynchronous language featuring events and function callbacks. The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise&quot;&gt;Promise API&lt;/a&gt; was introduced in ES6 standard as a way of preventing callback hell:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;function someFunction(function anotherFunction(data){
  function yetAnotherFunction() {
    function reallyCoreFunction() {
      //readability sucks now!
    }
  }
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is only three layers of callbacks, imagine what will happen when there are a hundred of them which is quite possible in a medium sized web app with multiple ajax calls and a dynamic interface. To solve this problem, functions started returning a “Promise” object instead of a callback function, so instead of multiple layers of callbacks, we can now use “function chaining” using the &lt;strong&gt;then&lt;/strong&gt; keyword like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;someFunction()
.then(function(){
  yetAnotherFunction();
})
.then(function(){
  reallyCoreFunction();
})
//readability is better now!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, the Promise API has improved the readability to a considerable extent. Instead of a callback mechanism, the callee returns an object called &lt;strong&gt;Promise&lt;/strong&gt; which can be chained for further execution. For this to happen, the callee has to call resolve() in order for the caller to end the wait and trigger further execution in the next chain:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/07/promise_model.png&quot; alt=&quot;Promise Model&quot; /&gt;{.size-full .wp-image-921 width=”755” height=”222”}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Promise Model&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But the async/await construct goes even further than this. You don’t even need to do function chaining using &lt;strong&gt;then&lt;/strong&gt;, but the &lt;strong&gt;await&lt;/strong&gt; keyword itself is enough to do this. Internally, the async/await model uses the Promise API to achieve its end because even in this model, the callee has to make the resolve() call in order to return execution control to the caller and the process continues after the next await statement:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/07/async_await_model.png&quot; alt=&quot;async await Model&quot; /&gt;{.size-full .wp-image-922 width=”755” height=”222”}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;async await Model&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;However, one disadvantage of async/await is that the caller can only wait, it cannot receive a returned value which is possible in the Promise model. Another disadvantage is that the caller function itself who invoked the await statement needs to be declared as async which means it cannot be a part of a sequential statements in another process, but has to work independently as shown &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function#Examples&quot;&gt;in this example&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;var sequentialStart = async function() {
  console.log(&apos;==SEQUENTIAL START==&apos;);
  const slow = await resolveAfter2Seconds(); // If the value of the expression following the await operator is not a Promise, it&apos;s converted to a resolved Promise.
  const fast = await resolveAfter1Second();
  console.log(slow);
  console.log(fast);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, the most important thing here is to realize that both Promises and async/await are just one level of asynchrony, let’s call this detail asynchrony or &lt;strong&gt;micro asynchrony&lt;/strong&gt;, but there is also the other kind that works at the broader level which can be termed &lt;strong&gt;macro asynchrony&lt;/strong&gt;, and this is where Backbone.Events comes into the picture.&lt;/p&gt;

&lt;p&gt;Consider that you have a complex web application with several views and each component should be able to trigger a message or event to any other component asynchronously in order for the app to render and function properly. Consider an app with a &lt;strong&gt;loginView&lt;/strong&gt; that needs to trigger an alert that a user has just signed in. Now, keeping the separation of concerns, the best practice here would be that the loginView shouldn’t try to render that part of the DOM and leave it to the other component: &lt;strong&gt;navbarView&lt;/strong&gt;. Now, how can our loginView be able to “tell” the navbarView asynchronously over the wire to display that alert? Both Promises and async/await are of no use here, and hence we need a macro-level all-purpose &lt;strong&gt;Event Bus&lt;/strong&gt;, similar to the one that Backbone.js provides us:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/07/backbone.events_model.png&quot; alt=&quot;backbone.events Model&quot; /&gt;{.size-full .wp-image-920 width=”757” height=”278”}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;backbone.events Model&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this case, our caller and callee are inside separate components and don’t even know each other. And separation of concerns imply that they cannot even contact each other directly. Now, Backbone provides this useful trigger/listen mechanism called &lt;strong&gt;Backbone.Events&lt;/strong&gt; using which any object in your app can listen to the events triggered on any other object! In this case, the loginView triggers an event called “navbarView.alert” and thus “tells” the navbarView that it has to do something (in this case show an alert). This mechanism also supports passing of any kind of data along with the triggering of event (in this case the &lt;em&gt;message&lt;/em&gt; parameter).&lt;/p&gt;

&lt;p&gt;Thus, we can see that combining the power of all these asynchronous models, we can easily handle any level of complexity in our app, provided that our &lt;a href=&quot;https://prahladyeri.github.io/blog/2018/07/the-right-way-to-architect-single-page-web-applications.html&quot;&gt;app itself is structured in the right way from the start&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>WordPress DIY: Adding syntax highlighting to your WordPress blog without using an external plugin</title>
   <link href="https://prahladyeri.github.io/blog/2018/07/wordpress-diy-adding-syntax-highlighting-to-your-wordpress-blog-without-using-an-external-plugin.html"/>
   <updated>2018-07-10T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/07/wordpress-diy-adding-syntax-highlighting-to-your-wordpress-blog-without-using-an-external-plugin</id>
   <content type="html">&lt;p&gt;Just as my other articles in &lt;a href=&quot;https://prahladyeri.github.io/blog/tag/wordpress+diy&quot;&gt;WordPress DIY&lt;/a&gt; series, this one also focuses on doing everything yourself by writing the code rather than using any external dependencies. There are two popular open source implementations of Syntax Highlighting JavaScript libraries: &lt;a href=&quot;https://github.com/google/code-prettify&quot;&gt;Google’s Prettify&lt;/a&gt; and &lt;a href=&quot;http://alexgorbatchev.com/SyntaxHighlighter/&quot;&gt;Alex Gorbatchev’s Syntax Highlighter&lt;/a&gt;, and in this article, we will use the former.&lt;!--more--&gt; Writing your own plugin for syntax highlighting is very straightforward if you know what you are doing. Just create a file named &lt;strong&gt;/wp-content/plugins/wp-prettify.php&lt;/strong&gt; in your WordPress installation and add the below code to it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;?php
/**
 * @package WP Prettify
 * @version 0.1.0
 */
/*
Plugin Name: WP Prettify
Plugin URI: http://github.com/prahladyeri/wp-prettify
Description: Wordpress implementation of Google Prettify Syntax Highlighter
Author: Prahlad Yeri
Author URI: https://prahladyeri.github.io/
Version: 0.1.0
License: GPL version 2 or later - http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*/


function add_syntax_highlighter() {
$text=&amp;lt;&amp;lt;&amp;lt;EOD
&amp;lt;script&amp;gt;
document.addEventListener(&apos;DOMContentLoaded&apos;, function(){
    var elements = document.getElementsByTagName(&apos;pre&apos;);
    for(var i=0;i&amp;lt;elements.length;i++) {
        if (elements[i].className.indexOf(&quot;prettyprint&quot;) == -1) {
            elements[i].className =   elements[i].className + &quot; prettyprint &quot;
            console.log(&apos;adding&apos;);
        }
        else {
          console.log(&apos;class already exists&apos;);
        }
    }
});
&amp;lt;/script&amp;gt;
&amp;lt;style&amp;gt;
pre.prettyprint.prettyprinted {
  border: 0px;
  padding: 5px;
  font-size: 15px;
}
&amp;lt;/style&amp;gt;

&amp;lt;script src=&quot;https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
EOD;

    echo $text;
}

add_action(&apos;wp_head&apos;, &apos;add_syntax_highlighter&apos;);
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Remember to enable the plugin by navigating to &lt;strong&gt;/wp-admin&lt;/strong&gt; before using it.&lt;/p&gt;

&lt;p&gt;This function first adds a &lt;strong&gt;prettyprint&lt;/strong&gt; class to all your &amp;lt;pre&amp;gt; tags to tell the Prettify library to highlight this block and thus making syntax highlighting painless on your part. The advantage of using Google’s solution is that it automatically detects the programming or scripting language inside the block, and you don’t have to use additional tag markup like &lt;strong&gt;“lang=php”&lt;/strong&gt; or something like that.&lt;/p&gt;

&lt;p&gt;The addition of CSS &lt;strong&gt;&amp;lt;style&amp;gt;&lt;/strong&gt; tag in the function is optional and not really needed. Its just my preference to hide the border and increase the padding and font size a bit.&lt;/p&gt;

&lt;p&gt;The demonstration of this self-written plugin is quite evident as this very site runs on this plugin and the above code block is highlighted using the Google’s Prettify library. If you can use the Chrome or Firefox Developer tools to analyze that &amp;lt;pre&amp;gt; block, it will show you that a &lt;strong&gt;prettyprint&lt;/strong&gt; class has been added!&lt;/p&gt;

&lt;p&gt;So, go ahead and write this plugin if you are building a WordPress blog for yourself and want to do this in a DIY way.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Best minimalist social networks for users tired of clutter</title>
   <link href="https://prahladyeri.github.io/blog/2018/07/alternative-reddit-like-social-networking-sites-for-the-minimalists.html"/>
   <updated>2018-07-10T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/07/alternative-reddit-like-social-networking-sites-for-the-minimalists</id>
   <content type="html">&lt;p&gt;The internet is a vast place, brimming with content that ranges from engaging and informative to downright overwhelming. For those who find comfort in clean, text-rich experiences, it can be a challenge to sift through the digital noise of flashy animations, autoplaying videos, and invasive ads. Minimalist social networks cater to users who prefer simplicity, efficiency, and thoughtful engagement over the sensory overload common to mainstream platforms. If you’re a fan of the classic Reddit or similar text-centric sites but are disillusioned with their current trends, this guide is for you. We’ll dive into the best alternatives and what makes each worth a look.&lt;/p&gt;

&lt;h3 id=&quot;1-why-minimalist-platforms-matter&quot;&gt;1. Why minimalist platforms matter&lt;/h3&gt;
&lt;p&gt;Minimalism on the web isn’t just an aesthetic choice; it’s a functional necessity for many users. Sites like Facebook, Twitter, and even new Reddit versions have increasingly incorporated dynamic elements that can hinder user experience. For those with slower internet connections or those who prioritize productivity, minimalist platforms offer:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Faster load times&lt;/strong&gt; due to reduced multimedia and animations.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Simpler user interfaces&lt;/strong&gt; that help keep the focus on content.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Improved reading and navigation&lt;/strong&gt; without visual clutter.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Reduced cognitive load&lt;/strong&gt; for more focused interactions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/alternative-reddit-like-social-networking-sites-for-the-minimalists.webp&quot; alt=&quot;alternative-reddit-like-social-networking-sites-for-the-minimalists&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;2-top-minimalist-social-networks-to-consider&quot;&gt;2. Top minimalist social networks to consider&lt;/h3&gt;

&lt;p&gt;Here’s a roundup of some of the best minimalist social networks available:&lt;/p&gt;

&lt;h4 id=&quot;hacker-news&quot;&gt;Hacker News&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Niche&lt;/strong&gt;: Technology and startups&lt;br /&gt;
&lt;strong&gt;Key Features&lt;/strong&gt;: Plain text format, high-quality discussions, strict moderation&lt;br /&gt;
&lt;strong&gt;Pros&lt;/strong&gt;: Excellent for tech enthusiasts, devoid of visual distractions.&lt;br /&gt;
&lt;strong&gt;Cons&lt;/strong&gt;: Limited to tech and startup news, may not appeal to a general audience.&lt;br /&gt;
&lt;strong&gt;Website&lt;/strong&gt;: &lt;a href=&quot;https://news.ycombinator.com&quot;&gt;Hacker News&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What sets it apart&lt;/strong&gt;: Hacker News was created by Y Combinator and has maintained its no-frills interface since inception. Its orange-and-white theme, simple text links, and threaded comments are loved by developers, entrepreneurs, and tech enthusiasts for their straightforwardness and depth.&lt;/p&gt;

&lt;h4 id=&quot;hubski&quot;&gt;Hubski&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Niche&lt;/strong&gt;: General interest, thoughtful discussions&lt;br /&gt;
&lt;strong&gt;Key Features&lt;/strong&gt;: Community-driven, tag-based content, user-focused interactions&lt;br /&gt;
&lt;strong&gt;Pros&lt;/strong&gt;: Emphasizes quality over quantity, strong community feel.&lt;br /&gt;
&lt;strong&gt;Cons&lt;/strong&gt;: Smaller user base, may feel quiet compared to larger platforms.&lt;br /&gt;
&lt;strong&gt;Website&lt;/strong&gt;: &lt;a href=&quot;https://hubski.com&quot;&gt;Hubski&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User insights&lt;/strong&gt;: Hubski’s community is known for its civility and depth. Conversations often venture into philosophy, literature, and less-mainstream topics, making it a haven for readers looking to escape low-effort posts and clickbait headlines.&lt;/p&gt;

&lt;h4 id=&quot;mastodon&quot;&gt;Mastodon&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Niche&lt;/strong&gt;: General social networking with a twist&lt;br /&gt;
&lt;strong&gt;Key Features&lt;/strong&gt;: Decentralized, federated servers, open-source&lt;br /&gt;
&lt;strong&gt;Pros&lt;/strong&gt;: No single entity controls it, greater privacy options.&lt;br /&gt;
&lt;strong&gt;Cons&lt;/strong&gt;: Finding the right server (instance) can be tricky at first.&lt;br /&gt;
&lt;strong&gt;Website&lt;/strong&gt;: &lt;a href=&quot;https://mastodon.social&quot;&gt;Mastodon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it’s unique&lt;/strong&gt;: Mastodon allows users to join different “instances” tailored to their interests. Each instance operates like its own social network but is federated with others, allowing cross-communication. This structure supports a diversity of communities without the top-down control of traditional networks.&lt;/p&gt;

&lt;h4 id=&quot;snapzu&quot;&gt;Snapzu&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Niche&lt;/strong&gt;: Broad topics, storytelling and discussions&lt;br /&gt;
&lt;strong&gt;Key Features&lt;/strong&gt;: Tribe-based topics, invite-only access&lt;br /&gt;
&lt;strong&gt;Pros&lt;/strong&gt;: Polished interface that is still minimalist, respectful discussions.&lt;br /&gt;
&lt;strong&gt;Cons&lt;/strong&gt;: Smaller community size, invite requirement.&lt;br /&gt;
&lt;strong&gt;Website&lt;/strong&gt;: &lt;a href=&quot;https://snapzu.com&quot;&gt;Snapzu&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inside Snapzu&lt;/strong&gt;: Snapzu’s “tribes” are akin to subreddits but with a cleaner, more user-friendly design. Discussions here lean toward thought-provoking and detailed, offering a breath of fresh air to those who miss old-school forums.&lt;/p&gt;

&lt;h3 id=&quot;3-comparison-table-for-minimalist-platforms&quot;&gt;3. Comparison table for minimalist platforms&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Platform&lt;/th&gt;
      &lt;th&gt;Niche&lt;/th&gt;
      &lt;th&gt;Key Features&lt;/th&gt;
      &lt;th&gt;Pros&lt;/th&gt;
      &lt;th&gt;Cons&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Hacker News&lt;/td&gt;
      &lt;td&gt;Technology/startups&lt;/td&gt;
      &lt;td&gt;Plain text, quality discussions&lt;/td&gt;
      &lt;td&gt;Fast, simple UI, high-quality content&lt;/td&gt;
      &lt;td&gt;Niche-focused&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Hubski&lt;/td&gt;
      &lt;td&gt;General interest&lt;/td&gt;
      &lt;td&gt;Tag-based, thoughtful posts&lt;/td&gt;
      &lt;td&gt;Community-driven, rich discussions&lt;/td&gt;
      &lt;td&gt;Smaller user base&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Mastodon&lt;/td&gt;
      &lt;td&gt;General social networking&lt;/td&gt;
      &lt;td&gt;Decentralized, federated&lt;/td&gt;
      &lt;td&gt;Privacy-focused, varied content&lt;/td&gt;
      &lt;td&gt;Steep learning curve for new users&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Snapzu&lt;/td&gt;
      &lt;td&gt;Broad topics&lt;/td&gt;
      &lt;td&gt;Tribe categories, minimal UI&lt;/td&gt;
      &lt;td&gt;Polished design, respectful community&lt;/td&gt;
      &lt;td&gt;Invite-only access&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Discuit.net&lt;/td&gt;
      &lt;td&gt;Community/social sharing&lt;/td&gt;
      &lt;td&gt;Markdown support, simple interface&lt;/td&gt;
      &lt;td&gt;Lightweight, ad-free, active communities&lt;/td&gt;
      &lt;td&gt;Limited user base, less feature-rich&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Peerlist.io&lt;/td&gt;
      &lt;td&gt;Tech professionals&lt;/td&gt;
      &lt;td&gt;Profiles, project showcase&lt;/td&gt;
      &lt;td&gt;Great for networking, verified credentials&lt;/td&gt;
      &lt;td&gt;Smaller network compared to LinkedIn&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Lemmy&lt;/td&gt;
      &lt;td&gt;General/community forums&lt;/td&gt;
      &lt;td&gt;Federated, decentralized&lt;/td&gt;
      &lt;td&gt;Privacy-focused, Fediverse support&lt;/td&gt;
      &lt;td&gt;Relatively new, limited user base&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;kbin&lt;/td&gt;
      &lt;td&gt;Aggregator and microblogging&lt;/td&gt;
      &lt;td&gt;Federation, ActivityPub support&lt;/td&gt;
      &lt;td&gt;Decentralized, ad-free, privacy-focused&lt;/td&gt;
      &lt;td&gt;Limited reach, niche audience&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Steemit&lt;/td&gt;
      &lt;td&gt;Content creation&lt;/td&gt;
      &lt;td&gt;Blockchain-based rewards&lt;/td&gt;
      &lt;td&gt;Incentivizes content creation&lt;/td&gt;
      &lt;td&gt;Cryptocurrency volatility&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;SaidIt.net&lt;/td&gt;
      &lt;td&gt;Social news&lt;/td&gt;
      &lt;td&gt;Voting, free speech&lt;/td&gt;
      &lt;td&gt;Privacy-focused, open community&lt;/td&gt;
      &lt;td&gt;Light moderation, niche audience&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;4-honorable-mentions&quot;&gt;4. Honorable mentions&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;4chan&lt;/strong&gt;&lt;br /&gt;
Known for its image board structure, &lt;strong&gt;4chan&lt;/strong&gt; remains an anonymous, minimalist site that caters to all types of discussions, from memes to activism. It has a reputation for being chaotic, so users should proceed with caution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Medium and Quora&lt;/strong&gt;&lt;br /&gt;
While &lt;strong&gt;Medium&lt;/strong&gt; and &lt;strong&gt;Quora&lt;/strong&gt; are not strictly minimalist, they deserve mention for their cleaner interfaces compared to mainstream social networks. Medium focuses on quality storytelling and articles, while Quora is home to user-generated questions and answers across a wide range of topics.&lt;/p&gt;

&lt;h3 id=&quot;5-why-you-should-explore-minimalist-social-networks&quot;&gt;5. Why you should explore minimalist social networks&lt;/h3&gt;

&lt;p&gt;Minimalist platforms remind us that social media doesn’t need to be a never-ending scroll of autoplay videos, pop-ups, and ads. They offer the opportunity to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Reconnect with genuine discussions and well-thought-out content.&lt;/li&gt;
  &lt;li&gt;Enjoy a seamless, fast experience without overwhelming multimedia.&lt;/li&gt;
  &lt;li&gt;Join niche communities with shared interests for meaningful interaction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a world where many platforms chase after engagement metrics, minimalist social networks stand as testaments to the power of simplicity, encouraging users to slow down, read, and think.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to create a Server-less Google Drive client using only HTML and JavaScript</title>
   <link href="https://prahladyeri.github.io/blog/2018/07/how-to-create-a-server-less-google-drive-client-using-only-htmljavascript.html"/>
   <updated>2018-07-09T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/07/how-to-create-a-server-less-google-drive-client-using-only-htmljavascript</id>
   <content type="html">&lt;p&gt;A few days ago, I had to work on a project of this kind and the information and documentation available on this topic was quite bewildering. As a result, I decided to write this article in order to make everything available at one place.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Almost everyone knows about the &lt;a href=&quot;https://github.com/google/google-api-python-client&quot;&gt;Google API Python Client&lt;/a&gt;(and similar others in Java, PHP, etc.) which can be accessed from the server, but few are aware that Google even supports a totally server-less implementation of a client using only client-side technology (HTML/JavaScript). The only caveat here is that your app won’t get a permanent access token like the server side apps do, but instead, has a temporary access until your web page isn’t closed. Here is how to go about doing it:&lt;/p&gt;

&lt;h2 id=&quot;step-1-register-your-app-by-visiting-the-google-cloud-console&quot;&gt;Step-1: Register your App by visiting the Google Cloud Console:&lt;/h2&gt;

&lt;p&gt;As usual, visit the &lt;a href=&quot;https://console.cloud.google.com/&quot;&gt;Google Cloud Console&lt;/a&gt;and register your app first, enable the Google Drive API by visiting “APIs &amp;amp; Services”=&amp;gt;“Dashboard”, then click on “Credentials” to create an API Key, an OAuth credential and setup the authentication screen for your user. While setting credentials, remember to add your app’s URL in authorized Redirect-URIs section. If you have multiple URLs for your app (like localhost for testing, www.YourSite.com for production, etc., then add them all to the list):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/07/credentials_config_redirect_uri.png&quot; alt=&quot;Credentials Configuration Redirect URIs&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Credentials Configuration: Redirect URIs&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;step-2-setup-the-oauth-consent-screen&quot;&gt;Step-2: Setup the OAuth consent screen.&lt;/h2&gt;

&lt;p&gt;This is what your user will see when they visit your app and it redirects them to Google for getting permissions for Drive access. Click on the “OAuth consent screen” tab and configure it with your app’s name, logo, etc.&lt;/p&gt;

&lt;h2 id=&quot;step-3-add-script-references&quot;&gt;Step-3: Add script references.&lt;/h2&gt;

&lt;p&gt;After you create the credentials and get your ClientID, API Key and secret key (not useful for our app, really), the next step is to start building the app. I usually prefer to keep the JavaScript logic in a separate file such as app.js instead of keeping it in the main index.html. Just add a script tag in your main index.html and add a reference to this app.js in which you’ll write the Google Drive logic:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;/static/app.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve kept the app.js in a sub-folder named /static, if yours is different, then adjust the path accordingly. You’ll also need to add a reference to Google authentication library in order to perform the OAuth authentication:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;script async defer src=&quot;https://apis.google.com/js/api.js&quot; 
 onload=&quot;this.onload=function(){};handleClientLoad()&quot; 
 onreadystatechange=&quot;if (this.readyState === &apos;complete&apos;) this.onload()&quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, also add a reference to jQuery if you want to use it. In most apps, its usually a must!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;step-4-build-the-app&quot;&gt;Step-4: Build the app.&lt;/h2&gt;

&lt;p&gt;Now, in your app.js, first add global variables that you’ll need throughout your app:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;var GoogleAuth;
var SCOPE = &apos;https://www.googleapis.com/auth/drive&apos;; //.metadata.readonly
SCOPE += &quot; https://www.googleapis.com/auth/drive.install&quot;;
SCOPE += &quot; https://www.googleapis.com/auth/drive.file&quot;;

var files = []; //array to store the list of files in user&apos;s drive.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Add the scopes as per your requirements, I had included &lt;strong&gt;auth/drive.install&lt;/strong&gt; and &lt;strong&gt;auth/drive.file&lt;/strong&gt; only because I had to provide an “open with this app” feature in the user’s own google drive interface so that they may visit our app and run their files through it, and thus use it as a “file opener”. If you don’t need to provide such extended features and just want full access to the user’s files, then the first one (&lt;strong&gt;auth/drive&lt;/strong&gt;) is sufficient.&lt;/p&gt;

&lt;p&gt;After that, write the entry point for our app in app.js. As per the script attribute, you’ll be redirected here by the Google authentication library:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/**
* This is the entry-point that gapi calls.
* 
* */
function handleClientLoad() {
 // Load the API&apos;s client and auth2 modules.
 // Call the initClient function after the modules load.
 gapi.load(&apos;client:auth2&apos;, initClient);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;gapi.load()&lt;/strong&gt; loads the auth library and prepares your app for authentication, then signals the control to &lt;strong&gt;initClient()&lt;/strong&gt; function where you’ll perform the actual authentication like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; /**
 * starts the client authorization, most useful for debugging.
 * 
 * */
function initClient() {
    // Retrieve the discovery document for version 3 of Google Drive API.
    // In practice, your app can retrieve one or more discovery documents.
    var discoveryUrl = &apos;https://www.googleapis.com/discovery/v1/apis/drive/v3/rest&apos;;

    // Initialize the gapi.client object, which app uses to make API requests.
    // Get API key and client ID from API Console.
    // &apos;scope&apos; field specifies space-delimited list of access scopes.
    gapi.client.init({
        apiKey: &apos;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&apos;,
        discoveryDocs: [discoveryUrl],
        clientId: &apos;yyyyyyyyyy-yyyyyyyyyyyyyy.apps.googleusercontent.com&apos;,
        scope: SCOPE
    }).then(function () {
      console.log(&quot;init happened successfully.&quot;);
      GoogleAuth = gapi.auth2.getAuthInstance();
      
      console.log(&quot;now binding updateSigninStatus&quot;);
      GoogleAuth.isSignedIn.listen(updateSigninStatus); // Listen for sign-in state changes.

      // Listen for sign-in state changes.
      console.log(&quot;now calling updateSigninStatus&quot;);
      updateSigninStatus(GoogleAuth.isSignedIn.get());

      // Handle initial sign-in state. (Determine if user is already signed in.)
      //var user = GoogleAuth.currentUser.get();
      
      // Call handleAuthClick function when user clicks on
      //      &quot;Sign In/Authorize&quot; button.
      $(&apos;#sign-in-or-out-button&apos;).click(function() {
        handleAuthClick();
      }); 
      $(&apos;#revoke-access-button&apos;).click(function() {
        revokeAccess();
      }); 

    }, function(error){
        console.log(&quot;ERROR in gapi.init:&quot;, error);
    });
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(Remember to replace the long string of “xxxxxx…” with your own API Key, and the long string of “yyyyyy….” with your own Client ID)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;gapi.client.init()&lt;/strong&gt; is an important function which does all the hard work of checking whether a user is authenticated, and if not, then redirect them to Google’s servers, perform the authentication, and if successful, redirect them back to your server and inside the &lt;strong&gt;.then()&lt;/strong&gt; block in which you’ll handle your applications logic (like displaying the logged-in user’s name which you get using &lt;strong&gt;GoogleAuth.currentUser.get()&lt;/strong&gt;, update the logged-in status (toggle sign-in and sign-out buttons accordingly), etc:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;gapi.client&lt;/strong&gt; is your global variable that could be used for doing all kinds of stuff. For example, this is how you can list the files available in the Google Drive’s root folder of the user as links in your document:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   gapi.client.drive.files.get({
        fileId: id,
        fields: &apos;name,webContentLink&apos;
    }).then(function(success) {
        var webContentLink = success.result.webContentLink;
        var fileName = success.result.name;
        console.log(&quot;SUCCESS! name is: &quot;, webContentLink);
        console.log(&quot;SUCCESS! webContentLink is: &quot;, webContentLink);
        $source = $(&quot;&amp;lt;a href=&apos;&quot; + webContentLink + &quot;&apos; &amp;gt;&quot; + fileName + &quot;&amp;lt;a&amp;gt;&quot;);
        $(&apos;body&apos;).append($source);
    }, function(fail){
        console.log(fail);
        console.log(&apos;Error &apos;+ fail.result.error.message);
    });
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;name&lt;/strong&gt; field represents the file name and &lt;strong&gt;webContentLink&lt;/strong&gt; is the actual link to the file in the user’s drive, you can use it however in your app once you get it (like download it using the AJAX &lt;strong&gt;jquery.get()&lt;/strong&gt; method, etc.).&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>WordPress DIY: Adding twitter cards meta to your blog without using an external plugin</title>
   <link href="https://prahladyeri.github.io/blog/2018/07/wordpress-diy-adding-twitter-cards-meta-to-your-blog.html"/>
   <updated>2018-07-08T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/07/wordpress-diy-adding-twitter-cards-meta-to-your-blog</id>
   <content type="html">&lt;p&gt;Just like my &lt;a href=&quot;https://prahladyeri.github.io/blog/2018/07/wordpress-diy-adding-google-analytics-script-to-your-blog.html&quot;&gt;last article&lt;/a&gt;, we won’t be focusing on using a third party plugin, but write our own plugin. I’m a minimalist and don’t prefer to use layer-2 solutions for really trivial things that can easily be achieved by writing code.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Now, though trivial to implement, the twitter cards support is a very important and useful thing for your blog. To understand why, consider the following example tweet from Python Software Foundation:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/07/no_twitter_card.png&quot; alt=&quot;Links had no Twitter Card Meta&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links had no Twitter Card Meta&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the posted links on this tweet from the domain djangoproject.com didn’t expand into a preview because they didn’t have twitter card meta tags in their pages. Unlike Facebook who expands all posted links, Twitter doesn’t do that automatically, but only after parsing some meta tags which should be in their required format. Only after that, the tweets expand into a full preview like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/07/twitter_card.png&quot; alt=&quot;Link had Twitter Meta&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Link had Twitter Card Meta&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So as the owner of a WordPress blog, you’d be certainly interested in having the tweets containing links to your own site expand into these previews, right? So, let’s go about doing it.&lt;/p&gt;

&lt;p&gt;Essentially, your web page should contain the following meta tags that twitter parses in order to come up with a preview:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;meta name=&quot;twitter:card&quot; content=&quot;summary&quot; /&amp;gt;
 &amp;lt;meta name=&quot;twitter:site&quot; content=&quot;@prahladyeri&quot; /&amp;gt;
 &amp;lt;meta name=&quot;twitter:creator&quot; value=&quot;@prahladyeri&quot; /&amp;gt;
 &amp;lt;meta name=&quot;twitter:url&quot; content=&quot;https://prahladyeri.github.io/blog/2018/06/people-migrating-from-github-to-gitlab-should-learn-about-these-details-first.html&quot; /&amp;gt;
 &amp;lt;meta name=&quot;twitter:title&quot; content=&quot;People migrating from Github to Gitlab should learn about these details first&quot; /&amp;gt;
 &amp;lt;meta name=&quot;twitter:description&quot; content=&quot;After &amp;amp;lt;a href=&amp;amp;quot;https://prahladyeri.github.io/blog/2018/06/microsofts-github-acquisition-an-unbiased-perspective.html&amp;amp;quot;&amp;amp;gt;Microsoft&amp;amp;#039;s recent acquisition of Github&amp;amp;lt;/a&amp;amp;gt;, a mass exodus has kind of begun and many small and large projects are moving their code bases to the much hyped &amp;amp;lt;a href=&amp;amp;quot;https://gitlab.com/&amp;amp;quot;&amp;amp;gt;Gitlab&amp;amp;lt;/a&amp;amp;gt; in a hurry, and these include both open and closed source projects. However, before migrating to Gitlab, they should take a pause and learn something about Gitlab and consider evaluating other alternatives too.&quot; /&amp;gt;
 &amp;lt;meta name=&quot;twitter:image&quot; content=&quot;https://secure.gravatar.com/avatar/3f253507b82dd33f352c08f649caaa54?rating=PG&amp;amp;size=75&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All these meta tags have different meanings. For example, &lt;strong&gt;twitter:url&lt;/strong&gt; is for the canonical URL of your page, &lt;strong&gt;twitter:title&lt;/strong&gt; is for the title that should be displayed in the preview, etc.&lt;/p&gt;

&lt;p&gt;Now, let’s add a simple PHP plugin file that automatically adds these tags in all the pages of our WordPress blog. Firstly, create a text file called &lt;strong&gt;“/wp-content/plugins/custom_headers.php”&lt;/strong&gt; in your WordPress installation folder (no need to create if you had done already by following the last article).&lt;/p&gt;

&lt;p&gt;After that, just add the below function and call it using &lt;strong&gt;add_action()&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;function add_twitter_card_header() {
    #twitter cards hack
    global $post;
    if(is_single() || is_page()) {
        $twitter_url    = get_permalink();
        $twitter_title  = get_the_title();
        $content = get_extended( $post-&amp;gt;post_content );
        $attch_id = get_post_thumbnail_id($post-&amp;gt;ID);
        $twitter_thumbs = wp_get_attachment_image_src($attch_id, full);
        $twitter_thumb  = $twitter_thumbs[0];
        if(!$twitter_thumb) {
            $twitter_thumb = &apos;https://secure.gravatar.com/avatar/3f253507b82dd33f352c08f649caaa54?rating=PG&amp;amp;size=75&apos;;
        }
        ?&amp;gt;
        &amp;lt;meta name=&quot;twitter:card&quot; content=&quot;summary&quot; /&amp;gt;
        &amp;lt;meta name=&quot;twitter:site&quot; content=&quot;@prahladyeri&quot; /&amp;gt;
        &amp;lt;meta name=&quot;twitter:creator&quot; value=&quot;@prahladyeri&quot; /&amp;gt;
        &amp;lt;meta name=&quot;twitter:url&quot; content=&quot;&amp;lt;?php echo $twitter_url; ?&amp;gt;&quot; /&amp;gt;
        &amp;lt;meta name=&quot;twitter:title&quot; content=&quot;&amp;lt;?php echo $twitter_title; ?&amp;gt;&quot; /&amp;gt;
        &amp;lt;meta name=&quot;twitter:description&quot; content=&quot;&amp;lt;?php echo esc_html($content[&apos;main&apos;]); ?&amp;gt;&quot; /&amp;gt;
        &amp;lt;meta name=&quot;twitter:image&quot; content=&quot;&amp;lt;?php echo $twitter_thumb; ?&amp;gt;&quot; /&amp;gt;
        &amp;lt;?php
    }
}

add_action(&apos;wp_head&apos;, &apos;add_twitter_card_header&apos;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This simple block of code will pick up all required things such as the post’s title content excerpt, etc. and supply them to twitter via the meta tags. Few important things you need to remember:&lt;/p&gt;

&lt;p&gt;1. Firstly, update the $twitter_thumb variable in the following block with your own gravatar:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if(!$twitter_thumb) { $twitter_thumb = &apos;https://secure.gravatar.com/avatar/3f253507b82dd33f352c08f649caaa54?rating=PG&amp;amp;size=75&apos;; }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will show the gravatar by default if you haven’t set a featured image in your post.&lt;/p&gt;

&lt;p&gt;2. You’ll need to add the &lt;strong&gt;“Read More”&lt;/strong&gt; meta tag in all your posts, otherwise &lt;strong&gt;$content[‘main’]&lt;/strong&gt; will return the whole thing instead of just the excerpt.&lt;/p&gt;

&lt;p&gt;Once you do this and publish your post, head over to the &lt;a href=&quot;https://cards-dev.twitter.com/validator&quot;&gt;Twitter Cards Validator Service&lt;/a&gt; and test your link. Its as easy as that!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Wordpress DIY: Adding Google Analytics Script to your Blog Without using an External Plugin</title>
   <link href="https://prahladyeri.github.io/blog/2018/07/wordpress-diy-adding-google-analytics-script-to-your-blog.html"/>
   <updated>2018-07-07T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/07/wordpress-diy-adding-google-analytics-script-to-your-blog</id>
   <content type="html">&lt;p&gt;Adding a custom script element to your Wordpress blog is really straightforward if you know what you are doing and there isn’t any need to install a third-party plugin for this.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Now, for something like spam protection (Akismet) or adding contact forms (Contact Form Seven), its quite understandable, but if you take the approach of adding third party plugins for every little plus and minus operation (such as adding a Google Analytics script), then its neither good for the maintenance nor security of your Wordpress blog.&lt;/p&gt;

&lt;p&gt;In this article, I’m going to explain how to create a simple custom plugin for your Wordpress blog that you’ll hand-code yourself. The custom plugin will be quite generic and can be later used for adding other elements too such as maybe twitter card attributes, etc.&lt;/p&gt;

&lt;p&gt;Create the following PHP Plugin file in the code editor of your choice and save it as &lt;strong&gt;/wp-content/plugins/custom_headers.php&lt;/strong&gt; in your Wordpress installation.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;?php 
/**
 * @package Custom Headers
 * @version 0.1.0
 */
/*
Plugin Name: Custom Headers
Plugin URI: http://github.com/prahladyeri/custom-headers/
Description: Add a bunch of custom headers before the head tag.
Author: Prahlad Yeri
Version: 0.1.0
Author URI: https://prahladyeri.github.io
*/

function add_analytics_header() {
$analytics=&amp;lt;&amp;lt;&amp;lt;EOD
&amp;lt;-- Global site tag (gtag.js) - Google Analytics --&amp;gt;
&amp;lt;script async src=&quot;https://www.googletagmanager.com/gtag/js?id=UA-XXXXXXX-X&quot;&amp;gt;
&amp;lt;script&amp;gt;
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag(&apos;js&apos;, new Date());

  gtag(&apos;config&apos;, &apos;UA-XXXXXXX-X&apos;);
&amp;lt;/script&amp;gt;
EOD;
    echo $analytics;
}
add_action(&apos;wp_head&apos;, &apos;add_analytics_header&apos;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Just remember to update the package meta stuff (Plugin Name, Author, etc.) as it suits you and put your own Analytics ID in place of UA-XXXXXXX-X.&lt;/p&gt;

&lt;p&gt;Its as simple as that, your plugin is ready! Just enable this plugin by going to Plugins-&amp;gt;Installed Plugins in your Wordpress admin panel.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Building Robust Single-Page Applications: Key Architectural Insights</title>
   <link href="https://prahladyeri.github.io/blog/2018/07/the-right-way-to-architect-single-page-web-applications.html"/>
   <updated>2018-07-06T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/07/the-right-way-to-architect-single-page-web-applications</id>
   <content type="html">&lt;p&gt;Lets face it, Web Apps are a complex and complicated beast, both on the front end and back end. The reason we had to come up with so many frameworks and libraries (angular/backbone/react/vue/marionette/require.js/etc.) is that the whole process is quite difficult and convoluted.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Once your app starts to scale in complexity, even the best of ideas and best practices in this field cannot prevent your app code from becoming a spaghetti of JavaScript and jQuery callbacks and DOM manipulation functions. Turning into a mess is essentially the nature of JavaScript when left to its own mechanism (event callback model and high asynchrony do ensure that). If you only use jQuery to architect your app, this is what it’ll soon end up becoming:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/07/spaghetti-way.png&quot; alt=&quot;The Spaghetti Way&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Spaghetti Way&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once your app.js ends up with millions of these jQuery functions, it will soon become beyond understanding and sanity even for you as the author of the code. And that’s exactly why we need to understand this complexity and architect some structure and foundation in our apps.&lt;/p&gt;

&lt;p&gt;One way of handling this complexity is the easy way - just delegate this whole thing to another complex beast of an opinionated framework such as angular or vue, and work with simple abstractions like angular views and controllers. This strategy does work to an extent (and you do get some mental sense of achievement too) but only as long as your app is limited in functionality and doesn’t scale in size and complexity. The problem with opinionated frameworks is that they are opinionated - they work only as long as your app is pigeonholed and fits in their own way of typically doing things, but the moment you step outside and want to do something that doesn’t confirm to that way (like rendering a complex DOM element or work with a difficult third-party UI library), then you are hit with a wall - unless you are prepared to venture too deeply into that framework and learn advanced stuff (like creating complex directives or providers in angular). But if you had to resort to that at the end, then why use an opinionated framework at all!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/07/opinionated-way.png&quot; alt=&quot;The Opinionated Framework Way&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Opinionated Framework Way&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As you can see in above diagram, using an opinionated framework is like driving a car with automatic gear system. You cannot control the speed or acceleration, nor can you synchronize the clutch action, its the black box of the automatic gear that does it for you. But unlike this simple gearbox analogy where speed and clutch synchrony are the only two variables, your web app has lots of variables, so the chances of going wrong and getting stuck with this approach drastically increases as your web app starts to scale in complexity. Manually doing things using the Backbone way may appear to be difficult or complex initially, but that’s a much better way than trading off that complexity for an opinionated black-box framework about who’s inner workings you don’t understand anything at all.&lt;/p&gt;

&lt;p&gt;The first step towards building a single page app should be deciding what your app is going to do and how its going to do it. Every app is different in features, functionality and work flow, hence it pays to use an uber-light framework such as Backbone instead of any heavy and opinionated ones. Backbone in the frameworks world is pretty much like what jQuery is in the libraries world, it doesn’t do much on its own besides giving you methods and objects for creating a layer of structure and organization (just as jQuery doesn’t do much on its own besides adding a sugar-syntax wrapper for native DOM manipulation functions of JavaScript).&lt;/p&gt;

&lt;p&gt;Now, because of this exact flexibility and freedom, there is no one correct way to build Backbone apps. Backbone is heavy in philosophy and light in implementation, and the philosophy being simple:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Separation of concerns (models, views and other components should be kept separate).&lt;/li&gt;
  &lt;li&gt;Separation of roles (Organizers and implementors should do their own thing, one shouldn’t step into the shoes of others).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Personally, I think that if you follow only these two principles sincerely and stick to them, then you can handle whatever amount complexity life throws at your app. Of course, to architect such an app is an art in itself and you may not get it right the first time. The architecture itself may even need to evolve with time and complexity as your app grows and scales. One global event coordinator (or organizer) can be enough initially, but later on, you may have to add a DOM coordinator for handling DOM events, a data coordinator for handling connections to the database objects, etc. as you scale.&lt;/p&gt;

&lt;p&gt;Here is a basic example architecture that you can probably use for an app of low to medium complexity:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/07/backbone-way1.png&quot; alt=&quot;The Backbone Way&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Backbone Way&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And &lt;a href=&quot;https://github.com/prahladyeri/experimental-backbone&quot;&gt;this is an example implementation&lt;/a&gt; which I’m developing as a side project. Of course, the app doesn’t do much presently besides user management and routing to dummy pages,&lt;/p&gt;

&lt;p&gt;Of course, this isn’t the only Backbone way to build apps, but I think its a good one for a start. The primary problem you’ll face if you stick to doing things your own way and not use an opinionated framework is that of organization. You need an organizer/coordinator object to co-ordinate with various components of your app and keep them in sync with each other and this is where the Backbone.Events API provided by the framework shines. The Backbone.View object also plays a great part in ensuring the separation of DOM manipulation code from rest of your app, and along with the underscore template library, it becomes a power combination for rendering DOM! With underscore templates, you can actually use javascript functions and variables inside a template, not just clumsy tags like ng-something!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;% if(modal[&apos;buttons&apos;]) { %&amp;gt;
  &amp;lt;div class=&quot;ui-button-container&quot;&amp;gt;
	&amp;lt;% _(model[&apos;buttons&apos;]).each(function(button) { %&amp;gt;
	  &amp;lt;a class=&quot;ui-button ui-button-pill &amp;lt;%= button.extra_class %&amp;gt; &quot; href=&quot;&amp;lt;%= button.href %&amp;gt;&quot;&amp;gt;
		&amp;lt;span class=&quot;label&quot;&amp;gt;
		  &amp;lt;span class=&quot;section&quot;&amp;gt;&amp;lt;%= button.label %&amp;gt;&amp;lt;/span&amp;gt; 
		&amp;lt;/span&amp;gt;
	  &amp;lt;/a&amp;gt;
	&amp;lt;% }) %&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;% } %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course, its not a very good practice to mix javascript with templates, but it just shows the power and flexibility of using backbone+underscore compared to other vendor frameworks.&lt;/p&gt;

&lt;p&gt;Ultimately, it all comes down to your preference, you can use angular or vue or react if you really want to. However, always understand the reason why you are hooking to a third party framework. If it is just for escaping the complexity of your app, then no amount of framework or libraries in the world are going to help you. Passing on the complexity to a black box like angular or vue is just a band-aid solution that will fall apart the moment you scale in complexity and you’ll get stuck by the limitations of the framework. At that point, you’ll have to make one of these two decisions:&lt;/p&gt;

&lt;p&gt;1. Abandon the framework and do everything right from scratch using the manual way of Backbone.&lt;/p&gt;

&lt;p&gt;2. Understand the framework internals too deeply and customize it to achieve what you want (but then what was the point of using this framework in the first place?)&lt;/p&gt;

&lt;p&gt;Ultimately, its your decision to choose a framework. Consider it wisely after weighing in all the pros and cons. Best of luck!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>What problem is React/JSX solving in your App?</title>
   <link href="https://prahladyeri.github.io/blog/2018/07/what-problem-is-react-jsx-solving.html"/>
   <updated>2018-07-05T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/07/what-problem-is-react-jsx-solving</id>
   <content type="html">&lt;p&gt;Framework wars and debates are very much a thing these days, be it Angular vs Backbone or Angular vs React, but the real debate isn’t about these frameworks. The real debate essentially comes down to which is the most efficient way of structuring your app and more importantly, rendering and managing your DOM.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Whilst the traditional jQuery/Backbone way is to render the DOM directly using methods like $(component).html(“DOM Code”), React considers it an anti-pattern and recommends the way of virtual DOM - a concept where you don’t render the DOM directly as it is, but keep a virtual copy of it and render only the differential which is essentially &lt;em&gt;patching&lt;/em&gt; the DOM.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/07/js-frameworks.jpeg&quot; alt=&quot;JavaScript Frameworks&quot; /&gt;&lt;/p&gt;

&lt;p&gt;One thing that comes to mind is that more than an improvement over the former approach, you are basically trading off CPU overhead for lesser memory usage. The former jQuery approach is more &lt;strong&gt;memory intensive&lt;/strong&gt; as the browser needs to keep large amount of DOM structural code in memory until its rendered (yeah, it gets pretty large in a non-trivial app with lots of widgets that may need to be rendered in a complex array of patterns). However, what exactly are we gaining by using the JSX virtual DOM method? The JSX approach is just as much &lt;strong&gt;CPU intensive&lt;/strong&gt; as the jQuery approach is RAM intensive because it takes just as many CPU cycles to compute the differential and come up with a “patch” version of the DOM. In fact, the trade-off may be even worse considering that React is more of a library overhead than jQuery which is just a light “write less do more” wrapper over JavaScript DOM manipulation functions.&lt;/p&gt;

&lt;p&gt;The real question we should be asking ourselves is why do we keep looking for that holy-grail JavaScript framework every now and then? Yesterday it was angularjs, today its React and tomorrow it will be vue.js. Instead of running after the shiny new framework, why not sit down and see what you are doing wrong with the present tools you have. After all, these tools are industry proven and they still exist for a reason.&lt;/p&gt;

&lt;p&gt;I think most of the issues with using jQuery come from incorrect usage patterns than any problems associated with actual rendering. Consider the following often used pattern for rendering view blocks:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$.get(&quot;partials/navbarTemplate.html&quot;, function(data){
  $(&quot;#div-navbar&quot;).html(data); //render a template
  ..
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This coding pattern is typically abhorred by most React experts as they feel that rendering a whole bunch of html code (data argument in this case) is an anti pattern as the browser has to repeatedly render a lot of boiler-plate DOM unnecessarily. However, that’s only true if you do this often times and repeatedly call this function. If you use a good architectural pattern for structuring your app (like the one provided by Backbone.js), you can render this same DOM in a very idempotent way:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;app.NavbarView = Backbone.View.extend({
    el: &quot;#div-navbar&quot;,
    initialize: function() {
        var temp = this;
        $.get(&quot;partials/navbarTemplate.html&quot;, function(e){
            temp.template = _.template(e, {});
        });
    },
    render: function() {
        this.$el.html(this.template());
    },

});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In above code, we are still using jQuery.get, but in a more organized and structured way. Firstly, we call jQuery.get only in the initialization of the view to get the template and store it in the cache, so the network overhead isn’t involved each time we need to use a template. Furthermore, backbone itself caches and stores the block element to be rendered (this.$el), so that we don’t have to trouble the browser with other areas of the DOM where rendering isn’t required. Finally, we just call the underscore template and render the element:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;this.$el.html(this.template());
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course, this could be probably further optimized by using jQuery.empty().append() instead of jQuery.html() if you want to tweak the last drop of performance!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;this.$el.empty().html(this.template());
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However, I don’t think this kind of premature optimization is really needed unless you are building a really complex app and even then this isn’t required in about 90% of the cases, the browsers have become considerably fast in recent years, at least in the area of DOM rendering.&lt;/p&gt;

&lt;p&gt;And this is exactly why I think that using virtual DOM libraries like React is an anti-pattern. You are essentially stepping into the shoes of the browser, isn’t it? If the kind of partial patching implemented by React is really efficient, wouldn’t the browsers be doing it themselves? Maybe they will take some of the best ideas from React and JSX, and implement it themselves in the coming future, but why should &lt;em&gt;you&lt;/em&gt; (as a programmer) be bothered with that is what I don’t understand.&lt;/p&gt;

&lt;p&gt;This whole debate about frameworks is pretty much centered on separation of concerns essentially (models, views and controllers/organizers should all be cleanly separated) and to some extent, React is going against that separation by doing what the browser is supposed to do.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Notes on open source vs free software</title>
   <link href="https://prahladyeri.github.io/blog/2018/07/notes-on-open-source-vs-free-software.html"/>
   <updated>2018-07-03T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/07/notes-on-open-source-vs-free-software</id>
   <content type="html">&lt;p&gt;Socialism and Communism have a lot in common, both ideologies aim to bring an equilibrium and welfare in the society by removing the income inequality between the rich and the poor. However, how they both go about doing that job is what makes the difference between sky and earth. Whilst Communism does advocate the use of force to achieve its aim, Socialism does not and that’s the major difference of spirit between them. If you remove this force element, you’ll find that they are both pretty much the same thing with only technical differences here and there.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/07/pexels-photo-273691-300x199.jpeg&quot; alt=&quot;pexels-photo-273691&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Coming to the world of software, it’s a similar case when it comes to GPL (free software or copyleft) and non-GPL (open source or liberal) variety of FOSS licenses. The &lt;a href=&quot;https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt&quot;&gt;GNU GPL&lt;/a&gt; forces their ideology upon the user of the software by expressly restricting him/her to the kind of use cases permitted on the said software:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt; To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This restriction or force factor is what makes GPL or LGPL less desirable than the other more liberal (also called “open source”) licenses, just as the communist ideology is less desirable than socialist ideology by most people.&lt;/p&gt;

&lt;p&gt;On the other hand, consider a liberal open source license such as the &lt;a href=&quot;https://opensource.org/licenses/MIT&quot;&gt;MIT license&lt;/a&gt;. This license doesn’t force anything on how the software may be used by the user (except that the license header itself carrying the name of the author should be passed around with any distributed copies):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the &quot;Software&quot;), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;They may use it in any kind of use cases they please, even include it in a closed source or proprietary software if they want. Similar is the case with other open source licenses such as the &lt;a href=&quot;https://opensource.org/licenses/BSD-3-Clause&quot;&gt;BSD Modified license&lt;/a&gt;or the &lt;a href=&quot;https://opensource.org/licenses/Apache-2.0&quot;&gt;Apache 2 license&lt;/a&gt;. None of these licenses force their users into any kind of restrictions, which is probably why they are more popular and used than the copyleft variety of licenses today.&lt;/p&gt;

&lt;p&gt;Finally, coming to the point of contrast between the copyleft and open source licenses, you’ll find that they are all ultimately trying to achieve more or less the same goal, though some would strive for an excellence methodology in software development while others would term it as a struggle for users’ freedom depending on what viewpoint you choose to look at it.&lt;/p&gt;

&lt;p&gt;Now, the reasons for GPL restrictions and &lt;a href=&quot;https://www.fsf.org/&quot;&gt;their ideology&lt;/a&gt; are quite understandable, but the way they go about implementing it is certainly not ideal from a practical standpoint. &lt;a href=&quot;https://stallman.org/&quot;&gt;Stallman’s approach&lt;/a&gt; of making copyleft software unusable when linked with non-GPL or proprietary code is like a child’s tit-for-tat attitude. Just as proprietary software companies won’t let you use their software without a license and make it unapproachable to those who don’t have it, Stallman wants to make the GPL world unapproachable to the proprietary companies by making this restriction on mixing GPL with proprietary code. In a perfect world (consisting of mostly power users who would rather visit support forums and do their own builds than use proprietary software), this may have worked wonders to Stallman’s cause, but unfortunately we don’t live in such a perfect world!&lt;/p&gt;

&lt;p&gt;On the other hand, the open source or liberal licenses that don’t put any restrictions on its usage serve the purpose of everyone and so everyone is happy and thrives in this world. Power users are happy because they no longer have to use privacy invasive and/or bloated proprietary software as they can compile their own builds or even visit support forums or issue trackers of various open source projects if required. On the other hand, proprietary software companies are also happy as they get to use the fruits (source code) of open source contributors without spending anything on their part. Of course, taking the moral cognizance of these fruits, many of them also do tend to contribute back by either financially supporting other open source projects or releasing their in-house developed source code as open source to the world at large. There are several examples of these such as Microsoft’s &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;Visual Studio Code&lt;/a&gt;, Adobe’s code editor called &lt;a href=&quot;http://brackets.io/&quot;&gt;Brackets.io&lt;/a&gt;, Apple’s &lt;a href=&quot;https://developer.apple.com/swift/&quot;&gt;Swift&lt;/a&gt;, etc. Now, consider that in a Richard Stallman’s “pureland GPL” world, we wouldn’t have all these contributions back from the proprietary companies!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Webpack Project has a Vulnerability</title>
   <link href="https://prahladyeri.github.io/blog/2018/06/webpack-project-is-sitting-on-a-vulnerability-avoid-it-at-all-costs.html"/>
   <updated>2018-06-28T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/06/webpack-project-is-sitting-on-a-vulnerability-avoid-it-at-all-costs</id>
   <content type="html">&lt;p&gt;The other day, I was going through this &lt;a href=&quot;https://medium.com/p/73fac4bc5068&quot;&gt;medium post&lt;/a&gt; which describes the kind of chaos and insecurity currently plaguing the JavaScript world, and the &lt;em&gt;numero uno&lt;/em&gt; reason for that is the astronomical number of npm packages.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;When you usually install a non-trivial library or application through a package manager, the expectation is that the number of dependencies should be as less as possible. This not only helps you in managing the disk space (very important for cloud hosting), but also makes a manageable code audit possible. For instance, the Python’s Flask package (which is a considerably large web framework, a lot more complex than Webpack which is just a JavaScript bundler) has the &lt;a href=&quot;https://github.com/pallets/flask/blob/master/setup.py&quot;&gt;following four dependencies&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; install_requires=[
 &apos;Werkzeug&amp;gt;=0.14&apos;,
 &apos;Jinja2&amp;gt;=2.10&apos;,
 &apos;itsdangerous&amp;gt;=0.24&apos;,
 &apos;click&amp;gt;=5.1&apos;,
],
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On the other hand, this trivial Webpack package on NPM has an astounding &lt;a href=&quot;https://www.npmjs.com/package/webpack&quot;&gt;25 dependencies on various 3rd party packages&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@webassemblyjs/ast
 @webassemblyjs/helper-module-context
 @webassemblyjs/wasm-edit
 @webassemblyjs/wasm-opt
 @webassemblyjs/wasm-parser
 acorn
 acorn-dynamic-import
 ajv
 ajv-keywords
 chrome-trace-event
 enhanced-resolve
 eslint-scope
 json-parse-better-errors
 loader-runner
 loader-utils
 memory-fs
 micromatch
 mkdirp
 neo-async
 node-libs-browser
 schema-utils
 tapable
 uglifyjs-webpack-plugin
 watchpack
 webpack-sources
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However, your worry hasn’t even started yet, your real worry starts when you realize that the other super-trivial package mentioned in that article called &lt;strong&gt;&lt;a href=&quot;https://www.npmjs.com/package/is-odd&quot;&gt;is-odd&lt;/a&gt;&lt;/strong&gt;  has a whopping statistics of &lt;strong&gt;1.4 million&lt;/strong&gt; downloads per week:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/06/is-odd.png&quot; alt=&quot;is odd&quot; /&gt;{.alignnone .size-full .wp-image-699 width=”453” height=”445”}&lt;/p&gt;

&lt;p&gt;Now, any programmer worth his salt will know that its a &lt;a href=&quot;https://stackoverflow.com/q/6211613/849365&quot;&gt;one line coding effort&lt;/a&gt; to determine whether a given number is odd or even, even in JavaScript:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;function isEven(n) {
 return n % 2 == 0;
}

function isOdd(n) {
 return Math.abs(n % 2) == 1;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, our master programmer who developed this “&lt;em&gt;is-odd as a service&lt;/em&gt;” not only goes ahead and registers whole new npm packages called is-odd and is-even, but also makes a lot of his other packages depend on it. Can you even begin to imagine what kind of bureaucratic politician you have to be in order to do that!&lt;/p&gt;

&lt;p&gt;Now, this in itself couldn’t have caused any problem, the real issue here is that the highly reputed and popular &lt;strong&gt;Webpack&lt;/strong&gt; project is also using one of his packages (for doing a trivial regular expression check on a string) and the dependency chain is such that the following four packages are also pulled in whenever you install &lt;strong&gt;Webpack&lt;/strong&gt; from npm since Webpack depends on them (and this is what explains is-odd’s 1.4 million weekly download figure):&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is-odd -&amp;gt; nanomatch -&amp;gt; micromatch -&amp;gt; webpack&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now, even a CS undergraduate should be able to see the management nightmare, not only of burdening your hard disk space of these additional packages whenever you &lt;strong&gt;npm install webpack&lt;/strong&gt;, but also the security nightmares associated with it. In future, if the developer of &lt;strong&gt;is-odd&lt;/strong&gt; package clubbed some malware in his code and propagated it throughout the node-ecosystem, can you even begin to imagine the disaster its going to cause on your production machines? I agree that this applies to other packaging systems like &lt;strong&gt;pip&lt;/strong&gt; and &lt;strong&gt;composer&lt;/strong&gt; too, but problem here is that the number of npm packages is too large and too unmanageable.&lt;/p&gt;

&lt;p&gt;And to top it, the developers in the ecosystem are paying no heed to this, they think this is something to be proud of and worthy of chest thumping. They should try to understand that DRY (Don’t Repeat Yourself) becomes a self-harming pattern beyond a certain extent, especially when you start releasing packages like is-odd and is-even. They get argumentative and counter you with a militant defense when you try to explain this point to them. For instance, I raised &lt;a href=&quot;https://github.com/webpack/webpack/issues/7591&quot;&gt;an issue on Webpack’s Github repository a few days ago&lt;/a&gt; for this exact problem (&lt;strong&gt;remove dependency from micromatch package&lt;/strong&gt;) and they simply closed it down giving some hilarious arguments. Their arguments are really interesting, but beyond sanity for someone who values security and maintainability of production applications.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Smyte is no more - The latest episode in the acquisition saga of Tech Giants</title>
   <link href="https://prahladyeri.github.io/blog/2018/06/smyte-the-latest-victim-in-the-acquisition-saga-of-tech-giants.html"/>
   <updated>2018-06-22T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/06/smyte-the-latest-victim-in-the-acquisition-saga-of-tech-giants</id>
   <content type="html">&lt;p&gt;Not even a month has been passed since Microsoft gave a big surprise to the world at large and the open source community by &lt;a href=&quot;https://prahladyeri.github.io/blog/2018/06/microsofts-github-acquisition-an-unbiased-perspective.html&quot;&gt;acquiring Github Inc.&lt;/a&gt; a few days ago, and there has been &lt;a href=&quot;https://techcrunch.com/2018/06/21/twitter-acquires-anti-abuse-technology-provider-smyte/&quot;&gt;another acquisition&lt;/a&gt; yesterday. This time, Twitter Inc. bought the well known online service &lt;a href=&quot;https://smyte.com/&quot;&gt;Smyte&lt;/a&gt;. Smyte was an online provider of anti-abuse technology services, i.e. things like AI algorithms for identifying online trolling, etc. Many customers were totally dependent on this provider’s API which suddenly disappeared off the radar after Twitter Inc. acquired them yesterday.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;A whole lot of customers and other depending services were affected because of this &lt;a href=&quot;https://twitter.com/seldo/status/1009873821141118976?s=09&quot;&gt;including npm&lt;/a&gt;, the popular Node.js package manager. As usual, &lt;a href=&quot;https://old.reddit.com/r/sysadmin/comments/8swr1e/reminder_that_external_dependencies_can_shut_down/&quot;&gt;Reddit&lt;/a&gt; was one of the first places where people turned up to know what’s going on and update their statuses. As one of the comments quite intelligently notes, this is a very good lesson on this modern web development way that involves depending on a zillion external services and leaving your application open to the whims of acquisitions and centralization of technology powers:&lt;/p&gt;

&lt;p&gt;::: {.reddit-embed data-embed-media=”www.redditmedia.com” data-embed-parent=”false” data-embed-live=”true” data-embed-uuid=”5bebab31-c06c-4d2c-bbd0-d938f9f97409” data-embed-created=”2018-06-22T14:47:54.034Z”}
&lt;a href=&quot;https://old.reddit.com/r/sysadmin/comments/8swr1e/reminder_that_external_dependencies_can_shut_down/e13j9tx/&quot;&gt;Comment&lt;/a&gt; from discussion &lt;a href=&quot;https://old.reddit.com/r/sysadmin/comments/8swr1e/reminder_that_external_dependencies_can_shut_down/&quot;&gt;Reminder that external dependencies can shut down without notice&lt;/a&gt;.
:::&lt;/p&gt;

&lt;p&gt;
&lt;script async=&quot;&quot; src=&quot;https://www.redditstatic.com/comment-embed.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p&gt;Similarly, Twitter was also filled today with tweets of complains from existing Smyte customers who have suddenly stopped receiving any communication from their service provider:&lt;/p&gt;

&lt;p&gt;https://twitter.com/arthens/status/1009925187364458496&lt;/p&gt;

&lt;p&gt;Jeremy Ashkenas, the inventor of famous Backbone.js and Underscore.js JavaScript libraries expressed his feelings in a very sarcastic tweet:&lt;/p&gt;

&lt;p&gt;https://twitter.com/jashkenas/status/1010014139320680449&lt;/p&gt;

&lt;p&gt;I don’t know what was Twitter’s intention before acquiring Smyte, but the way this is going on, this has to end in only one of the two ways:&lt;/p&gt;

&lt;p&gt;1. Either developers take lessons from these incidents and stop depending so much on layer-2 solutions and external dependencies.&lt;/p&gt;

&lt;p&gt;2. Technology giants become a bit more empathetic to the needs of smaller businesses and understand that just acquiring other services isn’t a solution to the innovation problem.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>People migrating from Github to Gitlab should learn about these details first</title>
   <link href="https://prahladyeri.github.io/blog/2018/06/people-migrating-from-github-to-gitlab-should-learn-about-these-details-first.html"/>
   <updated>2018-06-11T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/06/people-migrating-from-github-to-gitlab-should-learn-about-these-details-first</id>
   <content type="html">&lt;p&gt;After &lt;a href=&quot;https://prahladyeri.github.io/blog/2018/06/microsofts-github-acquisition-an-unbiased-perspective.html&quot;&gt;Microsoft’s recent acquisition of Github&lt;/a&gt;, a mass exodus has kind of begun and many small and large projects are moving their code bases to the much hyped &lt;a href=&quot;https://gitlab.com/&quot;&gt;Gitlab&lt;/a&gt; in a hurry, and these include both open and closed source projects. However, before migrating to Gitlab, they should take a pause and learn something about Gitlab and consider evaluating other alternatives too.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2018/06/gitlab_stack.png&quot; alt=&quot;Gitlab Stack&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gitlab Stack&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;According to above &lt;a href=&quot;https://stackshare.io/gitlab/gitlab&quot;&gt;StackShare.io chart&lt;/a&gt;, Gitlab basically runs on Microsoft Azure cloud hosting facility. So, if you are leaving Github in order to escape the clutches of Microsoft, then you are headed to a totally wrong place! Microsoft is exactly the place where your source code will reside in this case, only difference is that instead of controlling it directly (as in the case of Github), Microsoft will be controlling your code only technically.&lt;/p&gt;

&lt;p&gt;I know, some of you will be saying that you are self-hosting an open source copy of Gitlab and not actually moving to &lt;a href=&quot;https://gitlab.com&quot;&gt;Gitlab.com&lt;/a&gt;. In that case, please have a look at another item in that stack, namely Rails (RoR or Ruby on Rails framework).&lt;/p&gt;

&lt;p&gt;Though Rails is a great framework that developers enjoy to code with, its a performance hog when it comes to actually running on production! There is a reason why &lt;a href=&quot;https://jaredfriedman.wordpress.com/2015/09/15/why-i-wouldnt-use-rails-for-a-new-company/&quot;&gt;Twitter ditched Rails&lt;/a&gt; in favor of Node.js instead of fixing the interpreter like Facebook did with PHP. Apart from Rails being a performance hog, consider that a git hosting facility is not a simple CRUD app. Its very difficult to do advanced things like CI/CD right in a framework like Rails and the effect is showing. It may work out initially, but once your code base starts to increase and your integrations start to scale, you’ll hit the RoR scaling limit sooner or later, like &lt;a href=&quot;https://serverfault.com/questions/818489/gitlab-extremely-high-memory-consumption-by-ruby-bundle-process&quot;&gt;many others have&lt;/a&gt;. Its not uncommon for Gitlab to eat gigabytes of your RAM or consume 100% CPU. So, if you are trying to host Gitlab in a small Digital Ocean droplet or Amazon AWS Micro instance, you can just forget about it!&lt;/p&gt;

&lt;p&gt;Or, you can sit back and evaluate your options, it really depends on what you basically want. If you just want a free git hosting facility and don’t want to self-host, there is already Github. If you don’t like Microsoft, then you have Bitbucket, SourceForge, &lt;a href=&quot;http://salsa.debian.org/&quot;&gt;Debian Salsa&lt;/a&gt; and others too apart from Gitlab, so consider those options too before blindly deciding on Gitlab and falling for their marketing trap.&lt;/p&gt;

&lt;p&gt;On the other hand, if you are ready to self-host and have a smaller budget (just for an AWS Micro or Digital Ocean droplet, for instance), then you can use one of the several open source and light-weight git hosting software like &lt;a href=&quot;https://try.gogs.io/&quot;&gt;gogs&lt;/a&gt;, &lt;a href=&quot;https://try.gitea.io/&quot;&gt;gitea&lt;/a&gt;, &lt;a href=&quot;https://github.com/phacility/phabricator&quot;&gt;phabricator&lt;/a&gt; and many others and self-host.&lt;/p&gt;

&lt;p&gt;Finally, if you have a budget for hosting Gitlab on a larger instance (like AWS Large instance or 2GB droplet from Digital Ocean), then the first question I’d ask you is why not just stick to paid hosting plans of Github (or Gitlab/Bitbucket if you don’t like Microsoft). That will be a lot cheaper and lenient on your pockets than self hosting a copy of Gitlab on a larger instance.&lt;/p&gt;

&lt;p&gt;Whatever route you end up choosing, it should be a calmly taken logical decision after due consideration to all facts, not in this Github acquisition frenzy. All the best.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://prahladyeri.github.io/blog/2018/06/microsofts-github-acquisition-an-unbiased-perspective.html&quot;&gt;https://prahladyeri.github.io/blog/2018/06/microsofts-github-acquisition-an-unbiased-perspective.html&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackshare.io/gitlab/gitlab&quot;&gt;https://stackshare.io/gitlab/gitlab&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://gitlab.com&quot;&gt;https://gitlab.com&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://jaredfriedman.wordpress.com/2015/09/15/why-i-wouldnt-use-rails-for-a-new-company/&quot;&gt;https://jaredfriedman.wordpress.com/2015/09/15/why-i-wouldnt-use-rails-for-a-new-company/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://serverfault.com/questions/818489/gitlab-extremely-high-memory-consumption-by-ruby-bundle-process&quot;&gt;https://serverfault.com/questions/818489/gitlab-extremely-high-memory-consumption-by-ruby-bundle-process&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=10235446&quot;&gt;https://news.ycombinator.com/item?id=10235446&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/osldev-blog/our-first-eight-months-with-gitlab-2f447af92e50&quot;&gt;https://medium.com/osldev-blog/our-first-eight-months-with-gitlab-2f447af92e50&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://salsa.debian.org/&quot;&gt;http://salsa.debian.org/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://try.gogs.io/&quot;&gt;https://try.gogs.io/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://try.gitea.io/&quot;&gt;https://try.gitea.io/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/phacility/phabricator&quot;&gt;https://github.com/phacility/phabricator&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Microsoft's Github acquisition - A perspective</title>
   <link href="https://prahladyeri.github.io/blog/2018/06/microsofts-github-acquisition-an-unbiased-perspective.html"/>
   <updated>2018-06-08T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/06/microsofts-github-acquisition-an-unbiased-perspective</id>
   <content type="html">&lt;p&gt;As someone who has worked on Microsoft tooling since the days of Foxpro 2.6 to Visual Studio 2010 in IT career, and yet ditched them all for PHP, Python and open source years later, I think I am qualified to offer a somewhat neutral or objective perspective on this acquisition.&lt;/p&gt;

&lt;p&gt;When this news was &lt;strong&gt;&lt;a href=&quot;https://www.businessinsider.in/Microsoft-has-been-talking-about-buying-GitHub-a-startup-at-the-center-of-the-software-world-last-valued-at-2-billion/articleshow/64420905.cms&quot;&gt;first announced on last Friday&lt;/a&gt;&lt;/strong&gt;, I was naturally puzzled and so were a lot of other developers and not without reasons. The way this was initially announced without any clarification about what they were going to do with the hosting facility in future, speculations were bound to be raised and people were bound to be pissed off, especially given Microsoft’s history of being “not so friendly” with open source and “not so good” with some acquisitions.&lt;/p&gt;

&lt;p&gt;When it comes to open source, the Linux subreddit is the place where developers pour their hearts out and &lt;a href=&quot;https://www.reddit.com/r/linux/comments/8nukfa/microsoft_and_github_have_held_acquisition_talks/&quot;&gt;this particular thread&lt;/a&gt; quite summed up  their initial knee-jerk reaction. As the most upvoted comment says:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;its like Microsoft is obsessed with generating as much frustration in the world as possible&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Other reactions were also quite similar:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I LOL’ed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;!-- --&gt;
&lt;blockquote&gt;
  &lt;p&gt;First, Sourceforge started to install malware on the open source projects. So everyone run to Github. Now, Github might get owned by Microsoft. Not to mention Github is 100% closed source. &lt;strong&gt;Gitlab&lt;/strong&gt; looks good every day. There are other open source git solution too.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://gitlab.com/&quot;&gt;&lt;strong&gt;GitLab&lt;/strong&gt;&lt;/a&gt;, the top open source competitor to Github soon became the most discussed alternative (apparently, their marketing team did their bit too with the perfect timing!). And so, a mass exodus of projects soon began, which not only became the most discussed topic on reddit, but also caught a &lt;a href=&quot;https://www.reuters.com/article/us-github-microsoft-gitlab/gitlab-gains-developers-after-microsoft-buys-rival-github-idUSKCN1J12BR&quot;&gt;lot of media attention too&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Just within a couple of days from this announcement, some &lt;a href=&quot;https://motherboard.vice.com/en_us/article/ywen8x/13000-projects-ditched-github-for-gitlab-monday-morning&quot;&gt;13,000 projects had already migrated to Gitlab&lt;/a&gt; which included some prominent names. Naturally, it was time now for Microsoft to do some damage control. But if their PR team was any wiser, they should have already anticipated this and this damage control move should have been done at the outset as the first thing. How could they not expect this backlash considering Microsoft’s past history with open source (however distant it may be)?&lt;/p&gt;

&lt;p&gt;The first move came when Satya Nadella, the Microsoft CEO gave an interview to CNBC on Monday (4th June):&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/m164XggdRGA&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;One of the most important things Satya said in the interview was that Microsoft is a &lt;strong&gt;&lt;em&gt;developer tooling** **company at core&lt;/em&gt;&lt;/strong&gt; (let’s ignore Windows and Office for a moment!). However, most developers still had concerns, they wanted to know how this high level strategy of being open source developer friendly will turn out to be in practice.&lt;/p&gt;

&lt;p&gt;Most importantly, they needed an assurance that their daily driver for source control isn’t going to be integrated and hijacked by other Microsoft products like Skype, Linkedin or even a Passport account. And that assurance came yesterday from &lt;a href=&quot;https://en.wikipedia.org/wiki/Nat_Friedman&quot;&gt;Nat Friedman&lt;/a&gt;, the future CEO of Github &lt;a href=&quot;https://www.reddit.com/r/AMA/comments/8pc8mf/im_nat_friedman_future_ceo_of_github_ama/&quot;&gt;in his Reddit AMA&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Firstly, the fact that Github Inc. was going in the hands of an open source veteran who had contributed to GNOME and MONO projects in the past was itself quite reassuring. At least, Github isn’t going to be controlled by a typical corporate honcho who has nothing but shareholders’ interests in mind! &lt;a href=&quot;https://www.reddit.com/r/AMA/comments/8pc8mf/im_nat_friedman_future_ceo_of_github_ama/e0a5e3r/&quot;&gt;Nat assured most redditors&lt;/a&gt; that Github is going to stay as it is, its developer focus is not going to be shifted, nor is it going to be integrated with any other products. And most importantly, they will never require a Microsoft account to login to Github, rather, their other products might consider allowing a Github user to authenticate to their systems in future:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We love GitHub login. Your GitHub account is your developer identity, and many users are accustomed to signing into developer tools and services (e.g. Travis, Circle) with their GitHub accounts. So, if anything, we may decide to add GitHub as a login option to Microsoft.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nat also &lt;a href=&quot;https://www.reddit.com/r/AMA/comments/8pc8mf/im_nat_friedman_future_ceo_of_github_ama/e0a6eh1/&quot;&gt;goes on to further assure&lt;/a&gt; redditors in the AMA that they will always remain a “developer first” company, and are keen to learn a lot from Github from this acquisition. Rather than impose their own work culture on Github, they’ll be taking lessons from Github and try and be like them:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We bought GitHub because we appreciate how special it is. That’s why we have two principles for this acquisition going forward:&lt;/p&gt;

  &lt;p&gt;Developers first. We will evaluate every decision through the lens of what is best for developers. This includes GitHub’s status as an open platform with open APIs that any developer can use to extend GitHub’s functionality. And it includes our commitment that we will support developers on GitHub in their use of any language, any license, any operating system, any device, and any cloud.&lt;/p&gt;

  &lt;p&gt;Independence. We are not buying GitHub to turn it into Microsoft; we are buying GitHub because we believe in the importance of developers, and in GitHub’s unique role in the developer community. Our goal is to help GitHub be better at being GitHub, and if anything, to help Microsoft be a little more like GitHub.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All in all, this damage control or whatever Microsoft has done seems to have done the magic at least for the moment. People and projects seem to have stopped their exodus to Gitlab, though its difficult to say what could happen in the long term.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to use Bash+rsync to automate your periodical backups on Linux</title>
   <link href="https://prahladyeri.github.io/blog/2018/03/how-to-use-bashrsync-to-automate-your-periodical-backups-on-linux.html"/>
   <updated>2018-03-04T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2018/03/how-to-use-bashrsync-to-automate-your-periodical-backups-on-linux</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;Linux&lt;/strong&gt; is all about simplicity. Simple and time-tested tools like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iptables&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;netstat&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rsync&lt;/code&gt; can be called for help for basic tasks instead of untrusted third-party tools as happens in case of Windows. If you are a desktop user, then taking frequent backups of your data to a pen-drive or external disk is a typical problem to solve.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;In this article, I’ll show how I solved this problem using a combination of &lt;strong&gt;bash scripting&lt;/strong&gt; and &lt;strong&gt;rsync&lt;/strong&gt;, the basic tooling available on any linux distro these days, be it Ubuntu, Debian or Fedora.&lt;/p&gt;

&lt;p&gt;One of the things you may want to do is determine what folder(s) you want to backup to which device. You may want to copy the source code folder only to your pen drive and your images and documents to only external drive, for instance. Here is where identifying the disk label through bash comes in handy in the backup script:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if [ -d &quot;/media/prahlad/DATA128&quot; ]; then
	device_name=&quot;DATA128&quot;
	folder_name=&quot;/media/prahlad/DATA128&quot;
elif [ -d &quot;/media/prahlad/extHD&quot; ]; then
	device_name=&quot;extHD&quot;
	folder_name=&quot;/media/prahlad/extHD&quot;
else
	echo &quot;No Drive Found&quot;
	exit
fi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the above example, &lt;strong&gt;DATA128&lt;/strong&gt; is a pen-drive and &lt;strong&gt;extHD&lt;/strong&gt; is an external hard-drive and the above script determines which disk is inserted in the USB drive. You can then use the &lt;strong&gt;$folder_name&lt;/strong&gt; bash variable to dynamically copy to that device instead of hard-coding that path unnecessarily. You can also use &lt;strong&gt;$device_name&lt;/strong&gt; bash variable to include or skip specific folders when running the rsync command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rsync -va /home/prahlad/source &quot;$folder_name/source&quot;

if [ &quot;$device_name&quot; = &quot;extHD&quot; ]; then
 rsync -va /home/prahlad/Pictures &quot;$folder_name/home/Pictures&quot;
 rsync -va /home/prahlad/Documents &quot;$folder_name/home/Documents&quot;
fi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;rsync&lt;/strong&gt; command itself is also versatile enough to do a lot of things which are not possible using a simple copy-paste-replace using a file manager. For instance, the “a” or “–archive” option intelligently archives (copies) files while skipping identical ones based on checksum or modification date automatically. Further, the “–delete” option deletes files which are present on the destination backup device, but not on the source device which is typically the case when you want to backup your data. Run “man rsync” to see the full range of options exposed by this wonderful command.&lt;/p&gt;

&lt;p&gt;Finally, another advantage of using a script for backup automation is that you can implement custom actions through the script. For example, compressing the mozilla firefox user folder before taking its backup:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;echo &quot;backing up firefox...&quot;
tar czf ~/firefox-backup.tar.gz ~/.mozilla/firefox/
rsync -va ~/firefox-backup.tar.gz &quot;$folder_name/home/firefox-backup.tar.gz&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Below is a typical example of how you might implement a script to automate backup of your home folder and set this as a cron job to run say weekly or fortnightly:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/sh&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/media/prahlad/DATA128&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
 &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;device_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;DATA128&quot;&lt;/span&gt;
 &lt;span class=&quot;nv&quot;&gt;folder_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/media/prahlad/DATA128&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/media/prahlad/extHD&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
 &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;device_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;extHD&quot;&lt;/span&gt;
 &lt;span class=&quot;nv&quot;&gt;folder_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/media/prahlad/extHD&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else
 &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;No Drive Found&quot;&lt;/span&gt;
 &lt;span class=&quot;nb&quot;&gt;exit
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fi

&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Device: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$device_name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Folder: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$folder_name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#start copying &lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#home&lt;/span&gt;
rsync &lt;span class=&quot;nt&quot;&gt;-va&lt;/span&gt; ~/.bashrc &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$folder_name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/home/&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--delete&lt;/span&gt;
rsync &lt;span class=&quot;nt&quot;&gt;-va&lt;/span&gt; ~/.profile &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$folder_name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/home/&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--delete&lt;/span&gt;
rsync &lt;span class=&quot;nt&quot;&gt;-va&lt;/span&gt; ~/Downloads/ &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$folder_name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/home/Downloads/&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--delete&lt;/span&gt;
rsync &lt;span class=&quot;nt&quot;&gt;-va&lt;/span&gt; ~/Documents/ &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$folder_name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/home/Documents/&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--delete&lt;/span&gt;
rsync &lt;span class=&quot;nt&quot;&gt;-va&lt;/span&gt; ~/Pictures/ &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$folder_name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/home/Pictures/&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--delete&lt;/span&gt;
rsync &lt;span class=&quot;nt&quot;&gt;-va&lt;/span&gt; ~/.ssh/ &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$folder_name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/home/.ssh/&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--delete&lt;/span&gt;
rsync &lt;span class=&quot;nt&quot;&gt;-va&lt;/span&gt; ~/.gnupg/ &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$folder_name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/home/.gnupg/&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--delete&lt;/span&gt;
rsync &lt;span class=&quot;nt&quot;&gt;-va&lt;/span&gt; ~/.thunderbird/ &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$folder_name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/home/.thunderbird/&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--delete&lt;/span&gt;
rsync &lt;span class=&quot;nt&quot;&gt;-va&lt;/span&gt; ~/.mozilla/ &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$folder_name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/home/.mozilla/&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--delete&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Done&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can customize the above script by adding custom actions or if conditions to skip or include specific folders based on the device label.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Tux Drive - A Command Line Tool to Access Google Drive from Linux</title>
   <link href="https://prahladyeri.github.io/blog/2017/09/introducing-tuxdrive-a-command-line-tool-to-access-google-drive-from-linux.html"/>
   <updated>2017-09-20T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2017/09/introducing-tuxdrive-a-command-line-tool-to-access-google-drive-from-linux</id>
   <content type="html">&lt;p&gt;One of the most boring things we need to perform in life is using the &lt;a href=&quot;https://drive.google.com/drive/&quot;&gt;Google Drive&lt;/a&gt;. On one hand, so useful is this tool provided by Google (so many cloud GBs for free, yay!) but on the other hand, the web interface to access those files is not quite as intuitive, especially so for power users, and certainly so for linux users!&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Unlike the good old FTP/SFTP where you could just run some geeky commands in a terminal (or use a super-intuitive GUI like FileZilla to just drag and drop files), the Google Drive web interface results in a crippled work flow. To top it, we don’t even have a desktop client like the Windows folks do.&lt;/p&gt;

&lt;h1 id=&quot;tux-drive&quot;&gt;Tux Drive&lt;/h1&gt;

&lt;p&gt;To solve this problem, I have written a console based python program called &lt;a href=&quot;https://github.com/prahladyeri/tuxdrive&quot;&gt;&lt;strong&gt;tuxdrive&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/prahladyeri/tuxdrive/master/screenshot.png&quot; alt=&quot;Tux Drive running on Ubuntu 16.04 LTS&quot; /&gt;{width=”687” height=”302”}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tux Drive running on Ubuntu 16.04 LTS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Its nowhere as comprehensive as FTP/SFTP in flexibility (yet), but it does some basic things like &lt;strong&gt;&lt;em&gt;ls (or dir)&lt;/em&gt;&lt;/strong&gt; to list remote files and directories on your drive, &lt;strong&gt;&lt;em&gt;get file_name&lt;/em&gt;&lt;/strong&gt; to download a file, &lt;strong&gt;&lt;em&gt;cd remote_dir&lt;/em&gt;&lt;/strong&gt; to change the drive directory, etc.&lt;/p&gt;

&lt;p&gt;Here is the entire list of vocabulary that &lt;strong&gt;tuxdrive&lt;/strong&gt; is currently acquainted with:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; help (or ?): Shows this help facility.
 dir (or ls): Lists all files and folders on drive.
 !dir (or !ls): Lists all files and folders in current directory.
 get (or pull) &amp;lt;item&amp;gt;: Pulls the named file/folder from drive to current working directory.
 put (or push) &amp;lt;item&amp;gt;: Pushes the named file/folder from current working directory to drive.
 rm &amp;lt;item&amp;gt;: Delete the named file/folder on remote path.
 pwd: Print working directory (remote/drive).
 cd: Change working directory (remote/drive).
 lpwd: Print working directory (local).
 lcd: Change working directory (local).
 rdcache: Show remote directory mapping of id and folder paths.
 rfcache: Show remote files mapping of id and folder paths.
 mkdir: Create a directory on remote path.
 exit: Exits this program.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instructions to download and run &lt;strong&gt;tuxdrive&lt;/strong&gt; can be found on the &lt;a href=&quot;https://github.com/prahladyeri/tuxdrive&quot;&gt;project github&lt;/a&gt;. I sincerely hope this tool becomes useful to as many users as possible. If something good comes out of this, I’ll think about integrating dropbox too into this in future.&lt;/p&gt;

&lt;p&gt;Have a Good Day.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How To Make Your Ubuntu Desktop Faster</title>
   <link href="https://prahladyeri.github.io/blog/2017/09/how-to-trim-your-new-ubuntu-installation-of-extra-fat-and-make-it-faster.html"/>
   <updated>2017-09-19T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2017/09/how-to-trim-your-new-ubuntu-installation-of-extra-fat-and-make-it-faster</id>
   <content type="html">&lt;p&gt;The usual advice you get when seeking a more resource conserving distro is to use a lighter spin-off such as Xubuntu, Lubuntu, etc. However, not many people know that even a “heavy” distro such as Ubuntu LTS with the default Unity Desktop could be made much lighter by uninstalling some packages and removing others from the startup. Let us see how it can be achieved:&lt;/p&gt;

&lt;h2 id=&quot;remove-unwanted-items-from-startup-applications&quot;&gt;Remove unwanted items from Startup Applications&lt;/h2&gt;

&lt;p&gt;When you go to the Ubuntu Dash and start the “&lt;strong&gt;Startup Applications&lt;/strong&gt;” dialog, it doesn’t show you the whole picture. There are still many “hidden apps” that silently start in the background without you knowing it. In order for all these hidden apps to be displayed in that dialog, you’ll have to run the following command in the terminal once:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo sed -i &apos;s/NoDisplay=true/NoDisplay=false/g&apos; /etc/xdg/autostart/*.desktop
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once you run this and then open the “Startup Applications” dialog, you’ll be able to see the hidden apps too:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2017/09/startup_applications.png&quot; alt=&quot;Startup Applications (Ubuntu)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Startup Applications (Ubuntu)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From this dialog, you can disable an app from starting up by “unchecking” it. The most important that I typically uncheck on a new installation are &lt;strong&gt;Backup Monitor&lt;/strong&gt; and &lt;strong&gt;Desktop Sharing&lt;/strong&gt;. It doesn’t make sense to have such a huge backup schizophrenia on a non-production desktop computer, so I wonder why the Backup Monitor is enabled in the first place. About 99% of sysadmins either perform a manual backup periodically, or write an automation script use something like &lt;strong&gt;rsync&lt;/strong&gt; which is specifically designed for the job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Desktop Sharing&lt;/strong&gt; is an equally unnecessary tool to have been enabled by default on a linux desktop. This ain’t the Windows world where people use Remote Desktop clients to connect to other PCs on the same network. About 99% of linux folks use &lt;strong&gt;ssh&lt;/strong&gt; to connect to remote machines instead. Desktop Sharing should be an opt-in feature in any case and a user who specifically needs it can enable it when needed.&lt;/p&gt;

&lt;h2 id=&quot;remove-unnecessary-background-services-such-as-cups-and-avahi-daemon&quot;&gt;Remove unnecessary background services such as cups and avahi-daemon&lt;/h2&gt;

&lt;p&gt;On a fresh Ubuntu installation, run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo netstat -antpe&lt;/code&gt; and you’ll see the rarely used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cupsd&lt;/code&gt; daemon (&lt;em&gt;common unix printing service&lt;/em&gt;) running on a free TCP port and  silently leeching your memory and network resources. Similarly, you can run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo netstat -anupe&lt;/code&gt; to scan for UDP ports and you’ll similarly see the cups browser daemon (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cups-browsed&lt;/code&gt;) and the avahi daemon (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;avahi-daemon&lt;/code&gt;) services.&lt;/p&gt;

&lt;p&gt;Use of printers has become less and less, especially since the oncoming of digital age and internet revolution. Heck, even companies have been encouraging email communication these days in a bid to avoid paper work and save trees, and here we are - one of the top linux distributions running a printer service by default!&lt;/p&gt;

&lt;p&gt;My humble request to them is please make this an opt-in feature, those who need it will have enough sense to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo apt install cups&lt;/code&gt;. Until that happens, the rest of the folks can do the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo systemctl disable cupsd
sudo systemctl disable cups-browsed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We are not uninstalling it, just disabling the service just in case. And before you say that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cups&lt;/code&gt; is needed for PDF exports from browsers, then no, the latest versions of firefox and chrome come with their own PDF extensions and don’t depend on this service anymore.&lt;/p&gt;

&lt;p&gt;As for the other service (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;avahi-daemon&lt;/code&gt;), it really doesn’t make any sense as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;avahi&lt;/code&gt; is the linux implementation of Apple’s proprietary protocols for peer-to-peer communication between their iDevices. Nobody in their right minds would be using an open source OS like Ubuntu on their PC/Laptop to communicate with proprietary Apple products! The two just don’t play well together as Apple products only work in a closed eco-system.&lt;/p&gt;

&lt;p&gt;As for those rare folks who absolutely need avahi-daemon, let them install it themselves, why include this by default? Until that happens, the rest of us can disable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;avahi-daemon&lt;/code&gt; by simply running:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo systemctl disable avahi-daemon
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;disable-hud-if-you-dont-use-it&quot;&gt;Disable HUD (if you don’t use it)&lt;/h2&gt;

&lt;p&gt;One of the reasons why Xubuntu has such a low memory footprint (~200 MB at idle) is that it doesn’t come with memory leeching services such as the HUD. For example, on my laptop, HUD service eats a good 30MB of RAM which is too much for a service that I don’t even use. As &lt;a href=&quot;https://askubuntu.com/a/218073/49938&quot;&gt;described in this post&lt;/a&gt;, you can disable the HUD service from the terminal as follows:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo chmod -x /usr/lib/indicator-appmenu/hud-service # 32bit systems OR
sudo chmod -x /usr/lib/x86_64-linux-gnu/hud/hud-service # 64bit systems
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;disable-evolution-processes-if-you-use-thunderbird-or-anything-else-instead&quot;&gt;Disable evolution processes (if you use thunderbird or anything else instead)&lt;/h2&gt;

&lt;p&gt;Evolution processes(such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;evolution-calendar-factory&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;evolution-addressbook-factory&lt;/code&gt;) are huge memory leechers and a drain on your RAM. If you just start the system monitor and search for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;evolution&lt;/code&gt;, you’ll find 4-5 different processes consuming a good 120MB of your RAM! Now, for someone who doesn’t even use the evolution email client or any of these services, why should they be there in the first place? But unfortunately, you cannot just remove (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uninstall&lt;/code&gt;) these packages using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt&lt;/code&gt;. Trying to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt remove evolution-data-server&lt;/code&gt; will break your system as they’ve made it a core part of the desktop. What you can do instead (if you really want to claim back that RAM) is a &lt;a href=&quot;https://askubuntu.com/a/816353/49938&quot;&gt;workaround suggested in this post&lt;/a&gt;. Simply rename the folders as follows:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo mv /usr/lib/evolution-data-server /usr/lib/evolution-data-server-disabled
sudo mv /usr/lib/evolution /usr/lib/evolution-disabled
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Optionally, a less dirty hack is to remove the executable flag from the individual processes, so that they won’t start. This way, the processes won’t start again if you update your core packages in future.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo chmod -x /usr/lib/evolution/evolution-calendar-factory # less dirty hack
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;remove-gnome-software-center-only-if-you-dont-use-it&quot;&gt;Remove GNOME software center (only if you don’t use it)&lt;/h2&gt;

&lt;p&gt;This piece of junk (gnome software center) takes a good 70-80MB on any typical ubuntu installation. For managing software, I find the good old &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt install&lt;/code&gt; way to be much more intuitive than the gnome software center. For those rare cases when you absolutely need a GUI, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;synaptic package manager&lt;/code&gt; works absolutely fine and without taking a constant toll on your resources.&lt;/p&gt;

&lt;p&gt;As described in &lt;a href=&quot;https://askubuntu.com/a/783075/49938&quot;&gt;this answer&lt;/a&gt;, all it takes to safely remove gnome software center from your system is:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo apt purge gnome-software
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;After trimming the programs and services as mentioned above, your system should become nearly as light-weight as those other lighter distros like xubuntu, lubuntu, etc. and you’ll still be able to enjoy and work with a richer and more visually pleasant user interface!&lt;/p&gt;

&lt;p&gt;On my own laptop, I was able to reduce the idle memory consumption from ~550MB to ~300MB which is as close as it gets to ubuntu-mate!&lt;/p&gt;

&lt;h2 id=&quot;update&quot;&gt;Update&lt;/h2&gt;

&lt;p&gt;As of 18.04 LTS, Ubuntu has replaced gnome software center with a new piece of crap called “snappy” which is no better than its predecessor! To remove snappy, just follow this process:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#check what snaps are installed
snap list

#remove all snaps
sudo apt purge snapd

#delete the snap folder
rm -rf snap 

#optionally replace snaps with standard apps
sudo apt install gnome-calculator gnome-logs gnome-characters gnome-system-monitor 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I still don’t understand why ubuntu keeps shipping this extra needless junk like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cups&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;avahi&lt;/code&gt; with each new release (considering its useless for the average or most typical user out there). My best guess is they do this so that the user googles for ways to remove it and thus improve their linux skills!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Reference:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://askubuntu.com/questions/210387/how-can-i-disable-hud-service&quot;&gt;https://askubuntu.com/questions/210387/how-can-i-disable-hud-service&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://askubuntu.com/a/816353/49938&quot;&gt;https://askubuntu.com/a/816353/49938&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Effortlessly import Disqus comments into WordPress: a step-by-step guide</title>
   <link href="https://prahladyeri.github.io/blog/2017/09/demystifying-comments-migration-from-disqus-to-wordpress.html"/>
   <updated>2017-09-07T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2017/09/demystifying-comments-migration-from-disqus-to-wordpress</id>
   <content type="html">&lt;h2 id=&quot;why-migrate-from-disqus-to-wordpress&quot;&gt;Why Migrate from Disqus to WordPress?&lt;/h2&gt;

&lt;p&gt;Some time ago, I had switched from a self-hosted WordPress blog to a statically generated (Jekyll) blog hosted on Github Pages. For a commenting system, Disqus was quite an easy choice at that time since it was zero hassle for us site owners, and Disqus did all the heavy lifting from filtering the comments to storing and displaying them.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2017/09/pexels-photo-262508.jpeg&quot; alt=&quot;Blog&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But as time went on, I started realizing that implementing a static blog was not quite the right thing. Firstly, there were &lt;a href=&quot;https://en.wikipedia.org/wiki/Disqus#Criticism_and_privacy_concerns&quot;&gt;privacy issues&lt;/a&gt; around Disqus because of which many readers of my blog were discouraged from commenting. Secondly, the concept of a “static site” itself felt quite constraining to me as I couldn’t implement things like contact form or a questionnaire to interact with my viewers. As a result, I decided to switch back to a plain old self-hosted WordPress blog.&lt;/p&gt;

&lt;h2 id=&quot;migrating-jekyll-posts-to-wordpress&quot;&gt;Migrating Jekyll posts to WordPress&lt;/h2&gt;

&lt;p&gt;Now, importing the posts was quite straightforward using the Jekyll generated RSS feed link that was pretty straightforward to use. In case you don’t have it in your Jekyll blog, it’s very easy to write one using liquid template. Just create a file named &lt;strong&gt;rss.xml&lt;/strong&gt; in your root folder with below contents:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;rss&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns:atom=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.w3.org/2005/Atom&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;channel&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;{{ site.name | xml_escape }} - Articles&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;description&amp;gt;&lt;/span&gt;{% if site.description %}{{ site.description | xml_escape }}{% endif %}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;link&amp;gt;&lt;/span&gt;
 {{ site.url }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/link&amp;gt;&lt;/span&gt;
 {% for post in site.posts %}
 {% unless post.link %}
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;item&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;{{ post.title | xml_escape }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;description&amp;gt;&lt;/span&gt;{{ post.content | xml_escape }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;pubDate&amp;gt;&lt;/span&gt;{{ post.date | date: &quot;%a, %d %b %Y %H:%M:%S %z&quot; }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/pubDate&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;link&amp;gt;&lt;/span&gt;
 {{ site.url }}{{ post.url }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/link&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;guid&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;isPermaLink=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;{{ site.url }}{{ post.url }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/guid&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
 {% endunless %}
 {% endfor %}
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;/channel&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/rss&amp;gt;&lt;/span&gt;

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

&lt;p&gt;Once you generate the blog, you can import all posts into your WordPress by referring to the /rss.xml url on your existing Jekyll blog.&lt;/p&gt;

&lt;h2 id=&quot;importing-disqus-comments&quot;&gt;Importing Disqus Comments&lt;/h2&gt;

&lt;p&gt;However, the bigger issue here was importing the Disqus comments, becuase while Disqus does allow you to &lt;a href=&quot;https://help.disqus.com/customer/portal/articles/472149-comments-export&quot;&gt;export a dump of your site comments&lt;/a&gt;, their XML format is pretty weird and isn’t the standard one used by WordPress and other blogging systems, as a result of which there aren’t too many ready tools for importing comments from this format to any other system.&lt;/p&gt;

&lt;p&gt;As a result, I had to write my own WordPress importer tool. Since I did not want to go through the hassle of learning to create an “admin plugin” with all the bells and whistles, I decided to write a simple PHP console script to import the XML as mentioned &lt;a href=&quot;https://wordpress.stackexchange.com/a/76466/52396&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All I needed were two scripts: A parser script to parse the XML output of Disqus comments dump, and secondly, a WordPress handler that loops through these comments and imports them one by one by matching the post’s url attribute and running &lt;strong&gt;wp_new_comment()&lt;/strong&gt; to insert the comment (you can also use the older &lt;strong&gt;wp_insert_comment()&lt;/strong&gt;, but its not the recommended way according to the WordPress codex).&lt;/p&gt;

&lt;p&gt;Below is the source code for both these files. First one, &lt;strong&gt;console.php&lt;/strong&gt; is the WordPress handler that you need to run, passing the path of the Disqus comments dump file. And &lt;strong&gt;disqus_parse.php&lt;/strong&gt; is the parser which is called internally by console.php. You need to copy these two files anywhere inside your WP folder structure (I copied them to &lt;strong&gt;/wp-content/plugins/test/&lt;/strong&gt; folder), and run the console.php from the command line:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;console.php:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://gist.github.com/prahladyeri/e22e4e232416ff841be670601b396c62&quot;&gt;https://gist.github.com/prahladyeri/e22e4e232416ff841be670601b396c62&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;disqus_parse.php&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://gist.github.com/prahladyeri/d1e19d8a6d0c7ff23fe3e15f9050b6d3&quot;&gt;https://gist.github.com/prahladyeri/d1e19d8a6d0c7ff23fe3e15f9050b6d3&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;running-the-script&quot;&gt;Running the script&lt;/h2&gt;

&lt;p&gt;Finally, just keep one thing in mind before running the console.php. Your WordPress system might throw an exception in case it detects too many comments being inserted in a loop. To suppress that exception, you need to add the following line of code to the end of your theme’s &lt;strong&gt;functions.php&lt;/strong&gt; to disable the comment flood filter:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;add_filter(&apos;comment_flood_filter&apos;, &apos;__return_false&apos;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course, remember to comment that line once you are done importing the comments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt; &lt;a href=&quot;/blog/2020/03/comments-migration-from-disqus-to-wordpress.html&quot;&gt;Also check out this recent post which has further fixes and refactoring done to the console.php script&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to install Debian+LXDE on ANY Android Tablet</title>
   <link href="https://prahladyeri.github.io/blog/2017/07/install-debian-lxde-on-tablet.html"/>
   <updated>2017-07-05T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2017/07/install-debian-lxde-on-tablet</id>
   <content type="html">&lt;p&gt;Running a linux distro on android devices is a hot topic these days, and why not? After all, android is already based on linux kernel, but a pretty much locked-down and dumbed-down version of it. The OEM doesn’t give you root and in most cases, not even an open source bootloader or kernel.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;That way, its good for maybe the most average user who doesn’t care about the OS and just want to use their phones. But for a power user, that’s not enough.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/2017/07/debian_xfce_tablet.jpg&quot;&gt;&lt;img src=&quot;/uploads/2017/07/debian_xfce_tablet.jpg&quot; alt=&quot;Debian (LXDE) running on Xiaomi MiPad&quot; /&gt;{.size-full .wp-image-553}&lt;/a&gt; &lt;strong&gt;Debian (LXDE) running on Xiaomi MiPad Tablet&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The so called apps available in the Play Store don’t allow you to utilize the full power of your linux device. A power user always wants to have the power of a full linux distro (such as Debian or Ubuntu or Fedora) on his/her device.&lt;/p&gt;

&lt;p&gt;For a long time, I researched for the best way to install a linux distro on a tablet, preferably one that didn’t involve rooting or partitioning the device. The &lt;a href=&quot;https://wiki.debian.org/ChrootOnAndroid&quot;&gt;debian guide&lt;/a&gt; lists down several methods and apps to do this such as Linux Deploy, GNURoot, Termux, etc. Out of them, the least risky and one that supports most android devices (including my KitKat tablet) is &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.gnuroot.debian&quot;&gt;GNURoot-Debian&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you install this app on your tablet, it creates a self-contained &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chroot&lt;/code&gt; install of debian using a tool called &lt;a href=&quot;https://wiki.archlinux.org/index.php/Proot&quot;&gt;Proot&lt;/a&gt;. Once that is done, all you need to do is open up the app terminal and just start using it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apt-get update
apt-get install tmux vim gcc python python3 python3-pip
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course, these are just some of the packages that I’ve installed, you can do whatever you want with your linux installation. The only limitation is regarding multiple user logins. By default, the app will login you as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt; and while you can create additional users using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useradd&lt;/code&gt; command, don’t expect things like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setuid&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setgid&lt;/code&gt; to work. The only way to switch user is using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;su - yourLogin&lt;/code&gt; command (whilst the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;login&lt;/code&gt; command should work too in theory, I’m having a few problems with it presently, it might need some fixing in /etc/pam.d/* configuration files).&lt;/p&gt;

&lt;p&gt;Further, if you have good amount of RAM on your tablet, you may consider using a desktop environment along with your headless installation (LXDE is recommended as it performs best on minimal resources). In order to do that, you’ll need two things:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=x.org.server&amp;amp;hl=en&quot;&gt;XServer-XSDL app&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;LXDE/XFCE desktop installed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The former is used to interact with the headless XServer installation of your debian and provide you a graphical desktop environment. For the latter, you’ll have to just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get install lxde&lt;/code&gt; (or lxde-core depending on your choice). Then, in order to use the desktop any time on your tablet:&lt;/p&gt;

&lt;p&gt;1) Open XServer-XSDL app, follow the instructions until you reach a blue screen.&lt;/p&gt;

&lt;p&gt;2) Go to the debian installation and run:&lt;br /&gt;
export DISPLAY=:0 PULSE_SERVER=tcp:127.0.0.1:4712&lt;br /&gt;
startlxde &amp;amp;&lt;/p&gt;

&lt;p&gt;3) Go back to XServer-XSDL app to interact with the desktop.&lt;/p&gt;

&lt;p&gt;If all goes well, you should be able to see an lxde desktop like the one shown in the above screen. Coupled with the keyboard case and a bunch of great linux apps (such as vim, emacs, geany, inkscape, eclipse, etc.), you should be able to convert your mobile tablet into a great development machine on wheels.&lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.xda-developers.com/guide-installing-and-running-a-gnulinux-environment-on-any-android-device/&quot;&gt;https://www.xda-developers.com/guide-installing-and-running-a-gnulinux-environment-on-any-android-device/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://wiki.debian.org/ChrootOnAndroid&quot;&gt;https://wiki.debian.org/ChrootOnAndroid&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.gnuroot.debian&quot;&gt;https://play.google.com/store/apps/details?id=com.gnuroot.debian&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=x.org.server&amp;amp;hl=en&quot;&gt;https://play.google.com/store/apps/details?id=x.org.server&amp;amp;hl=en&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://wiki.archlinux.org/index.php/Proot&quot;&gt;https://wiki.archlinux.org/index.php/Proot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Flask Recipe - RESTful CRUD using sqlalchemy</title>
   <link href="https://prahladyeri.github.io/blog/2017/06/flask-recipe-restful-crud-using-sqlalchemy.html"/>
   <updated>2017-06-25T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2017/06/flask-recipe-restful-crud-using-sqlalchemy</id>
   <content type="html">&lt;p&gt;RESTful apps are a thing these days. When your application’s userbase gets quite large and the client could vary from a laptop to an android device to an iOS device, it pays to keep the backend code separate and use the server only for making RESTful calls using HTTP methods that pertain to basic OLTP transactions: SELECT, INSERT, UPDATE and DELETE.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Popular third-party apps like &lt;a href=&quot;https://en.wikipedia.org/wiki/Firebase#Realtime_Database&quot;&gt;Firebase&lt;/a&gt; essentially provide you this same thing - A REST based front to a database that could be accessed online using simple HTTP methods. But in this tutorial, we will learn how to create such a backend ourselves using Python’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flask&lt;/code&gt; framework and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlalchemy&lt;/code&gt;, a light-weight but powerful ORM library that can access ANY database using its flexible &lt;em&gt;sql expression language&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/restful-crud.png&quot; alt=&quot;RESTful CRUD App&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Rather than using firebase, if you develop your own implementation of your back-end, not only will it help you learn and become a better programmer, but also give you a flexible solution that you can scale and change as per your needs. Its also much cheaper to host your own solution on Amazon EC2 (or Lambda) compared to other costlier alternatives.&lt;/p&gt;

&lt;p&gt;Contrary to popular thinking, its not very difficult to create a database agnostic backend such as the one represented in the above diagram. With a minimal and powerful web framework such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flask&lt;/code&gt;, combined with the power of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlalchemy&lt;/code&gt;, you can get up and running within minutes. In fact, I’ve developed a prototype version called &lt;a href=&quot;https://github.com/prahladyeri/tiddly&quot;&gt;Tiddly&lt;/a&gt; that essentially does the same thing as above using just 172 lines of Python code. You can refer to that github repository for reference as we proceed through this tutorial, or directly start using it. But make sure you install the following dependencies before running it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip install flask
pip install sqlalchemy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first step towards creating the app is creating your database models. Once you’ve done the brainstorming and decided what tables and fields you are going to need, you can create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;models.py&lt;/code&gt; source file with something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy import Column, String, Integer
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

#TODO: Change as needed:
engine = create_engine(&quot;sqlite:///tiddly.db&quot;, echo=True)
Base = declarative_base()

Session = sessionmaker(bind=engine)
dbsession = Session()

class User(Base):
	__tablename__ = &quot;user&quot;
	id = Column(Integer, primary_key=True)
	email = Column(String)
	password = Column(String)
	name = Column(String)
	def repr(self):
		return &quot;&amp;lt;User(name=%s, email=%s, )&amp;gt;&quot; % (name, email)
		
class Dual(Base):
	__tablename__ = &quot;dual&quot;
	id = Column(Integer, primary_key=True)
	text = Column(String)
	def repr(self):
		return &quot;&amp;lt;Dual(id=%s, text=%s, )&amp;gt;&quot; % (id, text)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’m using sqlite database for example here, but you can use any one of your choice. A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user&lt;/code&gt; table is a pretty basic one in almost every app as it is used for authentication. Apart from that, I’ve also created a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dual&lt;/code&gt; table just to play around with.&lt;/p&gt;

&lt;p&gt;After that, create the second file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app.py&lt;/code&gt; that contains our application code. Define the following import statements along with your models as they will come very handy:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import flask
from flask import request, jsonify, session
import sqlalchemy
from sqlalchemy import inspect, desc
import json
import models
from models import engine, dbsession
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, the only thing that remains to be done is the plumbing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HTTP&lt;/code&gt; methods to their respective database operations. You can either create a separate view function for each one or use a single one for all of them. In this example, I’m using a single function for simplicity.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@app.route(&quot;/&amp;lt;table_name&amp;gt;&quot;, methods=[&quot;POST&quot;, &quot;PUT&quot;, &quot;DELETE&quot;, &quot;FETCH&quot;])
def fetch(table_name):
	print(&quot;verb: %s, tablename: %s&quot; % (request.method, table_name))
	if request.method == &quot;POST&quot; or request.method == &quot;PUT&quot;:
		data = request.get_json(force=True)
		print(&quot;data:&quot;, data)
		try:
			TableClass = models.get_class_by_tablename(table_name)
			if TableClass == None: raise Exception(&quot;Table not found: %s&quot; % table_name)
			if request.method == &quot;POST&quot;: #insert data
				object = TableClass(**data)
				dbsession.add(object)
				dbsession.commit()
			else: #update data
				object = dbsession.query(TableClass).filter_by(**{&quot;id&quot;:id}).first()
				if object == None: raise Exception(&quot;No data found.&quot;)
				#object.update(**data)
				for key in data.keys():
					setattr(object, key, data[key])
				#dbsession.add(object)
				dbsession.commit()
			return jsonify({
				&quot;status&quot;: &quot;success&quot;,
				&quot;id&quot;: object.id,
				})
		except Exception as e:
			return jsonify({
				&quot;status&quot;: &quot;error&quot;,
				&quot;error&quot;: str(e),
				})
	elif request.method == &quot;DELETE&quot;:
		try:
			TableClass = models.get_class_by_tablename(table_name)
			if TableClass == None: raise Exception(&quot;Table not found: %s&quot; % table_name)
			object = dbsession.query(TableClass).filter_by(**{&quot;id&quot;:id}).first()
			if object == None: raise Exception(&quot;No data found.&quot;)
			dbsession.delete(object)
			dbsession.commit()
			return jsonify({
				&quot;status&quot;: &quot;success&quot;,
				&quot;id&quot;: object.id,
				})
		except Exception as e:
			return jsonify({
				&quot;status&quot;: &quot;error&quot;,
				&quot;error&quot;: str(e),
				})
	elif request.method == &quot;FETCH&quot;:
		try:
			data = request.get_json(force=True)
			data = json.loads(data)
			print(&quot;data: &quot;, data)
			print(&quot;data-type: &quot;, type(data))
			TableClass = models.get_class_by_tablename(table_name)
			if TableClass == None: raise Exception(&quot;Table not found: %s&quot; % table_name)
			
			query = dbsession.query(TableClass).filter_by(**data[&apos;where&apos;])
			if &apos;orderby&apos; in data:
				for cname in data[&apos;orderby&apos;].split(&apos;,&apos;):
					reverse = False
					if cname.endswith(&apos; desc&apos;):
						reverse = True
						cname = cname[:-5]
					elif cname.endswith(&apos; asc&apos;):
						cname = cname[:-4]
					print(&quot;cname: &quot;, cname)
					column = getattr(TableClass, cname)
					if reverse: column = desc(column)
					query = query.order_by(column)
			if &apos;limit&apos; in data:
				query = query.limit(data[&apos;limit&apos;])
				query = query.offset(data[&apos;offset&apos;])
			object = query.all()
			data = [object_as_dict(t) for t in object]
			return jsonify({
				&quot;status&quot;: &quot;success&quot;, 
				&quot;data&quot;: data
				})
		except Exception as e:
			return jsonify({
				&quot;status&quot;: &quot;error&quot;,
				&quot;error&quot;: str(e),
				})
	else:
		return jsonify({
			&quot;status&quot;: &quot;error&quot;, &quot;error&quot;: &quot;Unrecognized verb.&quot;,
			})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve used a non-standard HTTP method, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FETCH&lt;/code&gt; for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT&lt;/code&gt; action. That’s because if you use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GET&lt;/code&gt; method, you aren’t allowed to actually post data (as in actual posting, don’t confuse with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST&lt;/code&gt; method) as per the HTTP specification. The other methods, viz &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PUT&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE&lt;/code&gt; are self-apparent and they stand for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UPDATE&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE&lt;/code&gt; actions respectively.&lt;/p&gt;

&lt;p&gt;As you can see, the app makes good use of the sql expression language of sqlalchemy to dynamically query any kind of data, not only using the usual &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;where&lt;/code&gt; clause, but also using ordering and pagination (limit/offset) parameters:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if &apos;orderby&apos; in data:
	for cname in data[&apos;orderby&apos;].split(&apos;,&apos;):
		reverse = False
		if cname.endswith(&apos; desc&apos;):
			reverse = True
			cname = cname[:-5]
		elif cname.endswith(&apos; asc&apos;):
			cname = cname[:-4]
		print(&quot;cname: &quot;, cname)
		column = getattr(TableClass, cname)
		if reverse: column = desc(column)
		query = query.order_by(column)
if &apos;limit&apos; in data:
	query = query.limit(data[&apos;limit&apos;])
	query = query.offset(data[&apos;offset&apos;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The front-end sends whatever it needs to the back-end using JSON format and the result is also in JSON. For example, the following JSON when posted to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/user&lt;/code&gt; endpoint using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FETCH&lt;/code&gt; method, returns the record from user table where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; field matches &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin&lt;/code&gt; and orders the results by email in descending order.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{&quot;where&quot;: {&quot;name&quot;:&quot;admin&quot;}, &quot;orderby&quot;: &quot;email desc&quot;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Adding the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;limit&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;offset&lt;/code&gt; clauses to the same can help the front-end with pagination.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{&quot;where&quot;: {&quot;name&quot;:&quot;admin&quot;}, &quot;orderby&quot;: &quot;email desc&quot;, &quot;limit&quot;:2, &quot;offset&quot;: 2}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Its also pretty trivial to implement user authentication with this design. I haven’t done it in this example for simplicity, but you can find it in the &lt;a href=&quot;https://github.com/prahladyeri/tiddly&quot;&gt;github code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All code in this tutorial and on github is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MIT&lt;/code&gt; licensed and free to use. So, enjoy coding, build your own RESTful CRUD app!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to create a Google Drive App in PHP</title>
   <link href="https://prahladyeri.github.io/blog/2017/01/how-to-create-google-drive-app-php.html"/>
   <updated>2017-01-02T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2017/01/how-to-create-google-drive-app-php</id>
   <content type="html">&lt;p&gt;This is the second article in the drive series for web programmers that aims to explain how to use the Google Drive API in your web applications to access files/folders on behalf of your logged-in users.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;One of the basic tasks here is to authenticate to google and access the drive on the user’s behalf once they grant permission to your app. This method of authentication is called &lt;a href=&quot;https://en.wikipedia.org/wiki/OAuth&quot;&gt;OAuth&lt;/a&gt; and is very much needed for implementing the drive api.&lt;/p&gt;

&lt;p&gt;However, a good documentation to implement this in a backend app, especially a php app is very much lacking. The so called &lt;a href=&quot;https://developers.google.com/drive/v3/web/quickstart/php&quot;&gt;quickstart for drive api&lt;/a&gt; and the web based example &lt;a href=&quot;https://developers.google.com/api-client-library/php/auth/web-app&quot;&gt;here&lt;/a&gt; show some example code, but what a lot of beginner programmers need is a step-by-step tutorial of how to go about doing it.&lt;/p&gt;

&lt;h3 id=&quot;i-register-a-google-app-by-visiting-the-google-api-console&quot;&gt;I: Register a google app by visiting the &lt;a href=&quot;https://console.developers.google.com/&quot;&gt;Google API console&lt;/a&gt;:&lt;/h3&gt;

&lt;p&gt;The way the latest version (V3) of drive API works is only through OAuth. It means you cannot put a password or API key inside your code and access the drive files. You need to register your backend app and generate OAuth credentials for the app, so that it can access the drive on the user’s behalf once the user grants permission to the app. So the first step is going to the &lt;a href=&quot;https://console.developers.google.com/&quot;&gt;Google API console&lt;/a&gt;, registering the app itself and generating OAuth credentials. The registration process is pretty straightforward, we just select “Create Project” from the dropdown and give a nice name for the project such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Drive Example App&lt;/code&gt; in our case.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/google-apis/drive_api_steps.png&quot; alt=&quot;Register Google App&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;ii-configure-the-credentials-and-download-the-client_idjson-file&quot;&gt;II: Configure the credentials and download the client_id.json file:&lt;/h3&gt;

&lt;p&gt;This is the credential file that validates to Google who you are (as a developer) and also your app that acts on your behalf. Download and save it as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_id.json&lt;/code&gt; in the same directory as your app.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/google-apis/configuration_steps_generic1.png&quot; alt=&quot;Configure Credentials&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;iii-write-your-back-end-app&quot;&gt;III: Write your back-end app:&lt;/h3&gt;

&lt;p&gt;First of all, you have to add the dependency of google-api php library to your project. If you are using composer, all you need to do is add this package to the composer.json:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;require&quot;: {
  &quot;google/apiclient&quot;: &quot;^2.0&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you don’t use composer, you can just download the latest version of library from &lt;a href=&quot;https://github.com/google/google-api-php-client&quot;&gt;their repo&lt;/a&gt;, and just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;require_once&lt;/code&gt; it in your code like this:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;require_once&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/path/to/google-api-php-client/vendor/autoload.php&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can follow this pattern for any kind of php project, be it based on Symfony, Laravel, CodeIgniter or even a pure php project. But this tutorial and code example is based on a pure php project.&lt;/p&gt;

&lt;p&gt;The first thing to do now is to handle the home page url (index.php).&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$client = new Google_Client();
$client-&amp;gt;setAuthConfig(&apos;client_id.json&apos;);
$client-&amp;gt;addScope(Google_Service_Drive::DRIVE);

if (file_exists(&quot;credentials.json&quot;)) {
	$access_token = (file_get_contents(&quot;credentials.json&quot;));
	$client-&amp;gt;setAccessToken($access_token);
	//Refresh the token if it&apos;s expired.
	if ($client-&amp;gt;isAccessTokenExpired()) {
		$client-&amp;gt;fetchAccessTokenWithRefreshToken($client-&amp;gt;getRefreshToken());
		file_put_contents($credentialsPath, json_encode($client-&amp;gt;getAccessToken()));
	}
	$drive_service = new Google_Service_Drive($client);
	$files_list = $drive_service-&amp;gt;files-&amp;gt;listFiles(array())-&amp;gt;getFiles(); 
	echo json_encode($files_list);
} else {
  $redirect_uri = &apos;http://&apos; . $_SERVER[&apos;HTTP_HOST&apos;] . &apos;/oauth2callback.php&apos;;
  header(&apos;Location: &apos; . filter_var($redirect_uri, FILTER_SANITIZE_URL));
} We first check whether we have the drive access credentials for the user locally stored in a file called credentials.json (not to be confused with client\_id.json we downloaded earlier which is for developer credentials). Again, we are assuming a single user scenario here. If your drive app needs to authenticate with multiple users, you’ll have to store separate credentials.json for each logged-in user in the database, and access that through a session or something.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Further, if credentials aren’t found locally, we direct them to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/oauth2callback.php&lt;/code&gt;, so google will authenticate them and send us the token for accessing the drive, and after that, we will put that token into the local file, credentials.json and redirect the user back to the index.php. Finally, we call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;listFiles()&lt;/code&gt; method that displays the list of all files and folders in that user’s drive. Here is the code for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oauth2callback.php&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$client = new Google_Client();
$client-&amp;gt;setAuthConfigFile(&apos;client_id.json&apos;);
$client-&amp;gt;setRedirectUri(&apos;http://&apos; . $_SERVER[&apos;HTTP_HOST&apos;] . &apos;/oauth2callback.php&apos;);
$client-&amp;gt;addScope(Google_Service_Drive::DRIVE); //::DRIVE_METADATA_READONLY

if (! isset($_GET[&apos;code&apos;])) {
  $auth_url = $client-&amp;gt;createAuthUrl();
  header(&apos;Location: &apos; . filter_var($auth_url, FILTER_SANITIZE_URL));
} else {
  $client-&amp;gt;authenticate($_GET[&apos;code&apos;]);
  $access_token = $client-&amp;gt;getAccessToken();
  file_put_contents(&quot;credentials.json&quot;, json_encode($access_token));
   
  $redirect_uri = &apos;http://&apos; . $_SERVER[&apos;HTTP_HOST&apos;] . &apos;/&apos;;
  header(&apos;Location: &apos; . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once you have the credentials locally (in the form of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;credentials.json&lt;/code&gt;), you can just use it to access the drive API. Thus, the result of this whole exercise is that only on first page load is the user redirected to google site to authenticate themselves. Once the app has the access token (credentials.json), its no longer required, the drive can be accessed directly by the app from then on. If all goes well, you should be able to see a screen such as this when you test this example app for the first time:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;img src=&quot;/uploads/old/google-apis/oauth_screen_generic.png&quot; alt=&quot;Google OAuth Screen&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ll leave the more comprehensive use of this API as an exercise to the reader who wants to develop a more fully featured app out of this. Click the below link to download the source for this example implementation from the Github repo:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/prahladyeri/php-drive-example/&quot;&gt;php_drive_example&lt;/a&gt;{.btn .btn-md .btn-success}&lt;/p&gt;

&lt;p&gt;Note:&lt;/p&gt;

&lt;p&gt;If you are getting an SSL certificate error while testing this on Windows, have a look at &lt;a href=&quot;http://stackoverflow.com/q/29822686/849365&quot;&gt;this&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to create a Google Drive App in Flask</title>
   <link href="https://prahladyeri.github.io/blog/2016/12/how-to-create-google-drive-app-python-flask.html"/>
   <updated>2016-12-29T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2016/12/how-to-create-google-drive-app-python-flask</id>
   <content type="html">&lt;p&gt;This is the first in a series of articles for web programmers that explain in detail about using the Google Drive API in your web applications to access files/folders on behalf of the users of your application. In my last project, I had to develop a python flask app for my users that required to access the files stored in their google drive account.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;The major challenge for me was to authenticate to google and access the drive on the user’s behalf once they grant permission to my app. This method of authentication is called &lt;a href=&quot;https://en.wikipedia.org/wiki/OAuth&quot;&gt;OAuth&lt;/a&gt; and is very much needed for implementing the drive api.&lt;/p&gt;

&lt;p&gt;However, a good documentation to implement this in a backend app, especially a python web-based app is very much lacking. The so called &lt;a href=&quot;https://developers.google.com/drive/v3/web/quickstart/python&quot;&gt;quickstart for drive api&lt;/a&gt; shows some example code, but what I needed was a step-by-step tutorial of how to go about doing it. Since I couldn’t find any such tutorial online, I thought about writing one myself.&lt;/p&gt;

&lt;h3 id=&quot;i-register-a-google-app-by-visiting-the-google-api-console&quot;&gt;I: Register a google app by visiting the &lt;a href=&quot;https://console.developers.google.com/&quot;&gt;Google API console&lt;/a&gt;:&lt;/h3&gt;

&lt;p&gt;The way the latest version (V3) of drive API works is only through OAuth. It means you cannot put a password or API key inside your code and access the drive files. You need to register your backend app and generate OAuth credentials for the app, so that it can access the drive on the user’s behalf once the user grants permission to the app. So the first step is going to the &lt;a href=&quot;https://console.developers.google.com/&quot;&gt;Google API console&lt;/a&gt;, registering the app itself and generating OAuth credentials. The registration process is pretty straightforward, you just select “Create Project” from the dropdown and give a nice name for your project such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Flask Drive Example App&lt;/code&gt; in our case.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/google-apis/drive_api_steps.png&quot; alt=&quot;Register Google App&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;ii-configure-the-credentials-and-download-the-client_idjson-file&quot;&gt;II: Configure the credentials and download the client_id.json file:&lt;/h3&gt;

&lt;p&gt;This is the credential file that validates to Google who you are (as a developer) and also your app that acts on your behalf. Download and save it as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_id.json&lt;/code&gt; in the same directory as the flask app.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/google-apis/configuration_steps.png&quot; alt=&quot;Configure Credentials&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;iii-write-your-back-end-app&quot;&gt;III: Write your back-end app:&lt;/h3&gt;

&lt;p&gt;The most important thing to know before building your app is to install these dependencies:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip install flask google-api-python-client
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can replace flask with django, pylons or any other framework you use, but this tutorial and code example is based on flask. The principle of accessing the drive api should still apply, so you should be able to make use of this code.&lt;/p&gt;

&lt;p&gt;The first thing to do is create a flask object and handle the home page url. It could in fact be any other url in your app, but in this example, I’ve used the home page url (/) to do the OAuth authentication on behalf of the logged in user.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;app = flask.Flask(__name__)

@app.route(&apos;/&apos;)
def index():
    credentials = get_credentials()
    if credentials == False:
        return flask.redirect(flask.url_for(&apos;oauth2callback&apos;))
    elif credentials.access_token_expired:
        return flask.redirect(flask.url_for(&apos;oauth2callback&apos;))
    else:
        print(&apos;now calling fetch&apos;)
        all_files = fetch(&quot;&apos;root&apos; in parents and mimeType = &apos;application/vnd.google-apps.folder&apos;&quot;, sort=&apos;modifiedTime desc&apos;)
        s = &quot;&quot;
        for file in all_files:
            s += &quot;%s, %s&amp;lt;br&amp;gt;&quot; % (file[&apos;name&apos;],file[&apos;id&apos;])
        return s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We first check whether we have the drive access credentials for the user locally stored in a file. This is done by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_credentials()&lt;/code&gt; function that checks the local access token file credentials.json (not to be confused with client_id.json we downloaded earlier which is for developer credentials). Again, we are assuming a single user scenario here. If your drive app needs to authenticate with multiple users, you’ll have to store separate credentials.json for each logged-in user in the database, and access that through a session or something.&lt;/p&gt;

&lt;p&gt;Further, if credentials aren’t found locally or have expired, we direct them to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/oauth2callback&lt;/code&gt;, so google will authenticate them and send us the token for accessing the drive, post which, we will put that token into the local file, credentials.json and redirect the user back to this index site. Finally, if the credentials are valid, we call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetch()&lt;/code&gt; function that displays the list of all root folders in that user’s drive along with their IDs. Here is the code for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oauth2callback&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@app.route(&apos;/oauth2callback&apos;)
def oauth2callback():
    flow = client.flow_from_clientsecrets(&apos;client_id.json&apos;,
            scope=&apos;https://www.googleapis.com/auth/drive&apos;,
            redirect_uri=flask.url_for(&apos;oauth2callback&apos;, _external=True)) # access drive api using developer credentials
    flow.params[&apos;include_granted_scopes&apos;] = &apos;true&apos;
    if &apos;code&apos; not in flask.request.args:
        auth_uri = flow.step1_get_authorize_url()
        return flask.redirect(auth_uri)
    else:
        auth_code = flask.request.args.get(&apos;code&apos;)
        credentials = flow.step2_exchange(auth_code)
        open(&apos;credentials.json&apos;,&apos;w&apos;).write(credentials.to_json()) # write access token to credentials.json locally
        return flask.redirect(flask.url_for(&apos;index&apos;))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once you have the credentials locally (in the form of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;credentials.json&lt;/code&gt;), you can just use it to access the drive API. Thus, the result of this whole exercise is that only on first page load is the user redirected to google site to authenticate themselves. Once the app has the access token (credentials.json), its no longer required, the result is displayed directly on the page from then on. If all goes well, you should be able to see a screen such as this when you test this example app for the first time:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/google-apis/oauth_screen.png&quot; alt=&quot;Google OAuth Screen&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I’ve also included the functions to download and upload files to the drive as they will be very useful:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;def download_file(file_id, output_file):
    credentials = get_credentials()
    http = credentials.authorize(httplib2.Http())
    service = discovery.build(&apos;drive&apos;, &apos;v3&apos;, http=http)
    #file_id = &apos;0BwwA4oUTeiV1UVNwOHItT0xfa2M&apos;
    request = service.files().export_media(fileId=file_id,mimeType=&apos;application/vnd.openxmlformats-officedocument.spreadsheetml.sheet&apos;)
    #request = service.files().get_media(fileId=file_id)
    
    fh = open(output_file,&apos;wb&apos;) #io.BytesIO()
    downloader = MediaIoBaseDownload(fh, request)
    done = False
    while done is False:
        status, done = downloader.next_chunk()
        #print (&quot;Download %d%%.&quot; % int(status.progress() * 100))
    fh.close()
    #return fh
    
def update_file(file_id, local_file):
    credentials = get_credentials()
    http = credentials.authorize(httplib2.Http())
    service = discovery.build(&apos;drive&apos;, &apos;v3&apos;, http=http)
    # First retrieve the file from the API.
    file = service.files().get(fileId=file_id).execute()
    # File&apos;s new content.
    media_body = MediaFileUpload(local_file, resumable=True)
    # Send the request to the API.
    updated_file = service.files().update(
        fileId=file_id,
        #body=file,
        #newRevision=True,
        media_body=media_body).execute()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;I’ll leave the more comprehensive use of these functions as an exercise to the reader who wants to develop a more fully featured app out of this. Click the below link to download the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flask_drive_example.py&lt;/code&gt; script for this example implementation from the Github gist:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://gist.githubusercontent.com/prahladyeri/0b92b9ca837a0f5474c732876220db78&quot;&gt; flask_drive_example.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>How to create a screencast in Xubuntu using ffmpeg</title>
   <link href="https://prahladyeri.github.io/blog/2016/07/how-to-create-screencast-xubuntu.html"/>
   <updated>2016-07-19T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2016/07/how-to-create-screencast-xubuntu</id>
   <content type="html">&lt;p&gt;In this article, I’m going to describe how to create a desktop screencast recording with or without audio in Xubuntu 16.04. With little changes, however, it should work on other distros too.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;The first thing to do is to ensure that your headphone microphone is enabled in Xubuntu audio settings. For some reasons, Xubuntu doesn’t seem to do this by default. Just click that tray icon top-right corner and click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sound Settings&lt;/code&gt;. Then click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Input Devices&lt;/code&gt; tab and select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Headset Microphone&lt;/code&gt; or something from the list, and unmute the device by clicking the grey color audio icon:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/xubuntu_mic_unmute.png&quot; alt=&quot;Xubuntu Mic Unmute&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And of course, you’ll need &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ffmpeg&lt;/code&gt; to do this, its a small command line audio utility in linux. Its only a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo apt-get&lt;/code&gt; away in case you don’t have it already. After that, all you have to do is run this command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ffmpeg -video_size 1024x767 -framerate 25 -f x11grab -i :0.0+0,0 -f alsa -ac 2 -i default output.mkv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To stop the screen recording, just press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+C&lt;/code&gt;. This command uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alsa&lt;/code&gt; module to record audio. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-ac 2&lt;/code&gt; parameter specifies the audio channel (stereo as opposed to mono). If you want, you can also specify an audio sampling rate using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-ar&lt;/code&gt; option which is usually 44100 by default. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-i default&lt;/code&gt; refers to the default available audio device. It usually works, but in case it doesn’t, you can specify the exact input device you want to use. In order to do that, run this command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;arecord -L
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And you will get an output such as this one:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dmix:CARD=PCH,DEV=0
	HDA Intel PCH, ALC3234 Analog
	Direct sample mixing device
dsnoop:CARD=PCH,DEV=0
	HDA Intel PCH, ALC3234 Analog
	Direct sample snooping device
hw:CARD=PCH,DEV=0
	HDA Intel PCH, ALC3234 Analog
	Direct hardware device without any conversions
plughw:CARD=PCH,DEV=0
	HDA Intel PCH, ALC3234 Analog
	Hardware device with all software conversions
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the above, you can see the plugged-in available sound card at the end. So you can take that device and specify it in this way instead of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default&lt;/code&gt; device:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ffmpeg -video_size 1024x767 -framerate 25 -f x11grab -i :0.0+0,0 -f alsa -ac 2 -i plughw:CARD=PCH,DEV=0 output.mkv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you don’t want audio in the screencast, however, then omit the alsa module and just run this instead:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ffmpeg -video_size 1024x767 -framerate 25 -f x11grab -i :0.0+0,0 output.mkv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve used other screencasting software in Linux before such as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RecordMyDesktop&lt;/code&gt; tool and the built-in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+Shift+Alt+R&lt;/code&gt; screencasting in GNOME. But this method of using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ffmpeg&lt;/code&gt; was what I found to be the most speedy and configurable. Try it and do let me know how your screencasting goes!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to Unbrick and revive a totally lifeless Xiaomi Mi-Pad</title>
   <link href="https://prahladyeri.github.io/blog/2016/07/how-to-unbrick-lifeless-xiaomi-mi-pad.html"/>
   <updated>2016-07-13T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2016/07/how-to-unbrick-lifeless-xiaomi-mi-pad</id>
   <content type="html">&lt;p&gt;Last week, my brand new Xiaomi Mi-Pad arrived via Flipkart and no sooner did I have it in my hands, I started installing useful apps like Greenify, ES File Explorer, etc. Two days went by and I was disappointed to learn that it doesn’t support a USB Dongle (Data Card) which is a very common method of Internet access in India. The only way I could have it was to root the tablet. And it was while rooting using SuperSU app that I foolishly selected a wrong click and ended up bricking my brand new tablet.&lt;!--more--&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;

&lt;p&gt;It was a &lt;em&gt;hard brick&lt;/em&gt;, meaning my tablet showed no signs of life when switched on or connected to a PC through a USB cable. Had it been a &lt;em&gt;soft brick&lt;/em&gt;, meaning that I were able to boot to MIUI Recovery by pressing Power+UpVolume or to FastBoot Recovery by pressing Power+DnVolume button, it would have been easy to recover. But my tablet was totally dead. Even after charging for next 6 hours, all it did was light up a black LED screen for a second or two and then vanished for any combination of those buttons.&lt;/p&gt;

&lt;p&gt;The problem was that the wrong click that I made had caused the tablet bootloader to be corrupted. And in the android world, if the bootloader is lost, then your device is literally as useful as a brick! Unlike 8086 PCs, android tablets don’t have a hard-wired BIOS to recover things from, the bootloader is as good as the BIOS.&lt;/p&gt;

&lt;h2 id=&quot;the-research&quot;&gt;The Research&lt;/h2&gt;

&lt;p&gt;Now obviously, I was disappointed and annoyed. After spending ₹10,999 on a brand new tablet, this was not what I was hoping for. Getting service from a Xiaomi service center was not an option as they are very rare in India. Besides, OEMs hardly provide warranty/support to people who try and root their devices, do they? So naturally, I had to myself find a way out of this. My research finally led me to a post by a Russian hacker who had successfully revived the tablet from this very situation. In this article, I’ll note down exactly what steps I had taken to revive it and hope that someone might find it useful.&lt;/p&gt;

&lt;h2 id=&quot;the-solution&quot;&gt;The Solution&lt;/h2&gt;

&lt;p&gt;Though the solution is easy and straight-forward, there are some tricky areas where you might run into trouble. If you don’t have the confidence to open up an electronic device using a screwdriver or flash a stock ROM, just say quits and if possible, ask help from someone you know who is used to do it. Basically, the process involves this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Open the back-cover by separating it from the screen (fingernails or a sharp object will do) and unscrew the plastic panel that covers the top area of the motherboard.&lt;/li&gt;
  &lt;li&gt;Disconnect the battery by slowly pulling the power belt (using a flat-head screwdriver or any sharp object).&lt;/li&gt;
  &lt;li&gt;Connect the USB cable to Mi-Pad end (but not the PC end).&lt;/li&gt;
  &lt;li&gt;Create a short circuit between the two tiny pads inside the MicroSD card slot using a metal object or wire.&lt;/li&gt;
  &lt;li&gt;Connect the USB cable to the PC end.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If done properly, the PC will detect a &lt;em&gt;Nvidia Boot Recovery for Mobile&lt;/em&gt; USB device and then you can flash your stock ROM using Xiaomi’s flashing software, Mi Flash Tool, thus putting the life back into your tablet.&lt;/p&gt;

&lt;h2 id=&quot;the-magic&quot;&gt;The Magic&lt;/h2&gt;

&lt;p&gt;The most tricky and magical aspect of this whole process is the short-circuiting in step four. Firstly, it is not easily diagnosable. The Nvidia USB driver is in fact detected by the PC even if you don’t create the short-circuit (but the battery needs to be disconnected of course), but then it will disappear instantly and you won’t get the time to flash your stock ROM. For some unknown laws of electricity (or maybe the laws coded inside this Nvidia driver), the short-circuit may not work instantly the first time. You’ll create the short-circuit, connect to the USB port and hear the Windows beep sound for USB connection, but moments later you will hear another beep of disconnection. If that happens, you’ll have to disconnect the USB cable and repeat from step-4: Create the short-circuit again and reconnect to the USB port - I had to do this about 50 times before the connection was stable enough for me to flash the ROM.&lt;/p&gt;

&lt;p&gt;Some forum posts advice you to plug in the power belt at exactly the same time as the USB connection is detected in the PC. However, I’ve found that its not true. Not once did I put the power belt back in the whole process until the ROM had been flashed completely.&lt;/p&gt;

&lt;h2 id=&quot;the-weapon&quot;&gt;The Weapon&lt;/h2&gt;

&lt;p&gt;You can use any metallic object to create the short circuit, even a tiny wire could be used. The important thing is that those two minuscule pads inside the MicroSD slot are connected by a metal. Personally, I used this tiny pin that came with the booklet along with the Mi-Pad itself!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/short.jpg&quot; alt=&quot;Short circuit on the two pads inside the MicroSD slot&quot; /&gt;{.size-large}&lt;/p&gt;

&lt;p&gt;All I did was place that pin inside the MicroSD slot, so it created a bridge on top of those two pads, and supported the pin using any available object (in this case a screwdriver).&lt;/p&gt;

&lt;p&gt;You’ll also need a 2.0mm screwdriver to open the back-panel above the motherboard. Please remember that opening those screws are going to void your warranty (which is going to happen in any case since you either tried to root or used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dd&lt;/code&gt; command to rewrite partitions in order to reach this stage!).&lt;/p&gt;

&lt;p&gt;On your Windows PC, you need the following installed:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Latest version of Mi Flash Tool (Can be downloaded &lt;a href=&quot;http://xiaomitips.com/download/miui-rom-flashing-tool/&quot;&gt;here&lt;/a&gt;).&lt;/li&gt;
  &lt;li&gt;Latest version of Mi Phone Manager PC Suite for the Nvidia drivers (Can be downloaded &lt;a href=&quot;http://en.miui.com/thread-92720-1-1.html&quot;&gt;here&lt;/a&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Alternatively, you can download the whole bundled software from &lt;a href=&quot;https://androidmtk.com/download-xiaomi-mi-flash-tool&quot;&gt;here&lt;/a&gt; that comes with Mi Flash Tool, NVidia Drivers and ADB Drivers too.&lt;/p&gt;

&lt;p&gt;You’ll also need to download the latest MIUI stock ROM for Mi-Pad from &lt;a href=&quot;http://en.miui.com/download-229.html&quot;&gt;here&lt;/a&gt; and extract somewhere on your PC to flash it later.&lt;/p&gt;

&lt;p&gt;Once your short-circuit works and the connection stays stable, waste no more time. Just fire up the Mi Flash tool and select the path to the ROM image you just extracted and click the flash button. Once the flash is successful, assemble back your Mi-Pad and on pressing the power button, you should see this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/MIUI.jpg&quot; alt=&quot;MI-Pad Recovered&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;Keep a cool mind and do it, it can be done! If you can’t do it, try and find a mobile repair shop nearby or catch a tech enthusiast in your circle who can do it.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://xiaomi.eu/community/threads/mipad-is-dead-brick.25162/page-3#post-228543&quot;&gt;Post by original Russian Hacker (BogNik) who did it first&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://xiaomitips.com/download/miui-rom-flashing-tool/&quot;&gt;Latest Mi Flash Tool&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://en.miui.com/thread-92720-1-1.html&quot;&gt;Latest Mi Phone Manager PC Suite&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://androidmtk.com/download-xiaomi-mi-flash-tool&quot;&gt;Latest Bundled Software - Flash Tool with Nvidia and ADB Drivers&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://en.miui.com/download-229.html&quot;&gt;Latest MIUI stock ROM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>How to deal with frequent disconnects of 3G USB Dongle on Linux Desktop</title>
   <link href="https://prahladyeri.github.io/blog/2016/06/how-to-deal-with-frequent-disconnects-of-3g-usb-dongle-on-linux-desktop.html"/>
   <updated>2016-06-28T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2016/06/how-to-deal-with-frequent-disconnects-of-3g-usb-dongle-on-linux-desktop</id>
   <content type="html">&lt;p&gt;One of the major issues on linux desktop these days has to do with 3G USB dongles/modems. In many countries like India, a USB dongle still remains a major way to access the Internet. The latest versions of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;network-manager&lt;/code&gt; has solved most of the issues relating to the basic recognition of these modems, but one major irritant still remains on Ubuntu 16.04 and a few other distros.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;It so happens that many a times, the connection entirely vanishes from the network-manager applet menu on the top right. For example, if you hibernate your laptop and then wake it, the menu is no longer there. However, if you manually take out the dongle and re-insert, the modem is recognized and the menu reappears. If you want to avoid that manual re-insertion and want to do a “soft reset” instead, here is what you can do:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Send a reset signal to your usb modem:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo usb_modeswitch -v 12d1 -p 1496 -R
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Send a modeswitch signal to your usb modem:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo usb_modeswitch -v 12d1 -p 1496 -J
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The parameters, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-v&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-p&lt;/code&gt; stand for vendor code and product code respectively and you can find them by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lsusb&lt;/code&gt; command. For example, the below entry is for vendor code &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;12d1&lt;/code&gt; (Huawei) and the product code is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1496&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Bus 001 Device 004: ID 12d1:1496 Huawei Technologies Co., Ltd. Broadband stick
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Please remember that for modeswitch signal, the parameter -J is vendor specific and works for Huawei modems only, please run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;man usb-modeswitch&lt;/code&gt; to find out your vendor specific modeswitch control parameter. For instance, its -O for Sony and -L for Cisco control parameter.&lt;/p&gt;

&lt;p&gt;Of course, once you try these commands and find that they are working, you can add them as a shortcut to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.bashrc&lt;/code&gt; file. Something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;alias &quot;reset_huawei=sudo usb_modeswitch -v 12d1 -p 1496 -R&quot;
alias &quot;modeswitch_huawei=sudo usb_modeswitch -v 12d1 -p 1496 -J&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, all you need to do is run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reset_huawei &amp;amp;&amp;amp; modeswitch_huawei&lt;/code&gt; and that’s it! No need to manually re-insert the dongle anymore.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>List of free online programming courses (MOOC) offering FREE certificates of accomplishment</title>
   <link href="https://prahladyeri.github.io/blog/2016/06/list-of-free-mooc-free-certificates-transcripts.html"/>
   <updated>2016-06-07T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2016/06/list-of-free-mooc-free-certificates-transcripts</id>
   <content type="html">&lt;p&gt;This post is inspired by the &lt;a href=&quot;https://courserajunkie.wordpress.com/2015/05/26/courseras-free-statements-of-accomplisments-die-a-quiet-death/&quot;&gt;Coursera Junkie post&lt;/a&gt; by Kathryn last year that happened to invite the ire of lots of learners interested in MOOC (Massive Online Open Courses). Whilst I was observing the comment chain in that post, I had never bothered to actually take the time to research any MOOC courses that offered free certificates or statements of accomplishment. But a few months ago, my once favorite online academy, &lt;a href=&quot;http://edx.org/&quot;&gt;edX.org&lt;/a&gt; had just crossed a line by starting to charge money for their certificates, thus shutting down the last zion of open education left for the learners. And because of this, I felt the need to start researching the alternatives.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Now, I know that lots of critics would be throwing this “free rider” argument against me saying that why should I bother about a few dollars worth if I’m getting a verified certificate, or that paying for a certificate somehow increases the “value” of the course, etc. However, you fail to understand that ultimately, the question is not about the certificate (which is just a piece of paper, anyways). It’s really about the learnings and insights about a subject that you take away from a MOOC course. It’s also about the collaboration and interaction with other co-students on the academy who take the course along with you. The PDF certificate or transcript is just a downloadable thing that you use to improve your LinkedIn profile, but more importantly, it’s an &lt;em&gt;acknowledgement&lt;/em&gt; by the academy that you underwent that course.&lt;/p&gt;

&lt;p&gt;Now, as an online academy, if you are charging money to give us that acknowledgement, you are no academy but just a profiteer who is running a business. The moment that money comes in the picture, an academy turns into a business and its education quality starts to deteriorate. Right to education, of all things, MUST be based on merit. You can’t bar someone’s entry to education based on the financial ability. This same concept of &lt;em&gt;merit&lt;/em&gt; also applies to Open Source software development. Every contributing developer is invited to the project on the basis of &lt;em&gt;merit&lt;/em&gt; alone. Further, every commit, every feature request and every decision is based on merit and voting, no company can buy its way through a FOSS project, the Linux kernel project is the proof of that!&lt;/p&gt;

&lt;p&gt;If Coursera and edX stopped being academies, and turned into profit-making entities, let them be - there is no dearth of online learning resources! Below are some MOOC courses that provide free transcripts or certificate of accomplishments upon completion (In future, if any of these academies turn out to be like Coursera/edX, rest assured, they will be removed from this list!):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=6&quot;&gt;CS101: Introduction to Computer Science I&lt;/a&gt; via &lt;em&gt;Saylor Academy&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=64&quot;&gt;CS102: Introduction to Computer Science II&lt;/a&gt; via &lt;em&gt;Saylor Academy&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=65&quot;&gt;CS107: C++ Programming&lt;/a&gt; via &lt;em&gt;Saylor Academy&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=66&quot;&gt;CS201: Elementary Data Structures&lt;/a&gt; via &lt;em&gt;Saylor Academy&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=67&quot;&gt;CS202: Discrete Structures&lt;/a&gt; via &lt;em&gt;Saylor Academy&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=71&quot;&gt;CS301: Computer Architecture&lt;/a&gt; via &lt;em&gt;Saylor Academy&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=73&quot;&gt;CS302: Software Engineering&lt;/a&gt; via &lt;em&gt;Saylor Academy&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=72&quot;&gt;CS303: Algorithms&lt;/a&gt; via &lt;em&gt;Saylor Academy&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=74&quot;&gt;CS304: Compilers&lt;/a&gt; via &lt;em&gt;Saylor Academy&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=75&quot;&gt;CS305 Web Development&lt;/a&gt; via &lt;em&gt;Saylor Academy&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=94&quot;&gt;CS401: Operating Systems&lt;/a&gt; via &lt;em&gt;Saylor Academy&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=84&quot;&gt;CS402: Computer Communications and Networks&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=93&quot;&gt;CS403: Introduction to Modern Database Systems&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=79&quot;&gt;CS404: Programming Languages&lt;/a&gt; via &lt;em&gt;Saylor Academy&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=96&quot;&gt;CS405: Artificial Intelligence&lt;/a&gt; via &lt;em&gt;Saylor Academy&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=92&quot;&gt;CS406: Information Security&lt;/a&gt; via &lt;em&gt;Saylor Academy&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=81&quot;&gt;CS408: Advanced Artificial Intelligence&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=90&quot;&gt;CS409: Cryptography&lt;/a&gt; via &lt;em&gt;Saylor Academy&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=91&quot;&gt;CS410: Advanced Databases&lt;/a&gt; via &lt;em&gt;Saylor Academy&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.saylor.org/course/view.php?id=95&quot;&gt;CS412: Mobile Applications Development&lt;/a&gt; via &lt;em&gt;Saylor Academy&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.udemy.com/learn-html5-programming-from-scratch/&quot;&gt;Learn HTML5 Programming From Scratch&lt;/a&gt; via &lt;em&gt;Udemy&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.open.edu.au/&quot;&gt;Courses from Open Universities, Australia&lt;/a&gt; via &lt;em&gt;open.edu.au&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.open2study.com/courses/concepts-in-game-development&quot;&gt;Concepts in Game Development&lt;/a&gt; via &lt;em&gt;open2study.com&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.guru99.com/compiler-design-tutorial.html&quot;&gt;Compiler Design Tutorial: What is, Types, Tools, Example&lt;/a&gt; via &lt;em&gt;guru99.com&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;Provision of free certificate transcripts/statement of accomplishments in each Academy:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;=&amp;gt; &lt;a href=&quot;https://sayloracademy.zendesk.com/hc/en-us/articles/210853008-About-course-completion-certificates&quot;&gt;Saylor Academy provides free certificate of completion upon passing the final exam of every course at learn.saylor.org&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A free certificate of completion is available for every course at learn.saylor.org by passing a course’s final exam with 70% or better. Your digital certificate can be shared privately or publicly, linked to from other websites, downloaded to a PDF file, and printed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;=&amp;gt; &lt;a href=&quot;https://support.udemy.com/customer/en/portal/articles/1497724-certificate-of-completion?b_id=11486&quot;&gt;Udemy offers certificate of completion for many of its courses&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Most courses offer a certificate of completion. When all lectures have been completed, a gold or green trophy will appear, signifying that the certificate of completion is ready for download. Click on the trophy to view the certificate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;=&amp;gt; &lt;a href=&quot;http://www.openuped.eu/mooc-features/47-recognition-options&quot;&gt;OpenupED has multiple recognition options. Apart from a free badge/certificate, some courses also offer official formal certificate by the university.&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Always a badge and/or a certificate. I.e. you have the opportunity to get a badge or a certificate of course completion for free (as evidence of completion).&lt;/p&gt;

  &lt;p&gt;FormalCertificateIn addition the majority of OpenupEd MOOCs provide the possibility to obtain a formal certificate&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;=&amp;gt; &lt;a href=&quot;http://online.stanford.edu/courses/allcourses&quot;&gt;Stanford.edu offers a statement of accomplishments for some of their “In Session” courses&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;In Session courses offer a Statement of Accomplishment issued by the instructor upon successful completion.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;=&amp;gt; &lt;a href=&quot;https://www.open2study.com/howitworks&quot;&gt;open2study.com offers a free downloadable certificate of accomplishment upon course completion&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Once the course finishes, you will be able to download a certificate of achievement, along with your final grades, via My Study Centre.&lt;/p&gt;
&lt;/blockquote&gt;
</content>
 </entry>
 
 <entry>
   <title>Ten useful LibreOffice Macro Recipes</title>
   <link href="https://prahladyeri.github.io/blog/2016/02/ten-libreoffice-macro-recipes.html"/>
   <updated>2016-02-26T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2016/02/ten-libreoffice-macro-recipes</id>
   <content type="html">&lt;p&gt;Macros are a great way to automate tasks in Spreadsheet applications, be it the good old Microsoft Excel or the equally efficient FOSS alternative, LibreOffice Calc. The best thing about macros is that they are often written in a very easy to grasp language called &lt;a href=&quot;https://en.wikipedia.org/wiki/BASIC&quot;&gt;Basic&lt;/a&gt; (though Calc also supports Python these days).&lt;/p&gt;

&lt;p&gt;As its very name suggests, Basic is a lenient programming language actually designed with ease of use in mind. For instance, upper/lower case doesn’t matter for variable names or keywords (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if/IF&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub/Sub&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;function/Function&lt;/code&gt; are equivalents), function braces are optional like Ruby and type-conversion happens automatically. This makes Basic highly useful for both power users and programmers. A LibreOffice Basic macro is just a function or sub procedure which does a specific useful task. In this tutorial, we will see ten such useful macros which can help you with various automation tasks.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;#howto&quot;&gt;Recipe 0: How to create a LibreOffice macro&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#readcell&quot;&gt;Recipe 1: Read cell contents&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#changecell&quot;&gt;Recipe 2: Change cell contents&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#searchandrepl&quot;&gt;Recipe 3: Search and Replace text&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#regex&quot;&gt;Recipe 4: Regular Expressions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#showopendialog&quot;&gt;Recipe 5: Show File-open dialog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#showsavedialog&quot;&gt;Recipe 6: Show File-save dialog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#readfromfiles&quot;&gt;Recipe 7: File I/O: Read from files&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#writetofiles&quot;&gt;Recipe 8: File I/O: Write to files&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#loadfromcsv&quot;&gt;Recipe 9: Load data from a CSV file&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#copytoclipboard&quot;&gt;Recipe 10: Copy text to clipboard&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#demo&quot;&gt;Demo&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#references&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;howto&quot;&gt;Recipe 0: How to create a LibreOffice macro&lt;/h2&gt;

&lt;p&gt;Whilst macros can be created in Writer and Draw too, in this specific tutorial, we will restrict ourselves to spreadsheets (Calc). To create a macro, just open the spreadsheet in LibreOffice and go to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tools-&amp;gt;Macros-&amp;gt;Organize Macros-&amp;gt;LibreOffice Basic&lt;/code&gt; menu. After that, if you want to create a macro specific to your spreadsheet (as usually is the case), expand your spreadsheet file on left and select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Standard&lt;/code&gt; and click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;New&lt;/code&gt;. This will open the LibreOffice Macro Editor as a separate window.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/macros_menu.png&quot; alt=&quot;Macros Menu&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;readcell&quot;&gt;Recipe 1: Read cell contents&lt;/h2&gt;

&lt;p&gt;One of the most basic things needed for automation is reading a cell’s contents. The following piece of code does exactly this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Sub read_cell
	dim document as object
	document = ThisComponent
	sheet = document.Sheets(0)
	MsgBox(sheet.getCellByPosition(0, 0).String)
End Sub
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dim&lt;/code&gt; is a keyword used to declare a variable but declaration is totally optional unless &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option Explicit&lt;/code&gt; is specified at the beginning of the module. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ThisComponent&lt;/code&gt; is the LibreOffice object that references the current spreadsheet (or a written document in case of Writer). The important thing here is the expression, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sheet.getCellByPosition(0, 0).String&lt;/code&gt; which gets the contents of first cell in the first row. Cells can be referenced using the co-ordinate system where (0,0) refers to cell at row-0 and column-0. Thus, any value across the entire spreadsheet can be fetched using this simple method.&lt;/p&gt;

&lt;p&gt;To run a macro from the editor, just place the cursor inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;function&lt;/code&gt; body of any macro and press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F5&lt;/code&gt; (or alternatively, click the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Run BASIC&lt;/code&gt; icon on the toolbar).&lt;/p&gt;

&lt;h2 id=&quot;changecell&quot;&gt;Recipe 2: Change cell contents&lt;/h2&gt;

&lt;p&gt;Another often needed thing is the ability to change the cell contents. The following code sets the first cell in the first row to “Hola! Mundo”, the Spanish expression for “Hello! World”:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Sub change_cell
	dim document as object
	document = ThisComponent
	sheet = document.Sheets(0)
	sheet.getCellByPosition(0, 0).String = &quot;Hola Mundo!&quot;
	MsgBox(&quot;Done&quot;)
End Sub
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;searchandrepl&quot;&gt;Recipe 3: Search and Replace&lt;/h2&gt;

&lt;p&gt;Searching and replacing specific strings could be an important part of your automation routine. Below is a fun macro that searches for the first names of some Linux experts (like Linus, Richard, Peter, etc.) and replaces it with their last names (Torvalds, Stallman, Anvin):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Sub replace_text
	Dim names() As String
	Dim surnames() As String
	Dim n As Long
	Dim document As Object
	Dim sheets as Object
	Dim sheet as Object
	Dim replace As Object

	names() = Array(&quot;Linus&quot;, &quot;Richard&quot;, &quot;Peter&quot;, &quot;Greg&quot;, &quot;Bill&quot;)
	surnames() = Array(&quot;Torvalds&quot;, &quot;Stallman&quot;, &quot;Anvin&quot;, &quot;Kroah&quot;, &quot;Gates&quot;)
	document = ThisComponent rem .CurrentController.Frame
	rem sheet = doc.CurrentSelection.Spreadsheet
	sheets = document.getSheets()
	sheet = sheets.getByIndex(0)
	replace = sheet.createReplaceDescriptor rem document.createReplaceDescriptor in case of Writer
	rem replace.SearchRegularExpression = True
	For n = lbound(names()) To ubound(names())
		replace.SearchString = names(n)
		replace.ReplaceString = surnames(n)
		sheet.replaceAll(replace)
	Next n
	MsgBox(&quot;Done&quot;)
End Sub
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;names()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;surnames()&lt;/code&gt; are actually arrays. Unlike C and Java, arrays in Basic are declared and accessed using round braces and not square ones. Also, what gets declared in an array declaration is the upper-bound, not the total size. Thus, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo(2)&lt;/code&gt; is actually a size-3 array ranging from indices 0 to 2.&lt;/p&gt;

&lt;h2 id=&quot;regex&quot;&gt;Recipe 4: Regular Expressions&lt;/h2&gt;

&lt;p&gt;Regular expressions are very useful in searching and replacing text based on specific patterns. The following macro searches for all the email addresses in your spreadsheet and replaces each one with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo@bar.com&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Sub replace_with_regex
  Dim names() As String
  Dim surnames() As String
  Dim n As Long
  Dim document As Object
  Dim sheets as Object
  Dim sheet as Object
  Dim replace As Object

  pattern = &quot;\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b&quot; rem regex pattern to match any email address
  document = ThisComponent rem .CurrentController.Frame
  sheets = document.getSheets()
  sheet = sheets.getByIndex(0)
  replace = sheet.createReplaceDescriptor rem document.createReplaceDescriptor in case of Writer
  replace.SearchRegularExpression = True
  replace.SearchString = pattern
  replace.ReplaceString = &quot;foo@bar.com&quot;
  sheet.replaceAll(replace)

  MsgBox(&quot;Done&quot;)
End Sub
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;showopendialog&quot;&gt;Recipe 5: Show File-open dialog&lt;/h2&gt;

&lt;p&gt;Showing the File-open dialog is a very common requirement, especially when you want to open an external file for processing. The below code uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FilePicker&lt;/code&gt; object to show the file-open dialog and return the selected file-name:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;function show_open_dialog
	dim aurl as object
	dim s as string
	dim oDlg as object
	
	oDlg = createUnoService(&quot;com.sun.star.ui.dialogs.FilePicker&quot;)
	oDlg.setMultiSelectionMode(false)
	oDlg.appendFilter(&quot;CSV Files (.csv)&quot;, &quot;*.csv&quot; ) 
	oDlg.execute
	aUrl = oDlg.getFiles()
	
	s = aUrl(0)
	if len(s) &amp;gt; 0 then
		MsgBox(&quot;File Selected: &quot; &amp;amp; s &amp;amp; chr(13))
	end if
	show_open_dialog = s
end function
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;createUnoService&lt;/code&gt; is a LibreOffice specific method for creating helper objects like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FilePicker&lt;/code&gt; in this example. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appendFilter&lt;/code&gt; method is used to filter out only &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CSV&lt;/code&gt; files in the dialog.&lt;/p&gt;

&lt;h2 id=&quot;showsavedialog&quot;&gt;Recipe 6: Show File-save dialog&lt;/h2&gt;

&lt;p&gt;For showing a File-save dialog, the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FilePicker&lt;/code&gt; object is used, initializing it with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FILESAVE_AUTOEXTENSION&lt;/code&gt; argument:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;function show_save_dialog
	dim aurl as object
	dim s as string
	dim oDlg as object
	
	sFilePickerArgs = Array(_
	com.sun.star.ui.dialogs.TemplateDescription.FILESAVE_AUTOEXTENSION )    
	oDlg = createUnoService(&quot;com.sun.star.ui.dialogs.FilePicker&quot;)
	oDlg.initialize(sFilePickerArgs())
	oDlg.setMultiSelectionMode(false)
	oDlg.appendFilter(&quot;CSV Files (.csv)&quot;, &quot;*.csv&quot; ) 
	oDlg.setTitle(&quot;Save As....&quot;)
	
	if oDlg.execute() then
		aUrl = oDlg.getFiles()
		s = aUrl(0)
		if len(s) &amp;gt; 0 then
			MsgBox(&quot;File Selected: &quot; &amp;amp; s &amp;amp; chr(13))
		end if
	else
		s = &quot;&quot;
	end if
	show_save_dialog = s
end function
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;readfromfiles&quot;&gt;Recipe 7: File I/O: Read from files&lt;/h2&gt;

&lt;p&gt;Raw file I/O is a feature provided by almost every language and Basic macros make it almost too easy. Below code is used to read a CSV file with three columns. Name of the file is set in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filename&lt;/code&gt; variable. The variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;num&lt;/code&gt; is a numerical tag used to reference the file-handler and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FreeFile()&lt;/code&gt; returns a free available number that can be used for tagging. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;open&lt;/code&gt; statement is self-explanatory. In Basic, files can be opened in Input, Output and Binary modes. Finally, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;input&lt;/code&gt; statement is used to actually read the file into the variables line after line.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sub file_io_read
	dim v1, v2, v3
	filename = &quot;/home/prahlad/data/test.csv&quot;
	num = FreeFile()
	open filename for input as #num 
	do while not eof(num)
		input #num, v1, v2, v3
		print v1 &amp;amp; &quot;::&quot; &amp;amp; v2 &amp;amp; &quot;::&quot; &amp;amp; v3
	loop
	close #num
	msgbox &quot;Done&quot;
end sub
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;writetofiles&quot;&gt;Recipe 8: File I/O: Write to files&lt;/h2&gt;

&lt;p&gt;For writing to files, a handler is opened in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;output&lt;/code&gt; mode instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;input&lt;/code&gt;, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write&lt;/code&gt; statement is used to actually write the variables to a file.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sub file_io_write
	filename = &quot;/home/prahlad/data/dummy.csv&quot;
	num = FreeFile()
	open filename for output as #num 
	write #num, &quot;col1&quot;, &quot;col2&quot;, &quot;col3&quot;
	write #num, &quot;1&quot;, &quot;2&quot;, &quot;3&quot;
	write #num, &quot;4&quot;, &quot;5&quot;, &quot;6&quot;
	close #num
	msgbox &quot;Done&quot;
end sub
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;loadfromcsv&quot;&gt;Recipe 9: Load data from a CSV file&lt;/h2&gt;

&lt;p&gt;Apart from working in raw I/O mode, it is sometimes required to load a complete CSV as a sheet in the current document. Using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;show_open_dialog&lt;/code&gt; function that we studied earlier, the following macro first prompts a user with a File-open dialog and then loads the specified CSV file as a new sheet:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sub load_from_csv
	fname = show_open_dialog
	if len(fname)&amp;gt;0 then
		dim fileProps(1) as new com.sun.star.beans.PropertyValue
		fileProps(0).Name = &quot;FilterName&quot;
		fileProps(0).Value = &quot;Text - txt - csv (StarCalc)&quot;
		fileProps(1).Name = &quot;FilterOptions&quot;
		fileProps(1).Value = &quot;44,34,76,1,,0,false,true,true,false&quot;
		document = StarDesktop.loadComponentFromURL(fname, &quot;_blank&quot;, 0, fileProps())        
	end if
	msgbox &quot;Done&quot;
end sub
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fileProps(0)&lt;/code&gt; is a property variable used for specifying the CSV file format, while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fileProps(1)&lt;/code&gt; specifies the default formatting options for the CSV (such as a delimiter, charset, etc.)&lt;/p&gt;

&lt;h2 id=&quot;copytoclipboard&quot;&gt;Recipe 10: Copy text to clipboard&lt;/h2&gt;

&lt;p&gt;Your custom processing might involve putting a specific text to the clipboard from LibreOffice Calc. Following code shows how to put the string “Hola!” to the system clipboard:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sub copy_to_clipboard
	oClip = CreateUnoService(&quot;com.sun.star.datatransfer.clipboard.SystemClipboard&quot;)
	oTR = createUnoListener(&quot;TR_&quot;, &quot;com.sun.star.datatransfer.XTransferable&quot;)
	oClip.setContents(oTR, null)
	msgbox &quot;Done&quot;
end sub

Function TR_getTransferData( aFlavor As com.sun.star.datatransfer.DataFlavor ) As Any
	If (aFlavor.MimeType = &quot;text/plain;charset=utf-16&quot;) Then
		TR_getTransferData = &quot;Hola!&quot;
	EndIf
End Function

Function TR_getTransferDataFlavors() As Any
	Dim aF As new com.sun.star.datatransfer.DataFlavor
	aF.MimeType = &quot;text/plain;charset=utf-16&quot;
	aF.HumanPresentableName = &quot;Unicode-Text&quot;
	TR_getTransferDataFlavors = Array(aF)
End Function

Function TR_isDataFlavorSupported( aFlavor As com.sun.star.datatransfer.DataFlavor ) As Boolean
	TR_isDataFlavorSupported = (aFlavor.MimeType = &quot;text/plain;charset=utf-16&quot;)
End Function
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Second function is a callback and is used for storing the string to clipboard. The last two are helper functions used by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SystemClipboard&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XTransferable&lt;/code&gt; helper objects and are required.&lt;/p&gt;

&lt;h2 id=&quot;demo&quot;&gt;Demo&lt;/h2&gt;

&lt;p&gt;Finally, the working LibreOffice Calc spreadsheet implementing all these examples can be found &lt;a href=&quot;/uploads/old/macro_recipes.ods&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Further Reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://api.libreoffice.org/examples/examples.html#Basic_examples&quot;&gt;http://api.libreoffice.org/examples/examples.html#Basic_examples&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://forum.openoffice.org/en/forum/viewtopic.php?f=25&amp;amp;t=36441&quot;&gt;https://forum.openoffice.org/en/forum/viewtopic.php?f=25&amp;amp;t=36441&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://ask.libreoffice.org/en/question/39940/calc-open-and-save-csv-file-with-given-filter-options/&quot;&gt;https://ask.libreoffice.org/en/question/39940/calc-open-and-save-csv-file-with-given-filter-options/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://wiki.openoffice.org/wiki/Documentation/DevGuide/OpenOffice.org_Developers_Guide&quot;&gt;https://wiki.openoffice.org/wiki/Documentation/DevGuide/OpenOffice.org_Developers_Guide&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://wiki.openoffice.org/wiki/Documentation/BASIC_Guide/Cells_and_Ranges&quot;&gt;https://wiki.openoffice.org/wiki/Documentation/BASIC_Guide/Cells_and_Ranges&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.excel-spreadsheet.com/vba/inputoutput.htm&quot;&gt;http://www.excel-spreadsheet.com/vba/inputoutput.htm&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://forum.openoffice.org/en/forum/viewtopic.php?f=45&amp;amp;t=13783&quot;&gt;https://forum.openoffice.org/en/forum/viewtopic.php?f=45&amp;amp;t=13783&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>How to assemble a fast and minimal Debian Desktop using Openbox</title>
   <link href="https://prahladyeri.github.io/blog/2016/02/minimal-debian-desktop-setup.html"/>
   <updated>2016-02-16T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2016/02/minimal-debian-desktop-setup</id>
   <content type="html">&lt;p&gt;Being a web developer, one of the nagging things to do is keeping your hardware in sync with your performance requirements. In the good old times, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;P4&lt;/code&gt; or even a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Celeron&lt;/code&gt; based PC or laptop was enough for programming. But with changing times, the required investment to get a sane performance has increased to astronomical levels.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Problem is not just with the limitations of hardware, but our requirements too have increased. Apart from the tons of most needed apps like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eclipse&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;firefox&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libreoffice&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;thunderbird&lt;/code&gt;, etc., you now have to keep VMs running in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;virtualbox&lt;/code&gt; at the same time you are having a chat on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;skype&lt;/code&gt; with your client. Add to that, &lt;em&gt;heavy&lt;/em&gt; things like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eclipse&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Android Emulator&lt;/code&gt; and your linux (or even Windows) desktop will start to buckle under the pressure.&lt;/p&gt;

&lt;p&gt;All in all, even an Intel &lt;em&gt;i5&lt;/em&gt; or &lt;em&gt;i7&lt;/em&gt; chip isn’t sufficient today to handle multiple heavy or memory consuming apps. The only solution I have found is to use a lighter window-manager like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Openbox&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fluxbox&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IceWM&lt;/code&gt; instead of a heavy Desktop Environment like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GNOME&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unity&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KDE&lt;/code&gt;. This tutorial contains all the steps needed to assemble on your own light-weight DE based on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Openbox&lt;/code&gt; on Debian (or one of its compatible derivatives like Ubuntu or Linux Mint).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2016/02/openbox.png&quot; alt=&quot;Openbox&quot; /&gt;{.size-full .wp-image-498 width=”956” height=”537”} &lt;strong&gt;Openbox&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://openbox.org&quot;&gt;Openbox&lt;/a&gt; is an open source project (GPL licensed) based on the good old blackbox. It provides just the minimal framework to build a no-flashy, sane desktop environment that gets out of your way, so most resources are used for running the &lt;em&gt;actual apps&lt;/em&gt; that you use. Openbox occupies less than 8 MB of RAM on your machine. Again, this kind of setup is not for everyone, in case you are a huge fan of the Unity dash or GNOME dash, you may not like Openbox at all. On the other hand, if you like the new MATE Desktop which is based on the old GNOME 2, you may find this approach interesting. In fact, Openbox is the window manager used by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LXDE&lt;/code&gt;, the DE most famous for being light on resources. You are, in fact, going to create something like your own version of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LXDE&lt;/code&gt; by following this tutorial, only its even more minimal and custom to your requirements. Here is how to do it:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Download the Debian minimal net-install ISO from &lt;a href=&quot;https://www.debian.org/CD/netinst/&quot;&gt;here&lt;/a&gt;. It is roughly 150-200 MB in size.&lt;/li&gt;
  &lt;li&gt;Either burn the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ISO&lt;/code&gt; to a USB drive or try it out in a VM first.&lt;/li&gt;
  &lt;li&gt;Use the text based or graphical installer to complete the installation.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Once you land on the command line, login as the superuser (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;su&lt;/code&gt;) and install these packages:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apt-get install network-manager
apt-get install xorg openbox xdm
apt-get install xbacklight pcmanfm lxappearance lxpanel gmrun gnome-terminal
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;To make the panel automatically show up, add this to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.config/openbox/autostart&lt;/code&gt; file using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nano&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim&lt;/code&gt; editors:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;lxpanel &amp;amp;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Restart&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note that this will create only a &lt;em&gt;bare minimum&lt;/em&gt;, workable DE with just a File Manager and no other usable apps. lxpanel is a “Windows-98 style” panel that sits on top/bottom of your desktop and gives a “start-menu” in which your programs are organized. lxappearance is a handy tool to switch GTK themes which comes very useful. You still have to install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;evince&lt;/code&gt; to read PDF books, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;geany&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gedit&lt;/code&gt; for a GUI text editor, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iceweasel&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;icedove&lt;/code&gt; for a browser and mail client respectively, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libreoffice-calc&lt;/code&gt; for a spreadsheet.&lt;/p&gt;

&lt;p&gt;But the good thing is that you are the &lt;em&gt;master&lt;/em&gt; of your desktop world now. You don’t have to live with that extra load of bloated baggage that heavy DEs usually come with. If you follow the above process correctly, you will end up with an installation size of less than 500 MB! And your own custom-made DE that works!&lt;/p&gt;

&lt;p&gt;As for customization, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Openbox&lt;/code&gt; is &lt;a href=&quot;http://openbox.org/wiki/Help:Configuration&quot;&gt;highly customizable&lt;/a&gt;. Arch Linux also has a wonderful &lt;a href=&quot;https://wiki.archlinux.org/index.php/openbox&quot;&gt;documentation&lt;/a&gt; on this topic, and &lt;a href=&quot;https://www.maketecheasier.com/configure-andcustomize-openbox/&quot;&gt;here&lt;/a&gt; is another brief guide. For menus, you can either install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;obmenu&lt;/code&gt; or if you are a simplicity fan like me, you can just edit your way through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.config/openbox/menu.xml&lt;/code&gt;. This, and the other file, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.config/openbox/rc.xml&lt;/code&gt; are the only two Openbox configuration files and they have all the needed options. In fact, if you decide to make use of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lxpanel&lt;/code&gt; main menu, you don’t even have to edit the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;menu.xml&lt;/code&gt; of Openbox.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lxpanel&lt;/code&gt; is not the only option for installing a Desktop panel, there are others too like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fbpanel&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tint2&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xfce4-panel&lt;/code&gt;. Read &lt;a href=&quot;https://www.maketecheasier.com/configure-andcustomize-openbox/&quot;&gt;this&lt;/a&gt; for more details.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;To add a battery indicator to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lxpanel&lt;/code&gt;, add this to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.config/lxpanel/default/panels/panel&lt;/code&gt;&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Plugin {
  type = batt
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;To save your openbox customizations between sessions (which is a basic requirement), copy the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rc.xml&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;menu.xml&lt;/code&gt; from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/X11/openbox&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.config/openbox/&lt;/code&gt; (create this folder if it doesn’t exist).&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gmrun&lt;/code&gt; is needed to show the run dialog that pops up when you press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Alt+F2&lt;/code&gt; in any “normal” DE. Once you install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gmrun&lt;/code&gt;, just add below code to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.config/openbox/rc.xml&lt;/code&gt; somewhere in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;keyboard&amp;gt;&lt;/code&gt; section, in order to make the keys work:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;!--start: Prahlad--&amp;gt;
&amp;lt;keybind key=&quot;A-F2&quot;&amp;gt;
&amp;lt;action name=&quot;Execute&quot;&amp;gt;
  &amp;lt;command&amp;gt;gmrun&amp;lt;/command&amp;gt;
&amp;lt;/action&amp;gt;
&amp;lt;/keybind&amp;gt;
&amp;lt;!--end: Prahlad--&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xdm&lt;/code&gt; is optional, but helpful if you don’t want to do a lot of configuration changes in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xorg&lt;/code&gt; files to show up the desktop automatically each time you login. Of course, you can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lightdm&lt;/code&gt; too if you want.&lt;/li&gt;
  &lt;li&gt;I don’t use bluetooth a lot and haven’t included any of those packages, but there are many of them such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bluez&lt;/code&gt; in case you need one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://openbox.org/wiki/Help:Configuration&quot;&gt;Openbox Wiki - Configuration&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://wiki.archlinux.org/index.php/openbox&quot;&gt;Arch Linux Openbox docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.maketecheasier.com/configure-andcustomize-openbox/&quot;&gt;Make Tech Easier - Openbox configuration guide&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.debian.org/CD/netinst/&quot;&gt;Debian Netinstall Download&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://bbs.archlinux.org/viewtopic.php?id=156272&quot;&gt;lxpanel Battery Indicator installation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Linux Desktop Survey Project - Feb/2016</title>
   <link href="https://prahladyeri.github.io/blog/2016/02/linux-desktop-survey-project-feb2016.html"/>
   <updated>2016-02-07T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2016/02/linux-desktop-survey-project-feb2016</id>
   <content type="html">&lt;p&gt;I will start this post by thanking the kind folks at the Reddit’s &lt;a href=&quot;https://www.reddit.com/r/linux&quot;&gt;linux subreddit&lt;/a&gt; and Voat’s &lt;a href=&quot;https://www.voat.co/v/linux&quot;&gt;linux subverse&lt;/a&gt; who wholeheartedly participated in this survey. &lt;a href=&quot;http://lws-rmsreturns.rhcloud.com/limesurvey/index.php/statistics_user/action/surveyid/556933/language/en&quot;&gt;Here&lt;/a&gt; are the results and they are pretty insightful. For a developer who builds apps targetting the linux platform, it helps to know what are the preferences of users who run those apps on a desktop. Not understanding these requirements beforehand has caused a lot of &lt;a href=&quot;https://en.wikipedia.org/wiki/Unity_%28user_interface%29&quot;&gt;unneeded backlash&lt;/a&gt; in the history of linux desktop development and a lot many forks were created which ended up causing a division of focus.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Not only did I learn a lot about what the linux desktop users want in general, but the survey also threw some light on what distros or desktop environments are generally perceived to be either giving a better performance or providing a better user experience.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;what-is-the-most-important-factor-when-choosing-a-distro&quot;&gt;What is the most important factor when choosing a Distro?&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;http://lws-rmsreturns.rhcloud.com/limesurvey/tmp/ec2d620c7c97d4d351ad3553d3dc4161.png&quot; alt=&quot;Linux Desktop Survey Project&quot; /&gt;&lt;/p&gt;

&lt;p&gt;These were the options I had set:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Better Performance and speed&lt;/li&gt;
  &lt;li&gt;Better User Experience&lt;/li&gt;
  &lt;li&gt;A Mix of both&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The prevailing assumption of a lot many people is that linux users are usually those “super nerdy” tech geeks who are only interested in arcane things like computing performance and speed. But on the contrary, we learn that linux users are just like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Windows&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OSX&lt;/code&gt; users for that matter, and they prefer a better user experience over performance!&lt;/p&gt;

&lt;p&gt;As you can see, most of the folks preferred a balance of both performance and user experience. In fact, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Better Performance and speed&lt;/code&gt; was the least favored factor for most people who chose their distros, which implies that how wrong our assumptions sometimes could be! As one redditor said, I should have included &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Software Freedom&lt;/code&gt; as an option since that is also quite a motivating factor for a lot many linux users.&lt;/p&gt;

&lt;p&gt;Here are two comments from participants who chose the middle-path, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A Mix of both&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I don’t feel I have to compromise on one or the other. A good user experience also encompasses acceptable performance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;A good UX is crucial for my own comfort, but the speed and performance must be reliable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is what the undecided folks (Other) have to say:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;While user experience is very important, I feel this is for nought if it is not matched with upstream packages. I find it terribly tedious to find myself on a distro which is lagging behind on the likes of gnome-shell and firefox.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Full choice over programs and user interface; my own OS&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Stability and security are the most important factors for me.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another option I should have added here was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Stability and security&lt;/code&gt; since these are the very foundations upon which linux sits.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;peoples-favorite-distro&quot;&gt;People’s Favorite Distro&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;http://lws-rmsreturns.rhcloud.com/limesurvey/tmp/dfdd05e767cc742c456552f065121e8c.png&quot; alt=&quot;Most Popular Distro&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Ubuntu’s popularity is pretty well-known in the linux world, but what surprised me was Arch. Perhaps, it was the bias effect of lot many people on linux subbredit being Arch fans, or maybe Arch users have actually increased relative to Ubuntu during recent years, who knows! In any case, this survey result does indicate to me that if tomorrow I were to develop a linux destop app, then which distros should I test on with some priority. This is just one of the benefits of conducting this survey.&lt;/p&gt;

&lt;p&gt;Here are a few user comments in this category:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Actually Arch derivative Antergos. This distribution has good a user experience and it is performant. It also delivers fresh software.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;I use Debian on my desktop, Arch as a DOM0 on my server, and RHEL/CENTOS at work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;I honestly hate the linux desktop. It has so many bugs and is visually very hard to parse.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;the-distro-with-best-performance&quot;&gt;The Distro with Best Performance&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;http://lws-rmsreturns.rhcloud.com/limesurvey/tmp/2784dfd5801a114e64e2de0393a8d1e9.png&quot; alt=&quot;The Distro with Best Performance&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Unlike the Phronix arcane performance benchmarks, I thought about asking the users directly which Distro gave them the best performance. I don’t doubt the benchmarks, but computing is such a complex phenomena that raw performance isn’t as much an indicator of overall performance as it used to be in the past. Besides, the best proof of pudding is in the eating. Linux enthusiasts distro-hop a lot and in that process learn about the distros a lot.&lt;/p&gt;

&lt;p&gt;Again, Arch was at top here, followed by Gentoo. Debian came third which was not at all surprising. In fact, I expected this vanilla of a distro to be on the top. Perhaps the “Arch fans” effect of linux subreddit again! What surprised me, however, was Fedora coming such low on performance score. Here is what the participants had to say:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Crunchbang (development started so name changed to Busenlabs)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;no idea but xubuntu runs ok on a dual core laptop from 2007 with 2gb of ram.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;First benchmarks, then if no existing distro fits the bill, Gentoo, Yocto project, or LFS.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;I tested a number of distros on a very slow AMD netbook and Mint with Cinnamon was faster than itself and others with XFCE and MATE.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;If performance was the only criteria, I would use windows.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Probably, arch. Whichever one isn’t in the hands of the NSA.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;which-distro-gives-the-best-user-experience&quot;&gt;Which Distro gives the Best User Experience&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;http://lws-rmsreturns.rhcloud.com/limesurvey/tmp/43ddb58e40e9ececf0e0ed58c106e182.png&quot; alt=&quot;Which Distro gives the Best User Experience&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Ubuntu is most people’s favorite in this area, but Ubuntu’s community clone &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Linux Mint&lt;/code&gt; bagging away the top position is also a fitting thing. Like Ubuntu, Mint is a distro which happens to “just work” according to most users. Though I personally don’t like all the extra amount of non-free codecs and plugins it comes pre-bundled with (such as Flash and Adobe), it is a good distro for existing Windows users to hop on. Here is what participants have to say about choosing the distro which provides the best user experience:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Mint is great out of the box and requires minimal configuration to “just work”.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Enhanced user experience means more convenience and more toys; Ubuntu repos cover both.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Arch derivative Antergos. Just enough training wheels to be easy, still all the goodness of Arch. Why not Ubuntu? Ubuntu is bloated and stale and only has the name of being easiest. While once true, the competition has caught up.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Windows (See the same answer under the “performance” question.)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Mint does a very good job of providing the right apps to control settings, out of the box.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Easy to use (Linux Mint), comes with programs that I’m familiar with (Firefox, Thunderbird, LibreOffice) and the interface is simple but also powerful.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Allows heavy customization to how I want it (Gentoo).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Purely for ease of use/install (Ubuntu).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Haven’t tried that many but of of the few that I have tried Mint has had the most clear and logical meny items.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;p&gt;Along with the many enthusiastic distro-hoppers, my search for the discovery of best linux desktop continues.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Eclipse Mars - Not ready for Linux Yet</title>
   <link href="https://prahladyeri.github.io/blog/2015/12/eclipse-mars-not-ready-for-linux.html"/>
   <updated>2015-12-30T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2015/12/eclipse-mars-not-ready-for-linux</id>
   <content type="html">&lt;p&gt;So after hearing about all the hype and praises about this Eclipse new release called &lt;a href=&quot;https://projects.eclipse.org/releases/mars&quot;&gt;Mars&lt;/a&gt;, I decided to give it a try on my Ubuntu laptop yesterday. Since I already use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Kepler&lt;/code&gt; the older version, I was looking for some big positives like performance improvements (most often talked about by a lot of Eclipse fans lately).&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/eclipse-red.png&quot; alt=&quot;Eclipse Red Logo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When I started this new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eclipse&lt;/code&gt; on ubuntu, I first had a faint hope that it was running faster (maybe a trick of the mind instilled by the new red logo!). But the true test of any software is how it performs under REAL world conditions. Alas! As I had expected, I was only to be disappointed on the performance front. Unless there is a drastic change in underlying core components such as a code refactoring or an improvement of graphic toolkit/library, the “performance” can only get worse, not better. After I created a simple &lt;em&gt;HelloADT&lt;/em&gt; project, here is what happened when I clicked on an Android Activity layout screen:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/Eclipse_Mars.png&quot; alt=&quot;Eclipse Mars&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And this is a modern &lt;em&gt;Intel core i3&lt;/em&gt; machine we are talking about with 4 GB RAM, not some old device. Just after this disaster of an IDE happened, I started my good old &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kepler&lt;/code&gt; version and opened the same ADT project which ran without any problems:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/Eclipse_Kepler.png&quot; alt=&quot;Eclipse Kepler&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It was good that I had kept my kepler installation folder intact, so I was able to revert. So moral of the story is:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Take all claims about this “big improvement” with a pinch of salt, especially if it is a Java based software.&lt;/li&gt;
  &lt;li&gt;Wait for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Eclipse mars&lt;/code&gt; to get more stable before using it for production work.&lt;/li&gt;
&lt;/ol&gt;
</content>
 </entry>
 
 <entry>
   <title>How to start Android App Development</title>
   <link href="https://prahladyeri.github.io/blog/2015/11/how-to-start-android.html"/>
   <updated>2015-11-26T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2015/11/how-to-start-android</id>
   <content type="html">&lt;p&gt;One of the most frequently asked questions on almost any social networking sites these days is how do I start android app programming? Indeed, it is as if Android Development has become some abstract and obscure layer of programming model that only few Illuminati sort of individuals seem accomplished at.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;There is no doubt that the process of compiling an android app from source-code to the actual bytecode that runs on your smartphone is a bit more complex than compiling your normal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Java&lt;/code&gt; program. And it seems even more complex than it deserves to be because of some drastic changes Google has introduced with Android Platform, such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Android is a whole new Operating System which comes with its own concepts.&lt;/li&gt;
  &lt;li&gt;Android has a completely new SDK which has both similarities and differences with the Standard &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Java SE&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Android introduces new programming models for smartphone interfaces like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Activity&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Intent&lt;/code&gt;, etc.&lt;/li&gt;
  &lt;li&gt;Android introduces a completely new way of building GUI interfaces using an XML dialect.&lt;/li&gt;
  &lt;li&gt;Android makes frequent changes to its IDE and keeps switching its recommended IDE.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Android world is always in a flux, though it has been stabilizing a bit since the last year. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Eclipse&lt;/code&gt; plus the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ADT Plugin&lt;/code&gt; was the original recommended IDE and still used by many, but a few years back that changed and now Android’s official development way is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Android Studio&lt;/code&gt; which is nothing but a decorated version of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Intellij IDEA&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In any case, it is important to note that learning Android Development is a full-time job, at least until you grasp the basics. Think of it as just another branch of development, just like you have the traditional &lt;em&gt;Desktop Programming&lt;/em&gt; using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Visual Studio or .NET&lt;/code&gt;, &lt;em&gt;Web Programming&lt;/em&gt; using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PHP/Python/HTML/CSS&lt;/code&gt;, this is the arena of &lt;em&gt;Mobile Apps Development&lt;/em&gt; or specifically, &lt;em&gt;Android Development&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;But that doesn’t mean you need to know the intricacies of how telephony works or how does your app interact with SIM-cards or the Contacts on your SDCard. Thankfully, Android has already done that low level job for you. Android is based on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Linux&lt;/code&gt; kernel which is very powerful and secure when it comes to controlling a handheld device. On top of Linux, Android ships with its own libraries that interact with the kernel, along with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dalvik&lt;/code&gt; runtime. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dalvik&lt;/code&gt; is nothing but a miniaturized version of your Java Runtime Engine (affectionately called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JRE&lt;/code&gt;). The thing about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dalvik&lt;/code&gt; is that it is much less resource intensive compared to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JRE&lt;/code&gt; since it is specifically designed for smart devices. Here is the entire android stack or architecture that you should keep referring to througout your app development process:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/Android-System-Architecture.png&quot; alt=&quot;Anatomy Physiology of an Android. Licensed under CC BY-SA 3.0 via Commons&quot; /&gt;&lt;/p&gt;

&lt;p&gt;^&lt;em&gt;Courtesy:\ “Android-System-Architecture”\ by\ Smieh\ -\ Anatomy\ Physiology\ of\ an\ Android.&lt;/em&gt;^&lt;/p&gt;

&lt;p&gt;The top layer of the stack consists of &lt;em&gt;Applications&lt;/em&gt; or apps that we generally use like Contacts, Browser, Home (the Launcher screen that first comes when you start your device), Phone (the dialler) are all apps.&lt;/p&gt;

&lt;p&gt;The second layer consists of &lt;em&gt;Frameworks&lt;/em&gt; - i.e stuff that the apps are made of. As you will soon learn, there are things like &lt;em&gt;Activities&lt;/em&gt;, &lt;em&gt;Views&lt;/em&gt;, &lt;em&gt;Content Providers&lt;/em&gt;, etc. which are going to be the &lt;em&gt;building blocks&lt;/em&gt; of our apps. The Android OS manages these blocks as soon as the user starts our app and starts using it.&lt;/p&gt;

&lt;h2 id=&quot;pre-requisites&quot;&gt;Pre-requisites&lt;/h2&gt;

&lt;p&gt;There are a few things that you already must know in order to start android-programming, the most important being &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Core Java&lt;/code&gt;, also known as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Java SE&lt;/code&gt; for Standard Edition (not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EE&lt;/code&gt; or Enterprise Edition). If you don’t know it, then go back to school (or a self-learning website) and learn that first. Java programming introduces enough nuances and complexities of its own, so you will likely get overwhelmed by both Java and Android if you try to learn them at the same time. Recommended Java tutorial site is the one found at &lt;a href=&quot;https://docs.oracle.com/javase/tutorial/&quot;&gt;Oracle website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The other thing you should know about is how to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Eclipse&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IntelliJ&lt;/code&gt; IDEs as you will have to use either one of them to start Android Programming. However, this isn’t a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hard requirement&lt;/code&gt; like that of Java. IDE is something that you can get used to after using it for a while. It might take some google-searches or errands at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StackOverflow&lt;/code&gt; at first, but once you get used to it for sometime, you can figure out most things pretty easily.&lt;/p&gt;

&lt;p&gt;Thirdly, it is not mandatory but helpful to know some good design principles. The way you style your widgets like TextViews, Labels, EditTexts, etc. very much determines whether people will like your app or not. There is also the thing that when you launch you app in the market, people will judge your app by its cover or front-screen and your download count will likely depend on that. Of course, you can hire a graphic designer for this or if you are a DIY guy like me, learn a professional Image Editing software like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GIMP&lt;/code&gt; (on Linux) or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Adobe Photoshop&lt;/code&gt; (on Windows).&lt;/p&gt;

&lt;h2 id=&quot;install-the-java-jdk&quot;&gt;Install the Java JDK&lt;/h2&gt;

&lt;p&gt;Now that you have had your breakfast and kept aside all your distractions to focus on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Android Mission&lt;/code&gt;, the first thing to do is install the Java JDK software which is required in order to write and compile Android Apps. Again, head over to Oracle website where you can find the &lt;a href=&quot;http://www.oracle.com/technetwork/java/javase/downloads/index.html&quot;&gt;latest download for Java SE&lt;/a&gt;. Choose your &lt;em&gt;OS&lt;/em&gt; and preferred version. Don’t install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Netbeans&lt;/code&gt; unless you need it for any other purpose, we will be using either &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Eclipse&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Android Studio&lt;/code&gt; for app development.&lt;/p&gt;

&lt;h2 id=&quot;install-the-android-sdk-sdk-tools-and-platform-tools&quot;&gt;Install the Android SDK, SDK Tools and Platform Tools&lt;/h2&gt;

&lt;p&gt;Head over to &lt;a href=&quot;http://developer.android.com/sdk/index.html&quot;&gt;Android Developer site&lt;/a&gt; and download the Android SDK. Now, if you want to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Android Studio&lt;/code&gt; (or you are a big fan of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grade&lt;/code&gt; build system which is default there), download that IDE which includes the entire bundle. Otherwise, head downwards to find an option called “SDK only” to download the minimal commandline version or if you want to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Eclipse&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ADT&lt;/code&gt; plugin. The thing here is that you need at least one IDE to develop otherwise it gets pretty complex to hand-code the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xml&lt;/code&gt; layouts, double-compile your code, first to Java bytecode (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.class&lt;/code&gt;) and then to Dalvik bytecode (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.dex&lt;/code&gt;) and finally zipping them into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;APK&lt;/code&gt; (the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JAR&lt;/code&gt; equivalent), that you are better off delegating these low details to an IDE.&lt;/p&gt;

&lt;p&gt;Again, its important to know, why double compilation is required to build Android apps. You see, your code is first compiled to Java bytecode and then, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dalvik&lt;/code&gt; compiler takes that output and compiles it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dex&lt;/code&gt; or Dalvik bytecode (why that happens is an interesting topic for my another blog post!). Here is how this happens:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/Dalvik_Compilation_Process.png&quot; alt=&quot;Dalvik Compilation Process&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;build-that-hello-world-app&quot;&gt;Build that Hello World App&lt;/h2&gt;

&lt;p&gt;Head over to the &lt;a href=&quot;http://developer.android.com/tools/studio/studio-config.html&quot;&gt;Configuration Section&lt;/a&gt; first to ensure that you’ve setup your IDE properly. Then, the &lt;a href=&quot;http://developer.android.com/tools/workflow/index.html&quot;&gt;Workflow section&lt;/a&gt; is your &lt;em&gt;Bible&lt;/em&gt; (or &lt;em&gt;Geeta&lt;/em&gt; or &lt;em&gt;Qoran&lt;/em&gt; or whatever thrills you). Read it, re-read it, bookmark it, keep referring to it until you are proficient in building and running apps. Needless to say, if you face any issues, there is always &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Google&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StackOverflow&lt;/code&gt; which should answer all your queries.&lt;/p&gt;

&lt;p&gt;In my next post, I will be covering about android features and a doing some basic things like layout design, interaction with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlite&lt;/code&gt; databases, etc. All the best!&lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://developer.android.com/tools/workflow/index.html&quot;&gt;Android Workflow section&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;http://developer.android.com/tools/studio/studio-config.html&quot;&gt;Android Configuration Section&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;http://developer.android.com/sdk/index.html&quot;&gt;Android SDK Downloads&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://en.wikipedia.org/wiki/Android_(operating_system)&quot;&gt;The Android Operating System&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;http://www.oracle.com/technetwork/java/javase/downloads/index.html&quot;&gt;Latest download for Java SE&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>An interesting tale from history: what the Noscript vs. Adblock Plus incident taught us about add-on security</title>
   <link href="https://prahladyeri.github.io/blog/2015/11/are-your-firefox-addons-really-that-safe-an-insider-story.html"/>
   <updated>2015-11-09T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2015/11/are-your-firefox-addons-really-that-safe-an-insider-story</id>
   <content type="html">&lt;p&gt;Firefox has long been a pillar of the open-source software community, renowned for its commitment to privacy, freedom, and transparency. With strict guidelines for what enters its add-on repository, it’s easy to assume that developers of highly popular extensions share these same values. But sometimes, reality challenges our assumptions in unexpected ways.&lt;/p&gt;

&lt;p&gt;Let’s revisit an intriguing tale from 2009 that changed the way we think about browser add-on security: the infamous clash between two popular Firefox add-ons, &lt;strong&gt;Adblock Plus (ABP)&lt;/strong&gt; and &lt;strong&gt;Noscript&lt;/strong&gt;. This story isn’t just a moment of open-source drama—it’s a cautionary tale about trust, transparency, and how we evaluate the tools we rely on every day.&lt;/p&gt;

&lt;h4 id=&quot;the-incident-noscript-vs-adblock-plus--a-code-war-unfolds&quot;&gt;&lt;strong&gt;The incident: Noscript vs. Adblock Plus – a code war unfolds&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;Back in 2009, Noscript, a popular security add-on developed by Giorgio Maone, was partially funded by advertisements. To earn revenue, Giorgio’s add-on quietly whitelisted his own ad-supported websites within Adblock Plus (ABP) without user consent. But it wasn’t a simple whitelist addition; Noscript altered ABP’s internal settings to achieve this, effectively hacking into a fellow add-on’s functionality. This was more than a simple slip-up; it was a violation of user trust.&lt;/p&gt;

&lt;p&gt;Naturally, the move didn’t go unnoticed. &lt;strong&gt;Wladimir Palant&lt;/strong&gt;, the developer behind Adblock Plus, discovered the modifications, sparking a public outcry and addressing it in a detailed blog post titled “&lt;a href=&quot;https://forum.adblockplus.org/viewtopic.php?t=7356&quot;&gt;Attention Noscript users&lt;/a&gt;” . What ensued was a very public “code war” between the developers, highlighting a significant vulnerability in how Firefox extensions interacted with each other at that time.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/abp-vs-noscript.webp&quot; alt=&quot;abp-vs-noscript.webp&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;why-this-incident-still-matters-today&quot;&gt;&lt;strong&gt;Why this incident still matters today&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;Noscript’s actions might seem minor in hindsight—especially after Giorgio’s public apology on his blog, “&lt;a href=&quot;http://hackademix.net/2009/05/04/dear-adblock-plus-and-noscript-users-dear-mozilla-community&quot;&gt;Dear Adblock Plus and Noscript users, dear Mozilla community&lt;/a&gt;” —but the implications ran deeper. This wasn’t just about one developer’s questionable judgment. It was a moment that revealed a larger issue: Firefox’s add-on architecture didn’t provide users with adequate control or visibility over permissions.&lt;/p&gt;

&lt;p&gt;It also raised broader questions: If a security-focused extension like Noscript could manipulate another extension, what about the myriad lesser-known plugins users were installing without a second thought?&lt;/p&gt;

&lt;h4 id=&quot;lessons-learned-modern-add-on-security-and-transparency&quot;&gt;&lt;strong&gt;Lessons learned: modern add-on security and transparency&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;Over the years, browsers have evolved, and so have the security measures around add-ons. Today, &lt;strong&gt;permission-based security models&lt;/strong&gt; are the industry standard, with browsers like Chrome and Firefox explicitly informing users about what an extension can access. Mozilla, in particular, has introduced &lt;strong&gt;WebExtensions&lt;/strong&gt;, a new architecture that offers enhanced isolation between add-ons to prevent similar incidents.&lt;/p&gt;

&lt;p&gt;However, it’s not just about architectural changes. This story underscores the need for vigilance when using third-party extensions. Here are a few best practices to keep in mind:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Review Permissions:&lt;/strong&gt; Always check the permissions requested by an add-on. If it seems to require more access than its function justifies, it’s worth reconsidering.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Stick to Trusted Developers:&lt;/strong&gt; While popularity doesn’t guarantee trustworthiness, sticking to well-known and highly rated add-ons with transparent histories is usually safer.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Stay Updated:&lt;/strong&gt; Browser policies and extension capabilities change frequently. Regularly review your installed extensions to ensure they’re up-to-date and still secure.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&quot;how-far-weve-come-the-evolution-of-add-on-policies&quot;&gt;&lt;strong&gt;How far we’ve come: the evolution of add-on policies&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;It’s interesting to reflect on how far we’ve come since the Noscript-ABP incident. Mozilla’s &lt;strong&gt;addons.mozilla.org (AMO)&lt;/strong&gt; now subjects all add-ons to automated and manual reviews. There are stricter guidelines, better transparency, and a comprehensive permission system in place.&lt;/p&gt;

&lt;p&gt;The Firefox add-on saga is a valuable reminder of how even open-source ecosystems are susceptible to lapses in transparency and accountability. It’s a testament to the need for constant evolution in how browsers handle user privacy and security.&lt;/p&gt;

&lt;h4 id=&quot;a-final-thought&quot;&gt;&lt;strong&gt;A final thought&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;In hindsight, the Noscript and ABP conflict was more than just a developer feud—it was a pivotal moment that shaped the future of browser security. As users, we benefit from this history, with safer add-ons and better guidelines. But as technology evolves, so do the risks, and it’s on all of us to stay informed and vigilant.&lt;/p&gt;

&lt;p&gt;So, the next time you click “Add to Firefox,” remember: every piece of software has a story, and it’s worth taking the time to understand it.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>How to add HTML tag highlighting to Geany</title>
   <link href="https://prahladyeri.github.io/blog/2015/11/how-to-add-html-tag-highlighting-geany.html"/>
   <updated>2015-11-07T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2015/11/how-to-add-html-tag-highlighting-geany</id>
   <content type="html">&lt;p&gt;Ever since I bid farewell to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Windows&lt;/code&gt; after they introduced that horrible &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Metro&lt;/code&gt; interface in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Windows 8&lt;/code&gt;, I had been on the lookout for FOSS Linux alternatives that can run easily on my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ubuntu 14.04&lt;/code&gt; machine. One such lookout was a replacement for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Notepad++&lt;/code&gt; as I always need a handy editor for scripting and other miscellaneous tasks (such as writing this draft in markdown syntax). For large projects, there are always “heavy” things like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eclipse&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;netbeans&lt;/code&gt; but I generally tend to avoid them if I can.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Now, the bread-and-butter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Linux&lt;/code&gt; alternative for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Notepad++&lt;/code&gt; is &lt;a href=&quot;http://www.geany.org/&quot;&gt;Geany&lt;/a&gt;, a light-weight editor developed by Enrico Tröger. Geany has all the wonderful features that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Notepad++&lt;/code&gt; has, except one: There is no highlighting available for matching html tag pairs. And as any Web Developer worth his salt will know, an HTML file will soon start turning out to be a huge cosmic soup of tags and braces once the app starts evolving, and this feature can become handy in the situation. And whilst there is a plugin called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pair Tag Highlighter&lt;/code&gt;, it isn’t readily available in the ubuntu 14.04 repository. Like many users, I decided to ignore this small nagging inconvenience and moved on. But recently, I was able to spare some time from my daily errands and decided to have a go at it.&lt;/p&gt;

&lt;p&gt;The solution to this isn’t that apparent as even my googlefu had to go through a lot of filters before I came across this &lt;a href=&quot;http://askubuntu.com/questions/589172/html-pair-tag-highlight-alternative-for-geany-editor-on-ubuntu-14-04&quot;&gt;only post&lt;/a&gt; that gave me the solution, though it was a bit more involved and required me to checkout a github repo and build the plugin. This means one of two things: Either a lot of people are not interested in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Geany&lt;/code&gt; any more and moved on to other editors like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sublime Text&lt;/code&gt;, or that they have decided to live with this inconvenience. If you are from the latter group, your inconvenience is soon going to be over. All you have to do is this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo apt-get install git
cd /tmp
git clone https://github.com/geany/geany-plugins.git
cd geany-plugins/
sed -i &apos;s/1.25/1.23/&apos; wscript
./waf configure --enable-plugins=pairtaghighlighter
make all
sudo make install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Things to note:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The fifth line uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sed&lt;/code&gt; tool to change the minimum required version in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wscript&lt;/code&gt; from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.25&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.23&lt;/code&gt;. However, after I’ve written this article, the plugin developer might have raised the minimum requirement and changed it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.26&lt;/code&gt; or something. So, replace accordingly.&lt;/li&gt;
  &lt;li&gt;Make sure you have the latest version of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python&lt;/code&gt; installed in order to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;waf&lt;/code&gt; tool. You also need to install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;intltool&lt;/code&gt; package if it does not already exist.&lt;/li&gt;
  &lt;li&gt;If all goes well, restart Geany and go to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tools-&amp;gt;Plugin Manager&lt;/code&gt; and enable the Plugin &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pair Tag Highlighter&lt;/code&gt;. Once you do that, your HTML code will start highlighting the opening and closing tags as shown below:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/geany-tag-highlight.png&quot; alt=&quot;Geany with HTML pair tag highlighting&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;References:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://askubuntu.com/questions/589172/html-pair-tag-highlight-alternative-for-geany-editor-on-ubuntu-14-04&quot;&gt;http://askubuntu.com/questions/589172/html-pair-tag-highlight-alternative-for-geany-editor-on-ubuntu-14-04&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://askubuntu.com/questions/616369/a-alternative-to-install-pairtaghighlighter-for-geany-1-23-21&quot;&gt;http://askubuntu.com/questions/616369/a-alternative-to-install-pairtaghighlighter-for-geany-1-23-21&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Sqlalchemy Hack - How to convert a table to dict on the fly</title>
   <link href="https://prahladyeri.github.io/blog/2015/07/sqlalchemy-hack-convert-dict.html"/>
   <updated>2015-07-04T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2015/07/sqlalchemy-hack-convert-dict</id>
   <content type="html">&lt;p&gt;In on of my recent projects, I came across the need to develop a JSON based REST API to fetch data from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlalchemy&lt;/code&gt; objects. Now, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Query&lt;/code&gt; object is a great way to access data using the powerful &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlalchemy orm&lt;/code&gt;, but it doesn’t give any built-in way to convert the result-set into a python &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dict&lt;/code&gt;.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;For instance, I have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Professor&lt;/code&gt; table in my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;models.py&lt;/code&gt;. Here is what I get when I query the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Professor&lt;/code&gt; model using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlalchemy&lt;/code&gt; ORM:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; session.query(models.Professor).all()
2015-07-05 11:07:57,282 INFO sqlalchemy.engine.base.Engine SELECT professors.id AS professors_id, professors.name AS professors_name,
partment, professors.email AS professors_email, professors.password AS professors_password, professors.phone AS professors_phone
FROM professors
2015-07-05 11:07:57,282 INFO sqlalchemy.engine.base.Engine ()
[&amp;lt;Professor(name=`Albus Dumbledore`)&amp;gt;, &amp;lt;Professor(name=`Severus Snape`)&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At the end, I got the two Professors’ name in a collection, but they are still &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlalchemy&lt;/code&gt; objects. How can I convert it to a python dict, so that it can be deserialized to JSON or be used for any other purpose. To solve this, just add the below code to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;models.py&lt;/code&gt; module, and just call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;models.to_dict()&lt;/code&gt; method to convert an sqlalchemy models collection to a dict (or alternatively, call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;models.from_dict()&lt;/code&gt; to build the model object collection from an existing dict!):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;def to_dict(model_instance, query_instance=None):
	if hasattr(model_instance, &apos;__table__&apos;):
		return {c.name: str(getattr(model_instance, c.name)) for c in model_instance.__table__.columns}
	else:
		cols = query_instance.column_descriptions
		return { cols[i][&apos;name&apos;] : model_instance[i]  for i in range(len(cols)) }

def from_dict(dict, model_instance):
	for c in model_instance.__table__.columns:
		setattr(model_instance, c.name, dict[c.name])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The usage is as follows:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;q = dbsession.query(models.Application).filter(models.Application.id==id)
professors = q.all()
di = models.to_dict(professors) #for converting a single table resultset

q = dbsession.query(models.Application)
application = q.first()
dd = models.to_dict(app)
dd[&apos;student&apos;] = models.to_dict(application.student, q) #for converting a relationship object which refers to another table. 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The reason we have to use a slightly different approach for the relationship objects (like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;application.student&lt;/code&gt; which refers to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;student&lt;/code&gt; model) is that the instance doesn’t have an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__table__&lt;/code&gt; object which is required to build the dict. Hence, the query instance is passed. This is just one approach I’ve used to convert &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlalchemy&lt;/code&gt; objects to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dict&lt;/code&gt;. If you have any other approach, please let me know.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Is Sourceforge.net safe anymore to download software</title>
   <link href="https://prahladyeri.github.io/blog/2015/06/is-sourceforge-safe-anymore.html"/>
   <updated>2015-06-25T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2015/06/is-sourceforge-safe-anymore</id>
   <content type="html">&lt;p&gt;You have probably heard about all the recent buzz surrounding the sourceforge.net website hosting adware/malware bundled installers and naive users falling prey to it. In some cases, SF even actively took control of &lt;a href=&quot;http://arstechnica.com/information-technology/2015/05/sourceforge-grabs-gimp-for-windows-account-wraps-installer-in-bundle-pushing-adware/&quot;&gt;abandoned developer accounts&lt;/a&gt;. to bundle their crapware.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/sf-logo.png&quot; alt=&quot;Sourceforge Logo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So, is it safe anymore to go to this site and download your favorite FLOSS software? The TLDR version is NO, unless you happen to trust the developer and the project is actively being developed by them. But to understand the big picture, you will have to see the root cause of this which goes much deeper than just sourceforge, and relates to software distribution practices in the Microsoft Windows world.&lt;/p&gt;

&lt;h2 id=&quot;what-went-wrong&quot;&gt;What went wrong?&lt;/h2&gt;

&lt;p&gt;Software Development consists of two important phases: Authoring of source code, and secondly, the build process where the code is compiled to binaries and bundled into installable packages that you may download. Many years ago, sourceforge was one of the rare sites that provided a place on the internet where open source developers can host their code and binaries, and users can download them. This used to go well for many years and thus sourceforge gained a nice reputation in the FLOSS Community.&lt;/p&gt;

&lt;p&gt;But soon enough, Sourceforge decided to cash in on this reputation by breaching this trust. And they started bundling adware and crapware to the developer builds. In some cases, &lt;a href=&quot;https://forum.filezilla-project.org/viewtopic.php?t=30240&quot;&gt;the lead developers&lt;/a&gt; were in cohorts with Sourceforge. And as &lt;a href=&quot;http://www.thewindowsclub.com/safe-software-download-sites&quot;&gt;this Windows Club article&lt;/a&gt; succintly explains, Sourceforge wasn’t alone in this. There were many othere like CNET.com, Download.com, etc.&lt;/p&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;

&lt;p&gt;Though the adware bundling is optional in theory, the default options are so tactfully placed that only the shrewdest of hackers can dodge all of them! Though this kind of behavior is sort of expected from B-grade sites like CNET and download.com, Sourceforge was something that the open source community held in high esteem. Though most of the developers have moved to github now, souceforge was once considered the Jerusalem for downloading FLOSS Software, especially Windows Software. Even today, their interface is the best suited for novice users who are just looking for a big green download button to click - Github interface is more geared towards developers and generally overwhelms them. But now that the Jerusalem folks have turned into heretics (for lack of a better analogy), what are our options?&lt;/p&gt;

&lt;p&gt;The best option is to get it from the developer’s site itself if they provide it. But sometimes that’s not possible as the developer may not always have bandwidth to host the binaries, and we have to depend on 3rd party sites like sourceforge. As the author of the linked article suggests, &lt;a href=&quot;http://majorgeeks.com&quot;&gt;MajorGeeks&lt;/a&gt; and &lt;a href=&quot;http://softpedia.com&quot;&gt;Softpedia&lt;/a&gt; are generally clean in my experience. Downloading software from those cleaner sites, however, is just a workaround and doesn’t solve the larger structural problem of Software Distribution in Windows world. So, what is that solution?&lt;/p&gt;

&lt;h2 id=&quot;the-linux-solution&quot;&gt;The Linux Solution!&lt;/h2&gt;

&lt;p&gt;One crisis we are facing in the Windows FLOSS world right now is the Middleman problem. The original developer (A) writes some code and compiles it, the middleman (SF) bundles adware to it and makes a quick buck, whilst an innocent user (B) clicks that download link. Since there is no way for B to verify if the package is actually bundled by A, SF continues to get his commissions.&lt;/p&gt;

&lt;p&gt;But what if there was a way for B to verify if the package is actually signed by A? In the Linux world, we do have such a mechanism, so there is no place for middlemen to make a profit there. If I download GNU Emacs from the &lt;a href=&quot;http://ftp.gnu.org/gnu/emacs/&quot;&gt;GNU ftp server&lt;/a&gt;, all I have to do is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg --verify emacs.sig emacs.tar.gz&lt;/code&gt; and the system will tell me whether my emacs copy is signed by developers of Emacs, or was it tampered with for injecting any malware!&lt;/p&gt;

&lt;p&gt;The second crisis the Windows FLOSS is facing is lack of a decent package management system. In above analogy, we can’t expect B to personally verify each downloaded software against A’s signature, s/he needs a good package manager that downloads the package from a trusted server and installs it afer verifying the signatures.&lt;/p&gt;

&lt;p&gt;In Linux world, we have two major packaging systems known by the name of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yum&lt;/code&gt;. How are they better, stable and secure than the Windows approach of users downloading arbitrary installers from SF-like sites and installing them, is something beyond the scope of this article. But suffice it to say that its very very high time that Windows gets a similar package manager to take care of the present structural issues surrounding it. &lt;a href=&quot;http://www.ninite.com/&quot;&gt;Ninite&lt;/a&gt; has tried to do something in this direction, but its a proprietary and paid solution. We need open source and something that everyone accepts as a standard. Maybe, perhaps Microsoft will take the initiative and do it themselves? Only time will tell.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to know whether my blog post will attract traffic or not</title>
   <link href="https://prahladyeri.github.io/blog/2015/06/how-to-know-whether-my-blog-post-will-attract-traffic-or-not.html"/>
   <updated>2015-06-10T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2015/06/how-to-know-whether-my-blog-post-will-attract-traffic-or-not</id>
   <content type="html">&lt;p&gt;One of the evergreen topics for discussions in the blogging world is SEO, and especially the various ways of attracting bless of the all-pervading search engines to one’s own site or blog. There are various ways to approach this problem, some of the popular ones are:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Using meta tags like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keyword&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;description&lt;/code&gt; to attract the search engines.&lt;/li&gt;
  &lt;li&gt;Performing keyword research (using Google’s planner tool or similar).&lt;/li&gt;
  &lt;li&gt;Getting link juice through social media marketing i.e creating backlinks.&lt;/li&gt;
  &lt;li&gt;Hiring SEO experts or web designers to get some more arcane suggestions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But lost in these technical details, folks are forgetting one basic foundation upon which the Internet is built: &lt;em&gt;Content is King&lt;/em&gt;. Yes, the sad truth is that Google (or Yahoo/Bing for that matter) cannot be fooled by any of these tools in the long term. Doing some social media marketing on Facebook might place your article on #1 position for a short period of time, but sooner or later, Google will figure out whether your content is actually worth it or not.&lt;/p&gt;

&lt;p&gt;But how do you write good content, and also ensure that it has some uniqueness that the million other sites on the Internet don’t already have? Well, I can’t help you with the first question. Since only &lt;em&gt;you&lt;/em&gt; know what your area of expertise is, only you can come up with a good topic and subject-matter for your blog or site. For instance, Web Development is my area of expertise, and I came up with this particular topic you are reading right now.&lt;/p&gt;

&lt;p&gt;But I can help you with the second question. Doing some research on this matter and performing a topic-wise traffic analysis of my blog has proven this one thing: &lt;em&gt;Content is King&lt;/em&gt;. So, my method of figuring out whether an article/post will rock or not, is based upon this simple canon. Once you decide the topic and subject-matter of your next blog post, do this simple reality check before you even bother drafting it:&lt;/p&gt;

&lt;p&gt;Almost every googler is looking for a specific solution for a problem or issue, be it shopping for a product, knowing a URL address or researching something. So, your next post or article should solve that problem in order to pass the content test. To get a quick idea of the competition, perform a google search from the perspective of your potential googler! A glance at the results will instantly tell you whether this problem is already solved, and by how many.&lt;/p&gt;

&lt;p&gt;For instance, before publishing this article, I did this simple Google search:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;How to know whether my blog post will attract traffic or not
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And this is what I got:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/how-to-know-whether-blog-topic-gets-traffic.png&quot; alt=&quot;How to know whether my blog post will attract traffic or not&quot; /&gt;&lt;/p&gt;

&lt;p&gt;All those are top ranking sites and deserve to be on page one (no offense meant to them). However, for someone specifically looking for a solution to this question (agreed, however small that set of users be), none of them have an immediate answer that could be surmised by the search result summary. Whilst they are all making clear points about ways to increase search engine traffic, none of them talk about knowing, in priory, whether a given method will work or not. So, at least for this particular query (maybe used by about 1% of all bloggers), this blog post deserves to be amongst the page one results.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to host a Flask app on Openshift</title>
   <link href="https://prahladyeri.github.io/blog/2015/02/how-to-host-flask-app-openshift.html"/>
   <updated>2015-02-06T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2015/02/how-to-host-flask-app-openshift</id>
   <content type="html">&lt;p&gt;&lt;em&gt;Note: This article may no longer be relevant as Red Hat has recently changed the openshift stack with docker and kubernetes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.openshift.com/&quot;&gt;Openshift free tier&lt;/a&gt; is an excellent way to host your python web app for staging or testing, and you can even host a low to medium traffic production site. Openshift provides several options (cartridges) for hosting including &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python, php, node.js, etc.&lt;/code&gt; but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python&lt;/code&gt; being my favorite language and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Flask&lt;/code&gt; being a minimalist and flexible framework, this combination is what I usually end up with.&lt;!--more--&gt;&lt;/p&gt;

&lt;h3 id=&quot;create-an-openshift-account&quot;&gt;Create an Openshift account&lt;/h3&gt;

&lt;p&gt;In case you haven’t already, head over to &lt;a href=&quot;https://www.openshift.com/&quot;&gt;Openshift&lt;/a&gt; and sign up for a free tier. You will be able to host at most three apps for each account. Openshift apps are hosted on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rhcloud.com&lt;/code&gt; domain and you’ll have to setup a subdomain first which will be part of your app url. For instance, if I register &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prahladyeri.rhcloud.com&lt;/code&gt; subdomain, I can create the following apps with that:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;myflaskapp-prahladyeri.rhcloud.com
myphpapp-prahladyeri.rhcloud.com
blog-prahladyeri.rhcloud.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;install-the-necessary-tools&quot;&gt;Install the necessary tools&lt;/h3&gt;

&lt;p&gt;You will need the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;git-scm.com/&quot;&gt;Git&lt;/a&gt;: Your app resides in a git repository, so you’ll need git installed on your machine to push changes. App is deployed automatically once you push.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://python.org&quot;&gt;Python and Flask&lt;/a&gt;: Obviously, you are going to need them if you are building a Flask app.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubygems.org/gems/rhc&quot;&gt;Openshift rhc tool&lt;/a&gt;: This &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ruby&lt;/code&gt; based tool is optional, only use it if you don’t want to use their online portal for creating apps or you aren’t familiar with ssh. Personally, I didn’t want to install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ruby&lt;/code&gt; on my machine just for this one purpose.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;pull-the-remote-repo&quot;&gt;Pull the remote repo&lt;/h3&gt;

&lt;p&gt;Once you create a python app, Openshift will provide you a git repository url as follows:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/openshift-git-repo.png&quot; alt=&quot;Openshift git url&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now open your command line and pull this starter repo to your local machine:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone &amp;lt;YOUR_SOURCE_URL&amp;gt; myFlaskApp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, the remote repository will be cloned in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;myFlaskApp&lt;/code&gt; folder. Browse it to see the scaffolding.&lt;/p&gt;

&lt;h3 id=&quot;add-your-flask-app&quot;&gt;Add your flask app&lt;/h3&gt;

&lt;p&gt;The scaffolding structure will be as follows:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wsgi.py
setup.py
.openshift/..
.settings/..
wsgi/..         =&amp;gt; your python source files go here.
wsgi/static..   =&amp;gt; your static folders viz css, img, fonts, et al. go here.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If the wsgi/ folder doesn’t exist, you’ll have to create it. Just modify the setup.py and add Flask and SQLAlchemy as your app dependencies along with your app name. This tells openshift to make sure that dependency packages are available whenever you push any code changes.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;from setuptools import setup

setup(name=&apos;myFlaskApp&apos;,
	  version=&apos;1.0&apos;,
	  description=&apos;myFlaskApp&apos;,
	  author=&apos;Prahlad Yeri&apos;,
	  author_email=&apos;prahladyeri@yahoo.com&apos;,
	  url=&apos;http://www.python.org/sigs/distutils-sig/&apos;,
	  install_requires=[&apos;Flask==0.10.1&apos;,&apos;SQLAlchemy==0.9.8&apos;],
	 )
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, create a text file named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;application&lt;/code&gt; in the wsgi/ folder with the following contents:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!/usr/bin/python
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;virtenv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;environ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;OPENSHIFT_PYTHON_DIR&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;/virtenv/&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;environ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;PYTHON_EGG_CACHE&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;virtenv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;lib/python2.7/site-packages&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;virtualenv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;virtenv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;bin/activate_this.py&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;execfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;virtualenv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__file__&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;virtualenv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;IOError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;myFlaskApp&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;application&lt;/span&gt;       
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a configuration file that tells openshift where your Flask app script resides. Now create a python file called myFlaskApp.py, this will be your HelloWorld script:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import flask
from flask import Flask
from flask import request

app = Flask(__name__)

@app.route(&quot;/&quot;)
def home():
	return &quot;Hello World&quot;

if __name__ == &quot;__main__&quot;:
	app.run(debug=True)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The last part of the code (app.run) is there so that you may test the Flask app by running this script on your local machine before pushing these changes.&lt;/p&gt;

&lt;h3 id=&quot;push-your-changes&quot;&gt;Push your changes&lt;/h3&gt;

&lt;p&gt;All that is left to be done now is committing your changes and pushing them to openshift:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git add .
git commit -m &quot;Initial commit for myFlaskApp&quot;
git push origin master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;voila-you-are-done&quot;&gt;Voila! You are done&lt;/h3&gt;

&lt;p&gt;Wasn’t it almost as easy as deploying a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;php&lt;/code&gt; script on your web host? If everything goes right, your Flask app will be hosted on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://myFlaskApp-mydomain.rhcloud.com/&lt;/code&gt;. Visit your app link and check it out.&lt;/p&gt;

&lt;h3 id=&quot;few-important-things&quot;&gt;Few important things&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;In case you aren’t familiar about how git over ssl works, the remote machine authenticates your machine using an SSL public key you have already provided them. So if this is your fist time, you’ll have to generate a private-public key pair (using ssh in linux or putty on windows). After that, you’ll have to update your public key to Openshift, so they can authenticate your machine. You can add it using the Settings menu on the Openshift portal.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Its very important that all your static files reside in &lt;strong&gt;wsgi/static&lt;/strong&gt; folder and that folder only. Openshift uses that path by default. But in case you are really stuck with using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/css&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/js&lt;/code&gt; in your existing app, as a solution you can clear the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;static_url_path&lt;/code&gt; in your flask app as follows:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;app = Flask(__name__, static_url_path=&apos;&apos;, static_folder=&apos;static&apos;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Refer to this Openshift &lt;a href=&quot;https://blog.openshift.com/build-your-app-on-openshift-using-flask-sqlalchemy-and-postgresql-92/&quot;&gt;tutorial&lt;/a&gt; for more details.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Make the most of SSH. Some times, you may want to connect with the remote server using secured shell (ssh/putty) for troubleshooting, viewing logs, etc. Your SSH url is included in your git source url. So, if your git url is of the form:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh://500XXXXXXXXXXXX01061a@prahladyeri-inn.rhcloud.com/~/git/prahladyeri.git/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just remove the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh://&lt;/code&gt; from the beginning and the other things after the domain, so the SSH host url becomes:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;500XXXXXXXXXXXX01061a@prahladyeri-inn.rhcloud.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;References:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.openshift.com/build-your-app-on-openshift-using-flask-sqlalchemy-and-postgresql-92/&quot;&gt;Build an app using Flask, SQLAlchemy and PostgreSQL&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubygems.org/gems/rhc&quot;&gt;Openshift rhc tool&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.openshift.com/&quot;&gt;Openshift Homepage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Migrating from WordPress to Jekyll: save money with a static site</title>
   <link href="https://prahladyeri.github.io/blog/2015/02/farewell-wordpress-hello-jekyll.html"/>
   <updated>2015-02-04T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2015/02/farewell-wordpress-hello-jekyll</id>
   <content type="html">&lt;p&gt;Here I am, signing off from a self-hosted &lt;a href=&quot;http://www.wordpress.org&quot;&gt;WordPress&lt;/a&gt; site and finding a welcome change in &lt;a href=&quot;http://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt;, a blog-aware static site generator. There is nothing new about this, several well-known bloggers have already migrated to Jekyll in the last few years. Ever since Tom Preston Werner created this software in 2008 and published his infamous article about &lt;a href=&quot;http://tom.preston-werner.com/2008/11/17/blogging-like-a-hacker.html&quot;&gt;Blogging Like a Hacker&lt;/a&gt;, it has become the go-to thing for at least the small and indie bloggers.&lt;/p&gt;

&lt;p&gt;While WordPress is a powerful platform, it can feel over-engineered for simple blogging needs. For indie bloggers who don’t need the complexities of a RDBMS like MySQL, a static site could be a more efficient solution. The hassles of administering and maintaining various themes and plugins could feel overwhelming at times. However, since there weren’t too many alternatives in those days and PHP hosting was an easy path, many ignored this factor.&lt;/p&gt;

&lt;h3 id=&quot;how-jekyll-can-save-you-hosting-costs&quot;&gt;How Jekyll can save you hosting costs&lt;/h3&gt;

&lt;p&gt;Jekyll generates static sites made of pure HTML/CSS. Static sites, unlike dynamic ones, don’t require server-side processing or database queries, which reduces hosting resource usage and speeds up load times. This makes static sites both cost-efficient and faster. In fact, &lt;a href=&quot;https://github.com/jekyll/jekyll/wiki/Sites&quot;&gt;Github pages&lt;/a&gt; provides you fully free static hosting for Zero USD per month!&lt;/p&gt;

&lt;h3 id=&quot;jekyll-vs-wordpress-a-closer-look&quot;&gt;Jekyll vs WordPress: A closer look&lt;/h3&gt;

&lt;p&gt;While both Jekyll and WordPress serve the purpose of creating websites, they differ in several key areas, making Jekyll a better choice for specific use cases like simple blogs or static sites.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Speed and Performance&lt;/strong&gt;: WordPress relies heavily on server-side PHP scripts and database queries to dynamically generate pages, which can slow down site performance, especially if not optimized. On the other hand, Jekyll pre-generates static HTML pages, significantly reducing load times as there’s no need to process requests or queries. Static sites also tend to perform better under heavy traffic since they’re served directly from the server without any backend processing.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Security&lt;/strong&gt;: WordPress’s widespread usage makes it a common target for hackers, particularly due to vulnerabilities in plugins, themes, or unpatched core software. Jekyll, by contrast, is far less prone to security breaches because static sites don’t require a database or server-side processing, reducing potential attack vectors. With Jekyll, you don’t have to worry about plugin updates or securing a backend.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Customization and Plugins&lt;/strong&gt;: WordPress shines when it comes to flexibility through its vast ecosystem of plugins and themes. However, this can lead to bloat, slowing down your site. Jekyll’s simplicity means fewer customization options compared to WordPress, but it also means less overhead. Instead of relying on plugins, you can customize your Jekyll site directly through code, giving you full control without unnecessary baggage.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Maintenance&lt;/strong&gt;: WordPress sites require regular maintenance, such as updating plugins, themes, and the WordPress core itself. This can be time-consuming and may lead to incompatibilities. With Jekyll, maintenance is minimal—once your site is deployed, it’s mostly hands-off. There’s no need to manage databases, perform software updates, or worry about downtime due to version conflicts.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In essence, if you’re running a content-heavy blog that doesn’t need dynamic features or heavy customization, Jekyll can save you time and resources. For more feature-rich or complex sites, WordPress remains a powerful choice but comes with its own set of maintenance responsibilities.&lt;/p&gt;

&lt;h3 id=&quot;seo-and-other-challenges-with-jekyll&quot;&gt;SEO and other challenges with Jekyll&lt;/h3&gt;

&lt;p&gt;While Jekyll offers numerous benefits in terms of speed, cost, and simplicity, there are a few considerations that you should keep in mind before making the switch.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;SEO (Search Engine Optimization)&lt;/strong&gt;: In WordPress, SEO optimization is often handled through plugins like Yoast, which makes it easy to tweak meta tags, sitemaps, and other SEO-related elements. With Jekyll, these features aren’t built-in, and you’ll need to configure your SEO manually. This means writing metadata directly into your HTML or Markdown files and creating your own XML sitemaps. Fortunately, there are &lt;a href=&quot;https://jekyll.github.io/plugins/&quot;&gt;Jekyll plugins&lt;/a&gt; for generating sitemaps, RSS feeds, and optimizing metadata for SEO, but setting them up might require some coding knowledge.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Dynamic Content&lt;/strong&gt;: One of WordPress’s biggest advantages is its ability to handle dynamic content like comments, forms, or membership systems. While services like &lt;a href=&quot;https://disqus.com/&quot;&gt;Disqus&lt;/a&gt; or &lt;a href=&quot;https://staticman.net/&quot;&gt;Staticman&lt;/a&gt; can add dynamic features like comments to Jekyll sites, they don’t offer the same level of functionality or flexibility that WordPress does. For bloggers who want more interactive features like user logins, contact forms, or complex ecommerce capabilities, WordPress may still be the better option.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Learning Curve&lt;/strong&gt;: While Jekyll is simpler in terms of maintenance, there’s a learning curve when it comes to getting started. You’ll need to be comfortable with the command line, Git, and Markdown, as well as basic HTML and CSS for customizations. While WordPress allows users to manage their site through a graphical interface, Jekyll requires a more hands-on approach, which might be daunting for beginners.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Third-Party Integrations&lt;/strong&gt;: WordPress integrates seamlessly with various third-party services through its plugin ecosystem, from payment gateways to email marketing platforms. Jekyll, while offering more control, may require additional effort to integrate with these services manually.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Despite these challenges, Jekyll’s advantages—especially in terms of speed, security, and cost—far outweigh the downsides for users looking for a simple, fast, and secure platform for static content.&lt;/p&gt;

&lt;h3 id=&quot;the-implementation&quot;&gt;The implementation&lt;/h3&gt;

&lt;p&gt;As any seasoned PHP programmer would tell you, programming in a language like PHP isn’t everyone’s cup of tea. While you don’t have to do any programming for hosting a static site, you should still know about a few things in order to implement a static site:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt;: Jekyll is a static site generation tool written in Ruby language to generate blog-aware static sites (like the one you are presently reading). Visit the link to find documentation that explain what Jekyll is, how to install Ruby and Jekyll on your system and publish your posts with it.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://daringfireball.net/projects/markdown/syntax&quot;&gt;Markdown&lt;/a&gt;: Markdown is a utilitarian formatting language crafted specially with online publishers in mind. Jekyll posts written in Markdown syntax are saved with the extension of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*.md&lt;/code&gt;. While you can write posts in HTML syntax too, knowing markdown comes very handy and saves time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For instance, when I write:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[Jekyll](http://jekyllrb.com/)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It becomes:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Making lists, headings, etc. is as easy. For instance,&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;### Three hashes means H3
#### Four hashes means H4
1. This is list-item1
2. This is list-item2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;becomes:&lt;/p&gt;

&lt;h3 id=&quot;three-hashes-means-h3&quot;&gt;Three hashes means H3&lt;/h3&gt;

&lt;h4 id=&quot;four-hashes-means-h4&quot;&gt;Four hashes means H4&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;This is list-item1&lt;/li&gt;
  &lt;li&gt;This is list-item2&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;tools-of-the-trade&quot;&gt;Tools of the trade&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://disqus.com/&quot;&gt;Disqus&lt;/a&gt;: Perhaps the only feature for which most indie bloggers need a dynamic site is that of posting comments. Thanks to services like Disqus, bloggers can now leave the hassle of maintaining their own database for storing their readers’ comments. Disqus does this for them.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jekyll/jekyll/wiki/Sites&quot;&gt;Github pages&lt;/a&gt;: GitHub Pages is an excellent option for bloggers looking for a reliable, free static web hosting service. It offers generous bandwidth and content hosting, making it a great choice for many users, especially those who want to keep costs low while maintaining flexibility.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.google.com/analytics&quot;&gt;Google Analytics&lt;/a&gt;: Google Analytics integrates seamlessly with Jekyll, providing valuable insights into your audience demographics and behaviors without needing additional WordPress plugins.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;outcome-why-jekyll-is-worth-the-switch&quot;&gt;Outcome: Why Jekyll is worth the switch&lt;/h3&gt;

&lt;p&gt;After making the switch from WordPress to Jekyll, I can confidently say that the benefits of a static site far outweigh the initial setup effort. From improved site performance and lower hosting costs to greater security and minimal maintenance, Jekyll has proven to be a highly efficient platform for my blogging needs.&lt;/p&gt;

&lt;p&gt;If you’re running a simple blog, personal portfolio, or documentation site, and you’re tired of managing a database and paying for expensive hosting, Jekyll might just be the solution you’ve been looking for. By leveraging GitHub Pages and static site generation, you can enjoy the peace of mind that comes with a fast, secure, and cost-effective website.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Take the leap today&lt;/strong&gt; and explore Jekyll as your next blogging platform. With plenty of resources and guides available, including some I’ve listed in the references and migration checklist below, you’ll find the transition smoother than expected.&lt;/p&gt;

&lt;h3 id=&quot;migration-checklist&quot;&gt;Migration checklist&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Step&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
      &lt;th&gt;Tools/Resources&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;1. Backup Your WordPress Site&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Before making any changes, back up your WordPress site, including the database and files.&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://wordpress.org/plugins/updraftplus/&quot;&gt;UpdraftPlus&lt;/a&gt;, &lt;a href=&quot;https://wordpress.org/plugins/all-in-one-wp-migration/&quot;&gt;All-in-One WP Migration&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;2. Install Ruby and Jekyll&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Set up Ruby and Jekyll on your local machine to create and manage your static site.&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://jekyllrb.com/docs/installation/&quot;&gt;Jekyll Installation Guide&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;3. Export WordPress Content&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Use a plugin or WordPress’s export tool to export your posts, pages, and media.&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://wordpress.org/plugins/jekyll-exporter/&quot;&gt;Jekyll Exporter Plugin&lt;/a&gt;, WordPress Export Tool&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;4. Convert WordPress Content to Jekyll&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Use the exported content to convert it to Jekyll’s format, which involves generating Markdown files.&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://wordpress.org/plugins/jekyll-exporter/&quot;&gt;Jekyll Exporter Plugin&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;5. Set Up Your Jekyll Site&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Create a new Jekyll site and configure your theme, layouts, and plugins.&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://jekyllrb.com/docs/&quot;&gt;Jekyll Docs&lt;/a&gt;, &lt;a href=&quot;https://jekyllthemes.io/&quot;&gt;Jekyll Themes&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;6. Customize Your Jekyll Site&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Modify the theme, layout, and styles to match your old WordPress site or give it a fresh design.&lt;/td&gt;
      &lt;td&gt;HTML/CSS, &lt;a href=&quot;http://joshualande.com/jekyll-github-pages-poole/&quot;&gt;Poole Jekyll Theme&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;7. Add Comments via Disqus&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Use Disqus to handle comments since Jekyll doesn’t support dynamic comment systems.&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://disqus.com/&quot;&gt;Disqus for Jekyll&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;8. Implement SEO&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Set up SEO by adding meta tags and optimizing content. Use Jekyll plugins for sitemaps and meta tags.&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://github.com/jekyll/jekyll-seo-tag&quot;&gt;Jekyll SEO Plugin&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;9. Set Up Analytics&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Integrate Google Analytics to track your site’s performance.&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://analytics.google.com/&quot;&gt;Google Analytics&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;10. Host Your Jekyll Site&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Choose a hosting service like GitHub Pages, Netlify, or your own server.&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub Pages&lt;/a&gt;, &lt;a href=&quot;https://www.netlify.com/&quot;&gt;Netlify&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;11. Test Your Jekyll Site&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Test your static site to ensure everything is functioning as expected.&lt;/td&gt;
      &lt;td&gt;Browser, Jekyll Local Server&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;12. Migrate DNS to New Hosting (Optional)&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;If you’re using a custom domain, update your DNS settings to point to the new host.&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://help.github.com/articles/setting-up-a-custom-domain-with-github-pages/&quot;&gt;GitHub Pages Custom Domain Setup&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;13. Finalize Migration&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Once tested, finalize the migration by making the Jekyll site live and notifying users of the change.&lt;/td&gt;
      &lt;td&gt;N/A&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;em&gt;Reference:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://hadihariri.com/2013/12/24/migrating-from-wordpress-to-jekyll/&quot;&gt;Wordpress to Jekyll - Migration guide&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://paulstamatiou.com/how-to-wordpress-to-jekyll/&quot;&gt;Wordpress to Jekyll - How to&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jekyll/jekyll/wiki/Sites&quot;&gt;Jekyll, A blog aware static site generator&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://tom.preston-werner.com/2008/11/17/blogging-like-a-hacker.html&quot;&gt;Blogging Like a Hacker&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Jekyll_%28software%29&quot;&gt;Jekyll, Wikipedia&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jekyll/jekyll/wiki/Sites&quot;&gt;Github pages&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://help.github.com/articles/setting-up-a-custom-domain-with-github-pages/&quot;&gt;How to set up a custom domain with Github pages&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://joshualande.com/jekyll-github-pages-poole/&quot;&gt;Poole - A minimalistic Jekyll theme&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>4 Ways to share your mobile internet with a PC/laptop</title>
   <link href="https://prahladyeri.github.io/blog/2014/12/4-ways-to-share-your-mobile-internet-with-a-pclaptop.html"/>
   <updated>2014-12-20T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2014/12/4-ways-to-share-your-mobile-internet-with-a-pclaptop</id>
   <content type="html">&lt;p&gt;The one thing I like about the world of Linux in general and also Android is the great number of options. Unlike Windows phone, where there is only one way to perform a task (if at all there is!), android provides a richer user experience in all departments.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/old/Android_tether_options.png&quot;&gt;&lt;img src=&quot;/uploads/old/Android_tether_options.png&quot; alt=&quot;Android Tethering Options&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Suppose you are traveling to some place and your laptop has lost its usual source of internet connectivity. You will naturally consider surfing the web on your cell-phone, or better still, try and find a way to share the internet on your cell-phone with your laptop, so your work won’t suffer.&lt;/p&gt;

&lt;p&gt;The android OS supports multiple ways to perform tethering (a.k.a share your mobile internet with other devices such as laptop).&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;&lt;em&gt;USB Tethering:&lt;/em&gt;&lt;/strong&gt; I mention this method first because it is the most power-efficient of the lot. Always try to use this method before others if you want to prioritize battery savings. The only shortcoming of this method is that you will only be able to share the  mobile internet with at most one device only (in the above example, your laptop). And of course, you will be able to keep your USB cable handy. To use this method, just hook up the USB cable to your laptop (making sure that the device drivers are installed). Then, on your cellphone, go to &lt;strong&gt;&lt;em&gt;Settings-&amp;gt;Tethering and Portable Hotspot&lt;/em&gt;&lt;/strong&gt; and tap on the &lt;strong&gt;&lt;em&gt;USB Tethering&lt;/em&gt;&lt;/strong&gt; option to activate it. (On your particular model, the actual setting name might differ as each manufacturer customizes android to their own liking)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;em&gt;Bluetooth Tethering:&lt;/em&gt;&lt;/strong&gt; This method is less efficient than USB tethering, but still quite efficient compared to others. Again, the disadvantage is that at most only one peer can be shared. Also, the speed will be much slower compared to Wifi and other techniques, but if 10mbps is workable for you, then this option could be a great help for you. To activate, tap on this option in &lt;strong&gt;&lt;em&gt;Settings-&amp;gt;Tethering and Portable Hotspot&lt;/em&gt;&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt; &lt;strong&gt;&lt;em&gt;Wifi Access Point:&lt;/em&gt;&lt;/strong&gt; This method is the most common of the lot as the concept of turning your cellphone into a Wifi access point has turned into quite a fad nowadays. However, it is also the least efficient energy wise. The advantage, of course, is that your cellphone can provide its bandwidth to multiple devices at the same time. To activate, tap on this option in &lt;strong&gt;&lt;em&gt;Settings-&amp;gt;Tethering and Portable Hotspot&lt;/em&gt;&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;em&gt;Using a USB Dongle as Mobile broadband:&lt;/em&gt;&lt;/strong&gt; This method applies only to GSM radios (not CDMA as they are locked by providers).  Though it doesn’t come under the definition of tethering, the effect is same. If none of the above methods work for you (or for some reason, you don’t want to use), as an alternative, you can take out the SIM card and put it in an unlocked USB dongle (also called &lt;a href=&quot;https://en.wikipedia.org/wiki/WorldSIM#Data_SIM_Card&quot;&gt;Data Card&lt;/a&gt;). Data Cards are manufactured by companies like ZTE and Huawei and unlocked versions are available in many &lt;a href=&quot;http://www.amazon.com/Generic-Wireless-7-2Mbps-Dongle-Function/dp/B00MHAKIFI/ref=sr_1_5?ie=UTF8&amp;amp;qid=1419075706&amp;amp;sr=8-5&amp;amp;keywords=usb+data+cards&quot;&gt;online retail stores&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
</content>
 </entry>
 
 <entry>
   <title>7 Reasons I would prefer an Android Phone over WP8</title>
   <link href="https://prahladyeri.github.io/blog/2014/12/7-reasons-i-would-prefer-an-android-phone-over-wp8.html"/>
   <updated>2014-12-19T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2014/12/7-reasons-i-would-prefer-an-android-phone-over-wp8</id>
   <content type="html">&lt;p&gt;My existing phone is a &lt;em&gt;Karbonn A30&lt;/em&gt; running &lt;em&gt;Android ICS&lt;/em&gt; and my next phone is going to be an Android phone too. Both as a &lt;strong&gt;user&lt;/strong&gt; and as a &lt;strong&gt;developer&lt;/strong&gt;, I’ve come upon the realization that Android provides a much better user experience and overall satisfaction in mobile computing.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/old/Android_WP8_Feature_compare.png&quot;&gt;&lt;img src=&quot;/uploads/old/Android_WP8_Feature_compare.png&quot; alt=&quot;Android-WP8 Feature comparison&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had briefly considered using Windows Phone once, but I’ve discarded that idea after giving it a trial run. Here are the 7 reasons why I would only go for a phone running the Android Operating system and not WP8.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;&lt;em&gt;More features:&lt;/em&gt;&lt;/strong&gt; This is one of the primary reasons a user buys any product, let alone a cell-phone. Android clearly wins in this department as it provides more features (out-of-the-box) compared to WP8. Just open the android settings menu and compare it to the feature-deprived WP8 settings screen to prove this to yourself. WP settings doesn’t even allow you to perform basic things like:
    &lt;ol&gt;
      &lt;li&gt;Setting up a custom mp3 ringtone (You need a PC with Zune software running for that.)&lt;/li&gt;
      &lt;li&gt;Setting up bluetooth options (No advanced options like bluetooth tethering )&lt;/li&gt;
      &lt;li&gt;Brightness settings are only low, medium and high (whereas Android provides a slider to set a custom percentage).&lt;/li&gt;
      &lt;li&gt;No built-in file manager (At least the lumia 520 I tested did not)&lt;/li&gt;
      &lt;li&gt;Not enough power stats (Android shows a complete run-down of what apps ate how much battery life, WP doesn’t even show the exact percent of battery remaining)&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;em&gt;More options for tethering:&lt;/em&gt;&lt;/strong&gt; One of the major reasons that I use my phone is to tether my phone’s internet, so as to use it on my laptop. Android provides me three options to do this: Wifi tethering (aka Access point), Bluetooth tethering, USB tethering. WP8 only provides Wifi sharing and that too is not quite customizable.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;em&gt;More free apps:&lt;/em&gt;&lt;/strong&gt; The Google play store is full of professional apps (many of them free, too). For instance, ES3 is a file-manager, archiver, FTP-manager and backup-manager combined. This app is not freely available on WP8 and I doubt the paid version would be having the features comparable to android version. Other examples are Terminal-IDE  (A fully-fledged IDE) and QPython (A fully-fledged programming environment!) that are both freely available on Android.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;em&gt;Opensource technologies:&lt;/em&gt;&lt;/strong&gt; Android stack completely rests on Open source technologies. The Linux kernel, Apache Harmony and Dalvik VM are all Opensource components.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;em&gt;Supported by Google:&lt;/em&gt;&lt;/strong&gt; Another reason to go with Android is that it is actively developed and supported by Google Inc, a company whose interests directly harmonize with that of the end-users.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;em&gt;More options for the Developer:&lt;/em&gt;&lt;/strong&gt; Even as a developer, you will have a much better time developing an Android app than a WP8 app. First of all, the development tools (EclipseADT and Android SDK) are pretty well-documented on &lt;a href=&quot;http://developer.android.com&quot;&gt;Android Developer site&lt;/a&gt;. Secondly, these are opensource tools that run on all platforms including Linux and OSX. On the other hand, developing a WP8 app requires Windows 8 64 bit version and nothing else.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;em&gt;No vendor lock-in:&lt;/em&gt;&lt;/strong&gt; As a corollary to point 4, there is no vendor lock-in when you go for Android. Because the source code of android OS is freely available from the &lt;a href=&quot;http://source.android.com&quot;&gt;repository&lt;/a&gt;, you can customize it if you need to.&lt;/li&gt;
&lt;/ol&gt;
</content>
 </entry>
 
 <entry>
   <title>9 Optimizations to make your Linux Desktop fly like a Rocket!</title>
   <link href="https://prahladyeri.github.io/blog/2014/06/9-optimizations-to-make-your-linux-desktop-fly-like-a-rocket.html"/>
   <updated>2014-06-28T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2014/06/9-optimizations-to-make-your-linux-desktop-fly-like-a-rocket</id>
   <content type="html">&lt;p&gt;This article is the result of notes I’ve prepared during tweaking, twisting and optimizing ubuntu variants over the last few years. In case you use any other distro, some of these settings may not be applicable to you. For best results, these changes must be done on top of a fresh installation, otherwise chances of things breaking increase a bit. Each step is optional - In case of software removals, do it only if you are not going to use the concerned software. Be careful before making any changes and know exactly what and why you are doing that.&lt;!--more--&gt;&lt;/p&gt;

&lt;h3 id=&quot;1-optimize-disk-access-with-noatime&quot;&gt;#1 Optimize disk access with noatime:&lt;/h3&gt;

&lt;p&gt;Each file and folder on your linux system has a file-creation timestamp and a modification timestamp. Apart from that, linux tries to keep track of “access time” for each of these files. Now keeping track of the access time has its performace-cost, and if you want to remove this performance cost, you need to specify “noatime” attribute in the disk partition entries in your /etc/fstab file. Edit this file in your text-editor and set noatime as follows:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;UUID=97102801-14e3-9752-7412-d9c57e30981w / ext4 errors=remount-ro 0,**noatime** 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;2-optimize-swappiness&quot;&gt;#2 Optimize Swappiness:&lt;/h3&gt;

&lt;p&gt;Swappiness is the tendency of the linux kernel to prefer disk-swapping vis-a-vis physical memory. The default swappiness value of 60 was kept considering server installations. If you are a desktop user having a machine with good RAM, you would normally prefer disk-swapping to be minimal. You can safely reduce this value to 10. To do so edit the file /etc/sysctl.conf and add the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vm.swappiness=10
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(Just change the entry if it already exists, don’t make a duplicate!)&lt;/p&gt;

&lt;h3 id=&quot;3-install-preload&quot;&gt;#3 Install preload:&lt;/h3&gt;

&lt;p&gt;If you typically use the same programs regularly, preload will help you by loading into memory, the programs that you use most frequently. To install on ubuntu:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo apt-get install preload
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;4-place-your-mission-critical-apps-in-devshm&quot;&gt;#4 Place your mission critical apps in /dev/shm:&lt;/h3&gt;

&lt;p&gt;Few weeks back, I was having performance issues with running Eclipse on ubuntu. After tweaking and optimizing various JVM settings in vain, the thing that really made the difference was &lt;a href=&quot;https://prahladyeri.github.io/blog/2014/06/real-way-make-eclipse-run-faster-ubuntu.html&quot;&gt;placing the entire JDK folder in RAMDISK&lt;/a&gt;. The /dev/shm folder is like a virtual ramdisk (on ubuntu and derivatives) where you can place your temporal, high-priority stuff to run it in “best performance” mode.&lt;/p&gt;

&lt;h3 id=&quot;5-remove-unwanted-programs-from-startup&quot;&gt;#5 Remove unwanted programs from startup:&lt;/h3&gt;

&lt;p&gt;Many linux distros such as ubuntu come loaded with a ton of baggage, and if you are someone like me, you might feel obliged to reduce some burden off your system by removing or disabling unwanted software and daemons from it. You can do this by going to “Startup Applications” in the System menu, but ubuntu hides the pre-installed apps by default. To overcome this limitation, open your terminal and issue the below command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo sed -i &apos;s/NoDisplay=true/NoDisplay=false/g&apos; /etc/xdg/autostart/*.desktop
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://prahladyeri.github.io/uploads/old/startup.png&quot;&gt;&lt;img src=&quot;/uploads/old/startup.png&quot; alt=&quot;ubuntu startup&quot; /&gt;{.alignnone .size-full .wp-image-3092 width=”649” height=”612”}&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can go through the startup programs list and can disable the unwanted ones. Common sense will tell you that if you don’t use bluetooth on your machine, you can get rid of the “Bluetooth Manager”. Similar is the case with “Backup Monitor” in case you don’t need to sync your backups in realtime. Here is the list of services that I’ve safely disabled without causing any issues:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Backup Monitor&lt;/li&gt;
  &lt;li&gt;Bluetooth Manager&lt;/li&gt;
  &lt;li&gt;Chat&lt;/li&gt;
  &lt;li&gt;Desktop sharing&lt;/li&gt;
  &lt;li&gt;Gwibber&lt;/li&gt;
  &lt;li&gt;Ocra screen reader&lt;/li&gt;
  &lt;li&gt;Personal file sharing&lt;/li&gt;
  &lt;li&gt;Screen saver&lt;/li&gt;
  &lt;li&gt;Ubuntu one&lt;/li&gt;
  &lt;li&gt;update notifier&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;6-uninstall-software-that-you-dont-use&quot;&gt;#6 Uninstall software that you don’t use:&lt;/h3&gt;

&lt;p&gt;The next step is to remove those software that you don’t use at all. Again, some common sense but with some caution is needed here. There are some programs (like empathy) that form the core part of ubuntu, so it won’t allow you to “apt-get remove..” them without removing unity itself. In such cases, we will disable such programs from starting up as services (next step). Some of the programs that you may safely remove are:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apt-get remove samba-common\
apt-get remove cups\
apt-get remove avahi-daemon avahi-autoipd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I typically uninstall all three after a new installation. The first one is needed for file-sharing in the local network if you have one. Second is the print daemon, and third is used to broadcast common network services across the local network and finding local hosts by using friendly names like “local.workstation”.&lt;/p&gt;

&lt;h3 id=&quot;7-disable-unwanted-daemons&quot;&gt;#7 Disable unwanted daemons:&lt;/h3&gt;

&lt;p&gt;In case you don’t want to remove the cups program as you might need printing in future, you can disable them for the time being. To do so, issue the below command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo sh -c &quot;echo &apos;manual&apos; \&amp;gt;\&amp;gt; /etc/init/cups.override&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You may disable any daemon in this manner by doing a manual override, just replace the “cups.override” with the daemon name that you want removed such as:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo sh -c &quot;echo &apos;manual&apos; \&amp;gt;\&amp;gt; /etc/init/bluetooth.override&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Later, if you want to enable that daemon, all you to do is delete the .override file.&lt;/p&gt;

&lt;h3 id=&quot;8-optimize-nautilus-to-behave-in-a-speedy-manner&quot;&gt;#8 Optimize Nautilus to behave in a speedy manner:&lt;/h3&gt;

&lt;p&gt;This is totally optional. Nautilus, by default, tries to show thumbnails of each and every file in a directory. If the directory contains a lot of files, this causes a noticable delay. Now if you are in the habit of regularly previewing thumbnails of your images, don’t do this optimization. Otherwise, if previewing thumbnails don’t matter to you and all you are interested in is speed (like me), you can go to Edit-&amp;gt;Preferences-&amp;gt;Preview-tab and set the preview settings to Never.&lt;/p&gt;

&lt;h3 id=&quot;9-disable-translation-downloads-in-aptitude&quot;&gt;#9 Disable translation downloads in aptitude:&lt;/h3&gt;

&lt;p&gt;This setting is for speeding up the downloads from apt repositories rather than your machine. By default, ubuntu adds additional translation repos when you issue “apt-get update” command to update your repository settings. If you only need english, you can disable translation downloads by editing /etc/apt/apt.conf.d/00aptitude and additing this line to it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Acquire::Languages &quot;none&quot;;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;References:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://askubuntu.com/questions/74653/how-can-i-remove-the-translation-entries-in-apt&quot;&gt;http://askubuntu.com/questions/74653/how-can-i-remove-the-translation-entries-in-apt&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://askubuntu.com/questions/2194/how-can-i-improve-overall-system-performance&quot;&gt;http://askubuntu.com/questions/2194/how-can-i-improve-overall-system-performance&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://askubuntu.com/questions/173094/how-can-i-use-ram-storage-for-the-tmp-directory-and-how-to-set-a-maximum-amount&quot;&gt;http://askubuntu.com/questions/173094/how-can-i-use-ram-storage-for-the-tmp-directory-and-how-to-set-a-maximum-amount&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>PHP-FPM vs node.js - The REAL Performance Battle</title>
   <link href="https://prahladyeri.github.io/blog/2014/06/php-fpm-vs-node-js-the-real-performance-battle.html"/>
   <updated>2014-06-22T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2014/06/php-fpm-vs-node-js-the-real-performance-battle</id>
   <content type="html">&lt;p&gt;Even after my last &lt;a href=&quot;/blog/2014/06/php-vs-node-js-real-statistics.html&quot;&gt;article&lt;/a&gt; about PHP and node.js benchmarking, my search for the holy grail of performance truth still continues. However, I do understand now that pitting PHP running on apache against a stand-alone node was a bit unfair with PHP for it was limited by what the apache configuration could handle.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/old/benchmark.png&quot;&gt;&lt;img src=&quot;/uploads/old/benchmark.png&quot; alt=&quot;Benchmark&quot; /&gt;{.alignnone .size-full .wp-image-3082 width=”1021” height=”622”}&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No, this time I went with nginx, a light and performance oriented server that was specifically designed to solve the &lt;a href=&quot;https://en.wikipedia.org/wiki/C10k&quot;&gt;C10K&lt;/a&gt; problem from the ground-up. And who better than &lt;a href=&quot;http://php-fpm.org/&quot;&gt;PHP-FPM&lt;/a&gt;, the enhanced Fastcgi process manager that implements asynchronous features (at least in theory) to take on &lt;a href=&quot;http://nodejs.org/&quot;&gt;node.js&lt;/a&gt;. node.js is the one server that implements all its features primarily using callbacks in javascript, and thus drastically improvising performance by leveraging the benefits of &lt;a href=&quot;https://en.wikipedia.org/wiki/Functional_programming&quot;&gt;functional programming&lt;/a&gt; (again, in theory).&lt;/p&gt;

&lt;p&gt;I used the same code I had used earlier but did a small improvement to it so that the random filenames generated for performing I/O are unique:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;?php 
//asyncdemo.php
$s=&quot;&quot;; //generate a random string of 108KB and a random filename
$filename=&quot;&quot;;
//generate a random filename
do {
	$fname = rand(1,99999).&apos;.txt&apos;;
} while(file_exists($fname));

//generate a random string of 108kb
for($i=0;$i&amp;lt;108000;$i++)
{
	$n=rand(0,57)+65;
	$s.=chr($n);
}

//write the string to disk
file_put_contents($fname,$s);

//read the string back from disk
$result = file_get_contents($fname);

//write the string back on the response stream
echo $result;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And here is the Javascript version:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;//server.js
var http = require(&apos;http&apos;);    
var server = http.createServer(handler);
var fs = require(&apos;fs&apos;);

function handler(request, response) {
	response.writeHead(200, {&apos;Content-Type&apos;: &apos;text/plain&apos;});

	//generate a random filename
	do{fname = (1 + Math.floor(Math.random()*99999999))+&apos;.txt&apos;;
	} while(fs.existsSync(fname));

	//generate a random string of 108kb
	var payload=&quot;&quot;;
	for(i=0;i&amp;amp;lt;108000;i++)
	{
		n=Math.floor(65 + (Math.random()*(122-65)) );
		payload+=String.fromCharCode(n);
	}

	//write the string to disk in async manner
	fs.writeFile(fname, payload, function(err) {
			if (err) console.log(err);

			//read the string back from disk in async manner
			fs.readFile(fname, function (err, data) {
				if (err) console.log(err);
				response.end(data); //write the string back on the response stream
			});  
		}
	);
}

server.listen(8080);
console.log(&apos;Running on localhost:8080&apos;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So, what happens when we run a piece of web application code performing async I/O for two thousand times (with two hundred concurrent) using a tool like apache-bench? Who is faster - PHP-FPM or node.js? Here is the answer.&lt;/p&gt;

&lt;p&gt;So, moral of the story is that even the latest and greatest of PHP world falls behind node.js (though by a much smaller margin than before). Now, I do understand that PHP’s market is very large, and with so many opensource CMSes like wordpress, mediawiki and drupal already powered by PHP, it is quite difficult to shake PHP’s market share in the near future.&lt;/p&gt;

&lt;p&gt;On the other hand, with the performance advantage that node.js offers, its a very lucrative option for startups small businesses that don’t have the funding to develop high-end enterprise apps in say, Java or SAP. More importantly, if tommorrow I were to given a task of developing a performance-driven app, is there one reason why I should not write it in node.js and go for PHP-FPM instead? Some food for thought. Comments are Welcome!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Summarized results:
PHP-FPM: 64.447 secondsnode.js: 42.441 seconds

The Machine:
Intel Pentium Dual-Core 2.30GHz running Linux 3.2.0

The Configurations:
PHP-FPM: PHP 5.4.23 (fpm-fcgi) (built: Jun 22 2014 14:51:15
NODE: node v0.10.28

Detailed Results:
--PHP-FPM-----
ab -c 200 -n 2000 http://localhost:8080/asyncdemo/asyncdemo.php

Concurrency Level:      200
Time taken for tests:   64.447 seconds
Complete requests:      2000
Failed requests:        6
   (Connect: 0, Receive: 0, Length: 6, Exceptions: 0)
Write errors:           0
Non-2xx responses:      6
Total transferred:      215649378 bytes
HTML transferred:       215355222 bytes
Requests per second:    31.03 [#/sec] (mean)
Time per request:       6444.742 [ms] (mean)
Time per request:       32.224 [ms] (mean, across all concurrent requests)
Transfer rate:          3267.70 [Kbytes/sec] receive


--NODE-----
ab -c 200 -n 2000 http://localhost:8080/

Concurrency Level:      200
Time taken for tests:   42.441 seconds
Complete requests:      2000
Failed requests:        1
   (Connect: 0, Receive: 0, Length: 1, Exceptions: 0)
Write errors:           0
Total transferred:      216155440 bytes
HTML transferred:       215953440 bytes
Requests per second:    47.12 [#/sec] (mean)
Time per request:       4244.115 [ms] (mean)
Time per request:       21.221 [ms] (mean, across all concurrent requests)
Transfer rate:          4973.69 [Kbytes/sec] received
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>PHP vs node.js: The REAL statistics</title>
   <link href="https://prahladyeri.github.io/blog/2014/06/php-vs-node-js-real-statistics.html"/>
   <updated>2014-06-09T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2014/06/php-vs-node-js-real-statistics</id>
   <content type="html">&lt;p&gt;When it comes to web programming, I’ve always coded in ASP.NET or the LAMP technologies for most part of my life. Now, the new buzz in the city is &lt;a href=&quot;https://en.wikipedia.org/wiki/Node.js&quot;&gt;node.js&lt;/a&gt;. It is a light-weight platform that runs javascript code on server-side and is said to improvise performance by using async I/O.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;http://notes.ericjiang.com/posts/751&quot;&gt;theory&lt;/a&gt; suggests that synchronous or blocking model of I/O works something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/nodejs-comp.png&quot; alt=&quot;Blocking I/O&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I/O is typically the costliest part of a web transaction. When a request arrives to the apache web server, it passes it to PHP interpreter for scripting any dynamic contents. Now comes the tricky part - If the PHP script wants to read something from the disk/database or write to it, that is the slowest link in the chain. When you call PHP function file_get_contents(), the entire thread is blocked until the contents are retrieved! The server can’t do anything until your script gets the file contents. Consider what happens when multiples of simultaneous requests are issued by different users to your server? They get queued, because no thread is available to do the job since they are all blocked in I/O!&lt;/p&gt;

&lt;p&gt;Here comes the unique selling-point of node.js. Since node.js implements async I/O in almost all its functions, the server thread in the above scenario is freed as soon as the file retrieval function (fs.readFile) is called. Then, once the I/O completes, node calls a function (passed earlier by fs.readFile) along with the data parameters. In the meantime, that valuable thread can be used for serving some other request.&lt;/p&gt;

&lt;p&gt;So thats the theory about it anyway. But I’m not someone who just accepts any new fad in the town just because it is hype and everyone uses it. Nope, I want to get under the covers and verify it for myself. I wanted to see whether this theory holds in actual practice or not.&lt;/p&gt;

&lt;p&gt;So I brought upon myself the job of writing two simple scripts for benchmarking this - one in PHP (hosted on apache2) and other in javascript (hosted on node.js). The test itself was very simple. The script would:&lt;/p&gt;

&lt;p&gt;1. Accept the request.\&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Generate a random string of 108 kilobytes.\&lt;/li&gt;
  &lt;li&gt;Write the string to a file on the disk.\&lt;/li&gt;
  &lt;li&gt;Read the contents back from disk.\&lt;/li&gt;
  &lt;li&gt;Return the string back on the response stream.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is the first script, index.php:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;?php
//index.php
$s=&quot;&quot;; //generate a random string of 108KB and a random filename
$fname = chr(rand(0,57)+65).chr(rand(0,57)+65).chr(rand(0,57)+65).chr(rand(0,57)+65).&apos;.txt&apos;;
for($i=0;$i&amp;amp;lt;108000;$i++)
{
	$n=rand(0,57)+65;
	$s = $s.chr($n);
}

//write s to a file
file_put_contents($fname,$s);
$result = file_get_contents($fname);
echo $result;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And here is the second script, server.js:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;//server.js
var http = require(&apos;http&apos;);    
var server = http.createServer(handler);

function handler(request, response) {
	//console.log(&apos;request received!&apos;);
	response.writeHead(200, {&apos;Content-Type&apos;: &apos;text/plain&apos;});

	s=&quot;&quot;; //generate a random string of 108KB and a random filename
	fname = String.fromCharCode(Math.floor(65 + (Math.random()*(122-65)) )) +
		String.fromCharCode(Math.floor(65 + (Math.random()*(122-65)) )) +
		String.fromCharCode(Math.floor(65 + (Math.random()*(122-65)) )) + 
		String.fromCharCode(Math.floor(65 + (Math.random()*(122-65)) )) + &quot;.txt&quot;;

	for(i=0;i&amp;amp;lt;108000;i++)
	{
		n=Math.floor(65 + (Math.random()*(122-65)) );
		s+=String.fromCharCode(n);
	}

	//write s to a file
	var fs = require(&apos;fs&apos;);
	fs.writeFile(fname, s, function(err, fd) {
			if (err) throw err;
			//console.log(&quot;The file was saved!&quot;);
			//read back from the file
			fs.readFile(fname, function (err, data) {
				if (err) throw err;
				result = data;
				response.end(result);
			});  
		}
	);
}

server.listen(8124);
console.log(&apos;Server running at http://127.0.0.1:8124/&apos;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then, I ran the apache benchmarking tool on both of them with 2000 requests (200 concurrent). When I saw the time stats of the result, I was astounded:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#PHP:
Concurrency Level:      200
Time taken for tests:   574.796 seconds
Complete requests:      2000

#node.js:
Concurrency Level:      200
Time taken for tests:   41.887 seconds
Complete requests:      2000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The truth is out. node.js was faster than PHP by more 14 times! These results are astonishing. It simply means that node.js IS going to be THE de-facto standard for writing performance driven apps in the upcoming future, there is no doubt about it!&lt;/p&gt;

&lt;p&gt;Agreed that the &lt;a href=&quot;http://nodejs.org&quot;&gt;nodejs&lt;/a&gt; ecosystem isn’t that widely developed yet, and most node modules for things like db connectivity, network access, utilities, etc. are actively being developed. But still, after seeing these results, its a no-brainer. Any extra effort spent in developing node.js apps is more than worth it. PHP might be still having the “king of web” status, but with node.js in the town, I don’t see that status staying for very long!&lt;/p&gt;

&lt;h2 id=&quot;update&quot;&gt;Update&lt;/h2&gt;

&lt;p&gt;After reading some comments from the below section, I felt obliged to create a C#/mono version too. This, unfortunately, has turned out to be the slowest of the bunch (~40 secs for 1 request). Either the Task library in mono is terribly implemented, or there is something terribly wrong with my &lt;a href=&quot;http://pastebin.mozilla.org/5406784&quot;&gt;code&lt;/a&gt;. I’ll fix it once I get some time and be back with my next post (maybe ASP.NET vs node.js vs PHP!).&lt;/p&gt;

&lt;h2 id=&quot;second-update&quot;&gt;Second Update&lt;/h2&gt;

&lt;p&gt;As for C#/ASP.NET, this is the most optimum version that I could manage. It still lags behind both PHP and node.js and most of the issued requests just get dropped. (And yes, I’ve tested it on both Linux/Mono and Windows-Server-2012/IIS environments). Maybe ASP.NET is inherently slower, so I’ll have to change the terms of this benchmark to take it into comparison:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public class Handler : System.Web.IHttpHandler
{
	private StringBuilder payload = null;

	private async void processAsync()
	{
		var r = new Random ();

		//generate a random string of 108kb
		payload=new StringBuilder();
		for (var i = 0; i &amp;amp;lt; 54000; i++)
			payload.Append( (char)(r.Next(65,90)));

		//create a unique file
		var fname = &quot;&quot;;
		do{fname = @&quot;c:\source\csharp\asyncdemo\&quot; + r.Next (1, 99999999).ToString () + &quot;.txt&quot;;
		} while(File.Exists(fname));            

		//write the string to disk in async manner
		using(FileStream fs = File.Open(fname,FileMode.CreateNew,FileAccess.ReadWrite))
		{
			var bytes=(new System.Text.ASCIIEncoding ()).GetBytes (payload.ToString());
			await fs.WriteAsync (bytes,0,bytes.Length);
			fs.Close ();
		}

		//read the string back from disk in async manner
		payload = new StringBuilder ();
		StreamReader sr = new StreamReader (fname);
		payload.Append(await sr.ReadToEndAsync ());
		sr.Close ();
		//File.Delete (fname); //remove the file
	}

	public void ProcessRequest (HttpContext context)
	{
		Task task = new Task(processAsync);
		task.Start ();
		task.Wait ();

		//write the string back on the response stream
		context.Response.ContentType = &quot;text/plain&quot;;
		context.Response.Write (payload.ToString());
	}

	public bool IsReusable 
	{
		get {
			return false;
		}
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;References:&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Node.js&quot;&gt;https://en.wikipedia.org/wiki/Node.js&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://notes.ericjiang.com/posts/751&quot;&gt;http://notes.ericjiang.com/posts/751&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://nodejs.org&quot;&gt;http://nodejs.org&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://code.google.com/p/node-js-vs-apache-php-benchmark/wiki/Tests&quot;&gt;https://code.google.com/p/node-js-vs-apache-php-benchmark/wiki/Tests&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content>
 </entry>
 
 <entry>
   <title>How to create a custom email plugin for Wordpress</title>
   <link href="https://prahladyeri.github.io/blog/2014/06/how-to-create-a-custom-email-plugin-for-wordpress.html"/>
   <updated>2014-06-07T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2014/06/how-to-create-a-custom-email-plugin-for-wordpress</id>
   <content type="html">&lt;p&gt;Last week, I suddenly stopped receiving email notifications for my &lt;a href=&quot;https://www.openshift.com/&quot;&gt;openshift&lt;/a&gt; hosted blog. I came to know after some &lt;a href=&quot;http://stackoverflow.com/questions/17583205/sendmail-on-openshift-php-codeigniter/17598537#17598537&quot;&gt;reading&lt;/a&gt; that commonly used cloud hosts such as openshift, aws, etc. are usually blacklisted by most email servers, hence its not a good idea to use them to send mails.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;In any case, why should I depend on my hosting provider for email sending. Until now, I had never bothered about how mail sending worked in wordpress as it used to work out of the box. So last week, I pulled up my socks and decided to put my php &lt;a href=&quot;http://en.wikipedia.org/wiki/Geany&quot;&gt;IDE&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/Xdebug&quot;&gt;debugger&lt;/a&gt; to some good work.&lt;/p&gt;

&lt;p&gt;I decided to use my &lt;a href=&quot;https://sendgrid.com&quot;&gt;sendgrid&lt;/a&gt; account to send mails. All that`s needed now is calling the web service with the credentials they’ve provided. But how to integrate this with my wordpress blog?&lt;/p&gt;

&lt;p&gt;Once I located &lt;strong&gt;&lt;em&gt;where&lt;/em&gt;&lt;/strong&gt; the mail sending functionality is there in wordpress code, adding a new method was a piece of cake!! Turns out that wordpress, by default, just executes the “mail” command which is usually just a symlink on unix boxes actually pointing to &lt;em&gt;/usr/bin/sendmail&lt;/em&gt; or something. I found it in a pluggable function &lt;em&gt;wp_mail()&lt;/em&gt;. (see &lt;em&gt;/wp-includes/pluggable.php&lt;/em&gt;). I also came to know from the codex that &lt;a href=&quot;http://codex.wordpress.org/Pluggable_Functions&quot;&gt;pluggable functions&lt;/a&gt; can be easily overridden by plugins.&lt;/p&gt;

&lt;p&gt;Now all I had to do was write a small plugin in the /wp-content/plugins/sendgrid/ folder and override this wp_mail() function with whatever I want.&lt;/p&gt;

&lt;p&gt;Lo and behold! I started receiving notifications for all comments and contact forms filled, by just writing this one plugin. I found the process so simple and easy to integrate with wordpress that I couldn’t help sharing with you. Here are the two php snippets that you need to place in /wp-content/plugins/&lt;em&gt;your-plugin-name&lt;/em&gt;/ and activate it. &lt;em&gt;Wordpress&lt;/em&gt; will do the rest!&lt;/p&gt;

&lt;p&gt;(First one is the main plugin php file that displays the plugin in your admin menu and overrides the wp_mail function. The second php file contains the actual custom function that sends email via sendgrid.)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;wp-content/plugins/sendgrid/myplugin.php:&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;?php /**
 * Plugin Name: Sendgrid Plugin
 * Plugin URI:  http://prahladyeri.github.io
 * Description: Mail sending using Sendgrid Web API
 * Version:     0.1
 * Author:      Prahlad Yeri
 * Author URI:  http://prahladyeri.github.io
 * License:     MIT
 */

//namespace MailDemo;
require_once(&apos;sendgrid.php&apos;);

add_action( &apos;init&apos;, &apos;plugin_init&apos; );

/**
 * Plugin Name: Prahlad&apos;s mail
 * Description: Alternative way to send a mail
 */
if (!function_exists(&apos;wp_mail&apos;)) 
{
	function wp_mail($to, $subject, $message, $headers = &apos;&apos;, $attachments = array())
	{
		$sto = &apos;&apos;;
		if (is_array($to))
		{
			$sto = implode(&apos;,&apos;,$to);
		} else {
			$sto = $to;
		}
		sendgridmail(&apos;wpadmin@mywebsite.com&apos;, $sto, $subject, $message, $headers);
	}
}

function plugin_init()
{
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;wp-content/plugins/sendgrid/sendgrid.php:&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;?php //wp-content/plugins/sendgrid/sendgrid.php
function sendgridmail($from, $to, $subject, $message, $headers) {        
$url = &apos;https://api.sendgrid.com/&apos;;
$user=&apos;your-sendgrid-username&apos;;   
$pass=&apos;your-sendgrid-password&apos;;       
	
$params = array(       
&apos;api_user&apos;  ?&amp;gt; $user,
		&apos;api_key&apos;   =&amp;gt; $pass,
		&apos;to&apos;        =&amp;gt; $to,
		&apos;subject&apos;   =&amp;gt; $subject,
		&apos;html&apos;      =&amp;gt; &apos;&apos;,
		&apos;text&apos;      =&amp;gt; $message,
		&apos;from&apos;      =&amp;gt; $from,
	  );


	$request =  $url.&apos;api/mail.send.json&apos;;

	// Generate curl request
	$session = curl_init($request);
	// Tell curl to use HTTP POST
	curl_setopt ($session, CURLOPT_POST, true);
	// Tell curl that this is the body of the POST
	curl_setopt ($session, CURLOPT_POSTFIELDS, $params);
	// Tell curl not to return headers, but do return the response
	curl_setopt($session, CURLOPT_HEADER, false);
	curl_setopt($session, CURLOPT_RETURNTRANSFER, true);

	print_r(&apos;obtaining the response&apos;);
	// obtain response
	$response = curl_exec($session);
	print_r(&apos;closing curl session&apos;);
	curl_close($session);
	
	// print everything out
	//print_r($response);
}

//only for testing:
/*$to      = &apos;prahladyeri@yahoo.com&apos;;
$subject = &apos;Testemail&apos;;
$message = &apos;It works!!&apos;;
echo &apos;To is: &apos; + $to;
#wp_mail($to, $subject, $message, array() );
sendgridmail($to, $subject, $message, array());
print_r(&apos;Just sent!&apos;);*/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;References:&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;http://codex.wordpress.org/Pluggable_Functions&quot;&gt;http://codex.wordpress.org/Pluggable_Functions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://stackoverflow.com/q/17583205/849365#17598537&quot;&gt;http://stackoverflow.com/q/17583205/849365#17598537&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://codex.wordpress.org/Pluggable_Functions&quot;&gt;http://codex.wordpress.org/Pluggable_Functions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Geany&quot;&gt;http://en.wikipedia.org/wiki/Geany&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Xdebug&quot;&gt;http://en.wikipedia.org/wiki/Xdebug&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sendgrid.com&quot;&gt;https://sendgrid.com&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sendgrid.com/docs/&quot;&gt;https://sendgrid.com/docs/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.openshift.com/&quot;&gt;https://www.openshift.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content>
 </entry>
 
 <entry>
   <title>The REAL way to make Eclipse run faster on Ubuntu</title>
   <link href="https://prahladyeri.github.io/blog/2014/06/real-way-make-eclipse-run-faster-ubuntu.html"/>
   <updated>2014-06-02T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2014/06/real-way-make-eclipse-run-faster-ubuntu</id>
   <content type="html">&lt;p&gt;If you are still one of those people who are frustrated with the crawling speed of eclipse IDE (especially after the recent clunky releases of Juno/Kepler), then you are in good company! Most of the advice you might have read until now for speeding up Eclipse must have involved tweaking the following parameters in eclipse.ini file:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;-Xmn512m&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-Xms1024m&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-Xmx1024m&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-Xss2m&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-XX&lt;/span&gt;:PermSize&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1024m &lt;span class=&quot;nt&quot;&gt;-XX&lt;/span&gt;:MaxPermSize&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1024m &lt;span class=&quot;nt&quot;&gt;-XX&lt;/span&gt;:+UseParallelGC
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Since Eclipse is pretty much a RAM hungry monster, feeding it with lots of RAM should surely make it run fast, right? Wrong! Until recently I had spent a lot of time tweaking those parameters, but no substantial performance could be gained (though I have 4GB of RAM with i3 which is not a bad configuration). The main issue here is that the underlying linux won’t provide the required boost to eclipse no matter whatever the parameters you provide. For instance, the system monitor shows that eclipse is only consuming 500Mb RAM, now what difference will it make if I provide 2048m to -XX:MaxPermSize?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/eclipse-monitor-300x236.png&quot; alt=&quot;System Monitor&quot; /&gt;&lt;/p&gt;

&lt;p&gt;System Monitor&lt;/p&gt;

&lt;p&gt;My search lead me to another better &lt;a href=&quot;http://ubuntuguide.net/ubuntu-using-ramdisk-for-better-performance-and-fast-response&quot;&gt;approach&lt;/a&gt; to solving this problem. If somehow we can load JDK into a shared memory or a RAM-Disk instead of it starting from the local hard-disk, both startup time and performance could be drastically improved.&lt;/p&gt;

&lt;p&gt;But how do we create a RAM-Disk on linux? Well, if you using ubuntu, then you are in &lt;a href=&quot;http://superuser.com/questions/45342/when-should-i-use-dev-shm-and-when-should-i-use-tmp&quot;&gt;luck!&lt;/a&gt; Ubuntu has a working RAM-Disk folder called &lt;strong&gt;/dev/shm&lt;/strong&gt; that could be globally used by any application as a temporary storage. If you go to that folder, you can see lots of files stored by pulseaudio.&lt;/p&gt;

&lt;p&gt;I thought why not copy the JDK folder to /dev/shm and provide that as a -vm parameter to eclipse. Lo and behold! Eclipse runs about 10 times faster on my ubuntu machine. Try it yourself and let me know (If you are having performance issues with Eclipse, that is..-).&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Google appengine tip: How to clear appcfg credential cache</title>
   <link href="https://prahladyeri.github.io/blog/2014/01/google-appengine-tip-how-to-clear-appcfg-credential-cache.html"/>
   <updated>2014-01-19T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2014/01/google-appengine-tip-how-to-clear-appcfg-credential-cache</id>
   <content type="html">&lt;p&gt;Many a times, it so happens that you need to work with multiple credentials while uploading/downloading apps on Google appengine. In such a scenario, it becomes difficult to switch credentials instantly.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/old/gae_new_Logo.png&quot;&gt;&lt;img src=&quot;/uploads/old/gae_new_Logo.png&quot; alt=&quot;gae_new_Logo&quot; /&gt;{.alignnone .size-full .wp-image-1382 width=”54” height=”48”}&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For instance, you have just uploaded an app using appcfg.py with your google credentials and they are still stored in the cache. So when you want to upload a new app, it won’t ask you for email/password and still try to retrieve old credentials automatically ignoring any command-line parameters you have given! Thus, you keep scratching your head as to why you are getting a permission-denied error while uploading/downloading the app!&lt;/p&gt;

&lt;p&gt;The only way out here is clearing the credential cache of appcfg. On Linux systems, these are stored in a file called:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/home/username/.appcfg_cookies
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On Windows based systems, these are typically stored in:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C:\Users\username\.appcfg_cookies
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Just delete this file and you are done! Next time, appcfg.py will ask you for a fresh google email and password, thus enabling you to upload/download your app.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;References:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://stackoverflow.com/q/5149914/849365&quot;&gt;http://stackoverflow.com/q/5149914/849365&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to create a Python app in Google App Engine</title>
   <link href="https://prahladyeri.github.io/blog/2013/12/how-to-create-a-python-app-in-google-app-engine.html"/>
   <updated>2013-12-06T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2013/12/how-to-create-a-python-app-in-google-app-engine</id>
   <content type="html">&lt;p&gt;Whilst the &lt;a href=&quot;https://developers.google.com/appengine/docs/python&quot;&gt;official pythonic reference&lt;/a&gt; for Google app engine is the way I learnt how to build my first GAE app, I found it a bit frustrating to go through &lt;a href=&quot;https://developers.google.com/appengine/docs/python/#Python_Selecting_the_Python_runtime&quot;&gt;each&lt;/a&gt; and &lt;a href=&quot;https://developers.google.com/appengine/docs/python/#Python_The_environment&quot;&gt;every&lt;/a&gt; link and understand large topics like caching and data stores in detail just to build a small hello world pythonic app in Google App Engine. What I wanted was a quick and dirty tutorial that let me build a small app first, and then let me improvise upon the areas that I needed to dig deeper.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/old/gae_python.png&quot;&gt;&lt;img src=&quot;/uploads/old/gae_python.png&quot; alt=&quot;How to Create a Pythonic app in Google App Engine&quot; /&gt;{.alignnone .wp-image-301 width=”150” height=”150”}&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I couldn’t find such a tutorial anywhere, so I’m writing this one.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Setup your Environment:&lt;/strong&gt; Download and install &lt;a href=&quot;http://www.python.org/getit/releases/2.7/&quot;&gt;python 2.7&lt;/a&gt; for your platform, if you haven’t done so already (as of this writing, only 2.5 and 2.7 versions are supported) . Then, download and install the GAE API from &lt;a href=&quot;https://developers.google.com/appengine/downloads#Google_App_Engine_SDK_for_Python&quot;&gt;here&lt;/a&gt;. MSI setups are available for windows platform. For linux, you can just unzip into a local folder like ‘~/programs/’. The zip file will create a subdir called ‘google_appengine’. Practically, the only two python scripts you are ever going to need to develop a GAE app are:&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dev\_appserver.py&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appcfg.py&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Register your app on appspot.com:&lt;/strong&gt; The next step is to register your subdomain on GAE by visiting &lt;a href=&quot;https://appengine.google.com/&quot;&gt;https://appengine.google.com&lt;/a&gt;. Once you register your app there, you will get a subdomain called &lt;strong&gt;http://&lt;em&gt;your-app-id&lt;/em&gt;.appspot.com&lt;/strong&gt;. There are also options for redirecting your custom domain such as &lt;strong&gt;www.mydomain.com&lt;/strong&gt; to your app subdomain.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Create your app on the local machine:&lt;/strong&gt; This is as simple as creating a folder on your machine such as ~/source/foo in linux or C:\source\foo in windows. Then just create a text file named app.yaml  with below contents inside this folder:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;application: your-app-id
version: 1
runtime: python27
api_version: 1
threadsafe: true
	
handlers:
- url: /.*
  script: helloworld.application
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note that &lt;strong&gt;your-app-id&lt;/strong&gt; is the name that you just registered for yourself, make sure that it is typed correctly. version parameter refers to the version of your app, while api_version is the version of GAE SDK used to run this app. The line “script: helloworld.application” indicates that this wsgi handler will be invoked for your app.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Create the wsgi handler:&lt;/strong&gt; This is as simple as creating a python file named “helloworld.py” in the same folder as above, and add below contents to it:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import webapp2

class MainPage(webapp2.RequestHandler):

    def get(self):
        self.response.headers[&apos;Content-Type&apos;] = &apos;text/plain&apos;
        self.response.write(&apos;Hello, World!&apos;)

application = webapp2.WSGIApplication([
    (&apos;/&apos;, MainPage),
], debug=True)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Test your app: &lt;/strong&gt;To test your app, open up your terminal and change directory to your GAE installation folder (alternatively, add the GAE installation folder to your PATH/$PATH environment variable to avoid doing this each time), and then type the below command:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;python dev_appserver.py ~/source/foo
#OR on windows:
python dev_appserver.py C:\source\foo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Deploy your app:&lt;/strong&gt; Want to host this app on GAE and check it out? Just fire up your terminal as described above and issue this command:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;python appcfg.py update ~/source/foo
#OR on windows:
python appcfg.py update C:\source\foo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Test your app:&lt;/strong&gt; The above command should host your app on your appspot subdomain (It will ask for your google username/password before doing so). Once the app is successfully hosted, you can check it out by visiting http://your-app-id.appspot.com.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Furthur reading:&lt;/strong&gt; Now that you have a working app, you can actually visit the official reference to read more about:
    &lt;ol&gt;
      &lt;li&gt;&lt;a href=&quot;https://developers.google.com/appengine/docs/python/gettingstartedpython27/handlingforms&quot;&gt;webapp2&lt;/a&gt;: The pythonic web framework used to handle requests and generate responses.&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://developers.google.com/appengine/docs/python/#Python_The_Datastore_and_services&quot;&gt;Datastore&lt;/a&gt;: The big data storage feature that GAE provides your app to store its data.&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://developers.google.com/appengine/docs/python/#Python_Quotas_and_limits&quot;&gt;Quotas and Limits&lt;/a&gt;: Learn about the various limits that google sets for your app to access resources (Don’t worry, they are enough to suffice a small to medium scale app).&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://developers.google.com/appengine/docs/python/#Python_App_caching&quot;&gt;App caching&lt;/a&gt;: Learn how to take advantage of various caching mechanisms in GAE to speed up your app.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;
</content>
 </entry>
 
 <entry>
   <title>How to create android dialogs in a reusable manner</title>
   <link href="https://prahladyeri.github.io/blog/2013/12/how-to-create-android-dialogs-in-a-reusable-manner.html"/>
   <updated>2013-12-01T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2013/12/how-to-create-android-dialogs-in-a-reusable-manner</id>
   <content type="html">&lt;p&gt;Creating dialogs is a very common need in your app to show a dialog box to the user in order to fetch a value, be it a mobile, desktop or even a web application. Furthermore, the values can range from anything like simple &lt;em&gt;OK-Cancel&lt;/em&gt; dialog results to a list of “check-able” values or even a date-range.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://prahladyeri.github.io/uploads/old/droid-man.png&quot;&gt;&lt;img src=&quot;/uploads/old/droid-man.png&quot; alt=&quot;droid-man&quot; /&gt;{.alignnone .wp-image-209 width=”60” height=”70”}&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I experienced the need to create an android dialog for each one of those for showing reports in a recent android app project. Whilst the java api offers maximum flexibility in creating dialog interface elements, there is no ready-made method that can be called to get, say a result for a message-dialog like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;result = MessageBox.Show();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Other languages like C# and VB provide such methods to show modal dialog boxes that return values after waiting for a modal dialog. But unfortunately, there is no concept of “modal” in android. A thread cannot just sit idle waiting for input as the resources are too valuable for that. Instead, there is the concept of callbacks, so that instead of you waiting for the dialog to return, the method calls back a function reference you have passed it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;AlertDialog.Builder builder=new AlertDialog.Builder(context);
builder.setTitle(&quot;Milk supply tracker&quot;);
builder.setMessage(message);
builder.setPositiveButton(&quot;Yes&quot;,listener);
builder.setNegativeButton(&quot;No&quot;,listener);
builder.create().show();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The listener here is the referece to a function that will be called when the Yes or No button will be clicked. This not only complicates your code, but makes it very difficult to reuse code for handling different situations like getting a selection from a range of values or getting a date/time range. To solve this problem, I created a separate java class called Dialog and added variations of ShowDialog() methods to handle each type of dialog:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class Dialog
{
	public static void ShowMessageDialog(Context context, String message)
	{
		ShowDialog(context,message,MessageBoxType.OKOnly,new String[]{},false, null,null);
	}

	public static void ShowMessageDialog(Context context, String message, MessageBoxType type , OnClickListener listener)
	 {
		 ShowDialog(context,message,type,new String[]{},false, listener,null);
	 }

	public static void ShowListDialog(Context context, String message, String[] listItems, boolean isMultiChoice, OnClickListener listener)
	 {
	 if (isMultiChoice)
		 ShowDialog(context, message, MessageBoxType.OkCancel , listItems, isMultiChoice, listener,null);
	 else
		 ShowDialog(context, message, MessageBoxType.OKOnly , listItems, isMultiChoice, null,listener);
	 }

	public static void ShowDateDialog(Context context,String message,OnDateSetListener listener)
	{
		Calendar c=Calendar.getInstance();
		int y=c.get(Calendar.YEAR);
		int m=c.get(Calendar.MONTH);
		int d=c.get(Calendar.DAY_OF_MONTH);

		DatePickerDialog dlg=new DatePickerDialog(context, listener, y, m, d);
		dlg.setTitle(message);
		dlg.show();
	}

}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, the ShowMessageDialog() accepts different parameters depending on whether a listener is required or not. ShowListDialog(), on the other hand passes an array of strings to create a dialog displaying a list of values from which a user may select. The isMultiChoice parameter tells whether a checkbox is required or not against each value in the select list. All this is actually implemented in the ShowDialog() private method, whereas the ShowDateDialog() has its own implementation. Here is the source for ShowDialog():&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;private static void ShowDialog(Context context, String message, MessageBoxType type , String[] listItems, boolean isMultiChoice, OnClickListener listener,OnClickListener selectedItemListener)
{
	AlertDialog.Builder builder=new AlertDialog.Builder(context);

	if (listItems.length&amp;gt;0 &amp;amp;&amp;amp; isMultiChoice==false)
	{
		CheckedItems=new ArrayList();//won&apos;t be used in this case.
		builder.setTitle(message);

		builder.setItems(listItems, selectedItemListener);
	}
	else if (listItems.length&amp;gt;0 &amp;amp;&amp;amp; isMultiChoice==true)
	{
		CheckedItems=new ArrayList();
		builder.setTitle(message);

		builder.setMultiChoiceItems(listItems, null, new OnMultiChoiceClickListener() 
		{
			@Override
			public void onClick(DialogInterface dialog, int which, boolean checked) 
			{
				if (checked)
					CheckedItems.add(which);
				else
				{
					if (CheckedItems.contains(which))
						CheckedItems.remove(which);
				}
			}
		});
	}
	else
	{
		builder.setTitle(&quot;Milk supply tracker&quot;);
		builder.setMessage(message);
	}

	if (listItems.length==0 || isMultiChoice)
	{
		switch(type)
		{
		case OKOnly:
			builder.setPositiveButton(&quot;OK&quot;,listener);
			break;
		case OkCancel:
			builder.setPositiveButton(&quot;OK&quot;,listener);
			builder.setNegativeButton(&quot;Cancel&quot;,listener);
			break;
		case YesNo:
			builder.setPositiveButton(&quot;Yes&quot;,listener);
			builder.setNegativeButton(&quot;No&quot;,listener);
			break;
		}           
	}

	builder.create().show();
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So hopefully, this class should suffice all your needs related to showing a dialog on your android app. Here is a working example of how the ShowListDialog() is actually called with checkboxes on:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;selItems=new String[]{&quot;apples&quot;,&quot;oranges&quot;,&quot;grapes&quot;};
Device.ShowListDialog(this,&quot;Select a fruit&quot; ,this.selItems, true, new DialogInterface.OnClickListener() {

		@Override
		public void onClick(DialogInterface dialog, int which) 
		{
			if (which==DialogInterface.BUTTON_POSITIVE)
			{
				for(int i:Device.CheckedItems)
				Dialog.ShowMessageDialog(ReportsActivity.this, &quot;selected:&quot; + selItems[i]);
			}
		}
	});
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt; &lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to uniquely identify your Android device in code</title>
   <link href="https://prahladyeri.github.io/blog/2013/11/how-to-uniquely-identify-your-android-device-in-code.html"/>
   <updated>2013-11-28T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2013/11/how-to-uniquely-identify-your-android-device-in-code</id>
   <content type="html">&lt;p&gt;My last android project involved tracking each device where the app is installed and storing the information to a database. It is quite a common need to uniquely identify your android device in code.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/droid-man.png&quot; alt=&quot;droid-man&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now, had it been a PC, it would have been easy to track the MAC-address of the NIC or an HDD serial to uniquely identify and track that device. But what is the equivalent for android?&lt;/p&gt;

&lt;p&gt;My research first led me to IMEI number (&lt;em&gt;TelephonyManager.getDeviceId()&lt;/em&gt;). IMEI is a unique number associated with your device (pretty much like a vehicle’s chassis number) and is widely used for tracking cellphones. The android API provides this ready-made function to uniquely identify your device if you wish to go this route:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public static String getDeviceIdTm(Context context)
{
	TelephonyManager tm=(TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
	return tm.getDeviceId();
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But wait, not all devices are equipped with Telephony. What about tablets and amazon kindle devices? It so happens, that this is just one way to track your device, but it is not full-proof. It will work only for phones and for other devices this function will return null.&lt;/p&gt;

&lt;p&gt;This led me to another way of tracking an Android device: An in-built variable that the Android system itself provides you: ANDROID_ID. In theory, this variable is all you need to know to identify your device uniquely:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public static String getDeviceIdAndroid(Context context)
{
	return Secure.getString(context.getContentResolver(),Secure.ANDROID_ID);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But Alas! Even this is not full-proof. It will work on most modern versions of android (HoneyComb and above). Again, due to a manufacturer bug, it will not return a unique value, but a constant value “9774d56d682e549c” on some handsets.&lt;/p&gt;

&lt;p&gt;This led me to a third way of identifying my device which was a bit hackish. I prefer not to use this method if any of the prior two methods worked.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public static String getDeviceIdPseudo(Context context)
 {
	 String tstr=&quot;&quot;;
	 if (Build.VERSION.SDK_INT &amp;gt; Build.VERSION_CODES.FROYO) {
		 tstr+= Build.SERIAL;
		 tstr += &quot;::&quot; + (Build.PRODUCT.length() % 10) + (Build.BOARD.length() % 10) + (Build.BRAND.length() % 10) + (Build.CPU_ABI.length() % 10) + (Build.DEVICE.length() % 10) + (Build.MANUFACTURER.length() % 10) + (Build.MODEL.length() % 10);
		 return tstr;
	}
 }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This method computes a Pseudo-id for your device taking reference to some hardware values. If the previous two methods don’t work, then this is all you are left with for device identification.&lt;/p&gt;

&lt;p&gt;I then integrated the above three methods to create a generic method called &lt;strong&gt;&lt;em&gt;getDeviceIdUnique()&lt;/em&gt;&lt;/strong&gt; that will work on all android devices - irrespective of whether its a phone/tablet or what make it is:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public static String getDeviceIdUnique(Context context)
 {
	 try {
		 String a = getDeviceIdTm(context);
		 String b = getDeviceIdAndroid(context);
		 String c = getDeviceIdPseudo(context);

		 if (a!=null &amp;amp;&amp;amp; a.length()&amp;gt;0 &amp;amp;&amp;amp; a.replace(&quot;0&quot;, &quot;&quot;).length()&amp;gt;0) 
			 return a;
		 else if (b!=null &amp;amp;&amp;amp; b.length()&amp;gt;0 &amp;amp;&amp;amp; b.equals(&quot;9774d56d682e549c&quot;)==false) 
			 return b;
		 else if (c!=null &amp;amp;&amp;amp; c.length()&amp;gt;0) 
			 return c;
		 else
			 return &quot;&quot;;
		 }
	 catch(Exception ex)
	 {
		 return &quot;&quot;;
	 }
 }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;References:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://stackoverflow.com/q/2785485/849365&quot;&gt;http://stackoverflow.com/q/2785485/849365&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://stackoverflow.com/q/4468248/849365&quot;&gt;http://stackoverflow.com/q/4468248/849365&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>The 7 "Bread and Butter" Plugins for your Wordpress blog</title>
   <link href="https://prahladyeri.github.io/blog/2013/11/the-7-bread-and-butter-plugins-for-your-wordpress-blog.html"/>
   <updated>2013-11-27T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2013/11/the-7-bread-and-butter-plugins-for-your-wordpress-blog</id>
   <content type="html">&lt;p&gt;Based on my experiments while setting up this blog, below are the 7 “bread and butter” plugins for your wordpress blog. These plugins came very handy for me and allowed me to seamlessly integrate much needed functionality in my blog without writing a single line of php code.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/old/database_active.png&quot;&gt;&lt;img src=&quot;/uploads/old/database_active.png&quot; alt=&quot;Wordpress Plugin&quot; /&gt;{.alignnone .wp-image-186 width=”77” height=”77”}&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;http://wordpress.org/plugins/contact-form-7/&quot;&gt;Contact Form 7&lt;/a&gt; (Contact Forms): &lt;/strong&gt;While designing this blog, a Contact page was one of my primary requirements. This plugin is popular and well maintained. Once installed, you will have a Contact tab on your dashboard that has a default contact-form with a short-code. You can copy-paste this short-code into any post or page of yours, and lo and behold! You have a contact form ready &lt;del&gt;such as &lt;a href=&quot;http://prahladyeri.github.io/contact/&quot;&gt;the one on this blog&lt;/a&gt;&lt;/del&gt;. Of course, you can customize the default contact-form to change the fields to suit your particular needs. By default, the form filled by the user is mailed to the admin user’s email address. If you want to store the form data in the WP database in addition to that, you will have to install the &lt;a href=&quot;http://wordpress.org/extend/plugins/flamingo/&quot;&gt;Flamingo&lt;/a&gt; plugin along with this.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;http://wordpress.org/plugins/jetpack/&quot;&gt;Jetpack&lt;/a&gt; (Multi-purpose): &lt;/strong&gt;This is what I would term the “bread-and-butter” plugin for wordpress blog owners. Brought to you by Automattic (the company behind Wordpress), this plugin provides almost every feature a blogger can think of such as:
    &lt;ul&gt;
      &lt;li&gt;Comprehensive statistics such as hits per page/post, incoming/outoing links, referrals, etc.&lt;/li&gt;
      &lt;li&gt;Social buttons and a social-networking based comment system.&lt;/li&gt;
      &lt;li&gt;Email subscriptions for your blog posts and comments.&lt;/li&gt;
      &lt;li&gt;A mobile theme that automatically streamlines your site for mobile visitors.&lt;/li&gt;
      &lt;li&gt;Allowing your readers to login to your blog using their wordpress.com account.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;http://wordpress.org/plugins/akismet/&quot;&gt;Akismet&lt;/a&gt; (Spam control): &lt;/strong&gt;Again, this is a “bread and butter” kind of plugin. What kind of blogger, in his right mind, will trust comments on his high-traffic blog to be spam free! Akismet is a web-service that specializes in eradicating spam. Once you install this plugin, all your comments will be scanned by this web service before they make way to your dashboard.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;http://wordpress.org/extend/plugins/wordpress-importer/&quot;&gt;WordPress Importer&lt;/a&gt; (migrating your existing wordpress blog): &lt;/strong&gt;If you are migrating your posts from another wordpress hosted blog or a wordpress.com blog (like I did), you need this plugin to import the posts that you exported from there. In fact, wordpress automatically prompts you to install this plugin when you go to Dashboard-&amp;gt;Tools-&amp;gt;Import to import your posts.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;http://wordpress.org/plugins/xml-sitemap-feed/&quot;&gt;XML Sitemap &amp;amp; Google News Feeds&lt;/a&gt; (Submitting sitemaps to spiders): &lt;/strong&gt;A well written blog must also be searchable by spiders and search-engines, so that it can send readers to your blog. Now, when you submit your blog url to the search-engines like Google or Bing, it pays to have a sitemap so that the spiders know how is your blog or website structured. After installing this plugin, an automatic sitemap url is seamlessly generated for you at http://Your-WP-URL/sitemap.xml. You can then submit this sitemap url to the search engines and thus increase the visibility of your wordpress blog.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;http://wordpress.org/plugins/w3-total-cache/&quot;&gt;W3 Total Cache&lt;/a&gt; (caching and performance): &lt;/strong&gt;Whilst I haven’t installed this plugin myself yet, it is said to improve the performance of your blog by serving static content to your users instead of running heavy-duty php scripts that increase your backend load. This plugin is also recommended by the XML Sitemap plugin.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;http://wordpress.org/plugins/backup/&quot;&gt;Backup&lt;/a&gt; (backing up your wordpress site):&lt;/strong&gt; You usually take an export of your posts from the Tools-Export menu, but if you want to backup your entire wordpress blog including the mysql database dump and all files used for hosting your wordpress blog, then this plugin is the way to go.&lt;/li&gt;
&lt;/ol&gt;
</content>
 </entry>
 
 <entry>
   <title>How to Generate PDFs in Python for Google App Engine</title>
   <link href="https://prahladyeri.github.io/blog/2013/11/how-to-generate-pdf-in-python-for-google-app-engine.html"/>
   <updated>2013-11-26T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2013/11/how-to-generate-pdf-in-python-for-google-app-engine</id>
   <content type="html">&lt;p&gt;One of my last projects based on google app engine and python involved storing form data in GAE datastore and generating PDF documents that the user can download. Whilst data storing was the easier part as google’s big data API it is pretty &lt;a href=&quot;https://developers.google.com/appengine/docs/python/datastore/&quot;&gt;well documented&lt;/a&gt;, the trickier aspect was to convert it to PDF using python.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/pdf-300x300.png&quot; alt=&quot;pdf&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This was especially difficult in the face of GAE not providing an easy mechanism for disk writing that most PDF generation libraries require. To share my endeavors, I’m writing this post about how to generate pdfs in python for Google app engine.&lt;/p&gt;

&lt;p&gt;The solution I came across was, as far as I know, the only possible way of generating PDFs in python! There are about three PDF generation utilities in python, each differing in terms of their area of usage:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.reportlab.com&quot;&gt;&lt;strong&gt;Reportlab PDF library&lt;/strong&gt;&lt;/a&gt;: This is the ideal library, if you want to create a pdf from scratch. It provides objects like canvas, pdfmetrics and ttfonts that help you with stuff like adding lines, shapes, images and paragraphs. This is pretty much comparable to the comprehensive iText java library or its C# port, iTextSharp. Their &lt;a href=&quot;http://www.reportlab.com/software/documentation/&quot;&gt;documentation&lt;/a&gt; is also good.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.xhtml2pdf.com/&quot;&gt;&lt;strong&gt;xhtml2pdf&lt;/strong&gt;&lt;/a&gt;: If you want to simply convert an existing html document to pdf, the xhtml2pdf library comes in very handy.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://pypi.python.org/pypi/pyPdf&quot;&gt;&lt;strong&gt;pyPDF&lt;/strong&gt;&lt;/a&gt;: If all you want to do is merge two PDFs page by page quickly, this library is the way to go.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I figured out after researching the above three libraries that a combination of xhtml2pdf and pyPDF is what I needed. Since I already had the html document template ready, I just put placeholders for my form data like __name__ , __occupation__, etc so that I can fill these before converting to PDF.&lt;/p&gt;

&lt;p&gt;Now, I could fill these values from my python program, but the real challenge was storing the resulting PDF to disk, which was not allowed by google app engine! Turns out, we don’t need to actually store anything to disk. By sending the CreatePDF() output to a &lt;a href=&quot;http://docs.python.org/library/stringio.html&quot;&gt;StringIO&lt;/a&gt; object, which is stored in memory instead of the filesystem, I could bypass the need to actually store anything to disk!!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;f=open(&apos;template.htm&apos;,&apos;r&apos;)
sourceHtml = unicode(f.read(), errors=&apos;ignore&apos;)
f.close()
sourceHtml = template.render(tvals)
sourceHtml = sourceHtml.replace(&apos;__name__&apos;,sname)
sourceHtml = sourceHtml.replace(&apos;__address__&apos;,saddress)
sourceHtml = sourceHtml.replace(&apos;__occupation__&apos;,will.occupation)
packet = StringIO.StringIO() #write to memory
pisa.CreatePDF(sourceHtml,dest=packet)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, it would have been simple to just self.response.write(packet) to send this pdf download to the user, but in my case, I had to merge this generated pdf with another template-pdf which contained information like symbols, images and page-numbers that for some reason, could not be placed into the html document. So, I had to create a PdfFileReader object (coutesy of PyPDF library!), and then merge each page of my generated document with this template document. Then where do I write this merged output? Any guesses? - another StringIO object!! And then finally, write this StringIO object to self.response, so the user can download it.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;packet.seek(0)
new =PdfFileReader(packet) #generated pdf
template = PdfFileReader(file(&quot;template.pdf&quot;, &quot;rb&quot;)) #template pdf
output=PdfFileWriter() #writer for the merged pdf
for i in range(new.getNumPages()):
	page=template.getPage(i)
	page.mergePage(new.getPage(i))
	output.addPage(page)

outputStream = StringIO.StringIO()
output.write(outputStream) #write merged output to the StringIO object

self.response.headers[&apos;Content-Type&apos;] = &apos;application/pdf&apos;
fname = (will.name if mirror==&apos;n&apos; else will.partner)
self.response.headers[&apos;Content-Disposition&apos;] = &apos;attachment; filename=&apos; + str(fname).replace(&apos; &apos;,&apos;_&apos;) + &apos;.pdf&apos;
self.response.write(outputStream.getvalue())
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Remember to add and include the below libraries before you do this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import StringIO
import xhtml2pdf.pisa as pisa
from pyPdf import PdfFileWriter,PdfFileReader
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from reportlab.pdfbase import pdfmetrics,ttfonts
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt; 
&lt;em&gt;References:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developers.google.com/appengine/docs/python/datastore/&quot;&gt;https://developers.google.com/appengine/docs/python/datastore/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.reportlab.com/software/documentation/&quot;&gt;http://www.reportlab.com/software/documentation/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.xhtml2pdf.com/&quot;&gt;http://www.xhtml2pdf.com/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://pypi.python.org/pypi/pyPdf&quot;&gt;https://pypi.python.org/pypi/pyPdf&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://stackoverflow.com/q/4899885/849365&quot;&gt;http://stackoverflow.com/q/4899885/849365&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://stackoverflow.com/q/13522638/849365&quot;&gt;http://stackoverflow.com/q/13522638/849365&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://docs.python.org/library/stringio.html&quot;&gt;http://docs.python.org/library/stringio.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>3 Steps to integrate barcode scanning in your Android app</title>
   <link href="https://prahladyeri.github.io/blog/2013/11/three-steps-to-integrate-barcode-scanning-in-your-android-app.html"/>
   <updated>2013-11-24T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2013/11/three-steps-to-integrate-barcode-scanning-in-your-android-app</id>
   <content type="html">&lt;p&gt;Whilst barcode scanning is a pretty complex and non-trivial task in itself, it could be overwhelming sometimes with android programming. Lucky for us, there exists an opensource project called &lt;a href=&quot;https://github.com/zxing/zxing&quot;&gt;&lt;em&gt;ZXing (pronounced Zebra-crossing)&lt;/em&gt;&lt;/a&gt; that solves this problem for us.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/barcode.png&quot; alt=&quot;Barcode&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The ZXing project has already done the heavy lifting by programming the core java components required to scan a 1d/2d barcode or even a PR-code in the form of a &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.google.zxing.client.android&quot;&gt;Google-play app&lt;/a&gt;. All you have to do is send an intent to this app and receive the scanned results that you may use in your app.&lt;/p&gt;

&lt;p&gt;The ZXing project is Apache licensed, so it is free to use without any kind of restrictions. Follow these steps to integrate ZXing with your app:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Step 1:&lt;/em&gt;&lt;/strong&gt; Download the source for IntentIntegrator.java and IntentResult.java from &lt;a href=&quot;http://code.google.com/p/zxing/source/browse/trunk#trunk%2Fandroid-integration%2Fsrc%2Fmain%2Fjava%2Fcom%2Fgoogle%2Fzxing%2Fintegration%2Fandroid&quot;&gt;here&lt;/a&gt; and add the files to your android project sources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Step 2:&lt;/em&gt;&lt;/strong&gt; Start an intent in that part of your code where you would like to initiate the barcode scanning (such as a menu handler):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;IntentIntegrator integrator = new IntentIntegrator(yourActivity);
integrator.initiateScan();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Step 3:&lt;/em&gt;&lt;/strong&gt; All that remains now is to handle the result of this activity in your onActivityResult() handler. This is how I did it in my code:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if (requestCode==IntentIntegrator.REQUEST_CODE)
{
	IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
	if (scanResult != null)
	{
		// handle scan result
		//MessageBox.run(this, &quot;&quot;, &quot;toString() returns: &quot; + scanResult.toString());
	}
	else
	{
		// else continue with any other code you need in the method
		MessageBox.run(this, &quot;&quot;, &quot;scanResult is null.&quot;);
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That`s all! The above code not only returns the barcode scanning result to your app, but even prompts the user to install a barcode scanner app in case one isn’t there. This is the easiest and recommended way of integrating barcode scanning in your android app.&lt;/p&gt;

&lt;p&gt;In case you want to embed the entire ZXing component in your app and don’t want to install an app separately for it, refer to the relevant links in the references section. However, this method is not recommended as your app won’t get the updates from ZXing.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;References:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://stackoverflow.com/q/11205183/849365&quot;&gt;http://stackoverflow.com/q/11205183/849365&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://stackoverflow.com/q/4854442/849365#4854637&quot;&gt;http://stackoverflow.com/q/4854442/849365#4854637&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://stackoverflow.com/q/16433860/849365&quot;&gt;http://stackoverflow.com/q/16433860/849365&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/zxing/zxing&quot;&gt;https://github.com/zxing/zxing&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://code.google.com/p/zxing/wiki/ScanningViaIntent&quot;&gt;http://code.google.com/p/zxing/wiki/ScanningViaIntent&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Some Wordpress tips and tricks</title>
   <link href="https://prahladyeri.github.io/blog/2013/11/some-wordpress-tips-and-tricks.html"/>
   <updated>2013-11-23T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2013/11/some-wordpress-tips-and-tricks</id>
   <content type="html">&lt;p&gt;Wordpress is a universally recognized and robust blogging platform written in the PHP language. Below are a bunch of Wordpress tricks that I’ve learned during my deployments, and I’d like to share with you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;1. Change breadcrumbs to start from “Home” instead of Site-Title&lt;/em&gt;&lt;/strong&gt;: When I set up this site, the breadcrumbs in the post used to read like this:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://prahladyeri.github.io/&quot;&gt;Prahlad Yeri » &lt;/a&gt;&lt;a href=&quot;https://prahladyeri.github.io/category/wordpress/&quot;&gt;wordpress » &lt;/a&gt;WordPress installation quick-start guide&lt;/p&gt;

&lt;p&gt;However, I wanted it to look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://prahladyeri.github.io/&quot;&gt;Home » &lt;/a&gt;&lt;a href=&quot;https://prahladyeri.github.io/category/wordpress/&quot;&gt;wordpress » &lt;/a&gt;WordPress installation quick-start guide&lt;/p&gt;

&lt;p&gt;In order to do that, you need to look into your theme-functions.php file and see where wordpress is echoing your breadcrumbs. In the mantra theme, I found this in the function mantra_breadcrumbs(). So accordingly, I changed get_bloginfo(‘name’) and hard-coded it to ‘Home’:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;//changed by prahlad
//echo &apos;&amp;lt;a href=&quot;&apos;.get_bloginfo(&apos;url&apos;).&apos;&quot;&amp;gt;&apos;.get_bloginfo(&apos;name&apos;).&apos; &amp;amp;raquo; &amp;lt;/a&amp;gt;&apos;; 
echo &apos;&amp;lt;a href=&quot;&apos;.get_bloginfo(&apos;url&apos;).&apos;&quot;&amp;gt;&apos;.&apos;Home&apos;.&apos; &amp;amp;raquo; &amp;lt;/a&amp;gt;&apos;;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You have to do this at multiple places in the mantra_breadcrumbs() (or your theme’s equivalent function) wherever get_bloginfo(‘name’) is used and replace it with ‘Home’.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;2. How to crop off the big page-title that appears on top of pages:&lt;/em&gt; &lt;/strong&gt;This is a nice little css trick.  Just open your theme’s style.css and switch off the display for “entry-title” class elements within the “page” class elements. This will hide the page titles only on page-titles (and not posts!):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/*start: prahlad - used to hide post-titles on all pages (not posts)*/
.page .entry-title {
 display: none;
}
/*end: prahlad*/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you notice that some whitespace is still left on top of the page, you can try some of these modifiers:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.site-header {
 padding-bottom: 0;
}

.site-content {
 margin-top: 0;
}

#content {
 margin-top: -20px;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;3. How to add a contact-form to your post or page:&lt;/em&gt;&lt;/strong&gt;  In order to do that you need a Contact-Form plugin. The most popular and well-maintained plugin at that is “Contact-Forms 7”:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://wordpress.org/plugins/contact-form-7/&quot;&gt;http://wordpress.org/plugins/contact-form-7/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a pretty decent form plugin that, from a single line of short-code (in your page or post), creates a basic all-purpose contact-form such as this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/old/Contact-Forms-7.png&quot; alt=&quot;Page created with Contact Forms 7&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Page created with Contact Forms 7&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you install this plugin, you will see an additional “Contact” tab in your dashboard. When you click that, you will see a default form with a short-code like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[contact-form-7 id=&quot;116&quot; title=&quot;Contact form 1&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can copy-paste this short-code anywhere in the editor for your page or post. For this site’s contact page, it looked something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[contact-form-7 id=&quot;116&quot; title=&quot;Contact form 1&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By default, the information in the text-fields are mailed to the admin user’s email-address when a user submits this form. You can change all this by editing the default form that you find in the “Contact” tab of your dashboard. You can add/remove the text-fields, add validation and much &lt;a href=&quot;http://contactform7.com/tag-syntax/&quot;&gt;more&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another plugin that works in combination with this is the &lt;a href=&quot;http://wordpress.org/plugins/flamingo&quot;&gt;flamingo plugin&lt;/a&gt;. It is useful in case you want to store the contact information in the wordpress database in addition to getting it mailed to your email address.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;4: Change the footer notices (such as “Powered by Wordpress”, etc..):&lt;/em&gt;&lt;/strong&gt; Just open your footer.php from editor and try to find a snippet such as this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;footer id=&quot;colophon&quot; role=&quot;contentinfo&quot;&amp;gt;
 &amp;lt;div class=&quot;site-info&quot;&amp;gt;
 &amp;lt;?php do_action( &apos;twentytwelve_credits&apos; ); ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Change it accordingly to suit your needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;References:&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.cryoutcreations.eu/forums/t/breadcrumbs-how-to-show-home-instead-of-site-title-in-the-breadcrumbs&quot;&gt;http://www.cryoutcreations.eu/forums/t/breadcrumbs-how-to-show-home-instead-of-site-title-in-the-breadcrumbs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://wordpress.org/plugins/contact-form-7/&quot;&gt;http://wordpress.org/plugins/contact-form-7/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.wpsquared.com/top-10-wordpress-contact-form-plugins/&quot;&gt;http://www.wpsquared.com/top-10-wordpress-contact-form-plugins/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://wordpress.org/support/topic/plugin-contact-form-7-16?replies=5&quot;&gt;http://wordpress.org/support/topic/plugin-contact-form-7-16?replies=5&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://contactform7.com/text-fields/&quot;&gt;http://contactform7.com/text-fields/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://contactform7.com/tag-syntax/&quot;&gt;http://contactform7.com/tag-syntax/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Wordpress installation quick-start guide</title>
   <link href="https://prahladyeri.github.io/blog/2013/11/wordpress-installation-quick-start-guide.html"/>
   <updated>2013-11-17T00:00:00+00:00</updated>
   <id>https://prahladyeri.github.io/blog/2013/11/wordpress-installation-quick-start-guide</id>
   <content type="html">&lt;p&gt;Wordpress is a blogging platform that is very easy to use, but involves many configuration trivia which may become overwhelming, unless organized and documented somewhere. Based on my experience of setting up this website, here are the things that I had to keep in mind to get it up and running:&lt;!--more--&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Wordpress file-structure:&lt;/strong&gt; A brief understanding of the file structure helps before delving into the installation details. The folder where you upload the contents of wordpress installation files has an index.php file that triggers other configuration files from there. You have to setup the mysql database credentials in the &lt;em&gt;wp-config.php&lt;/em&gt; file before starting the installation. Inside the root folder, there are two sub-folders, &lt;strong&gt;wp-admin&lt;/strong&gt; and &lt;strong&gt;wp-content&lt;/strong&gt;. The wp-admin folder stores the necessary files to bring up the admin interface when you type http://your-wp-url/wp-admin to administer your site for adding posts, moderating comments, etc. The wp-content folder on the other hand, stores contains files and folders to create the structure, functionality and look and feel of your blog. The wp-content folder further has &lt;strong&gt;wp-themes&lt;/strong&gt; and &lt;strong&gt;wp-plugins&lt;/strong&gt; folders. The former controls the structure and look and feel (themes), whereas the latter is for adding extra bits of extra functionality (plugins).
    &lt;ul&gt;
      &lt;li&gt;&lt;em&gt;root-folder » index.php, wp-config.php, wp-settings.php&lt;/em&gt;
        &lt;ul&gt;
          &lt;li&gt;&lt;em&gt;wp-admin - Files &amp;amp; folders to control admin interface.&lt;/em&gt;&lt;/li&gt;
          &lt;li&gt;&lt;em&gt;wp-content&lt;/em&gt;
            &lt;ul&gt;
              &lt;li&gt;&lt;em&gt;wp-themes - Files and folders to control look and feel.&lt;/em&gt;&lt;/li&gt;
              &lt;li&gt;&lt;em&gt;wp-plugins - Files and folders to add bits of extra functionality.&lt;/em&gt;&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li&gt;&lt;em&gt;wp-includes - Contains header files for other php scripts.&lt;/em&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;!-- --&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Famous File-minute installation:&lt;/strong&gt;Once you have the basic understanding, you may proceed with the much advertised&lt;a href=&quot;http://codex.wordpress.org/Installing_WordPress#Famous_5-Minute_Install&quot; title=&quot;famous five minute installation&quot;&gt;&lt;em&gt;famous five minute installation&lt;/em&gt;&lt;/a&gt; of your wordpress site. Basically, once you have set the mysql credentials in wp-config.php, wordpress does the rest by creating the required tables for your blog-posts, comments and other elements. But make sure, the wp-user you configure has suffient rights to create/alter/query/etc. on the wordpress database you have configured.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Configuration:&lt;/strong&gt; These settings in wp-config.php come quite handy during deployment:
    &lt;ul&gt;
      &lt;li&gt;DB_NAME - mysql database name reserved for the wordpress blog.&lt;/li&gt;
      &lt;li&gt;DB_USER - mysql user-id.&lt;/li&gt;
      &lt;li&gt;DB_PASSWORD - mysql password.&lt;/li&gt;
      &lt;li&gt;DB_HOST - hostname of the machine where mysql is running.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The above settings are almost always different in your local environment from that of your web-hosting machine. Hence, it is advisable that you keep the local version of wp-config.php separate by adding an exception to version control systems like git, svn, etc. if you are using any.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Site name settings:&lt;/strong&gt; Two php global variables called WP_HOME and WP_SITEURL are almost always a source of trouble during migration of wordpress sites. These variables tell wordpress the url of your currently hosted wordpress site (such as http://localhost/wp or http://www.mysite.com/blog). When you migrate your database settings to your web host, wordpress obviously can’t find the localhost there. To temporarily solve this issue, you can hard-code these values at the start of wp-config.php like this:
    &lt;ul&gt;
      &lt;li&gt;define(‘WP_HOME’,’http://workstation2/rhc/prahladyeri/php’);&lt;/li&gt;
      &lt;li&gt;define(‘WP_SITEURL’,’http://workstation2/rhc/prahladyeri/php’);&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have access to wp-admin, you should remove them from wp-config.php and go to Settings-&amp;gt;General to set these variables in a proper manner under the fields, “Wordpress Address (URL)” and “Site Address (URL)”.&lt;/p&gt;
</content>
 </entry>
 

</feed>
