Blog Python Model code and SQLite Database.

  • From VSCode using SQLite3 Editor, show your unique collection/table in database, display rows and columns in the table of the SQLite database. SQLDatabase.png
  • From VSCode model, show your unique code that was created to initialize table and create test data. Initialize-Data-Table.png

Lists and Dictionaries.

  • Dictionaries and Lists are used in the user file as there is a dictionary with each key’s values being a list after the values are queried from the database. Here you can see both being used

imglst

This image shows the painting list and how each painting(the purple 0, 1, 2, 3, etc) has an image, an id, and userID attributed to the painting. The purple 0 numbers are associated with a painting and in painting, we have its ID, the image encoded as base 64, and UserID.

Screenshot-2024-04-19-at-9-59-40-AM.png

Python API code, Postman/JSON

  • In VSCode, show Python API code definition for request and response using GET, POST, UPDATE methods. Discuss algorithmic condition used to direct request to appropriate Python method based on request method.

In the image below, the POST method within our API endpoint is responsible for creating a new user in our system. This function allows clients to send a request containing JSON data representing the user’s information, such as their name, user ID (UID), password (optional), date of birth (DOB), and favorite food.

The function interacts with the database to create a new user entry. If successful, it returns the JSON representation of the created user, including all provided and generated attributes.

The GET method is responsible for retrieving all users stored in the database and returning their information in JSON format.

The method queries the database to fetch all user records using the User.query.all() function.

It then prepares the retrieved user data into a JSON-ready format using a list comprehension, iterating over each user and calling their read() method (assuming such a method exists in the User class).

Finally, the method returns a Flask response object containing the JSON-formatted user data using jsonify().

Screenshot-2024-04-23-at-9-55-16-AM.png

The PUT (aka the UPDATE function) updates user information by recieving the updated user information and updating the user information with that. PUTfunction(update).png

APIs and JSON

The API is used to communicate between the frontend and the server. GET requests are used to retrieve data from the server and POST requests is more dynamic as it can be used for various purposes. In the context of the project it is used to set a value in the database, and retrieve data.

JSON or Javascript Object Notation is used to transmit a “name” for the value which is the key, and the value itself, so if I wanted to recieve a username, the key is “username” and the value would be the actual username value

Example of GET This function gets the painting from database and returns it
GETfunction.png

Example of POST This first checks if the user has authentication to post and once verified, it will upload the image POSTfunction.png

Token Required function This is an authentication function to ensure user has permissions to continue Token-Required.png

200 Code This is what happens when all user inputs are correct and login is successful 200code.png

400 Code This is what happens when the password for example is incorrect. Login is unsuccessful due to invalid user id or password 400code.png

404 Code This is a result of the URL being incorrect and not being able to find it 404Code

api.add_resource(HousePriceAPI, '/predict')


Frontend Work

These are the same error codes but in network using inspect element on chrome

Successful Authentication (200 Code) FrontendLogin Frontend200Code

Unsuccessful Authentication (400 Code) FrontendWrongLogin Frontend400Code

  • In JavaScript code, show and describe code that handles failure. Describe how the code shows failure to the user in the Chrome Browser screen.

If the response is not ok, it asks the user to log. Otherwise, if the user has not inputted a design name, an alert pops up and the design is not created (no fetch is made).

let painting = document.getElementById("paintingfile")
let paintingimg = document.getElementById("paintingimg")
const endpoint = "http://127.0.0.1:8008/uploadPainting"
function sendPainting() {
  let payload =
  {
   "painting":paintingimg.src
  }
  fetch(endpoint,
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      credentials: "include",
      body: JSON.stringify(payload)
    }).then(response => {
      if (response.ok) {
        return response.text()
      }
      throw new Error("Network response failed")
    }).then(data => {
      
    })
    .catch(error => {
      console.error("There was a problem with the fetch", error);
    });
}

  • Blog JavaScript API fetch code and formatting code to display JSON.

