From 213acd40e1aaab2d16e65fdc8d5ee229f065621e Mon Sep 17 00:00:00 2001 From: Jeff Date: Sat, 29 Sep 2018 14:27:56 -0500 Subject: [PATCH] initial commit --- .gitignore | 119 +++++++++++++++ Pipfile | 20 +++ Pipfile.lock | 92 ++++++++++++ README.md | 57 ++++++++ model.env | 4 + populate.py | 138 ++++++++++++++++++ requirements.txt | 9 ++ stock_api/manage.py | 15 ++ stock_api/stock_api/__init__.py | 0 .../stock_api/apps/daily_prices/__init__.py | 0 .../stock_api/apps/daily_prices/admin.py | 3 + stock_api/stock_api/apps/daily_prices/apps.py | 5 + .../daily_prices/migrations/0001_initial.py | 30 ++++ .../migrations/0002_auto_20180811_0436.py | 18 +++ .../apps/daily_prices/migrations/__init__.py | 0 .../stock_api/apps/daily_prices/models.py | 14 ++ .../apps/daily_prices/serializers.py | 7 + .../stock_api/apps/daily_prices/tests.py | 3 + stock_api/stock_api/apps/daily_prices/urls.py | 0 .../stock_api/apps/daily_prices/views.py | 9 ++ stock_api/stock_api/apps/tickers/__init__.py | 0 stock_api/stock_api/apps/tickers/admin.py | 3 + stock_api/stock_api/apps/tickers/apps.py | 5 + .../apps/tickers/migrations/0001_initial.py | 20 +++ .../apps/tickers/migrations/__init__.py | 0 stock_api/stock_api/apps/tickers/models.py | 8 + .../stock_api/apps/tickers/serializers.py | 8 + stock_api/stock_api/apps/tickers/tests.py | 3 + stock_api/stock_api/apps/tickers/urls.py | 1 + stock_api/stock_api/apps/tickers/views.py | 8 + stock_api/stock_api/settings.py | 124 ++++++++++++++++ stock_api/stock_api/urls.py | 33 +++++ stock_api/stock_api/wsgi.py | 16 ++ 33 files changed, 772 insertions(+) create mode 100644 .gitignore create mode 100644 Pipfile create mode 100644 Pipfile.lock create mode 100644 README.md create mode 100644 model.env create mode 100644 populate.py create mode 100644 requirements.txt create mode 100755 stock_api/manage.py create mode 100644 stock_api/stock_api/__init__.py create mode 100644 stock_api/stock_api/apps/daily_prices/__init__.py create mode 100644 stock_api/stock_api/apps/daily_prices/admin.py create mode 100644 stock_api/stock_api/apps/daily_prices/apps.py create mode 100644 stock_api/stock_api/apps/daily_prices/migrations/0001_initial.py create mode 100644 stock_api/stock_api/apps/daily_prices/migrations/0002_auto_20180811_0436.py create mode 100644 stock_api/stock_api/apps/daily_prices/migrations/__init__.py create mode 100644 stock_api/stock_api/apps/daily_prices/models.py create mode 100644 stock_api/stock_api/apps/daily_prices/serializers.py create mode 100644 stock_api/stock_api/apps/daily_prices/tests.py create mode 100644 stock_api/stock_api/apps/daily_prices/urls.py create mode 100644 stock_api/stock_api/apps/daily_prices/views.py create mode 100644 stock_api/stock_api/apps/tickers/__init__.py create mode 100644 stock_api/stock_api/apps/tickers/admin.py create mode 100644 stock_api/stock_api/apps/tickers/apps.py create mode 100644 stock_api/stock_api/apps/tickers/migrations/0001_initial.py create mode 100644 stock_api/stock_api/apps/tickers/migrations/__init__.py create mode 100644 stock_api/stock_api/apps/tickers/models.py create mode 100644 stock_api/stock_api/apps/tickers/serializers.py create mode 100644 stock_api/stock_api/apps/tickers/tests.py create mode 100644 stock_api/stock_api/apps/tickers/urls.py create mode 100644 stock_api/stock_api/apps/tickers/views.py create mode 100644 stock_api/stock_api/settings.py create mode 100644 stock_api/stock_api/urls.py create mode 100644 stock_api/stock_api/wsgi.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c057519 --- /dev/null +++ b/.gitignore @@ -0,0 +1,119 @@ +## Model .gitignore File for Python Projects### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class +*.pyc +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +docs/_static/ +docs/_templates + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +#pydev +.project +.project/ +.pydevproject +.pydevproject/ + +#pycharm +.idea/ + +#other +db.sqlite3 +.vscode/ +*.code-workspace \ No newline at end of file diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..38ac154 --- /dev/null +++ b/Pipfile @@ -0,0 +1,20 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +requests = "==2.19.1" +certifi = "==2018.4.16" +chardet = "==3.0.4" +idna = "==2.7" +python-decouple = "==3.1" +"urllib3" = "==1.23" +djangorestframework = "==3.8.2" +pytz = "==2018.5" +Django = "==2.1.1" + +[dev-packages] + +[requires] +python_version = "3.6" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..d97480a --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,92 @@ +{ + "_meta": { + "hash": { + "sha256": "5098e6f76b564cb8d219504a72a723ca16c395d3defb83ec75cda3168f6162f3" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.6" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "certifi": { + "hashes": [ + "sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7", + "sha256:9fa520c1bacfb634fa7af20a76bcbd3d5fb390481724c597da32c719a7dca4b0" + ], + "index": "pypi", + "version": "==2018.4.16" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "index": "pypi", + "version": "==3.0.4" + }, + "django": { + "hashes": [ + "sha256:04f2e423f2e60943c02bd2959174b844f7d1bcd19eabb7f8e4282999958021fd", + "sha256:e1cc1cd6b658aa4e052f5f2b148bfda08091d7c3558529708342e37e4e33f72c" + ], + "index": "pypi", + "version": "==2.1.1" + }, + "djangorestframework": { + "hashes": [ + "sha256:b6714c3e4b0f8d524f193c91ecf5f5450092c2145439ac2769711f7eba89a9d9", + "sha256:c375e4f95a3a64fccac412e36fb42ba36881e52313ec021ef410b40f67cddca4" + ], + "index": "pypi", + "version": "==3.8.2" + }, + "idna": { + "hashes": [ + "sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e", + "sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16" + ], + "index": "pypi", + "version": "==2.7" + }, + "python-decouple": { + "hashes": [ + "sha256:1317df14b43efee4337a4aa02914bf004f010cd56d6c4bd894e6474ec8c4fe2d" + ], + "index": "pypi", + "version": "==3.1" + }, + "pytz": { + "hashes": [ + "sha256:a061aa0a9e06881eb8b3b2b43f05b9439d6583c206d0a6c340ff72a7b6669053", + "sha256:ffb9ef1de172603304d9d2819af6f5ece76f2e85ec10692a524dd876e72bf277" + ], + "index": "pypi", + "version": "==2018.5" + }, + "requests": { + "hashes": [ + "sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1", + "sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a" + ], + "index": "pypi", + "version": "==2.19.1" + }, + "urllib3": { + "hashes": [ + "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf", + "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5" + ], + "index": "pypi", + "version": "==1.23" + } + }, + "develop": {} +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..c48fa42 --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +# Introduction + +This project aggregates historic stock data, relevant information, and news concerning specific publicly traded companies selected by the user. + +The goal of this project is to provide the user with an automatically updating API containing only the data they care about. + +# Quick Start + +## The API +* Clone this repo in the directory you want to place it in. +* Create a new virtual environment and activate it. +* Run `pip install -r requirements.txt`. +* Rename the `model.env` file to `.env` +* Retrieve a free API key from https://www.alphavantage.co +* Place your API key after `API_KEY=` in your `.env` file DO NOT PLACE IT IN QUOTES. +* Alter the setting for `URL_BASE=` to `http://localhost:8000` in your `.env` file DO NOT PLACE IT IN QUOTES. +* Start the Django development server `python manage.py runserver`. +* Navigate to `http://localhost:8000/api/stocks_followed/` and add the ticker symbol of a stock you want data on. +* Manually (for now) run the update scripts, which are located in `popoulate.py` and enjoy. + +# A Short Example + +Lets say you want all the stock data on Google (GOOG). After adding GOOG to `http://localhost:8000/api/stocks_followed/` do the following with your virtual environment activated. + +## Adding All Data + +``` python +>>> from populate import StockPopulate +>>> stock = StockPopulate('GOOG') + +# Create a variable for your stock data +>>> data = stock.get_all_data() +# Normalizes, and creates an iterable of the data, to post to our API +>>> stream = stock.normalize_all_data(data) +# Post the data to our API +>>>stock.post_all_data(stream) + +# Or if you prefer a one liner + stock.post_all_data(stock.normalize_all_data(stock.get_all_data())) +``` + +## Updating Data With Specific Date + +```python +>>> from populate import StockPopulate +>>> from datetime import date + +>>> stock = StockPopulate('GOOG') +# TODO check to see if the market was active that day. +# Pass the date argument as a string +date = str(date.today()) +# or +date = '2018-09-28' +data = stock.get_daily_data(date) +# post the data +stock.populate_updated_data(data) +``` \ No newline at end of file diff --git a/model.env b/model.env new file mode 100644 index 0000000..daf8c57 --- /dev/null +++ b/model.env @@ -0,0 +1,4 @@ +# https://simpleisbetterthancomplex.com/2015/11/26/package-of-the-week-python-decouple.html + +API_KEY=Alpha Vantage API key here +URL_BASE=Base URL here \ No newline at end of file diff --git a/populate.py b/populate.py new file mode 100644 index 0000000..6526e06 --- /dev/null +++ b/populate.py @@ -0,0 +1,138 @@ +import requests +from decouple import config +from datetime import date + + +class StockPopulate(object): + + """ + Uses the Python 3 requests package to download historic stock data and populate a django database + requires an api key from https://www.alphavantage.co/documentation/ + + :param symbol: the stock ticker symbol. + :arg api_key: your alphavantage api key. + :arg base_endpoint: the base of the endpoint you want to send post requests to. + + Change the above arguments in your .env file. + + """ + + def __init__(self, symbol, api_key=config('API_KEY'), base_endpoint=config('URL_BASE')): + + self.symbol = symbol + self.api_key = api_key + self.base_endpoint = base_endpoint + # self.date = date + + def get_all_data(self): + """ + Retrieves all daily historic stock proce data from the alphavantage api. + """ + + _parameters = { + 'function': 'TIME_SERIES_DAILY', + 'symbol': self.symbol, + 'outputsize': 'full', + 'apikey': self.api_key, + } + _url = 'https://www.alphavantage.co/query?' + + r = requests.get(_url, params=_parameters) + + if r.status_code == 200: + return r.json() + + else: + raise ConnectionError + + def get_daily_data(self, date): + + """ + Retrieves stock data for a specific date from the alphavantage stock api. + :arg date: The date you wish to receive stock data as a string. + + example usage date = str(datetime.date.today()) + """ + + _parameters = { + 'function': 'TIME_SERIES_DAILY', + 'symbol': self.symbol, + 'outputsize': 'compact', + 'apikey': self.api_key, + } + + _url = 'https://www.alphavantage.co/query?' + + r = requests.get(_url, params=_parameters) + + if r.status_code == 200: + + data = r.json()['Time Series (Daily)'] + + if date in data: + self.date = date + return data[date] + + else: + raise KeyError + + else: + raise ConnectionError + + def normalize_all_data(self, request): + """ + :arg request: use self.get_all_data() + A generator function used to iterate through self.get_all data() and prepare that data for posting to our api. + + """ + + for date, info in request['Time Series (Daily)'].items(): + self.date = date + + data = { + 'open': info.get('1. open'), + 'close': info.get('4. close'), + 'high': info.get('2. high'), + 'low': info.get('3. low'), + 'volume': info.get('5. volume'), + 'date': self.date, + 'symbol': self.symbol, + } + + yield data + + def post_all_data(self, data): + + """ + :arg data: The iterable, normalized data you wish to post to our api. + + example: + stock = StockPopulate('GOOG') + stock.post_all_data(stock.normalize_all_data(stock.get_all_data())) + """ + + for d in data: + + p = requests.post(self.base_endpoint + '/api/daily_prices/', data=d) + + print(p.status_code, p.text) + + def populate_updated_data(self, request): + + """ + use get_daily_data(str(date.today())) as request argument + """ + + data = { + 'open': request['1. open'], + 'close': request['4. close'], + 'high': request['2. high'], + 'low': request['3. low'], + 'volume': request['5. volume'], + 'date': self.date, + 'symbol': self.symbol, + } + + p = requests.post(self.base_endpoint + '/api/daily_prices/', data=data) + + print(p.status_code, p.text) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..9d747fd --- /dev/null +++ b/requirements.txt @@ -0,0 +1,9 @@ +certifi==2018.4.16 +chardet==3.0.4 +Django==2.1.1 +djangorestframework==3.8.2 +idna==2.7 +python-decouple==3.1 +pytz==2018.5 +requests==2.19.1 +urllib3==1.23 diff --git a/stock_api/manage.py b/stock_api/manage.py new file mode 100755 index 0000000..0e63b1a --- /dev/null +++ b/stock_api/manage.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == '__main__': + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'stock_api.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) diff --git a/stock_api/stock_api/__init__.py b/stock_api/stock_api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/stock_api/stock_api/apps/daily_prices/__init__.py b/stock_api/stock_api/apps/daily_prices/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/stock_api/stock_api/apps/daily_prices/admin.py b/stock_api/stock_api/apps/daily_prices/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/stock_api/stock_api/apps/daily_prices/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/stock_api/stock_api/apps/daily_prices/apps.py b/stock_api/stock_api/apps/daily_prices/apps.py new file mode 100644 index 0000000..150c8e4 --- /dev/null +++ b/stock_api/stock_api/apps/daily_prices/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class DailyPricesConfig(AppConfig): + name = 'daily_prices' diff --git a/stock_api/stock_api/apps/daily_prices/migrations/0001_initial.py b/stock_api/stock_api/apps/daily_prices/migrations/0001_initial.py new file mode 100644 index 0000000..2c602c6 --- /dev/null +++ b/stock_api/stock_api/apps/daily_prices/migrations/0001_initial.py @@ -0,0 +1,30 @@ +# Generated by Django 2.1 on 2018-08-11 04:28 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('tickers', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='DailyPrices', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateField()), + ('timestamp', models.DateTimeField(auto_now_add=True)), + ('open', models.FloatField()), + ('close', models.FloatField()), + ('high', models.FloatField()), + ('low', models.FloatField()), + ('volume', models.FloatField()), + ('symbol', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tickers.Ticker')), + ], + ), + ] diff --git a/stock_api/stock_api/apps/daily_prices/migrations/0002_auto_20180811_0436.py b/stock_api/stock_api/apps/daily_prices/migrations/0002_auto_20180811_0436.py new file mode 100644 index 0000000..7aa49ee --- /dev/null +++ b/stock_api/stock_api/apps/daily_prices/migrations/0002_auto_20180811_0436.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1 on 2018-08-11 04:36 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('daily_prices', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='dailyprices', + name='timestamp', + field=models.DateTimeField(auto_now=True), + ), + ] diff --git a/stock_api/stock_api/apps/daily_prices/migrations/__init__.py b/stock_api/stock_api/apps/daily_prices/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/stock_api/stock_api/apps/daily_prices/models.py b/stock_api/stock_api/apps/daily_prices/models.py new file mode 100644 index 0000000..99ca1aa --- /dev/null +++ b/stock_api/stock_api/apps/daily_prices/models.py @@ -0,0 +1,14 @@ +from django.db import models +from stock_api.apps.tickers.models import Ticker + +class DailyPrices(models.Model): + + symbol = models.ForeignKey(Ticker, on_delete=models.CASCADE, to_field='symbol') + date = models.DateField() + timestamp = models.DateTimeField(auto_now=True) + open = models.FloatField() + close = models.FloatField() + high = models.FloatField() + low = models.FloatField() + volume = models.FloatField() + diff --git a/stock_api/stock_api/apps/daily_prices/serializers.py b/stock_api/stock_api/apps/daily_prices/serializers.py new file mode 100644 index 0000000..acbf9ae --- /dev/null +++ b/stock_api/stock_api/apps/daily_prices/serializers.py @@ -0,0 +1,7 @@ +from rest_framework import serializers +from stock_api.apps.daily_prices.models import DailyPrices + +class DailyPricesSerializer(serializers.ModelSerializer): + class Meta: + model = DailyPrices + fields = '__all__' \ No newline at end of file diff --git a/stock_api/stock_api/apps/daily_prices/tests.py b/stock_api/stock_api/apps/daily_prices/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/stock_api/stock_api/apps/daily_prices/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/stock_api/stock_api/apps/daily_prices/urls.py b/stock_api/stock_api/apps/daily_prices/urls.py new file mode 100644 index 0000000..e69de29 diff --git a/stock_api/stock_api/apps/daily_prices/views.py b/stock_api/stock_api/apps/daily_prices/views.py new file mode 100644 index 0000000..630ba12 --- /dev/null +++ b/stock_api/stock_api/apps/daily_prices/views.py @@ -0,0 +1,9 @@ +from rest_framework import viewsets +from .models import DailyPrices +from .serializers import DailyPricesSerializer + + +class DailyPricesViewSet(viewsets.ModelViewSet): + + queryset = DailyPrices.objects.all() + serializer_class = DailyPricesSerializer diff --git a/stock_api/stock_api/apps/tickers/__init__.py b/stock_api/stock_api/apps/tickers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/stock_api/stock_api/apps/tickers/admin.py b/stock_api/stock_api/apps/tickers/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/stock_api/stock_api/apps/tickers/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/stock_api/stock_api/apps/tickers/apps.py b/stock_api/stock_api/apps/tickers/apps.py new file mode 100644 index 0000000..5e57fd5 --- /dev/null +++ b/stock_api/stock_api/apps/tickers/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class TickersConfig(AppConfig): + name = 'tickers' diff --git a/stock_api/stock_api/apps/tickers/migrations/0001_initial.py b/stock_api/stock_api/apps/tickers/migrations/0001_initial.py new file mode 100644 index 0000000..bf22fec --- /dev/null +++ b/stock_api/stock_api/apps/tickers/migrations/0001_initial.py @@ -0,0 +1,20 @@ +# Generated by Django 2.1 on 2018-08-09 21:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Ticker', + fields=[ + ('symbol', models.CharField(max_length=10, primary_key=True, serialize=False)), + ], + ), + ] diff --git a/stock_api/stock_api/apps/tickers/migrations/__init__.py b/stock_api/stock_api/apps/tickers/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/stock_api/stock_api/apps/tickers/models.py b/stock_api/stock_api/apps/tickers/models.py new file mode 100644 index 0000000..c74c902 --- /dev/null +++ b/stock_api/stock_api/apps/tickers/models.py @@ -0,0 +1,8 @@ +from django.db import models + +class Ticker(models.Model): + + symbol = models.CharField(primary_key=True, max_length=10) + + def __str__(self): + return self.symbol \ No newline at end of file diff --git a/stock_api/stock_api/apps/tickers/serializers.py b/stock_api/stock_api/apps/tickers/serializers.py new file mode 100644 index 0000000..6cf75c2 --- /dev/null +++ b/stock_api/stock_api/apps/tickers/serializers.py @@ -0,0 +1,8 @@ +from rest_framework import serializers +from stock_api.apps.tickers.models import Ticker + + +class TickerSerializer(serializers.ModelSerializer): + class Meta: + model = Ticker + fields = '__all__' \ No newline at end of file diff --git a/stock_api/stock_api/apps/tickers/tests.py b/stock_api/stock_api/apps/tickers/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/stock_api/stock_api/apps/tickers/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/stock_api/stock_api/apps/tickers/urls.py b/stock_api/stock_api/apps/tickers/urls.py new file mode 100644 index 0000000..39b3350 --- /dev/null +++ b/stock_api/stock_api/apps/tickers/urls.py @@ -0,0 +1 @@ +from django.conf.urls import url diff --git a/stock_api/stock_api/apps/tickers/views.py b/stock_api/stock_api/apps/tickers/views.py new file mode 100644 index 0000000..d092d47 --- /dev/null +++ b/stock_api/stock_api/apps/tickers/views.py @@ -0,0 +1,8 @@ +from rest_framework import viewsets +from .models import Ticker +from .serializers import TickerSerializer + +class TickerViewSet(viewsets.ModelViewSet): + + queryset = Ticker.objects.all() + serializer_class = TickerSerializer diff --git a/stock_api/stock_api/settings.py b/stock_api/stock_api/settings.py new file mode 100644 index 0000000..d06a8d1 --- /dev/null +++ b/stock_api/stock_api/settings.py @@ -0,0 +1,124 @@ +""" +Django settings for stock_api project. + +Generated by 'django-admin startproject' using Django 2.1. + +For more information on this file, see +https://docs.djangoproject.com/en/2.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/2.1/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'rnih-u=cxh^_qs^_*4&9#mp#_s29640d*k2yc*31m_j2erhf9&' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'rest_framework', + #program apps + 'stock_api.apps.tickers', + 'stock_api.apps.daily_prices', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'stock_api.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'stock_api.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/2.1/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/2.1/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/2.1/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/stock_api/stock_api/urls.py b/stock_api/stock_api/urls.py new file mode 100644 index 0000000..022be8a --- /dev/null +++ b/stock_api/stock_api/urls.py @@ -0,0 +1,33 @@ +"""stock_api URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/2.1/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path +from django.conf.urls import include, url +from rest_framework.routers import DefaultRouter + +# app urls +from stock_api.apps.tickers import views as ticker_views +from stock_api.apps.daily_prices import views as daily_views + + +router = DefaultRouter() +router.register(r'stocks_followed', ticker_views.TickerViewSet) +router.register(r'daily_prices', daily_views.DailyPricesViewSet) + +urlpatterns = [ + path('admin/', admin.site.urls), + url(r'api/', include(router.urls)) +] diff --git a/stock_api/stock_api/wsgi.py b/stock_api/stock_api/wsgi.py new file mode 100644 index 0000000..6157eae --- /dev/null +++ b/stock_api/stock_api/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for stock_api project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'stock_api.settings') + +application = get_wsgi_application()