<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Bruce Hillsworth</title>
    <description>The latest articles on Forem by Bruce Hillsworth (@hillswor).</description>
    <link>https://forem.com/hillswor</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%2F1059603%2F8859a792-05dc-4cea-bcaa-2be1bb1e802c.jpeg</url>
      <title>Forem: Bruce Hillsworth</title>
      <link>https://forem.com/hillswor</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/hillswor"/>
    <language>en</language>
    <item>
      <title>My First Full-Stack App</title>
      <dc:creator>Bruce Hillsworth</dc:creator>
      <pubDate>Mon, 12 Jun 2023 22:42:42 +0000</pubDate>
      <link>https://forem.com/hillswor/my-first-full-stack-app-41jo</link>
      <guid>https://forem.com/hillswor/my-first-full-stack-app-41jo</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I'm typically a skeptic when it comes to the fantastical and the mysterious. For years, I dismissed the notion of Sasquatch as ludicrous. Growing up in Colorado, where forests are commonplace, the idea of a giant ape residing in such areas seemed far-fetched. However, my perspective changed in early 2022, when my family and I relocated to the Pacific Northwest and were quickly awed by the forests of the Pacific Northwest.  They are truly a spectacle, with their overwhelming density and scale dwarfing even the expansive wilderness of Colorado. Within a short span of time, we encountered numerous unexplained phenomena within these forests that turned both me and my wife into believers. When I sought out similar experiences online, I found a plethora of resources, although many were poorly organized and outdated.&lt;/p&gt;

&lt;p&gt;Fast forward to 2023, I am nearing the completion of an intensive Full-Stack Web Development bootcamp at the Flatiron School. As part of the program, I've been tasked to develop a full-stack application using Python Flask for the backend, and React for the frontend and after months of working on Facebook, Instagram, and Snapchat clones, why not branch out and have fun with something a little different.  Not lacking in ambition, I started out with the goal of making an app that was better than the outdated and hard to navigate apps that were currently available.  I don't want this to be a summary of all my code and would rather share my journey through creating this app, some of the technologies I used, and what I learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting Point
&lt;/h2&gt;

&lt;p&gt;To reinforce my grasp of the technologies involved, I chose to develop the project solo. In contrast to previous group projects, I wanted to approach this one in a more thoughtful and methodical manner. My natural instinct was to map out my backend and then build my frontend to interact with it.  After careful planning and mapping out how I wanted my app's models to interact and relate, I started by setting up the Flask backend and React frontend in separate directories: "server" and "client."&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%2Fiws2gk8q6f01sc88xlaa.jpg" 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%2Fiws2gk8q6f01sc88xlaa.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the Flask App
&lt;/h2&gt;

&lt;p&gt;Within the "server" directory, I created an &lt;code&gt;app.py&lt;/code&gt; file to configure the Flask application. The &lt;code&gt;create_app()&lt;/code&gt; function played a vital role, where I set up CORS, secret key, SQLite database URI, and Flask extensions such as SQLAlchemy and Flask-Migrate. Additionally, I defined routes and API endpoints using Flask-RESTful.  The example below shows the configuration along with one of the routes that was used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from flask import Flask, request, session, make_response, jsonify
from flask_cors import CORS
from flask_sqlalchemy import SQLAlchemy
from flask_restful import Api, Resource
from flask_bcrypt import check_password_hash, generate_password_hash
from dotenv import load_dotenv
import os
from datetime import datetime

from models import User, Location, Sighting, Comment
from extensions import db, migrate


# Load environment variables from .env
load_dotenv()

# Retrieve the secret key from the environment variable
secret_key = os.getenv("SECRET_KEY")


def create_app():
    app = Flask(__name__)
    CORS(app)
    app.config["SECRET_KEY"] = secret_key
    app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///database.db"
    app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
    app.json.compact = False

    db.init_app(app)
    migrate.init_app(app, db)

    return app


app = create_app()
api = Api(app)


class Login(Resource):
    def post(self):
        data = request.get_json()
        email = data.get("email")
        password = data.get("password")

        user = User.query.filter_by(email=email).first()
        if user and check_password_hash(user.password, password):
            session["user_id"] = user.id
            return make_response(jsonify(user.to_dict()), 200)
        else:
            return make_response({"error": "Invalid username or password"}, 401)


api.add_resource(Login, "/login")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting Up the Models
&lt;/h2&gt;

&lt;p&gt;In the same "server" directory, I defined the database models: User, Location, Sighting, and Comment. These models represented the entities and relationships within the application. I utilized SQLAlchemy's ORM to establish relationships and create the necessary tables in the database. Each model had methods to convert instances to dictionaries, making it easier to work with JSON responses.  I did use BCrypt to hash the passwords for each user. Below is an example of the model imports and one of the models.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import validates
from sqlalchemy.ext.associationproxy import association_proxy
from flask_bcrypt import Bcrypt
import re
from datetime import datetime

from extensions import db

bcrypt = Bcrypt()