This code fetches data from a specified endpoint (galleryEndpoint) using the Fetch API in JavaScript. It sends a GET request to retrieve painting data in JSON format. Upon receiving a successful response, it parses the JSON data and dynamically creates HTML elements to display each painting’s image and artist credit. If there’s an error during the fetch process, it logs an error message to the console. Finally, the getPosts() function is called to initiate the data retrieval process when the script is executed.

const galleryEndpoint = "http://127.0.0.1:8008/getPainting"
let gallery =  document.getElementById("gallery")
function getPosts() {
    fetch(galleryEndpoint,
      {
        method: "GET",
        headers: {
          "Content-Type": "application/json",
        },
        credentials: "include",
      }).then(response => {
        if (response.ok) {
          return response.json()
        }
        throw new Error("Network response failed")
      }).then(data => {
        console.log("Response:", data);
        for(let i=0;i<data.paintings.length;i++)
        {
            
            let paintingDiv = document.createElement("div")
            paintingDiv.classList.add("painting")
            let paintingimg = document.createElement("img")
            gallery.appendChild(paintingDiv)
            paintingDiv.appendChild(paintingimg)
            paintingimg.src = data.paintings[i].image
            let artistCredit = document.createElement("h2")
            artistCredit.innerText = `Painted By ${data.paintings[i].username}`
            paintingDiv.appendChild(artistCredit)
        }
        
      })
      .catch(error => {
        console.error("There was a problem with the fetch", error);
      });
  }

  getPosts()

This is the model for the house prices machine learning model. I used linear regression on generated data in the CSV file to predict the final house price as the predicted_price variable, then printed for the user.

from flask import Flask, request, jsonify
from flask import Blueprint
from flask_restful import Api, Resource
import seaborn as sns
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
from sklearn.linear_model import LogisticRegression
import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
import pickle

house_price_api = Blueprint('house_price_api', __name__, url_prefix='/api/house_price')
api = Api(house_price_api)

class HousePriceAPI(Resource):
    def __init__(self):
        # Load the dataset
        data = pd.read_csv('house_prices.csv')
        
        # Preprocessing and feature selection/engineering
        # Remove the 'furnishingstatus' column
        X = data.drop(['price', 'furnishingstatus'], axis=1)
        y = data['price']
        
        # Train the model
        self.model = LinearRegression()
        self.model.fit(X, y)

    def preprocess_inputs(self, area, bedrooms, bathrooms, stories, mainroad, guestroom, basement, hotwaterheating, airconditioning, parking, prefarea):
        # Convert 'yes' or 'no' inputs to 1 or 0
        mainroad = 1 if mainroad.lower() == 'yes' else 0
        guestroom = 1 if guestroom.lower() == 'yes' else 0
        basement = 1 if basement.lower() == 'yes' else 0
        hotwaterheating = 1 if hotwaterheating.lower() == 'yes' else 0
        airconditioning = 1 if airconditioning.lower() == 'yes' else 0
        parking = 1 if parking.lower() == 'yes' else 0
        prefarea = 1 if prefarea.lower() == 'yes' else 0
        
        return area, bedrooms, bathrooms, stories, mainroad, guestroom, basement, hotwaterheating, airconditioning, parking, prefarea

    def predict_house_price(self, area, bedrooms, bathrooms, stories, mainroad, guestroom, basement, hotwaterheating, airconditioning, parking, prefarea):
        # Prepare input data
        input_data = pd.DataFrame({
            'area': [area],
            'bedrooms': [bedrooms],
            'bathrooms': [bathrooms],
            'stories': [stories],
            'mainroad': [mainroad],
            'guestroom': [guestroom],
            'basement': [basement],
            'hotwaterheating': [hotwaterheating],
            'airconditioning': [airconditioning],
            'parking': [parking],
            'prefarea': [prefarea]
        })
        
        # Make prediction
        predicted_price = self.model.predict(input_data)
        return predicted_price[0]

    def post(self):
        try:
            # Get data from request
            data = request.json
            # Extract features
            area = data['area']
            bedrooms = data['bedrooms']
            bathrooms = data['bathrooms']
            stories = data['stories']
            mainroad = data['mainroad']
            guestroom = data['guestroom']
            basement = data['basement']
            hotwaterheating = data['hotwaterheating']
            airconditioning = data['airconditioning']
            parking = data['parking']
            prefarea = data['prefarea']
            # Preprocess inputs
            area, bedrooms, bathrooms, stories, mainroad, guestroom, basement, hotwaterheating, airconditioning, parking, prefarea = self.preprocess_inputs(area, bedrooms, bathrooms, stories, mainroad, guestroom, basement, hotwaterheating, airconditioning, parking, prefarea)
            # Predict house price
            predicted_price = self.predict_house_price(area, bedrooms, bathrooms, stories, mainroad, guestroom, basement, hotwaterheating, airconditioning, parking, prefarea)
            predicted_price = predicted_price/10
            predicted_price = round(predicted_price, 2)

            return jsonify({'predicted_price': predicted_price })
        except Exception as e:
            return jsonify({'error': str(e)})

