Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .DS_Store
Binary file not shown.
Binary file added .github/.DS_Store
Binary file not shown.
8 changes: 5 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ dmypy.json
# Pyre type checker
.pyre/

shopsync-se-firebase-adminsdk-nkzuw-e871ea65d4.json
shopsync-se-firebase-adminsdk-nkzuw-5c1cd78bc9.json
shopsync-se-firebase-adminsdk-nkzuw-ca6838f54f.json
# shopsync-se-firebase-adminsdk-nkzuw-e871ea65d4.json
# shopsync-se-firebase-adminsdk-nkzuw-5c1cd78bc9.json
# shopsync-se-firebase-adminsdk-nkzuw-ca6838f54f.json
# src/frontend/shopsync-9ecdc-firebase-adminsdk-60nyc-7e5a173fe8.json
shopsync-9ecdc-firebase-adminsdk-60nyc-7e5a173fe8.json
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ sniffio==1.3.1
soupsieve==2.6
starlette==0.38.6
streamlit==1.38.0
streamlit-cookies-controller==0.0.4
streamlit-option-menu==0.4.0
tenacity==8.5.0
toml==0.10.2
Expand Down
Binary file modified src/.DS_Store
Binary file not shown.
2 changes: 2 additions & 0 deletions src/configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
'title_indicator': 'h4.sku-header a',
'price_indicator': 'div.priceView-customer-price span',
'link_indicator': 'a.image-link',
'image_indicator': 'image.primary-image',
'review_indicator': 'span.ugc-c-review-average'
}


Expand Down
166 changes: 110 additions & 56 deletions src/configs_mt.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,34 +23,54 @@
'data-item-id': True
},
'title_indicator': 'span.lh-title',
'price_indicator': 'div.lh-copy',
'link_indicator': 'a'
'price_indicator': 'div[data-automation-id="product-price"] span.w_iUH7',
'link_indicator': 'a',
'image_indicator': 'img.absolute.top-0.left-0',
'review_indicator': 'span.stars-container'
}

AMAZON = {
'site': 'amazon',
'url': 'https://www.amazon.com/s?k=',
'item_component': 'div',
'item_indicator': {
'data-component-type': 's-search-result'
},
'title_indicator': 'h2 a span',
'price_indicator': 'span.a-price span',
'link_indicator': 'h2 a.a-link-normal'
}
# AMAZON = {
# 'site': 'amazon',
# 'url': 'https://www.amazon.com/s?k=',
# 'item_component': 'div',
# 'item_indicator': {
# 'data-component-type': 's-search-result'
# },
# 'title_indicator': 'h2 a span',
# 'price_indicator': 'span.a-price span',
# 'link_indicator': 'h2 a.a-link-normal',
# 'image_indicator': 'img.s-image',
# 'review_indicator': 'span.a-declarative a i span'
# }

COSTCO = {
'site': 'costco',
'url': 'https://www.costco.com/CatalogSearch?dept=All&keyword=',
'item_component': 'div',
'item_indicator': {
'class': 'product-tile-set'
'data-testid': 'Grid'
},
'title_indicator': 'span a',
'price_indicator': 'div.price',
'link_indicator': 'span.description a',
'title_indicator': 'div[data-testid^="Text_ProductTile_"]', # Extract the title from the span
'price_indicator': 'div.MuiTypography-root', # Extract the price element
'link_indicator': 'a', # Anchor element for the product link
'image_indicator': 'img', # Use `img` tag for the product image
'review_indicator': 'div.product-rating' # Review container (verify its presence)
}

# TARGET = {
# 'site': 'target',
# 'url': 'https://www.target.com/s?searchTerm=',
# 'item_component': 'div',
# 'item_indicator': {
# 'data-test': '@web/ProductCard/ProductCardVariantDefault'
# },
# 'title_indicator': 'a[data-test="product-title"]',
# 'price_indicator': 'span[data-test="product-price"]',
# 'link_indicator': 'a[data-test="product-title"]',
# 'image_indicator': 'img[data-test="product-image"]',
# 'review_indicator': 'div[data-test="average-rating"]'
# }

BESTBUY = {
'site': 'bestbuy',
'url': 'https://www.bestbuy.com/site/searchpage.jsp?st=',
Expand All @@ -61,6 +81,8 @@
'title_indicator': 'h4.sku-title a',
'price_indicator': 'div.priceView-customer-price span',
'link_indicator': 'a.image-link',
'image_indicator': 'img.product-image',
'review_indicator': 'div.c-ratings-reviews p'
}


