2.4a and 2.4b Hacks/Notes
Lesson Notes:
- 
.model inherits is a parameter of class user (this is referencing the code in the lesson)
 - 
When you want to set something, you can create it or update it.
 - 
Use debugging to take a closer look at the code and how it works
 - 
For the initial data code block, if you were to put a red dot next to u6 and one at initusers(), when you debug it, it would go straight to initusers because that is the first time it is called.
 - 
Object Relational Model query, you can have a user give you an input, there will then be a query filter, meaning it will look if the user id is the same as the input,
 - 
JSON is easier to read in most code languages
 
College Board Notes:
- 
Programs are used in an iterative and interactive way when processing information to allow users to gain insight and knowledge about data
 - 
Programmers can use programs to filter and clean digital data, which is gaining insight and knowledge from data
 - 
Combining data sources, clustering data, and classifyin data are parts of the process of using programs to gain insight and knowledge from data
 - 
Insight and knowledge can be obtained from translating and transforming digitally representeed information
 - 
Patterns can emerge when data are transformed using programs
 - 
People can filter, sort, combine, transform, cluster, or even classify the data in the program and that can lead to more knowledge and insight.
 - 
Programs can be used to process data to acquire information
 - 
Tables, diagrams, text and other visual tools can be used to communicate insight and knowledge gained from data
 - 
Search tools are useful for efficiently finding information
 - 
Data filtering systems are important tools for finding information and recognizing patterns in data
 - 
Programs such as spreadsheets help efficiently organize and find trends in information
 
"""
These imports define the key objects
"""
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
"""
These object and definitions are used throughout the Jupyter Notebook.
"""
# Setup of key Flask object (app)
app = Flask(__name__)
# Setup SQLAlchemy object and properties for the database (db)
database = 'sqlite:///example.db'  # path and filename of database
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = database
app.config['SECRET_KEY'] = 'SECRET_KEY'
db = SQLAlchemy()
# This belongs in place where it runs once per project
db.init_app(app)
""" database dependencies to support sqlite examples """
import datetime
from datetime import datetime
import json
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 a Python shell and follow along '''
# 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 Info(db.Model):
    __tablename__ = 'info'  # table name is plural, class name is singular
    # Define the User schema with "vars" from object
    id = db.Column(db.Integer, primary_key=True)
    _email = db.Column(db.String(255), unique=False, nullable=False)
    _uid = db.Column(db.String(255), unique=True, nullable=False)
    _fname = db.Column(db.String(255), unique=True, nullable=False)
    _lname = db.Column(db.String(255), unique=False, nullable=False)
    _score = db.Column(db.String(255), unique=False, nullable=False)
    # constructor of a User object, initializes the instance variables within object (self)
    def __init__(self, fname, uid, lname, email, score):
        self._fname = fname
        self._uid = uid    # variables with self prefix become part of the object, 
        self._lname = lname
        self.email = email
        self._score = score
    # a name getter method, extracts name from object
    @property
    def fname(self):
        return self._fname
    
    # a setter function, allows name to be updated after initial object creation
    @fname.setter
    def fname(self, fname):
        self._fname = fname
    @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
    # a getter method, extracts email from object
    @property
    def lname(self):
        return self._lname
    
    # a setter function, allows name to be updated after initial object creation
    @lname.setter
    def lname(self, lname):
        self._lname = lname
    
    @property
    def email(self):
        return self._email
    
    # a setter function, allows name to be updated after initial object creation
    @email.setter
    def email(self, email):
        self._email = email
    
    # dob property is returned as string, to avoid unfriendly outcomes
    @property
    def score(self):
        return self._score
    
    # a setter function, allows name to be updated after initial object creation
    @score.setter
    def score(self, score):
        self._score = score
    
    # 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,
            "uid": self.uid,
            "fname": self.fname,
            "lname": self.lname,
            "email": self.email,
            "score": self.score,
        }
"""Database Creation and Testing """
# Builds working data for testing
def initInfo():
    with app.app_context():
        """Create database and tables"""
        db.create_all()
        """Tester data for table"""
        u1 = Info(fname='Srihita', uid='sri', lname='Kotturi', email='srihitak@gmail.com', score='10')
        u2 = Info(fname='Lalitha', uid='lallu', lname='Chittila', email='lalithac@gmail.com', score='11')
        u3 = Info(fname='Chandram', uid='ram', lname='Kotturi', email='chandramk@gmail.com', score='12')
        u4 = Info(fname='Sumedh', uid='sumi', lname='Kotturi', email='sumedhk@gmail.com', score='13')
        u5 = Info(fname='Sobha', uid='sailu', lname='Chittila', email='sobhac@gmail.com', score='14')
        u6 = Info(fname='Aneel', uid='aneel', lname='Maha', email='aneelm@gmail.com', score='15')
        infos = [u1, u2, u3, u4, u5, u6]
        """Builds sample user/note(s) data"""
        for info in infos:
            try:
                '''add user to table'''
                object = info.create()
                print(f"Created new uid {object.uid}")
            except:  # error raised if object nit created
                '''fails with bad or duplicate data'''
                print(f"Records exist uid {info.uid}, or error.")
                
initInfo()