api.add_resource(HousePriceAPI, '/predict')



Deployment notes

  • JavaScript Fetch, URI: The project must manage the switch between development and deployment URI access for frontend requests. This is achieved by including the assets/js/config.js file in the teacher_portfolio project. This file should be referenced whenever a URL endpoint fetch is performed.

  • Nginx and CORS: For deployment, CORS policies need to be configured in Nginx. THis involves setting the “Acess-Control-Allow-“ WORK WITH NGINX OR ELSE YOU WILL FAIL

  • Python, CORS: For both localhost development and deployment, the project needs to consider Cross-Origin Resource Sharing (CORS). The CORS policies should be built into the Python code, specifically in your __init__.py file. This setup allows the necessary access between your frontend and backend, supporting both development and deployment environments. Note, remove similar code from main.py if you picked up an old version from Teacher.

  • Python authentcation: @token_required function is used to gaurd HTTP endpoints. Allowed credentials are used which returns a value that represents the user object from the database. The user object can be used in the application logic of the API to identify the request to the owner

  • Certbot, changes HTTP to HTTPS for deployment usage.

Uses of HTTP rrequests in Login and Authorization

  • Ungaurded requests
    • POST request for signing up

    Get both gaurded and unguarded APIS

Common URI and Header options

To facilitate consistency in fetch operations, it’s best to isolate variables into a single location. This code should be imported and used as a template for every fetch operation.

// The URI used in fetch is determined by the hostname of the requester.
export var uri;
if (location.hostname === "localhost") {
        uri = "http://localhost:8086";
} else if (location.hostname === "127.0.0.1") {
        uri = "http://127.0.0.1:8086";
} else {
        uri = "https://flask2.nighthawkcodingsociety.com";
}


// A fetch header template should be used to avoid omissions or other errors, 
// (*) the atererisk is assigned, other values are shown for reference. 
export const options = {
    method: 'GET', // *GET, POST, PUT, DELETE, etc.
    mode: 'cors', // no-cors, *cors, same-origin
    cache: 'default', // *default, no-cache, reload, force-cache, only-if-cached
    credentials: 'include', // include, same-origin, omit
    headers: {
        'Content-Type': 'application/json',
    },
};

Simple HTML login

<!-- 
A simple HTML login form that triggers a login action when the button is pressedi, login_user() function.
-->
<!-- Login Form -->
<form action="javascript:login_user()">
    <p><label>
        User ID:
        <input type="text" name="uid" id="uid" required>
    </label></p>
    <p><label>
        Password:
        <input type="password" name="password" id="password" required>
    </label></p>
    <p>
        <button>Login</button>
    </p>
    <p id="error-message" style="color: red;"></p>
</form>


