Collections

This image shows my sqlite file, specifically the posts, and information stored in it. Since my feature is about users being able to ask questions and have other users respond to them, this database shows the questions asked by users and the responses to those questions.

This image shows my sqlite file, specifically the users, and the information stored in it. This database shows all the users that have signed up for my website.

""" database dependencies to support sqliteDB examples """
from random import randrange
from datetime import date
import os, base64
import json

from __init__ import app, db
from sqlalchemy.exc import IntegrityError
from werkzeug.security import generate_password_hash, check_password_hash


''' Tutorial: https://www.sqlalchemy.org/library.html#tutorials, try to get into Python shell and follow along '''

# Define the Post class to manage actions in 'posts' table,  with a relationship to 'users' table
class Post(db.Model):
    __tablename__ = 'posts'

    # Define the Notes schema
    id = db.Column(db.Integer, primary_key=True)
    note = db.Column(db.Text, unique=False, nullable=False)
    doq = db.Column(db.Date, unique=False)
    parentPostId = db.Column(db.Integer,  unique=False, nullable=False)

    # Define a relationship in Notes Schema to userID who originates the note, many-to-one (many notes to one user)
    userID = db.Column(db.Integer, db.ForeignKey('users.id'))

    # Constructor of a Notes object, initializes of instance variables within object
    def __init__(self, id, note, doq, parentPostId):
        self.userID = id
        self.note = note
        self.doq = doq
        self.parentPostId = parentPostId


    # Returns a string representation of the Notes object, similar to java toString()
    # returns string
    def __repr__(self):
        return "Notes(" + str(self.id) + "," + self.note + "," + str(self.userID) + ")"

    # CRUD create, adds a new record to the Notes table
    # returns the object added or None in case of an error
    def create(self):
        try:
            # creates a Notes object from Notes(db.Model) class, passes initializers
            db.session.add(self)  # add prepares to persist person object to Notes table
            db.session.commit()  # SqlAlchemy "unit of work pattern" requires a manual commit
            return self
        except IntegrityError:
            db.session.remove()
            return None

    # CRUD read, returns dictionary representation of Notes object
    # returns dictionary
    def read(self):
        # encode image
        #path = app.config['UPLOAD_FOLDER']
        #file = os.path.join(path, self.image)
        #file_text = open(file, 'rb')
        #file_read = file_text.read()
        #file_encode = base64.encodebytes(file_read)
        
        return {
            "id": self.id,
            "userID": self.userID,
            "note": self.note,
            "doq": self.doq,
            "parentPostId": self.parentPostId
            #"base64": str(file_encode)
        }


