diff --git a/lib/alembic.ini b/alembic.ini similarity index 94% rename from lib/alembic.ini rename to alembic.ini index 953863ddd..698dc6d5b 100644 --- a/lib/alembic.ini +++ b/alembic.ini @@ -2,7 +2,7 @@ [alembic] # path to migration scripts -script_location = migrations +script_location = alembic # template used to generate migration file names; The default value is %%(rev)s_%%(slug)s # Uncomment the line below if you want the files to be prepended with date and time @@ -36,10 +36,10 @@ prepend_sys_path = . # sourceless = false # version location specification; This defaults -# to migrations/versions. When using multiple version +# to alembic/versions. When using multiple version # directories, initial revisions must be specified with --version-path. # The path separator used here should be the separator specified by "version_path_separator" below. -# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions # version path separator; As mentioned above, this is the character used to split # version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. @@ -55,7 +55,7 @@ version_path_separator = os # Use os.pathsep. Default configuration used for ne # are written from script.py.mako # output_encoding = utf-8 -sqlalchemy.url = sqlite:///freebies.db +sqlalchemy.url = sqlite:///./freebie.db [post_write_hooks] diff --git a/lib/migrations/README b/alembic/README similarity index 100% rename from lib/migrations/README rename to alembic/README diff --git a/lib/migrations/env.py b/alembic/env.py similarity index 94% rename from lib/migrations/env.py rename to alembic/env.py index c7aab9656..70189dfca 100644 --- a/lib/migrations/env.py +++ b/alembic/env.py @@ -16,10 +16,9 @@ # add your model's MetaData object here # for 'autogenerate' support -# from myapp import mymodel -# target_metadata = mymodel.Base.metadata -from models import Base +from lib.models import Base target_metadata = Base.metadata +# target_metadata = None # other values from the config, defined by the needs of env.py, # can be acquired: @@ -66,7 +65,7 @@ def run_migrations_online() -> None: with connectable.connect() as connection: context.configure( - connection=connection, target_metadata=target_metadata, render_as_batch=True, + connection=connection, target_metadata=target_metadata ) with context.begin_transaction(): diff --git a/lib/migrations/script.py.mako b/alembic/script.py.mako similarity index 100% rename from lib/migrations/script.py.mako rename to alembic/script.py.mako diff --git a/lib/migrations/versions/5f72c58bf48c_create_companies_devs.py b/alembic/versions/33d971f86e64_create_tables.py similarity index 57% rename from lib/migrations/versions/5f72c58bf48c_create_companies_devs.py rename to alembic/versions/33d971f86e64_create_tables.py index c191bb2f9..9d5449a92 100644 --- a/lib/migrations/versions/5f72c58bf48c_create_companies_devs.py +++ b/alembic/versions/33d971f86e64_create_tables.py @@ -1,8 +1,8 @@ -"""create companies, devs +"""create tables -Revision ID: 5f72c58bf48c -Revises: 7a71dbf71c64 -Create Date: 2023-03-15 15:06:20.944586 +Revision ID: 33d971f86e64 +Revises: +Create Date: 2025-05-26 13:47:58.359064 """ from alembic import op @@ -10,8 +10,8 @@ # revision identifiers, used by Alembic. -revision = '5f72c58bf48c' -down_revision = '7a71dbf71c64' +revision = '33d971f86e64' +down_revision = None branch_labels = None depends_on = None @@ -29,11 +29,22 @@ def upgrade() -> None: sa.Column('name', sa.String(), nullable=True), sa.PrimaryKeyConstraint('id') ) + op.create_table('freebies', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('item_name', sa.String(), nullable=True), + sa.Column('value', sa.Integer(), nullable=True), + sa.Column('company_id', sa.Integer(), nullable=True), + sa.Column('dev_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['companies.id'], ), + sa.ForeignKeyConstraint(['dev_id'], ['devs.id'], ), + sa.PrimaryKeyConstraint('id') + ) # ### end Alembic commands ### def downgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('freebies') op.drop_table('devs') op.drop_table('companies') # ### end Alembic commands ### diff --git a/lib/freebies.db b/freebie.db similarity index 81% rename from lib/freebies.db rename to freebie.db index 12beb1c96..cc2331ab6 100644 Binary files a/lib/freebies.db and b/freebie.db differ diff --git a/lib/__pycache__/models.cpython-38.pyc b/lib/__pycache__/models.cpython-38.pyc new file mode 100644 index 000000000..c790dc05f Binary files /dev/null and b/lib/__pycache__/models.cpython-38.pyc differ diff --git a/lib/migrations/versions/7a71dbf71c64_create_db.py b/lib/migrations/versions/7a71dbf71c64_create_db.py deleted file mode 100644 index 23e0a655b..000000000 --- a/lib/migrations/versions/7a71dbf71c64_create_db.py +++ /dev/null @@ -1,24 +0,0 @@ -"""create db - -Revision ID: 7a71dbf71c64 -Revises: -Create Date: 2023-03-15 15:05:55.516631 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '7a71dbf71c64' -down_revision = None -branch_labels = None -depends_on = None - - -def upgrade() -> None: - pass - - -def downgrade() -> None: - pass diff --git a/lib/models.py b/lib/models.py index 2681bee5a..f47485b1c 100644 --- a/lib/models.py +++ b/lib/models.py @@ -1,29 +1,82 @@ -from sqlalchemy import ForeignKey, Column, Integer, String, MetaData -from sqlalchemy.orm import relationship, backref -from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy import create_engine, ForeignKey, Column, Integer, String +from sqlalchemy.orm import relationship, sessionmaker, declarative_base -convention = { - "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", -} -metadata = MetaData(naming_convention=convention) - -Base = declarative_base(metadata=metadata) +Base = declarative_base() class Company(Base): __tablename__ = 'companies' - id = Column(Integer(), primary_key=True) - name = Column(String()) - founding_year = Column(Integer()) + id = Column(Integer, primary_key=True) + name = Column(String) + founding_year = Column(Integer) + + freebies = relationship('Freebie', back_populates='company') def __repr__(self): return f'' + def give_freebie(self, dev, item_name, value): + new_freebie = Freebie(item_name=item_name, value=value, company=self, dev=dev) + session.add(new_freebie) + session.commit() + + @classmethod + def oldest_company(cls): + return session.query(cls).order_by(cls.founding_year).first() + + @property + def devs(self): + return list({freebie.dev for freebie in self.freebies}) + + class Dev(Base): __tablename__ = 'devs' - id = Column(Integer(), primary_key=True) - name= Column(String()) + id = Column(Integer, primary_key=True) + name = Column(String) + + freebies = relationship('Freebie', back_populates='dev') def __repr__(self): return f'' + + @property + def companies(self): + return list({freebie.company for freebie in self.freebies}) + + def received_one(self, item_name): + return any(f.item_name == item_name for f in self.freebies) + + def give_away(self, dev, freebie): + if freebie in self.freebies: + freebie.dev = dev + session.commit() + + +class Freebie(Base): + __tablename__ = 'freebies' + + id = Column(Integer, primary_key=True) + item_name = Column(String) + value = Column(Integer) + company_id = Column(Integer, ForeignKey('companies.id')) + dev_id = Column(Integer, ForeignKey('devs.id')) + + company = relationship('Company', back_populates='freebies') + dev = relationship('Dev', back_populates='freebies') + + def __repr__(self): + return f'' + + def print_details(self): + return f'{self.dev.name} owns a {self.item_name} from {self.company.name}' + + +# --- Database setup --- +engine = create_engine('sqlite:///freebie.db') +Session = sessionmaker(bind=engine) +session = Session() + +def reset_database(): + Base.metadata.drop_all(engine) + Base.metadata.create_all(engine) diff --git a/lib/seed.py b/lib/seed.py index b16becbbb..c5d75258e 100644 --- a/lib/seed.py +++ b/lib/seed.py @@ -1,3 +1,50 @@ #!/usr/bin/env python3 # Script goes here! +from models import Company, Dev, Freebie, session, reset_database + +# Reset and seed the database +reset_database() + +# Create companies +google = Company(name="Google", founding_year=1998) +amazon = Company(name="Amazon", founding_year=1994) + +# Create developers +annie = Dev(name="Annie") +faith = Dev(name="Faith") +charlie = Dev(name="Charlie") + +# Add all to session +session.add_all([google, amazon, annie, faith, charlie]) +session.commit() + +# Create freebies +f1 = Freebie(item_name="T-shirt", value=20, company=google, dev=annie) +f2 = Freebie(item_name="Sticker", value=5, company=google, dev=faith) +f3 = Freebie(item_name="Mug", value=15, company=amazon, dev=annie) +f4 = Freebie(item_name="Backpack", value=50, company=amazon, dev=charlie) + +session.add_all([f1, f2, f3, f4]) +session.commit() + +# ---- Optional: test methods directly here ---- +print("Companies for Annie:", [c.name for c in annie.companies]) +print("Does Annie have a Mug?", annie.received_one("Mug")) +print("Oldest company:", Company.oldest_company().name) + +# Give a new freebie +google.give_freebie(faith, "Laptop Sleeve", 30) + +# Print all freebies +print("\nFreebie Details:") +for freebie in session.query(Freebie).all(): + print(freebie.print_details()) + +# Test give_away +print("\nTesting give_away...") +mug = session.query(Freebie).filter_by(item_name="Mug").first() +annie.give_away(faith, mug) + +print("After giveaway, Bob has:", [f.item_name for f in faith.freebies]) +