class User(db.Model):
    __tablename__ = "users"

    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(length=254), unique=True, nullable=False)
    password = db.Column(db.String(length=254))
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    last_login = db.Column(
        db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow
    )

    sightings = db.relationship("Sighting", backref="user", lazy=True)
    comments = db.relationship("Comment", backref="user", lazy=True)
    locations = association_proxy("sightings", "location")

    @validates("email")
    def validate_email(self, key, email):
        assert (
            re.match(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$", email)
            is not None
        ), "Invalid email address"
        return email

    def set_password(self, password):
        hashed_password = bcrypt.generate_password_hash(password)
        self.password = hashed_password.decode("utf-8")

    def verify_password(self, password):
        return bcrypt.check_password_hash(self.password, password)

    def to_dict(self):
        return {
            "id": self.id,
            "email": self.email,
        }

    def __repr__(self):
        return f"&amp;lt;User email={self.email}&amp;gt;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Migrations and Seeding Data
&lt;/h2&gt;

&lt;p&gt;To manage database schema changes, I used Flask-Migrate, which enabled easy migration creation and execution. I started with an initial migration and then used a &lt;code&gt;seed.py&lt;/code&gt; file to populate the tables with test data. The faker package helped generate realistic data for users, locations, sightings, and comments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from faker import Faker
from random import choice

from models import User, Location, Sighting, Comment
from extensions import db
from app import app

fake = Faker()


def clear_data():
    with app.app_context():
        db.session.query(User).delete()
        db.session.query(Location).delete()
        db.session.query(Sighting).delete()
        db.session.query(Comment).delete()
        db.session.commit()


def seed_db(num_users=10, num_locations=5, num_sightings=20, num_comments=50):
    with app.app_context():
        for _ in range(num_users):
            user = User(
                email=fake.unique.email(),
            )
            user.set_password("abc123")
            db.session.add(user)

        db.session.commit()

        # Get all users
        users = User.query.all()

        # Seed locations
        for _ in range(num_locations):
            location = Location(
                name=fake.city(),
                state=fake.state_abbr(),
                description=fake.text(max_nb_chars=200),
            )
            db.session.add(location)

        db.session.commit()

        # Get all locations
        locations = Location.query.all()

        # Seed sightings
        for _ in range(num_sightings):
            sighting = Sighting(
                user_id=choice(users).id,
                location_id=choice(locations).id,
                sighting_date=fake.date_this_year(),
                sighting_time=fake.time_object(),
                description=fake.text(max_nb_chars=1000),
                created_at=fake.date_time_this_year(),
                updated_at=fake.date_time_this_year(),
            )
            db.session.add(sighting)

        db.session.commit()

        # Get all sightings
        sightings = Sighting.query.all()

        # Seed comments
        for _ in range(num_comments):
            comment = Comment(
                user_id=choice(users).id,
                sighting_id=choice(sightings).id,
                comment_text=fake.text(max_nb_chars=500),
                created_at=fake.date_time_this_year(),
                updated_at=fake.date_time_this_year(),
            )
            db.session.add(comment)

        db.session.commit()


# Call the functions
clear_data()
seed_db()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating the Frontend with Tailwind CSS
&lt;/h2&gt;

&lt;p&gt;To build the React frontend, I utilized Create React App. It quickly set up a functional frontend within the "client" directory. In this project, I incorporated Tailwind CSS—a utility-first CSS framework. Tailwind CSS allowed me to rapidly prototype the user interface by applying utility classes directly in the HTML markup. This approach provided immediate visual feedback and simplified the styling process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Throughout this project, I learned several valuable lessons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Contrary to what I believed going into this project, I now feel that I would've been better served beginning with the frontend and building the backend as needed. Going with a backend first approach really created conflicts and limitations on the frontend build.&lt;/li&gt;
&lt;li&gt;Understanding backend and database interactions: Working with Flask, SQLAlchemy, and database migrations enhanced my understanding of how backend systems interact.&lt;/li&gt;
&lt;li&gt;Leveraging frontend frameworks: Utilizing Create React App streamlined the frontend development process, providing a solid foundation for building user interfaces.&lt;/li&gt;
&lt;li&gt;Incorporating Tailwind CSS for rapid prototyping: Tailwind CSS allowed me to prototype the frontend quickly, providing a visual approach to styling directly in the markup. However, it's important to be mindful of potential code clutter and repetitive code that may arise when using utility classes extensively.&lt;/li&gt;
&lt;li&gt;Integrating different parts of the application: Coordinating the frontend and backend requires careful consideration of API endpoints and data structures.&lt;/li&gt;
&lt;li&gt;Learning from existing resources: Researching and studying similar applications online helped me avoid pitfalls and learn best practices.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Building the "Squatch-Spotter" application from scratch using Flask, React, and Tailwind CSS was an enlightening journey. By carefully configuring the Flask app, defining models, managing migrations, and creating the React frontend with Tailwind CSS, I gained valuable insights into full-stack web development. The combination of Flask, React, and Tailwind CSS provided a powerful toolkit for creating a robust and visually appealing application.&lt;/p&gt;

</description>
      <category>flask</category>
      <category>react</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Clicking with Python</title>
      <dc:creator>Bruce Hillsworth</dc:creator>
      <pubDate>Mon, 22 May 2023 19:37:42 +0000</pubDate>
      <link>https://forem.com/hillswor/clicking-with-python-21l8</link>
      <guid>https://forem.com/hillswor/clicking-with-python-21l8</guid>
      <description>&lt;p&gt;In this blog post, we will explore the development of a Python Command-Line Interface (CLI) application that serves as a bike database. This app is a follow up to a previous project where we used JavaScript and a bike API to create a front end application allowing users to see stolen bikes within defined search ranges.  This CLI,  allows users to manage their bikes, search for stolen bikes, report stolen bikes, and update their bike information. We will walk through the main file of the app, cli.py, and showcase its key features with examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Bike Database CLI App
&lt;/h2&gt;

&lt;p&gt;To begin, let's understand the setup and dependencies of our bike database CLI app. The app utilizes SQLAlchemy, a popular Object-Relational Mapping (ORM) library, to interact with the SQLite database. In a nutshell, SQLAlchemy builds SQL database tables based on Python classes.  As such, the first step in building this project was determining which classes and tables I would need and what their relationships would be with each other.  In this project, I opted to have a User, Bike, and StolenBike class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from sqlalchemy import (
    create_engine,
    Column,
    Integer,
    String,
    Boolean,
    UniqueConstraint,
    ForeignKey,
)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref
import click
import re
import ipdb

Base = declarative_base()


class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True)
    username = Column(String, unique=True)
    email = Column(String, unique=True)

    __table_args__ = (
        UniqueConstraint("username", name="uq_username"),
        UniqueConstraint("email", name="uq_email"),
    )

    bikes = relationship("Bike", backref="user")
    stolen_bikes = relationship("StolenBike", backref="user")

    def __repr__(self):
        return "&amp;lt;User(username='%s', email='%s')&amp;gt;" % (self.username, self.email)


