Model stocks.py

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


class Stock(db.Model):
    __tablename__ = 'stocks'  # 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)
    _ticker = db.Column(db.String(255), unique=False, nullable=False)
    _rating = db.Column(db.String(255), unique=False, nullable=False)


    # constructor of a User object, initializes the instance variables within object (self)
    def __init__(self, name, ticker, rating="neutral"):
        self._name = name    # variables with self prefix become part of the object, 
        self._ticker = ticker
        self._rating = rating

    # 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 ticker(self):
        return self._ticker
    
    # a setter function, allows name to be updated after initial object creation
    @ticker.setter
    def ticker(self, ticker):
        self._ticker = ticker

    @property
    def rating(self):
        return self._rating
    
    # a setter function, allows name to be updated after initial object creation
    @rating.setter
    def rating(self, rating):
        self._rating = rating

    # 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,
            "ticker": self.ticker,
            "rating": self.rating,
        }

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

    # CRUD delete: remove self
    # None
    def delete(self):
        db.session.delete(self)
        db.session.commit()
        return None
    
    ## Created a database that stores stocks above


"""Database Creation and Testing """


# Builds working data for testing
def initStocks():
    """Create database and tables"""
    db.create_all()
    """Tester data for table"""
    s1 = Stock(name='APPLE<', ticker='APPL', rating='STONG SELL')
    s2 = Stock(name='NVIDIA', ticker='NVDA', rating='STRONG BUY')
    s3 = Stock(name='EXXON MOBILE', ticker='XOM', rating='NEUTRAL')

    stocks = [s1, s2, s3,]

    """Builds sample user/note(s) data"""
    for stock in stocks:
        try:
            '''add user/post data to table'''
            stock.create()
        except IntegrityError:
            '''fails with bad or duplicate data'''
            db.session.remove()
            print(f"Records exist, duplicate email, or error: {stock.id}")

API stocks.py

from flask import Blueprint, request, jsonify
from flask_restful import Api, Resource # used for REST API building
from datetime import datetime

from model.stocks import Stock

stock_api = Blueprint('stock_api', __name__,
                   url_prefix='/api/stocks')

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

class StockAPI:        
    class _Create(Resource):
        def post(self):
            ''' Read data for json body '''
            body = request.get_json()
            
            ''' Avoid garbage in, error checking '''
            # validate name
            name = body.get('name')
            if name is None or len(name) < 2:
                return {'message': f'Name is missing, or is less than 2 characters'}, 210
            # validate uid
            ticker = body.get('ticker')
            if ticker is None or len(ticker) < 2:
                return {'message': f'Ticker ID is missing, or is less than 2 characters'}, 210
            
            ''' #1: Key code block, setup USER OBJECT '''
            so = Stock(name=name, 
                      ticker=ticker)
            
            ''' Additional garbage error checking '''
            
            rating = body.get("rating")
            if rating is not None:
                so.rating = rating
            
            ''' #2: Key Code block to add user to database '''
            # create user in database
            stock = so.create()
            # success returns json of user
            if stock:
                return jsonify(stock.read())
            # failure returns error
            return {'message': f'Processed {name}, either a format occured'}, 210

    class _Read(Resource):
        def get(self):
            stocks = Stock.query.all()    # read/extract all users from database
            json_ready = [stock.read() for stock in stocks]  # prepare output in json
            return jsonify(json_ready)  # jsonify creates Flask response object, more specific to APIs than json.dumps

    # building RESTapi endpoint
    api.add_resource(_Create, '/create')
    api.add_resource(_Read, '/')