Skip to content

Commit

Permalink
Merge branch 'dev' of https://github.com/bounswe/bounswe2024group2 in…
Browse files Browse the repository at this point in the history
…to dev
  • Loading branch information
hikasap committed Dec 16, 2024
2 parents 09aa5c4 + b883426 commit 19f7294
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 47 deletions.
21 changes: 12 additions & 9 deletions backend/marketfeed/management/commands/generate_posts.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
from onboarding.models import User

class Command(BaseCommand):
help = 'Generate fake data for the Post model'

def add_arguments(self, parser):
parser.add_argument('total', type=int, help='Number of fake posts to generate')
help = 'Generate fake data for the Post model if there are fewer than 100 posts in the database'

def handle(self, *args, **kwargs):
fake = Faker()
total = kwargs['total']
existing_posts = Post.objects.count()

if existing_posts>=100:
self.stdout.write(self.style.SUCCESS('There are already 100 or more posts in the database. No new posts created.'))
return

economy_topics = [
f"Why is everyone talking about {fake.bs()}?"[:100],
Expand Down Expand Up @@ -61,10 +62,11 @@ def handle(self, *args, **kwargs):

users = list(User.objects.all())
if not users:
self.stdout.write(self.style.ERROR('No users found in the database. Please create some users first.'))
return
fake_user = User.objects.create_user(username=fake.user_name(), email=fake.email(), password="Password123*")
users = [fake_user]
self.stdout.write(self.style.WARNING('No users found in the database. A new user has been created.'))

for _ in range(total):
for _ in range(100):
title = random.choice(economy_topics)
content = random.choice(economy_contents)
author = random.choice(users)
Expand All @@ -78,4 +80,5 @@ def handle(self, *args, **kwargs):
)
post.save()

self.stdout.write(self.style.SUCCESS(f'Successfully created {total} fake posts.'))
self.stdout.write(self.style.SUCCESS(f'Successfully created 100 fake posts.'))

134 changes: 111 additions & 23 deletions backend/news/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@
from rest_framework.response import Response
from .serializers import NewsSerializer
from bs4 import BeautifulSoup
from html.parser import HTMLParser


class HTMLContentParser(HTMLParser):
def __init__(self):
super().__init__()
self.image_url = None

def handle_starttag(self, tag, attrs):
if tag == "img" and self.image_url is None:
for attr in attrs:
if attr[0] == "src":
self.image_url = attr[1]


class NewsView(generics.CreateAPIView):
serializer_class = NewsSerializer
Expand All @@ -14,52 +28,126 @@ def post(self, request):

urls = {
"financial times": "https://www.ft.com/rss/home",
"stock market": "https://www.spglobal.com/spdji/en/rss/rss-details/?rssFeedName=all-indices",
"cryptocurrency": "https://cointelegraph.com/rss"
"cryptocurrency": "https://cointelegraph.com/rss",
"comprehensive financial news": "http://feeds.benzinga.com/benzinga",
"financeasia": "https://www.financeasia.com/rss/latest",
"expert analysis": "https://moneyweek.com/feed/all",
"turkey": "https://www.ntv.com.tr/ekonomi.rss",
}

if feed_name not in urls:
return Response(
{"error": "Feed name not found. Available options are: 'financial times', 'stock market', 'cryptocurrency'."},
status=status.HTTP_404_NOT_FOUND
{"error": f"Feed name not found. Available options are: {', '.join(urls.keys())}."},
status=status.HTTP_404_NOT_FOUND,
)

