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, + } diff --git a/app/routes/character_routes.py b/app/routes/character_routes.py index 2892a23..3d22707 100644 --- a/app/routes/character_routes.py +++ b/app/routes/character_routes.py @@ -1,104 +1,98 @@ -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 from sqlalchemy import func, union, except_ -from openai import OpenAI + +from google import genai + +client = genai.Client() bp = Blueprint("characters", __name__, url_prefix="/characters") -client = OpenAI() + @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 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)) + @bp.get("") 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() - return jsonify(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) - - response = {"Character Name" : character.name, - "Greetings" : []} - for greeting in character.greetings: - response["Greetings"].append({ - "greeting" : greeting.greeting_text - }) - - return jsonify(response) + + response = [greeting.to_dict() for greeting in character.greetings] + + return response + @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) - + return {"message": f"Greetings already generated for {character_obj.name} "}, 201 + + greetings = generate_greetings(character_obj) + new_greetings = [] for greeting in greetings: - text = greeting[greeting.find(" ")+1:] new_greeting = Greeting( - greeting_text = text.strip("\""), - character = character_obj + greeting_text=greeting, + 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) + return {"message": 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} - ] +def generate_greetings(character): + 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 = client.models.generate_content( + model="gemini-2.5-flash", contents=input_message ) - return(completion.choices[0].message.content.split("\n")) -def validate_model(cls,id): + lines = response.text.splitlines() + + # Strip leading and trailing quotes and commas from each greeting + return [line.strip("\"',") for line in lines] + + +def validate_model(cls, id): try: id = int(id) except: - response = response = {"message": f"{cls.__name__} {id} invalid"} - abort(make_response(response , 400)) + response = {"message": f"{cls.__name__} {id} invalid"} + abort(make_response(response, 400)) query = db.select(cls).where(cls.id == id) model = db.session.scalar(query) @@ -106,4 +100,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)) diff --git a/migrations/versions/d7a96ea32a49_add_character_model.py b/migrations/versions/0344241db67d_.py similarity index 62% rename from migrations/versions/d7a96ea32a49_add_character_model.py rename to migrations/versions/0344241db67d_.py index 43d6168..47a14f8 100644 --- a/migrations/versions/d7a96ea32a49_add_character_model.py +++ b/migrations/versions/0344241db67d_.py @@ -1,8 +1,8 @@ -"""Add character model +"""empty message -Revision ID: d7a96ea32a49 +Revision ID: 0344241db67d Revises: -Create Date: 2024-06-11 11:15:08.811220 +Create Date: 2024-11-05 16:05:21.795648 """ from alembic import op @@ -10,7 +10,7 @@ # revision identifiers, used by Alembic. -revision = 'd7a96ea32a49' +revision = '0344241db67d' down_revision = None branch_labels = None depends_on = None @@ -26,10 +26,18 @@ def upgrade(): 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/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/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 ### diff --git a/requirements.txt b/requirements.txt index f9c8b70..a7a270b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,32 +1,46 @@ -alembic==1.13.1 -annotated-types==0.6.0 -anyio==4.3.0 -blinker==1.7.0 -certifi==2024.2.2 -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 -greenlet==3.0.3 -h11==0.14.0 -httpcore==1.0.5 -httpx==0.27.0 -idna==3.7 +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.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 -pydantic==2.7.1 -pydantic_core==2.18.2 -pytest==8.0.0 +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.10.6 +pydantic_core==2.27.2 +pyparsing==3.2.1 +pytest==8.3.5 python-dotenv==1.0.1 -sniffio==1.3.1 -SQLAlchemy==2.0.25 -tqdm==4.66.4 -typing_extensions==4.9.0 -Werkzeug==3.0.1 +requests==2.32.3 +rsa==4.9 +SQLAlchemy==2.0.38 +tqdm==4.67.1 +typing_extensions==4.12.2 +uritemplate==4.1.1 +urllib3==2.3.0 +Werkzeug==3.1.3 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