<?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>DEV Community: Simon Whelan</title>
    <description>The latest articles on DEV Community by Simon Whelan (@siwhelan).</description>
    <link>https://dev.to/siwhelan</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%2F1003901%2F9fffe9cd-221e-4ad0-8e52-1dba78fd17e1.jpeg</url>
      <title>DEV Community: Simon Whelan</title>
      <link>https://dev.to/siwhelan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/siwhelan"/>
    <language>en</language>
    <item>
      <title>Optimised Django App Setup in Windows VSCode 🚀🐍🛠️ - Part 2</title>
      <dc:creator>Simon Whelan</dc:creator>
      <pubDate>Sun, 02 Apr 2023 12:02:36 +0000</pubDate>
      <link>https://dev.to/siwhelan/optimised-django-app-setup-in-windows-vscode-part-2-191g</link>
      <guid>https://dev.to/siwhelan/optimised-django-app-setup-in-windows-vscode-part-2-191g</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/siwhelan/optimized-django-app-setup-in-windows-vscode-lg2"&gt;last post&lt;/a&gt; in this series, we built a Django app with some basic HTML and CSS and ran it on a local server. &lt;/p&gt;

&lt;p&gt;In this post, we'll be giving the app the planned functionality, adding some more HTML/CSS and packaging the whole thing in a Docker container.&lt;/p&gt;

&lt;p&gt;First up, we need to install &lt;a href="https://pypi.org/project/requests/"&gt;Requests&lt;/a&gt;. This is a popular Python library used to make HTTP requests. It allows you to send HTTP/1.1 requests extremely easily, and comes with a number of useful features out of the box, including:&lt;/p&gt;

&lt;p&gt;🌐 Support for HTTP/1.1 and HTTP/2&lt;br&gt;
🔄 Automatic connection pooling and retries&lt;br&gt;
🍪 Support for cookies and sessions&lt;br&gt;
🧱 Built-in JSON support&lt;br&gt;
🔑 Support for custom authentication schemes&lt;br&gt;
💨 Automatic decompression of gzip and deflate responses&lt;br&gt;
🔐 Support for SSL/TLS verification and client-side certificates&lt;/p&gt;

&lt;p&gt;And loads of other useful stuff.&lt;/p&gt;

&lt;p&gt;With requests, you can send HTTP requests to any web server and receive the response back in Python. This makes it a powerful tool for web scraping, testing, and building web applications.&lt;/p&gt;

&lt;p&gt;To install requests, navigate to your root folder in your terminal and enter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.\venv\Scripts\Activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;if your venv is not already active, and then:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;to install Requests in your virtual environment. &lt;/p&gt;

&lt;p&gt;Now navigate you your &lt;code&gt;views.py&lt;/code&gt; file and replace the existing code with the following:&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;from&lt;/span&gt; &lt;span class="nn"&gt;django.http&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;JsonResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;HttpResponseBadRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;HttpResponseServerError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.shortcuts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_wayback_availability&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="o"&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;api_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://archive.org/wayback/available"&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&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;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;timestamp&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;# Send GET request to Wayback Machine API with URL and (optional) timestamp
&lt;/span&gt;        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# raise exception for 4xx or 5xx HTTP status codes
&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# parse JSON response
&lt;/span&gt;    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exceptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestException&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="c1"&gt;# handle any exceptions that occurred during the request
&lt;/span&gt;        &lt;span class="k"&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="s"&gt;"Error: &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="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&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="n"&gt;request&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;request&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="s"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;url&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;POST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;timestamp&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;POST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Return HTTP 400 Bad Request if user did not provide a URL
&lt;/span&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponseBadRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Please provide a URL."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;wayback_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;check_wayback_availability&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;wayback_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Return HTTP 500 Internal Server Error if an error occurred while checking the URL
&lt;/span&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponseServerError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"An error occurred while checking the URL."&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;wayback_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"archived_snapshots"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="c1"&gt;# Construct URL for closest snapshot
&lt;/span&gt;            &lt;span class="n"&gt;snapshot_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wayback_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"archived_snapshots"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;"closest"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="c1"&gt;# Render template with URL
&lt;/span&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render&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="s"&gt;"snapshot.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"snapshot_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;snapshot_url&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Return regular HTTP response indicating that the URL 
&lt;/span&gt;            &lt;span class="c1"&gt;# is not available in the Wayback Machine, with a button to refresh/start over
&lt;/span&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render&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="s"&gt;"not_found.html"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Render index.html template for GET requests
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render&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="s"&gt;"index.html"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This code defines two functions and a view in Django for a web app that interacts with the Wayback Machine API. The first function, &lt;code&gt;check_wayback_availability()&lt;/code&gt;, sends a GET request to the Wayback Machine API with a given URL and an optional timestamp. If the API returns a response with archived snapshots of the URL, the function returns the response data in JSON format. If an error occurs while making the request, the function returns None.&lt;/p&gt;

&lt;p&gt;The second function, &lt;code&gt;index()&lt;/code&gt;, is a view that handles both GET and POST requests. If it receives a POST request, it retrieves the URL and timestamp from the request, checks the Wayback Machine API for a snapshot, and renders the appropriate template based on the availability of the URL. If the URL is available in the Wayback Machine, the view renders a template with a link to the closest snapshot. Otherwise, it renders a template indicating that the URL is not available.&lt;/p&gt;

&lt;p&gt;Ok, now navigate to your app's &lt;code&gt;templates&lt;/code&gt; folder and replace the exisiting code in your &lt;code&gt;index.html&lt;/code&gt; file with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{% load static %}
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&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;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"ie=edge"&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;Wayback Machine Checker&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Bootstrap CSS --&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;"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"&lt;/span&gt; &lt;span class="na"&gt;integrity=&lt;/span&gt;&lt;span class="s"&gt;"sha384-BJTk5RbVdJvYtOrNQ2Fi0Ntq62V7AXu3C9An/7Fgj0aodH8pHHhjcfnLZjMDT76m"&lt;/span&gt; &lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;"anonymous"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Custom CSS --&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 'wayback_app/css/styles.css' %}"&lt;/span&gt;&lt;span class="nt"&gt;&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;br&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container py-5 mt-5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row justify-content-center mb-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-md-8 text-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Wayback Machine Checker&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row justify-content-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-md-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"wayback-form"&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;class=&lt;/span&gt;&lt;span class="s"&gt;"custom-form wayback-form"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    {% csrf_token %}
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-floating mb-3"&lt;/span&gt;&lt;span class="nt"&gt;&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;"text"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-control rounded-0"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"url"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"url"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Enter URL"&lt;/span&gt; &lt;span class="na"&gt;required&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;"url"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"d-grid gap-2 col-4 mx-auto"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                      &lt;span class="nt"&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="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-primary rounded-0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Check&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;

                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"result"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Bootstrap JS --&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://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"&lt;/span&gt; &lt;span class="na"&gt;integrity=&lt;/span&gt;&lt;span class="s"&gt;"sha384-T2I4fj1nJWg9JwFVzL0sq0S7IxCjZryEGv+vJ20dbD8x4y4x4aKIFzYQWVuJ8Ipk"&lt;/span&gt; &lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;"anonymous"&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;/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;Now add a new file in the same directory as &lt;code&gt;index.html&lt;/code&gt; called &lt;code&gt;not_found.html&lt;/code&gt; and paste the following code into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{% load static %}
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&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;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"ie=edge"&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;Wayback Machine Checker&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Bootstrap CSS --&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;"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"&lt;/span&gt; &lt;span class="na"&gt;integrity=&lt;/span&gt;&lt;span class="s"&gt;"sha384-BJTk5RbVdJvYtOrNQ2Fi0Ntq62V7AXu3C9An/7Fgj0aodH8pHHhjcfnLZjMDT76m"&lt;/span&gt; &lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;"anonymous"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Custom CSS --&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 'wayback_app/css/styles.css' %}"&lt;/span&gt;&lt;span class="nt"&gt;&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container py-5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;The URL is not available in the Wayback Machine&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row justify-content-center mt-5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-md-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"d-grid gap-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-primary rounded-0"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"window.location.href=''"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Return&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Bootstrap JS --&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://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"&lt;/span&gt; &lt;span class="na"&gt;integrity=&lt;/span&gt;&lt;span class="s"&gt;"sha384-T2I4fj1nJWg9JwFVzL0sq0S7IxCjZryEGv+vJ20dbD8x4y4x4aKIFzYQWVuJ8Ipk"&lt;/span&gt; &lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;"anonymous"&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;/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;And now a second new file named &lt;code&gt;snapshot.html&lt;/code&gt; with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{% load static %}
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&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;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"ie=edge"&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;Wayback Machine Checker&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Bootstrap CSS --&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;"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"&lt;/span&gt; &lt;span class="na"&gt;integrity=&lt;/span&gt;&lt;span class="s"&gt;"sha384-BJTk5RbVdJvYtOrNQ2Fi0Ntq62V7AXu3C9An/7Fgj0aodH8pHHhjcfnLZjMDT76m"&lt;/span&gt; &lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;"anonymous"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Custom CSS --&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 'wayback_app/css/styles.css' %}"&lt;/span&gt;&lt;span class="nt"&gt;&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container py-5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Wayback Machine Snapshot&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row justify-content-center mt-5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-md-6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;The URL is available in the Wayback Machine at the following URL:&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ snapshot_url }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ snapshot_url }}&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Bootstrap JS --&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://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"&lt;/span&gt; &lt;span class="na"&gt;integrity=&lt;/span&gt;&lt;span class="s"&gt;"sha384-T2I4fj1nJWg9JwFVzL0sq0S7IxCjZryEGv+vJ20dbD8x4y4x4aKIFzYQWVuJ8Ipk"&lt;/span&gt; &lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;"anonymous"&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;/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;Finally, in your static folder, update the &lt;code&gt;styles.css&lt;/code&gt; file to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.custom-form&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f8f9fa&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.wayback-form&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;500px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.center&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&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 is just some very basic CSS to make our app interface look a little nicer!&lt;/p&gt;

