Terminology

Object-Oriented Programming (OOP) uses classes as blueprints for creating Objects. Classes have Attributes assigned to the object with the keyword self and Methods specific to the object.

Additional info:

  • an Object is an Instance of the Class/Template
  • there can be many Objects created from the same Class
  • each Object contains its own Instance Data
  • the data is setup by the Constructor, this is the "init" method in a Python class
  • all methods in the Class/Template become part of the Object, methods are accessed using dot notation (object.method())

The Initial Code

This is the code provided to us as an example.

# A gateway in necessary as a web server cannot communicate directly with Python.
# In this case, imports are focused on generating hash code to protect passwords.
from werkzeug.security import generate_password_hash, check_password_hash
import json

# Define a User Class/Template
# -- A User represents the data we want to manage
class User:    
    # constructor of a User object, initializes the instance variables within object (self)
    def __init__(self, name, uid, password):
        self._name = name    # variables with self prefix become part of the object, 
        self._uid = uid
        self.set_password(password)

    # 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, method='sha256')

    # 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
    
    # output content using str(object) in human readable form, uses getter
    def __str__(self):
        return f'name: "{self.name}", id: "{self.uid}", psw: "{self.password}"'

    # output command to recreate the object, uses attribute directly
    def __repr__(self):
        return f'Person(name={self._name}, uid={self._uid}, password={self._password})'


# tester method to print users
def tester(users, uid, psw):
    result = None
    for user in users:
        # test for match in database
        if user.uid == uid and user.is_password(psw):  # check for match
            print("* ", end="")
            result = user
        # print using __str__ method
        print(str(user))
    return result
        

# place tester code inside of special if!  This allows include without tester running
if __name__ == "__main__":

    # define user objects
    u1 = User(name='Thomas Edison', uid='toby', password='123toby')
    u2 = User(name='Nicholas Tesla', uid='nick', password='123nick')
    u3 = User(name='Alexander Graham Bell', uid='lex', password='123lex')
    u4 = User(name='Eli Whitney', uid='eli', password='123eli')
    u5 = User(name='Hedy Lemarr', uid='hedy', password='123hedy')

    # put user objects in list for convenience
    users = [u1, u2, u3, u4, u5]

    # Find user
    print("Test 1, find user 3")
    u = tester(users, u3.uid, "123lex")


    # Change user
    print("Test 2, change user 3")
    u.name = "John Mortensen"
    u.uid = "jm1021"
    u.set_password("123qwerty")
    u = tester(users, u.uid, "123qwerty")


    # Make dictionary
    ''' 
    The __dict__ in Python represents a dictionary or any mapping object that is used to store the attributes of the object. 
    Every object in Python has an attribute that is denoted by __dict__. 
    Use the json.dumps() method to convert the list of Users to a JSON string.
    '''
    print("Test 3, make a dictionary")
    json_string = json.dumps([user.__dict__ for user in users]) 
    print(json_string)

    print("Test 4, make a dictionary")
    json_string = json.dumps([vars(user) for user in users]) 
    print(json_string)