# Define the User class to manage actions in the 'users' table
# -- Object Relational Mapping (ORM) is the key concept of SQLAlchemy
# -- a.) db.Model is like an inner layer of the onion in ORM
# -- b.) User represents data we want to store, something that is built on db.Model
# -- c.) SQLAlchemy ORM is layer on top of SQLAlchemy Core, then SQLAlchemy engine, SQL
class User(db.Model):
    __tablename__ = 'users'  # table name is plural, class name is singular

    # Define the User schema with "vars" from object
    id = db.Column(db.Integer, primary_key=True)
    _name = db.Column(db.String(255), unique=False, nullable=False)
    _uid = db.Column(db.String(255), unique=True, nullable=False)
    _password = db.Column(db.String(255), unique=False, nullable=False)
    _dob = db.Column(db.Date)
    
    # Defines a relationship between User record and Notes table, one-to-many (one user to many notes)
    posts = db.relationship("Post", cascade='all, delete', backref='users', lazy=True)

    # constructor of a User object, initializes the instance variables within object (self)
    def __init__(self, name, uid, password="123qwerty", dob=date.today()):
        self._name = name    # variables with self prefix become part of the object, 
        self._uid = uid
        self.set_password(password)
        self._dob = dob

    # a name getter method, extracts name from object
    @property
    def name(self):
        return self._name
    
    # a setter function, allows name to be updated after initial object creation
    @name.setter
    def name(self, name):
        self._name = name
    
    # a getter method, extracts email from object
    @property
    def uid(self):
        return self._uid
    
    # a setter function, allows name to be updated after initial object creation
    @uid.setter
    def uid(self, uid):
        self._uid = uid
        
    # check if uid parameter matches user id in object, return boolean
    def is_uid(self, uid):
        return self._uid == uid
    
    @property
    def password(self):
        return self._password[0:10] + "..." # because of security only show 1st characters

    # update password, this is conventional setter
    def set_password(self, password):
        """Create a hashed password."""
        self._password = generate_password_hash(password, "pbkdf2:sha256", salt_length=10)

    # check password parameter versus stored/encrypted password
    def is_password(self, password):
        """Check against hashed password."""
        result = check_password_hash(self._password, password)
        return result
    
    # dob property is returned as string, to avoid unfriendly outcomes
    @property
    def dob(self):
        dob_string = self._dob.strftime('%m-%d-%Y')
        return dob_string
    
    # dob should be have verification for type date
    @dob.setter
    def dob(self, dob):
        self._dob = dob
    
    @property
    def age(self):
        today = date.today()
        return today.year - self._dob.year - ((today.month, today.day) < (self._dob.month, self._dob.day))
    
    # output content using str(object) in human readable form, uses getter
    # output content using json dumps, this is ready for API response
    def __str__(self):
        return json.dumps(self.read())

    # CRUD create/add a new record to the table
    # returns self or None on error
    def create(self):
        try:
            # creates a person object from User(db.Model) class, passes initializers
            db.session.add(self)  # add prepares to persist person object to Users table
            db.session.commit()  # SqlAlchemy "unit of work pattern" requires a manual commit
            return self
        except IntegrityError:
            db.session.remove()
            return None

    # CRUD read converts self to dictionary
    # returns dictionary
    def read(self):
        return {
            "id": self.id,
            "name": self.name,
            "uid": self.uid,
            "dob": self.dob,
            "age": self.age,
            "posts": [post.read() for post in self.posts]
        }

    # CRUD update: updates user name, password, phone
    # returns self
    def update(self, name="", uid="", password=""):
        """only updates values with length"""
        if len(name) > 0:
            self.name = name
        if len(uid) > 0:
            self.uid = uid
        if len(password) > 0:
            self.set_password(password)
        db.session.commit()
        return self

    # CRUD delete: remove self
    # None
    def delete(self):
        db.session.delete(self)
        db.session.commit()
        return None


"""Database Creation and Testing """


# Builds working data for testing
def initUsers():
    with app.app_context():
        """Create database and tables"""
        db.create_all()
        """Tester data for table"""
        u1 = User(name='Thomas Edison', uid='toby', password='123toby', dob=date(1847, 2, 11))
        u2 = User(name='Nicholas Tesla', uid='niko', password='123niko', dob=date(1856, 7, 10))
        u3 = User(name='Alexander Graham Bell', uid='lex')
        u4 = User(name='Grace Hopper', uid='hop', password='123hop', dob=date(1906, 12, 9))
        users = [u1, u2, u3, u4]

        """Builds sample user/note(s) data"""
        for user in users:
            try:
                '''add a few 1 to 4 notes per user'''
                for num in range(randrange(1, 4)):
                    note = "#### " + user.name + " note " + str(num) + ". \n Generated by test data."
                    user.posts.append(Post(id=user.id, note=note, doq=date.today(), parentPostId=1))
                '''add user/post data to table'''
                user.create()
            except IntegrityError:
                '''fails with bad or duplicate data'''
                db.session.remove()
                print(f"Records exist, duplicate email, or error: {user.uid}")
            