<!-- 
This script handles user authentication. Upon successful authentication, 
it redirects to a page that requires a JWT (JSON Web Token) for access. 
-->
<script>
    // In the context of GitHub Pages, the Liquid site.baseurl variable is used to locate the config.js file.
    import { uri, options } from '/teacher_portfolio/assets/js/api/config.js';

    // Set the URLs for the endpoints used in this script.
    const url = uri + '/api/users/authenticate';
    const redirect =  uri + '/python/database'; 

    // Method to login user
    function login_user(){

        // Set body to include login data from HTML form
        const body = {
            uid: document.getElementById("uid").value,
            password: document.getElementById("password").value,
        };

        // Modify the options to use the POST method and include the request body.
        const authOptions = {
            ...options, // This will copy all properties from options
            method: 'POST', // Override the method property
            cache: 'no-cache', // Set the cache property
            body: JSON.stringify(body)
        };

        // Clear any previous error messages
        document.getElementById("error-message").textContent = "";

        // Fetch the JWT object from the Web API.
        fetch(url, options)
        .then(response => {
            // trap error response from Web API
            if (!response.ok) {
                const errorMsg = 'Login error: ' + response.status;
                console.log(errorMsg);
                document.getElementById("error-message").textContent = errorMsg;
                return;
            }
            // Success!!!
            // Redirect to Database presentation page
            window.location.href = redirect;
        })
        .catch(error => {
            // Handle any network errors.
            console.log('Possible CORS or service down error: ' + error);
            document.getElementById("error-message").textContent = 'Possible CORS or service down error: ' + error;
        });
    }


</script>
  • Login and redirect the user

Login Backend Code

This code illustrates how the Python backend handles a login request.

  • The provided Python code demonstrates the key components of building an endpoint.
  • The POST method demonstrates the process of validating and obtaining the user ID and password from the JSON request.
  • This section illustrates how to query the backend database to obtain a Python user object.
  • At various stages, if a check fails, an error code and message are provided.
  • Upon successful lookup of a user in the database, a cookie is generated.
    • This is returned to the frontend and becomes the foundation for being authenticated.
    • The generated cookie from the backend is saved in the browser and is associated with the web server. This association is crucial as it allows the server to recognize and authenticate the user in subsequent requests.
    • Note, it is generally best to avoid storing sensitive data in cookies.
# ... imports

# blueprint for user api endpoints
user_api = Blueprint('user_api', __name__,
                   url_prefix='/api/users')
api = Api(user_api)

# ... more resource code

# api resource class for security
class _Security(Resource):
    # method to authenticate user
    def post(self):
        try:
            body = request.get_json()
            if not body:
                return {
                    "message": "Please provide user details",
                    "data": None,
                    "error": "Bad request"
                }, 400
                
            ''' Get Request Data '''
            uid = body.get('uid')
            if uid is None:
                return {'message': f'User ID is missing'}, 401
            password = body.get('password')
            
            ''' Find User ID in Database '''
            user = User.query.filter_by(_uid=uid).first()
            if user is None or not user.is_password(password):
                return {'message': f"Invalid user id or password"}, 401
            if user:
                try:
                    # Add an expiration date to the JWT token
                    token = jwt.encode(
                        {"_uid": user._uid},
                        current_app.config["SECRET_KEY"],
                        algorithm="HS256"
                    )
                    resp = Response("Authentication for %s successful" % (user._uid))
                    resp.set_cookie("jwt", token,
                            max_age=3600,
                            secure=True,
                            httponly=True,
                            path='/',
                            samesite='None'  # This is the key part for cross-site requests
                            )
                    return resp
                except Exception as e:
                    return {
                        "error": "Something went wrong in cookie creation!",
                        "message": "Failed to generate JWT token: " + str(e)
                    }, 500
            return {
                "message": "Error fetching auth token!",
                "data": None,
                "error": "Unauthorized"
            }, 404
        except Exception as e:
            return {
                    "message": "Something went wrong in data processing!",
                    "error": str(e),
                    "data": None
            }, 500

            
    # building a RESTapi endpoint
    api.add_resource(_Security, '/authenticate')

<!-- HTML table layout for the page. The table is populated by the JavaScript code below. -->
<table>
  <thead>
  <tr>
    <th>Name</th>
    <th>ID</th>
    <th>Age</th>
  </tr>
  </thead>
  <tbody id="result">
    <!-- javascript generated data -->
  </tbody>
</table>

<!-- 
The JavaScript code below fetches user data from an API and displays it in the table. i
It uses the Fetch API to make a GET request to the '/api/users/' endpoint. 
The 'uri' variable and 'options' object are imported from the 'config.js' file.

