<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jampa Matos</title>
    <description>The latest articles on Forem by Jampa Matos (@jampamatos).</description>
    <link>https://forem.com/jampamatos</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F740318%2Faeb8de8d-9229-45f6-a1c8-cf17b2c83bf6.jpeg</url>
      <title>Forem: Jampa Matos</title>
      <link>https://forem.com/jampamatos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jampamatos"/>
    <language>en</language>
    <item>
      <title>Press Start to Continue</title>
      <dc:creator>Jampa Matos</dc:creator>
      <pubDate>Sat, 07 Jun 2025 05:43:26 +0000</pubDate>
      <link>https://forem.com/jampamatos/press-start-to-continue-2m8g</link>
      <guid>https://forem.com/jampamatos/press-start-to-continue-2m8g</guid>
      <description>&lt;p&gt;These blog-like communities are a bit funny to me. &lt;/p&gt;

&lt;p&gt;People pop in here, all professorial-like, sharing insights and dropping little tips about stuff they've discovered. And that's totally cool. But sometimes it feels like people are doing it because they're supposed to, not necessarily because they're bursting with amazing things they just HAVE to share.&lt;/p&gt;

&lt;p&gt;And look, I get it. You gotta show up to get noticed. Part of networking is having a voice and, as we say here in Brazil, being able to "sell your fish" (I’m sure there’s an equivalent expression in English, but honestly, I'm too lazy to look it up). And nothing says "I'm GOOD" more than teaching people some neat new trick to make that div box land EXACTLY where you want it -- because honestly, THOSE DIV BOXES ARE ALIVE AND ACTIVELY TESTING MY PATIENCE AND ONE DAY THEY'LL DRIVE ME CRAZY!&lt;/p&gt;

&lt;p&gt;Right, that was a rant. Sorry.&lt;/p&gt;

&lt;p&gt;Anyway, I'm pretty green to the whole tech thing. It's funny, cause I've been at it for a while now, as my &lt;a href="https://github.com/jampamatos" rel="noopener noreferrer"&gt;Github account&lt;/a&gt; is almost 4 years old, but it's been a slow and steady process. I'm entirely self-taught, purely a hobbyist, and by hobbyist I really mean it: every single line of code I've ever written was purely and simply for FUN. But yeah, definitely still a newbie.&lt;/p&gt;

&lt;p&gt;And the thing about teaching yourself is: when you're teaching something you don't know to people that also don't know the thing, it gets nasty.&lt;/p&gt;

&lt;p&gt;Anyway, the point I was trying to make is , because of that, I do not have a lot to share. As you could probably guess by looking over my other posts.&lt;/p&gt;

&lt;p&gt;But I've finally reached the point where I'm ready to make the leap from hobbyist to PROFESSIONAL coder. I want to turn something I love doing for fun into something that will make me scream, cry, and want to throw things -- but will also pay for my inevitable ulcer treatments.&lt;/p&gt;

&lt;p&gt;I want to be a DEV.&lt;/p&gt;

&lt;p&gt;And if that means writing stuff down and giving y’all a little piece of my mind to become a more active part of this community, well, then consider me officially on board.&lt;/p&gt;

&lt;h3&gt;
  
  
  Talking about myself
&lt;/h3&gt;

&lt;p&gt;Since I cannot talk shop, and I cannot share helpful working anecdotes as insights, let me offer the next best thing:&lt;/p&gt;

&lt;p&gt;I want to talk about me!&lt;/p&gt;

&lt;p&gt;Or rather, let me tell you a little story about how I've always wanted to code but consistently convinced myself I was too dumb to do it. Honestly, I still feel that way most days, but this time around I'm choosing to push through.&lt;/p&gt;

&lt;p&gt;We've all heard about impostor syndrome, and yeah, that's probably exactly what's going on here. The more you learn, the more you realize how little you actually know. Eventually, you start staring at increasingly abstract concepts until they make no sense at all, and then you realize it is all made up and nothing is real and now you're missing your Friday night where you could be desperately dancing in a club by just being desperate for ABSOLUTELY NO REASON since THIS (franticly pointing at the computer screen where the absolute mad words read &lt;code&gt;className="px-4 py-2 text-center space-x-2"&lt;/code&gt;) IS NOT A THING THAT EXISTS IN REAL LIFE!&lt;/p&gt;

&lt;p&gt;And yes, honestly, I feel like I'm becoming dumber, not smarter, as I go along. I feel kind of terrible that large language models emerged right in the middle of my learning curve because now I'm convinced that I can't code without ChatGPT.&lt;/p&gt;

&lt;p&gt;It's weird though. Whenever I read back code that I wrote with an LLM’s help, I genuinely understand it. I can explain it, spot errors, and when ChatGPT inevitably gets stuck on a loop suggesting the same useless patches again and again, I'm able to step back, reconsider, and ultimately find a solution on my own. Every single time.&lt;/p&gt;

&lt;p&gt;But, when I'm staring at the flashing cursor in an empty file in my IDE, I feel dumb and dumber. I have no idea where to start. I feel ashamed that I'm even trying to look for a job in the industry since I don't know how to start a fudging PYTHON CLASS without some lookups. &lt;/p&gt;

&lt;p&gt;And if that's a whole project, brother, I don't even know where to start.&lt;/p&gt;

&lt;p&gt;But put me in front of that blank file, cursor flashing mockingly in my IDE, and suddenly I don't know anything. I have absolutely no clue where to start. I feel ashamed for even considering a job in the industry when I can't begin writing a simple PYTHON CLASS without checking references.&lt;/p&gt;

&lt;p&gt;And if it’s an entire project, brother, I’m completely lost.&lt;/p&gt;

&lt;p&gt;You know those kids who buy a Ramones T-shirt, hang around with real punk rock fans, and spend the whole time anxious that sooner or later they'll be exposed as an impostor?&lt;/p&gt;

&lt;p&gt;Well, Visual Studio Code is my Ramones shirt.&lt;/p&gt;

&lt;p&gt;But it's funny, because I didn’t always feel this way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Beat on the brat
&lt;/h3&gt;

&lt;p&gt;When I was really young, and I’m not exactly sure how young, but let's say around 9 or 10 maybe, my dad came home from work one day carrying a rectangular box.&lt;/p&gt;

&lt;p&gt;Inside was this strange device: a black plastic brick with keys covered in numbers and letters, and a small coaxial cable you'd plug into your TV.&lt;/p&gt;

&lt;p&gt;That, my young under-40 friends, was a &lt;a href="https://en.wikipedia.org/wiki/TK85" rel="noopener noreferrer"&gt;TK85&lt;/a&gt;, a Brazilian clone of the legendary  &lt;a href="https://en.wikipedia.org/wiki/ZX81" rel="noopener noreferrer"&gt;ZX81&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That was a computer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F70fr204qc3x2m4jitx89.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F70fr204qc3x2m4jitx89.JPG" alt="TK85 computer" width="800" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My 8-year-old brain was utterly mesmerized.&lt;/p&gt;

&lt;p&gt;Funny, because when you look at it now, especially compared to the triple-screen PC setup I’m typing these words on, complete with a graphics card that cost more than my last paycheck, it’s genuinely mind-blowing.&lt;/p&gt;

&lt;p&gt;That computer didn't even have built-in storage. You had to buy an external MODULE to save your progress onto a CASSETTE TAPE, probably costing as much as the computer itself.&lt;/p&gt;

&lt;p&gt;It didn’t have a dedicated screen; you had to plug it into your TV.&lt;/p&gt;

&lt;p&gt;No operating system. No pre-installed software. No plug-and-play peripherals.&lt;/p&gt;

&lt;p&gt;Just a keyboard and a dream.&lt;/p&gt;

&lt;p&gt;Oh, and right, a Sinclair BASIC interpreter and an enormous BASIC book.&lt;/p&gt;

&lt;p&gt;Now, I'd absolutely love to tell you how this brave 6-year-old soldiered through the entire BASIC manual, mastered it cover-to-cover, and by age 7 created an application that transformed his home into a high-tech haven straight out of the 24th-and-one-half century. But no. I couldn’t make heads or tails of any of that gibberish.&lt;/p&gt;

&lt;p&gt;I mean, come on, it was a BASIC book for a 5 year old boy!&lt;/p&gt;

&lt;p&gt;(Seriously, I have NO idea how old I actually was back then.)&lt;/p&gt;

&lt;p&gt;But those books did have some words in a &lt;code&gt;monospace&lt;/code&gt; font that stood out from the rest. And I quickly discovered that typing those particular words into this mysterious device made things happen.&lt;/p&gt;

&lt;p&gt;You could perform little calculations. You could display your name on TV. You could draw a tiny square. Heck, you could even draw an entire Christmas tree made of little white squares. All of that just by typing WORDS!&lt;/p&gt;

&lt;p&gt;That was uncanny!&lt;/p&gt;

&lt;p&gt;And I truly wanted to explore it all. But then, a few months later, my dad came home with another rectangular box containing a Sega Genesis console, three game cartridges, and absolutely no instruction manual required.&lt;/p&gt;

&lt;p&gt;So I put the computer aside. At least for a while.&lt;/p&gt;

&lt;h3&gt;
  
  
  For the Win!
&lt;/h3&gt;

&lt;p&gt;Years later, we got our first PC, our personal computer. It was an Intel 486DX2, running at a blazing-fast 33MHz. It probably had around 4 or 8MB of RAM, and definitely less than 100MB of hard drive space. It had two floppy disk drives: a 5¼-inch drive capable of storing an astonishing 360KB of data, and a 3½-inch one that handled up to 1.44MB.&lt;/p&gt;

&lt;p&gt;When you turned it on, THINGS WOULD HAPPEN on the screen. Lines of text scrolled by, informing you about memory allocations and boot procedures, all while a quiet ticking echoed steadily from inside the computer. And when all was finally said and done, you'd once again be greeted by a black screen and a blinking prompt awaiting your commands.&lt;/p&gt;

&lt;p&gt;But this prompt was different. It stood beside some curious characters, specifically: &lt;code&gt;C:\&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And you couldn’t just type anything you wanted, because it would rudely inform you that it didn’t understand your so-called "command."&lt;/p&gt;

&lt;p&gt;Oh, and this time around, there was no instructional manual, and, obviously, no internet. So figuring things out became a bit trickier.&lt;/p&gt;

&lt;p&gt;Around that period, classes promising to teach you "how to use a computer" began popping up everywhere. And trust me, if using computers seems intuitive nowadays, back then it certainly wasn't. Naturally, I enrolled in one of those.&lt;/p&gt;

&lt;p&gt;Those were fun times. You'd leave your computer class carrying a floppy disk with a fresh new copy (pirated, of course, but come on!) of Prince of Persia, Indy 500, or Where in the World Is Carmen Sandiego? And right about now, if you're around your 40s, you're definitely humming that catchy little tune in your head, wondering which country the suspect who changed all their money into marks was heading to next. &lt;/p&gt;

&lt;p&gt;You learned how to navigate directories, create, delete, and search for files using nothing but the keyboard. You’d constantly interact with console-based programs, playing entire video games within these text-based environments.&lt;/p&gt;

&lt;p&gt;That was MS-DOS, the grandpa of PowerShell, the grumpy old-timer who sparred endlessly with another grumpy old guy called Unix Bash.&lt;/p&gt;

&lt;p&gt;Exploring these uncharted territories was amazing. Every day brought a new command to master; some of those I still use today when fiddling with terminals during development.&lt;/p&gt;

&lt;p&gt;But then, one day, we learned a different new command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;win&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Typing those three little letters and pressing enter transported you to another dimension, as it was the Feywild of computing: Windows 3.11, a GRAPHICAL USER INTERFACE where you could actually control the computer with a MOUSE.&lt;/p&gt;

&lt;p&gt;You could CLICK things. You could RIGHT-CLICK things. And they were DIFFERENT actions!&lt;/p&gt;

&lt;p&gt;You could DRAG AND DROP things.&lt;/p&gt;

&lt;p&gt;You could OPEN a window. And then you could CLOSE that same window.&lt;/p&gt;

&lt;p&gt;You could draw masterpieces in PAINTBRUSH. You could play SOLITAIRE. You could play MINESWEEPER. Or not, because let's face it, that game made no sense at all.&lt;/p&gt;

&lt;p&gt;But there it was—something both utterly weird and spectacular. Seriously, it hit me so hard that years later it directly inspired the layout of &lt;a href="https://www.jampamatos.jampa.br/" rel="noopener noreferrer"&gt;my own website today&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;(I realize my site has more of a Windows 95 vibe than a 3.11, but the feeling is exactly the same.)&lt;/p&gt;

&lt;p&gt;Having that kind of control over things was incredible. I felt like Dean Pelton when he discovered VR: I COULD DO THINGS THAT MADE JESUS WEEP, FOR THERE WERE NO MORE WORLDS TO CONQUER!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ioobfmukmpeshjusuj3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ioobfmukmpeshjusuj3.png" alt="AND JESUS WEPT" width="225" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(If you didn’t get that very specific reference, I completely understand.)&lt;/p&gt;

&lt;p&gt;The point is, you could create amazing things on that computer, and that deeply resonated with me.&lt;/p&gt;

&lt;p&gt;I wanted to learn more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kick ass and chew bubble gum
&lt;/h3&gt;

&lt;p&gt;I really enjoyed fiddling around with that old computer. So much so that eventually, we upgraded it to a shiny new Intel Pentium machine, complete with an impressive CD-ROM drive, multimedia capabilities, and a set of stereo speakers.&lt;/p&gt;

&lt;p&gt;Around that time, exciting new things began to happen everywhere. Newsstands started selling magazines bundled with CDs brimming with games that you could take home and play right away.&lt;/p&gt;

&lt;p&gt;Let me pause for a quick sidebar: these early magazines were really something special. The gaming industry was booming, and there were plenty of new games coming out all the time. But proper channels for selling computer games weren’t widespread yet, and newsstand magazines certainly weren't the ideal distribution method back then.&lt;/p&gt;

&lt;p&gt;I come from a small town in the countryside of Minas Gerais, Brazil, where, in the early '90s, nobody would dream of opening a computer game store. Computers were still extremely expensive and uncommon, so it wasn't exactly a brilliant business idea.&lt;/p&gt;

&lt;p&gt;Because of this, the magazines we bought typically didn't include complete games. Instead, they had DEMO versions, letting you play for about 15 minutes or just like the first stage before stopping abruptly and politely asking you to go buy the full version.&lt;/p&gt;

&lt;p&gt;It's funny because you'd expect us to get annoyed, but honestly, we had a blast playing those brief segments. We'd finish one demo level, eagerly jump into the next, and then onto another, enjoying the small tastes of many different games.&lt;/p&gt;

&lt;p&gt;Maybe we were just less demanding back then, because everything was so new and exciting.&lt;/p&gt;

&lt;p&gt;To be fair, some magazines also provided SHAREWARE versions. For example, I remember playing Duke Nukem 3D's shareware version, which included the entire first act, an enormous amount of gameplay for free. Big shout-out to the folks at &lt;a href="https://www.apogeeent.com/" rel="noopener noreferrer"&gt;Apogee&lt;/a&gt; for making my childhood (and my friends') a whole lot more awesome.&lt;/p&gt;

