from datetime import datetime
import logging
from typing import List, Optional
from pydantic import BaseConfig
from sqlmodel import Field, SQLModel, Session
import shortuuid
import random
from faker import Faker

# Line items that would be on a receipt
# Each line item has an id, sku, price, quantity, and transaction_id
class LineItem(SQLModel, table=True):
    id: str = Field(default=None, primary_key=True)
    sku: str = Field(foreign_key="product.sku")
    price: float
    quantity: int
    transaction_id: int = Field(foreign_key="transaction.id")

# Each transaction has an id, store_id, date, and total
class Transaction(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    store_id: str = Field(foreign_key="store.id")
    date: datetime
    total: float


# TODO extract real sales data, transform it, and load into db
# for now random data in the correct format will do.
def run(engine):
    # Allow arbitary types for Pydantic validation
    BaseConfig.arbitrary_types_allowed = True

    # Create a fake data generator
    fake = Faker()

    # Create a session to interact with the database
    with Session(engine) as session:
        # Get all of the store ids
        store_ids = session.exec(f'SELECT id FROM store').fetchall()
        logging.debug(store_ids)
        # Get all of the products
        products = session.exec(f'SELECT * FROM product').fetchall()
        logging.debug(p.name for p in products)
        # Define a list of transactions and sales
        transactions = List(Transaction)
        sales = List(LineItem)
        # generate 100k random transactions
        for i in range(0, 100000):
            # lineitems is a temp list to hold the line items for this transaction
            lineitems = List(LineItem)
            # temp_products is a temp copy of the products list to prevent picking the same product twice in the same transaction
            # [:] it to make a copy and not a reference
            temp_products = products[:]
            # shuffle the temp products list to make it random
            random.shuffle(temp_products)
            # add a random amount of line items to the transaction (no more than the total aount of products to prevent index out of range)
            for j in range(0, random.randint(0, len(products)-1)):
                # pick the next product from the temp products list and remove it from the list (pop)
                p = temp_products.pop()
                # create a new line item with the current transaction id, product p, and a random quantity
                lineitems.append(LineItem(transaction_id=i, id=shortuuid.uuid(), price=p.price, quantity=random.randint(1, 10), sku=p.sku))
            # add the line items for this transaction to the sales list
            sales.extend(lineitems)
            # create a new transaction with a random store id (from the list of store ids), date, and total
            transactions.append(Transaction(
                store_id=random.choice(store_ids)[0],
                date=fake.date_time_between(start_date="-1y", end_date="now"),
                total=sum(item.price * item.quantity for item in lineitems),
                id=i
            ))
        # insert the transactions into the database
        session.add_all(transactions)
        session.commit()
        # insert the sales into the database
        session.add_all(sales)
        session.commit()

random.choice typing.List shortuuid.uuid random.shuffle sqlmodel.Session sqlmodel.Field faker.Faker logging.debug random.randint
[((374, 411), 'sqlmodel.Field', 'Field', ([], {'default': 'None', 'primary_key': '(True)'}), '(default=None, primary_key=True)\n', (379, 411), False, 'from sqlmodel import Field, SQLModel, Session\n'), ((427, 459), 'sqlmodel.Field', 'Field', ([], {'foreign_key': '"""product.sku"""'}), "(foreign_key='product.sku')\n", (432, 459), False, 'from sqlmodel import Field, SQLModel, Session\n'), ((521, 556), 'sqlmodel.Field', 'Field', ([], {'foreign_key': '"""transaction.id"""'}), "(foreign_key='transaction.id')\n", (526, 556), False, 'from sqlmodel import Field, SQLModel, Session\n'), ((679, 716), 'sqlmodel.Field', 'Field', ([], {'default': 'None', 'primary_key': '(True)'}), '(default=None, primary_key=True)\n', (684, 716), False, 'from sqlmodel import Field, SQLModel, Session\n'), ((737, 766), 'sqlmodel.Field', 'Field', ([], {'foreign_key': '"""store.id"""'}), "(foreign_key='store.id')\n", (742, 766), False, 'from sqlmodel import Field, SQLModel, Session\n'), ((1082, 1089), 'faker.Faker', 'Faker', ([], {}), '()\n', (1087, 1089), False, 'from faker import Faker\n'), ((1153, 1168), 'sqlmodel.Session', 'Session', (['engine'], {}), '(engine)\n', (1160, 1168), False, 'from sqlmodel import Field, SQLModel, Session\n'), ((1293, 1317), 'logging.debug', 'logging.debug', (['store_ids'], {}), '(store_ids)\n', (1306, 1317), False, 'import logging\n'), ((1429, 1468), 'logging.debug', 'logging.debug', (['(p.name for p in products)'], {}), '(p.name for p in products)\n', (1442, 1468), False, 'import logging\n'), ((1542, 1559), 'typing.List', 'List', (['Transaction'], {}), '(Transaction)\n', (1546, 1559), False, 'from typing import List, Optional\n'), ((1576, 1590), 'typing.List', 'List', (['LineItem'], {}), '(LineItem)\n', (1580, 1590), False, 'from typing import List, Optional\n'), ((1777, 1791), 'typing.List', 'List', (['LineItem'], {}), '(LineItem)\n', (1781, 1791), False, 'from typing import List, Optional\n'), ((2093, 2122), 'random.shuffle', 'random.shuffle', (['temp_products'], {}), '(temp_products)\n', (2107, 2122), False, 'import random\n'), ((2642, 2658), 'shortuuid.uuid', 'shortuuid.uuid', ([], {}), '()\n', (2656, 2658), False, 'import shortuuid\n'), ((2684, 2705), 'random.randint', 'random.randint', (['(1)', '(10)'], {}), '(1, 10)\n', (2698, 2705), False, 'import random\n'), ((3005, 3029), 'random.choice', 'random.choice', (['store_ids'], {}), '(store_ids)\n', (3018, 3029), False, 'import random\n')]