&lt;p&gt;Ok, lets see if this works! Navigate to your root directory and run the following in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py runserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now visit &lt;a href="http://127.0.0.1:8000/wayback_app/"&gt;http://127.0.0.1:8000/wayback_app/&lt;/a&gt; in your browser to see it in action - Feel free to grab any link you like and try it out!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_O4Q5mDy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/svx5v2zr0192z9hvyxxu.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_O4Q5mDy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/svx5v2zr0192z9hvyxxu.PNG" alt="Image of the app input" width="880" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok - now to package the whole thing up into a Docker container. &lt;/p&gt;

&lt;p&gt;First run &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip freeze &amp;gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;to grab any new dependencies, then follow these steps:&lt;/p&gt;

&lt;p&gt;Install Docker on your system, if you haven't already. You can download Docker Desktop for Windows from &lt;a href="https://www.docker.com/products/docker-desktop"&gt;https://www.docker.com/products/docker-desktop&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Open the app and make sure Docker is running. You can check this in your terminal with:&lt;/p&gt;

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

&lt;/div&gt;

&lt;p&gt;Create a &lt;code&gt;Dockerfile&lt;/code&gt; in the root of your Django project directory, you can do this in the VSCode terminal:&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="nb"&gt;type&lt;/span&gt; &lt;span class="n"&gt;nul&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Dockerfile&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now open the &lt;code&gt;Dockerfile&lt;/code&gt; and add the following:&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;# Use the official Python base image&lt;/span&gt;
FROM python:3.9-slim

&lt;span class="c"&gt;# Set environment variables&lt;/span&gt;
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

&lt;span class="c"&gt;# Set the working directory&lt;/span&gt;
WORKDIR /app

&lt;span class="c"&gt;# Install system dependencies&lt;/span&gt;
RUN apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    gcc &lt;span class="se"&gt;\&lt;/span&gt;
    python3-dev &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    apt-get clean &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# Copy requirements.txt and install Python dependencies&lt;/span&gt;
COPY requirements.txt /app/requirements.txt
RUN pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# Copy the Django project&lt;/span&gt;
COPY &lt;span class="nb"&gt;.&lt;/span&gt; /app/

&lt;span class="c"&gt;# Expose the port the app runs on&lt;/span&gt;
EXPOSE 8000

&lt;span class="c"&gt;# Start the Django development server&lt;/span&gt;
CMD &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"python"&lt;/span&gt;, &lt;span class="s2"&gt;"manage.py"&lt;/span&gt;, &lt;span class="s2"&gt;"runserver"&lt;/span&gt;, &lt;span class="s2"&gt;"0.0.0.0:8000"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Dockerfile defines a new Docker image based on the official Python 3.9 slim image, sets environment variables, installs system dependencies, installs the Python packages from the requirements.txt file, and copies your Django project into the container.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;.dockerignore&lt;/code&gt; file in the root of your Django project directory to exclude unnecessary files and folders from the Docker build context. You can do this in the VSCode terminal:&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="nb"&gt;type&lt;/span&gt; &lt;span class="n"&gt;nul&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dockerignore&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the .dockerignore file in VSCode and add the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gs"&gt;__pycache__&lt;/span&gt;
&lt;span class="err"&gt;*&lt;/span&gt;.pyc
&lt;span class="err"&gt;*&lt;/span&gt;.pyo
&lt;span class="err"&gt;*&lt;/span&gt;.pyd
.Python
env/
venv/
ENV/
.vscode/
.git/
.gitignore
Dockerfile
&lt;span class="err"&gt;*&lt;/span&gt;.sqlite3
&lt;span class="err"&gt;*&lt;/span&gt;.log
media/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now navigate to your root directory (if you aren't already there) and build the Docker image by entering the following into your terminal:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build -t wayback_project:latest .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;After building the image, you can run the Docker container using the following command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -it -p 8000:8000 wayback_project:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now your Django project is running in a Docker container. Open your browser and visit &lt;a href="http://localhost:8000/wayback_app/"&gt;http://localhost:8000/wayback_app/&lt;/a&gt; to see your Django web app in action!&lt;/p&gt;

&lt;p&gt;That's it for this tutorial on setting up a Django app with optimized dependencies and packaging it in a Docker container! We've covered a lot of ground, from installing Requests to interacting with the Wayback Machine API, to building a Docker image and running our app in a container.&lt;/p&gt;

&lt;p&gt;But this is just the beginning - there are many more ways to optimize and improve your Django app, and you may want to add more features or functionality in the future. For example, you could integrate a database, add user authentication and authorisation, or deploy your app to a cloud platform.&lt;/p&gt;

&lt;p&gt;So keep this tutorial handy as a reference, and feel free to modify and extend it as you see fit. And if you have any feedback or suggestions for future topics, please let me know in the comments!&lt;/p&gt;

&lt;p&gt;The full code can be viewed &lt;a href="https://github.com/siwhelan/wayback_app"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading! 👋&lt;/p&gt;

</description>
      <category>python</category>
      <category>webdev</category>
      <category>docker</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Optimised Django App Setup in Windows VSCode 🚀🐍🛠️</title>
      <dc:creator>Simon Whelan</dc:creator>
      <pubDate>Tue, 28 Mar 2023 17:09:38 +0000</pubDate>
      <link>https://dev.to/siwhelan/optimized-django-app-setup-in-windows-vscode-lg2</link>
      <guid>https://dev.to/siwhelan/optimized-django-app-setup-in-windows-vscode-lg2</guid>
      <description>&lt;p&gt;Setting up a new Django project is a multi-step process that involves installing various tools and packages, creating directories, and configuring settings. This process can take up valuable time and effort that could be better spent on developing your app's functionality.&lt;/p&gt;

&lt;p&gt;To help streamline the process and make it easier to start my own Django projects, I created a step-by-step guide that combines essential tools and best practices like VSCode, venv, and .gitignore. This step-by-step is something I created for myself to make setting up Django projects easier and faster. As I found it helpful, I thought it might be beneficial to share it with other developers learning Django. &lt;/p&gt;

&lt;p&gt;In this tutorial, I'll guide you through my process of setting up a new Django web app quickly and efficiently. We'll cover everything from installing Python and Django to creating a virtual environment and generating a requirements.txt file. By the end of this tutorial, you'll have a fully functioning Django app up and running! 🚀🐍🛠️&lt;/p&gt;

&lt;p&gt;We're going to build a basic &lt;a href="https://www.djangoproject.com/" rel="noopener noreferrer"&gt;Django&lt;/a&gt; app that interacts with the Internet Archive's &lt;a href="https://archive.org/web/" rel="noopener noreferrer"&gt;Wayback Machine&lt;/a&gt; API. While intentionally simple, this app demonstrates the use of various technologies and best practices, making it a perfect example for our easy to follow Django app template.&lt;/p&gt;

&lt;p&gt;Our app will allow users to input a URL and an optional timestamp, and then check if the Wayback Machine has a snapshot of that URL available. If a snapshot exists, the app will display the archived content; if not, it will inform the user that the URL isn't available in the Wayback Machine.&lt;/p&gt;

&lt;p&gt;Here's a quick overview of the app's functionality:&lt;/p&gt;

&lt;p&gt;1) We define a &lt;code&gt;check_wayback_availability()&lt;/code&gt; function that takes a URL and an optional timestamp as arguments. It sends a GET request to the Wayback Machine API and returns the JSON data.&lt;/p&gt;