Expand All @@ -85,43 +107,9 @@ def run(self):
List of items from the dict
"""

# api_url = 'https://redsky.target.com/redsky_aggregations/v1/web/plp_search_v1'

# page = '/s/' + self.query
# params = {
# 'key': '5938CFDFD3FB4A7DB7C060583C86663C',
# 'channel': 'WEB',
# 'count': '24',
# 'default_purchasability_filter': 'false',
# 'include_sponsored': 'true',
# 'keyword': self.query,
# 'offset': '0',
# 'page': page,
# 'platform': 'desktop',
# 'pricing_store_id': '3991',
# 'useragent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0',
# 'visitor_id': 'AAA',
# }

# data = requests.get(api_url, params=params).json()
# items = []
# if 'search' in data['data']:
# for p in data['data']['search']['products']:
# item = {
# 'timestamp': datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
# 'title': html.unescape(p['item']['product_description']['title']),
# 'price': '$' + str(p['price']['current_retail']),
# 'website': 'target',
# #'link': shorten_url(p['item']['enrichment']['buy_url'])
# 'link': p['item']['enrichment']['buy_url']
# }
# items.append(item)

# self.result = items
# set up the request parameters
params = {
'api_key': '5938CFDFD3FB4A7DB7C060583C86663C',
'search_term': 'Iphone',
'api_key': 'A6D50D2FA74944AEB91D4B18CBFDE4B0',
'search_term': self.query,
'type': 'search',
'sort_by': 'best_match'
}
Expand Down Expand Up @@ -178,7 +166,7 @@ def run(self):
self.result = []

data = response.dict()

print(data)
items = []
for p in data['searchResult']['item']:
item = {
Expand All @@ -187,11 +175,77 @@ def run(self):
'price': '$' + p['sellingStatus']['currentPrice']['value'],
'website': 'ebay',
#'link': shorten_url(p['viewItemURL'])
'link': p['viewItemURL']
'link': p['viewItemURL'],
'image_url': p['galleryURL'] if 'galleryURL' in p else 'https://via.placeholder.com/150',
'review': p['sellerInfo']['positiveFeedbackPercent'] + '%' if 'sellerInfo' in p and 'positiveFeedbackPercent' in p['sellerInfo'] else 'No Reviews'
}
items.append(item)

self.result = items


CONFIGS = [WALMART, AMAZON, COSTCO, BESTBUY]


class scrape_amazon(Thread):
def __init__(self, query):
self.result = []
self.query = query
super(scrape_amazon, self).__init__()

def run(self):
"""Scrape Amazon product data using Rainforest API

Parameters
----------
query: str
Item to look for in the API

Returns
----------
items: list
List of items from the API response
"""
API_KEY = '34C1B2689C074F3BB120B22625C22F72'
BASE_URL = 'https://api.rainforestapi.com/request'

params = {
'api_key': API_KEY,
'type': 'search',
'amazon_domain': 'amazon.com',
'search_term': self.query,
}

try:
# Send request to Rainforest API
response = requests.get(BASE_URL, params=params)
data = response.json()

# Extract items from response
items = []
if "search_results" in data:
for product in data["search_results"]:
try:
item = {
'timestamp': datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
'title': product['title'] if 'title' in product else 'No Title',
'price': f"${product['price']['value']}" if 'price' in product and 'value' in product['price'] else 'No Price',
'currency': product['price']['currency'] if 'price' in product and 'currency' in product['price'] else 'USD',
'website': 'amazon',
'link': product['link'] if 'link' in product else 'No Link',
'review': f"{product['rating']} stars" if 'rating' in product else 'No Rating',
'image_url': product['image'] if 'image' in product else 'https://via.placeholder.com/150'

}
items.append(item)
except Exception as e:
print(f"Error processing product: {e}")
continue

self.result = items

except Exception as e:
print(f"Error fetching data from Amazon API: {e}")
self.result = []


CONFIGS = [WALMART, COSTCO, BESTBUY]
6 changes: 4 additions & 2 deletions src/formattr.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"""


def formatResult(website, titles, prices, links):
def formatResult(website, titles, prices, links, image_url=None, review=None):
"""
The formatResult function takes the scraped HTML as input, and extracts the
necessary values from the HTML code. Ex. extracting a price '$19.99' from
Expand All @@ -34,8 +34,10 @@ def formatResult(website, titles, prices, links):
"price": price,
"link": f'www.{website}.com{link}',
"website": website,
"image_url": image_url or "https://via.placeholder.com/150",
"review": review or "No Reviews"
}
print(product['title'])
#print(product['title'])
if website=='walmart':
if link[0:4]=='http':
product['link']=f'{link}'
Expand Down
Binary file added src/frontend/.DS_Store
Binary file not shown.
38 changes: 37 additions & 1 deletion src/frontend/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
from PIL import Image
from PIL import Image
from dotenv import load_dotenv
import json
import slash_user_interface as slash_user_interface
from streamlit_cookies_controller import CookieController
import time

# Load environment variables from .env file
load_dotenv()
Expand All @@ -27,8 +31,8 @@ def initialize_firebase(mock=False):
if not firebase_admin._apps:
firebase_admin.initialize_app()
return True
json_path = os.path.join(os.path.dirname(__file__), 'shopsync-9ecdc-firebase-adminsdk-60nyc-a335ead1ea.json')

json_path = os.path.join(os.path.dirname(__file__), 'shopsync-se-firebase-adminsdk-nkzuw-e871ea65d4.json')
try:
# Path to Firebase service account key
cred = credentials.Certificate(json_path)
Expand Down Expand Up @@ -209,6 +213,25 @@ def app():
# If login successful, update session state
st.session_state.logged_in = True
st.session_state.user_email = email

with open('tokens.json') as openfile:
json_object = json.load(openfile)

controller = CookieController()
token = controller.get("csrftoken")

tokenData = {
"email": email,
"expiration": time.time() + 70000
}

json_object["tokens"][token] = tokenData

json_output = json.dumps(json_object, indent=4)

with open('tokens.json', 'w') as openfile:
openfile.write(json_output)

st.success('Logged in successfully!')
st.rerun() # Rerun the app to reflect login state

Expand Down Expand Up @@ -299,6 +322,19 @@ def signup(username, email, password):
raise ValueError("An unexpected error occurred.")

def logout():
# Delete token on logout
with open('tokens.json', 'r') as openfile:
json_object = json.load(openfile)
controller = CookieController()
token = controller.get("csrftoken")

del json_object["tokens"][token]

json_output = json.dumps(json_object, indent=4)

with open('tokens.json', 'w') as openfile:
openfile.write(json_output)

# Clear session state on logout
st.session_state.logged_in = False
st.session_state.user_email = None
Expand Down
Loading