This code is from my users file. This Python script sets up a SQLite database using Flask and SQLAlchemy to mimic a social media platform. It defines two main classes: User and Post, which correspond to tables in the database. The User class holds user details like name, user ID, password, and birthdate, with methods for adding, updating, and deleting users. Similarly, the Post class represents individual posts, containing content, creation date, and parent post ID. The initUsers() function is responsible for initializing the database, creating tables, and populating them with sample user data along with associated posts. This setup enables developers to test user and post management features in the application.

List and Dictionaries

def get(self): # Read Method
            uid = request.args.get('uid')
            if uid:
                # Query the post by id
                user = User.query.filter_by(_uid=uid).first()
                if user:
                    return jsonify(user.read())  # Assuming 'post.read()' method returns a JSON-serializable dict
                else:
                    return jsonify({"message": "No user found with the username " + str(uid)})
            users = User.query.all()    
            json_ready = [user.read() for user in users]  
            return jsonify(json_ready) 

List and dictionaries when extracted from database.

This image shows the python object.

This image shows the output when I debug on the frontend of my website.

APIs and JSON

def post(self): # Create method
            ''' Read data for json body '''
            body = request.get_json()
            
            ''' Avoid garbage in, error checking '''
            # validate name
            note = body.get('note')
            if note is None or len(note) < 10:
                return {'message': f'Note is missing, or is less than 10 characters'}, 400
            # validate uid
            uid = body.get('uid')
            if uid is None or len(uid) < 2:
                return {'message': f'User ID is missing, or is less than 2 characters'}, 400
            # look for password and dob
            #doq = date of question
            doq = body.get('doq')
            parentPostId = body.get('parentPostId')
            ''' #1: Key code block, setup USER OBJECT '''
            uo = Post(id=uid, note= note, doq= doq, parentPostId=parentPostId )
                
            ''' Additional garbage error checking '''
            # convert to date type
            if doq is not None:
                try:
                    uo.doq = datetime.strptime(doq, '%Y-%m-%d').date()
                except:
                    return {'message': f'Date of birth format error {doq}, must be mm-dd-yyyy'}, 400
            
            ''' #2: Key Code block to add user to database '''
            # create user in database
            question = uo.create()
            # success returns json of user
            if question:
                return jsonify(question.read())
            # failure returns error
            return {'message': f'Processed {question}, either a format error or User ID {uid} is duplicate'}, 400

This code is for handling a “POST” request in a Python API. It first checks if the request contains the required data, like a note and user ID. If everything is okay, it creates a new entry in the database with this data. If successful, it returns the newly created entry. If there’s an issue, like missing data or a duplicate user ID, it returns an error message instead. It’s like a form where you fill in some details, and when you submit it, it saves the information you provided. If something’s wrong with what you filled out, it tells you so you can fix it.

if uid is None or len(uid) < 2:
                return {'message': f'User ID is missing, or is less than 2 characters'}, 400
            # look for password and dob
            #doq = date of question
            doq = body.get('doq')
            parentPostId = body.get('parentPostId')
            ''' #1: Key code block, setup USER OBJECT '''
            uo = Post(id=uid, note= note, doq= doq, parentPostId=parentPostId )
                
            ''' Additional garbage error checking '''
            # convert to date type
            if doq is not None:
                try:
                    uo.doq = datetime.strptime(doq, '%Y-%m-%d').date()
                except:
                    return {'message': f'Date of birth format error {doq}, must be mm-dd-yyyy'}, 400

This code snippet first checks if the user ID (uid) is either missing or less than 2 characters long. If it is, it returns an error message indicating that the user ID is invalid. Then, it retrieves additional data like the date of question (doq) and parent post ID from the request body. After that, it sets up a new Post object with the provided data. It also performs additional error checking, such as converting the date format. If there’s an error during this process, it returns an error message indicating the issue. Overall, this code ensures that the data provided in the POST request is valid and properly formatted before proceeding with creating a new post object.