&lt;p&gt;2) The &lt;code&gt;index&lt;/code&gt; view function handles both GET and POST requests. If it receives a POST request, it retrieves the URL and timestamp from the request, checks the Wayback Machine for a snapshot, and renders the appropriate template based on the availability of the URL.&lt;/p&gt;

&lt;p&gt;Although the app itself is fairly simple, it showcases how our Django app template can be used to set up a project quickly and efficiently. By following this tutorial, you'll learn how to implement this app while building a time-saving optimised template that you can use in future projects.&lt;/p&gt;

&lt;p&gt;First off, let's build a basic Django environment - &lt;/p&gt;

&lt;p&gt;Step 1: Install Python 3 and Django&lt;/p&gt;

&lt;p&gt;Make sure you have Python 3 and Django installed on your system. You can download Python from the &lt;a href="https://www.python.org/downloads/" rel="noopener noreferrer"&gt;official website&lt;/a&gt; and install Django using pip:&lt;/p&gt;

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

&lt;/div&gt;

&lt;p&gt;Step 2: Install VSCode&lt;/p&gt;

&lt;p&gt;If you haven't already, download and install &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 3: Open VSCode and launch the terminal&lt;/p&gt;

&lt;p&gt;Open VSCode and press Ctrl+~ or click on 'Terminal' in the top menu, then select 'New Terminal' to open the integrated terminal.&lt;/p&gt;

&lt;p&gt;Step 4: Create a new directory for your project&lt;/p&gt;

&lt;p&gt;Create a new directory for your project using the following commands, I've called mine 'wayback' but you can call yours whatever you like!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir wayback
cd wayback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Step 5: Create a virtual environment&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python -m venv venv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will create a virtual environment named "venv" in your project directory.&lt;/p&gt;

&lt;p&gt;Step 6: Activate the virtual environment&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.\venv\Scripts\Activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Your terminal should now show the virtual environment's name in the prompt.&lt;/p&gt;

&lt;p&gt;Step 7: Install Django in the virtual environment&lt;/p&gt;

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

&lt;/div&gt;

&lt;p&gt;Step 8: Start a new Django project&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;django-admin startproject wayback_project .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will create a new Django project in your current directory.&lt;/p&gt;

&lt;p&gt;Step 9: Create a new Django app&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py startapp wayback_app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Step 10: Run the development server&lt;/p&gt;

&lt;p&gt;Run the development server using the following command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py runserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You should see a message saying that the development server is running at &lt;a href="http://127.0.0.1:8000/" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Open a browser and visit &lt;a href="http://127.0.0.1:8000/" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/&lt;/a&gt; to see your new Django web app running. It should look something like this - &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffp6bnjupom7ef1iw8nr0.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffp6bnjupom7ef1iw8nr0.PNG" alt="Django template page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You now have a Django web app running in a virtual environment using Python 3 in the VSCode terminal on Windows!&lt;/p&gt;

&lt;p&gt;Ok, we've successfully set up a basic Django environment and created a new project and app. Now, it's time to set up &lt;code&gt;urls.py&lt;/code&gt; in your Django project so that your app's URLs can be included in your project's URLs. In a Django project, you can create multiple apps, each with its own URLs. However, to make these app URLs accessible within the project, you need to include them in the project's main URLs. This enables users to navigate through the app's URLs within the project's URL structure.&lt;/p&gt;

&lt;p&gt;In your Django project, open the your_project_name/urls.py file, where your_project_name is the name of your Django project, for this example, mine is 'wayback_project'. Import the include function by modifying the existing import statement at the top of the file:&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;from&lt;/span&gt; &lt;span class="n"&gt;django.urls&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the urlpatterns list, add a new entry that includes the URLs from your app:&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="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wayback_app/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wayback_app.urls&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;Now, you need to create a urls.py file inside your app's directory. In the VSCode terminal, run:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type nul &amp;gt; wayback_app\urls.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This command will create an empty urls.py file inside your app's directory.&lt;/p&gt;

&lt;p&gt;Open the newly created urls.py file in your app's directory, and add the following content:&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;from&lt;/span&gt; &lt;span class="n"&gt;django.urls&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;

&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="c1"&gt;# Add your app's URL patterns here
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file is where you will define the URL patterns specific to your app.&lt;/p&gt;

&lt;p&gt;Now your Django project is set up to include URLs from your app. As you create new views in your app, you can add corresponding URL patterns to the urls.py file in your app's directory. Make sure to update the urlpatterns list in the wayback_app/urls.py file with the new paths.&lt;/p&gt;

&lt;p&gt;After you have installed all the necessary packages for your Django project, you can use the pip freeze command to generate a requirements.txt file containing a list of all the installed packages and their versions.&lt;/p&gt;

&lt;p&gt;To generate the requirements.txt file, navigate to your project directory in the VSCode terminal and run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip freeze &amp;gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a requirements.txt file in your project directory containing a list of all the installed packages and their versions.&lt;/p&gt;

&lt;p&gt;You can then use this file to install the same packages and their versions in a different environment by running the following command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will install all the packages listed in the requirements.txt file in the current environment.&lt;/p&gt;

&lt;p&gt;A requirements.txt file ensures all required packages are installed, making it easier to reproduce the same environment in different places. This is crucial for Docker, which requires consistent environments to prevent issues and inconsistencies when running an app in a containerised environment.&lt;/p&gt;

&lt;p&gt;To add a &lt;code&gt;.gitignore&lt;/code&gt; file to your Django project, follow these steps:&lt;/p&gt;

&lt;p&gt;In the VSCode terminal, navigate to the root of your project directory if you are not already there.&lt;/p&gt;

&lt;p&gt;Create a new .gitignore file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type nul &amp;gt; .gitignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This command will create an empty .gitignore file in your project directory.&lt;/p&gt;

&lt;p&gt;Open the .gitignore file in VSCode by clicking on it in the file explorer or using the File &amp;gt; Open menu.&lt;/p&gt;

&lt;p&gt;Add the following content to the .gitignore file to exclude common files and directories that should not be tracked by Git:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Python artifacts
__pycache__/
*.pyc
*.pyo
*.pyd
.Python
env/
venv/
ENV/

# Django artifacts
*.log
*.pot
*.pyc
db.sqlite3
media/

# VSCode artifacts
.vscode/

# Git artifacts
.git/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Save the file by pressing Ctrl+S or clicking File &amp;gt; Save.&lt;/p&gt;

&lt;p&gt;You now have a .gitignore file in your Django project that excludes common files and directories from being tracked by Git. You can customize the file to include or exclude any additional files or directories specific to your project.&lt;/p&gt;

&lt;p&gt;Now that you have a basic Django app up and running, let's add some basic HTML and CSS to our web app. This will help to make our app more visually appealing and user-friendly.&lt;/p&gt;

&lt;p&gt;Create two folders in your wayback_app folder named 'static' and 'templates'.&lt;/p&gt;

&lt;p&gt;Inside the 'static' folder, create a new file named 'styles.css'. Open it and paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.welcome&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;darkblue&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;Inside the 'templates' folder, create a new file named 'index.html'. Add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{% load static %}
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&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;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&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;Wayback App&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 'wayback_app/css/styles.css' %}"&lt;/span&gt;&lt;span class="nt"&gt;&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&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"welcome"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Welcome to the Wayback App!&lt;span class="nt"&gt;&amp;lt;/h1&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;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the settings.py file located in the wayback_project folder, and modify the TEMPLATES and STATIC_URL settings as follows:&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;TEMPLATES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="bp"&gt;...&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;APP_DIRS&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="bp"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;STATIC_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/static/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll also need to add your app to the INSTALLED_APPS setting - it should look something like this:&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;INSTALLED_APPS&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;wayback_app&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;django.contrib.admin&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;django.contrib.auth&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;django.contrib.contenttypes&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;django.contrib.sessions&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;django.contrib.messages&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;django.contrib.staticfiles&lt;/span&gt;&lt;span class="sh"&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;Almost there! Now open the views.py file in wayback_app and add the following lines:&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;from&lt;/span&gt; &lt;span class="n"&gt;django.shortcuts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;home&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wayback_app/index.html&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;This code is like a recipe for creating a webpage. It has a function called &lt;code&gt;home()&lt;/code&gt; that takes a request (like a message) and uses it to put together an HTML page. It uses a Django function called render to take the &lt;code&gt;index.html&lt;/code&gt; file in the wayback_app folder and put it together into a page for the user to see.&lt;/p&gt;

