119 lines
4.4 KiB
Python
119 lines
4.4 KiB
Python
import logging
|
|
from datetime import datetime
|
|
|
|
from sqlalchemy import (Boolean, Column, DateTime, Float, ForeignKey, Integer,
|
|
String, create_engine, exc)
|
|
from sqlalchemy.ext.declarative import declarative_base
|
|
from sqlalchemy.orm import relationship, sessionmaker
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
Base = declarative_base()
|
|
engine = create_engine('sqlite:///restock.db')
|
|
Session = sessionmaker(bind=engine, autoflush=False)
|
|
|
|
|
|
class Shop(Base):
|
|
__tablename__ = 'shop'
|
|
id = Column(Integer, primary_key=True)
|
|
name = Column(String, unique=True, nullable=False)
|
|
|
|
def __repr__(self):
|
|
return f'Shop<{self.name}>'
|
|
|
|
def __ne__(self, shop):
|
|
return self.name != shop.name
|
|
|
|
|
|
class Product(Base):
|
|
__tablename__ = 'product'
|
|
id = Column(Integer, primary_key=True)
|
|
name = Column(String, nullable=False)
|
|
url = Column(String, nullable=False, unique=True)
|
|
price = Column(Float, nullable=False)
|
|
price_currency = Column(String, nullable=False)
|
|
available = Column(Boolean, nullable=False)
|
|
updated_at = Column(DateTime)
|
|
tweet_id = Column(Integer, unique=True)
|
|
shop_id = Column(Integer, ForeignKey('shop.id'), nullable=False)
|
|
shop = relationship('Shop', foreign_keys=[shop_id])
|
|
|
|
def __repr__(self):
|
|
return f'Product<{self.name}@{self.shop.name}>'
|
|
|
|
def __ne__(self, product):
|
|
return self.name != product.name or self.price != product.price or self.available != product.available \
|
|
or self.url != product.url or self.shop != product.shop
|
|
|
|
def ok(self):
|
|
return self.name and self.url and self.price and self.price_currency and self.available is not None
|
|
|
|
|
|
def create_tables():
|
|
Base.metadata.create_all(engine)
|
|
logger.debug('tables created')
|
|
|
|
|
|
def list_shops():
|
|
session = Session()
|
|
shops = session.query(Shop).all()
|
|
session.close()
|
|
return shops
|
|
|
|
|
|
def upsert_shops(names):
|
|
session = Session()
|
|
try:
|
|
for name in names:
|
|
shop = Shop(name=name)
|
|
query = session.query(Shop).filter(Shop.name == shop.name)
|
|
shop_database = query.first()
|
|
if not shop_database:
|
|
logger.info(f'{shop} added')
|
|
session.add(shop)
|
|
session.commit()
|
|
logger.debug('transaction committed')
|
|
except exc.SQLAlchemyError:
|
|
logger.exception('cannot commit transaction')
|
|
finally:
|
|
session.close()
|
|
|
|
|
|
def upsert_products(products, notifier=None):
|
|
session = Session()
|
|
try:
|
|
for product in products:
|
|
query = session.query(Product).filter(Product.name == product.name, Product.shop == product.shop)
|
|
product_database = query.first()
|
|
now = datetime.utcnow()
|
|
tweet_id = None
|
|
if not product_database:
|
|
# product is new and available so we need to create an initial thread
|
|
if notifier and product.available:
|
|
product.tweet_id = notifier.create_thread(product).id
|
|
product.updated_at = now
|
|
session.add(product)
|
|
logger.info(f'{product} added')
|
|
elif product != product_database:
|
|
# notifications
|
|
if notifier and product.available != product_database.available:
|
|
if product.available and not product_database.tweet_id:
|
|
# product is now available so we need to create an initial tweet (or thread)
|
|
tweet = notifier.create_thread(product)
|
|
if tweet:
|
|
tweet_id = tweet.id
|
|
elif not product.available and product_database.available and product_database.tweet_id:
|
|
# product is out of stock so we need to reply to previous tweet to close the thread
|
|
notifier.close_thread(tweet_id=product_database.tweet_id,
|
|
duration=now-product_database.updated_at)
|
|
query.update({Product.price: product.price, Product.price_currency: product.price_currency,
|
|
Product.available: product.available, Product.url: product.url,
|
|
Product.tweet_id: tweet_id, Product.updated_at: now})
|
|
logger.info(f'{product} updated')
|
|
session.commit()
|
|
logger.debug('transaction committed')
|
|
except exc.SQLAlchemyError:
|
|
logger.exception('cannot commit transaction')
|
|
finally:
|
|
session.close()
|