Data Structures review • 37 min read

Description

Data Structures review

Collections

  • Blog Python Model code and SQLite Database.

  • The ID column is a numeric way of keeping track of users
  • UID is used in the login functions to recognize the correct user
  • The passwords are encrypted and are how you log in
  • The Score is the variable connected to my game and is updated as you progress.
  • Builds working data for testing. This code defines and creates a set of tester users.
def __init__(self, name, uid, password="123qwerty", dob=date.today(), hashmap={}, role="User", score=0):
        self._name = name    # variables with self prefix become part of the object, 
        self._uid = uid
        self.set_password(password)
        self._dob = dob
        self._hashmap = hashmap
        self._role = role
        self._score= score


__tablename__ = 'players'  # table name is plural, class name is singular

    # Define the Player 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)
    _tokens = db.Column(db.Integer)    

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

    # 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 tokens(self):
        return self._tokens
    
    # dob should be have verification for type date
    @tokens.setter
    def tokens(self, tokens):
        self._tokens = tokens
    
    
    # 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 player object from Player(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,
            "tokens": self.tokens,
            "password": self._password
        }

    # CRUD update: updates name, uid, password, tokens
    # returns self
    def update(self, dictionary):
        """only updates values in dictionary with length"""
        for key in dictionary:
            if key == "name":
                self.name = dictionary[key]
            if key == "uid":
                self.uid = dictionary[key]
            if key == "password":
                self.set_password(dictionary[key])
            if key == "tokens":
                self.tokens = dictionary[key]
        db.session.commit()
        return self

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


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), score=40, workout="none", diet="none")
        u2 = User(name='Nicholas Tesla', uid='niko', password='123niko', dob=date(1856, 7, 10), score =12, workout="none", diet="none")
        u3 = User(name='Alexander Graham Bell', uid='lex', score=4, workout="none", diet="none")
        u4 = User(name='Grace Hopper', uid='hop', password='123hop', dob=date(1906, 12, 9),score=6, workout="none", diet="none")
        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, image='ncs_logo.png',score=user.score))
                '''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 returns a list of every single user and their score. returns it in a user data table.

  • In VSCode use Debugger and list, show two distinct example examples of dictionaries, show Keys/Values using debugger. When you’re logged in, you can view a list of every single attribute of the users.

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.

  • 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.

  • 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.

  • This code creates the path to the CRUD methods

    api.add_resource(_CRUD, ‘/’) api.add_resource(_DesignCRUD, ‘/design’)”

  • In VSCode, show algorithmic conditions used to validate data on a POST condition.

    • This confirms that the data/user you want to pass actually 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.

    Post Postman

    Get Postman

    put Postman

  • In Postman, show the JSON response for error for 400 when missing body on a POST request.

    Error

  • In Postman, show the JSON response for error for 404 when providing an unknown user ID to a UPDATE request.
cur_user = jwt.decode(token, current_app.config["SECRET_KEY"], algorithms=["HS256"])['_uid']

  • 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.
  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

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.

    Get.

  • The get method is used to fetch the database of users from the backend. Whenever the user is logged into, the get method is called and the leaderboard is fetched. The GET method also sorts the JSON file before returning it so it can function as a true leaderboard.

put,

  • sends the users score into the backend. Every time the user clicks the cookie, it sends returns a payload to the backend with the updated score value.
  • post,

  • logs the user into the game.
  • In the Chrome browser, show a demo (GET) of obtaining an Array of JSON objects that are formatted into the browsers screen.
  • 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.

  • In JavaScript code, describe fetch and method that obtained the Array of JSON objects.
  • After fetching the JSON, it will display the array in a table that shows each users name and score.
// Fetch data from the provided URL with the given options
fetch(url, options)
    .then(response => {
        // Check if the response is not ok (status code outside the range 200-299)
        if (!response.ok) {
            // If not ok, throw an error with a message containing the status text
            throw new Error('Network response was not ok: ' + response.statusText);
        }
        // If response is ok, parse it as JSON
        return response.json();
    })
    .then(data => {
        // Clear the table before inserting new data
        resultContainer.innerHTML = '';

        // Iterate through each user object in the fetched data
        data.forEach(user => {
            // Create a table row element
            const tr = document.createElement("tr");
            // Create table data elements for name, uid, and score
            const nameTd = document.createElement("td");
            const idTd = document.createElement("td");
            const scoreTd = document.createElement("td");

            // Set the text content of nameTd to the user's name
            nameTd.textContent = user.name;
            // Set the text content of idTd to the user's uid
            idTd.textContent = user.uid;
            // Set the text content of scoreTd to the user's score
            scoreTd.textContent = user.score; // Make sure 'score' corresponds to your data structure

            // Append nameTd, idTd, and scoreTd to the table row (tr)
            tr.appendChild(nameTd);
            tr.appendChild(idTd);
            tr.appendChild(scoreTd);

            // Append the table row (tr) to the result container
            resultContainer.appendChild(tr);
        });
    })
    .catch(error => {
        // If any error occurs during the fetch or processing of data, log the error
        console.error('Fetch error:', error);
        // Display an error message in the result container
        resultContainer.innerHTML = `<tr><td colspan="3">Error loading leaderboard: ${error.message}</td></tr>`;
    });

Here is the fetch for the score which will put the updated score value into the SQLITE database shown at the start.

function getScore() {
    fetch('http://127.0.0.1:8086/api/users/')
      .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.
  • This code iterates through the entire JSON list and displays it onto the page as a leaderboard.
fetch(url, options)
            .then(response => {
                if (!response.ok) {
                    throw new Error('Network response was not ok: ' + response.statusText);
                }
                return response.json();
            })
            .then(data => {
                // Clear the table before inserting new data
                resultContainer.innerHTML = '';
                // Assuming each user object in 'data' has 'name', 'uid', and 'score' properties
                data.forEach(user => {
                    const tr = document.createElement("tr");
                    const nameTd = document.createElement("td");
                    const idTd = document.createElement("td");
                    const scoreTd = document.createElement("td");

                    nameTd.textContent = user.name;
                    idTd.textContent = user.uid;
                    scoreTd.textContent = user.score; // Make sure 'score' corresponds to your data structure

                    tr.appendChild(nameTd);
                    tr.appendChild(idTd);
                    tr.appendChild(scoreTd);

                    resultContainer.appendChild(tr);
                });
            })
            .catch(error => {
                console.error('Fetch error:', error);
                resultContainer.innerHTML = `<tr><td colspan="3">Error loading leaderboard: ${error.message}</td></tr>`;
            });
fetch(url, authOptions)
        .then(response => {
            // Check if response is in the 2xx range
            if (!response.ok) {
                throw new Error('Authentication failed. Status: ' + response.status);
            }

            // Extract JWT from the response
            return response.text();
        })

Optional/Extra, 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.

  • Show algorithms and preparation for predictions.

  • Discuss concepts and understanding of Linear Regression algorithms.

a data analysis technique that predicts the value of unknown data by using another related and known data value. It mathematically models the unknown or dependent variable and the known or independent variable as a linear equation

  • Discuss concepts and understanding of Decision Tree analysis algorithms.

a tree-like model that acts as a decision support tool, visually displaying decisions and their potential outcomes, consequences, and costs.

 def predict(self, passenger):
        """ Predict the survival probability of a passenger.

        Args:
            passenger (dict): A dictionary representing a passenger. The dictionary should contain the following keys:
                'pclass': The passenger's class (1, 2, or 3)
                'sex': The passenger's sex ('male' or 'female')
                'age': The passenger's age
                'sibsp': The number of siblings/spouses the passenger has aboard
                'parch': The number of parents/children the passenger has aboard
                'fare': The fare the passenger paid
                'embarked': The port at which the passenger embarked ('C', 'Q', or 'S')
                'alone': Whether the passenger is alone (True or False)

        Returns:
           dictionary : contains die and survive probabilities 
        """
        # clean the passenger data
        passenger_df = pd.DataFrame(passenger, index=[0])
        passenger_df['sex'] = passenger_df['sex'].apply(lambda x: 1 if x == 'male' else 0)
        passenger_df['alone'] = passenger_df['alone'].apply(lambda x: 1 if x == True else 0)
        onehot = self.encoder.transform(passenger_df[['embarked']]).toarray()
        cols = ['embarked_' + str(val) for val in self.encoder.categories_[0]]
        onehot_df = pd.DataFrame(onehot, columns=cols)
        passenger_df = pd.concat([passenger_df, onehot_df], axis=1)
        passenger_df.drop(['embarked', 'name'], axis=1, inplace=True)
        
        # predict the survival probability and extract the probabilities from numpy array
        die, survive = np.squeeze(self.model.predict_proba(passenger_df))
        # return the survival probabilities as a dictionary
        return {'die': die, 'survive': survive}