&lt;p&gt;So when someone goes to a certain URL on your website, this code helps create the page that they see. &lt;/p&gt;

&lt;p&gt;Finally, run the development server again using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py runserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when you visit &lt;a href="http://localhost:8000/wayback_app/" rel="noopener noreferrer"&gt;http://localhost:8000/wayback_app/&lt;/a&gt;, you should see the "Welcome to the Wayback App!" message, and if your static file is correctly set up, it should be in blue!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwlfshmtggi0z511n5dbc.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwlfshmtggi0z511n5dbc.PNG" alt="welcome to the wayback app!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok, that's enough for Part 1 of this series! In the next part we'll build our app, implement some more complex HTML and CSS to make the app more visually pleasing, and package it in a Docker container with all it's dependencies, making it easy to deploy and run on any machine.&lt;/p&gt;

&lt;p&gt;Hope you've found this helpful so far, and if you have any questions please ask!&lt;/p&gt;

&lt;p&gt;Thanks for reading! 👋&lt;/p&gt;

</description>
      <category>python</category>
      <category>django</category>
      <category>docker</category>
      <category>beginners</category>
    </item>
    <item>
      <title>stockControl 🥕🥫🦐 - A comprehensive system for tracking recipes, ingredients and costs - *Update*</title>
      <dc:creator>Simon Whelan</dc:creator>
      <pubDate>Wed, 15 Feb 2023 21:18:33 +0000</pubDate>
      <link>https://dev.to/siwhelan/stockcontrol-a-comprehensive-system-for-tracking-recipes-ingredients-and-costs-update-4bj3</link>
      <guid>https://dev.to/siwhelan/stockcontrol-a-comprehensive-system-for-tracking-recipes-ingredients-and-costs-update-4bj3</guid>
      <description>&lt;p&gt;I had planned to get an earlier update out to this, but I was having too much fun building it!&lt;/p&gt;

&lt;p&gt;An update to my &lt;a href="https://dev.to/siwhelan/stockcontrol-a-comprehensive-system-for-tracking-inventory-recipes-ingredients-and-costs-5hc"&gt;Stock Control&lt;/a&gt; web app being built with Python, Django and Bootstrap!&lt;/p&gt;

&lt;p&gt;For those who may be unfamiliar with the technologies I'm using, here are some brief introductions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Django?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.djangoproject.com/" rel="noopener noreferrer"&gt;Django&lt;/a&gt; is a high-level Python web framework that is widely used for building web applications. It provides a lot of built-in features, such as a powerful ORM, a templating engine, and a robust authentication system, that make it easy to build complex web applications. Django is also known for its scalability, security, and ease of use, making it a popular choice among developers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Bootstrap?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://getbootstrap.com/" rel="noopener noreferrer"&gt;Bootstrap&lt;/a&gt; is a popular front-end framework for building responsive web applications, and provides a set of pre-designed HTML, CSS, and JavaScript components that can be easily integrated into any web project. With Bootstrap, developers can create professional-looking and user-friendly web interfaces without having to write custom code from scratch. Some of the features of Bootstrap include a grid system for laying out web pages, typography styles, forms, buttons, navigation bars, and modal dialogs. Bootstrap also includes support for CSS preprocessors like Sass and Less, and is regularly updated to keep up with modern web development trends.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is MongoDB?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.mongodb.com/" rel="noopener noreferrer"&gt;MongoDB&lt;/a&gt; is a document-oriented NoSQL database that stores data in a JSON-like format called BSON. Unlike traditional SQL databases, MongoDB doesn't use tables and rows. Instead, it stores data as documents that can contain nested structures and arrays. MongoDB is highly scalable, flexible, and can handle large amounts of unstructured data, making it a great choice for building modern web applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why use Django with MongoDB?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While Django is traditionally used with SQL databases, it's also possible to use it with NoSQL databases like MongoDB. There are several benefits to using Django with MongoDB, such as:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Flexibility:&lt;/em&gt; MongoDB's flexible data model allows you to store and retrieve data in a way that suits your application's needs. This makes it easier to work with complex data structures and adapt to changing requirements.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Scalability:&lt;/em&gt; MongoDB is designed to scale horizontally, which means you can add more servers to handle a growing amount of data. This makes it a great choice for building large-scale web applications.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Performance:&lt;/em&gt; MongoDB's native query language, as well as its ability to store data in memory, can result in faster query times compared to traditional SQL databases.&lt;/p&gt;

&lt;p&gt;Also....I just enjoy using it!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Web App&lt;/strong&gt;🥕🥫🦐 - &lt;/p&gt;

&lt;p&gt;Really pleased with how this is coming together, it's been a fantastic dive into Bootstrap and HTML and the methods of bringing backend Python code into a fully functional interface. &lt;/p&gt;

&lt;p&gt;So far the application has several key features, including:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;View all ingredients&lt;/strong&gt;: This feature allows the user to view all of the ingredients currently stored in the database. Each ingredient is displayed with its name, pack size, and price per pack. The user can also edit or delete ingredients as needed, although these are sat in the backend waiting to be implemented! I will also be adding an 'amount' field to track how much of each ingredient the kitchen should have in stock. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add new ingredient&lt;/strong&gt;: This feature allows the user to add a new ingredient to the database. The user can enter the name, pack size, and price per pack of the new ingredient.&lt;/p&gt;