These images shows the URL endpoints for performing GET, POST, and UPDATE requests, along with the required parameters/body content needed for each request. It helps users understand how to structure their requests correctly to interact with the API.

These images display the JSON response data returned by the API when the request is successful (status code 200) for GET, POST, and UPDATE methods. It provides insight into the format and content of the response that users can expect when their requests are processed successfully.

This image showcases the JSON response returned by the API when the request fails due to a missing body in a POST request (status code 400). It informs users about the specific error that occurred and potentially provides guidance on how to resolve the issue, such as including the required body content.

This image illustrates the JSON response generated by the API when the requested resource (user ID) is not found, resulting in a 404 error status code. It informs users that the provided user ID does not exist in the system and prompts them to verify the correctness of the ID they are attempting to update.

Frontend

This image shows my post, get, and update methods. This image shows the user posting a question and searching a question and displays the questions that has been searched.

This image shows my code obtaining an array of JSON objects that are formatted into my screen. When the user searches the word what, two questions come up,”what is 2+2” and “what is a integral?”

function topicSearch() {
    const enteredTopic = document.getElementById("search-input").value;

    var myHeaders = new Headers();
    myHeaders.append("Content-Type", "application/json");
    
    var requestOptions = {
        method: 'GET',
        headers: myHeaders,
        redirect: 'follow'
    };

    fetch("http://127.0.0.1:8181/api/post/?searchString=" + enteredTopic, requestOptions)
        .then(response => {
            if (response.ok) {
                console.log(enteredTopic + " has been searched");
                return response.json(); // Parse the JSON in the response body
            } else {
                console.error("Search failed");
                const errorMessageDiv = document.getElementById('errorMessage');
                errorMessageDiv.innerHTML = '<label style="color: red;">Search Failed</label>';
                throw new Error('Search failed');
            }
        })
        .then(data => {
            // Here 'data' is the parsed JSON object from the response body
            console.log(data); // You can see your fetched data here
            createTableFromJSON(data); // Assuming 'createTableFromJSON' expects the JSON data as parameter
        })
        .catch(error => {
            // Handle any errors that occurred during the fetch() or in the promise chain
            console.error('Error:', error);
        });
}

This JavaScript function performs a topic search by sending a GET request to a specified API endpoint with the entered topic as a query parameter. If the response is successful, it parses the JSON data and displays it, typically in the form of a table. However, if the search fails, it logs an error message and displays a corresponding error message on the webpage.

function createTableFromJSON(jsonData) {
    // Create a table
    var table = document.createElement("table");
    var tr = table.insertRow(-1); // Table Row

    // Add table headers
    var headers = ["Questions"];
    for(var i = 0; i < headers.length; i++) {
        var th = document.createElement("th"); // Table Header
        th.innerHTML = headers[i];
        tr.appendChild(th);
    }

    // Add JSON data to the table as rows
    for(var i = 0; i < jsonData.length; i++) {
        tr = table.insertRow(-1);
        for(var j = 0; j < headers.length; j++) {
            var tabCell = tr.insertCell(-1);
            tabCell.innerHTML = "<a href='question?questionId="+jsonData[i]['id']+"'>"+jsonData[i]['note']+"</a>";
        }
    }

    // Finally, add the newly created table with JSON data to a container
    var divContainer = document.getElementById("tableContainer");
    divContainer.innerHTML = "";
    divContainer.appendChild(table);
}

In this code snippet, the createTableFromJSON function iterates over the jsonData array, which presumably contains JSON objects representing questions. It dynamically creates HTML table rows (<tr>) and cells (<td>) based on the data in each JSON object. The iteration is done using for loops to go through each element in the array and populate the table with the question data.

fetch("http://127.0.0.1:8181/api/post/", requestOptions)
    .then(response => {
        if (response.ok) {
            console.log("Question Received");
            alert("Question has been sent, you will receive a response soon.");
          } else {
            console.error("Question creation failed");
            // You can handle failed login attempts here
            const errorMessageDiv = document.getElementById('errorMessage');
            errorMessageDiv.innerHTML = '<label style="color: red;">Question Creation Failed</label>';
          }
      })
      .then(result => { 
        console.log(result);
        
        })
      .catch(error => console.log('error', error)); 