The script executes sequentially when the page is loaded.
-->
<script type="module">
  // Import 'uri' variable and 'options' object from 'config.js'
  import { uri, options } from '/teacher_portfolio/assets/js/api/config.js';

  // Set the URL to the 'users' endpoint
  const url = uri + '/api/users/';

  // Get the HTML element where the results will be displayed
  const resultContainer = document.getElementById("result");

  // Make a GET request to the API
  fetch(url, options)
    // response is a RESTful "promise" on any successful fetch
    .then(response => {
      // If the response status is not 200, display an error message
      if (response.status !== 200) {
          const errorMsg = 'Database response error: ' + response.status;
          console.log(errorMsg);
          const tr = document.createElement("tr");
          const td = document.createElement("td");
          td.innerHTML = errorMsg;
          tr.appendChild(td);
          resultContainer.appendChild(tr);
          return;
      }
      // valid response will contain JSON data
      response.json().then(data => {
          console.log(data);
          for (const row of data) {
            // Create a new table row and cells for each piece of data
            // tr and td build out for each row
            const tr = document.createElement("tr");
            const name = document.createElement("td");
            const id = document.createElement("td");
            const age = document.createElement("td");
            // data is specific to the API
            name.innerHTML = row.name; 
            id.innerHTML = row.uid; 
            age.innerHTML = row.age; 
            // this builds td's into tr
            tr.appendChild(name);
            tr.appendChild(id);
            tr.appendChild(age);
            // Append the row to the table
            resultContainer.appendChild(tr);
          }
      })
  })
  // If the fetch request fails (e.g., due to network issues), display an error message
  .catch(err => {
    console.error(err);
    const tr = document.createElement("tr");
    const td = document.createElement("td");
    td.innerHTML = err + ": " + url;
    tr.appendChild(td);
    resultContainer.appendChild(tr);
  });
</script>

Accessing Data, backend code

The code snippets below illustrate a guarded GET method, where @token_required is the guard.

  • The GET method can fail due to the guard, most often resulting in ‘Unauthorized’ or ‘Forbidden’ responses.

  • The token_required method is the guarding function and handles failure possibilities.

  • If token_required is successful, it returns a user object from the database, which is based on the user ID in the token.

  • The GET method and authentication enable the logged-in user to query all users from the database. The user object returned from the database is not used in this method (self, _). The underscore is a convention indicating that it is not used.

- If the security policy of your application restricts users to only viewing their own data, the method would replace the underscore with ‘user’ and use it in the database query.

- The GET method returns the protected user list from the database as a result of authenticated access.
# from user.py file
# ... more import and blueprint code
class UserAPI:        
    class _CRUD(Resource):
        # ... more resource code omitted
        # The @token_required decorator ensures that the user is authenticated before they can access the data.
        @token_required()
        # The get method retrieves all users from the database.
        # The underscore (_) indicates that the current_user is not used in this method.
        def get(self, _): 
            users = User.query.all()    # Query all users from the database
            json_ready = [user.read() for user in users]  # Prepare the data for JSON output
            return jsonify(json_ready)  # Convert the data to a JSON response

    # Add the _CRUD resource to the API at the root endpoint ('/')
    api.add_resource(_CRUD, '/')