class Bike(Base):
    __tablename__ = "bikes"

    id = Column(Integer, primary_key=True)
    brand = Column(String)
    model = Column(String)
    year = Column(Integer)
    serial_number = Column(String)
    stolen = Column(Boolean, default=False)
    user_id = Column(Integer, ForeignKey("users.id"))

    def __repr__(self):
        return (
            "&amp;lt;Bike(brand='%s', model='%s', year='%s', serial_number='%s', stolen='%s')&amp;gt;"
            % (
                self.brand,
                self.model,
                self.year,
                self.serial_number,
                self.stolen,
            )
        )


class StolenBike(Base):
    __tablename__ = "stolen_bikes"

    id = Column(Integer, primary_key=True)
    date_stolen = Column(String)
    city = Column(String)
    state = Column(String(2))
    zip_code = Column(String(5))
    user_id = Column(Integer, ForeignKey("users.id"))
    bike_id = Column(Integer, ForeignKey("bikes.id"))

    def __repr__(self):
        return (
            "&amp;lt;StolenBike(date_stolen='%s', city='%s', state='%s', zip_code='%s')&amp;gt;"
            % (
                self.date_stolen,
                self.city,
                self.state,
                self.zip_code,
            )
        )

    bike = relationship("Bike", backref=backref("stolen_bikes", uselist=False))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SQLAlchemy, then used these three classes to create corresponding tables of users, bikes, and stolen_bikes.  The relationships between each table are defined within the Python classes using SQLAlchemy's realtionship() method:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;From the User class&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bikes = relationship("Bike", backref="user")
stolen_bikes = relationship("StolenBike", backref="user")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;From the StolenBike class&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bike = relationship("Bike", backref=backref("stolen_bikes", uselist=False))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The relationship established in the User class is one-to-many relationship to bikes and stolen bikes.  This means a single user can have multiple bikes and multiple stolen bikes.  Likewise, bike data and stolen bike data is accessible through a user instance.  The relationship established within the StolenBike class creates a one-to-one relationship between Bike and StolenBike.  A stolen bike can only have one bike and vice-versa.&lt;/p&gt;

&lt;p&gt;The next step is establishing a connection to our database so we can query and interact with it using Python.  The main components to establish this connection are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;create_engine:&lt;/strong&gt; Creates a database engine using SQLAlchemy and specifies the path to the SQLite database file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session:&lt;/strong&gt; Defines a session class that serves as an interface to interact with the database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;session:&lt;/strong&gt; Creates an instance of the session class for database operations.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;database_path = "db/spracket.db"
engine = create_engine(f"sqlite:///{database_path}")
Session = sessionmaker(bind=engine)
session = Session()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Main Features and Functionality
&lt;/h2&gt;

&lt;p&gt;The bike database CLI app offers several features for managing bike information. Let's dive into each feature and provide examples of their usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Displaying User's Bikes
&lt;/h2&gt;

&lt;p&gt;The display_users_bikes function retrieves and displays all bikes associated with the current user. If the user has registered bikes, their details, including brand, model, year, serial number, and stolen status, are presented in a tabular format.  Since I was using click, the current_user was stored via a global variable set in either the create_new_user function or the validate existing user function.  In the end, I would like to re-work this code and move this variable out of the global scope but for the purposes of this project, it provides the necessary functionality.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def display_users_bikes():
    bikes = current_user.bikes
    if bikes:
        table_data = [
            (
                bike.id,
                bike.brand,
                bike.model,
                bike.year,
                bike.serial_number,
                bike.stolen,
            )
            for bike in bikes
        ]
        headers = ["ID", "Brand", "Model", "Year", "Serial Number", "Stolen"]
        table = tabulate.tabulate(table_data, headers=headers, tablefmt="fancy_grid")
        click.echo(f"\n{current_user.username}'s Bikes:")
        click.echo(
            click.style("\n" + (table) + "\n", fg="green", bg="black", bold=True)
        )
    else:
        click.echo("You have no bikes in your profile.")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Would you like to view your profile, register a new bike, remove a bike from your profile, update one of your bikes, report one of your bikes stolen or search our stolen database (view, register, remove, update, report, search): view

ekline's Bikes:

╒══════╤════════════════════════════╤══════════╤════════╤═════════════════╤══════════╕
│   ID │ Brand                      │ Model    │   Year │   Serial Number │ Stolen   │
╞══════╪════════════════════════════╪══════════╪════════╪═════════════════╪══════════╡
│   14 │ Hill, Dawson and Hernandez │ family   │   1995 │        73263739 │ True     │
├──────┼────────────────────────────┼──────────┼────────┼─────────────────┼──────────┤
│   55 │ Harris-Christensen         │ rule     │   2002 │        69092985 │ True     │
├──────┼────────────────────────────┼──────────┼────────┼─────────────────┼──────────┤
│   56 │ Ross-Wilkerson             │ return   │   2008 │        28058472 │ False    │
├──────┼────────────────────────────┼──────────┼────────┼─────────────────┼──────────┤
│   60 │ Marsh-Vazquez              │ recently │   1972 │        78964785 │ True     │
├──────┼────────────────────────────┼──────────┼────────┼─────────────────┼──────────┤
│   65 │ Smith-Swanson              │ site     │   2005 │        27040331 │ False    │
├──────┼────────────────────────────┼──────────┼────────┼─────────────────┼──────────┤
│   78 │ Walter-White               │ budget   │   1985 │        28449713 │ False    │
╘══════╧════════════════════════════╧══════════╧════════╧═════════════════╧══════════╛

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Removing a Bike
&lt;/h2&gt;

