14 socials integration #34
@@ -0,0 +1,22 @@
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def unified_timestamp(timestamp_ms: int | None = None, timestamp_s: int | None = None) -> str:
|
||||
"""
|
||||
Transform the timestamp from milliseconds or seconds to a unified string format.
|
||||
The resulting string is a formatted string 'YYYY-MM-DD HH:MM'.
|
||||
Args:
|
||||
timestamp_ms: Timestamp in milliseconds.
|
||||
timestamp_s: Timestamp in seconds.
|
||||
Raises:
|
||||
ValueError: If neither timestamp_ms nor timestamp_s is provided.
|
||||
"""
|
||||
if timestamp_ms is not None:
|
||||
timestamp = timestamp_ms // 1000
|
||||
elif timestamp_s is not None:
|
||||
timestamp = timestamp_s
|
||||
else:
|
||||
raise ValueError("Either timestamp_ms or timestamp_s must be provided")
|
||||
assert timestamp > 0, "Invalid timestamp data received"
|
||||
|
||||
return datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M')
|
||||
@@ -1,6 +1,6 @@
|
||||
import statistics
|
||||
from datetime import datetime
|
||||
from pydantic import BaseModel
|
||||
from app.api.core import unified_timestamp
|
||||
|
||||
|
||||
class ProductInfo(BaseModel):
|
||||
@@ -64,24 +64,8 @@ class Price(BaseModel):
|
||||
"""Timestamp in format YYYY-MM-DD HH:MM"""
|
||||
|
||||
def set_timestamp(self, timestamp_ms: int | None = None, timestamp_s: int | None = None) -> None:
|
||||
"""
|
||||
Sets the timestamp from milliseconds or seconds.
|
||||
The timestamp is saved as a formatted string 'YYYY-MM-DD HH:MM'.
|
||||
Args:
|
||||
timestamp_ms: Timestamp in milliseconds.
|
||||
timestamp_s: Timestamp in seconds.
|
||||
Raises:
|
||||
ValueError: If neither timestamp_ms nor timestamp_s is provided.
|
||||
"""
|
||||
if timestamp_ms is not None:
|
||||
timestamp = timestamp_ms // 1000
|
||||
elif timestamp_s is not None:
|
||||
timestamp = timestamp_s
|
||||
else:
|
||||
raise ValueError("Either timestamp_ms or timestamp_s must be provided")
|
||||
assert timestamp > 0, "Invalid timestamp data received"
|
||||
|
||||
self.timestamp = datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M')
|
||||
""" Use the unified_timestamp function to set the timestamp."""
|
||||
self.timestamp = unified_timestamp(timestamp_ms, timestamp_s)
|
||||
|
||||
@staticmethod
|
||||
def aggregate(prices: dict[str, list['Price']]) -> list['Price']:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from pydantic import BaseModel
|
||||
from app.api.core import unified_timestamp
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +14,10 @@ class SocialPost(BaseModel):
|
||||
description: str = ""
|
||||
comments: list["SocialComment"] = []
|
||||
|
||||
def set_timestamp(self, timestamp_ms: int | None = None, timestamp_s: int | None = None) -> None:
|
||||
""" Use the unified_timestamp function to set the time."""
|
||||
self.time = unified_timestamp(timestamp_ms, timestamp_s)
|
||||
|
||||
class SocialComment(BaseModel):
|
||||
"""
|
||||
Represents a comment on a social media post.
|
||||
@@ -20,6 +25,10 @@ class SocialComment(BaseModel):
|
||||
time: str = ""
|
||||
description: str = ""
|
||||
|
||||
def set_timestamp(self, timestamp_ms: int | None = None, timestamp_s: int | None = None) -> None:
|
||||
""" Use the unified_timestamp function to set the time."""
|
||||
self.time = unified_timestamp(timestamp_ms, timestamp_s)
|
||||
|
||||
|
||||
class SocialWrapper:
|
||||
"""
|
||||
|
||||
@@ -5,6 +5,7 @@ import re
|
||||
|
|
||||
import html
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
from datetime import datetime
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
from app.api.core.social import *
|
||||
|
||||
|
||||
@@ -12,14 +13,10 @@ class ChanWrapper(SocialWrapper):
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def __time_str(self, timestamp: str) -> str:
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
"""Converte una stringa da MM/GG/AA di timestamp nel formato GG/MM/AA"""
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
if len(timestamp) < 8: return ""
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
month = timestamp[:2]
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
day = timestamp[3:5]
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
year = timestamp[6:8]
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
return f"{day}/{month}/{year}"
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
def __time_str(self, timestamp: str) -> int:
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
"""Converte una stringa da MM/GG/AA(DAY)HH:MM:SS di 4chan a millisecondi"""
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
time = datetime.strptime(timestamp, "%m/%d/%y(%a)%H:%M:%S")
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
return int(time.timestamp() * 1000)
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
|
||||
def __unformat_html_str(self, html_element: str) -> str:
|
||||
"""Pulisce il commento rimuovendo HTML e formattazioni inutili"""
|
||||
@@ -78,15 +75,16 @@ class ChanWrapper(SocialWrapper):
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
if not comment:
|
||||
continue
|
||||
|
||||
social_comment = SocialComment(time=time, description=comment)
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
social_comment = SocialComment(description=comment)
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
social_comment.set_timestamp(timestamp_ms=time)
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
comments_list.append(social_comment)
|
||||
|
||||
social_post: SocialPost = SocialPost(
|
||||
time=time,
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
title=title,
|
||||
description=thread_description,
|
||||
comments=comments_list
|
||||
)
|
||||
social_post.set_timestamp(timestamp_ms=time)
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
social_posts.append(social_post)
|
||||
|
||||
return social_posts[:limit]
|
||||
|
||||
|
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
|
||||
@@ -23,13 +23,13 @@ SUBREDDITS = [
|
||||
|
||||
def extract_post(post: Submission) -> SocialPost:
|
||||
social = SocialPost()
|
||||
social.time = str(post.created)
|
||||
social.set_timestamp(timestamp_ms=post.created)
|
||||
social.title = post.title
|
||||
social.description = post.selftext
|
||||
|
||||
for top_comment in post.comments:
|
||||
comment = SocialComment()
|
||||
comment.time = str(top_comment.created)
|
||||
comment.set_timestamp(timestamp_ms=top_comment.created)
|
||||
comment.description = top_comment.body
|
||||
social.comments.append(comment)
|
||||
|
||||
|
||||
22
tests/api/test_social_4chan.py
Normal file
@@ -0,0 +1,22 @@
|
||||
import re
|
||||
import pytest
|
||||
from app.api.social.chan import ChanWrapper
|
||||
|
||||
@pytest.mark.social
|
||||
@pytest.mark.api
|
||||
class TestChanWrapper:
|
||||
def test_initialization(self):
|
||||
wrapper = ChanWrapper()
|
||||
assert wrapper is not None
|
||||
|
||||
def test_get_top_crypto_posts(self):
|
||||
wrapper = ChanWrapper()
|
||||
posts = wrapper.get_top_crypto_posts(limit=2)
|
||||
assert isinstance(posts, list)
|
||||
assert len(posts) == 2
|
||||
for post in posts:
|
||||
assert post.title != ""
|
||||
assert post.time != ""
|
||||
assert re.match(r'\d{4}-\d{2}-\d{2}', post.time)
|
||||
assert isinstance(post.comments, list)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import os
|
||||
import re
|
||||
import pytest
|
||||
from app.api.social.reddit import MAX_COMMENTS, RedditWrapper
|
||||
|
||||
@@ -18,6 +19,8 @@ class TestRedditWrapper:
|
||||
assert len(posts) == 2
|
||||
for post in posts:
|
||||
assert post.title != ""
|
||||
assert re.match(r'\d{4}-\d{2}-\d{2}', post.time)
|
||||
|
||||
assert isinstance(post.comments, list)
|
||||
assert len(post.comments) <= MAX_COMMENTS
|
||||
for comment in post.comments:
|
||||
22
tests/api/test_social_x_api.py
Normal file
@@ -0,0 +1,22 @@
|
||||
import os
|
||||
import re
|
||||
import pytest
|
||||
from app.api.social.x import XWrapper
|
||||
|
||||
@pytest.mark.social
|
||||
@pytest.mark.api
|
||||
@pytest.mark.skipif(not os.getenv("X_API_KEY"), reason="X_API_KEY not set in environment variables")
|
||||
class TestXWrapper:
|
||||
def test_initialization(self):
|
||||
wrapper = XWrapper()
|
||||
assert wrapper is not None
|
||||
|
||||
def test_get_top_crypto_posts(self):
|
||||
wrapper = XWrapper()
|
||||
posts = wrapper.get_top_crypto_posts(limit=2)
|
||||
assert isinstance(posts, list)
|
||||
assert len(posts) == 2
|
||||
for post in posts:
|
||||
assert post.title != ""
|
||||
assert re.match(r'\d{4}-\d{2}-\d{2}', post.time)
|
||||
assert isinstance(post.comments, list)
|
||||
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore
Pulire il file che ha troppi commenti, in modo da rendere il codice più leggibile
Rimuovere inoltre la quantità di try non necessaria, dato che se l'API ha dei problemi non voglio che mi restituisca un oggetto vuoto, ma voglio l'errore