Merge pull request #41 from WebEngineering2/symbol_price

Save symbol price in database
This commit is contained in:
Florian Kaiser 2022-04-05 15:32:55 +02:00 committed by GitHub
commit 772a4d2c60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 165 additions and 12 deletions

View File

@ -16,3 +16,6 @@ BOT_PASSWORD=
ADMIN_EMAIL= ADMIN_EMAIL=
ADMIN_USERNAME= ADMIN_USERNAME=
ADMIN_PASSWORD= ADMIN_PASSWORD=
# API URL (used for load_share_price.py and generate_sample_transactions.py)
API_URL=

View File

@ -7,6 +7,7 @@ from flask_cors import CORS
from app.blueprints.keyword import keyword_blueprint from app.blueprints.keyword import keyword_blueprint
from app.blueprints.portfolio import portfolio_blueprint from app.blueprints.portfolio import portfolio_blueprint
from app.blueprints.shares import shares_blueprint from app.blueprints.shares import shares_blueprint
from app.blueprints.share_price import share_price_blueprint
from app.blueprints.transactions import transaction_blueprint from app.blueprints.transactions import transaction_blueprint
from app.blueprints.telegram import telegram_blueprint from app.blueprints.telegram import telegram_blueprint
from app.blueprints.user import users_blueprint from app.blueprints.user import users_blueprint
@ -28,6 +29,7 @@ def create_app(config_filename=None):
# api blueprints # api blueprints
application.register_blueprint(keyword_blueprint) application.register_blueprint(keyword_blueprint)
application.register_blueprint(shares_blueprint) application.register_blueprint(shares_blueprint)
application.register_blueprint(share_price_blueprint)
application.register_blueprint(transaction_blueprint) application.register_blueprint(transaction_blueprint)
application.register_blueprint(portfolio_blueprint) application.register_blueprint(portfolio_blueprint)
application.register_blueprint(users_blueprint) application.register_blueprint(users_blueprint)

View File

@ -2,10 +2,11 @@ import os
from apiflask import APIBlueprint from apiflask import APIBlueprint
from app.schema import PortfolioResponseSchema from app.models import SharePrice
from app.auth import auth
from app.db import database as db from app.db import database as db
from app.helper_functions import make_response, get_email_or_abort_401 from app.helper_functions import make_response, get_email_or_abort_401
from app.auth import auth from app.schema import PortfolioResponseSchema
portfolio_blueprint = APIBlueprint('portfolio', __name__, url_prefix='/api') portfolio_blueprint = APIBlueprint('portfolio', __name__, url_prefix='/api')
__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
@ -23,11 +24,18 @@ def get_portfolio():
if transactions is not None: if transactions is not None:
for row in transactions: for row in transactions:
return_portfolio.append({ data = {
"symbol": row[0], "symbol": row[0],
"count": row[1], "count": row[1],
# "price": row[2], # "calculated_price": row[2],
"last_transaction": row[3] "last_transaction": row[3],
}) 'current_price': 0
}
query_share_price = db.session.query(SharePrice).filter_by(symbol=row[0]).first()
if query_share_price is not None:
data['current_price'] = query_share_price.as_dict()['price']
return_portfolio.append(data)
return make_response(return_portfolio, 200, "Successfully loaded symbols") return make_response(return_portfolio, 200, "Successfully loaded symbols")

View File

@ -0,0 +1,88 @@
import datetime
import os
from apiflask import APIBlueprint, abort
from app.models import SharePrice
from app.db import database as db
from app.helper_functions import make_response
from app.auth import auth
from app.schema import SymbolPriceSchema
share_price_blueprint = APIBlueprint('share_price', __name__, url_prefix='/api')
__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
@share_price_blueprint.route('/symbols', methods=['GET'])
@share_price_blueprint.output({}, 200)
@share_price_blueprint.auth_required(auth)
@share_price_blueprint.doc(summary="Returns all transaction symbols", description="Returns all transaction symbols for all users")
def get_transaction_symbols():
symbols = db.session.execute("SELECT symbol FROM `transactions` GROUP BY symbol;").all()
return_symbols = []
for s in symbols:
return_symbols.append(s[0])
return make_response(return_symbols, 200, "Successfully loaded symbols")
@share_price_blueprint.route('/symbol', methods=['POST'])
@share_price_blueprint.output({}, 200)
@share_price_blueprint.input(schema=SymbolPriceSchema)
@share_price_blueprint.auth_required(auth)
@share_price_blueprint.doc(summary="Returns all transaction symbols", description="Returns all transaction symbols for all users")
def add_symbol_price(data):
if not check_if_symbol_data_exists(data):
abort(400, message="Symbol missing")
if not check_if_price_data_exists(data):
abort(400, message="Price missing")
if not check_if_time_data_exists(data):
abort(400, message="Time missing")
symbol = data['symbol']
price = data['price']
time = data['time']
share_price = SharePrice(
symbol=symbol,
price=price,
date=datetime.datetime.strptime(time, '%Y-%m-%dT%H:%M:%S.%fZ'),
)
db.session.add(share_price)
db.session.commit()
return make_response(share_price.as_dict(), 200, "Successfully added price")
def check_if_symbol_data_exists(data):
if 'symbol' not in data:
return False
if data['symbol'] == "" or data['symbol'] is None:
return False
return True
def check_if_price_data_exists(data):
if 'price' not in data:
return False
if data['price'] == "" or data['price'] is None:
return False
return True
def check_if_time_data_exists(data):
if 'time' not in data:
return False
if data['time'] == "" or data['time'] is None:
return False
return True