&lt;p&gt;The remove_bike function allows users to remove a bike from their profile. Users are prompted to enter the ID of the bike they want to remove. If the ID is valid and the bike belongs to the user, it will be deleted from 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;@click.command()
@click.option(
    "--id",
    prompt="Looking at the bike ID's, which bike would you like to remove?",
    type=int,
    help="Specify the ID of the bike you would like to remove.",
)
def remove_bike(id):
    if id in [bike.id for bike in current_user.bikes]:
        bike = session.query(Bike).filter_by(id=id).first()
        session.delete(bike)
        session.commit()
        click.clear()
        click.echo("Bike successfully removed.")
        display_users_bikes()
        main_menu()
    else:
        click.clear()
        click.echo("Bike not found.")
        main_menu()


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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;matthew10's Bikes:

matthew10's Bikes:

╒══════╤══════════════╤═════════╤════════╤═════════════════╤══════════╕
│   ID │ Brand        │ Model   │   Year │   Serial Number │ Stolen   │
╞══════╪══════════════╪═════════╪════════╪═════════════════╪══════════╡
│   30 │ Martin Group │ into    │   1993 │        73585237 │ False    │
╘══════╧══════════════╧═════════╧════════╧═════════════════╧══════════╛

Looking at the bike ID's, which bike would you like to remove?: 30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bike successfully removed.
You have no bikes in your profile.
Would you like to view your profile, register a new bike, remove a bike from your profile, update one of your bikes, report one of your bikes stolen or search our stolen database (view, register, remove, update, report, search):
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Updating Bike Information
&lt;/h2&gt;

&lt;p&gt;The update_bike function enables users to modify the information of a specific bike in their profile. Users are prompted to enter the ID of the bike they want to update, followed by the field they wish to modify (brand, model, year, or serial number) and the new value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@click.command()
@click.option(
    "--id",
    prompt="Using the bike ID's, which bike would you like to update?",
    type=int,
    callback=validate_bike_id,
    help="Specify the ID of the bike you would like to update.",
)
@click.option(
    "--option",
    prompt="What would you like to update?",
    type=click.Choice(["brand", "model", "year", "serial_number", "stolen"]),
)
@click.option(
    "--value",
    prompt="What would you like to update it to?",
    callback=validate_value,
    help="Specify what you want to update to.",
)
def update_bike(id, option, value):
    bike = session.query(Bike).filter_by(id=id).first()
    if option == "brand":
        bike.brand = value
        session.commit()
        click.clear()
        click.echo("Bike brand successfully updated.")
        display_users_bikes()
        main_menu()
    elif option == "model":
        bike.model = value
        session.commit()
        click.clear()
        click.echo("Bike model successfully updated.")
        display_users_bikes()
        main_menu()
    elif option == "year":
        while True:
            try:
                value = validate_year(
                    None, None, value
                )  # Validate year using the callback
                break  # Break the loop if validation succeeds
            except click.BadParameter as e:
                click.echo(str(e))  # Print the error message
                value = click.prompt(
                    "What would you like to update it to?"
                )  # Re-prompt for the value
        bike.year = value
        session.commit()
        click.clear()
        click.echo("Bike year successfully updated.")
        display_users_bikes()
        main_menu()
    elif option == "serial_number":
        bike.serial_number = value
        session.commit()
        click.clear()
        click.echo("Bike serial number successfully updated.")
        display_users_bikes()
        main_menu()
    elif option == "stolen":
        bike.stolen = value
        session.commit()
        click.clear()
        click.echo("Bike stolen status successfully updated.")
        display_users_bikes()
        main_menu()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dcollier's Bikes:

╒══════╤══════════════╤═════════╤════════╤═════════════════╤══════════╕
│   ID │ Brand        │ Model   │   Year │   Serial Number │ Stolen   │
╞══════╪══════════════╪═════════╪════════╪═════════════════╪══════════╡
│   81 │ Park-Houston │ know    │   1970 │        29602575 │ True     │
╘══════╧══════════════╧═════════╧════════╧═════════════════╧══════════╛

Using the bike ID's, which bike would you like to update?: 81
What would you like to update? (brand, model, year, serial_number, stolen): model
What would you like to update it to?: trx
Bike model successfully updated.

dcollier's Bikes:

╒══════╤══════════════╤═════════╤════════╤═════════════════╤══════════╕
│   ID │ Brand        │ Model   │   Year │   Serial Number │ Stolen   │
╞══════╪══════════════╪═════════╪════════╪═════════════════╪══════════╡
│   81 │ Park-Houston │ trx     │   1970 │        29602575 │ True     │
╘══════╧══════════════╧═════════╧════════╧═════════════════╧══════════╛
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Reporting a Stolen Bike
&lt;/h2&gt;

&lt;p&gt;The report_stolen function allows users to report a bike as stolen. Users are prompted to enter the ID of the stolen bike, and the corresponding record is updated to reflect the stolen status.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ekline's Bikes:

╒══════╤════════════════════════════╤══════════╤════════╤═════════════════╤══════════╕
│   ID │ Brand                      │ Model    │   Year │   Serial Number │ Stolen   │
╞══════╪════════════════════════════╪══════════╪════════╪═════════════════╪══════════╡
│   14 │ Hill, Dawson and Hernandez │ family   │   1995 │        73263739 │ True     │
├──────┼────────────────────────────┼──────────┼────────┼─────────────────┼──────────┤
│   55 │ Harris-Christensen         │ rule     │   2002 │        69092985 │ True     │
├──────┼────────────────────────────┼──────────┼────────┼─────────────────┼──────────┤
│   56 │ Ross-Wilkerson             │ return   │   2008 │        28058472 │ False    │
├──────┼────────────────────────────┼──────────┼────────┼─────────────────┼──────────┤
│   60 │ Marsh-Vazquez              │ recently │   1972 │        78964785 │ False    │
├──────┼────────────────────────────┼──────────┼────────┼─────────────────┼──────────┤
│   65 │ Smith-Swanson              │ site     │   2005 │        27040331 │ False    │
├──────┼────────────────────────────┼──────────┼────────┼─────────────────┼──────────┤
│   78 │ Walter-White               │ budget   │   1985 │        28449713 │ False    │
╘══════╧════════════════════════════╧══════════╧════════╧═════════════════╧══════════╛