&lt;p&gt;An example of how the ingredients are currently stored in the database -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
{
  "_id": {
    "$oid": "63de7fb1ddb55617db8abac2"
  },
  "product_code": "0003",
  "ingredient": "flour",
  "price_per_pack": 2.99,
  "pack_size": 1000
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;View recipes&lt;/strong&gt;: This feature lets the user explore all the recipes that are currently saved in the database, complete with their names, ingredients, and codes (these codes are set automatically upon entry by checking the last entry and incrementing it by 1). I have plans to add more options for adding, editing, and deleting recipes soon, and I've already got the backend code set up - I just need to put it into action!&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;add_recipe()&lt;/code&gt; function is particularly useful, as it prompts the user to input each ingredient for a recipe and cross-references it with the ingredients database. If an ingredient is not yet listed in the database, the function will call &lt;code&gt;add_new_ingredient()&lt;/code&gt; and ask the user for it's details, and then add the new ingredient to both the recipe and ingredients databases at the same time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recipe cost calculation&lt;/strong&gt;: The application automatically calculates the cost of each recipe based on the cost of the ingredients used. This helps the user make informed decisions about pricing and profitability. One function that is waiting in the wings is the updating of ingredients to allow accurate pricing as supplier costs fluctuate, this then automatically updates the total cost in the recipe database as well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stocktake&lt;/strong&gt;: Yet to be implemented in the backend, this feature will enable users to conduct physical stock inventory counts and reflect the changes in the database. To do this, users will need to input the currently held quantity for each ingredient, and the application will automatically update the database with the new values. Additionally, a sales entry feature is planned to be added later on. This feature will factor in the current stock on hand with the sales data to calculate the gross profit margins for the kitchen.&lt;/p&gt;

&lt;p&gt;I will also be including a wastage input that will both keep a price tally of any ingredients/portions of recipes that are wasted, and also remove the relevant amount of any wasted product from the amount recorded in the ingredients database. This will allow for a much more accurate GP figure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Screenshots?....Screenshots.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Apologies for the quality on these - if anyone has any tips for uploading higher def images on dev.to I'm all ears!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpn2ywi41q7gvtnsxwfkz.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpn2ywi41q7gvtnsxwfkz.PNG" alt="View All Ingredients"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsijl7oamz7pa7zo02pzt.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsijl7oamz7pa7zo02pzt.PNG" alt="Stock Entry"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wh93p2telxd0vnsrufd.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wh93p2telxd0vnsrufd.PNG" alt="Recipe Cards"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fex8oum7k3tfxo2vzdc7g.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fex8oum7k3tfxo2vzdc7g.PNG" alt="Add Ingredient"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Direct links - &lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pn2ywi41q7gvtnsxwfkz.PNG" rel="noopener noreferrer"&gt;Pic1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sijl7oamz7pa7zo02pzt.PNG" rel="noopener noreferrer"&gt;Pic2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5wh93p2telxd0vnsrufd.PNG" rel="noopener noreferrer"&gt;Pic3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ex8oum7k3tfxo2vzdc7g.PNG" rel="noopener noreferrer"&gt;Pic4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, forgive the basic recipes - I'll be making these a little more interesting over time!&lt;/p&gt;

&lt;p&gt;Still lots to do, but it's flying together so it's all good!&lt;/p&gt;

&lt;p&gt;The full code is available &lt;a href="https://github.com/siwhelan/stockControl" rel="noopener noreferrer"&gt;here&lt;/a&gt; for anyone interested, and I'll have another update in a week or so for anyone who's following along.&lt;/p&gt;

&lt;p&gt;Any questions, feel free to reach out. &lt;/p&gt;

&lt;p&gt;Thanks for reading! 👋 &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>python</category>
      <category>programming</category>
      <category>django</category>
    </item>
    <item>
      <title>stockControl 🥕🥫🦐 - A comprehensive system for tracking inventory, recipes, ingredients and costs</title>
      <dc:creator>Simon Whelan</dc:creator>
      <pubDate>Tue, 07 Feb 2023 20:44:38 +0000</pubDate>
      <link>https://dev.to/siwhelan/stockcontrol-a-comprehensive-system-for-tracking-inventory-recipes-ingredients-and-costs-5hc</link>
      <guid>https://dev.to/siwhelan/stockcontrol-a-comprehensive-system-for-tracking-inventory-recipes-ingredients-and-costs-5hc</guid>
      <description>&lt;p&gt;Many people who change careers after a considerable amount of time say that it was a difficult decision. For me, it was one of the easiest I've ever made. My journey started as a 15-year-old dishwasher and culminated in managing every aspect of the kitchen operations of a small chain of restaurants with over £6 million in annual revenue and a staff of 80+ across multiple locations in three countries. However, with the industry going through extremely difficult times over the last 5-10 years, I ultimately realised that I was ready for a change.&lt;/p&gt;

&lt;p&gt;Now that I'm on the cusp of this career change, actively seeking that first job as a software engineer, I am at the point of building several portfolio projects to both hone and practice my coding skills and have something concrete and visible to back up my experience.  &lt;/p&gt;

&lt;p&gt;After building a &lt;a href="https://github.com/siwhelan/taskManagerGUI" rel="noopener noreferrer"&gt;Task Manager&lt;/a&gt;, and a &lt;a href="https://siwhelan.dev/" rel="noopener noreferrer"&gt;personal website&lt;/a&gt; I was looking for something more complex. I decided to create something that has been vital in almost every role I've had up until this point - a Stock Control web app. &lt;/p&gt;

&lt;p&gt;Stock control is a crucial aspect of running a commercial kitchen. It involves tracking and managing the inventory of food and supplies to ensure efficient operation and profitability:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost savings&lt;/strong&gt;: Proper stock control helps to minimize waste and reduce the cost of overstocking or understocking. By keeping track of inventory levels, chefs can order only what they need and avoid waste due to spoilage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improved food quality&lt;/strong&gt;: Stock control helps to ensure that the kitchen always has the ingredients it needs to prepare dishes to the desired standard. This helps to maintain consistent food quality and customer satisfaction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Increased efficiency&lt;/strong&gt;: Stock control helps to streamline kitchen operations by reducing the time spent searching for ingredients and supplies and allowing kitchen staff to focus on preparation and cooking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better food safety&lt;/strong&gt;: Stock control helps to prevent food waste, which can pose a risk to food safety. By ensuring that food is used before it goes bad, kitchens can maintain a clean and safe environment for their customers and staff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better decision-making&lt;/strong&gt;: Stock control provides valuable information that can be used to make informed business decisions, such as adjusting menu offerings or adjusting ordering schedules to match demand.&lt;/p&gt;

&lt;p&gt;I've used several existing solutions to this; however, it was rare to find one that had everything that was needed. Many had functionality missing or relied on secondary programs rather than being contained into one box. For a working stock control system, you primarily need - &lt;/p&gt;

&lt;p&gt;📊 An ingredients database&lt;br&gt;
🍲 A recipes database&lt;br&gt;
💰 Prices of all ingredients (which need to be updated regularly)&lt;br&gt;
💳 Total price of recipe (to determine the meal's selling price)&lt;br&gt;
💹 A financial record of daily sales&lt;br&gt;
🍔 A record of which food items have been sold&lt;br&gt;
🗑️ A wastage entry&lt;br&gt;
📦 A method of placing supplier orders and/or entering stock received from supplier&lt;/p&gt;

&lt;p&gt;And a program to allow user to enter currently held stock (usually weekly/monthly). This will take all the above info, work out what stock you &lt;em&gt;should&lt;/em&gt; have based on your ordering and sales, and then give you a gross profit percentage. &lt;/p&gt;

&lt;p&gt;Adventurous? Absolutely, but with a deep understanding of the required functionality, I'm finding the 'sketching' of the program is going well. I have already started the backend of the system, but it is currently very much in its infancy. Taking on advice from several experienced developers and recruiters, this one is being built very much in the open!&lt;/p&gt;

&lt;p&gt;The GitHub repo is &lt;a href="https://github.com/siwhelan/stockControl" rel="noopener noreferrer"&gt;here&lt;/a&gt;. The plan is to spend a couple of hours on it each day and post a weekly update here on dev.to. This not only helps me to recap all the pieces I've added, but also allows anyone that wishes to follow along to do so 😊&lt;/p&gt;

&lt;p&gt;My transition from managing kitchen operations to becoming a software engineer has been an exciting and challenging journey. I have decided to build this Stock Control web app to showcase my coding skills and demonstrate my expertise in this area. This app will bring together all the essential elements required for efficient stock management in a commercial kitchen, including ingredients and recipes databases, financial record-keeping, supplier ordering, and more. &lt;/p&gt;

&lt;p&gt;As always, feedback is more than welcome. I am confident that this project will help me reach my goal of becoming a successful software engineer and I look forward to sharing the finished product with the world.&lt;/p&gt;

&lt;p&gt;Thanks for reading! 👋&lt;/p&gt;

</description>
      <category>saas</category>
      <category>softwaredevelopment</category>
      <category>architecture</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Task Manager 2.0 📝📤📆 - A feature-rich Task Management app built with Python &amp; customTkinter</title>
      <dc:creator>Simon Whelan</dc:creator>
      <pubDate>Sun, 22 Jan 2023 21:01:40 +0000</pubDate>
      <link>https://dev.to/siwhelan/task-manager-20-a-feature-rich-task-management-app-built-with-python-customtkinter-1c83</link>
      <guid>https://dev.to/siwhelan/task-manager-20-a-feature-rich-task-management-app-built-with-python-customtkinter-1c83</guid>
      <description>&lt;p&gt;Introducing Task Manager: A Fun and User-Friendly Way to Organize Your Tasks! 📝📤📆&lt;/p&gt;

&lt;p&gt;An update to my previous &lt;a href="https://dev.to/siwhelan/task-manager-47jh"&gt;article&lt;/a&gt; on a Task Management app being built with Python and customTkinter, with several new features implemented and code refactored to improve efficiency. This is a personal project that I took on as a learning and portfolio exercise after completing a Software Engineering Bootcamp with &lt;a href="https://www.hyperiondev.com/" rel="noopener noreferrer"&gt;HyperionDev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The program is based on a task manager that I created as a capstone project during the bootcamp, but I wanted to take it to the next level by adding a GUI to make the backend more user-friendly. The original program can be found &lt;a href="https://github.com/siwhelan/taskManager" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Changelog -
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Implemented Add/View Tasks&lt;/li&gt;
&lt;li&gt;Implemented a search feature to search tasks assigned to a particular person or task ID&lt;/li&gt;
&lt;li&gt;Created 'Edit Tasks' to change task details, such as the user assigned, due dates, and mark tasks as completed&lt;/li&gt;
&lt;li&gt;Addition of default admin account added upon first installation&lt;/li&gt;
&lt;li&gt;Addition of error handling and success frames for displaying relevant messages to the user&lt;/li&gt;
&lt;li&gt;Several bug fixes&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Enough intro - let's get to the app! 🚀
&lt;/h4&gt;

&lt;p&gt;As developers, we all know the importance of keeping our tasks organized and on track. But, let's be real - sometimes the task of managing our tasks can feel like a tedious task in itself. That's why I'm excited to introduce Task Manager 2.0 - an updated task management program that makes organization simple and user-friendly.&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%2Fycz16hqjvhakem9l25xg.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%2Fycz16hqjvhakem9l25xg.JPG" alt="main window of task manager app" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with the &lt;a href="https://github.com/TomSchimansky/CustomTkinter" rel="noopener noreferrer"&gt;customtkinter&lt;/a&gt; library, Task Manager features a sleek and modern graphical user interface. But, it's not just about looks - the program also includes practical features such as a dark mode, UI scaling, and the ability to view, add, edit, and delete tasks with ease. Plus, the program includes a login system that utilizes a MongoDB database to store and verify user credentials, ensuring the security of your task and user information.&lt;/p&gt;

&lt;p&gt;The program employs &lt;code&gt;pymongo&lt;/code&gt; to connect to the MongoDB database storing information on tasks and user login information, and passwords are hashed using &lt;a href="https://www.n-able.com/blog/sha-256-encryption" rel="noopener noreferrer"&gt;SHA-256&lt;/a&gt; for added security 🔑. This makes Task Manager a great example of a complete software project that includes a GUI, database, and security features, all wrapped up in a user-friendly package.&lt;/p&gt;

&lt;p&gt;When a user logs in, their inputted password is hashed and compared against the stored password in the database. When registering new users, the password is hashed before the user is added to the database, and all password input fields show '*' when typing, so there are no cleartext/visible passwords at any point.&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%2Fhedn1zq60wx7f9qsjdmm.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%2Fhedn1zq60wx7f9qsjdmm.png" alt="code to hash password" width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Want to try it? Feel free!
&lt;/h4&gt;

&lt;p&gt;Installation is a simple affair - install the necessary dependencies and libraries, set up a MongoDB server, and voila! You're ready to start managing your tasks like a pro. Full instructions for all of these can be found in the Github repo - &lt;a href="https://github.com/siwhelan/taskManagerGUI" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before running the app, make sure your MongoDB server is up and running. On the first use, a default admin account with the username "admin" and password "adm1n" will be automatically created and added to the "login_info" collection within the MongoDB instance. This account can be used to log in and manage tasks.&lt;/p&gt;

&lt;p&gt;One of the first things that you'll notice when you open the application is the ability to change the appearance mode and theme. The appearance mode can be set to "System" (standard), "Dark", or "Light" and the theme can be set to "blue" (standard), "green", or "dark-blue". This allows you to customize the look and feel of the application to suit your preferences.&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%2Fpzulpq7u96vbjgsig86m.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%2Fpzulpq7u96vbjgsig86m.JPG" alt="Image of task menu dropdown" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the last blog, a dropdown menu has been added to create a Task Menu. This consists of 'View All', 'Add Task', 'Edit Task' and 'Delete Task'. All self explanatory, but we'll go through them briefly anyway!&lt;/p&gt;

&lt;h4&gt;
  
  
  Add Task -
&lt;/h4&gt;

&lt;p&gt;A window with inputs for username, task title, task description and due date. These are then saved as a new entry in the MongoDB collection 'tasks' along with the default entry of 'date assigned' which is set using the &lt;code&gt;datetime&lt;/code&gt; module in Python and the &lt;code&gt;datetime.now()&lt;/code&gt; function as well as a 'completed' field that is defaulted to 'No'.&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%2Fy9k4xqob0xhp02jcmoy3.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%2Fy9k4xqob0xhp02jcmoy3.JPG" alt="add task window" width="752" height="796"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Task due dates are stored as datetime objects in the database to allow for easier reporting and usage throughout the program.&lt;/p&gt;

&lt;h4&gt;
  
  
  Edit Task -
&lt;/h4&gt;

&lt;p&gt;'Edit task' first requires a Task ID number, upon selection the &lt;code&gt;view_all()&lt;/code&gt; function is called which inserts all task data into the main textbox for reference. The relevant task number must be entered into the correct field along with any info that is to be edited. Blank fields are skipped and any new info is added to the database in place of the current task info.&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%2Fu5epx675wrx9wd6jm7j9.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%2Fu5epx675wrx9wd6jm7j9.JPG" alt="edit task window" width="752" height="796"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once a task has been completed, the Edit Task window can be called and a checkmark within the window ticked, this then changes the default &lt;code&gt;completed: 'No’&lt;/code&gt; to &lt;code&gt;completed: 'Yes’&lt;/code&gt;. &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%2F91bvzemu1tljbtmboi2r.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%2F91bvzemu1tljbtmboi2r.JPG" alt="completed task" width="752" height="796"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This can be confirmed by calling the View All function once again, and displaying the updated tasks.&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%2Fxta3xtb183xkvuszvgx2.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%2Fxta3xtb183xkvuszvgx2.JPG" alt="task marked as complete" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Delete Task -
&lt;/h4&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%2F5z7bszcfakgtf27m6pcc.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%2F5z7bszcfakgtf27m6pcc.JPG" alt="delete task input field" width="512" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A much simpler input field that asks for the relevant task number. Upon entering, the task with the inputted Task ID is removed from the database. &lt;/p&gt;

&lt;p&gt;A 'Task does not exist' error is shown if the Task ID is not found.&lt;/p&gt;

&lt;h4&gt;
  
  
  Search -
&lt;/h4&gt;

&lt;p&gt;The task manager app has a built-in search feature that makes it quick and easy for users to find specific tasks. This feature can be accessed by typing in a search query into the search bar located at the bottom of the main window and then clicking the 'search' button. The search function is user-friendly and allows you to search for tasks based on different criteria like the user assigned to the task and the Task ID. This makes it easy to filter and locate the tasks you need, or keep track of a specific user's progress.&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%2Fssk9i75fi0p7v1nblx2u.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%2Fssk9i75fi0p7v1nblx2u.JPG" alt="search bar" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Reports -
&lt;/h4&gt;

&lt;p&gt;One of the most exciting features of Task Manager 2.0 is the ability to generate reports on tasks 📈 and the option menu to switch between different task views and functions, this allows you to have a better understanding of your progress and to make data-driven decisions. &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%2Fhk87ozofqg5qsbrx0m8n.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%2Fhk87ozofqg5qsbrx0m8n.JPG" alt="task reports" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition to being a great tool for individual developers, Task Manager can also be used by small businesses to manage tasks assigned to team members. The program's user-friendly interface and ability to view, add, edit, and delete tasks makes it easy for team leaders to assign and monitor tasks, while the login system and MongoDB database ensures that each team member's tasks are secure and easy to access.&lt;/p&gt;

&lt;p&gt;The ability to generate reports on tasks and switch between different task views and functions 📊 also allows managers to have a better understanding of the team's progress and make data-driven decisions. This can be especially useful for small businesses with limited resources and a need to optimize their workflow and performance.&lt;/p&gt;

&lt;p&gt;Overall, Task Manager is not only a great tool for individual developers but also for small businesses looking for an easy-to-use and secure way to manage tasks for their team members.&lt;/p&gt;

&lt;p&gt;Give it a try and see for yourself! 🚀&lt;/p&gt;

&lt;p&gt;P.S. Don't forget to check out the Github &lt;a href="https://github.com/siwhelan/taskManagerGUI/blob/master/README.md" rel="noopener noreferrer"&gt;README&lt;/a&gt; for more details on installation and usage.&lt;/p&gt;

&lt;p&gt;I've added several checks/if statements and error handling in places I thought it would be useful, but I'm sure there'll still be a few bugs! Let me know if you spot any and I'll get them fixed asap!&lt;/p&gt;

&lt;p&gt;Any other feedback or questions, or if you have any feature suggestions or requests, feel free to leave a comment!&lt;/p&gt;

&lt;p&gt;Thanks for reading 👋&lt;/p&gt;

</description>
    </item>
    <item>
      <title>dontClickTheMail🚫📰🗞️: Clean Up Your Reddit Feed - Scrape Daily Mail articles to read without clicking the link!</title>
      <dc:creator>Simon Whelan</dc:creator>
      <pubDate>Mon, 16 Jan 2023 18:38:08 +0000</pubDate>
      <link>https://dev.to/siwhelan/dontclickthemail-clean-up-your-reddit-feed-scrape-daily-mail-articles-to-read-without-clicking-the-link-4136</link>
      <guid>https://dev.to/siwhelan/dontclickthemail-clean-up-your-reddit-feed-scrape-daily-mail-articles-to-read-without-clicking-the-link-4136</guid>
      <description>&lt;p&gt;Are you tired of seeing Daily Mail links polluting your Reddit feeds? Look no further! With &lt;a href="https://github.com/siwhelan/dontClickTheMail" rel="noopener noreferrer"&gt;dontClickTheMail&lt;/a&gt;, you can easily filter out Daily Mail articles and still be able to read their content without clicking the link to the website!&lt;/p&gt;

&lt;p&gt;This Python script uses the popular &lt;a href="https://praw.readthedocs.io/en/stable/" rel="noopener noreferrer"&gt;PRAW&lt;/a&gt; library, which allows access to the Reddit API, as well as BeautifulSoup and requests to scrape the content of Daily Mail articles. Once the script finds a Daily Mail article in a specified subreddit, it will scrape the article's content and post it as a comment, so you and other users can read the article without giving the Daily Mail any clicks or ad revenue.&lt;/p&gt;

&lt;p&gt;To get started, you'll need to have a Reddit account and a Reddit API client ID and client secret. You can obtain these by following the instructions &lt;a href="https://praw.readthedocs.io/en/stable/getting_started/quick_start.html#" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Once you have your client ID and secret, you'll need to create a &lt;code&gt;praw.ini&lt;/code&gt; file in your working directory. This file should contain the configuration details that PRAW uses to connect to the Reddit API, such as your client ID and client secret. It's recommended to use a praw.ini file to store these sensitive details as it's more secure and easier to manage. In addition to this, you should also replace "yourusernamehere" with your Reddit username in the script.&lt;/p&gt;

&lt;p&gt;Your praw.ini should look something like this:&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%2Ftxtatn4syca09eabagjs.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%2Ftxtatn4syca09eabagjs.png" alt="example of praw.ini file" width="800" height="263"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The script also requires the following Python modules:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pypi.org/project/beautifulsoup4/" rel="noopener noreferrer"&gt;BeautifulSoup&lt;/a&gt;&lt;br&gt;
&lt;a href="https://pypi.org/project/requests/" rel="noopener noreferrer"&gt;requests&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can install these dependencies by running the following command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install praw bs4 requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once you have everything set up, you can run the script and specify which subreddits you'd like to scan for Daily Mail articles. &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%2Fsrbqrs092n4h79798oku.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%2Fsrbqrs092n4h79798oku.png" alt="code to choose subreddits" width="800" height="227"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The script will scan the 100 newest posts in the specified subreddits, and post the article's content as a comment if it finds a Daily Mail article that the bot hasn't already commented on.&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%2Fhz1wp67i79s23611iwt1.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%2Fhz1wp67i79s23611iwt1.png" alt="code for post limit" width="800" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Screenshots! &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%2Fxyj4jt7jpgl66qum71mr.jpeg" 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%2Fxyj4jt7jpgl66qum71mr.jpeg" alt="screenshot1" width="640" height="570"&gt;&lt;/a&gt;&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%2Fqlwq4dp7db48hsmkwebf.jpeg" 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%2Fqlwq4dp7db48hsmkwebf.jpeg" alt="screenshot2" width="640" height="844"&gt;&lt;/a&gt;&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%2Fsnzkmbuv0kzmuj9619os.jpeg" 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%2Fsnzkmbuv0kzmuj9619os.jpeg" alt="screenshot3" width="607" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, if you're tired of seeing Daily Mail articles in your Reddit feeds, give the "dontClickTheMail" script a try and enjoy a cleaner and more personalized browsing experience!&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Please note that this script is for educational purposes only and should be used responsibly and in accordance with &lt;a href="https://www.reddit.com/wiki/api/" rel="noopener noreferrer"&gt;Reddit's API rules&lt;/a&gt;. Do not use it to spam Reddit, or to post comments that violate Reddit's terms of service. The author of this program is not responsible for any consequences that may result from the use of this program.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>chatgpt</category>
    </item>
    <item>
      <title>Boost your Python development in VS Code: Top picks for extensions 💻🚀🛠️</title>
      <dc:creator>Simon Whelan</dc:creator>
      <pubDate>Sun, 15 Jan 2023 11:13:53 +0000</pubDate>
      <link>https://dev.to/siwhelan/boost-your-python-development-in-vs-code-top-picks-for-extensions-2jkh</link>
      <guid>https://dev.to/siwhelan/boost-your-python-development-in-vs-code-top-picks-for-extensions-2jkh</guid>
      <description>&lt;p&gt;As a software developer, I know the importance of having clear, readable, and consistent code. However, with so many opinions and personal coding styles out there, this can be difficult to get right. Luckily, in Python, we have an agreed-upon format known as &lt;a href="https://peps.python.org/pep-0008/" rel="noopener noreferrer"&gt;PEP 8&lt;/a&gt; to adhere to when writing our code. PEP 8 is a set of guidelines for writing Python code that was created by the Python community to improve the readability and maintainability of Python code. &lt;/p&gt;

&lt;p&gt;In this post, we'll be discussing some of my favorite VS Code extensions, many of which can help you adhere to PEP 8 guidelines with ease and boost your Python development. We'll be discussing extensions such as Prettier, Black, flake8, Monokai Pro, SnapCode and GitHub. These are the tools that I've personally found to be the most useful and effective in my daily work. I'll be providing a brief overview of each extension, as well as specific examples of how they can be used in a real-world scenario, based on my own experience. So, whether you're a seasoned Python developer or just starting out, I hope you'll find this post helpful in your own development journey. Let's get started!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prettier&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://prettier.io/" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt; is a code formatter that can automatically format your code to a consistent style. It supports a wide range of languages, including Python, and can be easily integrated with VS Code. It can improve your code by making it more readable and consistent, which can make it easier to collaborate with others and understand your own code later on. For example, you can set up Prettier to automatically format your code when you save a file by adding the following to your VS Code settings: "editor.formatOnSave": true.&lt;/p&gt;

&lt;p&gt;Prettier has a whole host of customisable settings. Some of the most useful are -&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;printWidth&lt;/em&gt;&lt;/strong&gt;: This setting controls the maximum line length for your code. It can help you keep your code looking clean and organized by wrapping lines that exceed the specified width. This is particularly useful when working with PEP 8, which recommends a line length of 79 characters.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;singleQuote&lt;/em&gt;&lt;/strong&gt;: This setting controls whether single or double quotes are used for strings in your code. In python, single quotes are more commonly used than double quotes, but it can be set based on the user preference.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;trailingComma&lt;/em&gt;&lt;/strong&gt;: This setting controls whether trailing commas should be added to the end of lists, objects, and function arguments in your code. This can help you keep your code organized and improve readability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;tabWidth&lt;/em&gt;&lt;/strong&gt;: This setting controls the number of spaces used for each tab in your code. It can help you keep your code looking clean and consistent by ensuring that all tabs are the same width. PEP 8 dictates that 4 spaces is the correct width.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;parser&lt;/em&gt;&lt;/strong&gt;: This setting allows you to specify the parser that will be used to parse the code. For Python, the options are 'babylon' and 'flow', default is 'babylon' which supports most of the features of Python.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep in mind that there are many other options and configurations available, so it's worth exploring the documentation and experimenting with different settings to find the ones that work best for you and your codebase!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Black&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pypi.org/project/black/" rel="noopener noreferrer"&gt;Black&lt;/a&gt; is another code formatter, but it is specifically designed for Python. This extension can improve your code by making it more consistent and easier to read, which can help you write more maintainable code. For example, you can use the --line-length 79 command to limit the line length to 79 characters. Black is an auto-formatter that is ran in the terminal using the ‘black’ command.&lt;/p&gt;

&lt;p&gt;You can install Black via the link above or with &lt;/p&gt;

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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;flake8&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Linting is the process of automatically checking the source code for potential errors, bugs, and other issues, including adherence to specific style guides like PEP 8. One popular and well-respected tool for this is &lt;a href="https://pypi.org/project/flake8/" rel="noopener noreferrer"&gt;Flake8&lt;/a&gt;, which can be integrated with VS Code. It supports PEP 8 and can improve your code by catching errors and style issues early on, helping you avoid bugs and write more readable code. To use it, you can configure Flake8 to run automatically every time you save a file by adding "python.linting.flake8Enabled": true to your VS Code settings.&lt;/p&gt;

&lt;p&gt;It's worth mentioning that flake8 doesn't have an automatic formatting feature, it only checks the code and returns errors, warnings or info for you to alter yourself.&lt;/p&gt;

&lt;p&gt;flake8 can be installed with &lt;/p&gt;

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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Monokai Pro&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://monokai.pro/" rel="noopener noreferrer"&gt;Monokai Pro&lt;/a&gt; is a color scheme for VS Code that is designed to be easy on the eyes and make it easier to read your code. It can improve your code by making it more readable, which can help you understand your code better and make fewer mistakes. You can set it as your default color scheme by going to VS Code settings and searching for "color scheme" and selecting Monokai Pro. This is a paid theme, however there is also a fully functional, never-ending 'trial' with an occaisonal pop-up asking you to buy.&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%2Frw9e1i5iqpvnnypkie6q.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%2Frw9e1i5iqpvnnypkie6q.jpg" alt="Monokai Pro stock image" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CodeSnap&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=adpyke.codesnap" rel="noopener noreferrer"&gt;CodeSnap&lt;/a&gt; is a VS Code extension that allows you to take screenshots of your code and share them with your friends and colleagues. It's a great way to show off your work and get feedback on your code. CodeSnap offers a variety of color options for the background of your screenshots, including the ability to add a custom colour. Furthermore, CodeSnap also allows you to add a custom watermark, such as a logo or copyright notice, to your screenshots for branding or credit purposes. &lt;/p&gt;

&lt;p&gt;In addition to the custom color and watermark options, CodeSnap also has a feature that allows you to resize your screenshots. This means that you can easily adjust the size of your screenshots to fit your needs. This can be particularly useful when sharing your screenshots on social media or other platforms where image size may be a consideration.&lt;/p&gt;

&lt;p&gt;CodeSnap also allows you to use a transparent background, which can help to create a more polished and professional look. With these additional features, CodeSnap is a great tool for enhancing the visual appeal and professionalism of your screenshots.&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%2F28glkbvo2t0hvpticxok.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%2F28glkbvo2t0hvpticxok.png" alt="snapcode image - blue background" width="800" height="297"&gt;&lt;/a&gt;&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%2Fxbixv0s8ndutivqlppxy.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%2Fxbixv0s8ndutivqlppxy.png" alt="snapcode image" width="800" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://code.visualstudio.com/docs/sourcecontrol/github" rel="noopener noreferrer"&gt;GitHub Extension&lt;/a&gt; for VSCode is a powerful tool that can help you streamline your workflow and improve your collaboration with other developers. Some of the key features of this extension include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Direct access to the GitHub website, allowing you to view and manage your repositories, issues, and pull requests directly from within VS Code.&lt;/li&gt;
&lt;li&gt;The ability to create, review, and merge pull requests directly from VS Code, eliminating the need to switch between different tools.&lt;/li&gt;
&lt;li&gt;Integrated support for Git, allowing you to perform common Git tasks such as committing, pushing, and pulling directly from within VS Code.&lt;/li&gt;
&lt;li&gt;The ability to set up Black and Prettier to automatically format your code when you push to GitHub, ensuring that your code adheres to PEP 8 guidelines at all times and reducing the risk of introducing style issues into your codebase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The GitHub Extension for VSCode is a must-have for any developer who is working with GitHub, whether you're a solo developer or part of a team. With this extension, you can easily manage your repositories, collaborate with other developers, and ensure that your code is always well-formatted and easy to read.&lt;/p&gt;

&lt;p&gt;In conclusion, as a developer, it's essential to have tools that can help you write clear, readable, and consistent code. The VS Code extensions discussed in this post are all great choices for Python developers. They can help you adhere to PEP 8 guidelines, improve your code, and make your development process more efficient and enjoyable. With so many options and configurations available, it's worth experimenting with different settings to find the ones that work best for you and your codebase. Remember, there are many other extensions available for VS Code, so don't be afraid to explore and see what else is out there. &lt;/p&gt;

&lt;p&gt;Feel free to add any extensions you enjoy using in the comments!&lt;/p&gt;

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

</description>
      <category>mongodb</category>
      <category>database</category>
      <category>backend</category>
      <category>programming</category>
    </item>
    <item>
      <title>Task Manager 📝📤📆</title>
      <dc:creator>Simon Whelan</dc:creator>
      <pubDate>Sun, 08 Jan 2023 11:19:58 +0000</pubDate>
      <link>https://dev.to/siwhelan/task-manager-47jh</link>
      <guid>https://dev.to/siwhelan/task-manager-47jh</guid>
      <description>&lt;p&gt;I've been working on a personal project since finishing a Software Engineering Bootcamp with HyperionDev. The program is based on a task manager that I built as a capstone project in the bootcamp, but I wanted to take it a step further and build a GUI that would make the backend more user-friendly.&lt;/p&gt;

&lt;p&gt;Initially, the program was built around reading and writing text files that contained user login information, tasks assigned to users, and reports on the status of those tasks (you can find the program &lt;a href="https://github.com/siwhelan/taskManager" rel="noopener noreferrer"&gt;here&lt;/a&gt;). While these text files were fine for a small practice project, they are unrealistic for real-world applications (especially for storing user passwords). I've since replaced the text files with a &lt;a href="https://www.mongodb.com/" rel="noopener noreferrer"&gt;MongoDB &lt;/a&gt;database.&lt;/p&gt;

&lt;p&gt;I'm building the GUI using &lt;a href="https://github.com/TomSchimansky/CustomTkinter" rel="noopener noreferrer"&gt;CustomTkinter&lt;/a&gt;, which is a custom version of &lt;a href="https://docs.python.org/3/library/tkinter.html" rel="noopener noreferrer"&gt;tkinter&lt;/a&gt; that offers a more modern look for the app, as well as additional features like the ability to set the appearance mode and default color theme.&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%2Farcdus3bj3i9vxmpe0ir.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%2Farcdus3bj3i9vxmpe0ir.JPG" alt="main window light mode" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm encrypting user login passwords using SHA-256 and storing them in the database. This isn't a perfect solution, but using something like bcrypt seemed like overkill for a project like this. When a user logs in, their inputted password is hashed and compared against the stored password in the database.&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%2Fb1ktqhukko51hvonqt8o.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%2Fb1ktqhukko51hvonqt8o.JPG" alt="login window" width="606" height="649"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When registering new users, the password is hashed before the user is added to the database, and all password input fields show '*' when typing, so there are no cleartext/visible passwords at any point.&lt;/p&gt;

&lt;p&gt;Upon successful login, the main window is opened, presenting the user with several options such as 'Add User', 'Add/View Tasks', 'Generate Reports', and a search field.&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%2Fu45bccl94v28aduexakw.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%2Fu45bccl94v28aduexakw.JPG" alt="main_window" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the 'Add User' button is clicked, the script gets the values entered in the username and password entry widgets and checks if they are empty strings. If either of them are empty, it displays a window with a message saying 'Incorrect info, please try again'. If both the username and password are non-empty, the script hashes the password using SHA-256 and stores the username and hashed password in a MongoDB database. It then displays a message saying 'User added successfully' and closes the window.&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%2F6wt3qnnw2g7xet2euecy.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%2F6wt3qnnw2g7xet2euecy.JPG" alt="add_user window" width="604" height="654"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The 'Generate Reports' button performs the following:&lt;/p&gt;

&lt;p&gt;-&amp;gt; Connects to the MongoDB server using the MongoClient function.&lt;br&gt;
   -&amp;gt; Retrieves the 'login_info' collection from the 'task_manager' database and counts the number of unique usernames in the collection.&lt;br&gt;
   -&amp;gt; Retrieves the 'tasks' collection from the 'task_manager' database and counts the number of tasks in the collection.&lt;br&gt;
   -&amp;gt; Counts the number of tasks that are completed and not completed.&lt;br&gt;
   -&amp;gt; Finds the tasks that are overdue and not completed by searching for tasks with a due date earlier than the current date and that are not completed.&lt;br&gt;
   -&amp;gt; Calculates the percentage of tasks that are incomplete and overdue.&lt;br&gt;
   -&amp;gt; Outputs the report to a textbox widget in the main window.&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%2Ftbhb2wjn5nw6uflv1853.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%2Ftbhb2wjn5nw6uflv1853.JPG" alt="reports" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a work in progress, and there is still more to do:&lt;/p&gt;

&lt;p&gt;-&amp;gt; Implement Add/View Tasks&lt;br&gt;
   -&amp;gt; Implement a search feature to search tasks assigned to a particular person and/or by date/completed status&lt;br&gt;
   -&amp;gt; Create 'Edit Tasks' to change the user assigned, due dates, and mark tasks as completed&lt;br&gt;
   -&amp;gt; Increase error handling&lt;br&gt;
   -&amp;gt; Tidy up/refactor/streamline code (it's currently a mess back here!)&lt;/p&gt;

&lt;p&gt;Optional:&lt;/p&gt;

&lt;p&gt;-&amp;gt; Increase security with bcrypt password protection&lt;br&gt;
   -&amp;gt; Add specific password conditions, i.e. length, including numbers/characters etc&lt;/p&gt;

&lt;p&gt;I'll post the full project on &lt;a href="https://github.com/siwhelan" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; once it's finished, and as always I'm happy to take any feedback.&lt;/p&gt;

&lt;p&gt;Thanks for reading 👋&lt;/p&gt;

</description>
      <category>vibecoding</category>
    </item>
  </channel>
</rss>