url = urls[feed_name]
feed = feedparser.parse(url)
try:
feed = feedparser.parse(url)
num_entries = 30
entries = feed.entries[:num_entries] if num_entries else feed.entries
except Exception as e:
return Response({"error": f"Failed to fetch feed: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

response = []
for entry in feed.entries:
if feed_name == "financial times":

response_entry = {
for entry in entries:
try:
if feed_name == "financial times":
response_entry = {
"title": entry.get("title", "No title available"),
"link": entry.get("link", "#"),
"author": entry.get("author", "Financial Times"),
"published": entry.get("published", "No publish date available"),
"description": entry.get("summary", "No summary available"),
"image": entry.get("media_thumbnail")[0]['url'] if entry.get("media_thumbnail") and len(entry.get("media_thumbnail")) > 0 else ""
"image": entry.get("media_thumbnail")[0]['url']
if entry.get("media_thumbnail") and len(entry.get("media_thumbnail")) > 0
else "",
}

elif feed_name == "cryptocurrency":

html_content = entry['summary_detail']['value']
elif feed_name == "cryptocurrency":
html_content = entry['summary_detail']['value']
soup = BeautifulSoup(html_content, 'html.parser')
paragraphs = soup.find_all('p')
summary_text = paragraphs[1].get_text(strip=True) if len(paragraphs) > 1 else ''
response_entry = {
"title": entry.get("title", "No title available"),
"link": entry.get("link", "#"),
"author": entry.get("author", "Unknown"),
"published": entry.get("published", "No publish date available"),
"description": summary_text,
"image": entry.get("media_content")[0]['url']
if entry.get("media_content") and len(entry.get("media_content")) > 0
else "",
}

soup = BeautifulSoup(html_content, 'html.parser')
elif feed_name == "comprehensive financial news":
media_content = entry.get("media_content", [])
image_url = media_content[0]["url"] if media_content and isinstance(media_content, list) else ""
response_entry = {
"title": entry.get("title", "No title available"),
"link": entry.get("link", "#"),
"author": entry.get("dc:creator", "Unknown"),
"published": entry.get("pubDate", "No publish date available"),
"description": BeautifulSoup(entry.get("description", ""), "html.parser").get_text(strip=True),
"image": image_url,
}

paragraphs = soup.find_all('p')
if len(paragraphs) > 1:
summary_text = paragraphs[1].get_text(strip=True)
else:
summary_text = ''
response_entry = {
elif feed_name == "financeasia":
raw_description = entry.get("description", "")
soup = BeautifulSoup(raw_description, "html.parser")
image_tag = soup.find("img")
image_url = image_tag["src"] if image_tag else ""
clean_description = soup.get_text(strip=True)
response_entry = {
"title": entry.get("title", "No title available"),
"link": entry.get("link", "#"),
"author": entry.get("author", "Unknown"),
"published": entry.get("pubDate", "No publish date available"),
"description": clean_description,
"image": image_url,
}

elif feed_name == "expert analysis":
response_entry = {
"title": entry.title,
"link": entry.link,
"author": entry.get("author", "Unknown"),
"published": entry.published if "published" in entry else "Unknown",
"description": entry.description if "description" in entry else "",
"image": "",
}
if "enclosures" in entry:
for enclosure in entry.enclosures:
if enclosure.get("type", "").startswith("image/"):
response_entry["image"] = enclosure["url"]
break

elif feed_name == "turkey":
content_list = entry.get("content", [])
content_html = content_list[0].get("value", "") if content_list and isinstance(content_list[0], dict) else ""
parser = HTMLContentParser()
parser.feed(content_html)
image_url = parser.image_url if parser.image_url else ""
soup = BeautifulSoup(content_html, "html.parser")
description_text = soup.get_text(separator="\n").strip()
response_entry = {
"title": entry.get("title", "No title available"),
"link": entry.get("link", "#"),
"author": entry.get("author", "NTV"),
"published": entry.get("published", "No publish date available"),
"description": summary_text,
"image": entry.get("media_content")[0]['url'] if entry.get("media_content") and len(entry.get("media_content")) > 0 else ""
"description": description_text,
"image": image_url,
}

response.append(response_entry)
else:
continue

response.append(response_entry)

except Exception as e:
response.append({
"title": "Error parsing entry",
"error": str(e),
})

return Response(response, status=status.HTTP_200_OK)
3 changes: 2 additions & 1 deletion docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ services:
python3 manage.py collectstatic --noinput &&
python3 manage.py update_currencies &&
python3 manage.py update_stocks &&
python3 manage.py update_indices &&
python3 manage.py update_indices &&
python3 manage.py generate_posts &&
gunicorn backend.wsgi:application --bind 0.0.0.0:8000'
restart: always
environment:
Expand Down
46 changes: 34 additions & 12 deletions mobile/src/pages/Markets.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,17 +124,23 @@ const Markets = ({ navigation }) => {

const renderStockItem = ({ item }) => (
<TouchableOpacity
style={styles.stockCard}
onPress={() => navigation.navigate('StockDetails', { id: item.id })}
>
<View>
<Text style={styles.stockCode}>{item.symbol || 'N/A'}</Text>
<Text style={styles.stockName}>{item.name || 'No Name'}</Text>
</View>
<Text style={styles.stockPrice}>
{parseFloat(item.price || 0).toFixed(2)} {item.currency?.code || ''}
</Text>
</TouchableOpacity>
style={styles.stockCard}
onPress={() => navigation.navigate('StockDetails', { id: item.id })}
>
<View style={styles.stockTextContainer}>
<Text style={styles.stockCode}>{item.symbol || 'N/A'}</Text>
<Text numberOfLines={1} ellipsizeMode="tail" style={styles.stockName}>
{item.name || 'No Name'}
</Text>
</View>
<View>
<Text style={styles.stockPrice}>
{parseFloat(item.price || 0).toFixed(2)}{' '}
{item.currency === 2 ? 'TRY' : item.currency === 3 ? 'USD' : 'N/A'}
</Text>
</View>
</TouchableOpacity>

);

if (loading) {
Expand Down Expand Up @@ -216,6 +222,22 @@ const styles = StyleSheet.create({
searchLoader: {
marginBottom: 10,
},
stockTextContainer: {
flex: 1, // Ensures the name and code take up available space
marginRight: 10, // Adds spacing between the name and the price
},
stockName: {
fontSize: 14,
color: '#333333',
marginTop: 5, // Adds spacing between the code and name
},
stockPrice: {
fontSize: 16,
fontWeight: '600',
color: '#4CAF50',
textAlign: 'right', // Aligns the price text to the right
},

loaderContainer: {
flex: 1,
justifyContent: 'center',
Expand Down Expand Up @@ -243,7 +265,7 @@ const styles = StyleSheet.create({
stockName: {
fontSize: 14,
color: '#333333',
marginTop: 4,
paddingTop: 20,
},
stockPrice: {
fontSize: 16,
Expand Down
3 changes: 2 additions & 1 deletion mobile/src/pages/PortfolioDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const PortfolioDetails = ({ route, navigation }) => {
const [selectedStock, setSelectedStock] = useState(null);
const [searchLoading, setSearchLoading] = useState(false);


const predefinedColors = ['#1E90FF', '#FFD700', '#8A2BE2', '#FF8C00', '#00CED1'];

const generateColor = () => {
Expand Down Expand Up @@ -384,7 +385,7 @@ const PortfolioDetails = ({ route, navigation }) => {

<TextInput
style={styles.input}
placeholder="Price Bought (TRY)"
placeholder={`Price Bought (${selectedStock?.currency === 2 ? 'TRY' : selectedStock?.currency === 3 ? 'USD' : ''})`}
keyboardType="numeric"
placeholderTextColor="#000"
value={priceBought}
Expand Down
Loading

0 comments on commit 19f7294

Please sign in to comment.