View File

@ -1,13 +1,13 @@
import os
import datetime import datetime
import os
from apiflask import abort, APIBlueprint from apiflask import abort, APIBlueprint
from app.auth import auth
from app.db import database as db from app.db import database as db
from app.helper_functions import make_response, get_email_or_abort_401 from app.helper_functions import make_response, get_email_or_abort_401
from app.models import Transaction from app.models import Transaction
from app.schema import TransactionSchema, TransactionResponseSchema from app.schema import TransactionSchema, TransactionResponseSchema
from app.auth import auth
transaction_blueprint = APIBlueprint('transaction', __name__, url_prefix='/api') transaction_blueprint = APIBlueprint('transaction', __name__, url_prefix='/api')
__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))

View File

@ -51,3 +51,14 @@ class Share(db.Model):
def as_dict(self): def as_dict(self):
return {c.name: getattr(self, c.name) for c in self.__table__.columns} return {c.name: getattr(self, c.name) for c in self.__table__.columns}
class SharePrice(db.Model):
__tablename__ = 'share_price'
id = db.Column('id', db.Integer(), nullable=False, unique=True, primary_key=True)
symbol = db.Column('symbol', db.String(255))
price = db.Column('price', db.Float())
date = db.Column('date', db.DateTime())
def as_dict(self):
return {c.name: getattr(self, c.name) for c in self.__table__.columns}

View File

@ -75,6 +75,12 @@ class TransactionSchema(Schema):
price = Float() price = Float()
class SymbolPriceSchema(Schema):
symbol = String()
time = String(validate=validate.Regexp(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z"))
price = Float()
class TelegramIdSchema(Schema): class TelegramIdSchema(Schema):
telegram_user_id = String() telegram_user_id = String()

View File

@ -1,9 +1,9 @@
import os
import random import random
import faker import faker
import requests import requests
url = 'http://127.0.0.1:5000/api'
username = '' username = ''
password = '' password = ''
@ -12,7 +12,7 @@ shares = ["TWTR", "GOOG", "AAPL", "MSFT", "AMZN", "FB", "NFLX", "TSLA", "BABA",
fake = faker.Faker() fake = faker.Faker()
token = requests.post(url + '/user/login', json={"email": username, "password": password}).json()['data']['token'] token = requests.post(os.getenv("API_URL") + '/user/login', json={"email": username, "password": password}).json()['data']['token']
for i in range(1, 1000): for i in range(1, 1000):
payload = { payload = {
@ -22,4 +22,4 @@ for i in range(1, 1000):
"time": fake.date_time().isoformat() + ".000Z" "time": fake.date_time().isoformat() + ".000Z"
} }
response = requests.post(url + '/transaction', json=payload, headers={'Authorization': 'Bearer ' + token}) response = requests.post(os.getenv("API_URL") + '/transaction', json=payload, headers={'Authorization': 'Bearer ' + token})

33
api/load_share_price.py Normal file
View File

@ -0,0 +1,33 @@
import datetime
import os
import threading
import requests
import yfinance
def thread_function(s):
my_share_info = yfinance.Ticker(s)
my_share_data = my_share_info.info
if my_share_data['regularMarketPrice'] is not None:
payload = {
"symbol": s,
"price": float(my_share_data['regularMarketPrice']),
"time": datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S.000Z")
}
requests.post(os.getenv("API_URL") + '/symbol', json=payload, headers={'Authorization': 'Bearer ' + token})
username = os.getenv('ADMIN_EMAIL')
password = os.getenv('ADMIN_PASSWORD')
token = requests.post(os.getenv("API_URL") + '/user/login', json={"email": username, "password": password}).json()['data']['token']
response = requests.get(os.getenv("API_URL") + '/symbols', headers={'Authorization': 'Bearer ' + token}).json()['data']
for symbol in response:
x = threading.Thread(target=thread_function, args=(symbol,))
x.start()

View File

@ -11,4 +11,6 @@ bcrypt==3.2.0
pytest~=7.1.1 pytest~=7.1.1
pytest-cov pytest-cov
marshmallow~=3.15.0 marshmallow~=3.15.0
faker~=4.0.0 faker~=4.0.0
yfinance~=0.1.6
requests~=2.22.0