Using the bike ID's, which bike would you like to report stolen?: 60
What date was the bike stolen(MM-DD-YYYY)?: 05-22-2023
What city was the bike stolen in?: Denver
What state was the bike stolen in?: CO
What ZIP code was the bike stolen in?: 80237

│   60 │ Marsh-Vazquez              │ recently │   1972 │        78964785 │ True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Searching For Stolen Bikes
&lt;/h2&gt;

&lt;p&gt;The search_stolen_bikes function enables users to search the stolen_bikes table based on different criteria such as city, state, and zip_code.  It pulls data from multiple tables using the relationships established in the classes above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@click.command()
@click.option(
    "--action",
    prompt="Would you like to view all bikes or search by city, state, or ZIP code?",
    type=click.Choice(["all", "city", "state", "zip_code"]),
)
def search_stolen_bikes(action):
    if action == "all":
        stolen_bikes = session.query(StolenBike).all()
        if stolen_bikes:
            table_data = [
                (
                    stolen_bike.bike.id,
                    stolen_bike.date_stolen,
                    stolen_bike.bike.brand,
                    stolen_bike.bike.model,
                    stolen_bike.bike.year,
                    stolen_bike.city,
                    stolen_bike.state,
                    stolen_bike.zip_code,
                )
                for stolen_bike in stolen_bikes
            ]
            headers = [
                "ID",
                "Date Stolen",
                "Brand",
                "Model",
                "Year",
                "City",
                "State",
                "ZIP Code",
            ]
            table = tabulate.tabulate(table_data, headers, tablefmt="fancy_grid")
            click.echo(f"\nStolen Bikes:\n")
            click.echo(
                click.style("\n" + table + "\n", fg="green", bg="black", bold=True)
            )
            main_menu()
        else:
            click.echo("No stolen bikes found.")
            main_menu()
    elif action == "city":
        city = click.prompt("What city would you like to search for?")
        stolen_bikes = session.query(StolenBike).filter_by(city=city).all()
        if stolen_bikes:
            table_data = [
                (
                    stolen_bike.bike.id,
                    stolen_bike.date_stolen,
                    stolen_bike.bike.brand,
                    stolen_bike.bike.model,
                    stolen_bike.bike.year,
                    stolen_bike.bike.serial_number,
                    stolen_bike.city,
                    stolen_bike.state,
                    stolen_bike.zip_code,
                )
                for stolen_bike in stolen_bikes
            ]
            headers = [
                "ID",
                "Date Stolen",
                "Brand",
                "Model",
                "Year",
                "Serial Number",
                "City",
                "State",
                "ZIP Code",
            ]
            table = tabulate.tabulate(table_data, headers, tablefmt="fancy_grid")
            click.echo(f"\nStolen Bikes:\n")
            click.echo(
                click.style("\n" + table + "\n", fg="green", bg="black", bold=True)
            )
            main_menu()
        else:
            click.echo(f"No stolen bikes found in {city}.")
            main_menu()
    elif action == "state":
        state = click.prompt("What state would you like to search for?")
        stolen_bikes = session.query(StolenBike).filter_by(state=state).all()
        if stolen_bikes:
            table_data = [
                (
                    stolen_bike.bike.id,
                    stolen_bike.date_stolen,
                    stolen_bike.bike.brand,
                    stolen_bike.bike.model,
                    stolen_bike.bike.year,
                    stolen_bike.bike.serial_number,
                    stolen_bike.city,
                    stolen_bike.state,
                    stolen_bike.zip_code,
                )
                for stolen_bike in stolen_bikes
            ]
            headers = [
                "ID",
                "Date Stolen",
                "Brand",
                "Model",
                "Year",
                "Serial Number",
                "City",
                "State",
                "ZIP Code",
            ]
            table = tabulate.tabulate(table_data, headers, tablefmt="fancy_grid")
            click.echo(f"\nStolen Bikes:\n")
            click.echo(
                click.style("\n" + table + "\n", fg="green", bg="black", bold=True)
            )
            main_menu()
        else:
            click.echo(f"No stolen bikes found in {state}.")
            main_menu()
    elif action == "zip_code":
        zip_code = click.prompt("What ZIP code would you like to search for?")
        stolen_bikes = session.query(StolenBike).filter_by(zip_code=zip_code).all()
        if stolen_bikes:
            table_data = [
                (
                    stolen_bike.bike.id,
                    stolen_bike.date_stolen,
                    stolen_bike.bike.brand,
                    stolen_bike.bike.model,
                    stolen_bike.bike.year,
                    stolen_bike.bike.serial_number,
                    stolen_bike.city,
                    stolen_bike.state,
                    stolen_bike.zip_code,
                )
                for stolen_bike in stolen_bikes
            ]
            headers = [
                "ID",
                "Date Stolen",
                "Brand",
                "Model",
                "Year",
                "Serial Number",
                "City",
                "State",
                "ZIP Code",
            ]
            table = tabulate.tabulate(table_data, headers, tablefmt="fancy_grid")
            click.echo(f"\nStolen Bikes:\n")
            click.echo(
                click.style("\n" + table + "\n", fg="green", bg="black", bold=True)
            )
            main_menu()
        else:
            click.echo(f"No stolen bikes found in {zip_code}.")
            main_menu()

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Would you like to view your profile, register a new bike, remove a bike from your profile, update one of your bikes, report one of your bikes stolen or search our stolen database (view, register, remove, update, report, search): search
Would you like to view all bikes or search by city, state, or ZIP code? (all, city, state, zip_code): state
What state would you like to search for?: CO

Stolen Bikes:


╒══════╤═════════════════════╤════════════════════════╤════════════╤════════╤═════════════════╤═════════════════╤═════════╤════════════╕
│   ID │ Date Stolen         │ Brand                  │ Model      │   Year │   Serial Number │ City            │ State   │   ZIP Code │
╞══════╪═════════════════════╪════════════════════════╪════════════╪════════╪═════════════════╪═════════════════╪═════════╪════════════╡
│   23 │ 12-31-1995          │ Carr, Nolan and Snyder │ management │   1984 │        79114073 │ Nashville       │ CO      │      81078 │
├──────┼─────────────────────┼────────────────────────┼────────────┼────────┼─────────────────┼─────────────────┼─────────┼────────────┤
│   43 │ 05-11-2010          │ Wilcox-Alexander       │ treat      │   2019 │        15076632 │ Stricklandmouth │ CO      │      80580 │
├──────┼─────────────────────┼────────────────────────┼────────────┼────────┼─────────────────┼─────────────────┼─────────┼────────────┤
│   44 │ 01-24-1980          │ Day-Reynolds           │ that       │   1981 │        34812471 │ South Jaredton  │ CO      │      81517 │
├──────┼─────────────────────┼────────────────────────┼────────────┼────────┼─────────────────┼─────────────────┼─────────┼────────────┤
│   75 │ 2023-05-21 00:00:00 │ Hood Inc               │ specific   │   1971 │        93030168 │ Denver          │ CO      │      80237 │
├──────┼─────────────────────┼────────────────────────┼────────────┼────────┼─────────────────┼─────────────────┼─────────┼────────────┤
│   81 │ 2023-05-20 00:00:00 │ Park-Houston           │ trx        │   1970 │        29602575 │ Denver          │ CO      │      80237 │
├──────┼─────────────────────┼────────────────────────┼────────────┼────────┼─────────────────┼─────────────────┼─────────┼────────────┤
│   60 │ 2023-05-22 00:00:00 │ Marsh-Vazquez          │ recently   │   1972 │        78964785 │ Denver          │ CO      │      80237 │
╘══════╧═════════════════════╧════════════════════════╧════════════╧════════╧═════════════════╧═════════════════╧═════════╧════════════╛
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using Python Click
&lt;/h2&gt;

&lt;p&gt;You may have noticed a lot of the functions in the CLI are decorated in @click.command() and @click.option() functions. &lt;br&gt;
 Python Click is a popular library for creating command-line interfaces (CLIs) in Python. It provides a simple and elegant way to define and organize CLI commands, options, and arguments. Let's discuss some of the benefits and potential drawbacks of using Python Click in the development of our bike database CLI app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Python Click
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Simplicity:&lt;/strong&gt; Python Click offers a straightforward and intuitive syntax for defining CLI commands, making it easy to understand and use. It follows the principle of "convention over configuration," which means it provides sensible defaults and handles much of the CLI plumbing, allowing developers to focus on writing the actual functionality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Command Organization:&lt;/strong&gt; Click allows you to structure your CLI app into logical commands and subcommands, providing a clear and organized structure. This makes it easier for users to navigate and discover available functionality, especially when dealing with complex applications with multiple commands and options.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automatic Help Generation:&lt;/strong&gt; Click automatically generates help messages and usage instructions for your CLI commands based on their definitions. This eliminates the need for manually documenting each command and provides users with a convenient way to explore available options and arguments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option Parsing and Validation:&lt;/strong&gt; Click simplifies the process of parsing command-line options and arguments, handling conversions, and providing built-in validation mechanisms. It supports a wide range of option types, including flags, strings, integers, floats, and more. This streamlines the development process and reduces the likelihood of errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integration with Python Ecosystem:&lt;/strong&gt; Python Click seamlessly integrates with other Python libraries and frameworks, making it a versatile choice for CLI development. It works well with popular libraries such as argparse, docopt, and Flask, allowing you to reuse code and integrate with existing projects effortlessly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drawbacks of Python Click
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Learning Curve:&lt;/strong&gt; While Click strives to provide a simple and intuitive API, there is still a learning curve involved in understanding its concepts and best practices. If you are new to Click or CLI development in general, you may need to spend some time familiarizing yourself with the library and its documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Additional Dependency:&lt;/strong&gt; Python Click is an external dependency that needs to be installed alongside your project. If you prefer to keep your project's dependencies minimal, adding another library might be seen as a downside. However, Click has become a popular choice in the Python community, and its benefits often outweigh this consideration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customization Limitations:&lt;/strong&gt; Click offers a wide range of functionality out-of-the-box, but there might be cases where you require custom behavior that is not readily available. While Click is highly extensible, and you can subclass and customize its components, there could be scenarios where you may need to work around the library's constraints.&lt;/p&gt;

&lt;p&gt;Despite these potential drawbacks, Python Click provides a powerful and flexible framework for building command-line interfaces in Python. Its simplicity, organization capabilities, and integration with the Python ecosystem make it a popular choice among developers.&lt;/p&gt;

&lt;p&gt;By leveraging Python Click in the development of our bike database CLI app, we were able to easily define and structure commands, handle input parsing and validation, and generate helpful usage instructions. This resulted in a user-friendly and efficient CLI tool for managing bike information.&lt;/p&gt;

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

&lt;p&gt;In this blog post, we explored the development of a Python CLI app for managing a bike database. We discussed the setup and dependencies, as well as the app's key features such as displaying, removing, updating, and reporting stolen bikes. &lt;/p&gt;

</description>
      <category>cli</category>
      <category>python</category>
      <category>click</category>
      <category>tabulate</category>
    </item>
    <item>
      <title>Clarity Through Destructuring</title>
      <dc:creator>Bruce Hillsworth</dc:creator>
      <pubDate>Fri, 28 Apr 2023 02:50:01 +0000</pubDate>
      <link>https://forem.com/hillswor/clarity-through-destructuring-2eeg</link>
      <guid>https://forem.com/hillswor/clarity-through-destructuring-2eeg</guid>
      <description>&lt;p&gt;For aspiring software developers, it's easy to overlook certain concepts that seem trivial or confusing at first. One such concept that I initially dismissed was object and array destructuring in JavaScript. However, as I progressed in my learning journey and started working with frameworks like React, I quickly realized the power and convenience that destructuring can provide.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Destructuring?
&lt;/h2&gt;