Test 1, find user 3
name: "Thomas Edison", id: "toby", psw: "sha256$Z1N..."
name: "Nicholas Tesla", id: "nick", psw: "sha256$lmi..."
* name: "Alexander Graham Bell", id: "lex", psw: "sha256$skw..."
name: "Eli Whitney", id: "eli", psw: "sha256$z3s..."
name: "Hedy Lemarr", id: "hedy", psw: "sha256$unQ..."
Test 2, change user 3
name: "Thomas Edison", id: "toby", psw: "sha256$Z1N..."
name: "Nicholas Tesla", id: "nick", psw: "sha256$lmi..."
* name: "John Mortensen", id: "jm1021", psw: "sha256$fRT..."
name: "Eli Whitney", id: "eli", psw: "sha256$z3s..."
name: "Hedy Lemarr", id: "hedy", psw: "sha256$unQ..."
Test 3, make a dictionary
[{"_name": "Thomas Edison", "_uid": "toby", "_password": "sha256$Z1NzXec5YjlZqD34$376c6ff4ca05a88823d928ecce732678ad08afb6edf4e244a728b314a7a72319"}, {"_name": "Nicholas Tesla", "_uid": "nick", "_password": "sha256$lmiD42i5byXOIJrh$02d0889e17d1961a3238eab5d70c0eb6a45d3a9f6af965b5046ab9a9adb13aba"}, {"_name": "John Mortensen", "_uid": "jm1021", "_password": "sha256$fRTRHDPqtsZ9wZUf$e8f1a773fc2f8a82ba9355fde0ec1979d444e1275b12ab272b9cc4b575d553a3"}, {"_name": "Eli Whitney", "_uid": "eli", "_password": "sha256$z3sNJVuGtqrRP127$23fff7259b500d92c473a3588fd98889cb16e4ce6bd1616ff7d4981a2b5b32fa"}, {"_name": "Hedy Lemarr", "_uid": "hedy", "_password": "sha256$unQlpwEcj1L9YhZc$7beeec4d8dff0e3eeff6fd2a465fbbcc551889a3bff0cecefa6bb02a66e351f9"}]
Test 4, make a dictionary
[{"_name": "Thomas Edison", "_uid": "toby", "_password": "sha256$Z1NzXec5YjlZqD34$376c6ff4ca05a88823d928ecce732678ad08afb6edf4e244a728b314a7a72319"}, {"_name": "Nicholas Tesla", "_uid": "nick", "_password": "sha256$lmiD42i5byXOIJrh$02d0889e17d1961a3238eab5d70c0eb6a45d3a9f6af965b5046ab9a9adb13aba"}, {"_name": "John Mortensen", "_uid": "jm1021", "_password": "sha256$fRTRHDPqtsZ9wZUf$e8f1a773fc2f8a82ba9355fde0ec1979d444e1275b12ab272b9cc4b575d553a3"}, {"_name": "Eli Whitney", "_uid": "eli", "_password": "sha256$z3sNJVuGtqrRP127$23fff7259b500d92c473a3588fd98889cb16e4ce6bd1616ff7d4981a2b5b32fa"}, {"_name": "Hedy Lemarr", "_uid": "hedy", "_password": "sha256$unQlpwEcj1L9YhZc$7beeec4d8dff0e3eeff6fd2a465fbbcc551889a3bff0cecefa6bb02a66e351f9"}]

Hacks

Here is a section for my completion of the hacks from this lesson.

New Attributes and Test

Below, I added the classOf attribute, the dob attribute, and the updated tester which shows the calculation of the user's age.

See the tester in action at the bottom (focus on Test 5 and Test 6).

from datetime import date
#old code comments deleted to de-clutter; you can see them above
class User:    
    def __init__(self, name, uid, password, classOf, dob):
        self._name = name
        self._uid = uid
        self.set_password(password)
        self._classOf = classOf
        self._dob = dob
    @property
    def name(self):
        return self._name
    @name.setter
    def name(self, name):
        self._name = name
    @property
    def uid(self):
        return self._uid
    @uid.setter
    def uid(self, uid):
        self._uid = uid
    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
    def set_password(self, password):
        """Create a hashed password."""
        self._password = generate_password_hash(password, method='sha256')
    def is_password(self, password):
        """Check against hashed password."""
        result = check_password_hash(self._password, password)
        return result
    def __str__(self):
        return f'name: "{self.name}", id: "{self.uid}", psw: "{self.password}"'
    def __repr__(self):
        return f'Person(name={self._name}, uid={self._uid}, password={self._password})'
    
    #here's the classOf getter
    @property
    def classOf(self):
        return self._classOf
    
    #here's the classOf setter
    @classOf.setter
    def classOf(self, classOf):
        self._classOf = classOf
    
    #here's the date of birth getter
    @property
    def dob(self):
        return self._dob
    
    #here's the date of birth setter
    @dob.setter
    def dob(self, dob):
        self._dob = dob


# tester method to print users
def tester(users, uid, psw):
    result = None
    for user in users:
        # test for match in database
        if user.uid == uid and user.is_password(psw):  # check for match
            print("* ", end="")
            result = user
        # print using __str__ method
        print(str(user))
    return result