&lt;p&gt;(Death Rally was the first game, that I had as a shareware as well, that I got actually GOOD at. So much so that, when I got the change, I bought the whole thing. Probably the first full game I ever bought).&lt;/p&gt;

&lt;p&gt;But I digress. The point is, I'd buy those magazines-with-CDs-packed-with-demos. Most games at that time ran on DOS, so you'd have to exit Windows (these computers booted directly into Windows 95 without stopping at DOS) and manually type a command from the CD-ROM drive.&lt;/p&gt;

&lt;p&gt;Typing that command would launch a simple little program, presenting you with menus to pick genres and games, very much like the tiny CLI apps you build at the start of any coding bootcamp. You know the drill: prompt the user, store their input, run some conditional logic, and output what the user asked for.&lt;/p&gt;

&lt;p&gt;Back then, I spent a lot of time in front of my computer. Half the time I played (demo) games; the other half, I spent exploring, clicking around, trying to understand how things worked.&lt;/p&gt;

&lt;p&gt;I don't exactly remember why I first did it. Maybe I saw someone opening a &lt;code&gt;.bat&lt;/code&gt; file in Windows' notorious Notepad and got curious, but I noticed the commands used to launch these CD menus were always in &lt;code&gt;.bat&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;What I clearly remember is the day I opened one of those &lt;code&gt;.bat&lt;/code&gt; files in Notepad and suddenly realized I could make sense of what I was seeing.&lt;/p&gt;

&lt;p&gt;It was basically the exact text displayed on the screen when the menus popped up, mixed in with strange words like ECHO. But I recognized some of those other words as DOS commands I'd already learned. This was my starting point, a way to begin decoding the mystery.&lt;/p&gt;

&lt;p&gt;If you don't know what a &lt;code&gt;.bat&lt;/code&gt; file is, think of it as a script-like executable in DOS, similar to a &lt;code&gt;.sh&lt;/code&gt; shell script in Bash. It lets you bundle multiple commands together and execute them all at once. You could even write simple scripts with inputs, variables, and conditionals.&lt;/p&gt;

&lt;p&gt;Of course, I didn't understand all of that back then. But I did realize those were DOS commands, and it dawned on me that I could create my own interactive menus, launching games, returning to Windows, or anything else I wanted.&lt;/p&gt;