# from auth_middlewares.py file
# ... more import code
# The token_required function is a decorator that checks if the user is authenticated.
# If roles are provided, it also checks if the user has the required role.
def token_required(roles=None):
    def decorator(f):
        @wraps(f)
        def decorated(*args, **kwargs):
            token = request.cookies.get("jwt")  # Get the JWT token from the cookies
            # If the token is missing, return an error message
            if not token:
                return {
                    "message": "Authentication Token is missing!",
                    "data": None,
                    "error": "Unauthorized"
                }, 401
            try:
                # Decode the token and get the user's data
                data = jwt.decode(token, current_app.config["SECRET_KEY"], algorithms=["HS256"])
                current_user = User.query.filter_by(_uid=data["_uid"]).first()
                # If the user doesn't exist or doesn't have the required role, return an error message
                if current_user is None:
                    return {
                        "message": "Invalid Authentication token!",
                        "data": None,
                        "error": "Unauthorized"
                    }, 401

                # If the doesn't have the required role, return an error message
                if roles and current_user.role not in roles:
                    return {
                        "message": "Insufficient permissions. Required roles: {}".format(roles),
                        "data": None,
                        "error": "Forbidden"
                    }, 403

            # If there's an error (likely with the token), return an error message
            except Exception as e:
                return {
                    "message": "Something went wrong, likely with the token!",
                    "data": None,
                    "error": str(e)
                }, 500

            # If the user is authenticated and has the required role (if applicable), call the decorated function
            return f(current_user, *args, **kwargs)

        return decorated

    return decorator

Developer Operation (DevOps) for Deployment After creating a working Flask application, it can be deployed on an Ubuntu server. Here are the key steps to deployment using a publicly available tool chain.

AWS requirements. Create a server, assign a public IP, and set up a DNS record. This requires an AWS account and appropriate permissions to create and manage EC2 instances, Elastic IPs, and Route 53 records.

EC2. Launch an instance. AMI, pick Ubuntu and an image type to match your sizing needs. Create a key pair for SSH access in case of AWS console issues. The security group should allow inbound traffic for SSH (port 22), HTTP (port 80), and HTTPS (port 443). The security group can also allow additional access, such as Cockpit, to bypass school firewalls. Elastic IP. This is an optional step, but highly recommended, to ensure the IP address remains the same after an EC2 reboot. Otherwise, the server will require a Domain or Subdomain update after each reboot.

Route 53 Domain or Subdomain. Create a domain or subdomain. This is a prerequisite for mapping your Nginx configuration to a live server. Configuration files. Create configuration files and store them in GitHub for version control.

Docker. Create a Docker file and docker-compose.yml file. Builds the Python application. Mounts to perisistent volumes. The “instance” path is a standard location for SQLite.db database or file Uploads. Exposes a port to the running python application to system Nginx. Create an Nginx configuration, this will be moved from project directory to /etc/nginx/sites-available. Listens for DNS domain or sub domain request from the internet Reverse proxies the DNS request to the web application port exposed by Docker Allows access to the servers specified in its configuration, protecting applicaton to perform CORS only on specificied resources. Allows access to the HTTP methods specified in its configuration Python. The application manages settings in the “init.py”. Allows access to the servers and IP addresses in the configuration, enabling CORS to specified resources. Specifies location for database and uploads so they are outside of the Docker container, in the instance directory.

Sample Docker file

# Use the official Python 3.10 image from Docker Hub as the base image
FROM docker.io/python:3.10

# Set the working directory to the root of the Docker container
WORKDIR /

# Update the system packages and install Python3, pip, and git
RUN apt-get update && apt-get upgrade -y && \
    apt-get install -y python3 python3-pip git

# Copy the local files into the Docker container
COPY . /

# Install the Python dependencies specified in requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

# Install gunicorn, a WSGI HTTP server for Python web applications
RUN pip install gunicorn

# Set the command line arguments for gunicorn
# --workers=1: Use 1 worker process
# --bind=0.0.0.0:8086: Bind the server to all available network interfaces and use port 8086
ENV GUNICORN_CMD_ARGS="--workers=1 --bind=0.0.0.0:8086"

# Expose port 8086 to the host machine, allowing external access to the application
EXPOSE 8086

# Start the gunicorn server when the container is run, using the application defined in main.py
CMD [ "gunicorn", "main:app" ]

### Sample docker-compose.yml

# Specify the Docker Compose file version
version: '3'

# Define the services that make up your app
services:
    # The 'web' service is your Flask application
    web:
        # Name of the Docker image to be built for the 'web' service
        image: flask_portfolio_v1
        # Build the Dockerfile in the current directory
        build: .
        # Map port 8286 on the host to port 8086 in the container
        ports:
                - "8286:8086"
        # Mount the 'instance' directory from the host to '/instance' in the container
        # This allows the container to access the data in the 'instance' directory
        volumes:
                - ./instance:/instance
        # Automatically restart the container if it crashes, unless it was manually stopped
        restart: unless-stopped
