Collections

  • Blog Python Model code and SQLite Database.
    • Below code is code for Design database, initiates columns, creates function for creating, reading from, updating, and deleting designs from database.
## Python Model
class Design(db.Model):
    __tablename__ = 'designs'

    name = db.Column(db.String, unique=False, primary_key=True)
    type = db.Column(db.Text, unique=False, nullable=False)
    content = db.Column(db.String, unique=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'))
    likes = db.Column(db.Integer, unique=False, nullable=False)
    dislikes = db.Column(db.Integer, unique=False, nullable=False)
    description = db.Column(db.String, unique=False, nullable=False)
    def __init__(self, id, type, content, name, likes=0, dislikes=0, description=""):
        self.userID = id
        self.type = type
        self.content = content
        self.name = name
        self.likes = likes
        self.dislikes = dislikes
        self.description = description

    # Returns a string representation of the Notes object, similar to java toString()
    # returns string
    def __repr__(self):
        return {"Name": self.name, "Content": self.content, "Type": self.type, "Owner": self.userID, "Likes": self.likes, "Dislikes": self.dislikes, "Description": self.description,}

    # 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):
        
        return {
            "Name": self.name,
            "Owner": self.userID,
            "Content": self.content,
            "Type": self.type,
            "Likes": self.likes,
            "Dislikes": self.dislikes,
            "Description": self.description,
        }
    
    def update(self, name="", content="", type="", likes=0, dislikes=0, description=''):
        """only updates values with length"""
        if len(name) > 0:
            self.name = name
        if len(content) > 0:
            self.content = content
        if len(type) > 0:
            self.type = type
        if likes != 0:
            self.likes += likes
        if dislikes != 0:
            self.dislikes += dislikes
        if len(description) != 0:
            self.description = description
        db.session.commit()
        return self
    
    def delete(self):
        db.session.delete(self)
        db.session.commit()
        return None

  • From VSCode using SQLite3 Editor, show your unique collection/table in database, display rows and columns in the table of the SQLite database.
    • Screenshot 1
    • Database has 5 columns with information regarding designs
      • Design Name
      • Design Content in HTML, this is displayed on frontend
      • Design Description
      • User who owns design, this is linked to the user database, and automatically filled
      • Design likes and dislikes, stored as integers
  • From VSCode model, show your unique code that was created to initialize table and create test data.
    • See Code Below
    • Code initializes three users, two default ones as requested by teacher, and an admin account for personal use.
    • Design databases starts off as empty, data entries are created upon use of website
# User Initialization Code
# 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='Bobby', uid='1bob23', password='b0b765', type="admin")
        u2 = User(name='John Doe', uid='jdo', password='1cls#cc!@', type="standard")
        u3 = User(name='Admin_User', uid='admin', type="admin")
        users = [u1, u2, u3]

        """Builds sample user/note(s) data"""
        for user in users:
            try:
                '''add a few 1 to 4 notes per user'''
                '''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}")

Lists and Dictionaries

Blog Python API code and use of List and Dictionaries.

  • In VSCode using Debugger, show a list as extracted from database as Python objects.
    • GET request is sent to backend to search for all public designs. Backend fetches all public designs into a list in python debugger called design_return (red line). List contains all designs as python objects (red line). This is also an example of a dictionary stored as each element of the list is a dictionary of data from database. Screenshot 2
  • In VSCode use Debugger and list, show two distinct example examples of dictionaries, show Keys/Values using debugger.
    • After design is fetched, it is stored in python as a dictionary. This is viewable from the debugger as shown below. The purple values on the left are the keys, the values are shown on the right. Screenshot 3
    • When a design is created, the frontend sends an object with data regarding the design, such as its name and description. The backend then stores this data as a dictionary with keys in purple on the left and values on the right.

APIs and JSON

Blog Python API code and use of Postman to request and respond with 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.
    • Within the code shown above, the API contains several CRUDs, such as a CRUD for modifying users and one for modifying Designs.
    • A resource is then added to the API under the appropriate link.
    • When a request is sent to the link, the appropriate function is called according to the type of request send.
user_api = Blueprint('user_api', __name__,
                   url_prefix='/api/users')

# API docs https://flask-restful.readthedocs.io/en/latest/api.html
api = Api(user_api)    

class UserAPI:        
    class _CRUD(Resource):  # User API operation for Create, Read.  THe Update, Delete methods need to be implemeented
        # more code omitted
    
api.add_resource(_CRUD, '/')
api.add_resource(_DesignCRUD, '/design')
  • In VSCode, show algorithmic conditions used to validate data on a POST condition.
    • Algorithmic conditions ensure that inputted data is valid. The following two conditions are part of the user creation code. They ensure that the password is secure by ensuring that it is longer than a certain length, and ensure that a Name and password exists.
            if name is None or len(name) < 2:
                return {'message': f'Name is missing, or is less than 2 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
            
  • In Postman, show URL request and Body requirements for GET, POST, and UPDATE methods.
  • In Postman, show the JSON response data for 200 success conditions on GET, POST, and UPDATE methods. Screenshot 5 Screenshot 6 Screenshot 7
  • In Postman, show the JSON response for error for 400 when missing body on a POST request. Screenshot 8 In Postman, show the JSON response for error for 404 when providing an unknown user ID to a UPDATE request.
  • Updated user api code automatically retrieves user ID from JWT token by decoding it.
  • This means that no UserID needs to be provided, simply a name and password
  • This also ensures that users can only update their own user
  • See line below
cur_user = jwt.decode(token, current_app.config["SECRET_KEY"], algorithms=["HS256"])['_uid']

Frontend

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

  • In Chrome inspect, show response of JSON objects from fetch of GET, POST, and UPDATE methods.
    • After a search is done, a GET request is sent to the backend. The backend then fetches appropriate designs from the database as shown above. The data is then sent to the frontend as shown below. Screenshot 9
  • In the Chrome browser, show a demo (GET) of obtaining an Array of JSON objects that are formatted into the browsers screen.
    • This array of designs is then formatted into a table Screenshot 10
  • In JavaScript code, describe fetch and method that obtained the Array of JSON objects.
    • The following fetch gets all public designs from the database by sending a GET request (default) to the search URL.
    • It verifies the data that is returned from the backend, then begins displaying the data inside a table.
function getPublic() {
    fetch('http://127.0.0.1:8086/api/users/search')
      .then(response => {
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        return response.json();
      })
      .then(data => {
        console.log(data); // Handle the data returned from the server
        displayDataInTable(data.Designs);
      })
      .catch(error => {
        console.error('There was a problem with the fetch operation:', error);
      });
  }
  • In JavaScript code, show code that performs iteration and formatting of data into HTML.
    • The following function gets the container of the table, then creates a new string with HTML to append
    • It adds a header row to the table, then iterates through each element of the design data, checking if the content or name includes the search term. If so, it adds a new row to the database with the design’s information.
function displayDataInTable(data) {
  // get table containers
    var tableContainer = document.getElementById('tableContainer');
    var tableHTML = '<table id="table">';
    const searchTerm = getSearchTerm();
    tableHTML += '<tr><th>Name</th><th>Content</th><th>Description</th><th>Likes</th><th>Dislikes</th><th>Type</th></tr>';
    data.forEach(function (item) {
      // Check if searchTerm is a substring of Name or Content
      if (searchTerm && (item.Name.includes(searchTerm) || (item.Content && item.Content.includes(searchTerm)))) {
        tableHTML += '<tr>';
        tableHTML += '<td class="nr">' + item.Name + '</td>';
        tableHTML += '<td>' + (item.Content || '') + '</td>';
        tableHTML += '<td>' + item.Description + '</td>';
        // set value of table, create like button
        // buttons gets value of textcontent in row through searching, runs function to like
        tableHTML += '<td><button onclick="toggleLike(this, this.closest(`tr`).querySelector(`.nr`).textContent)" data-item-id="' + item.id + '">Like</button><span class="likesCount">' + item.Likes + '</span></td>';
        tableHTML += '<td><button onclick="toggleDislike(this, this.closest(`tr`).querySelector(`.nr`).textContent)" data-item-id="' + item.id + '">Dislike</button><span class="dislikesCount">' + item.Dislikes + '</span></td>';
        tableHTML += '<td>' + item.Type + '</td>';
        tableHTML += '</tr>';
      }
    });
    tableHTML += '</table>';
    tableContainer.innerHTML = tableHTML;
  }
  • In the Chrome browser, show a demo (POST or UPDATE) gathering and sending input and receiving a response that show update. Repeat this demo showing both success and failure.
    • Success of PUT request: Screenshot 11
    • Failure of POST request: Screenshot 12
  • In JavaScript code, show and describe code that handles success. Describe how code shows success to the user in the Chrome Browser screen.
    • The following code verifies that the user is logged in, the response is OK, and the design is entered
    • It then displays a success alert to the user, and sets the display attribute of the popup to none, allowing the user to access the button editor.
  • 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 user is not logged in, it opens up the login window and sends a alert telling the user to log in.
    • If the user has not inputted a design name, the code does not fetch at all and alerts the user of this.
if (name1.trim() !== "") {
    const authOptions = {
      mode: 'cors', // no-cors, *cors, same-origin
      credentials: 'include', // include, same-origin, omit
      headers: {
        'Content-Type': 'application/json',
      },
      method: 'POST', // Override the method property
      cache: 'no-cache', // Set the cache property
      body: JSON.stringify(body)
    };
    console.log(body)
    fetch(url, authOptions)
      .then(response => {
        // handle error response from Web API
        if (!response.ok) {
          alert("Please Login First!")
          CloseWindow("buttoneditor")
          TaskbarIconClick("settings")
        }
        document.getElementById("popup").style.display = "none";
        alert("Design Created Successfully!")
      })
      // catch fetch errors (ie ACCESS to server blocked)
      .catch(err => {
        console.log(body);
        console.error(err);
      });
  } else {
    alert("Please enter a design name!");
  }

Algorithm Analysis

In the ML projects, there is a great deal of algorithm analysis. Think about preparing data and predictions.

  • Show algorithms and preparation of data for analysis. This includes cleaning, encoding, and one-hot encoding.
    • Below code demonstrates data cleaning in titanic ML project
    • Garbage In, Garbage Out, if bad data is fed in bad data will come out therefore we need to clean data and remove bad datapoint
    • Encoding: data may come in different forms, i.e. 1, male, female, we need to turn these all into numbers so that model can function, model functions only with numbers, does not work well with other data types.
    def _preprocess_data(self):
        images = []
        labels = []
        max_images_per_place = 12
        ## gets data, first iterates through the path of the dataset provided.
        for location_folder in os.listdir(self.dataset_path):
            ## generates full path from root by combining base with folder
            location_folder_path = os.path.join(self.dataset_path, location_folder)
            if os.path.isdir(location_folder_path):
                count = 0
                ## selects every file from the image, up to the specific max
                for image_file in os.listdir(location_folder_path):
                    if count >= max_images_per_place:
                        break
                    image_path = os.path.join(location_folder_path, image_file)
                    ## opens each image path as a PIL image
                    image = self.data_transforms(Image.open(image_path))
                    ## adds image to image list
                    images.append(image)
                    ## adds to label as one of categories that shall be classifird
                    labels.append(location_folder)
                    count += 1

        ## section below initializes data and prepares for model
        label_counter = Counter(labels)
        self.label_vocab = {label: i for i, label in enumerate(label_counter)}
        labels = [self.label_vocab[label] for label in labels]

        images = torch.stack(images)
        labels = torch.tensor(labels)

        self.image_dataset = torch.utils.data.TensorDataset(images, labels)
        self.data_loader = DataLoader(self.image_dataset, batch_size=self.batch_size, shuffle=True)
  • Show algorithms and preparation for predictions.
    • Functions below use decision tree classifier and linear regression to train model
    • First function trains model, second one transforms inputted dataset to a data frame array, and then runs a prediction using the previously trained model
def train_model(self, num_epochs=1):
        ## process data, set initial variables
        self._preprocess_data()
        self._initialize_model()

        ## specific optimizer and criteria using torch
        criterion = nn.CrossEntropyLoss()
        optimizer = optim.SGD(self.model.parameters(), lr=0.001, momentum=0.9)

        ## train model
        self.model.train()
        ## determines loss for each epoch, loss should be decreasing with each epoch
        for epoch in range(num_epochs):
            running_loss = 0.0
            for inputs, labels in self.data_loader:
                optimizer.zero_grad()
                outputs = self.model(inputs)
                loss = criterion(outputs, labels)
                loss.backward()
                optimizer.step()
                running_loss += loss.item() * inputs.size(0)
            ## determine loss for entire epoch, divides loss by length of dataset
            epoch_loss = running_loss / len(self.image_dataset)
            ## display epoch and loss
            print(f"Epoch {epoch+1}/{num_epochs}, Loss: {epoch_loss:.4f}")

def predict_image_class(self, image):
    image = self.data_transforms(image).unsqueeze(0)
    self.model.eval()
    with torch.no_grad():
        output = self.model(image)
        _, predicted = torch.max(output, 1)
        # Convert predicted to a standard Python datatype
        predicted_item = predicted.item()
        return predicted_item
  • Discuss concepts and understanding of Linear Regression algorithms.
  • Linear regression is a way of making predictions, compares inputs and outputs and finds a “line” that best fit
  • Uses the line of best fit to make future prediction
  • Reason why data needs to be encoded and all data in number form
  • Image 1
  • Discuss concepts and understanding of Decision Tree analysis algorithms.
  • Decision tree involves terminal nodes, or inputs
  • Decision tree has decision nodes that make decisions based upon terminal nodes and inputs. These nodes make another output based upon inputs and send it out. All nodes eventually converge into a root node that has final decision of tree
  • Image 2