&lt;p&gt;Destructuring is a way of extracting values from arrays and objects and assigning them to variables in a concise and readable manner. It allows you to "destructure" the data into smaller pieces, making it easier to work with and more expressive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Array Destructuring in JavaScript
&lt;/h2&gt;

&lt;p&gt;Array destructuring is used to extract values from an array and assign them to variables. Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const numbers = [1, 2, 3, 4, 5];
const [first, second, , fourth] = numbers;

console.log(first); // 1
console.log(second); // 2
console.log(fourth); // 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we're destructuring the numbers array and assigning the first element to the first variable, the second element to the second variable, and the fourth element to the fourth variable. Notice how we're using commas to skip over the third element (which has a value of 3).&lt;/p&gt;

&lt;h2&gt;
  
  
  Object Destructuring in JavaScript
&lt;/h2&gt;

&lt;p&gt;Object destructuring is used to extract properties from an object and assign them to variables. Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const person = {
  firstName: "John",
  lastName: "Doe",
  age: 30,
  address: {
    street: "123 Main St",
    city: "Anytown",
    state: "CA",
    zip: "12345"
  }
};

const { firstName, lastName, address: { city } } = person;

console.log(firstName); // "John"
console.log(lastName); // "Doe"
console.log(city); // "Anytown"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we're destructuring the person object and assigning the firstName and lastName properties to variables with the same names. We're also destructuring the address property and assigning the city property to a variable with the same name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Destructuring in React
&lt;/h2&gt;

&lt;p&gt;Destructuring is especially useful when working with React.  In React components, props are often passed down to child components.  Destructuring allows you to extract only the props you need, making your code easier to read and understand. Here's the basic syntax for destructuring props:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function ChildComponent({ prop1, prop2 }) {
  // Use prop1 and prop2 here
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a more detailed example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react";

