Skip to content

Commit

Permalink
Merge pull request #34 from rstudio/aron-simpler-content-deps
Browse files Browse the repository at this point in the history
simplify content and its dependencies
  • Loading branch information
aronatkins authored Jun 21, 2024
2 parents 9046ba3 + e759692 commit 0c18b66
Show file tree
Hide file tree
Showing 11 changed files with 315 additions and 13,691 deletions.
21 changes: 8 additions & 13 deletions __tests__/apps/flask/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
# Stock Pricing Service
# Simple Flask application

## About this example
Recreate the `manifest.json` with:

An API allows you to turn your models into production services that other tools and teams can use. APIs are a great way for software engineering teams to use your models without translating them into different languages.


## Learn more

* [Flask Introduction](https://palletsprojects.com/p/flask/)
* [Flask Documentation](https://flask.palletsprojects.com/en/1.1.x/)

## Requirements

* Python 2 versions 2.7 or higher; Python 3 versions 3.5 or higher
```bash
python3.11 -m venv env
. ./env/bin/activate
pip install rsconnect-python
rsconnect write-manifest flask --overwrite .
```
133 changes: 11 additions & 122 deletions __tests__/apps/flask/app.py
Original file line number Diff line number Diff line change
@@ -1,131 +1,20 @@
# -*- coding: utf-8 -*-
import os
import numpy as np
import pandas as pd
from flask import Flask
from flask_restx import Api, Resource, fields
from flask import Flask, jsonify, request, url_for

# Fetch prices from local CSV using pandas
prices = pd.read_csv(
os.path.join(os.path.dirname(__file__), "prices.csv"),
index_col=0,
parse_dates=True,
)


# Configure the Flask app using RestX for swagger documentation
app = Flask(__name__)
app.config["SWAGGER_UI_DOC_EXPANSION"] = "list"
app.config["RESTX_MASK_SWAGGER"] = False
app.config["ERROR_INCLUDE_MESSAGE"] = False


api = Api(
app,
version="0.1.0",
title="Stocks API",
description="The Stocks API provides pricing and volatility data for a "
"limited number of US equities from 2010-2018",
)
ns = api.namespace("stocks")

# Define stock and price models for marshalling and documenting response objects
tickers_model = ns.model(
"Tickers",
{
"tickers": fields.List(
fields.String(description="Ticker of the stock"),
description="All available stock tickers",
),
},
)

stock_model = ns.model(
"Stock",
{
"ticker": fields.String(description="Ticker of the stock"),
"price": fields.Float(description="Latest price of the stock"),
"volatility": fields.Float(description="Latest volatility of the stock price"),
},
)

price_model = ns.model(
"Price",
{
"date": fields.Date,
"high": fields.Float(description="High price for this date"),
"low": fields.Float(description="Low price for this date"),
"close": fields.Float(description="Closing price for this date"),
"volume": fields.Integer(description="Daily volume for this date"),
"adjusted": fields.Float(description="Split-adjusted price for this date"),
},
)


class TickerNotFound(Exception):
def __init__(self, ticker):
self.ticker = ticker
self.message = "Ticker `{}` not found".format(self.ticker)

def __str__(self):
return "TickerNotFound('{}')".format(self.ticker)


# Our simple API only has a few GET endpoints
@ns.route("/")
class StockList(Resource):
"""Shows a list of all available tickers"""

@ns.marshal_with(tickers_model)
def get(self):
tickers = prices["ticker"].unique()
return {"tickers": tickers}


@ns.route("/<string:ticker>")
@ns.response(404, "Ticker not found")
@ns.param("ticker", "The ticker for the stock")
class Stock(Resource):
"""Shows the latest price and volatility for the specified stock"""

@ns.marshal_list_with(stock_model)
def get(self, ticker):
if ticker not in prices["ticker"].unique():
raise TickerNotFound(ticker)

ticker_prices = prices[prices["ticker"] == ticker]
current_price = ticker_prices["close"].last("1d").round(2)
current_volatility = np.log(
ticker_prices["adjusted"] / ticker_prices["adjusted"].shift(1)
).var()

return {
"ticker": ticker,
"price": current_price,
"volatility": current_volatility,
@app.route("/ping")
def ping():
return jsonify(
{
"headers": dict(request.headers),
"environ": dict(os.environ),
"link": url_for("ping"),
"external_link": url_for("ping", _external=True),
}


@ns.route("/<string:ticker>/history")
@ns.response(404, "Ticker not found")
@ns.param("ticker", "The ticker for the stock")
class StockHistory(Resource):
"""Shows the price history for the specified stock"""

@ns.marshal_list_with(price_model)
def get(self, ticker):
if ticker not in prices["ticker"].unique():
raise TickerNotFound(ticker)

ticker_prices = prices[prices["ticker"] == ticker]
ticker_prices["date"] = ticker_prices.index
return ticker_prices.to_dict("records")


@api.errorhandler(TickerNotFound)
def handle_ticker_not_found(error):
return {"message": error.message}, 404
)


if __name__ == "__main__":
app.run(debug=True)
app.run()
20 changes: 7 additions & 13 deletions __tests__/apps/flask/manifest.json
Original file line number Diff line number Diff line change
@@ -1,33 +1,27 @@
{
"version": 1,
"locale": "en_US.UTF-8",
"metadata": {
"appmode": "python-api",
"entrypoint": "app"
},
"locale": "en_US.UTF-8",
"python": {
"version": "3.8.3",
"version": "3.11.8",
"package_manager": {
"name": "pip",
"version": "19.2.3",
"version": "24.0",
"package_file": "requirements.txt"
}
},
"files": {
"requirements.txt": {
"checksum": "8932b9a8a1e488ba6dac3592d8012ced"
"checksum": "cfdea7934019617dc8e2d9a7ea631e00"
},
"README.md": {
"checksum": "5a68ee77af4872bc1b0187a0adb26a5c"
"checksum": "6c47ae2907b4af6076c12704f2e3a375"
},
"app.py": {
"checksum": "9799c3b834b555cf02e5896ad2997674"
},
"prices.csv": {
"checksum": "e0bc27e3dd358c360863807e09079985"
},
"thumbnail.jpg": {
"checksum": "c25a15924781145e8831661e641e5202"
"checksum": "564fcab35569c7ee7b3ddc206fe8cfd0"
}
}
}
}
Loading

0 comments on commit 0c18b66

Please sign in to comment.