From 232e416f738a08945bc027a2870e174d6c35b8da Mon Sep 17 00:00:00 2001 From: Adrian Prado Date: Mon, 17 Jun 2024 18:16:33 -0500 Subject: [PATCH 01/18] Removed solution from master branch --- app/routes/character_routes.py | 47 ++-------------------------------- 1 file changed, 2 insertions(+), 45 deletions(-) diff --git a/app/routes/character_routes.py b/app/routes/character_routes.py index 2892a23..d16651f 100644 --- a/app/routes/character_routes.py +++ b/app/routes/character_routes.py @@ -44,54 +44,11 @@ def get_characters(): @bp.get("//greetings") def get_greetings(char_id): - character = validate_model(Character, char_id) - - if not character.greetings: - return make_response(jsonify(f"No greetings found for {character.name} "), 201) - - response = {"Character Name" : character.name, - "Greetings" : []} - for greeting in character.greetings: - response["Greetings"].append({ - "greeting" : greeting.greeting_text - }) - - return jsonify(response) + pass @bp.post("//generate") def add_greetings(char_id): - character_obj = validate_model(Character, char_id) - greetings = generate_greetings(character_obj) - print(greetings) - - if character_obj.greetings: - return make_response(jsonify(f"Greetings already generated for {character_obj.name} "), 201) - - new_greetings = [] - - for greeting in greetings: - text = greeting[greeting.find(" ")+1:] - new_greeting = Greeting( - greeting_text = text.strip("\""), - character = character_obj - ) - new_greetings.append(new_greeting) - - db.session.add_all(new_greetings) - db.session.commit() - - return make_response(jsonify(f"Greetings successfully added to {character_obj.name}"), 201) - -def generate_greetings(character): - - input_message = f"I am writing a video game in the style of The Witcher. I have an npc named {character.name} who is {character.age} years old. They are a {character.occupation} who has a {character.personality} personality. Please generate a python style list of 10 stock phrases they might use when the main character talks to them" - completion = client.chat.completions.create( - model="gpt-3.5-turbo", - messages=[ - {"role": "user", "content": input_message} - ] - ) - return(completion.choices[0].message.content.split("\n")) + pass def validate_model(cls,id): try: From 4c9b012b82765c51ea7d08a9b5c8589506221137 Mon Sep 17 00:00:00 2001 From: Adrian Prado Date: Mon, 17 Jun 2024 20:59:02 -0500 Subject: [PATCH 02/18] removed OpenAI library from imports --- app/routes/character_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes/character_routes.py b/app/routes/character_routes.py index d16651f..9808ce3 100644 --- a/app/routes/character_routes.py +++ b/app/routes/character_routes.py @@ -3,7 +3,7 @@ from ..models.character import Character from ..models.greeting import Greeting from sqlalchemy import func, union, except_ -from openai import OpenAI + bp = Blueprint("characters", __name__, url_prefix="/characters") client = OpenAI() From df2f4eda1733934c1cc45d7a8dc8f9ed1e058396 Mon Sep 17 00:00:00 2001 From: Adrian Prado Date: Fri, 21 Jun 2024 01:38:15 -0500 Subject: [PATCH 03/18] Remove OpenAI References --- app/routes/character_routes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/app/routes/character_routes.py b/app/routes/character_routes.py index 9808ce3..6cf5b52 100644 --- a/app/routes/character_routes.py +++ b/app/routes/character_routes.py @@ -6,7 +6,6 @@ bp = Blueprint("characters", __name__, url_prefix="/characters") -client = OpenAI() @bp.post("") def create_character(): From bc81ef4ef0f7c3931792d297d9162fa32fc8caa9 Mon Sep 17 00:00:00 2001 From: Adrian Prado Date: Fri, 21 Jun 2024 01:43:10 -0500 Subject: [PATCH 04/18] remove --- migrations/README | 1 - migrations/alembic.ini | 50 -------- migrations/env.py | 113 ------------------ migrations/script.py.mako | 24 ---- migrations/versions/657ab8362756_.py | 34 ------ migrations/versions/8ed7775249e9_.py | 34 ------ .../d7a96ea32a49_add_character_model.py | 35 ------ migrations/versions/e06848138ff6_.py | 34 ------ 8 files changed, 325 deletions(-) delete mode 100644 migrations/README delete mode 100644 migrations/alembic.ini delete mode 100644 migrations/env.py delete mode 100644 migrations/script.py.mako delete mode 100644 migrations/versions/657ab8362756_.py delete mode 100644 migrations/versions/8ed7775249e9_.py delete mode 100644 migrations/versions/d7a96ea32a49_add_character_model.py delete mode 100644 migrations/versions/e06848138ff6_.py diff --git a/migrations/README b/migrations/README deleted file mode 100644 index 0e04844..0000000 --- a/migrations/README +++ /dev/null @@ -1 +0,0 @@ -Single-database configuration for Flask. diff --git a/migrations/alembic.ini b/migrations/alembic.ini deleted file mode 100644 index ec9d45c..0000000 --- a/migrations/alembic.ini +++ /dev/null @@ -1,50 +0,0 @@ -# A generic, single database configuration. - -[alembic] -# template used to generate migration files -# file_template = %%(rev)s_%%(slug)s - -# set to 'true' to run the environment during -# the 'revision' command, regardless of autogenerate -# revision_environment = false - - -# Logging configuration -[loggers] -keys = root,sqlalchemy,alembic,flask_migrate - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = WARN -handlers = console -qualname = - -[logger_sqlalchemy] -level = WARN -handlers = -qualname = sqlalchemy.engine - -[logger_alembic] -level = INFO -handlers = -qualname = alembic - -[logger_flask_migrate] -level = INFO -handlers = -qualname = flask_migrate - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(levelname)-5.5s [%(name)s] %(message)s -datefmt = %H:%M:%S diff --git a/migrations/env.py b/migrations/env.py deleted file mode 100644 index 4c97092..0000000 --- a/migrations/env.py +++ /dev/null @@ -1,113 +0,0 @@ -import logging -from logging.config import fileConfig - -from flask import current_app - -from alembic import context - -# this is the Alembic Config object, which provides -# access to the values within the .ini file in use. -config = context.config - -# Interpret the config file for Python logging. -# This line sets up loggers basically. -fileConfig(config.config_file_name) -logger = logging.getLogger('alembic.env') - - -def get_engine(): - try: - # this works with Flask-SQLAlchemy<3 and Alchemical - return current_app.extensions['migrate'].db.get_engine() - except (TypeError, AttributeError): - # this works with Flask-SQLAlchemy>=3 - return current_app.extensions['migrate'].db.engine - - -def get_engine_url(): - try: - return get_engine().url.render_as_string(hide_password=False).replace( - '%', '%%') - except AttributeError: - return str(get_engine().url).replace('%', '%%') - - -# add your model's MetaData object here -# for 'autogenerate' support -# from myapp import mymodel -# target_metadata = mymodel.Base.metadata -config.set_main_option('sqlalchemy.url', get_engine_url()) -target_db = current_app.extensions['migrate'].db - -# other values from the config, defined by the needs of env.py, -# can be acquired: -# my_important_option = config.get_main_option("my_important_option") -# ... etc. - - -def get_metadata(): - if hasattr(target_db, 'metadatas'): - return target_db.metadatas[None] - return target_db.metadata - - -def run_migrations_offline(): - """Run migrations in 'offline' mode. - - This configures the context with just a URL - and not an Engine, though an Engine is acceptable - here as well. By skipping the Engine creation - we don't even need a DBAPI to be available. - - Calls to context.execute() here emit the given string to the - script output. - - """ - url = config.get_main_option("sqlalchemy.url") - context.configure( - url=url, target_metadata=get_metadata(), literal_binds=True - ) - - with context.begin_transaction(): - context.run_migrations() - - -def run_migrations_online(): - """Run migrations in 'online' mode. - - In this scenario we need to create an Engine - and associate a connection with the context. - - """ - - # this callback is used to prevent an auto-migration from being generated - # when there are no changes to the schema - # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html - def process_revision_directives(context, revision, directives): - if getattr(config.cmd_opts, 'autogenerate', False): - script = directives[0] - if script.upgrade_ops.is_empty(): - directives[:] = [] - logger.info('No changes in schema detected.') - - conf_args = current_app.extensions['migrate'].configure_args - if conf_args.get("process_revision_directives") is None: - conf_args["process_revision_directives"] = process_revision_directives - - connectable = get_engine() - - with connectable.connect() as connection: - context.configure( - connection=connection, - target_metadata=get_metadata(), - **conf_args - ) - - with context.begin_transaction(): - context.run_migrations() - - -if context.is_offline_mode(): - run_migrations_offline() -else: - run_migrations_online() diff --git a/migrations/script.py.mako b/migrations/script.py.mako deleted file mode 100644 index 2c01563..0000000 --- a/migrations/script.py.mako +++ /dev/null @@ -1,24 +0,0 @@ -"""${message} - -Revision ID: ${up_revision} -Revises: ${down_revision | comma,n} -Create Date: ${create_date} - -""" -from alembic import op -import sqlalchemy as sa -${imports if imports else ""} - -# revision identifiers, used by Alembic. -revision = ${repr(up_revision)} -down_revision = ${repr(down_revision)} -branch_labels = ${repr(branch_labels)} -depends_on = ${repr(depends_on)} - - -def upgrade(): - ${upgrades if upgrades else "pass"} - - -def downgrade(): - ${downgrades if downgrades else "pass"} diff --git a/migrations/versions/657ab8362756_.py b/migrations/versions/657ab8362756_.py deleted file mode 100644 index b5baa66..0000000 --- a/migrations/versions/657ab8362756_.py +++ /dev/null @@ -1,34 +0,0 @@ -"""empty message - -Revision ID: 657ab8362756 -Revises: e06848138ff6 -Create Date: 2024-06-14 10:29:34.966812 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '657ab8362756' -down_revision = 'e06848138ff6' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - with op.batch_alter_table('greeting', schema=None) as batch_op: - batch_op.add_column(sa.Column('character_id', sa.Integer(), nullable=False)) - batch_op.create_foreign_key(None, 'character', ['character_id'], ['id']) - - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - with op.batch_alter_table('greeting', schema=None) as batch_op: - batch_op.drop_constraint(None, type_='foreignkey') - batch_op.drop_column('character_id') - - # ### end Alembic commands ### diff --git a/migrations/versions/8ed7775249e9_.py b/migrations/versions/8ed7775249e9_.py deleted file mode 100644 index b0070e1..0000000 --- a/migrations/versions/8ed7775249e9_.py +++ /dev/null @@ -1,34 +0,0 @@ -"""empty message - -Revision ID: 8ed7775249e9 -Revises: d7a96ea32a49 -Create Date: 2024-06-11 17:29:06.612117 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '8ed7775249e9' -down_revision = 'd7a96ea32a49' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('greeting', - sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), - sa.Column('greeting_text', sa.String(), nullable=False), - sa.Column('character_id', sa.Integer(), nullable=False), - sa.ForeignKeyConstraint(['character_id'], ['character.id'], ), - sa.PrimaryKeyConstraint('id') - ) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('greeting') - # ### end Alembic commands ### diff --git a/migrations/versions/d7a96ea32a49_add_character_model.py b/migrations/versions/d7a96ea32a49_add_character_model.py deleted file mode 100644 index 43d6168..0000000 --- a/migrations/versions/d7a96ea32a49_add_character_model.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Add character model - -Revision ID: d7a96ea32a49 -Revises: -Create Date: 2024-06-11 11:15:08.811220 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = 'd7a96ea32a49' -down_revision = None -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('character', - sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), - sa.Column('name', sa.String(), nullable=False), - sa.Column('personality', sa.String(), nullable=False), - sa.Column('occupation', sa.String(), nullable=False), - sa.Column('age', sa.Integer(), nullable=False), - sa.PrimaryKeyConstraint('id') - ) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('character') - # ### end Alembic commands ### diff --git a/migrations/versions/e06848138ff6_.py b/migrations/versions/e06848138ff6_.py deleted file mode 100644 index 8db8df3..0000000 --- a/migrations/versions/e06848138ff6_.py +++ /dev/null @@ -1,34 +0,0 @@ -"""empty message - -Revision ID: e06848138ff6 -Revises: 8ed7775249e9 -Create Date: 2024-06-13 17:12:14.268947 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = 'e06848138ff6' -down_revision = '8ed7775249e9' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - with op.batch_alter_table('greeting', schema=None) as batch_op: - batch_op.drop_constraint('greeting_character_id_fkey', type_='foreignkey') - batch_op.drop_column('character_id') - - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - with op.batch_alter_table('greeting', schema=None) as batch_op: - batch_op.add_column(sa.Column('character_id', sa.INTEGER(), autoincrement=False, nullable=False)) - batch_op.create_foreign_key('greeting_character_id_fkey', 'character', ['character_id'], ['id']) - - # ### end Alembic commands ### From 4ef1e880a9b2cec72e02a0763988cffd1fd22732 Mon Sep 17 00:00:00 2001 From: Adrian Prado Date: Tue, 5 Nov 2024 10:49:00 -0600 Subject: [PATCH 05/18] added generate_greetings function header --- app/routes/character_routes.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/routes/character_routes.py b/app/routes/character_routes.py index 6cf5b52..e0db8bf 100644 --- a/app/routes/character_routes.py +++ b/app/routes/character_routes.py @@ -4,7 +4,6 @@ from ..models.greeting import Greeting from sqlalchemy import func, union, except_ - bp = Blueprint("characters", __name__, url_prefix="/characters") @bp.post("") @@ -49,6 +48,9 @@ def get_greetings(char_id): def add_greetings(char_id): pass +def generate_greetings(character): + pass + def validate_model(cls,id): try: id = int(id) From e59eefae150fe217e0404faedbb38905bd07231b Mon Sep 17 00:00:00 2001 From: Adrian Prado Date: Tue, 12 Nov 2024 16:47:15 -0600 Subject: [PATCH 06/18] Solution created using Gemini --- app/routes/character_routes.py | 57 +++++++++++--- migrations/README | 1 + migrations/alembic.ini | 50 ++++++++++++ migrations/env.py | 113 +++++++++++++++++++++++++++ migrations/script.py.mako | 24 ++++++ migrations/versions/0344241db67d_.py | 43 ++++++++++ requirements.txt | 23 ++++++ reset_db.py | 7 ++ 8 files changed, 306 insertions(+), 12 deletions(-) create mode 100644 migrations/README create mode 100644 migrations/alembic.ini create mode 100644 migrations/env.py create mode 100644 migrations/script.py.mako create mode 100644 migrations/versions/0344241db67d_.py create mode 100644 reset_db.py diff --git a/app/routes/character_routes.py b/app/routes/character_routes.py index e0db8bf..2b363c3 100644 --- a/app/routes/character_routes.py +++ b/app/routes/character_routes.py @@ -4,6 +4,11 @@ from ..models.greeting import Greeting from sqlalchemy import func, union, except_ +import google.generativeai as genai +import os + +genai.configure(api_key=os.environ.get("GEMINI_API_KEY")) + bp = Blueprint("characters", __name__, url_prefix="/characters") @bp.post("") @@ -28,28 +33,56 @@ def get_characters(): response = [] for character in characters: - response.append( - { - "id" : character.id, - "name" : character.name, - "personality" : character.personality, - "occupation" : character.occupation, - "age" : character.age - } - ) + response.append(character.to_dict()) return jsonify(response) @bp.get("//greetings") def get_greetings(char_id): - pass + character = validate_model(Character, char_id) + + if not character.greetings: + return make_response(jsonify(f"No greetings found for {character.name} "), 201) + + response = {"Character Name" : character.name, + "Greetings" : []} + for greeting in character.greetings: + response["Greetings"].append({ + "greeting" : greeting.greeting_text + }) + + return jsonify(response) + @bp.post("//generate") def add_greetings(char_id): - pass + character_obj = validate_model(Character, char_id) + greetings = generate_greetings(character_obj) + + if character_obj.greetings: + return make_response(jsonify(f"Greetings already generated for {character_obj.name} "), 201) + + new_greetings = [] + + for greeting in greetings: + new_greeting = Greeting( + greeting_text = greeting.strip("\""), + character = character_obj + ) + new_greetings.append(new_greeting) + + db.session.add_all(new_greetings) + db.session.commit() + + return make_response(jsonify(f"Greetings successfully added to {character_obj.name}"), 201) def generate_greetings(character): - pass + model = genai.GenerativeModel("gemini-1.5-flash") + input_message = f"I am writing a video game in the style of The Witcher. I have an npc named {character.name} who is {character.age} years old. They are a {character.occupation} who has a {character.personality} personality. Please generate a python style list of 10 stock phrases they might use when the main character talks to them. Please Return just the list without a variable name and square brackets." + response = model.generate_content(input_message) + response_sani = response.text.split("\n") + return response_sani[:-1] + def validate_model(cls,id): try: diff --git a/migrations/README b/migrations/README new file mode 100644 index 0000000..0e04844 --- /dev/null +++ b/migrations/README @@ -0,0 +1 @@ +Single-database configuration for Flask. diff --git a/migrations/alembic.ini b/migrations/alembic.ini new file mode 100644 index 0000000..ec9d45c --- /dev/null +++ b/migrations/alembic.ini @@ -0,0 +1,50 @@ +# A generic, single database configuration. + +[alembic] +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic,flask_migrate + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[logger_flask_migrate] +level = INFO +handlers = +qualname = flask_migrate + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/migrations/env.py b/migrations/env.py new file mode 100644 index 0000000..4c97092 --- /dev/null +++ b/migrations/env.py @@ -0,0 +1,113 @@ +import logging +from logging.config import fileConfig + +from flask import current_app + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) +logger = logging.getLogger('alembic.env') + + +def get_engine(): + try: + # this works with Flask-SQLAlchemy<3 and Alchemical + return current_app.extensions['migrate'].db.get_engine() + except (TypeError, AttributeError): + # this works with Flask-SQLAlchemy>=3 + return current_app.extensions['migrate'].db.engine + + +def get_engine_url(): + try: + return get_engine().url.render_as_string(hide_password=False).replace( + '%', '%%') + except AttributeError: + return str(get_engine().url).replace('%', '%%') + + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +config.set_main_option('sqlalchemy.url', get_engine_url()) +target_db = current_app.extensions['migrate'].db + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def get_metadata(): + if hasattr(target_db, 'metadatas'): + return target_db.metadatas[None] + return target_db.metadata + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, target_metadata=get_metadata(), literal_binds=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + # this callback is used to prevent an auto-migration from being generated + # when there are no changes to the schema + # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html + def process_revision_directives(context, revision, directives): + if getattr(config.cmd_opts, 'autogenerate', False): + script = directives[0] + if script.upgrade_ops.is_empty(): + directives[:] = [] + logger.info('No changes in schema detected.') + + conf_args = current_app.extensions['migrate'].configure_args + if conf_args.get("process_revision_directives") is None: + conf_args["process_revision_directives"] = process_revision_directives + + connectable = get_engine() + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=get_metadata(), + **conf_args + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/migrations/script.py.mako b/migrations/script.py.mako new file mode 100644 index 0000000..2c01563 --- /dev/null +++ b/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/migrations/versions/0344241db67d_.py b/migrations/versions/0344241db67d_.py new file mode 100644 index 0000000..47a14f8 --- /dev/null +++ b/migrations/versions/0344241db67d_.py @@ -0,0 +1,43 @@ +"""empty message + +Revision ID: 0344241db67d +Revises: +Create Date: 2024-11-05 16:05:21.795648 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '0344241db67d' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('character', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(), nullable=False), + sa.Column('personality', sa.String(), nullable=False), + sa.Column('occupation', sa.String(), nullable=False), + sa.Column('age', sa.Integer(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('greeting', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('greeting_text', sa.String(), nullable=False), + sa.Column('character_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['character_id'], ['character.id'], ), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('greeting') + op.drop_table('character') + # ### end Alembic commands ### diff --git a/requirements.txt b/requirements.txt index f9c8b70..d3d48f3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,15 +2,27 @@ alembic==1.13.1 annotated-types==0.6.0 anyio==4.3.0 blinker==1.7.0 +cachetools==5.5.0 certifi==2024.2.2 +charset-normalizer==3.4.0 click==8.1.7 distro==1.9.0 Flask==3.0.2 Flask-Migrate==4.0.5 Flask-SQLAlchemy==3.1.1 +google-ai-generativelanguage==0.6.10 +google-api-core==2.22.0 +google-api-python-client==2.151.0 +google-auth==2.35.0 +google-auth-httplib2==0.2.0 +google-generativeai==0.8.3 +googleapis-common-protos==1.65.0 greenlet==3.0.3 +grpcio==1.67.1 +grpcio-status==1.67.1 h11==0.14.0 httpcore==1.0.5 +httplib2==0.22.0 httpx==0.27.0 idna==3.7 iniconfig==2.0.0 @@ -18,15 +30,26 @@ itsdangerous==2.1.2 Jinja2==3.1.3 Mako==1.3.2 MarkupSafe==2.1.5 +openai==1.35.3 packaging==23.2 pluggy==1.4.0 +proto-plus==1.25.0 +protobuf==5.28.3 psycopg2-binary==2.9.9 +pyasn1==0.6.1 +pyasn1_modules==0.4.1 pydantic==2.7.1 pydantic_core==2.18.2 +pyparsing==3.2.0 pytest==8.0.0 python-dotenv==1.0.1 +requests==2.32.3 +rsa==4.9 sniffio==1.3.1 SQLAlchemy==2.0.25 tqdm==4.66.4 typing_extensions==4.9.0 +uritemplate==4.1.1 +urllib3==2.2.3 Werkzeug==3.0.1 +wonderwords==2.2.0 diff --git a/reset_db.py b/reset_db.py new file mode 100644 index 0000000..105f11d --- /dev/null +++ b/reset_db.py @@ -0,0 +1,7 @@ +from app import create_app, db + + +my_app = create_app() +with my_app.app_context(): + db.session.clear() + print("Session cleared.") \ No newline at end of file From b382021822ce1f559c58a1b1abda362abdd096a2 Mon Sep 17 00:00:00 2001 From: Adrian Prado Date: Tue, 12 Nov 2024 16:53:03 -0600 Subject: [PATCH 07/18] Changed variable names and added comments --- app/routes/character_routes.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/routes/character_routes.py b/app/routes/character_routes.py index 2b363c3..82a3c6e 100644 --- a/app/routes/character_routes.py +++ b/app/routes/character_routes.py @@ -66,7 +66,7 @@ def add_greetings(char_id): for greeting in greetings: new_greeting = Greeting( - greeting_text = greeting.strip("\""), + greeting_text = greeting.strip("\""), #Removes quotes from each string character = character_obj ) new_greetings.append(new_greeting) @@ -78,10 +78,10 @@ def add_greetings(char_id): def generate_greetings(character): model = genai.GenerativeModel("gemini-1.5-flash") - input_message = f"I am writing a video game in the style of The Witcher. I have an npc named {character.name} who is {character.age} years old. They are a {character.occupation} who has a {character.personality} personality. Please generate a python style list of 10 stock phrases they might use when the main character talks to them. Please Return just the list without a variable name and square brackets." + input_message = f"I am writing a rantasy RPG video game. I have an npc named {character.name} who is {character.age} years old. They are a {character.occupation} who has a {character.personality} personality. Please generate a python style list of 10 stock phrases they might use when the main character talks to them. Please Return just the list without a variable name and square brackets." response = model.generate_content(input_message) - response_sani = response.text.split("\n") - return response_sani[:-1] + response_split = response.text.split("\n") #Splits response into a list of stock phrases, ends up with an empty string at index -1 + return response_split[:-1] #Returns the stock phrases list, just without the empty string at the end def validate_model(cls,id): From e80f5dac234ae87780d69ac758275209a4a5e789 Mon Sep 17 00:00:00 2001 From: Kelsey Steven Date: Tue, 26 Nov 2024 12:14:39 -0800 Subject: [PATCH 08/18] Update to remove jsonify and return data structures directly following patterns in Unit 2 --- app/routes/character_routes.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/routes/character_routes.py b/app/routes/character_routes.py index e0db8bf..2de5831 100644 --- a/app/routes/character_routes.py +++ b/app/routes/character_routes.py @@ -1,4 +1,4 @@ -from flask import Blueprint, jsonify, request, abort, make_response +from flask import Blueprint, request, abort, make_response from ..db import db from ..models.character import Character from ..models.greeting import Greeting @@ -15,7 +15,7 @@ def create_character(): db.session.add(new_character) db.session.commit() - return make_response(new_character.to_dict(), 201) + return new_character.to_dict(), 201 except KeyError as e: abort(make_response({"message": f"missing required value: {e}"}, 400)) @@ -38,7 +38,7 @@ def get_characters(): } ) - return jsonify(response) + return response @bp.get("//greetings") def get_greetings(char_id): From 2592fbd98507c240efc6152428c0f656be804d67 Mon Sep 17 00:00:00 2001 From: Kelsey Steven Date: Tue, 26 Nov 2024 12:20:47 -0800 Subject: [PATCH 09/18] Remove jsonify and make_response where possible --- app/routes/character_routes.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/routes/character_routes.py b/app/routes/character_routes.py index 82a3c6e..3806916 100644 --- a/app/routes/character_routes.py +++ b/app/routes/character_routes.py @@ -1,4 +1,4 @@ -from flask import Blueprint, jsonify, request, abort, make_response +from flask import Blueprint, request, abort, make_response from ..db import db from ..models.character import Character from ..models.greeting import Greeting @@ -20,7 +20,7 @@ def create_character(): db.session.add(new_character) db.session.commit() - return make_response(new_character.to_dict(), 201) + return new_character.to_dict(), 201 except KeyError as e: abort(make_response({"message": f"missing required value: {e}"}, 400)) @@ -35,14 +35,14 @@ def get_characters(): for character in characters: response.append(character.to_dict()) - return jsonify(response) + return response @bp.get("//greetings") def get_greetings(char_id): character = validate_model(Character, char_id) if not character.greetings: - return make_response(jsonify(f"No greetings found for {character.name} "), 201) + return {"message": f"No greetings found for {character.name} "}, 201 response = {"Character Name" : character.name, "Greetings" : []} @@ -51,7 +51,7 @@ def get_greetings(char_id): "greeting" : greeting.greeting_text }) - return jsonify(response) + return response @bp.post("//generate") @@ -60,7 +60,7 @@ def add_greetings(char_id): greetings = generate_greetings(character_obj) if character_obj.greetings: - return make_response(jsonify(f"Greetings already generated for {character_obj.name} "), 201) + return {"message": f"Greetings already generated for {character_obj.name} "}, 201 new_greetings = [] @@ -74,7 +74,7 @@ def add_greetings(char_id): db.session.add_all(new_greetings) db.session.commit() - return make_response(jsonify(f"Greetings successfully added to {character_obj.name}"), 201) + return {"message": f"Greetings successfully added to {character_obj.name}"}, 201 def generate_greetings(character): model = genai.GenerativeModel("gemini-1.5-flash") @@ -88,7 +88,7 @@ def validate_model(cls,id): try: id = int(id) except: - response = response = {"message": f"{cls.__name__} {id} invalid"} + response = {"message": f"{cls.__name__} {id} invalid"} abort(make_response(response , 400)) query = db.select(cls).where(cls.id == id) From cb4eb893db5c40d2ed3186d9b756a6b6786ee2d4 Mon Sep 17 00:00:00 2001 From: Kelsey Steven Date: Tue, 26 Nov 2024 12:28:02 -0800 Subject: [PATCH 10/18] Small changes to typo & casing in prompt to match lesson --- app/routes/character_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes/character_routes.py b/app/routes/character_routes.py index 3806916..ad1fd59 100644 --- a/app/routes/character_routes.py +++ b/app/routes/character_routes.py @@ -78,7 +78,7 @@ def add_greetings(char_id): def generate_greetings(character): model = genai.GenerativeModel("gemini-1.5-flash") - input_message = f"I am writing a rantasy RPG video game. I have an npc named {character.name} who is {character.age} years old. They are a {character.occupation} who has a {character.personality} personality. Please generate a python style list of 10 stock phrases they might use when the main character talks to them. Please Return just the list without a variable name and square brackets." + input_message = f"I am writing a fantasy RPG video game. I have an npc named {character.name} who is {character.age} years old. They are a {character.occupation} who has a {character.personality} personality. Please generate a Python style list of 10 stock phrases they might use when the main character talks to them. Please return just the list without a variable name and square brackets." response = model.generate_content(input_message) response_split = response.text.split("\n") #Splits response into a list of stock phrases, ends up with an empty string at index -1 return response_split[:-1] #Returns the stock phrases list, just without the empty string at the end From d778d71a70577c1a81adaba332dfba09f1758dcc Mon Sep 17 00:00:00 2001 From: Kelsey Steven Date: Tue, 26 Nov 2024 12:30:36 -0800 Subject: [PATCH 11/18] Remove duplicate response var name in validate_model --- app/routes/character_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes/character_routes.py b/app/routes/character_routes.py index 2de5831..6f5faeb 100644 --- a/app/routes/character_routes.py +++ b/app/routes/character_routes.py @@ -55,7 +55,7 @@ def validate_model(cls,id): try: id = int(id) except: - response = response = {"message": f"{cls.__name__} {id} invalid"} + response = {"message": f"{cls.__name__} {id} invalid"} abort(make_response(response , 400)) query = db.select(cls).where(cls.id == id) From f82340c656817aa3b15b4ad2c24b6f7d406224e1 Mon Sep 17 00:00:00 2001 From: Kelsey Steven <97124999+kelsey-steven-ada@users.noreply.github.com> Date: Thu, 6 Mar 2025 15:48:55 -0800 Subject: [PATCH 12/18] Update dependencies for Python 3.13 --- requirements.txt | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/requirements.txt b/requirements.txt index f9c8b70..ac73ff4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,32 +1,31 @@ -alembic==1.13.1 +alembic==1.14.1 annotated-types==0.6.0 anyio==4.3.0 -blinker==1.7.0 -certifi==2024.2.2 -click==8.1.7 +blinker==1.9.0 +certifi==2025.1.31 +click==8.1.8 distro==1.9.0 -Flask==3.0.2 -Flask-Migrate==4.0.5 +Flask==3.1.0 +Flask-Migrate==4.1.0 Flask-SQLAlchemy==3.1.1 -greenlet==3.0.3 h11==0.14.0 httpcore==1.0.5 httpx==0.27.0 -idna==3.7 +idna==3.10 iniconfig==2.0.0 -itsdangerous==2.1.2 -Jinja2==3.1.3 -Mako==1.3.2 -MarkupSafe==2.1.5 -packaging==23.2 -pluggy==1.4.0 -psycopg2-binary==2.9.9 +itsdangerous==2.2.0 +Jinja2==3.1.5 +Mako==1.3.9 +MarkupSafe==3.0.2 +packaging==24.2 +pluggy==1.5.0 +psycopg2-binary==2.9.10 pydantic==2.7.1 pydantic_core==2.18.2 -pytest==8.0.0 +pytest==8.3.4 python-dotenv==1.0.1 sniffio==1.3.1 -SQLAlchemy==2.0.25 +SQLAlchemy==2.0.38 tqdm==4.66.4 -typing_extensions==4.9.0 -Werkzeug==3.0.1 +typing_extensions==4.12.2 +Werkzeug==3.1.3 From 059da5a735d4ce1967d58129662c538e78960520 Mon Sep 17 00:00:00 2001 From: Kelsey Steven Date: Fri, 7 Mar 2025 15:49:55 -0800 Subject: [PATCH 13/18] Fresh install dependencies to avoid building older version of dependency that takes time --- requirements.txt | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/requirements.txt b/requirements.txt index ac73ff4..a7a270b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,31 +1,46 @@ -alembic==1.14.1 -annotated-types==0.6.0 -anyio==4.3.0 +alembic==1.15.1 +annotated-types==0.7.0 blinker==1.9.0 +cachetools==5.5.2 certifi==2025.1.31 +charset-normalizer==3.4.1 click==8.1.8 -distro==1.9.0 Flask==3.1.0 Flask-Migrate==4.1.0 Flask-SQLAlchemy==3.1.1 -h11==0.14.0 -httpcore==1.0.5 -httpx==0.27.0 +google-ai-generativelanguage==0.6.15 +google-api-core==2.24.1 +google-api-python-client==2.163.0 +google-auth==2.38.0 +google-auth-httplib2==0.2.0 +google-generativeai==0.8.4 +googleapis-common-protos==1.69.1 +grpcio==1.70.0 +grpcio-status==1.70.0 +httplib2==0.22.0 idna==3.10 iniconfig==2.0.0 itsdangerous==2.2.0 -Jinja2==3.1.5 +Jinja2==3.1.6 Mako==1.3.9 MarkupSafe==3.0.2 packaging==24.2 pluggy==1.5.0 +proto-plus==1.26.0 +protobuf==5.29.3 psycopg2-binary==2.9.10 -pydantic==2.7.1 -pydantic_core==2.18.2 -pytest==8.3.4 +pyasn1==0.6.1 +pyasn1_modules==0.4.1 +pydantic==2.10.6 +pydantic_core==2.27.2 +pyparsing==3.2.1 +pytest==8.3.5 python-dotenv==1.0.1 -sniffio==1.3.1 +requests==2.32.3 +rsa==4.9 SQLAlchemy==2.0.38 -tqdm==4.66.4 +tqdm==4.67.1 typing_extensions==4.12.2 +uritemplate==4.1.1 +urllib3==2.3.0 Werkzeug==3.1.3 From 1c72085a9ac2b0e5a398462ec1d0829e0dcdf1e0 Mon Sep 17 00:00:00 2001 From: Kelsey Steven <97124999+kelsey-steven-ada@users.noreply.github.com> Date: Mon, 10 Mar 2025 08:53:38 -0700 Subject: [PATCH 14/18] Update requirements.txt --- requirements.txt | 81 +++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 45 deletions(-) diff --git a/requirements.txt b/requirements.txt index d3d48f3..a7a270b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,55 +1,46 @@ -alembic==1.13.1 -annotated-types==0.6.0 -anyio==4.3.0 -blinker==1.7.0 -cachetools==5.5.0 -certifi==2024.2.2 -charset-normalizer==3.4.0 -click==8.1.7 -distro==1.9.0 -Flask==3.0.2 -Flask-Migrate==4.0.5 +alembic==1.15.1 +annotated-types==0.7.0 +blinker==1.9.0 +cachetools==5.5.2 +certifi==2025.1.31 +charset-normalizer==3.4.1 +click==8.1.8 +Flask==3.1.0 +Flask-Migrate==4.1.0 Flask-SQLAlchemy==3.1.1 -google-ai-generativelanguage==0.6.10 -google-api-core==2.22.0 -google-api-python-client==2.151.0 -google-auth==2.35.0 +google-ai-generativelanguage==0.6.15 +google-api-core==2.24.1 +google-api-python-client==2.163.0 +google-auth==2.38.0 google-auth-httplib2==0.2.0 -google-generativeai==0.8.3 -googleapis-common-protos==1.65.0 -greenlet==3.0.3 -grpcio==1.67.1 -grpcio-status==1.67.1 -h11==0.14.0 -httpcore==1.0.5 +google-generativeai==0.8.4 +googleapis-common-protos==1.69.1 +grpcio==1.70.0 +grpcio-status==1.70.0 httplib2==0.22.0 -httpx==0.27.0 -idna==3.7 +idna==3.10 iniconfig==2.0.0 -itsdangerous==2.1.2 -Jinja2==3.1.3 -Mako==1.3.2 -MarkupSafe==2.1.5 -openai==1.35.3 -packaging==23.2 -pluggy==1.4.0 -proto-plus==1.25.0 -protobuf==5.28.3 -psycopg2-binary==2.9.9 +itsdangerous==2.2.0 +Jinja2==3.1.6 +Mako==1.3.9 +MarkupSafe==3.0.2 +packaging==24.2 +pluggy==1.5.0 +proto-plus==1.26.0 +protobuf==5.29.3 +psycopg2-binary==2.9.10 pyasn1==0.6.1 pyasn1_modules==0.4.1 -pydantic==2.7.1 -pydantic_core==2.18.2 -pyparsing==3.2.0 -pytest==8.0.0 +pydantic==2.10.6 +pydantic_core==2.27.2 +pyparsing==3.2.1 +pytest==8.3.5 python-dotenv==1.0.1 requests==2.32.3 rsa==4.9 -sniffio==1.3.1 -SQLAlchemy==2.0.25 -tqdm==4.66.4 -typing_extensions==4.9.0 +SQLAlchemy==2.0.38 +tqdm==4.67.1 +typing_extensions==4.12.2 uritemplate==4.1.1 -urllib3==2.2.3 -Werkzeug==3.0.1 -wonderwords==2.2.0 +urllib3==2.3.0 +Werkzeug==3.1.3 From 513976868d44ccd907e2ab4ca44a50fbcf7f4a98 Mon Sep 17 00:00:00 2001 From: Ansel Rognlie Date: Tue, 9 Dec 2025 10:27:22 -0800 Subject: [PATCH 15/18] updates solution from Learn snippets --- app/routes/character_routes.py | 55 ++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/app/routes/character_routes.py b/app/routes/character_routes.py index ad1fd59..6dac0c7 100644 --- a/app/routes/character_routes.py +++ b/app/routes/character_routes.py @@ -4,27 +4,28 @@ from ..models.greeting import Greeting from sqlalchemy import func, union, except_ -import google.generativeai as genai -import os +from google import genai -genai.configure(api_key=os.environ.get("GEMINI_API_KEY")) +client = genai.Client() bp = Blueprint("characters", __name__, url_prefix="/characters") + @bp.post("") def create_character(): request_body = request.get_json() - try: + try: new_character = Character.from_dict(request_body) db.session.add(new_character) db.session.commit() return new_character.to_dict(), 201 - + except KeyError as e: abort(make_response({"message": f"missing required value: {e}"}, 400)) + @bp.get("") def get_characters(): character_query = db.select(Character) @@ -37,59 +38,63 @@ def get_characters(): return response + @bp.get("//greetings") def get_greetings(char_id): character = validate_model(Character, char_id) - + if not character.greetings: - return {"message": f"No greetings found for {character.name} "}, 201 - - response = {"Character Name" : character.name, - "Greetings" : []} + return {"message": f"No greetings found for {character.name}"}, 201 + + response = {"character_name": character.name, "greetings": []} + for greeting in character.greetings: - response["Greetings"].append({ - "greeting" : greeting.greeting_text + response["greetings"].append({ + "greeting": greeting.greeting_text }) - + return response @bp.post("//generate") def add_greetings(char_id): character_obj = validate_model(Character, char_id) - greetings = generate_greetings(character_obj) if character_obj.greetings: return {"message": f"Greetings already generated for {character_obj.name} "}, 201 - + + greetings = generate_greetings(character_obj) + new_greetings = [] for greeting in greetings: new_greeting = Greeting( - greeting_text = greeting.strip("\""), #Removes quotes from each string - character = character_obj + # Strip leading and trailing quotes and commas from each greeting + greeting_text=greeting.strip("\"',"), + character=character_obj ) new_greetings.append(new_greeting) - + db.session.add_all(new_greetings) db.session.commit() return {"message": f"Greetings successfully added to {character_obj.name}"}, 201 + def generate_greetings(character): - model = genai.GenerativeModel("gemini-1.5-flash") input_message = f"I am writing a fantasy RPG video game. I have an npc named {character.name} who is {character.age} years old. They are a {character.occupation} who has a {character.personality} personality. Please generate a Python style list of 10 stock phrases they might use when the main character talks to them. Please return just the list without a variable name and square brackets." - response = model.generate_content(input_message) - response_split = response.text.split("\n") #Splits response into a list of stock phrases, ends up with an empty string at index -1 - return response_split[:-1] #Returns the stock phrases list, just without the empty string at the end + response = client.models.generate_content( + model="gemini-2.5-flash", contents=input_message + ) + return response.text.splitlines() -def validate_model(cls,id): +def validate_model(cls, id): try: id = int(id) except: response = {"message": f"{cls.__name__} {id} invalid"} - abort(make_response(response , 400)) + abort(make_response(response, 400)) query = db.select(cls).where(cls.id == id) model = db.session.scalar(query) @@ -97,4 +102,4 @@ def validate_model(cls,id): return model response = {"message": f"{cls.__name__} {id} not found"} - abort(make_response(response, 404)) \ No newline at end of file + abort(make_response(response, 404)) From 9531d346a0207147c6af40b1f09c3305c91d2591 Mon Sep 17 00:00:00 2001 From: Ansel Rognlie Date: Tue, 9 Dec 2025 10:37:50 -0800 Subject: [PATCH 16/18] updates formatting --- app/__init__.py | 4 ++-- app/models/base.py | 3 ++- app/models/character.py | 23 ++++++++++++----------- app/models/greeting.py | 11 ++++++----- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index efa908d..9dbe643 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -4,6 +4,7 @@ from .db import db, migrate from .models import character, greeting + def create_app(test_config=None): app = Flask(__name__) @@ -11,7 +12,6 @@ def create_app(test_config=None): db_to_use = os.environ.get("SQLALCHEMY_DATABASE_URI") else: db_to_use = os.environ.get("SQLALCHEMY_TEST_DATABASE_URI") - app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_DATABASE_URI'] = db_to_use @@ -21,4 +21,4 @@ def create_app(test_config=None): app.register_blueprint(bp) - return app \ No newline at end of file + return app diff --git a/app/models/base.py b/app/models/base.py index 2278416..fa2b68a 100644 --- a/app/models/base.py +++ b/app/models/base.py @@ -1,4 +1,5 @@ from sqlalchemy.orm import DeclarativeBase + class Base(DeclarativeBase): - pass \ No newline at end of file + pass diff --git a/app/models/character.py b/app/models/character.py index 64892a9..032967f 100644 --- a/app/models/character.py +++ b/app/models/character.py @@ -1,6 +1,7 @@ from sqlalchemy.orm import Mapped, mapped_column, relationship from ..db import db + class Character(db.Model): id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) name: Mapped[str] @@ -8,23 +9,23 @@ class Character(db.Model): occupation: Mapped[str] age: Mapped[int] greetings: Mapped[list["Greeting"]] = relationship(back_populates="character") - + def to_dict(self): return { - "id" : self.id, - "name" : self.name, - "personality" : self.personality, - "occupation" : self.occupation, - "age" : self.age + "id": self.id, + "name": self.name, + "personality": self.personality, + "occupation": self.occupation, + "age": self.age } - + @classmethod def from_dict(cls, data_dict): new_character = cls( - name = data_dict["name"], - personality = data_dict["personality"], - occupation = data_dict["occupation"], - age = data_dict["age"] + name=data_dict["name"], + personality=data_dict["personality"], + occupation=data_dict["occupation"], + age=data_dict["age"] ) return new_character diff --git a/app/models/greeting.py b/app/models/greeting.py index 094c7bd..e800396 100644 --- a/app/models/greeting.py +++ b/app/models/greeting.py @@ -2,14 +2,15 @@ from sqlalchemy import ForeignKey from ..db import db + class Greeting(db.Model): id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) greeting_text: Mapped[str] - character_id: Mapped[int] = mapped_column(ForeignKey("character.id")) + character_id: Mapped[int] = mapped_column(ForeignKey("character.id")) character: Mapped["Character"] = relationship(back_populates="greetings") -def to_dict(self): + def to_dict(self): return { - "id" : self.id, - "greeting" : self.greeting_text, - } \ No newline at end of file + "id": self.id, + "greeting": self.greeting_text, + } From 0253ad390720736f56c2497ec10a6576b182bc3f Mon Sep 17 00:00:00 2001 From: Ansel Rognlie Date: Tue, 9 Dec 2025 10:41:05 -0800 Subject: [PATCH 17/18] updates formatting --- app/__init__.py | 4 ++-- app/models/base.py | 3 ++- app/models/character.py | 23 ++++++++++++----------- app/models/greeting.py | 12 +++++++----- app/routes/character_routes.py | 26 ++++++++++++++++---------- 5 files changed, 39 insertions(+), 29 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index efa908d..9dbe643 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -4,6 +4,7 @@ from .db import db, migrate from .models import character, greeting + def create_app(test_config=None): app = Flask(__name__) @@ -11,7 +12,6 @@ def create_app(test_config=None): db_to_use = os.environ.get("SQLALCHEMY_DATABASE_URI") else: db_to_use = os.environ.get("SQLALCHEMY_TEST_DATABASE_URI") - app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_DATABASE_URI'] = db_to_use @@ -21,4 +21,4 @@ def create_app(test_config=None): app.register_blueprint(bp) - return app \ No newline at end of file + return app diff --git a/app/models/base.py b/app/models/base.py index 2278416..fa2b68a 100644 --- a/app/models/base.py +++ b/app/models/base.py @@ -1,4 +1,5 @@ from sqlalchemy.orm import DeclarativeBase + class Base(DeclarativeBase): - pass \ No newline at end of file + pass diff --git a/app/models/character.py b/app/models/character.py index 64892a9..032967f 100644 --- a/app/models/character.py +++ b/app/models/character.py @@ -1,6 +1,7 @@ from sqlalchemy.orm import Mapped, mapped_column, relationship from ..db import db + class Character(db.Model): id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) name: Mapped[str] @@ -8,23 +9,23 @@ class Character(db.Model): occupation: Mapped[str] age: Mapped[int] greetings: Mapped[list["Greeting"]] = relationship(back_populates="character") - + def to_dict(self): return { - "id" : self.id, - "name" : self.name, - "personality" : self.personality, - "occupation" : self.occupation, - "age" : self.age + "id": self.id, + "name": self.name, + "personality": self.personality, + "occupation": self.occupation, + "age": self.age } - + @classmethod def from_dict(cls, data_dict): new_character = cls( - name = data_dict["name"], - personality = data_dict["personality"], - occupation = data_dict["occupation"], - age = data_dict["age"] + name=data_dict["name"], + personality=data_dict["personality"], + occupation=data_dict["occupation"], + age=data_dict["age"] ) return new_character diff --git a/app/models/greeting.py b/app/models/greeting.py index 094c7bd..7c9fd8d 100644 --- a/app/models/greeting.py +++ b/app/models/greeting.py @@ -2,14 +2,16 @@ from sqlalchemy import ForeignKey from ..db import db + class Greeting(db.Model): id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) greeting_text: Mapped[str] - character_id: Mapped[int] = mapped_column(ForeignKey("character.id")) + character_id: Mapped[int] = mapped_column(ForeignKey("character.id")) character: Mapped["Character"] = relationship(back_populates="greetings") -def to_dict(self): + + def to_dict(self): return { - "id" : self.id, - "greeting" : self.greeting_text, - } \ No newline at end of file + "id": self.id, + "greeting": self.greeting_text, + } diff --git a/app/routes/character_routes.py b/app/routes/character_routes.py index 6f5faeb..d1b6342 100644 --- a/app/routes/character_routes.py +++ b/app/routes/character_routes.py @@ -6,20 +6,22 @@ bp = Blueprint("characters", __name__, url_prefix="/characters") + @bp.post("") def create_character(): request_body = request.get_json() - try: + try: new_character = Character.from_dict(request_body) db.session.add(new_character) db.session.commit() return new_character.to_dict(), 201 - + except KeyError as e: abort(make_response({"message": f"missing required value: {e}"}, 400)) + @bp.get("") def get_characters(): character_query = db.select(Character) @@ -30,33 +32,37 @@ def get_characters(): for character in characters: response.append( { - "id" : character.id, - "name" : character.name, - "personality" : character.personality, - "occupation" : character.occupation, - "age" : character.age + "id": character.id, + "name": character.name, + "personality": character.personality, + "occupation": character.occupation, + "age": character.age } ) return response + @bp.get("//greetings") def get_greetings(char_id): pass + @bp.post("//generate") def add_greetings(char_id): pass + def generate_greetings(character): pass -def validate_model(cls,id): + +def validate_model(cls, id): try: id = int(id) except: response = {"message": f"{cls.__name__} {id} invalid"} - abort(make_response(response , 400)) + abort(make_response(response, 400)) query = db.select(cls).where(cls.id == id) model = db.session.scalar(query) @@ -64,4 +70,4 @@ def validate_model(cls,id): return model response = {"message": f"{cls.__name__} {id} not found"} - abort(make_response(response, 404)) \ No newline at end of file + abort(make_response(response, 404)) From 45ef2683de450d2e8b2ff83a34b42fa9d6a0b464 Mon Sep 17 00:00:00 2001 From: Ansel Rognlie Date: Tue, 9 Dec 2025 11:29:36 -0800 Subject: [PATCH 18/18] adds get one route and condenses route logic --- app/routes/character_routes.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/app/routes/character_routes.py b/app/routes/character_routes.py index d1b6342..3e422ac 100644 --- a/app/routes/character_routes.py +++ b/app/routes/character_routes.py @@ -27,22 +27,17 @@ def get_characters(): character_query = db.select(Character) characters = db.session.scalars(character_query) - response = [] - - for character in characters: - response.append( - { - "id": character.id, - "name": character.name, - "personality": character.personality, - "occupation": character.occupation, - "age": character.age - } - ) + response = [character.to_dict() for character in characters] return response +@bp.get("/") +def get_character(char_id): + character = validate_model(Character, char_id) + return character.to_dict() + + @bp.get("//greetings") def get_greetings(char_id): pass