function Person(props) {
  const { firstName, lastName, age } = props;

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;{firstName} {lastName}&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;{age}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default Person;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we're destructuring the props object and assigning the firstName, lastName, and age properties to variables with the same names. This makes it easier to access and use these values within the component's JSX.&lt;/p&gt;

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

&lt;p&gt;Destructuring can greatly simplify and improve your code by making it more readable and expressive. Whether you're working with arrays, objects, or React components, mastering destructuring can make your coding experience more enjoyable and productive.&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Losing Control</title>
      <dc:creator>Bruce Hillsworth</dc:creator>
      <pubDate>Fri, 07 Apr 2023 22:39:29 +0000</pubDate>
      <link>https://forem.com/hillswor/losing-control-5d8o</link>
      <guid>https://forem.com/hillswor/losing-control-5d8o</guid>
      <description>&lt;p&gt;As a new developer, every single day is information overload.  I am convinced that the pounding I feel in my head after encountering another challenging concept is actually my brain morphing, evolving and rearranging itself to brace for... you guessed it!  More information.  Objects, arrays, pure functions, context, THIS!  These are just some of the concepts that loop through the brain of an aspiring developer and I am no exception.  Yet, as I sit here, having completed my first collaborative frontend project, version control is what I am ruminating over.  This is by no means a complete rundown of version control, rather a synopsis of what I learned working with others for the first time with an emphasis on version control.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Project
&lt;/h2&gt;

&lt;p&gt;The goal of the project was to build a single page app that fetches data from an external API and utilizes JavaScript to manipulate the DOM and provide data without without a page refresh.  One of the members of my group is a bike enthusiast and put us on to a website called bikeindex.org that maintains an API.  The API allows people to search the database for stolen bikes, report their bike as stolen, and register a newly purchased bike.  After some brainstorming, we decided to do a page that upon load would pull the user's location and then display bikes that have been stolen locally.  &lt;/p&gt;

&lt;h2&gt;
  
  
  What We Did Right
&lt;/h2&gt;

&lt;p&gt;This was the first collaborative project for all three of us so we definitely ran into hurdles but we also did some things correctly.  The first thing we did right, was taking the time to brainstorm and dial in what exactly we wanted to display.  We determined that we wanted to populate the bike data on a reusable card that would allow us to use a render function.  Likewise, we also decided we would need to get the geolocation of the user and would populate the page on load with local stolen bikes.  Deciding on these base level items gave us a path from start to finish and ensured that none of us strayed too far from the original concept.  Within a short period of time we developed a function to access the user location using Abstract API and combined it with an initialize function that fetched stolen bike data from the bikeindex.org API and rendered bikes on individual cards using a render function.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Geolocation Function Using XML:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function httpGetAsync(url, callback) {
  const xmlHttp = new XMLHttpRequest();
  xmlHttp.onreadystatechange = function () {
    if (xmlHttp.readyState === 4) {
      if (xmlHttp.status === 200) {
        callback(xmlHttp.responseText);
      } else {
        callback(null, new Error("Request failed"));
      }
    }
  };
  xmlHttp.open("GET", url, true);
  xmlHttp.send(null);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Initialize Function:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function initialize(response) {
  if (response === null) {
    fetch(
      `https://bikeindex.org:443/api/v3/search?page=1&amp;amp;per_page=25&amp;amp;location=ip&amp;amp;distance=${distance}&amp;amp;stolenness=proximity`
    )
      .then((response) =&amp;gt; response.json())
      .then((stolenBikes) =&amp;gt; {
        bikes = stolenBikes.bikes.filter(
          (x) =&amp;gt; x.large_img &amp;amp;&amp;amp; x.title &amp;amp;&amp;amp; x.description
        );
        if (!bikes.length) {
          alert("Try extending the search radius");
        }
        bikes.forEach((bike) =&amp;gt; renderDisplayCardsOnPageLoad(bike));
      });
  } else {
    locationObject = JSON.parse(response);
    zipCode = locationObject.postal_code;
    fetch(
      `https://bikeindex.org:443/api/v3/search?page=1&amp;amp;per_page=100&amp;amp;query=image&amp;amp;location=${zipCode}&amp;amp;distance=${distance}&amp;amp;stolenness=proximity`
    )
      .then((response) =&amp;gt; response.json())
      .then((stolenBikes) =&amp;gt; {
        console.log(stolenBikes);
        bikes = stolenBikes.bikes.filter(
          (x) =&amp;gt; x.large_img &amp;amp;&amp;amp; x.title &amp;amp;&amp;amp; x.description
        );
        if (!bikes.length) {
          alert("Try extending the search radius");
        }
        bikes.forEach((bike) =&amp;gt; renderDisplayCardsOnPageLoad(bike));
      });
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Render Function:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function renderDisplayCardsOnPageLoad(bike) {
  let imageOpacity = true;

  const stolenLocation = document.createElement("p");
  stolenLocation.setAttribute("id", "bike-name");
  stolenLocation.textContent = getCityAndState(bike);
  const card = document.createElement("div");
  card.setAttribute("class", "card");
  const img = document.createElement("img");
  img.setAttribute("src", bike.large_img);
  const location = document.createElement("p");
  location.textContent = getCityAndState(bike);

  img.addEventListener("click", (e) =&amp;gt; {
    imageOpacity = !imageOpacity;

    e.preventDefault();
    if (!imageOpacity) {
      const reportButton = document.createElement("button");
      reportButton.innerText = "REPORT SIGHTING";
      reportButton.setAttribute("id", "report");

      const description = document.createElement("p");
      description.textContent = bike.title;
      const serialNumber = document.createElement("p");
      const serialNumberString = `Serial Number: ${bike.serial}`;
      serialNumber.textContent = serialNumberString;
      const dateStolen = document.createElement("p");
      dateStolen.textContent = getDateStolen(bike);
      const location = document.createElement("p");
      location.textContent = getCityAndState(bike);
      e.target.style.opacity = 0.15;
      imageOpacity = false;

      card.appendChild(reportButton);
      card.appendChild(serialNumber);
      card.appendChild(dateStolen);
      card.appendChild(description);

      reportButton.addEventListener("click", (e) =&amp;gt; {
        card.innerHTML = "";
        const returnButton = document.createElement("button");
        returnButton.innerText = "RETURN";
        returnButton.className = "back-btn";
        const reportFormSubmit = document.createElement("button");
        reportFormSubmit.setAttribute = ("type", "submit");
        reportFormSubmit.setAttribute = ("value", "submit");
        reportFormSubmit.innerText = "SUBMIT";
        reportFormSubmit.className = "btn";
        const reportForm = document.createElement("form");
        reportForm.id = "report-form";
        const reportFormLocation = document.createElement("input");
        reportFormLocation.type = "text";
        reportFormLocation.className = "field";
        reportFormLocation.id = "report_form_location";
        reportFormLocation.placeholder = "   ENTER SIGHTING LOCATION";
        const reportFormComments = document.createElement("input");
        reportFormComments.type = "text";
        reportFormComments.className = "field";
        reportFormComments.id = "report_form_comments";
        reportFormComments.className = "field";
        reportFormComments.placeholder = "   ADDITIONAL COMMENTS";
        const reportFormName = document.createElement("input");
        reportFormName.type = "text";
        reportFormName.id = "report_form_name";
        reportFormName.className = "field";
        reportFormName.placeholder = "   NAME (optional)";
        const bikeDetails = document.createElement("button");
        bikeDetails.textContent = "BIKE DETAILS";
        bikeDetails.className = "btn";
        const location = document.createElement("p");

        location.textContent = getCityAndState(bike);
        location.className = "location";

        card.appendChild(reportForm);
        reportForm.appendChild(reportFormLocation);
        reportForm.appendChild(reportFormComments);
        reportForm.appendChild(reportFormName);
        reportForm.appendChild(reportFormSubmit);
        reportForm.appendChild(returnButton);
        returnButton.addEventListener("click", (e) =&amp;gt; {
          card.innerHTML = "";
          img.style.opacity = 1;
          card.appendChild(img);
          card.appendChild(stolenLocation);
        });

        reportForm.addEventListener("submit", (e) =&amp;gt; {
          e.preventDefault();
          const fll = report_form_location.value;
          const flc = report_form_comments.value;
          const fln = report_form_name.value;
          card.innerHTML = "";
          img.style.opacity = 1;
          card.appendChild(img);
          card.appendChild(stolenLocation);
          createSightingObj(bike, fll, flc, fln);
        });
      });
    } else {
      card.innerHTML = "";
      e.target.style.opacity = 1;
      card.appendChild(img);
      card.appendChild(stolenLocation);
      imageOpacity = true;
    }
  });

  card.appendChild(img);
  card.appendChild(stolenLocation);
  gallery.appendChild(card);
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Where We Lost Control
&lt;/h2&gt;

&lt;p&gt;Feeling pretty good about our progress we opted to have some fun and each of us opened our own branch and played with the code as we saw fit.  This is where our first invaluable lesson on version control came in to play and following our the tips I learned and would advise any new developer to follow unless they want to handle nightmare merges down the road...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Split Up The Work&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As new developers we all wanted to be very involved in every aspect of the app.  All the code above is what the final code wound up being but each of us, on our own time, made our own render and initialize functions.  While similar in technique, they were also very different.  We also had a tendency to make sweeping changes vs small.  This became particularly burdensome when we would go to merge files which leads me to my next tip...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Commit Small and Commit Often&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This should be self explanatory but all the members of my group strayed away from this concept too frequently.  The end result was less time spent on our code and making our app better vs nightmare merge sessions where we are trying to get all of our code to work together. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Single Intent&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This coincides with small commits but even small commits could influence multiple areas of an app.  As stated above, when we played with the code, we each did huge changes to multiple areas(JS, HTML, CSS) prior to committing.  By keeping commits to single intents, reversing the code to enable better commits is more readily available.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Branch Away&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A technique that I feel helped me, too late of course, was to  have a separate branch for each area that I was working on.  For instance, a branch just to handle the JavaScript file, a branch for the CSS, and a branch for the HTML.  I found it to be incredibly difficult to merge commits that had dabbled in multiple spaces.&lt;/p&gt;

</description>
      <category>github</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