&lt;p&gt;(This became even more intriguing when I learned you could call one &lt;code&gt;.bat&lt;/code&gt; file from another, and that the first thing the computer executed on boot, before loading Windows, was a file called &lt;code&gt;autoexec.bat&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;Now, I've said before that I'm self-taught. That's mostly true, though I have taken bootcamp courses and used resources like &lt;a href="https://www.theodinproject.com/" rel="noopener noreferrer"&gt;The Odin Project&lt;/a&gt;. But here... Boy, this moment right here, I truly learned entirely by myself. Just reading through someone else's code, figuring out what each command did, piece by piece.&lt;/p&gt;

&lt;p&gt;It's a feeling I savor to this day. It might even have been my smartest moment.&lt;/p&gt;

&lt;h3&gt;
  
  
  ... as time goes by
&lt;/h3&gt;

&lt;p&gt;But along came elementary school, and then high school. Biology captured my interest, while math faded into the background. Truth is, I had no clue how one would pursue a career in tech. Computers were fun, sure, but programming felt utterly alien, inaccessible, and impossibly distant.&lt;/p&gt;

&lt;p&gt;It's funny, because I always claimed to hate math. Yet no other subject could match the warm, fuzzy feeling I experienced whenever I finally sat down, tackled a problem, and actually solved it. That pure sense of accomplishment and victory was simply incredible.&lt;/p&gt;

&lt;p&gt;But as far as coding went, I learned next to nothing. Sure, maybe I could write small Excel macros, but beyond that? It seemed unattainable.&lt;/p&gt;

&lt;p&gt;I think I concluded that a career in programming wasn't realistic simply because I couldn't find a way to truly create a full-fledged program myself. Those &lt;code&gt;.bat&lt;/code&gt; files were amusing, sure, but how could I ever hope to draw some muscular blond dude in a red tank-top and Johnny Bravo shades blasting ugly aliens in the streets using a batch script? It made zero sense.&lt;/p&gt;

&lt;p&gt;And then I tried opening an &lt;code&gt;.exe&lt;/code&gt; file in Notepad, and that made even less sense.&lt;/p&gt;

&lt;p&gt;So no, writing &lt;code&gt;.bat&lt;/code&gt; scripts wasn't real programming, and I believed there wasn't any feasible way to pursue programming further. How could I aspire to something that felt entirely foreign, especially since all the software I encountered in Brazil came from abroad?&lt;/p&gt;

&lt;p&gt;Life moved on. I made choices, some good, many questionable. I drifted through creative roles, labor-intensive jobs, management positions, then college to study Biological Sciences. Eventually, I became a high school teacher. Later, I tried pursuing a Master's degree.&lt;/p&gt;

&lt;p&gt;Then the pandemic hit.&lt;/p&gt;

&lt;p&gt;Being somewhat older during the pandemic was particularly tough: it meant that (1) I was part of a risk group, and (2) my parents were older and vulnerable. I moved back home to support them, spending more time with these two lovely people who’d always supported me.&lt;/p&gt;

&lt;p&gt;But being back home also meant I suddenly had a lot more free time on my hands.&lt;/p&gt;

&lt;p&gt;One day, for some reason, I'd bought a Unity Game Development course from&lt;a href="https://academy.zenva.com/" rel="noopener noreferrer"&gt;Zenva&lt;/a&gt; via &lt;a href="https://www.humblebundle.com/" rel="noopener noreferrer"&gt;the Humble Bundle&lt;/a&gt;. It had been sitting there untouched for over a year, maybe even longer.&lt;/p&gt;

&lt;p&gt;So, on a whim, I decided to give it a go.&lt;/p&gt;

&lt;p&gt;Once again, I was astonished, bedazzled, and otherwise stupefied. Everything came flooding back: the power to create things, to shape ideas into reality. Writing words that seemed meaningless but somehow conjured powerful, magical behaviors. It felt just like wizardry.&lt;/p&gt;

&lt;p&gt;The game engine itself was fun, but the real magic happened within that Visual Studio Code file. There I was, typing commands reminiscent of those DOS days, hunting down bizarre bugs by scouring documentation pages that echoed the BASIC book from when I was 4 (seriously, I have no idea how old I actually was). Running a piece of code and seeing the exact output I expected gave me that familiar rush of solving math problems.&lt;/p&gt;

&lt;p&gt;It all came rushing back.&lt;/p&gt;

&lt;p&gt;I'm not a particularly spiritual guy. I don't really believe in fate, destiny, or even talent.&lt;/p&gt;

&lt;p&gt;But sometimes, life tosses things at me that make me question my perspective.&lt;/p&gt;

&lt;p&gt;Because, looking back, it all feels kind of connected.&lt;/p&gt;

&lt;h3&gt;
  
  
  All Play and No Work Makes Jampa A Poor Boy
&lt;/h3&gt;

&lt;p&gt;Ever since then, I've been at it. Constantly. I dove into Python, then HTML/CSS/Javascript -- and nearly died of Flexbox-induced rage. Then Ruby and Rails. Learned what an API was, built an API. Built two APIs. Played around with some more C#, tried Lua and gaming with LöVE2D. Touched React, then Flask, Django, NodeJS, TypeScript, Golang, Wails, Tailwind... and then some more. And then something else. And yet another thing.&lt;/p&gt;

&lt;p&gt;I built a landing page. Then a calculator. Then a CLI chess game. A PONG-like game. A Flappy Bird clone. My own website.&lt;/p&gt;

&lt;p&gt;But then I'd start a project, hit a wall or lose interest, and dump it. Start another, get bored, dump it. Start yet another, realize it's been done before, dump that too. And so on, and so forth.&lt;/p&gt;

&lt;p&gt;The thing about being self-taught is that you're your own motivator, but also your own saboteur. You have to constantly motivate the guy who failed yesterday, and that guy must somehow keep motivating the motivator too.&lt;/p&gt;

&lt;p&gt;It's easy to get lost, easy to stretch yourself thin. It’s hard to set clear goals because, when you love doing something just for the sake of doing it, the thing itself becomes its own goal and reward.&lt;/p&gt;

&lt;p&gt;I mean, I'd hop onto &lt;a href="https://www.codewars.com/dashboard" rel="noopener noreferrer"&gt;Codewars&lt;/a&gt; and solve challenges just for kicks. These challenges are fun, satisfying, and bring back that fuzzy, warm feeling, but at the end of the day, you're not really creating anything meaningful. It’s like someone shredding a Joe Satriani guitar solo: you're probably the only one really enjoying it.&lt;/p&gt;

&lt;p&gt;It’s like a fix, you know what I’m saying?&lt;/p&gt;

&lt;p&gt;I guess what I'm trying to say is that while it's all fun and games, without accountability or mentorship, I feel like I've gone about as far as I can on my own.&lt;/p&gt;

&lt;p&gt;I crave mentorship to show me the ropes: I know what I want to build, but how should I build it? What's the best way, the coolest way, the safest way?&lt;/p&gt;

&lt;p&gt;Why are you learning this technology when another would suit you better? Why use pointers when a simple variable does the job? Why build this whole algorithm yourself when there are already 53 libraries doing exactly what you want?&lt;/p&gt;

&lt;p&gt;And I need accountability. Something to MAKE ME STICK TO THE PLAN. To know someone out there is counting on me. To have confidence that the thing I'm building has a purpose, an actual user, or even another fellow developer, waiting for it.&lt;/p&gt;

&lt;p&gt;I’m genuinely afraid to take that leap of faith again, to jump back into the unknown, into a field that's very specialized, demanding, and filled with incredibly knowledgeable people, especially considering my limited professional experience. I’m worried the real punk kids will ask me what my favorite Ramones song is, and I’ll have no answer. I'm terrified I'll walk into a tech interview and hear "No ChatGPT allowed," and I’ll melt down faster than the T-1000 in Terminator 2.&lt;/p&gt;

&lt;p&gt;But I also believe I'm a smart guy. Like, weirdly, specifically, not-sure-how-or-why-but-definitely-smarter-than-average smart. But smart nonetheless.&lt;/p&gt;

&lt;p&gt;And I genuinely want to contribute.&lt;/p&gt;

&lt;p&gt;Bottom line: I'm here, ready and willing to go beyond level one. Ready to leave the demo behind and dive into the full game. To push myself further and grow. As a coder, as a developer, as a software engineer, as a nerd in general.&lt;/p&gt;

&lt;p&gt;So I guess what I'm ultimately saying is: if you're hiring, hit me up at &lt;a href="mailto:jp.coutm@gmail.com"&gt;jp.coutm@gmail.com&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;(This text absolutely did NOT end the way I expected when I started typing, but I kinda like how it turned out. Also, if you made it this far, cheers! And tell me your favorite ice-cream flavor!)&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>career</category>
      <category>motivation</category>
      <category>programming</category>
    </item>
    <item>
      <title>From Messy Data to Super Mario Pipeline: My First Adventure in Data Engineering</title>
      <dc:creator>Jampa Matos</dc:creator>
      <pubDate>Thu, 20 Jun 2024 23:24:21 +0000</pubDate>
      <link>https://forem.com/jampamatos/from-messy-data-to-super-mario-pipeline-my-first-adventure-in-data-engineering-1apo</link>
      <guid>https://forem.com/jampamatos/from-messy-data-to-super-mario-pipeline-my-first-adventure-in-data-engineering-1apo</guid>
      <description>&lt;p&gt;&lt;strong&gt;Welcome to the thrilling tale of my very first automated data pipeline!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine you’ve just been handed a database that looks like it’s been through Bowser’s castle and back. Yes, it’s that messy. The mission? To transform this chaos into a clean, analytics-ready dataset with as little human intervention as possible. Sounds like a job for Mario himself, right? Well, buckle up, because this is the story of how I tackled the subscriber data cleanup project, dodged fireballs, and came out victorious.&lt;/p&gt;

&lt;p&gt;When I first opened the &lt;code&gt;cademycode.db&lt;/code&gt; file, I felt like Mario entering a warp pipe into an unknown world. It was a mess. Missing values, inconsistent data types, and duplicates everywhere! But hey, every great adventure starts with a challenge, and this was mine.&lt;/p&gt;

&lt;p&gt;In this post, I’ll take you through my journey of building my very first automated data pipeline. We’ll dive deep into the nitty-gritty details and share the ups and downs along the way. Whether you’re a fellow data plumber or just curious about the magical world of data cleaning, you’re in for a treat.&lt;/p&gt;

&lt;p&gt;So grab a 1-Up mushroom, get comfy, and let’s embark on this data adventure together! Spoiler alert: There might be some gold coins and hidden blocks of wisdom along the way.&lt;/p&gt;

&lt;p&gt;Ready to jump into the pipe? Let’s start with how I set up the project and the initial hurdles I had to overcome. Spoiler alert: There were quite a few!&lt;/p&gt;

&lt;h2&gt;
  
  
  Entering the Warp Pipe
&lt;/h2&gt;

&lt;p&gt;Setting up this project was like entering a warp pipe into an unknown world. I knew the journey ahead would be filled with challenges, but I was ready to tackle them head-on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;First things first, I needed to clone the repository and set up my working environment. I created a directory called &lt;code&gt;subscriber-pipeline&lt;/code&gt; and jumped right into it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /home/jampamatos/workspace/codecademy/Data/subscriber-pipeline
&lt;span class="nb"&gt;cd&lt;/span&gt; /home/jampamatos/workspace/codecademy/Data/subscriber-pipeline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, I set up a virtual environment to keep my project dependencies isolated. If there's one thing Mario taught me, it's to always be prepared!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tools and Technologies
&lt;/h3&gt;

&lt;p&gt;Since there were no red flower or mushrooms to collect, here’s a list of the tools and technologies I used for this project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python:&lt;/strong&gt; The hero of our story. I used Python for data manipulation and scripting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQLite:&lt;/strong&gt; Our trusty sidekick. This lightweight database was perfect for managing the data.&lt;/li&gt;
&lt;li&gt;**Pandas: **The power-up we needed to handle data manipulation with ease.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jupyter Notebook:&lt;/strong&gt; My go-to tool for exploring and experimenting with data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bash:&lt;/strong&gt; The magical spell that automated our pipeline.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Initial Hurdles
&lt;/h3&gt;

&lt;p&gt;Setting up the environment was smooth sailing until I encountered my first Goomba: installing the required Python packages. After a few head bumps, I finally managed to get everything installed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pandas sqlite3 jupyter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But wait, there’s more! I also needed to install some additional packages for logging and testing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;unittest logging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Facing the First Boss: Database Connection
&lt;/h3&gt;

&lt;p&gt;With everything set up, it was time to connect to the database. This felt like facing the first boss. I opened the &lt;code&gt;cademycode.db&lt;/code&gt; file, unsure of what awaited me inside. Using SQLite, I established a connection and was ready to explore the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;

&lt;span class="n"&gt;con&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dev/cademycode.db&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Database connection established successfully.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suffice to say, the database was indeed as messy as Bowser’s castle. But that’s a story for the next section.&lt;/p&gt;

&lt;p&gt;In the next part of our adventure, we'll dive into inspecting and cleaning the data. Get ready to battle missing values, inconsistent data types, and duplicates galore!&lt;/p&gt;

&lt;h2&gt;
  
  
  Battling the Data Monsters
&lt;/h2&gt;

&lt;p&gt;With the setup complete and the database connection established, it was time to dive into the data. This part of the journey felt like battling hordes of Koopa Troopas. Every step revealed new challenges, but with determination (and some Italian pasta), I tackled them head-on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Inspection
&lt;/h3&gt;

&lt;p&gt;The first step was to inspect the data and understand the lay of the land. Using Pandas, I loaded the tables from &lt;code&gt;cademycode.db&lt;/code&gt; into DataFrames and took a peek at what I was dealing with.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;

&lt;span class="n"&gt;tables&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_sql_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT name FROM sqlite_master WHERE type=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;table&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;con&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;table_names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tables&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_sql_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;con&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;table_names&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Table: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;head&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output revealed the initial state of the data – missing values, inconsistent data types, and duplicates galore. It was like entering a haunted house in Luigi's Mansion!&lt;/p&gt;

&lt;p&gt;(If you're curious about the output messages, you can check them at the project's &lt;a href="https://github.com/jampamatos/codecademy_stuff/blob/main/Data/subscriber-pipeline/Subscriber%20Pipeline.ipynb"&gt;Jupyter Notebook&lt;/a&gt;.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling Missing Values
&lt;/h3&gt;

&lt;p&gt;Next, I identified and handled the missing values. This was akin to collecting power-ups to boost my chances of success. For some columns, I filled the missing values with zeros, while for others, I used the median value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;students_df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cademycode_students&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Fill missing values
&lt;/span&gt;&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;job_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inplace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;current_career_path_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inplace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;num_course_taken&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;num_course_taken&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;median&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;inplace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;time_spent_hrs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;time_spent_hrs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;median&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;inplace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, the choice on whether I filled data with zeroes or with median values is explained in the project's &lt;a href="https://github.com/jampamatos/codecademy_stuff/blob/main/Data/subscriber-pipeline/Subscriber%20Pipeline.ipynb"&gt;Jupyter Notebook&lt;/a&gt;, but in a nutshell it was because &lt;code&gt;job_id&lt;/code&gt; and &lt;code&gt;current_career_path_id&lt;/code&gt;were given &lt;code&gt;0&lt;/code&gt; to indicate 'unemployed' and 'not enrolled' status.&lt;/p&gt;

&lt;h3&gt;
  
  
  Correcting Data Types
&lt;/h3&gt;

&lt;p&gt;The next challenge was correcting inconsistent data types. This felt like trying to fit puzzle pieces together. With a bit of Pandas magic, I converted columns to their appropriate data types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Convert data types
&lt;/span&gt;&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dob&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dob&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;coerce&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;job_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_numeric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;job_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;coerce&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;num_course_taken&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_numeric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;num_course_taken&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;coerce&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;current_career_path_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_numeric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;current_career_path_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;coerce&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;time_spent_hrs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_numeric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;time_spent_hrs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;coerce&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dealing with Duplicates
&lt;/h3&gt;

&lt;p&gt;No Mario adventure is complete without encountering duplicates – like those pesky Bullet Bills that keep reappearing! I identified and removed duplicate records to ensure the data was clean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Remove duplicates
&lt;/span&gt;&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drop_duplicates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inplace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;jobs_df_cleaned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cademycode_student_jobs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;drop_duplicates&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Extracting Nested Data
&lt;/h3&gt;

&lt;p&gt;One of the trickiest parts was dealing with nested data in the &lt;code&gt;contact_info&lt;/code&gt; column. It was like the water stage. To nail it, I had to write a function to extract the nested information and split it into separate columns.&lt;/p&gt;

&lt;p&gt;Before we continue, we should notice that the data in &lt;code&gt;contact_info&lt;/code&gt; was in the form of a json object, containing the mailing address and an email, such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"mailing_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"470 Essex Curve, Copan, Mississippi, 86309"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cleopatra_singleton7791@inlook.com"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Here is a nice place to add that the data used in this project is fictional, so don't worry!)&lt;/p&gt;

&lt;p&gt;So, in order to extract that, we could treat it like a json object after all. So that's what we did:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;extract_contact_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contact_info&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contact_info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'"'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Series&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mailing_address&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSONDecodeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Series&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mailing_address&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;contact_info&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;extract_contact_info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;contact_info&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;inplace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the data cleaned and ready, I felt like I had just collected a Super Star power-up. But the adventure was far from over. Next up, I had to create the output CSV and ensure it was analytics-ready.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Final Battle for Clean Data
&lt;/h2&gt;

&lt;p&gt;With the data cleaned and ready, it was time for the final showdown: combining the data into a single, analytics-ready CSV. You know, like grabbing giant Bowser by the tail and throwing him around in Super Mario 64. There were obstacles to overcome, but I was determined to save the day (or in this case, the data).&lt;/p&gt;

&lt;h3&gt;
  
  
  Combining the Data
&lt;/h3&gt;

&lt;p&gt;First, I needed to combine the cleaned data from multiple tables into a single DataFrame. Using Pandas, I performed the necessary joins to bring everything together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Merge dataframes
&lt;/span&gt;&lt;span class="n"&gt;merged_df_cleaned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;students_df&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jobs_df_cleaned&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;how&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;left&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;left_on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;job_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right_on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;job_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;final_df_cleaned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;merged_df_cleaned&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cademycode_courses&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;how&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;left&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;left_on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;current_career_path_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right_on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;career_path_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Validating the Final Dataset&lt;/p&gt;

&lt;p&gt;Once the data was combined, I needed the flagpole at the end of the level: to validate the final dataset. I checked for any inconsistencies or missing values that might have slipped through.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Fill remaining missing values
&lt;/span&gt;&lt;span class="n"&gt;final_df_cleaned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;final_df_cleaned&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;career_path_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;final_df_cleaned&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;career_path_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;career_path_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;final_df_cleaned&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;career_path_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;hours_to_complete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;final_df_cleaned&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hours_to_complete&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Generating the Output CSV
&lt;/h3&gt;

&lt;p&gt;With the data validated, it was time to generate the final CSV. I mean, even Super Mario Bros had a save game feature, right?:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Save final DataFrame to CSV
&lt;/span&gt;&lt;span class="n"&gt;final_df_cleaned&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dev/final_output.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Overcoming Challenges
&lt;/h3&gt;

&lt;p&gt;Of course, no epic battle is without its challenges. One of the biggest hurdles was ensuring that the final DataFrame retained all the original rows and that no data was lost during the merges. After some debugging (and a few extra lives), I successfully retained the integrity of the data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Celebrating the Victory
&lt;/h3&gt;

&lt;p&gt;Finally, with the output CSV generated and validated, it was a triumphant moment. I could rest knowing that the data was now clean and ready for analysis.&lt;/p&gt;

&lt;p&gt;Or could I?&lt;/p&gt;

&lt;p&gt;With the final CSV in hand, the next step was to ensure that the pipeline could run automatically with minimal intervention. This meant developing unit tests and logs to keep everything in check.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our Princess Is in Another Castle
&lt;/h2&gt;

&lt;p&gt;After all the hard work of cleaning and combining the data, it might feel like the job is done. But as any Mario fan knows, "our princess is in another castle!" The journey isn't complete until the pipeline is foolproof and can run automatically without constant supervision. This meant developing unit tests and logging to ensure everything runs smoothly.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Importance of Unit Tests
&lt;/h3&gt;

&lt;p&gt;Unit tests are like Mario's power-ups—they help you tackle challenges and keep you safe from unexpected pitfalls. I implemented unit tests to ensure the data integrity and functionality of the pipeline. These tests checked for things like schema consistency, the presence of null values, and the correct number of rows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestDataCleaning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_no_null_values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertFalse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;final_df_cleaned&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isnull&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;There are null values in the final table&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_correct_number_of_rows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;original_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cademycode_students&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;final_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;final_df_cleaned&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;original_length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;final_length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The number of rows differs after the merges&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_schema_consistency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;original_schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cademycode_students&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;final_schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;final_df_cleaned&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;original_schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;discard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;contact_info&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;original_schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mailing_address&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;original_schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;issubset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;final_schema&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The final table schema does not include all original columns&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;first-arg-is-ignored&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Implementing Logging
&lt;/h3&gt;

&lt;p&gt;Logging is essential for tracking the pipeline's execution and troubleshooting issues. Think of it as Mario's map—it helps you see where you've been and identify any trouble spots. I implemented logging to record each step of the pipeline, including updates and errors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;

&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basicConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;logs/data_pipeline.log&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                    &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%(asctime)s:%(levelname)s:%(message)s&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Pipeline code...
&lt;/span&gt;    &lt;span class="nf"&gt;log_update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Pipeline executed successfully.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;log_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error running the pipeline: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;To keep track of updates, I created a changelog that records version numbers, new rows added, and missing data counts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write_changelog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_rows_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;missing_data_count&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;logs/changelog.txt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Version: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;New rows added: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;new_rows_count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Missing data count: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;missing_data_count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Challenges and Learnings
&lt;/h3&gt;

&lt;p&gt;One of the biggest challenges was ensuring the unit tests covered all edge cases. Oh, when we see 'OK' in the test suite, that feeling is unbeatable! And, with thorough testing and logging, I ensured the pipeline was robust and reliable.&lt;/p&gt;

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

&lt;p&gt;With unit tests and logging in place, I felt confident that my pipeline could handle anything thrown its way. It was a moment of triumph, like finally rescuing Princess Peach after a long adventure.&lt;/p&gt;

&lt;p&gt;Now, all that's left was to create a Bash script to automate the pipeline and move the updated files to the production directory. Talk about more data plumbing fun!&lt;/p&gt;

&lt;h2&gt;
  
  
  Automating the Pipeline—Mario Kart Style
&lt;/h2&gt;

&lt;p&gt;With the data cleaned, combined, and validated, and with unit tests and logging in place, it was time to put the pipeline on autopilot. Like Mario's Kart has to be race—ready, everything needed to run smoothly and efficiently, with no banana peels or red shells in sight.&lt;/p&gt;

&lt;h3&gt;
  
  
  Purpose of the Bash Script
&lt;/h3&gt;

&lt;p&gt;The Bash script propelled the pipeline forward with speed and precision, you know, like shooting turtle shells at other racers (but a little less fun. Just a little!). It was designed to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Execute the Python script that runs the data pipeline.
2.Check the changelog to determine if an update occurred.
3.Move the updated files from the working directory to the production directory.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Script
&lt;/h3&gt;

&lt;p&gt;Here's the Bash script that made it all possible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# Path to the Python script&lt;/span&gt;
&lt;span class="nv"&gt;PYTHON_SCRIPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/home/jampamatos/workspace/codecademy/Data/subscriber-pipeline/main.py"&lt;/span&gt;

&lt;span class="c"&gt;# Path to the production directory&lt;/span&gt;
&lt;span class="nv"&gt;PROD_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/home/jampamatos/workspace/codecademy/Data/subscriber-pipeline/prod"&lt;/span&gt;

&lt;span class="c"&gt;# Path to the changelog&lt;/span&gt;
&lt;span class="nv"&gt;CHANGELOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/home/jampamatos/workspace/codecademy/Data/subscriber-pipeline/log/changelog.txt"&lt;/span&gt;

&lt;span class="c"&gt;# Current version from the changelog&lt;/span&gt;
&lt;span class="nv"&gt;CURRENT_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oP&lt;/span&gt; &lt;span class="s1"&gt;'Version: \K.*'&lt;/span&gt; &lt;span class="nv"&gt;$CHANGELOG&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Execute the Python script&lt;/span&gt;
python3 &lt;span class="nv"&gt;$PYTHON_SCRIPT&lt;/span&gt;

&lt;span class="c"&gt;# Check if the script executed successfully&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Pipeline executed successfully."&lt;/span&gt;

    &lt;span class="c"&gt;# New version from the changelog&lt;/span&gt;
    &lt;span class="nv"&gt;NEW_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oP&lt;/span&gt; &lt;span class="s1"&gt;'Version: \K.*'&lt;/span&gt; &lt;span class="nv"&gt;$CHANGELOG&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Check if there was an update&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CURRENT_VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NEW_VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Update detected. Moving files to production."&lt;/span&gt;

        &lt;span class="c"&gt;# Move updated files to the production directory&lt;/span&gt;
        &lt;span class="nb"&gt;mv&lt;/span&gt; /home/jampamatos/workspace/codecademy/Data/subscriber-pipeline/dev/clean_cademycode.db &lt;span class="nv"&gt;$PROD_DIR&lt;/span&gt;/
        &lt;span class="nb"&gt;mv&lt;/span&gt; /home/jampamatos/workspace/codecademy/Data/subscriber-pipeline/dev/final_output.csv &lt;span class="nv"&gt;$PROD_DIR&lt;/span&gt;/

        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Files moved to production."&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"No updates detected. No files moved to production."&lt;/span&gt;
    &lt;span class="k"&gt;fi
else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Pipeline execution failed. Check logs for details."&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Challenges on the Track
&lt;/h3&gt;

&lt;p&gt;Like any Mario Kart race, there were more than a few obstacles along the way. One of the trickiest parts was ensuring the script correctly identified updates and moved the files only when necessary. After a few laps of testing and tweaking, I had the script running smoothly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating an Alias
&lt;/h3&gt;

&lt;p&gt;To make running the script as easy as throwing a green shell, I created an alias. This allowed me to execute the script with a simple command, no matter where I was in the terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;run_pipeline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/home/jampamatos/workspace/codecademy/Data/subscriber-pipeline/run_pipeline.sh"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By adding this line to my ~/.bashrc file and reloading the shell, I could start the pipeline with a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;run_pipeline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the Bash script in place, my pipeline was ready to zoom along the track with minimal human intervention. It was a satisfying moment, like crossing the finish line in first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;After navigating through a maze of messy data, cleaning and validating it, automating the process, and ensuring everything runs smoothly with unit tests and logging, we've finally crossed the finish line. It’s been a wild ride, but let's take a moment to reflect on our adventure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary of Tasks
&lt;/h3&gt;

&lt;p&gt;Throughout this project, we aimed to build a data engineering pipeline to transform a messy database of long-term canceled subscribers into a clean, analytics-ready dataset. Here's a summary of the key tasks we accomplished:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Setting Up the Project:&lt;/strong&gt; We began by setting up our working directory and ensuring all necessary files and tools were in place.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inspecting and Cleaning the Data:&lt;/strong&gt; We imported the tables from &lt;code&gt;cademycode.db&lt;/code&gt; into dataframes, inspected them for missing or invalid data, and performed various data cleaning operations. This included handling null values, correcting data types, and dealing with duplicates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creating the Output CSV:&lt;/strong&gt; Using the cleaned data, we produced an analytics-ready SQLite database and a flat CSV file. We validated the final table to ensure no data was lost or duplicated during the joins.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developing Unit Tests and Logs:&lt;/strong&gt; We converted our Jupyter Notebook into a Python script. The script includes unit tests to check for updates to the database and to protect the update process. It also includes logging to track updates and errors. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creating the Bash Script:&lt;/strong&gt; We created a Bash script to handle running the Python script and moving updated files from the working directory to the production directory. The script checks the changelog to determine if an update occurred before moving the files. &lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;Building my first automated data pipeline was an exciting and challenging journey. It felt like a typical Super Mario stage, filled with obstacles and power-ups. Along the way, I learned valuable lessons about data cleaning, automation, and the importance of thorough testing and logging.&lt;/p&gt;

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

&lt;p&gt;In conclusion, this project successfully demonstrates how to build a robust data engineering pipeline that automates the transformation of raw data into a clean and usable format. By following a structured approach, we ensured that the pipeline is reliable, maintainable, and easy to understand. The inclusion of unit tests and logging provides additional safeguards and transparency, making it easier to monitor and debug the process.&lt;/p&gt;

&lt;p&gt;This project not only serves as a valuable addition to my portfolio but also equips me with practical experience in handling real-world data engineering challenges. The skills and methodologies applied here are transferable to a wide range of data engineering tasks, ensuring I am well-prepared for future projects and roles in the field.&lt;/p&gt;

&lt;p&gt;Thank you for joining me on this adventure! If you have any questions or comments, feel free to leave them below. I’d love to hear about your own data engineering experiences and any tips you might have. Until next time, keep racing towards your data goals and may your pipelines always be free of banana peels!&lt;/p&gt;

</description>
      <category>dataengineering</category>
      <category>python</category>
      <category>automation</category>
      <category>sql</category>
    </item>
    <item>
      <title>Pedaling Through Data: A Wheelie Fun Bike Rental Analysis with Python and PostgreSQL</title>
      <dc:creator>Jampa Matos</dc:creator>
      <pubDate>Wed, 05 Jun 2024 19:00:46 +0000</pubDate>
      <link>https://forem.com/jampamatos/pedaling-through-data-a-wheelie-fun-bike-rental-analysis-with-python-and-postgresql-13dc</link>
      <guid>https://forem.com/jampamatos/pedaling-through-data-a-wheelie-fun-bike-rental-analysis-with-python-and-postgresql-13dc</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Hey everyone!&lt;/p&gt;

&lt;p&gt;Ever wondered how weather affects bike rentals in the city? Well, &lt;a href="https://www.codecademy.com"&gt;Codecademy&lt;/a&gt; did too! As part of their Data Engineering Course, they handed me some data and said, "Analyze this!" So, I grabbed my Python, Jupyter Notebook, and PostgreSQL, and embarked on a data adventure to uncover the secrets of bike rental patterns.&lt;/p&gt;

&lt;p&gt;In this post, I'll walk you through the process, share some cool insights, and maybe even crack a few jokes along the way. So, grab your helmet, and let's ride through this data journey together!&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Preparation
&lt;/h3&gt;

&lt;p&gt;First things first, let's talk data. For this project, Codecademy provided two datasets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Citi Bike ridership data:&lt;/strong&gt; This gives us the lowdown on when and where people are riding those snazzy blue bikes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NOAA weather data:&lt;/strong&gt; Because we need to know if it's raining cats and dogs or if it's sunny and perfect for a ride.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cleaning and transforming the data is like tuning up your bike – it's gotta be done before you hit the road. Here are some highlights of what I did:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dealt with missing values:&lt;/strong&gt; Because incomplete data is like riding a bike with a flat tire.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merged the datasets:&lt;/strong&gt; Think of it as combining the best parts of two bikes into one super-bike.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Formatted columns:&lt;/strong&gt; Making sure dates look like dates and not some weird alien language.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a snippet of the code that got the job done (you can check out the entire code at the &lt;a href="https://github.com/jampamatos/codecademy_stuff/tree/main/Data/bike_rental"&gt;GitHub repository&lt;/a&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;

&lt;span class="c1"&gt;# Load the datasets
&lt;/span&gt;&lt;span class="n"&gt;bike_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;citi_bike_data.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;weather_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;weather_data.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Handle missing values
&lt;/span&gt;&lt;span class="n"&gt;bike_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inplace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;weather_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ffill&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inplace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Merge datasets on the date column
&lt;/span&gt;&lt;span class="n"&gt;merged_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bike_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weather_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Format columns
&lt;/span&gt;&lt;span class="n"&gt;merged_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;merged_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the data prepped and ready, it was time to create the backbone of our analysis – the PostgreSQL database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up PostgreSQL
&lt;/h3&gt;

&lt;p&gt;Building a database schema is like designing the perfect bike frame – it needs to be sturdy and efficient. Here's the lowdown on how I set up my PostgreSQL database:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tables:&lt;/strong&gt; Created tables for bike rentals and weather data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relationships:&lt;/strong&gt; Linked the tables on the date column because it's all about timing!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a sneak peek at the SQL code for creating the tables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;bike_rentals&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ride_id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;start_station&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;end_station&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;user_type&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;weather&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;weather_id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;temperature&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;precipitation&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;snowfall&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After setting up the tables, I inserted the cleaned data into PostgreSQL. Smooth sailing so far!&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Analysis
&lt;/h3&gt;

&lt;p&gt;Now comes the fun part – analyzing the data! WiGitHub repositoryth our PostgreSQL database set up, it was time to dig into the data and uncover some insights. Here are the key questions we aimed to answer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How does weather affect the number of bike rentals?&lt;/li&gt;
&lt;li&gt;Are there specific weather conditions that lead to a spike or drop in bike rentals?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To tackle these questions, I used SQL to query the database and visualize the results with Python. Here’s an example of one of the SQL queries I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;br&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;br&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;br&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ride_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;trip_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;precipitation&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; 
    &lt;span class="n"&gt;bike_rentals&lt;/span&gt; &lt;span class="n"&gt;br&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; 
    &lt;span class="n"&gt;weather&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;br&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; 
    &lt;span class="n"&gt;br&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;br&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;precipitation&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; 
    &lt;span class="n"&gt;br&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query gave us a detailed view of bike rentals per day, categorized by user type and correlated with temperature and precipitation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Results and Discussion
&lt;/h3&gt;

&lt;p&gt;Here are some of the cool insights we uncovered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Temperature vs. Bike Rentals:&lt;/strong&gt; Warmer days see more bike rentals. No surprises there! Who doesn't love a sunny ride?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Precipitation vs. Bike Rentals:&lt;/strong&gt; Rainy days? Not so much. Bike rentals drop significantly when it’s raining.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a visualization of the temperature vs. trip counts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;plt&lt;/span&gt;

&lt;span class="c1"&gt;# Sample data for plotting
&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;merged_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;trip_counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;merged_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;trip_count&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trip_counts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Temperature vs. Bike Rentals&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Temperature (°C)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Trip Counts&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;And another one for precipitation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;precipitation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;merged_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;precipitation&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;precipitation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trip_counts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Precipitation vs. Bike Rentals&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Precipitation (mm)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Trip Counts&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As expected, there’s a strong positive correlation between warm weather and bike rentals. On the flip side, rain is a big no-no for bike enthusiasts.&lt;/p&gt;

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

&lt;p&gt;This project was a blast to work on and gave me some great insights into how weather affects bike rentals. It was a perfect blend of data cleaning, database management, and analysis – all while pedaling through Codecademy's Data Engineering Course.&lt;/p&gt;

&lt;p&gt;If you have any thoughts, suggestions, or just want to share your own bike rental stories, drop a comment below! Let's keep the conversation rolling.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code and Data
&lt;/h3&gt;

&lt;p&gt;You can check out the complete code and datasets on my &lt;a href="https://github.com/jampamatos/codecademy_stuff/tree/main/Data/bike_rental"&gt;GitHub repository&lt;/a&gt;. Feel free to fork it, play around with the data, and maybe even come up with your own insights.&lt;/p&gt;

&lt;p&gt;Thanks for reading, and happy riding!&lt;/p&gt;

</description>
      <category>python</category>
      <category>postgres</category>
      <category>datascience</category>
      <category>sql</category>
    </item>
    <item>
      <title>Lazy Programmer's Guide to Automating Price Calculations</title>
      <dc:creator>Jampa Matos</dc:creator>
      <pubDate>Thu, 23 May 2024 16:17:17 +0000</pubDate>
      <link>https://forem.com/jampamatos/lazy-programmers-guide-to-automating-price-calculations-4g93</link>
      <guid>https://forem.com/jampamatos/lazy-programmers-guide-to-automating-price-calculations-4g93</guid>
      <description>&lt;h2&gt;
  
  
  Introduction: Automating Price Calculations for the Lazy (and Efficient) Programmer
&lt;/h2&gt;

&lt;p&gt;Let's be honest: programmers, whether seasoned or aspiring, are driven by a certain kind of laziness. But this isn't the couch-potato kind; it's the kind that fuels innovation — finding ways to get more done with less effort. Why spend hours on repetitive tasks when a few lines of code can do the job for you?&lt;/p&gt;

&lt;p&gt;This mindset has been my guiding star as I navigate my dual roles. On my spare time, I code; at nine-to-five (seven-thirty-to-five-thirty, actually), I manage my dad's paint store. Among my various responsibilities, one of the most daunting is calculating the cost and selling prices of our products. Now, if you're familiar with Brazil's tax system, you'll understand the gravity of this task. For those who aren't, let me paint you a picture.&lt;/p&gt;

&lt;p&gt;In Brazil, taxes are a labyrinthine mess. Beyond federal tax rates, each state has the liberty to set its own tax rates, creating a patchwork of regulations that would make any sane person’s head spin. Enter 'substituição tributária' (ST) — a regime where the tax burden is transferred from the seller to the manufacturer or importer. The idea is to streamline tax collection, but in practice, it often complicates matters further, especially when products cross state lines.&lt;/p&gt;

&lt;p&gt;Calculating these costs manually is not just tedious; it's a soul-crushing endeavor. The repetitive nature of punching numbers into a calculator, cross-referencing tax tables, and double-checking figures is enough to make anyone yearn for a better way. That's where my programmer's "laziness" kicked in. I saw a clear need for automation — a way to take these convoluted calculations and simplify them into a process that’s as effortless as possible.&lt;/p&gt;

&lt;p&gt;Thus, the web-based price calculator project was born. Armed with formulas from my accountant and a determination to streamline our pricing workflow, I set out to build a tool that could handle the heavy lifting. This post will take you through the journey of its creation, detailing the code, the challenges, and the solutions. Along the way, I’ll share insights into how you too can leverage programming to simplify complex tasks in your work or business. So, let's dive in and explore how we can turn lazy into efficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Accountant's Formulas: Transforming Tedious Calculations into Computable Algorithms
&lt;/h2&gt;

&lt;p&gt;In the world of business, especially one as intricate as a paint store, pricing products accurately is crucial. My accountant, the wizard behind our financial curtain, provided me with a set of formulas to calculate our costs and selling prices. These formulas consider various taxes and additional charges that are part and parcel of Brazil's complex tax landscape.&lt;/p&gt;

&lt;p&gt;The general formula for calculating the cost is as follows:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Cost Price = Unit Price + ICMS(%) + IPI(%) + Freight(%)&lt;br&gt;
Selling Price = Cost Price + Markup(%) + Additional(%) + Federal Tax (fixed at 7%)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It’s important to note that each percentage is applied on top of the previous values, compounding the total cost incrementally.&lt;/p&gt;

&lt;p&gt;But it's not as straightforward as plugging numbers into a formula. The calculation of ICMS (a state tax) depends on whether the product falls under 'substituição tributária' (ST) or not, and this is where things get interesting.&lt;/p&gt;

&lt;p&gt;For products without ST (identified by CFOP codes 5101, 5102, 6101, and 6102):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the ICMS rate is 4%, the effective ICMS in the formula becomes 13.27%.&lt;/li&gt;
&lt;li&gt;If the ICMS rate is 12%, the effective ICMS in the formula becomes 7.32%.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For products with ST (identified by CFOP codes 5401, 5403, 5405, 6401, 6403, and 6405):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the IPI rate is 0%, the ICMS in the formula is 19.7%.&lt;/li&gt;
&lt;li&gt;If the IPI rate is 1.3%, the ICMS in the formula is 20.3%.&lt;/li&gt;
&lt;li&gt;If the IPI rate is 3.25%, the ICMS in the formula is 21.26%.&lt;/li&gt;
&lt;li&gt;If the IPI rate is 6.5%, the ICMS in the formula is 24%.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don’t entirely understand the logic behind these values — they’re the magic numbers handed down by my accountant. But I trust her expertise, and these are the values we need to work with.&lt;/p&gt;
&lt;h3&gt;
  
  
  Manual Calculations: A Tedious Routine
&lt;/h3&gt;

&lt;p&gt;Before automation, the process was as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Gather Information:&lt;/strong&gt; Collect the unit price, ICMS rate, IPI rate, freight percentage (if any), markup percentage, and additional charges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apply Formulas:&lt;/strong&gt; Using the provided formulas, manually calculate the effective ICMS rate based on the CFOP code and applicable ICMS/ IPI rates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calculate Cost Price:&lt;/strong&gt; Add up all the percentages and apply them to the unit price.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calculate Selling Price:&lt;/strong&gt; Add the calculated cost price to the markup, additional charges, and federal tax.&lt;/li&gt;
&lt;li&gt;**Repeat: **Perform these steps for each product, using a calculator to ensure accuracy.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This manual process was not only time-consuming but also prone to errors. Every miscalculation could lead to incorrect pricing, impacting our profit margins or making us uncompetitive.&lt;/p&gt;
&lt;h3&gt;
  
  
  From Manual to Algorithmic
&lt;/h3&gt;

&lt;p&gt;The beauty of these steps is that they form an algorithm, a series of computable steps. And this is where automation shines. By translating these steps into code, I could create a tool that performs these calculations instantly and accurately. No more tedious number crunching, no more manual errors — just a streamlined, efficient process that ensures we get our pricing right every time.&lt;/p&gt;

&lt;p&gt;In the next section, I'll walk you through how I transformed these formulas into a web-based price calculator, turning hours of work into a task that takes mere seconds.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementing the First Part: From Supplier Invoices to a Functional MVP
&lt;/h2&gt;

&lt;p&gt;In our paint store, every supplier sends us invoices in the form of XML files. These XML files are standardized by the Federal Revenue Service of Brazil, ensuring they follow a consistent structure. This consistency makes it feasible to automate the extraction of necessary data for our price calculations.&lt;/p&gt;
&lt;h3&gt;
  
  
  Technologies Used
&lt;/h3&gt;

&lt;p&gt;To build this solution, we chose a stack that includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flask:&lt;/strong&gt; A lightweight web framework for Python that makes it easy to set up routes and handle requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jinja2:&lt;/strong&gt; The templating engine used by Flask to render HTML pages dynamically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript (with jQuery):&lt;/strong&gt; To handle client-side interactions and make AJAX requests for dynamic updates.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Setting Up Routes
&lt;/h3&gt;

&lt;p&gt;For the initial implementation, we set up two main routes: &lt;code&gt;index&lt;/code&gt;and &lt;code&gt;calculate&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;code&gt;index&lt;/code&gt;Route
&lt;/h4&gt;

&lt;p&gt;The index route serves the main page of our application. It renders the form that allows users to upload an XML file, input the markup percentage, additional charges, and freight costs. We also set default values for these inputs to streamline the user experience.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product_data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="n"&gt;markup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;additional&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;freight&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include_tax&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;code&gt;calculate&lt;/code&gt;Route
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;calculate&lt;/code&gt;route processes the uploaded XML file. It extracts the necessary data, applies the formulas provided by my accountant, and calculates the cost and selling prices. This route then renders the results back to the &lt;code&gt;index.html&lt;/code&gt; template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/calculate&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calculate&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;xmlFile&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;url_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;xmlFile&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;url_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;markup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;markup&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;additional&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;additional&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;freight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;freight&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;include_tax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;federalTax&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;

    &lt;span class="n"&gt;namespaces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nfe&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http://www.portalfiscal.inf.br/nfe&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;product_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extract_data_from_xml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;namespaces&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;product_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cost_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculate_cost_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Valor Unitário&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Alíq. ICMS&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Alíq. IPI&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CFOP&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;freight&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;selling_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculate_selling_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cost_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;markup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;additional&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include_tax&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Preço de Custo Final&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cost_price&lt;/span&gt;
        &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Preço de Venda&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;selling_price&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product_data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;product_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;markup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;markup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;additional&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;additional&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;freight&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;freight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include_tax&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;include_tax&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Crafting the MVP
&lt;/h3&gt;

&lt;p&gt;The Minimum Viable Product (MVP) focused on getting the basic functionality right. This included:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Form Setup:&lt;/strong&gt; An HTML form for uploading XML files and inputting calculation parameters (markup, additional charges, freight, and federal tax inclusion).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Extraction:&lt;/strong&gt; Using Python's &lt;code&gt;xml.etree.ElementTree&lt;/code&gt; to parse the XML files and extract relevant product data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calculation Logic:&lt;/strong&gt; Implementing the cost and selling price formulas in Python.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result Display:&lt;/strong&gt; Dynamically rendering the calculated prices back to the user through the HTML template.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s the &lt;code&gt;index.html&lt;/code&gt; template for the form and result display:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Calculadora de Preço de Venda&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/static/style.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://code.jquery.com/jquery-3.6.0.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"productData"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;product_data&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nx"&gt;tojson&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"additionalData"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;additional&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"freightData"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;freight&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"includeTaxData"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;include_tax&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nx"&gt;tojson&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/static/script.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Calculadora de Preço de Venda&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/calculate"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt; &lt;span class="na"&gt;enctype=&lt;/span&gt;&lt;span class="s"&gt;"multipart/form-data"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"xmlFile"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Carregar XML:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"xmlFile"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"xmlFile"&lt;/span&gt; &lt;span class="na"&gt;accept=&lt;/span&gt;&lt;span class="s"&gt;".xml"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"markup"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Markup (%):&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"number"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"markup"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"markup"&lt;/span&gt; &lt;span class="na"&gt;step=&lt;/span&gt;&lt;span class="s"&gt;"0.1"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"50"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"additional"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Valor Adicional (%):&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"number"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"additional"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"additional"&lt;/span&gt; &lt;span class="na"&gt;step=&lt;/span&gt;&lt;span class="s"&gt;"0.1"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"freight"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Frete (%):&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"number"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"freight"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"freight"&lt;/span&gt; &lt;span class="na"&gt;step=&lt;/span&gt;&lt;span class="s"&gt;"0.1"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"federalTax"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Incluir Imposto Federal (7%):&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"federalTax"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"federalTax"&lt;/span&gt; &lt;span class="na"&gt;checked&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Calcular&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;

    {% if product_data %}
        &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Dados do Formulário:&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
            Markup Selecionado: {{ markup }}%&lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
            Valor Adicional Selecionado: {{ additional }}%&lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
            Frete Adicionado: {{ freight }}%&lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
            Incluir Imposto Federal: {{ 'Sim' if include_tax else 'Não' }}
        &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Cálculo dos Preços:&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;table&lt;/span&gt; &lt;span class="na"&gt;border=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Produto&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;CFOP&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Custo Unit&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Aliq. ICMS&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Aliq. IPI&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Frete&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Imposto Federal&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Markup (%)&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Custo Final&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Venda Final&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
            {% for product in product_data %}
            &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{ product['Nome do Produto'] }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{ product['CFOP'] }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;R$ {{ product['Valor Unitário'] }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{ product['Alíq. ICMS'] }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{ product['Alíq. IPI'] }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{ freight }}%&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{ 'Sim' if include_tax else 'Não' }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"number"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"markup-input"&lt;/span&gt; &lt;span class="na"&gt;step=&lt;/span&gt;&lt;span class="s"&gt;"0.1"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{ markup }}"&lt;/span&gt; &lt;span class="na"&gt;data-index=&lt;/span&gt;&lt;span class="s"&gt;"{{ loop.index0 }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;R$ {{ '%.2f' | format(product['Preço de Custo Final']) }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;R$ {{ '%.2f' | format(product['Preço de Venda']) }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
            {% endfor %}
        &lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
    {% endif %}
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;With this MVP in place, we had a functional tool that could handle the complexities of our pricing calculations, saving us time and reducing the potential for errors. But this was just the beginning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Interactivity: Editable Markup Fields for Dynamic Price Updates
&lt;/h2&gt;

&lt;p&gt;In the real world, the need for flexibility in pricing is paramount. Sometimes, different products on the same invoice require different markup percentages. Manually re-uploading the XML file and recalculating prices each time a markup needs to be adjusted is not only inefficient but also prone to errors. To address this, I introduced an interactive, editable markup field that updates the selling price automatically. This is where JavaScript comes into play.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enhancing User Experience with JavaScript
&lt;/h3&gt;

&lt;p&gt;To achieve this dynamic behavior, we needed to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make the markup field editable directly within the product table.&lt;/li&gt;
&lt;li&gt;Use JavaScript to detect changes in the markup field and trigger recalculations.&lt;/li&gt;
&lt;li&gt;Send the updated data to the server via AJAX and update the selling price without refreshing the page.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's a detailed breakdown of the implementation:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Updating the HTML Template
&lt;/h4&gt;

&lt;p&gt;First, we needed to ensure the markup field in our HTML table was editable and had the necessary attributes for our JavaScript to identify and manipulate it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;td&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"number"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"markup-input"&lt;/span&gt; &lt;span class="na"&gt;step=&lt;/span&gt;&lt;span class="s"&gt;"0.1"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{ markup }}"&lt;/span&gt; &lt;span class="na"&gt;data-index=&lt;/span&gt;&lt;span class="s"&gt;"{{ loop.index0 }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This line creates an input field within the table cell, sets its initial value to the current markup, and adds a &lt;code&gt;data-index&lt;/code&gt;attribute to keep track of the product's position in the list.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. JavaScript for Real-Time Updates
&lt;/h4&gt;

&lt;p&gt;We then added a JavaScript file to handle the dynamic updates. This script listens for changes in the markup input fields and makes AJAX requests to update the prices accordingly.&lt;/p&gt;

&lt;p&gt;Here’s the content of &lt;code&gt;static/script.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;productData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;additional&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;additionalData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;freight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;freightData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;includeTax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;includeTaxData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.markup-input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;markup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;val&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;updatePrices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;markup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updatePrices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;markup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ajax&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/update_prices&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;markup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;markup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;product_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;productData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;additional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;additional&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;freight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;freight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;include_tax&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;includeTax&lt;/span&gt;
            &lt;span class="p"&gt;}),&lt;/span&gt;
            &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;table&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;td&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;R$ &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;new_selling_price&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reads the product data and other relevant values from the hidden JSON elements in the HTML.&lt;/li&gt;
&lt;li&gt;Listens for input events on the markup fields.&lt;/li&gt;
&lt;li&gt;Sends an AJAX request to the server whenever a markup value changes, passing the updated markup and necessary data.&lt;/li&gt;
&lt;li&gt;Updates the displayed selling price with the new value returned from the server.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. Handling AJAX Requests in Flask
&lt;/h4&gt;

&lt;p&gt;We needed a new route in our Flask app to handle the AJAX requests and perform the necessary recalculations.&lt;/p&gt;

&lt;p&gt;Here’s the updated &lt;code&gt;main.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/update_prices&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_prices&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;markup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;markup&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;additional&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;additional&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;freight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;freight&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;include_tax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;include_tax&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;product_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;product_data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;product_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;cost_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculate_cost_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Valor Unitário&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Alíq. ICMS&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Alíq. IPI&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CFOP&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;freight&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;new_selling_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculate_selling_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cost_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;markup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;additional&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include_tax&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;new_selling_price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;new_selling_price&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This route:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Receives the updated markup and product data via a JSON payload.&lt;/li&gt;
&lt;li&gt;Recalculates the cost and selling prices using the same formulas as before.&lt;/li&gt;
&lt;li&gt;Returns the new selling price to the client, which JavaScript then updates in the table.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4. Adjusting the HTML Template
&lt;/h4&gt;

&lt;p&gt;Finally, ensure that the HTML template includes the necessary scripts and hidden JSON data.&lt;/p&gt;

&lt;p&gt;Here’s the updated &lt;code&gt;index.html&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Calculadora de Preço de Venda&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/static/style.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://code.jquery.com/jquery-3.6.0.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"productData"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;product_data&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nx"&gt;tojson&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"additionalData"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;additional&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"freightData"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;freight&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"includeTaxData"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;include_tax&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nx"&gt;tojson&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/static/script.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Calculadora de Preço de Venda&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/calculate"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt; &lt;span class="na"&gt;enctype=&lt;/span&gt;&lt;span class="s"&gt;"multipart/form-data"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"xmlFile"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Carregar XML:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"xmlFile"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"xmlFile"&lt;/span&gt; &lt;span class="na"&gt;accept=&lt;/span&gt;&lt;span class="s"&gt;".xml"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"markup"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Markup (%):&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"number"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"markup"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"markup"&lt;/span&gt; &lt;span class="na"&gt;step=&lt;/span&gt;&lt;span class="s"&gt;"0.1"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"50"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"additional"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Valor Adicional (%):&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"number"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"additional"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"additional"&lt;/span&gt; &lt;span class="na"&gt;step=&lt;/span&gt;&lt;span class="s"&gt;"0.1"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"freight"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Frete (%):&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"number"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"freight"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"freight"&lt;/span&gt; &lt;span class="na"&gt;step=&lt;/span&gt;&lt;span class="s"&gt;"0.1"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"federalTax"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Incluir Imposto Federal (7%):&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"federalTax"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"federalTax"&lt;/span&gt; &lt;span class="na"&gt;checked&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Calcular&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;

    {% if product_data %}
        &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Dados do Formulário:&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
            Markup Selecionado: {{ markup }}%&lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
            Valor Adicional Selecionado: {{ additional }}%&lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
            Frete Adicionado: {{ freight }}%&lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
            Incluir Imposto Federal: {{ 'Sim' if include_tax else 'Não' }}
        &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Cálculo dos Preços:&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;table&lt;/span&gt; &lt;span class="na"&gt;border=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Produto&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;CFOP&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Custo Unit&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Aliq. ICMS&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Aliq. IPI&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Frete&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Imposto Federal&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Markup (%)&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Custo Final&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Venda Final&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
            {% for product in product_data %}
            &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{ product['Nome do Produto'] }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{ product['CFOP'] }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;R$ {{ product['Valor Unitário'] }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{ product['Alíq. ICMS'] }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{ product['Alíq. IPI'] }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{ freight }}%&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{ 'Sim' if include_tax else 'Não' }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"number"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"markup-input"&lt;/span&gt; &lt;span class="na"&gt;step=&lt;/span&gt;&lt;span class="s"&gt;"0.1"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{ markup }}"&lt;/span&gt; &lt;span class="na"&gt;data-index=&lt;/span&gt;&lt;span class="s"&gt;"{{ loop.index0 }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;R$ {{ '%.2f' | format(product['Preço de Custo Final']) }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;R$ {{ '%.2f' | format(product['Preço de Venda']) }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
            {% endfor %}
        &lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
    {% endif %}
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these enhancements, we transformed a static form into an interactive tool that allows for real-time price adjustments. This not only streamlines our workflow but also provides flexibility in pricing, making the tool much more powerful and user-friendly. In the next section, I'll discuss potential future iterations and improvements to further enhance the functionality and usability of this project. &lt;/p&gt;

&lt;h2&gt;
  
  
  Future Iterations: Enhancing Functionality and User Experience
&lt;/h2&gt;

&lt;p&gt;With the basic functionality in place, there are several enhancements that can make the price calculator even more robust and user-friendly. Here are the next steps I'm considering for future iterations:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Configurations Page
&lt;/h3&gt;

&lt;p&gt;Currently, the tax rates and CFOP codes are hard-coded into the application. This approach works for an MVP, but it's not scalable. Tax rates can change, and new CFOP codes with different tax regimes can emerge. To address this, I plan to implement a configurations page where users can update these values directly within the app.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Editable Tax Rates and CFOP Codes:&lt;/strong&gt; Allow users to add, edit, and delete tax rates and CFOP codes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistent Settings:&lt;/strong&gt; Store these configurations in a database to ensure they are retained between sessions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Drag-and-Drop XML Upload
&lt;/h3&gt;

&lt;p&gt;To improve the user experience, I want to add drag-and-drop functionality for XML uploads. This would make the process more intuitive and quicker.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Drag-and-Drop Interface:&lt;/strong&gt; Implement a drag-and-drop zone for users to easily upload XML files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fallback to Button Upload:&lt;/strong&gt; Maintain the current file upload button as a fallback option.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Hosting on Store’s Domain
&lt;/h3&gt;

&lt;p&gt;Currently, the application is running on a Heroku server. To give it a more professional look and ensure better accessibility, I plan to host it on the store's domain.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Custom Domain:&lt;/strong&gt; Migrate the app to be hosted on the store's domain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Accessibility:&lt;/strong&gt; Ensure the app is easily accessible to all users, including employees and customers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Improved Aesthetics with CSS
&lt;/h3&gt;

&lt;p&gt;While functionality is king, aesthetics shouldn't be neglected. A well-designed interface can significantly enhance the user experience. I plan to use CSS to make the page more visually appealing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Modern UI Design:&lt;/strong&gt; Implement a clean, modern design for the app using CSS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Responsive Design:&lt;/strong&gt; Ensure the app works well on different devices, including desktops, tablets, and smartphones.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The journey of creating this web-based price calculator has been both challenging and rewarding. From automating tedious manual calculations to adding dynamic, real-time updates, each step has made the app more efficient and user-friendly. But the journey doesn't end here. With future iterations focusing on configurability, user experience, and professional hosting, the app will continue to evolve and serve its purpose even better.&lt;/p&gt;

&lt;p&gt;Stay tuned for more updates as I continue to enhance and refine this project. If you're on a similar journey or have any suggestions, I'd love to hear from you! Let's keep innovating and making our lives (and jobs) a bit easier, one line of code at a time.&lt;/p&gt;

</description>
      <category>python</category>
      <category>automation</category>
      <category>flask</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Golds and Dragons: the Quest of an AI Awakened World</title>
      <dc:creator>Jampa Matos</dc:creator>
      <pubDate>Tue, 14 Feb 2023 01:03:15 +0000</pubDate>
      <link>https://forem.com/jampamatos/golds-and-dragons-the-quest-of-an-ai-awakened-world-55p9</link>
      <guid>https://forem.com/jampamatos/golds-and-dragons-the-quest-of-an-ai-awakened-world-55p9</guid>
      <description>&lt;p&gt;So, I have just finished watching &lt;a href="https://www.youtube.com/watch?v=jPhJbKBuNnA" rel="noopener noreferrer"&gt;Tom Scott's latest video&lt;/a&gt;, on how new AI Language Models are a bit scaring, because we really don't know where we're heading from here. I won't paraphrase him here or anything, but, in a nutshell, he says that new technologies follow a typical sigmoid curve, with a slow start as they are being developed, a steep increase as new ideas reshape the technology, and a flattening of the curve as the technology saturates, and that he really don't know where in the curve technologies like &lt;a href="https://openai.com/blog/chatgpt/" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt; are right now, meaning: we could be in the middle, where new, exciting ways to use AI will be developed, in the end, where we've reached the peek of what AI assistance as we know it, or in the very beginning, where AI Language Models are a thunder, a sign in the distance of everything that is about to change forever.&lt;/p&gt;

&lt;p&gt;Unless you're living in a cave, you yourself have probably used some form of AI in the past months: maybe you played a little with generating crazy images by prompt with &lt;a href="https://openai.com/dall-e-2/" rel="noopener noreferrer"&gt;Dall-e&lt;/a&gt;, or discussed philosophy with ChatGPT for fun, but the reality is that AI powered programs are already a tool in a variety of industries and fields, from helping people write documents and essays, to assisting researchers in reading and cataloging information from papers. There have been instances of AI models mimicking established artists' styles to generate new images or text. I have even seen a lawyer on Twitter saying that ChatGPT wrote a document for him from scratch, with minor changes needed to be done, and that even if AI won't replace lawyers anytime soon, they might've just replaced interns.&lt;/p&gt;

&lt;p&gt;But it seems to me that it is in computer science -- and in software development in general -- that these technologies show more prominent use. Not only ChatGPT, but things like &lt;a href="https://github.com/features/copilot" rel="noopener noreferrer"&gt;GitHub's Copilot&lt;/a&gt; and &lt;a href="https://replit.com/site/ghostwriter" rel="noopener noreferrer"&gt;Replit's Ghostwriter&lt;/a&gt; can turn hours and hours of googling and stack-overflowing in solid minutes of straight up problem solving. For the time I've been using these technologies, it seems to me as if the process of discussing coding with ChatGPT can feel like a junior-senior mentorship: there is back an forth, there is encouragement, there is learning and productive idea shaping. The AI make mistakes, that sometimes we have to point out for it. It corrects our misspelling code or catch one missed ';' that has been driving you crazy for days. We tell it what we want to achieve and it helps us develop the steps we need to follow to write an algorithm. It writes our documentation for us. It explains to us back that one colleague's code that simply does not make sense. There's a plethora of ways you can use these tools to make coding more -- and I really mean to use this word here -- &lt;strong&gt;fun&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When I prompted ChatGTP if it "think[s] [it is] a game changer in computer science and software engeneering?", it answered me that "AI language models have certainly been a game changer in the fields of computer science and software engineering, but it remains to be seen what the full extent of their impact will be". And this is something completely difficult to measure -- as Tom said, we still don't know where in the curve we are in right now, and as time rolls by, all will be clear, but as of now, we are in the dark. How can this technology improve in the near future? What challenges will it overcome -- and what others will it generate? How are we going to deal with the fact that machine learning systems require gargantuan amounts of data to be fed into it? Are we going to freely give in to reap the benefits of using this tech? And what about the data itself? Will it make AI systems to perpetuate existing biases and discrimination?&lt;/p&gt;

&lt;p&gt;I feel like all of these questions highlight the need for continued reflection on the impact of AI, and the overboard considerations involved in their development and use. While the benefits of using AI language models are clear, we have to consider that, well, maybe they need to be weighted.&lt;/p&gt;

&lt;p&gt;I started this post aiming it to be an essay, but I've realized halfway that I don't really know what I want to say about these technologies. I don't really understand them, not as much as I do understand what goes inside Ruby's kernel when I type and run some code. That is to say: if it, as a tool, works for me, than it works. It completes its job, to make my life a little bit easier. And as a tool, it is a wonder.&lt;/p&gt;

&lt;p&gt;But, more often than not, a tool might not be just a tool. It may come with strings attached.&lt;/p&gt;

&lt;p&gt;And if Tom had a minor existential breakthrough while trying to tidy up his mailbox, I might just have had mine while watching his video.&lt;/p&gt;

&lt;p&gt;Nonetheless, we are now, again, in uncharted territory. What comes from it might be gold.&lt;/p&gt;

&lt;p&gt;Or dragons.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;by the way: while this post was completely written by me, I have used ChatGPT to brainstorm ideas and to fine tune my writing. It did even chose the four tags for me. I guess I'll have to thank it for that. So, thanks, ChatGPT!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>themes</category>
      <category>ergonomics</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Day 23 - The epiphany</title>
      <dc:creator>Jampa Matos</dc:creator>
      <pubDate>Tue, 09 Nov 2021 22:33:47 +0000</pubDate>
      <link>https://forem.com/jampamatos/day-23-the-epiphany-4o</link>
      <guid>https://forem.com/jampamatos/day-23-the-epiphany-4o</guid>
      <description>&lt;p&gt;Well, I've been doing a &lt;a href="https://todayjampalearned.wordpress.com/"&gt;kind of "Today I learned" blog&lt;/a&gt;, but I've now decided to give Dev a go, and bring here all my insights at this whole coding path that I've decided to take.&lt;/p&gt;

&lt;p&gt;Funny thing that I had to jump from day 11 to day 23, which makes for almost two weeks without thinking much about what I learned, but I guess it's because I've turned on the automatic at this point. You know, learning new stuff, trying to solve a coding problem, googling it up to find that answer to what to do next, playing videogames when I'm fed up, etc.&lt;/p&gt;

&lt;p&gt;But then I came across this new problem -- the last exercise of the Fundamental part of &lt;a href="https://www.theodinproject.com/"&gt;The Odin Project&lt;/a&gt;. Nothing big, really, just you typical array of objects.&lt;/p&gt;

&lt;p&gt;But then for some reason I froze before it. Couldn't fix it, couldn't solve it, whenever I found something that looked like a solution, the more it seemed to run through my fingers.&lt;/p&gt;

&lt;p&gt;Weekend came and went and I'm still there, fighting myself and those little 10 lines of code.&lt;/p&gt;

&lt;p&gt;And then, today, at work (and I obviously don't work with coding), it came to me, like a bucket full of cold water hitting me in the face: the solution.&lt;/p&gt;

&lt;p&gt;Now, I've seen people talking about how Devs eventually have to stand up and take a walk and do other stuff that the solution eventually might come, and as far-fetch as it sounds, it seems to really work.&lt;/p&gt;

&lt;p&gt;I guess I could call it an epiphany. Probably many of several ones yet to come.&lt;/p&gt;

&lt;p&gt;Funny thing, the Solemnity of the Epiphany is the Catholic celebration of the Three Wise Kings that came to visit Jesus after he was born, following the Star of Bethlehem. The Brazilian folklore has a special celebration of this, with music, theatrical performances and such. My neighbor is part of the "King's troupe", and they were rehearsing yesterday.&lt;/p&gt;

&lt;p&gt;It's a stretch, but, hey, I'll take whatever good news I can get.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