# NEW: AGE CALCULATOR (from provided template)
def calculate_age(dob):
    today = date.today()
    return today.year - dob.year - ((today.month, today.day) < (dob.month, dob.day))

if __name__ == "__main__":

    # defining user objects again
    u1 = User(name='Thomas Edison', uid='toby', password='123toby', classOf='1865', dob=(1847, 2, 11))
    u2 = User(name='Nicholas Tesla', uid='nick', password='123nick', classOf='1874', dob=(1856, 7, 10))
    u3 = User(name='Alexander Graham Bell', uid='lex', password='123lex', classOf='1865', dob=(1847, 3, 3))
    u4 = User(name='Eli Whitney', uid='eli', password='123eli', classOf='1783', dob=(1765, 12, 8))
    u5 = User(name='Hedy Lemarr', uid='hedy', password='123hedy', classOf='1932', dob=(1917, 11, 9))
    users = [u1, u2, u3, u4, u5]
    #HERE IS THE START OF MY UNIQUE TESTS
    print("Test 5, find the oldest user")
    agelist = []
    for user in users:
        tempdob = date(user._dob[0], user._dob[1], user._dob[2])
        age = calculate_age(tempdob)
        agelist.append(age)
    max_age = max(agelist)
    for index, item in enumerate(agelist):
        if item == max_age:
            oldest = index
    for index, user in enumerate(users):
        if index == oldest:
            print("* ", end="")
        print(user._name + ":", agelist[index], "years old.")
    
    print("Test 6, show user graduation dates")
    for user in users:
        print(user.name, "graduated in", user.classOf + ".")

dob = date(2004, 12, 31)
#age = calculate_age(dob)
Test 5, find the oldest user
Thomas Edison: 175 years old.
Nicholas Tesla: 166 years old.
Alexander Graham Bell: 175 years old.
* Eli Whitney: 257 years old.
Hedy Lemarr: 105 years old.
Test 6, show user graduation dates
Thomas Edison graduated in 1865.
Nicholas Tesla graduated in 1874.
Alexander Graham Bell graduated in 1865.
Eli Whitney graduated in 1783.
Hedy Lemarr graduated in 1932.

Cafe Gato Objects

Below is an example of using objects for my portion of our group project (linked here). As shown on that page, my use of CRUD mainly involves letting a user create a personal event at the cafe, with details clearly listed in a readable table.

To go along with that, below, I've created the object Event, which includes all attributes that are also listed on the frontend version.

Attributes, Getters and Setters

class Event:    
    def __init__(self, name, email, event_name, event_details, date, start_time, end_time):
        self._name = name
        self._email = email
        self._event_name = event_name
        self._event_details = event_details
        self._date = date
        self._start_time = start_time
        self._end_time = end_time
    
    #here's the name getter
    @property
    def name(self):
        return self._name

    #here's the name setter
    @name.setter
    def name(self, name):
        self._name = name
    
    #here's the email getter
    @property
    def email(self):
        return self._email
    
    #here's the email setter
    @email.setter
    def email(self, email):
        self._email = email
    
    #here's the event_name getter
    @property
    def event_name(self):
        return self._event_name
    
    #here's the event_name setter
    @event_name.setter
    def event_name(self, event_name):
        self._event_name = event_name
    
    #here's the event_details getter
    @property
    def event_details(self):
        return self._event_details
    
    #here's the event_details setter
    @event_details.setter
    def event_details(self, event_details):
        self._event_details = event_details
    
    #here's the date getter
    @property
    def date(self):
        return self._date
    
    #here's the date setter
    @date.setter
    def date(self, date):
        self._date = date

    #here's the start_time getter
    @property
    def start_time(self):
        return self._start_time
    
    #here's the start_time setter
    @start_time.setter
    def start_time(self, start_time):
        self._start_time = start_time

    #here's the end_time getter
    @property
    def end_time(self):
        return self._end_time
    
    #here's the end_time setter
    @end_time.setter
    def end_time(self, end_time):
        self._end_time = end_time

Tests

Here are two tests that are applicable with our site.