In the post_api function, when a successful response is received from the server, the code logs a success message to the console using console.log(“Question Received”);. Additionally, an alert is displayed to the user using alert(“Question has been sent, you will receive a response soon.”);. This alert informs the user that their question has been successfully sent and prompts them to expect a response. This feedback mechanism provides a clear indication to the user that their action was successful.

fetch("http://127.0.0.1:8181/api/post/", requestOptions)
    .then(response => {
        if (response.ok) {
            console.log("Question Received");
            alert("Question has been sent, you will receive a response soon.");
          } else {
            console.error("Question creation failed");
            // You can handle failed login attempts here
            const errorMessageDiv = document.getElementById('errorMessage');
            errorMessageDiv.innerHTML = '<label style="color: red;">Question Creation Failed</label>';
          }
      })
      .then(result => { 
        console.log(result);
        
        })
      .catch(error => console.log('error', error)); 

In the same post_api function, if the response from the server is not successful, the code logs an error message to the console using console.error(“Question creation failed”);. Additionally, it updates the HTML content of an element with the id errorMessage to display a red-colored label indicating that the question creation has failed. This is done using the line errorMessageDiv.innerHTML = ‘’;. By updating the HTML content dynamically, the code visually notifies the user of the failure, allowing them to understand that something went wrong with their action.

Optional/Extra, Algorithm Analysis

from flask import Flask, request, jsonify
from flask import Blueprint
from flask_restful import Api, Resource
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

# Load the heart disease dataset
heart_disease_data = pd.read_csv('/home/jared/vscode/JaredsBlogBE/data/DatasetHeartDisease.csv')  # Update path to your heart disease dataset

# Clean the data (if necessary)
# For simplicity, assume the data is already clean

# Split data into features (X) and target variable (y)
X = heart_disease_data[['age', 'sex', 'chest pain type', 'resting bps', 'cholesterol', 'fasting blood sugar',
                        'resting ecg', 'max heart rate', 'exercise angina', 'oldpeak', 'ST slope']]
y = heart_disease_data['target']

# Split data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Standardize the features
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# Train the logistic regression model
logreg = LogisticRegression(max_iter=1000)
logreg.fit(X_train_scaled, y_train)

# Create Blueprint for Heart Disease API
heart_disease_api = Blueprint('heart_disease_api', __name__, url_prefix='/api/heart_disease')
api = Api(heart_disease_api)

class HeartDiseaseAPI(Resource):
    def predict_heart_disease(self, data):
        try:
            # Prepare data for prediction
            input_data = pd.DataFrame([data])
            input_data_scaled = scaler.transform(input_data)

            # Predict the outcome
            prediction = logreg.predict(input_data_scaled)

            return {'Prediction': int(prediction[0])}  # Assuming 1 for heart disease, 0 for no heart disease

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

    def post(self):
        try:
            data = request.json
            result = self.predict_heart_disease(data)
            return jsonify(result)
        except Exception as e:
            return jsonify({'error': str(e)})

api.add_resource(HeartDiseaseAPI, '/predict')

This Flask API code sets up a system to predict whether someone has heart disease using logistic regression. It starts by loading and splitting the heart disease dataset into features and outcomes. These features are then standardized for consistent analysis. The logistic regression model is trained on the standardized features, learning how to predict the likelihood of heart disease based on certain factors. When a prediction is needed, the API’s method prepares the input data for the model by scaling it similarly to the training data. Though it doesn’t directly cover decision tree analysis, it gives a good insight into how logistic regression works, which is often used for similar predictions. This code shows the steps involved in building and using a predictive model within a Flask API, making it easier to understand the process.