# This block defines a server that listens on port 80
server {
    # Listen for incoming connections on port 80 for both IPv4 and IPv6
    listen 80;
    listen [::]:80;

    # DNS. The server_name directive 
    server_name flask2.nighthawkcodingsociety.com;

    # This block defines how to respond to requests for the root location ("/")
    location / {
        # Port. The proxy_pass directive sets the protocol and address of the proxied server
        proxy_pass http://localhost:8286;

        # This block handles preflighted requests (HTTP OPTIONS method)
        if ($request_method = OPTIONS) {
            # Cookies. This allows the browser to include credentials in the request
            add_header "Access-Control-Allow-Credentials" "true" always;

            # CORS. This specifies the cross origin that is allowed to access the resource
            add_header "Access-Control-Allow-Origin"  "https://nighthawkcoders.github.io" always;

            # Methods. This specifies the methods that are allowed when accessing the resource
            add_header "Access-Control-Allow-Methods" "GET, POST, PUT, OPTIONS, HEAD" always;

            # This specifies how long the results of a preflight request can be cached
            add_header "Access-Control-Allow-MaxAge" 600 always;

            # This specifies the headers that are allowed in the actual request
            add_header "Access-Control-Allow-Headers" "Authorization, Origin, X-Requested-With, Content-Type, Accept" always;

            # This indicates that the server has successfully fulfilled the request and there is no additional content to send in the response payload body
            return 204;
        }

    }
}

Sample init.py file

from flask import Flask
from flask_cors import CORS
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
import os

"""
These object can be used throughout project.
1.) Objects from this file can be included in many blueprints
2.) Isolating these object definitions avoids duplication and circular dependencies
"""

# Setup of key Flask object (app)
app = Flask(__name__)
# Allowed servers for cross-origin resource sharing (CORS)
cors = CORS(app, supports_credentials=True, origins=['http://localhost:4100', 'http://127.0.0.1:4100', 'http://127.0.0.1:8086', 'https://nighthawkcoders.github.io'])

# Secret key for session handling and CSRF protection
SECRET_KEY = os.environ.get('SECRET_KEY') or 'SECRET_KEY'
app.config['SECRET_KEY'] = SECRET_KEY

# Setup SQLAlchemy object and properties for the database (db)
# Local SQLite database within the instance folder
dbURI = 'sqlite:///volumes/sqlite.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = dbURI
db = SQLAlchemy()
Migrate(app, db)

# Images storage settings and location
app.config['MAX_CONTENT_LENGTH'] = 5 * 1024 * 1024  # maximum size of uploaded content
app.config['UPLOAD_EXTENSIONS'] = ['.jpg', '.png', '.gif']  # supported file types
app.config['UPLOAD_FOLDER'] = os.path.join(app.instance_path, 'uploads')
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)


Relationship of Full Stack and Deployment to College Board questions

How computer systems and networks facilitate the transfer of data?

  • Full Stack application: The frontend sends requests to the backend over the network, and the backend processes these requests and sends responses back to the frontend.
  • JavaScript Fetch, URI, Credentials, and Cookie: JavaScript fetch API is used for sending requests, and credentials and cookies are part of the HTTP protocol.

How information is transmitted on the Internet?

  • When you deploy your application: It becomes accessible on the Internet, users send requests to your server over the Internet, and your server sends responses back over the Internet.
  • Python, CORS, instance data, CSRF: These points relate to server-side processing of information.

Purpose of Internet protocols

  • Internet protocols like HTTP and HTTPS: Standardize how requests and responses are formatted and transmitted over the Internet.

SSL/TLS certificates

  • SSL/TLS certificates: Use public key cryptography to establish secure connections between clients and servers.

Relationship between Internet and World Wide Web

  • The Internet: Infrastructure allowing computers to connect and communicate.
  • The World Wide Web: A service operating over the Internet, what users interact with when using deployed applications.