-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #39 from JJtan2002/watchlist
Base implementation of watchlist and resources
- Loading branch information
Showing
26 changed files
with
668 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from celery import Celery | ||
from celery.schedules import crontab | ||
|
||
app = Celery('tasks', broker='redis://localhost:6379/0', backend='redis://localhost:6379/0') | ||
|
||
app.conf.beat_schedule = { | ||
'fetch_stock_data': { | ||
'task': 'watchlist.tasks.fetch_stock_data', | ||
'schedule': crontab(hour=0, minute=0), # Adjust this based on your requirements | ||
}, | ||
} | ||
|
||
app.conf.timezone = 'UTC +8' | ||
|
||
if __name__ == '__main__': | ||
app.start() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import os | ||
import django | ||
from datetime import datetime | ||
|
||
# Set up Django environment | ||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cashflow.settings') | ||
django.setup() | ||
|
||
# Import models after setting up Django | ||
|
||
from watchlist.models import StockData # Adjusted import path | ||
|
||
# Example data to insert | ||
test_data = [ | ||
{'ticker': 'AAPL', 'close_price': 150.0, 'date': datetime(2023, 7, 7)}, | ||
{'ticker': 'MSFT', 'close_price': 280.0, 'date': datetime(2023, 7, 7)}, | ||
{'ticker': 'GOOGL', 'close_price': 2700.0, 'date': datetime(2023, 7, 7)}, | ||
] | ||
|
||
# Insert test data | ||
for data in test_data: | ||
stock_data, created = StockData.objects.update_or_create( | ||
id=f"{data['ticker']}_{data['date']}", | ||
defaults={ | ||
'ticker': data['ticker'], | ||
'close_price': data['close_price'], | ||
'date': data['date'], | ||
}, | ||
) | ||
|
||
print("Test data inserted successfully!") |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
from celery import shared_task | ||
import requests | ||
from sqlalchemy import create_engine | ||
from sqlalchemy.orm import sessionmaker | ||
from datetime import datetime | ||
from backend.models import StockData | ||
|
||
API_KEY = 'your_api_key' | ||
STOCKS = ['AAPL', 'MSFT', 'GOOGL'] # Top 25 by market cap in S&P 500 | ||
|
||
# PostgreSQL database configuration | ||
DATABASE_URI = 'postgresql://username:password@localhost:5432/yourdatabase' | ||
engine = create_engine(DATABASE_URI) | ||
Session = sessionmaker(bind=engine) | ||
session = Session() | ||
|
||
@shared_task | ||
def fetch_stock_data(): | ||
for ticker in STOCKS: | ||
response = requests.get(f'https://api.example.com/stock/{ticker}/quote', params={'api_key': API_KEY}) | ||
data = response.json() | ||
close_price = data['close'] | ||
date = datetime.strptime(data['date'], '%Y-%m-%dT%H:%M:%S') | ||
|
||
stock_data = StockData( | ||
id=f'{ticker}_{date}', | ||
ticker=ticker, | ||
close_price=close_price, | ||
date=date | ||
) | ||
|
||
session.merge(stock_data) | ||
session.commit() |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from django.contrib import admin | ||
from .models import Watchlist, StockData | ||
|
||
# Register your models here. | ||
admin.site.register(Watchlist) | ||
admin.site.register(StockData) | ||
# Register your models here. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class WatchlistConfig(AppConfig): | ||
default_auto_field = 'django.db.models.BigAutoField' | ||
name = 'watchlist' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Generated by Django 5.0.6 on 2024-07-10 15:26 | ||
|
||
import django.db.models.deletion | ||
from django.conf import settings | ||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [ | ||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='StockData', | ||
fields=[ | ||
('id', models.CharField(max_length=100, primary_key=True, serialize=False)), | ||
('ticker', models.CharField(max_length=10)), | ||
('close_price', models.FloatField()), | ||
('date', models.DateTimeField()), | ||
], | ||
), | ||
migrations.CreateModel( | ||
name='Watchlist', | ||
fields=[ | ||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('symbol', models.CharField(max_length=10)), | ||
('entry_price', models.DecimalField(decimal_places=2, max_digits=15)), | ||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), | ||
], | ||
), | ||
] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import users.models | ||
from django.db import models | ||
|
||
class Watchlist(models.Model): | ||
user = models.ForeignKey(users.models.User, on_delete=models.CASCADE) | ||
symbol = models.CharField(max_length=10) | ||
entry_price = models.DecimalField(decimal_places=2, max_digits=15) | ||
|
||
def __str__(self): | ||
return f'{self.user.username} - {self.symbol}' | ||
|
||
@staticmethod | ||
def create_from_json(data: dict, user_pk: int): | ||
try: | ||
user: users.models.User = users.models.User.objects.get(pk=user_pk) | ||
except users.models.User.DoesNotExist: | ||
raise PermissionError() | ||
|
||
watchlist = Watchlist() | ||
watchlist.user = user | ||
|
||
if not data.get("ticker"): | ||
raise Exception("Ticker is required.") | ||
watchlist.symbol = data.get("ticker") | ||
|
||
if data.get("entry") is not None: | ||
watchlist.entry_price = data.get("entry") | ||
|
||
watchlist.save() | ||
|
||
return watchlist | ||
|
||
class StockData(models.Model): | ||
id = models.CharField(max_length=100, primary_key=True) | ||
ticker = models.CharField(max_length=10) | ||
close_price = models.FloatField() | ||
date = models.DateTimeField() | ||
|
||
def __str__(self): | ||
return f"{self.ticker} - {self.date}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from rest_framework import serializers | ||
from .models import Watchlist, StockData | ||
|
||
class WatchlistSerializer(serializers.ModelSerializer): | ||
class Meta: | ||
model = Watchlist | ||
fields = ['id', 'symbol', 'entry_price'] | ||
|
||
class StockDataSerializer(serializers.ModelSerializer): | ||
class Meta: | ||
model = StockData | ||
fields = ['id', 'ticker', 'close_price', 'date'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
from celery import shared_task | ||
import requests | ||
from datetime import datetime | ||
from .models import StockData | ||
|
||
API_KEY = 'your_api_key' | ||
STOCKS = ['AAPL', 'MSFT', 'GOOGL'] # Top 25 by market cap in S&P 500 | ||
|
||
@shared_task | ||
def fetch_stock_data(): | ||
for ticker in STOCKS: | ||
response = requests.get(f'https://api.example.com/stock/{ticker}/quote', params={'api_key': API_KEY}) | ||
data = response.json() | ||
close_price = data['close'] | ||
date = datetime.strptime(data['date'], '%Y-%m-%dT%H:%M:%S') | ||
|
||
stock_data, created = StockData.objects.update_or_create( | ||
id=f'{ticker}_{date}', | ||
defaults={ | ||
'ticker': ticker, | ||
'close_price': close_price, | ||
'date': date, | ||
}, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from django.test import TestCase | ||
|
||
# Create your tests here. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from django.urls import path | ||
from .views import WatchlistAPIView, StockDataAPIView | ||
|
||
urlpatterns = [ | ||
path('watchlist/', WatchlistAPIView.as_view(), name='watchlist'), | ||
path('watchlist/<int:watchlist_pk>/', WatchlistAPIView.as_view(), name='watchlist'), | ||
path('stockdata/', StockDataAPIView.as_view(), name='stockdata'), | ||
path('stockdata/<int:stock_data_pk>/', StockDataAPIView.as_view(), name='stockdata-detail'), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from typing import Union | ||
from django.http import JsonResponse | ||
|
||
|
||
def custom_server_error_response(error_message: str = '', status_code=500): | ||
if 500 <= status_code < 600: | ||
return JsonResponse({'success': False, 'message': error_message}, status=status_code) | ||
raise Exception # TODO: implement custom exceptions | ||
|
||
|
||
def custom_user_error_response(error_message: str = '', status_code=400): | ||
if 400 <= status_code < 500: | ||
return JsonResponse({'success': False, 'message': error_message}, status=status_code) | ||
raise Exception # TODO: implement custom exceptions | ||
|
||
|
||
def custom_success_response(success_message: Union[str, dict] = '', status_code=200): | ||
if 200 <= status_code < 300: | ||
response = {'success': True, 'message': success_message} | ||
return JsonResponse(response, status=status_code) | ||
|
||
raise Exception # TODO: implement custom exceptions |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
from django.db.models import QuerySet, Sum | ||
from users.models import User | ||
from rest_framework.response import Response | ||
from rest_framework.decorators import api_view, permission_classes | ||
from rest_framework.views import APIView | ||
from rest_framework.permissions import IsAuthenticated | ||
from .serializers import WatchlistSerializer, StockDataSerializer | ||
from datetime import date | ||
from watchlist.models import Watchlist, StockData | ||
from .utils import custom_success_response, custom_server_error_response | ||
|
||
class WatchlistAPIView(APIView): | ||
permission_classes = (IsAuthenticated,) | ||
serializer_class = WatchlistSerializer | ||
|
||
def get(self, request): | ||
user: User = request.user | ||
watchlists = Watchlist.objects.filter(user=self.request.user) | ||
|
||
|
||
serializer = self.serializer_class(watchlists, many=True) | ||
return Response(serializer.data) | ||
|
||
def post(self, request): | ||
user: User = request.user | ||
|
||
data = request.data | ||
Watchlist.create_from_json(data, user_pk=user.pk) | ||
|
||
return custom_success_response("Ticker added with success!") | ||
|
||
def delete(self, request, watchlist_pk): | ||
user: User = request.user | ||
|
||
if not watchlist_pk: | ||
return custom_server_error_response("No watchlist id was given. Please try again.") | ||
|
||
watchlist = Watchlist.objects.get(pk=watchlist_pk) | ||
|
||
watchlist.delete() | ||
return custom_success_response("Ticker deleted with success!") | ||
|
||
class StockDataAPIView(APIView): | ||
permission_classes = (IsAuthenticated,) | ||
serializer_class = StockDataSerializer | ||
|
||
def get(self, request): | ||
stock_data = StockData.objects.all() | ||
serializer = self.serializer_class(stock_data, many=True) | ||
return Response(serializer.data) |
Oops, something went wrong.