(The date attribute is formatted as a string in the "mm/dd/yyyy" format and the start_time and end_time attributes are both formatted as "hh/mm", with hours in military time. This is due to how the <input type="date"> and <input type="time"> tags format their values. With that in mind, the tests are...)

  1. A test that lists the dates of the events and identifies the earliest.
  2. A test that lists the start and end time of each event (in AM/PM format) and identifies whether or not the event occurs at a valid time for the day of the week it takes place in.
from datetime import *
if __name__ == "__main__":
    #defining the three events for testing
    e1 = Event(name="Thomas Edison", email="tedison@lightbulb.edu",
        event_name="The Edison Troupe Meet",
        event_details="We 10 selected geniuses will meet in the events room for a convergence.",
        date="02/23/2023", start_time="13:00", end_time="14:00")
    e2 = Event(name="John Mortensen", email="jmortensen@powayusd.com",
        event_name="Extra Credit Code Meetup",
        event_details="Come to work on ideation and any confusion with the Full Stack CPT project. No phones.",
        date="02/25/2023", start_time="09:00", end_time="11:00")
    e3 = Event(name="Karl Giant", email="giantrichguy@wallstreet.org",
        event_name="Karl and Cats",
        event_details="Karl would like to see cats with friends (if he can fit in the building).",
        date="02/26/2023", start_time="16:00", end_time="17:00")
    events = [e1, e2, e3]

    # START OF TEST 1
    print("TEST 1 BELOW:")
    datelist = []
    for event in events:
        datelist.append(date(int(event._date[6:10]), int(event._date[0:2]), int(event._date[3:5])))
    soonest = datelist[2]
    for index, date in enumerate(datelist):
        if date <= soonest:
            soonest = date
            soonind = index
    for index, event in enumerate(events):
        if index == soonind:
            print("* ", end="")
        print(event._event_name + ":", event._date)
    
    # START OF TEST 2
    print("\nTEST 2 BELOW:")
    startlist = []
    endlist = []
    for event in events:
        if 0 < int(event._start_time[0:2]) < 12:
            startlist.append(str(int(event._start_time[0:2])) + ":" + event._start_time[3:5] + " AM")
        elif int(event._start_time[0:2]) > 12:
            startlist.append(str(int(event._start_time[0:2]) - 12) + event._start_time[2:5] + " PM")
        elif int(event._start_time[0:2]) == 12:
            startlist.append("12:" + event._start_time[3:5] + " PM")
        else:
            startlist.append("12:" + event._start_time[3:5] + " AM")
        if 0 < int(event._end_time[0:2]) < 12:
            endlist.append(str(int(event._end_time[0:2])) + ":" + event._end_time[3:5] + " AM")
        elif int(event._end_time[0:2]) > 12:
            endlist.append(str(int(event._end_time[0:2]) - 12) + event._end_time[2:5] + " PM")
        elif int(event._start_time[0:2]) == 12:
            endlist.append("12:" + event._end_time[3:5] + " PM")
        else:
            endlist.append("12:" + event._end_time[3:5] + " AM")
    for index, event in enumerate(events):
        if datetime.weekday(datelist[index]) < 5:
            if (8 <= int(event._start_time[0:2]) < 17) and (8 < int(event._end_time[0:2]) <= 17):
                print("Valid: ", end="")
            else:
                print("Invalid: ", end="")
        else:
            if (10 <= int(event._start_time[0:2]) < 18) and (10 < int(event._end_time[0:2]) <= 18):
                print("Valid: ", end="")
            else:
                print("Invalid: ", end="")
        print(event._event_name, "(" + startlist[index], "to", endlist[index] + ")")
TEST 1 BELOW:
* The Edison Troupe Meet: 02/23/2023
Extra Credit Code Meetup: 02/25/2023
Karl and Cats: 02/26/2023

TEST 2 BELOW:
Valid: The Edison Troupe Meet (1:00 PM to 2:00 PM)
Invalid: Extra Credit Code Meetup (9:00 AM to 11:00 AM)
Valid: Karl and Cats (4:00 PM to 5:00 PM)

(The "Extra Credit Code Meetup" was invalid because it was scheduled for 9:00 AM on a Saturday. According to the cafe hours, they open at 10:00 AM on Saturdays, meaning the event is scheduled for before opening. This makes it invalid.)