From 5c102ca1342115055a9f2d8925fb767d292c1230 Mon Sep 17 00:00:00 2001 From: LP Date: Wed, 2 Nov 2022 13:53:07 -0700 Subject: [PATCH 01/34] set up .env file --- migrations/README | 1 + migrations/alembic.ini | 45 ++++++++++++++++++ migrations/env.py | 96 +++++++++++++++++++++++++++++++++++++++ migrations/script.py.mako | 24 ++++++++++ 4 files changed, 166 insertions(+) create mode 100644 migrations/README create mode 100644 migrations/alembic.ini create mode 100644 migrations/env.py create mode 100644 migrations/script.py.mako diff --git a/migrations/README b/migrations/README new file mode 100644 index 000000000..98e4f9c44 --- /dev/null +++ b/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/migrations/alembic.ini b/migrations/alembic.ini new file mode 100644 index 000000000..f8ed4801f --- /dev/null +++ b/migrations/alembic.ini @@ -0,0 +1,45 @@ +# 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 + +[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 + +[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 000000000..8b3fb3353 --- /dev/null +++ b/migrations/env.py @@ -0,0 +1,96 @@ +from __future__ import with_statement + +import logging +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool +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') + +# 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', + str(current_app.extensions['migrate'].db.engine.url).replace('%', '%%')) +target_metadata = current_app.extensions['migrate'].db.metadata + +# 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 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=target_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.') + + connectable = engine_from_config( + config.get_section(config.config_ini_section), + prefix='sqlalchemy.', + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=target_metadata, + process_revision_directives=process_revision_directives, + **current_app.extensions['migrate'].configure_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 000000000..2c0156303 --- /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"} From c696890eb943f28ed964a7b38ede102f3745f6be Mon Sep 17 00:00:00 2001 From: LP Date: Wed, 2 Nov 2022 14:58:04 -0700 Subject: [PATCH 02/34] made a working endpoint for getting all tasks. registered blueprints --- app/__init__.py | 2 ++ app/models/task.py | 16 +++++++++++++++- app/routes.py | 17 ++++++++++++++++- tests/test_wave_01.py | 2 +- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 2764c4cc8..14b242cca 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -30,5 +30,7 @@ def create_app(test_config=None): migrate.init_app(app, db) # Register Blueprints here + from app.routes import task_bp + app.register_blueprint(task_bp) return app diff --git a/app/models/task.py b/app/models/task.py index c91ab281f..dbaa25411 100644 --- a/app/models/task.py +++ b/app/models/task.py @@ -2,4 +2,18 @@ class Task(db.Model): - task_id = db.Column(db.Integer, primary_key=True) + task_id = db.Column(db.Integer, primary_key=True, autoincrement = True) + title = db.Column(db.String) + description = db.Column(db.String) + completed_at = db.Column(db.DateTime, default = None) + + def make_dict(self): + """given a task, return a dictionary + with all the attibutes of that task.""" + task_dict = { + "task_id": self.task_id, + "title": self.title, + "description": self.description, + "completed_at": self.completed_at + } + return task_dict \ No newline at end of file diff --git a/app/routes.py b/app/routes.py index 3aae38d49..449b93180 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1 +1,16 @@ -from flask import Blueprint \ No newline at end of file +from flask import Blueprint, jsonify +from app.models.task import Task + +#make a blueprint +task_bp = Blueprint("task_bp", __name__, url_prefix = "/tasks") + +@task_bp.route("", methods = ["GET"]) +def get_all_tasks(): + tasks = Task.query.all() + response = [] + for task in tasks: + task_dict = task.make_dict + response.append(task_dict) + return jsonify(response), 200 + + diff --git a/tests/test_wave_01.py b/tests/test_wave_01.py index dca626d78..f18e1c566 100644 --- a/tests/test_wave_01.py +++ b/tests/test_wave_01.py @@ -2,7 +2,7 @@ import pytest -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_get_tasks_no_saved_tasks(client): # Act response = client.get("/tasks") From 86c1c2784b8ead181d29745d5749b6bf6791d1b8 Mon Sep 17 00:00:00 2001 From: LP Date: Wed, 2 Nov 2022 14:58:33 -0700 Subject: [PATCH 03/34] adding routes file to commit --- app/routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes.py b/app/routes.py index 449b93180..18365109a 100644 --- a/app/routes.py +++ b/app/routes.py @@ -9,7 +9,7 @@ def get_all_tasks(): tasks = Task.query.all() response = [] for task in tasks: - task_dict = task.make_dict + task_dict = task.make_dict() response.append(task_dict) return jsonify(response), 200 From 77d641e725ad72e0ed91984189c43214d15f7a46 Mon Sep 17 00:00:00 2001 From: LP Date: Thu, 3 Nov 2022 09:47:51 -0700 Subject: [PATCH 04/34] added a column is_complete to the model that defaults to false. get all with one entry works now --- app/models/task.py | 18 ++++++++++++++++-- app/routes.py | 6 ++++++ tests/test_wave_01.py | 2 +- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/app/models/task.py b/app/models/task.py index dbaa25411..ee0cda182 100644 --- a/app/models/task.py +++ b/app/models/task.py @@ -1,4 +1,5 @@ from app import db +import os class Task(db.Model): @@ -6,14 +7,27 @@ class Task(db.Model): title = db.Column(db.String) description = db.Column(db.String) completed_at = db.Column(db.DateTime, default = None) + #we want to make a column that is aboolean, + #and define that column to be True when there is a time stamp + #and False when there is not a time stamp for completed. + #for now I could make it be false no matter what ?? + is_complete = db.Column(db.Boolean, default = False) + #old tries: + #is_complete = db.column_property(os.path.exists(self.completed_at)) + + # if completed_at == None: + # is_complete = False + # else: + # is_complete = True def make_dict(self): """given a task, return a dictionary with all the attibutes of that task.""" task_dict = { - "task_id": self.task_id, + "id": self.task_id, "title": self.title, "description": self.description, - "completed_at": self.completed_at + #I'm putting 'is_complete' to match test wave 1 + "is_complete": self.is_complete } return task_dict \ No newline at end of file diff --git a/app/routes.py b/app/routes.py index 18365109a..f52b6a783 100644 --- a/app/routes.py +++ b/app/routes.py @@ -13,4 +13,10 @@ def get_all_tasks(): response.append(task_dict) return jsonify(response), 200 +""" +@task_bp.route("", methods = ["GET"]) +def get_one_task(): + task + """ + diff --git a/tests/test_wave_01.py b/tests/test_wave_01.py index f18e1c566..040ef81c3 100644 --- a/tests/test_wave_01.py +++ b/tests/test_wave_01.py @@ -13,7 +13,7 @@ def test_get_tasks_no_saved_tasks(client): assert response_body == [] -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_get_tasks_one_saved_tasks(client, one_task): # Act response = client.get("/tasks") From b98d68a8464aa28dec66593b62aaf1537a1fa5c0 Mon Sep 17 00:00:00 2001 From: LP Date: Thu, 3 Nov 2022 10:00:46 -0700 Subject: [PATCH 05/34] added get route to get one task with a given id --- app/routes.py | 26 ++++++++++++++----- migrations/versions/5f789784c2c0_.py | 28 ++++++++++++++++++++ migrations/versions/ff6611d1de75_.py | 39 ++++++++++++++++++++++++++++ tests/test_wave_01.py | 2 +- 4 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 migrations/versions/5f789784c2c0_.py create mode 100644 migrations/versions/ff6611d1de75_.py diff --git a/app/routes.py b/app/routes.py index f52b6a783..54f6bb216 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,4 +1,4 @@ -from flask import Blueprint, jsonify +from flask import Blueprint, jsonify, abort, make_response from app.models.task import Task #make a blueprint @@ -13,10 +13,24 @@ def get_all_tasks(): response.append(task_dict) return jsonify(response), 200 -""" -@task_bp.route("", methods = ["GET"]) -def get_one_task(): - task - """ + +@task_bp.route("/", methods = ["GET"]) +def get_one_task(task_id): + task = validate_task(task_id) + task_dict = task.make_dict() + return make_response({"task": task_dict}, 200) + + +def validate_task(task_id): + try: + task_id = int(task_id) + except ValueError: + response_str = f"Task {task_id} must be an integer" + abort(make_response({"message": response_str}, 400)) + task = Task.query.get(task_id) + if not task: + response_str = f"Task {task_id} not found" + abort(make_response({"message": response_str}, 404)) + return task diff --git a/migrations/versions/5f789784c2c0_.py b/migrations/versions/5f789784c2c0_.py new file mode 100644 index 000000000..916db40a1 --- /dev/null +++ b/migrations/versions/5f789784c2c0_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: 5f789784c2c0 +Revises: ff6611d1de75 +Create Date: 2022-11-03 09:46:40.697550 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '5f789784c2c0' +down_revision = 'ff6611d1de75' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('task', sa.Column('is_complete', sa.Boolean(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('task', 'is_complete') + # ### end Alembic commands ### diff --git a/migrations/versions/ff6611d1de75_.py b/migrations/versions/ff6611d1de75_.py new file mode 100644 index 000000000..23d455275 --- /dev/null +++ b/migrations/versions/ff6611d1de75_.py @@ -0,0 +1,39 @@ +"""empty message + +Revision ID: ff6611d1de75 +Revises: +Create Date: 2022-11-02 14:01:38.832132 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'ff6611d1de75' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('goal', + sa.Column('goal_id', sa.Integer(), nullable=False), + sa.PrimaryKeyConstraint('goal_id') + ) + op.create_table('task', + sa.Column('task_id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('title', sa.String(), nullable=True), + sa.Column('description', sa.String(), nullable=True), + sa.Column('completed_at', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('task_id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('task') + op.drop_table('goal') + # ### end Alembic commands ### diff --git a/tests/test_wave_01.py b/tests/test_wave_01.py index 040ef81c3..410400f9d 100644 --- a/tests/test_wave_01.py +++ b/tests/test_wave_01.py @@ -32,7 +32,7 @@ def test_get_tasks_one_saved_tasks(client, one_task): ] -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_get_task(client, one_task): # Act response = client.get("/tasks/1") From 688dd46b39117298223f0597a3763b67144cbd73 Mon Sep 17 00:00:00 2001 From: LP Date: Thu, 3 Nov 2022 10:04:42 -0700 Subject: [PATCH 06/34] wrote a test for 404 not found for get request with id of 1 and empty db --- tests/test_wave_01.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/test_wave_01.py b/tests/test_wave_01.py index 410400f9d..2e5a85fa9 100644 --- a/tests/test_wave_01.py +++ b/tests/test_wave_01.py @@ -51,7 +51,7 @@ def test_get_task(client, one_task): } -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_get_task_not_found(client): # Act response = client.get("/tasks/1") @@ -60,10 +60,9 @@ def test_get_task_not_found(client): # Assert assert response.status_code == 404 - raise Exception("Complete test with assertion about response body") - # ***************************************************************** - # **Complete test with assertion about response body*************** - # ***************************************************************** + assert response_body == { + "message": "Task 1 not found" + } @pytest.mark.skip(reason="No way to test this feature yet") From cce934204b83ba7b4f7576b2b5539096bdbb2220 Mon Sep 17 00:00:00 2001 From: LP Date: Thu, 3 Nov 2022 11:29:53 -0700 Subject: [PATCH 07/34] tried to add a post method but it broke everything --- app/routes.py | 19 +++++++++++++++++++ tests/test_wave_01.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/app/routes.py b/app/routes.py index 54f6bb216..6782c3184 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,5 +1,6 @@ from flask import Blueprint, jsonify, abort, make_response from app.models.task import Task +from app import db #make a blueprint task_bp = Blueprint("task_bp", __name__, url_prefix = "/tasks") @@ -20,6 +21,17 @@ def get_one_task(task_id): task_dict = task.make_dict() return make_response({"task": task_dict}, 200) +@task_bp.route("", methods = "POST") +def post_new_task(): + request_body = request.get_json() + new_task = make_new_task(request_body) + db.session.add(new_task) + db.session.commit() + task_dict = new_task.make_dict() + response = {"task": task_dict} + return make_response(response, 201) + + def validate_task(task_id): try: @@ -33,4 +45,11 @@ def validate_task(task_id): abort(make_response({"message": response_str}, 404)) return task +def make_new_task(task_dict): + new_task = Task( + title = task_dict["title"], + description = task_dict["description"], + is_complete = task_dict["is_complete"] + ) + return new_task diff --git a/tests/test_wave_01.py b/tests/test_wave_01.py index 2e5a85fa9..fd1d0bc26 100644 --- a/tests/test_wave_01.py +++ b/tests/test_wave_01.py @@ -65,7 +65,7 @@ def test_get_task_not_found(client): } -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_create_task(client): # Act response = client.post("/tasks", json={ From 1c134e7b6d2fc6ef8755584ea0c6d9b8344816f0 Mon Sep 17 00:00:00 2001 From: LP Date: Thu, 3 Nov 2022 11:32:54 -0700 Subject: [PATCH 08/34] making changes to get post working --- app/routes.py | 12 ++++++++++++ tests/test_wave_01.py | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/routes.py b/app/routes.py index 54f6bb216..e86870b92 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,9 +1,21 @@ from flask import Blueprint, jsonify, abort, make_response from app.models.task import Task +from app import db #make a blueprint task_bp = Blueprint("task_bp", __name__, url_prefix = "/tasks") +@task_bp.route("", methods = ["POST"]) +def post_new_task(): + request_body = request.get_json() + new_task = make_new_task(request_body) + db.session.add(new_task) + db.session.commit() + task_dict = new_task.make_dict() + response = {"task": task_dict} + return make_response(response, 201) + + @task_bp.route("", methods = ["GET"]) def get_all_tasks(): tasks = Task.query.all() diff --git a/tests/test_wave_01.py b/tests/test_wave_01.py index 2e5a85fa9..fd1d0bc26 100644 --- a/tests/test_wave_01.py +++ b/tests/test_wave_01.py @@ -65,7 +65,7 @@ def test_get_task_not_found(client): } -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_create_task(client): # Act response = client.post("/tasks", json={ From 4d0b632d3e78693233263707d4121ffdc486d6c0 Mon Sep 17 00:00:00 2001 From: LP Date: Thu, 3 Nov 2022 11:39:55 -0700 Subject: [PATCH 09/34] post method is now working --- app/routes.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/app/routes.py b/app/routes.py index 6782c3184..b70289379 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,10 +1,15 @@ -from flask import Blueprint, jsonify, abort, make_response +from flask import Blueprint, jsonify, abort, make_response, request from app.models.task import Task from app import db #make a blueprint task_bp = Blueprint("task_bp", __name__, url_prefix = "/tasks") +#constants: (refactor to make this from the database later.) +COL_NAMES = ["title", "description", "is_complete"] #later, add completed ad +COL_DEFAULTS = [None, "", False] +COL_NAME_DEFAULT_DICT = dict(zip(COL_NAMES, COL_DEFAULTS)) + @task_bp.route("", methods = ["GET"]) def get_all_tasks(): tasks = Task.query.all() @@ -21,10 +26,11 @@ def get_one_task(task_id): task_dict = task.make_dict() return make_response({"task": task_dict}, 200) -@task_bp.route("", methods = "POST") +@task_bp.route("", methods = ["POST"]) def post_new_task(): request_body = request.get_json() - new_task = make_new_task(request_body) + dict_of_field_vals = fill_empties_with_defaults(request_body) + new_task = make_new_task(dict_of_field_vals) db.session.add(new_task) db.session.commit() task_dict = new_task.make_dict() @@ -53,3 +59,15 @@ def make_new_task(task_dict): ) return new_task +def fill_empties_with_defaults(request_body): + """Go through entered fields. + If it has an entry, use that, if not, use the default.""" + task_dict = {} + for field, default in COL_NAME_DEFAULT_DICT.items(): + + if field not in request_body: + task_dict[field] = default + else: + task_dict[field] = request_body[field] + + return task_dict From b3a0ae6a1e5099131b7ab865a8b309c56aa79dc3 Mon Sep 17 00:00:00 2001 From: LP Date: Thu, 3 Nov 2022 12:05:56 -0700 Subject: [PATCH 10/34] implemented put successfully --- app/routes.py | 21 ++++++++++++++++++++- tests/test_wave_01.py | 2 +- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/app/routes.py b/app/routes.py index b70289379..74a50b48f 100644 --- a/app/routes.py +++ b/app/routes.py @@ -37,7 +37,15 @@ def post_new_task(): response = {"task": task_dict} return make_response(response, 201) - +@task_bp.route("/", methods = ["PUT", "PATCH"]) +def update_task(task_id): + task = validate_task(task_id) + request_body = request.get_json() + task = update_given_values(task, request_body) + db.session.commit() + response = {"task": task.make_dict()} #refactor this line and line 37 above to helper function? or method on class? + return make_response(response, 200) + def validate_task(task_id): try: @@ -71,3 +79,14 @@ def fill_empties_with_defaults(request_body): task_dict[field] = request_body[field] return task_dict + +#can I make this a method for Tasks? +def update_given_values(task, request_body): + if "title" in request_body: + task.title = request_body["title"] + if "description" in request_body: + task.description = request_body["description"] + if "is_complete" in request_body: + task.is_complete = request_body["is_complete"] + #can add completed_at when you put that in. + return task diff --git a/tests/test_wave_01.py b/tests/test_wave_01.py index fd1d0bc26..1a0e5e555 100644 --- a/tests/test_wave_01.py +++ b/tests/test_wave_01.py @@ -92,7 +92,7 @@ def test_create_task(client): assert new_task.completed_at == None -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_update_task(client, one_task): # Act response = client.put("/tasks/1", json={ From 715c4695252eb29a048c55a1908b1d14af001968 Mon Sep 17 00:00:00 2001 From: LP Date: Thu, 3 Nov 2022 14:33:31 -0700 Subject: [PATCH 11/34] added assert statement to test_update_task_not_found and checked that it worked. --- tests/test_wave_01.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/test_wave_01.py b/tests/test_wave_01.py index 1a0e5e555..8b9ee8700 100644 --- a/tests/test_wave_01.py +++ b/tests/test_wave_01.py @@ -118,7 +118,7 @@ def test_update_task(client, one_task): assert task.completed_at == None -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_update_task_not_found(client): # Act response = client.put("/tasks/1", json={ @@ -129,12 +129,8 @@ def test_update_task_not_found(client): # Assert assert response.status_code == 404 - - raise Exception("Complete test with assertion about response body") - # ***************************************************************** - # **Complete test with assertion about response body*************** - # ***************************************************************** - + assert response_body == {"message": "Task 1 not found"} + @pytest.mark.skip(reason="No way to test this feature yet") def test_delete_task(client, one_task): From 39da99c47679a9ace9b7cdfb8ab6f7925ab24aed Mon Sep 17 00:00:00 2001 From: LP Date: Thu, 3 Nov 2022 14:57:43 -0700 Subject: [PATCH 12/34] added delete route successfully --- app/routes.py | 7 +++++++ tests/test_wave_01.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/routes.py b/app/routes.py index 74a50b48f..8d28574b3 100644 --- a/app/routes.py +++ b/app/routes.py @@ -46,6 +46,13 @@ def update_task(task_id): response = {"task": task.make_dict()} #refactor this line and line 37 above to helper function? or method on class? return make_response(response, 200) +@task_bp.route("/", methods = ["DELETE"]) +def delete_task(task_id): + task = validate_task(task_id) + db.session.delete(task) + db.session.commit() + response_body = {"details": f'Task {task_id} "{task.title}" successfully deleted'} + return make_response(response_body, 200) def validate_task(task_id): try: diff --git a/tests/test_wave_01.py b/tests/test_wave_01.py index 8b9ee8700..e743bab12 100644 --- a/tests/test_wave_01.py +++ b/tests/test_wave_01.py @@ -132,7 +132,7 @@ def test_update_task_not_found(client): assert response_body == {"message": "Task 1 not found"} -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_delete_task(client, one_task): # Act response = client.delete("/tasks/1") From 7766313d717946b3c09729c5c4d63b3c95d2df36 Mon Sep 17 00:00:00 2001 From: LP Date: Fri, 4 Nov 2022 16:09:32 -0700 Subject: [PATCH 13/34] if title or description missing from post, no entry posted. Deleted 'fill_empties_with_defaults'. made is_complete default to False if nothing is entered for 'completed_at' --- app/models/task.py | 10 ++++++++- app/routes.py | 47 ++++++++++++++++++++++--------------------- tests/test_wave_01.py | 11 ++++------ 3 files changed, 37 insertions(+), 31 deletions(-) diff --git a/app/models/task.py b/app/models/task.py index ee0cda182..e4e582130 100644 --- a/app/models/task.py +++ b/app/models/task.py @@ -30,4 +30,12 @@ def make_dict(self): #I'm putting 'is_complete' to match test wave 1 "is_complete": self.is_complete } - return task_dict \ No newline at end of file + return task_dict + + @classmethod + def from_dict(cls, data_dict): + new_object = cls( + title = data_dict["title"], + description = data_dict["description"], + is_complete = data_dict["is_complete"]) + return new_object diff --git a/app/routes.py b/app/routes.py index 8d28574b3..6351b7b92 100644 --- a/app/routes.py +++ b/app/routes.py @@ -29,8 +29,14 @@ def get_one_task(task_id): @task_bp.route("", methods = ["POST"]) def post_new_task(): request_body = request.get_json() - dict_of_field_vals = fill_empties_with_defaults(request_body) - new_task = make_new_task(dict_of_field_vals) + #dict_of_field_vals = fill_empties_with_defaults(request_body) + if "title" not in request_body or "description" not in request_body: + response_str = "Invalid data" + abort(make_response({"details":response_str}, 400)) + if "completed_at" not in request_body: + request_body["is_complete"] = False + + new_task = Task.from_dict(request_body) db.session.add(new_task) db.session.commit() task_dict = new_task.make_dict() @@ -66,27 +72,6 @@ def validate_task(task_id): abort(make_response({"message": response_str}, 404)) return task -def make_new_task(task_dict): - new_task = Task( - title = task_dict["title"], - description = task_dict["description"], - is_complete = task_dict["is_complete"] - ) - return new_task - -def fill_empties_with_defaults(request_body): - """Go through entered fields. - If it has an entry, use that, if not, use the default.""" - task_dict = {} - for field, default in COL_NAME_DEFAULT_DICT.items(): - - if field not in request_body: - task_dict[field] = default - else: - task_dict[field] = request_body[field] - - return task_dict - #can I make this a method for Tasks? def update_given_values(task, request_body): if "title" in request_body: @@ -97,3 +82,19 @@ def update_given_values(task, request_body): task.is_complete = request_body["is_complete"] #can add completed_at when you put that in. return task + +# I used this in my journal.py. This isn't want the tests are asking for, so I'll delete it. +# def fill_empties_with_defaults(request_body): +# """Go through entered fields. +# If it has an entry, use that, if not, use the default.""" +# task_dict = {} +# for field, default in COL_NAME_DEFAULT_DICT.items(): + +# if field not in request_body: +# task_dict[field] = default +# else: +# task_dict[field] = request_body[field] + +# return task_dict + + diff --git a/tests/test_wave_01.py b/tests/test_wave_01.py index e743bab12..a3ae932f5 100644 --- a/tests/test_wave_01.py +++ b/tests/test_wave_01.py @@ -147,7 +147,7 @@ def test_delete_task(client, one_task): assert Task.query.get(1) == None -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_delete_task_not_found(client): # Act response = client.delete("/tasks/1") @@ -156,15 +156,12 @@ def test_delete_task_not_found(client): # Assert assert response.status_code == 404 - raise Exception("Complete test with assertion about response body") - # ***************************************************************** - # **Complete test with assertion about response body*************** - # ***************************************************************** - + assert response_body == {"message": "Task 1 not found"} + assert Task.query.all() == [] -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_create_task_must_contain_title(client): # Act response = client.post("/tasks", json={ From 5635280be020193db78a3403d9fedbc56ee8a524 Mon Sep 17 00:00:00 2001 From: LP Date: Fri, 4 Nov 2022 16:10:16 -0700 Subject: [PATCH 14/34] all tests of wave1 passed: CRUD endpoints set up --- tests/test_wave_01.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_wave_01.py b/tests/test_wave_01.py index a3ae932f5..e46a1bbc6 100644 --- a/tests/test_wave_01.py +++ b/tests/test_wave_01.py @@ -178,7 +178,7 @@ def test_create_task_must_contain_title(client): assert Task.query.all() == [] -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_create_task_must_contain_description(client): # Act response = client.post("/tasks", json={ From a9fdc69e4166c0890b609f4c00101ba5873f51f8 Mon Sep 17 00:00:00 2001 From: LP Date: Mon, 7 Nov 2022 13:09:16 -0800 Subject: [PATCH 15/34] refactored completed_at to accept info expected from wave 1. all CRUD endpoints working --- app/models/task.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/models/task.py b/app/models/task.py index e4e582130..db2efa4c7 100644 --- a/app/models/task.py +++ b/app/models/task.py @@ -34,8 +34,18 @@ def make_dict(self): @classmethod def from_dict(cls, data_dict): + #make the following a helper function: + if "completed_at" in data_dict: + completed_at = data_dict["completed_at"] + if not completed_at: + is_complete = False + else: + is_complete = True + else: is_complete = False + #end helper function. + new_object = cls( title = data_dict["title"], description = data_dict["description"], - is_complete = data_dict["is_complete"]) + is_complete = is_complete) return new_object From 4d3b714c93e28458f5e5e09b0fe415c0c20d01e4 Mon Sep 17 00:00:00 2001 From: LP Date: Tue, 8 Nov 2022 09:10:27 -0800 Subject: [PATCH 16/34] implemented sort query correctly --- app/routes.py | 14 +++++++++++++- tests/test_wave_02.py | 4 ++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/app/routes.py b/app/routes.py index 6351b7b92..ff650c551 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,6 +1,9 @@ from flask import Blueprint, jsonify, abort, make_response, request from app.models.task import Task from app import db +from sqlalchemy import desc, asc + + #make a blueprint task_bp = Blueprint("task_bp", __name__, url_prefix = "/tasks") @@ -12,7 +15,16 @@ @task_bp.route("", methods = ["GET"]) def get_all_tasks(): - tasks = Task.query.all() + #refactor to helper function + #this isn't working. not sure why. + sort_order = request.args.get("sort") + if sort_order == "desc": + tasks = Task.query.order_by(Task.title.desc()).all() #Task.title is just a string?? + elif sort_order == "asc": + tasks = Task.query.order_by(Task.title.asc()).all() #look up doc for asc. + else: + tasks = Task.query.all() + #refactor to helper function end here? response = [] for task in tasks: task_dict = task.make_dict() diff --git a/tests/test_wave_02.py b/tests/test_wave_02.py index a087e0909..c9a76e6b1 100644 --- a/tests/test_wave_02.py +++ b/tests/test_wave_02.py @@ -1,7 +1,7 @@ import pytest -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_get_tasks_sorted_asc(client, three_tasks): # Act response = client.get("/tasks?sort=asc") @@ -29,7 +29,7 @@ def test_get_tasks_sorted_asc(client, three_tasks): ] -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_get_tasks_sorted_desc(client, three_tasks): # Act response = client.get("/tasks?sort=desc") From a7ad15b7bd542afcd4ceb17b6c1b6dcbd758386e Mon Sep 17 00:00:00 2001 From: LP Date: Sun, 13 Nov 2022 16:37:51 -0800 Subject: [PATCH 17/34] endpoint with mark complete marks a given task complete --- app/routes.py | 15 +++++++++++++++ tests/test_wave_03.py | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/routes.py b/app/routes.py index ff650c551..5a643993c 100644 --- a/app/routes.py +++ b/app/routes.py @@ -2,6 +2,7 @@ from app.models.task import Task from app import db from sqlalchemy import desc, asc +from datetime import date @@ -64,6 +65,20 @@ def update_task(task_id): response = {"task": task.make_dict()} #refactor this line and line 37 above to helper function? or method on class? return make_response(response, 200) +#can the following be combined with the route above? +@task_bp.route("//", methods = ["PUT", "PATCH"]) +def update_task_completion(task_id, complete_tag): + task = validate_task(task_id) + if complete_tag == "mark_complete": + #put in a line here about making the timestamp for completed_at + task.completed_at = date.today().strftime("%B %d, %Y") + task.is_complete = True + # #elif complete_tag == "mark_incomplete": + db.session.commit() + response = {"task": task.make_dict()} + return make_response(response, 200) + + @task_bp.route("/", methods = ["DELETE"]) def delete_task(task_id): task = validate_task(task_id) diff --git a/tests/test_wave_03.py b/tests/test_wave_03.py index 32d379822..6f4ab0f6d 100644 --- a/tests/test_wave_03.py +++ b/tests/test_wave_03.py @@ -5,7 +5,7 @@ import pytest -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_mark_complete_on_incomplete_task(client, one_task): # Arrange """ From 950f7c540dc2c3be92929e1fe8ec9f6745fa2632 Mon Sep 17 00:00:00 2001 From: LP Date: Sun, 13 Nov 2022 16:39:31 -0800 Subject: [PATCH 18/34] mark_incomplete endpoint marks a task incomplete --- app/routes.py | 6 ++++-- tests/test_wave_03.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/routes.py b/app/routes.py index 5a643993c..22dbc6530 100644 --- a/app/routes.py +++ b/app/routes.py @@ -72,8 +72,10 @@ def update_task_completion(task_id, complete_tag): if complete_tag == "mark_complete": #put in a line here about making the timestamp for completed_at task.completed_at = date.today().strftime("%B %d, %Y") - task.is_complete = True - # #elif complete_tag == "mark_incomplete": + task.is_complete = True #not sure if this line is redundant + elif complete_tag == "mark_incomplete": + task.completed_at = None + task.is_complete = False #not sure if this line is redundant db.session.commit() response = {"task": task.make_dict()} return make_response(response, 200) diff --git a/tests/test_wave_03.py b/tests/test_wave_03.py index 6f4ab0f6d..b47b2a787 100644 --- a/tests/test_wave_03.py +++ b/tests/test_wave_03.py @@ -42,7 +42,7 @@ def test_mark_complete_on_incomplete_task(client, one_task): assert Task.query.get(1).completed_at -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_mark_incomplete_on_complete_task(client, completed_task): # Act response = client.patch("/tasks/1/mark_incomplete") From 6709dcbb8e5da78e2799c75e6fa360d1dde53145 Mon Sep 17 00:00:00 2001 From: LP Date: Sun, 13 Nov 2022 16:42:46 -0800 Subject: [PATCH 19/34] tests checking for task not found 404 respond correctly. --- tests/test_wave_03.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/tests/test_wave_03.py b/tests/test_wave_03.py index b47b2a787..62bb3c6da 100644 --- a/tests/test_wave_03.py +++ b/tests/test_wave_03.py @@ -62,7 +62,7 @@ def test_mark_incomplete_on_complete_task(client, completed_task): assert Task.query.get(1).completed_at == None -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_mark_complete_on_completed_task(client, completed_task): # Arrange """ @@ -99,7 +99,7 @@ def test_mark_complete_on_completed_task(client, completed_task): assert Task.query.get(1).completed_at -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_mark_incomplete_on_incomplete_task(client, one_task): # Act response = client.patch("/tasks/1/mark_incomplete") @@ -119,7 +119,7 @@ def test_mark_incomplete_on_incomplete_task(client, one_task): assert Task.query.get(1).completed_at == None -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_mark_complete_missing_task(client): # Act response = client.patch("/tasks/1/mark_complete") @@ -128,13 +128,11 @@ def test_mark_complete_missing_task(client): # Assert assert response.status_code == 404 - raise Exception("Complete test with assertion about response body") - # ***************************************************************** - # **Complete test with assertion about response body*************** - # ***************************************************************** - + assert response_body == { + "message": "Task 1 not found" + } -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_mark_incomplete_missing_task(client): # Act response = client.patch("/tasks/1/mark_incomplete") @@ -143,7 +141,6 @@ def test_mark_incomplete_missing_task(client): # Assert assert response.status_code == 404 - raise Exception("Complete test with assertion about response body") - # ***************************************************************** - # **Complete test with assertion about response body*************** - # ***************************************************************** + assert response_body == { + "message": "Task 1 not found" + } \ No newline at end of file From b415889493fa9f3be53fac8bc7bad6a1d8840d7e Mon Sep 17 00:00:00 2001 From: LP Date: Mon, 14 Nov 2022 14:36:18 -0800 Subject: [PATCH 20/34] trying to get integrating with slacking working. no success yet, but I want to save my progress just in case I'm on track. --- app/__init__.py | 2 ++ app/routes.py | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/__init__.py b/app/__init__.py index 14b242cca..c1e642151 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -14,6 +14,8 @@ def create_app(test_config=None): app = Flask(__name__) app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False + #app.config["SLACK_API_KEY"] = os.environ.get("SLACK_API_KEY") + if test_config is None: app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get( "SQLALCHEMY_DATABASE_URI") diff --git a/app/routes.py b/app/routes.py index 22dbc6530..90cdb4dbf 100644 --- a/app/routes.py +++ b/app/routes.py @@ -3,6 +3,8 @@ from app import db from sqlalchemy import desc, asc from datetime import date +import os +import requests @@ -70,9 +72,20 @@ def update_task(task_id): def update_task_completion(task_id, complete_tag): task = validate_task(task_id) if complete_tag == "mark_complete": - #put in a line here about making the timestamp for completed_at task.completed_at = date.today().strftime("%B %d, %Y") task.is_complete = True #not sure if this line is redundant + #post a message to slack to say the task is complete. + #----------Make the following into a helper function later-----# + path = "https://slack.com/api/chat.postMessage" + query_params = { + "token" : os.environ.get("SLACK_API_KEY"), + "channel" : "task-notifications" + } + + response = requests.post(path, params = query_params) + #return make_response(response, 200) + + #----------end helper function--------------# elif complete_tag == "mark_incomplete": task.completed_at = None task.is_complete = False #not sure if this line is redundant From ab234b67feccde2e5c77c4eac470e0acece68547 Mon Sep 17 00:00:00 2001 From: LP Date: Tue, 15 Nov 2022 11:01:19 -0800 Subject: [PATCH 21/34] updated put and patch route so that the slack bot posts a message to slack when a task is complete. all tests are still working. --- app/routes.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/routes.py b/app/routes.py index 90cdb4dbf..d6e4515b4 100644 --- a/app/routes.py +++ b/app/routes.py @@ -78,11 +78,15 @@ def update_task_completion(task_id, complete_tag): #----------Make the following into a helper function later-----# path = "https://slack.com/api/chat.postMessage" query_params = { - "token" : os.environ.get("SLACK_API_KEY"), - "channel" : "task-notifications" + #"token" : os.environ.get("SLACK_API_KEY"), + "channel" : "task-notifications", + "text": "Someone just completed the task " + task.title + } + headers = { + "Authorization" : "Bearer " + os.environ.get("SLACK_API_KEY") } - response = requests.post(path, params = query_params) + response = requests.post(path, params = query_params, headers = headers) #return make_response(response, 200) #----------end helper function--------------# From 98cfdaade56d9725296ac193c3ac8ad8fda9a999 Mon Sep 17 00:00:00 2001 From: LP Date: Tue, 15 Nov 2022 13:24:45 -0800 Subject: [PATCH 22/34] beginning to add goal_bp. made a blueprint and migrate to the db --- app/__init__.py | 4 +++- app/models/goal.py | 14 ++++++++++++++ app/models/task.py | 12 ------------ app/routes/goal.py | 6 ++++++ app/{routes.py => routes/task.py} | 7 ++----- migrations/versions/cab89553b1f6_.py | 28 ++++++++++++++++++++++++++++ 6 files changed, 53 insertions(+), 18 deletions(-) create mode 100644 app/routes/goal.py rename app/{routes.py => routes/task.py} (98%) create mode 100644 migrations/versions/cab89553b1f6_.py diff --git a/app/__init__.py b/app/__init__.py index c1e642151..9be2c8014 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -32,7 +32,9 @@ def create_app(test_config=None): migrate.init_app(app, db) # Register Blueprints here - from app.routes import task_bp + from app.routes.task import task_bp app.register_blueprint(task_bp) + from app.routes.goal import goal_bp + app.register_blueprint(goal_bp) return app diff --git a/app/models/goal.py b/app/models/goal.py index b0ed11dd8..c179df939 100644 --- a/app/models/goal.py +++ b/app/models/goal.py @@ -3,3 +3,17 @@ class Goal(db.Model): goal_id = db.Column(db.Integer, primary_key=True) + title = db.Column(db.String) + +def make_dict(self): + """return a ditionary with all attributes of a goal""" + goal_dict = { + "id": self.goal_id, + "title": self.title + } + +@classmethod +def from_dict(cls, data_dict): + new_object = cls( + title = data_dict["title"]) + return new_object diff --git a/app/models/task.py b/app/models/task.py index db2efa4c7..fd26c4b39 100644 --- a/app/models/task.py +++ b/app/models/task.py @@ -7,18 +7,7 @@ class Task(db.Model): title = db.Column(db.String) description = db.Column(db.String) completed_at = db.Column(db.DateTime, default = None) - #we want to make a column that is aboolean, - #and define that column to be True when there is a time stamp - #and False when there is not a time stamp for completed. - #for now I could make it be false no matter what ?? is_complete = db.Column(db.Boolean, default = False) - #old tries: - #is_complete = db.column_property(os.path.exists(self.completed_at)) - - # if completed_at == None: - # is_complete = False - # else: - # is_complete = True def make_dict(self): """given a task, return a dictionary @@ -27,7 +16,6 @@ def make_dict(self): "id": self.task_id, "title": self.title, "description": self.description, - #I'm putting 'is_complete' to match test wave 1 "is_complete": self.is_complete } return task_dict diff --git a/app/routes/goal.py b/app/routes/goal.py new file mode 100644 index 000000000..abd0d9c7a --- /dev/null +++ b/app/routes/goal.py @@ -0,0 +1,6 @@ +from flask import Blueprint, jsonify, abort, make_response, request +from app.models.task import Task +from app import db + +goal_bp = Blueprint("goal_bp", __name__, url_prefix = "/goals") + diff --git a/app/routes.py b/app/routes/task.py similarity index 98% rename from app/routes.py rename to app/routes/task.py index d6e4515b4..928c81389 100644 --- a/app/routes.py +++ b/app/routes/task.py @@ -6,8 +6,6 @@ import os import requests - - #make a blueprint task_bp = Blueprint("task_bp", __name__, url_prefix = "/tasks") @@ -74,11 +72,11 @@ def update_task_completion(task_id, complete_tag): if complete_tag == "mark_complete": task.completed_at = date.today().strftime("%B %d, %Y") task.is_complete = True #not sure if this line is redundant + #post a message to slack to say the task is complete. #----------Make the following into a helper function later-----# path = "https://slack.com/api/chat.postMessage" query_params = { - #"token" : os.environ.get("SLACK_API_KEY"), "channel" : "task-notifications", "text": "Someone just completed the task " + task.title } @@ -87,9 +85,8 @@ def update_task_completion(task_id, complete_tag): } response = requests.post(path, params = query_params, headers = headers) - #return make_response(response, 200) - #----------end helper function--------------# + elif complete_tag == "mark_incomplete": task.completed_at = None task.is_complete = False #not sure if this line is redundant diff --git a/migrations/versions/cab89553b1f6_.py b/migrations/versions/cab89553b1f6_.py new file mode 100644 index 000000000..eddacbce0 --- /dev/null +++ b/migrations/versions/cab89553b1f6_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: cab89553b1f6 +Revises: 5f789784c2c0 +Create Date: 2022-11-15 13:23:02.551355 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'cab89553b1f6' +down_revision = '5f789784c2c0' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('goal', sa.Column('title', sa.String(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('goal', 'title') + # ### end Alembic commands ### From 2aa522235b7147916959887da9a08c9606a9fee5 Mon Sep 17 00:00:00 2001 From: LP Date: Wed, 16 Nov 2022 10:00:59 -0800 Subject: [PATCH 23/34] post route successfully added for goal. exclamationpoint --- app/models/goal.py | 23 ++++++++++++----------- app/routes/goal.py | 15 ++++++++++++++- app/routes/task.py | 3 +-- tests/test_wave_05.py | 2 +- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/app/models/goal.py b/app/models/goal.py index c179df939..ae7a56d94 100644 --- a/app/models/goal.py +++ b/app/models/goal.py @@ -5,15 +5,16 @@ class Goal(db.Model): goal_id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String) -def make_dict(self): - """return a ditionary with all attributes of a goal""" - goal_dict = { - "id": self.goal_id, - "title": self.title - } + def make_dict(self): + """return a ditionary with all attributes of a goal""" + goal_dict = { + "id": self.goal_id, + "title": self.title + } + return goal_dict -@classmethod -def from_dict(cls, data_dict): - new_object = cls( - title = data_dict["title"]) - return new_object + @classmethod + def from_dict(cls, data_dict): + new_object = cls( + title = data_dict["title"]) + return new_object diff --git a/app/routes/goal.py b/app/routes/goal.py index abd0d9c7a..66034dc88 100644 --- a/app/routes/goal.py +++ b/app/routes/goal.py @@ -1,6 +1,19 @@ from flask import Blueprint, jsonify, abort, make_response, request -from app.models.task import Task +from app.models.goal import Goal from app import db goal_bp = Blueprint("goal_bp", __name__, url_prefix = "/goals") +@goal_bp.route("", methods = ["POST"]) +def post_new_goal(): + request_body = request.get_json() + if "title" not in request_body: + response_str = "Invalid data" + abort(make_response({"details":response_str}, 400)) + + new_goal = Goal.from_dict(request_body) + db.session.add(new_goal) + db.session.commit() + goal_dict = new_goal.make_dict() + response = {"goal": goal_dict} + return make_response(response, 201) diff --git a/app/routes/task.py b/app/routes/task.py index 928c81389..8284b5f28 100644 --- a/app/routes/task.py +++ b/app/routes/task.py @@ -17,7 +17,6 @@ @task_bp.route("", methods = ["GET"]) def get_all_tasks(): #refactor to helper function - #this isn't working. not sure why. sort_order = request.args.get("sort") if sort_order == "desc": tasks = Task.query.order_by(Task.title.desc()).all() #Task.title is just a string?? @@ -25,7 +24,7 @@ def get_all_tasks(): tasks = Task.query.order_by(Task.title.asc()).all() #look up doc for asc. else: tasks = Task.query.all() - #refactor to helper function end here? + #refactor to helper function end here response = [] for task in tasks: task_dict = task.make_dict() diff --git a/tests/test_wave_05.py b/tests/test_wave_05.py index aee7c52a1..1a2c78195 100644 --- a/tests/test_wave_05.py +++ b/tests/test_wave_05.py @@ -61,7 +61,7 @@ def test_get_goal_not_found(client): # ---- Complete Test ---- -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_create_goal(client): # Act response = client.post("/goals", json={ From beada76553d0c932b171fb7016b92f92d86185fc Mon Sep 17 00:00:00 2001 From: LP Date: Wed, 16 Nov 2022 10:06:53 -0800 Subject: [PATCH 24/34] get all goals route is working --- app/routes/goal.py | 9 +++++++++ tests/test_wave_05.py | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/routes/goal.py b/app/routes/goal.py index 66034dc88..335f958d3 100644 --- a/app/routes/goal.py +++ b/app/routes/goal.py @@ -17,3 +17,12 @@ def post_new_goal(): goal_dict = new_goal.make_dict() response = {"goal": goal_dict} return make_response(response, 201) + +@goal_bp.route("", methods = ["GET"]) +def get_all_goals(): + goals = Goal.query.all() + response = [] + for goal in goals: + goal_dict = goal.make_dict() + response.append(goal_dict) + return jsonify(response), 200 diff --git a/tests/test_wave_05.py b/tests/test_wave_05.py index 1a2c78195..c2417570f 100644 --- a/tests/test_wave_05.py +++ b/tests/test_wave_05.py @@ -1,7 +1,7 @@ import pytest -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_get_goals_no_saved_goals(client): # Act response = client.get("/goals") @@ -12,7 +12,7 @@ def test_get_goals_no_saved_goals(client): assert response_body == [] -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_get_goals_one_saved_goal(client, one_goal): # Act response = client.get("/goals") From a74795b05c8a8f95e6cb09d16eacc64ac85b0703 Mon Sep 17 00:00:00 2001 From: LP Date: Wed, 16 Nov 2022 10:14:39 -0800 Subject: [PATCH 25/34] get one task working for one task --- app/routes/goal.py | 19 +++++++++++++++++++ app/routes/task.py | 1 + tests/test_wave_05.py | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/routes/goal.py b/app/routes/goal.py index 335f958d3..8980c80ab 100644 --- a/app/routes/goal.py +++ b/app/routes/goal.py @@ -26,3 +26,22 @@ def get_all_goals(): goal_dict = goal.make_dict() response.append(goal_dict) return jsonify(response), 200 + +@goal_bp.route("/", methods = ["GET"]) +def get_one_goal(goal_id): + goal = validate_goal(goal_id) + goal_dict = goal.make_dict() + return make_response({"goal": goal_dict}, 200) + +#ideally, combine this with validate task, passing in the class as well. +def validate_goal(goal_id): + try: + goal_id = int(goal_id) + except ValueError: + response_str = f"Task {goal_id} must be an integer" + abort(make_response({"message": response_str}, 400)) + goal = Goal.query.get(goal_id) + if not goal: + response_str = f"Task {goal_id} not found" + abort(make_response({"message": response_str}, 404)) + return goal \ No newline at end of file diff --git a/app/routes/task.py b/app/routes/task.py index 8284b5f28..c5d9c512c 100644 --- a/app/routes/task.py +++ b/app/routes/task.py @@ -102,6 +102,7 @@ def delete_task(task_id): response_body = {"details": f'Task {task_id} "{task.title}" successfully deleted'} return make_response(response_body, 200) +#ideally, combine this with validate goal, passing in the class as well. def validate_task(task_id): try: task_id = int(task_id) diff --git a/tests/test_wave_05.py b/tests/test_wave_05.py index c2417570f..f04195348 100644 --- a/tests/test_wave_05.py +++ b/tests/test_wave_05.py @@ -29,7 +29,7 @@ def test_get_goals_one_saved_goal(client, one_goal): ] -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_get_goal(client, one_goal): # Act response = client.get("/goals/1") From efff68e3d441a89937b7bdcb8358fac0869077c4 Mon Sep 17 00:00:00 2001 From: LP Date: Wed, 16 Nov 2022 10:19:36 -0800 Subject: [PATCH 26/34] goal get one goal with no goal returns 404 --- app/routes/goal.py | 4 ++-- tests/test_wave_05.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/routes/goal.py b/app/routes/goal.py index 8980c80ab..44cf45be5 100644 --- a/app/routes/goal.py +++ b/app/routes/goal.py @@ -38,10 +38,10 @@ def validate_goal(goal_id): try: goal_id = int(goal_id) except ValueError: - response_str = f"Task {goal_id} must be an integer" + response_str = f"Goal {goal_id} must be an integer" abort(make_response({"message": response_str}, 400)) goal = Goal.query.get(goal_id) if not goal: - response_str = f"Task {goal_id} not found" + response_str = f"Goal {goal_id} not found" abort(make_response({"message": response_str}, 404)) return goal \ No newline at end of file diff --git a/tests/test_wave_05.py b/tests/test_wave_05.py index f04195348..a7b75b69f 100644 --- a/tests/test_wave_05.py +++ b/tests/test_wave_05.py @@ -46,19 +46,19 @@ def test_get_goal(client, one_goal): } -@pytest.mark.skip(reason="test to be completed by student") +#@pytest.mark.skip(reason="test to be completed by student") def test_get_goal_not_found(client): pass # Act response = client.get("/goals/1") response_body = response.get_json() - raise Exception("Complete test") # Assert - # ---- Complete Test ---- - # assertion 1 goes here - # assertion 2 goes here - # ---- Complete Test ---- + assert response.status_code == 404 + + assert response_body == { + "message": "Goal 1 not found" + } #@pytest.mark.skip(reason="No way to test this feature yet") From eef38e59a5a136632422fc539d4887a675fa461a Mon Sep 17 00:00:00 2001 From: LP Date: Wed, 16 Nov 2022 10:35:48 -0800 Subject: [PATCH 27/34] put routes in place for goal, working for goal not found and goal found --- app/routes/goal.py | 16 +++++++++++++++ tests/test_wave_05.py | 46 +++++++++++++++++++++++++++---------------- 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/app/routes/goal.py b/app/routes/goal.py index 44cf45be5..a5cbf97a7 100644 --- a/app/routes/goal.py +++ b/app/routes/goal.py @@ -33,6 +33,22 @@ def get_one_goal(goal_id): goal_dict = goal.make_dict() return make_response({"goal": goal_dict}, 200) +@goal_bp.route("/", methods = ["PUT", "PATCH"]) +def update_goal(goal_id): + goal = validate_goal(goal_id) + request_body = request.get_json() + #This could be a helper function----# + if "title" in request_body: + goal.title = request_body["title"] + else: + response_str = f"You must include an updated goal title" + abort(make_response({"message": response_str}, 400)) + #end a helper function# + db.session.commit() + response = {"goal": goal.make_dict()} + return make_response(response, 200) + + #ideally, combine this with validate task, passing in the class as well. def validate_goal(goal_id): try: diff --git a/tests/test_wave_05.py b/tests/test_wave_05.py index a7b75b69f..38c08bb1d 100644 --- a/tests/test_wave_05.py +++ b/tests/test_wave_05.py @@ -1,4 +1,5 @@ import pytest +from app.models.goal import Goal #@pytest.mark.skip(reason="No way to test this feature yet") @@ -80,32 +81,43 @@ def test_create_goal(client): } -@pytest.mark.skip(reason="test to be completed by student") +#@pytest.mark.skip(reason="test to be completed by student") def test_update_goal(client, one_goal): - raise Exception("Complete test") + # Act - # ---- Complete Act Here ---- + response = client.put("/goals/1", json={ + "title": "Updated Goal Title" + }) + response_body = response.get_json() + # Assert + assert response.status_code == 200 + assert "goal" in response_body + assert response_body == { + "goal": { + "id": 1, + "title": "Updated Goal Title" + } + } + # Act + goal = Goal.query.get(1) # Assert - # ---- Complete Assertions Here ---- - # assertion 1 goes here - # assertion 2 goes here - # assertion 3 goes here - # ---- Complete Assertions Here ---- + assert goal.title == "Updated Goal Title" -@pytest.mark.skip(reason="test to be completed by student") +#@pytest.mark.skip(reason="test to be completed by student") def test_update_goal_not_found(client): - raise Exception("Complete test") # Act - # ---- Complete Act Here ---- - + response = client.put("/goals/1", json={ + "title": "Updated Goal Title" + }) + response_body = response.get_json() + # Assert - # ---- Complete Assertions Here ---- - # assertion 1 goes here - # assertion 2 goes here - # ---- Complete Assertions Here ---- - + assert response.status_code == 404 + assert response_body == { + "message": "Goal 1 not found" + } @pytest.mark.skip(reason="No way to test this feature yet") def test_delete_goal(client, one_goal): From 4bfb38e4bed09cb6e6aae9b3fccb74631cfdf785 Mon Sep 17 00:00:00 2001 From: LP Date: Wed, 16 Nov 2022 10:49:10 -0800 Subject: [PATCH 28/34] delete one task working --- app/routes/goal.py | 13 ++++++++++++- tests/test_wave_05.py | 9 ++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app/routes/goal.py b/app/routes/goal.py index a5cbf97a7..269f462a3 100644 --- a/app/routes/goal.py +++ b/app/routes/goal.py @@ -48,6 +48,16 @@ def update_goal(goal_id): response = {"goal": goal.make_dict()} return make_response(response, 200) +@goal_bp.route("/", methods = ["DELETE"]) +def delete_goal(goal_id): + goal = validate_goal(goal_id) + db.session.delete(goal) + db.session.commit() + response_str = f'Goal {goal_id} "{goal.title}" successfully deleted' + response_body = {"details": response_str} + return make_response(response_body, 200) + + #ideally, combine this with validate task, passing in the class as well. def validate_goal(goal_id): @@ -60,4 +70,5 @@ def validate_goal(goal_id): if not goal: response_str = f"Goal {goal_id} not found" abort(make_response({"message": response_str}, 404)) - return goal \ No newline at end of file + return goal + diff --git a/tests/test_wave_05.py b/tests/test_wave_05.py index 38c08bb1d..0509682b6 100644 --- a/tests/test_wave_05.py +++ b/tests/test_wave_05.py @@ -119,7 +119,7 @@ def test_update_goal_not_found(client): "message": "Goal 1 not found" } -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_delete_goal(client, one_goal): # Act response = client.delete("/goals/1") @@ -134,12 +134,7 @@ def test_delete_goal(client, one_goal): # Check that the goal was deleted response = client.get("/goals/1") - assert response.status_code == 404 - - raise Exception("Complete test with assertion about response body") - # ***************************************************************** - # **Complete test with assertion about response body*************** - # ***************************************************************** + assert Goal.query.get(1) == None @pytest.mark.skip(reason="test to be completed by student") From c3deccd08c465a4418eb3f7a249ecdacba68e49b Mon Sep 17 00:00:00 2001 From: LP Date: Wed, 16 Nov 2022 10:53:53 -0800 Subject: [PATCH 29/34] all endpoints for Goals have been set up. exclamation point. --- app/routes/goal.py | 4 ++-- tests/test_wave_05.py | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/routes/goal.py b/app/routes/goal.py index 269f462a3..7e802e005 100644 --- a/app/routes/goal.py +++ b/app/routes/goal.py @@ -41,8 +41,8 @@ def update_goal(goal_id): if "title" in request_body: goal.title = request_body["title"] else: - response_str = f"You must include an updated goal title" - abort(make_response({"message": response_str}, 400)) + response_str = f"Invalid data" + abort(make_response({"details": response_str}, 400)) #end a helper function# db.session.commit() response = {"goal": goal.make_dict()} diff --git a/tests/test_wave_05.py b/tests/test_wave_05.py index 0509682b6..11763e9fa 100644 --- a/tests/test_wave_05.py +++ b/tests/test_wave_05.py @@ -137,21 +137,21 @@ def test_delete_goal(client, one_goal): assert Goal.query.get(1) == None -@pytest.mark.skip(reason="test to be completed by student") +#@pytest.mark.skip(reason="test to be completed by student") def test_delete_goal_not_found(client): - raise Exception("Complete test") - # Act - # ---- Complete Act Here ---- + response = client.delete("/goals/1") + response_body = response.get_json() # Assert - # ---- Complete Assertions Here ---- - # assertion 1 goes here - # assertion 2 goes here - # ---- Complete Assertions Here ---- + assert response.status_code == 404 + + assert response_body == {"message": "Goal 1 not found"} + + assert Goal.query.all() == [] -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_create_goal_missing_title(client): # Act response = client.post("/goals", json={}) From 200a9221c60435405eeba96baec25c263da8e669 Mon Sep 17 00:00:00 2001 From: LP Date: Wed, 16 Nov 2022 12:26:15 -0800 Subject: [PATCH 30/34] setting up relationship between task and goal --- app/models/goal.py | 5 ++-- app/models/task.py | 2 ++ migrations/versions/7fc34fd31471_.py | 34 ++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 migrations/versions/7fc34fd31471_.py diff --git a/app/models/goal.py b/app/models/goal.py index ae7a56d94..8a6d0d662 100644 --- a/app/models/goal.py +++ b/app/models/goal.py @@ -2,13 +2,14 @@ class Goal(db.Model): - goal_id = db.Column(db.Integer, primary_key=True) + id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String) + tasks = db.relationship("Task", back_populates="goal") def make_dict(self): """return a ditionary with all attributes of a goal""" goal_dict = { - "id": self.goal_id, + "id": self.id, "title": self.title } return goal_dict diff --git a/app/models/task.py b/app/models/task.py index fd26c4b39..79d20fab1 100644 --- a/app/models/task.py +++ b/app/models/task.py @@ -8,6 +8,8 @@ class Task(db.Model): description = db.Column(db.String) completed_at = db.Column(db.DateTime, default = None) is_complete = db.Column(db.Boolean, default = False) + goal_id = db.Column(db.Integer, db.ForeignKey('goal.id')) + goal = db.relationship("Goal", back_populates="tasks") def make_dict(self): """given a task, return a dictionary diff --git a/migrations/versions/7fc34fd31471_.py b/migrations/versions/7fc34fd31471_.py new file mode 100644 index 000000000..074f04818 --- /dev/null +++ b/migrations/versions/7fc34fd31471_.py @@ -0,0 +1,34 @@ +"""empty message + +Revision ID: 7fc34fd31471 +Revises: cab89553b1f6 +Create Date: 2022-11-16 12:24:53.476089 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '7fc34fd31471' +down_revision = 'cab89553b1f6' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('goal', sa.Column('id', sa.Integer(), nullable=False)) + op.drop_column('goal', 'goal_id') + op.add_column('task', sa.Column('goal_id', sa.Integer(), nullable=True)) + op.create_foreign_key(None, 'task', 'goal', ['goal_id'], ['id']) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(None, 'task', type_='foreignkey') + op.drop_column('task', 'goal_id') + op.add_column('goal', sa.Column('goal_id', sa.INTEGER(), autoincrement=True, nullable=False)) + op.drop_column('goal', 'id') + # ### end Alembic commands ### From a7a5d8a26385d9307c87fcb33164a433ebfdd5e2 Mon Sep 17 00:00:00 2001 From: LP Date: Wed, 16 Nov 2022 12:54:36 -0800 Subject: [PATCH 31/34] got nested route for adding a goalwith tasks working --- app/models/goal.py | 2 +- app/models/task.py | 6 ++++-- app/routes/goal.py | 22 +++++++++++++++++++++- tests/test_wave_06.py | 2 +- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/app/models/goal.py b/app/models/goal.py index 8a6d0d662..7a6f3e281 100644 --- a/app/models/goal.py +++ b/app/models/goal.py @@ -4,7 +4,7 @@ class Goal(db.Model): id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String) - tasks = db.relationship("Task", back_populates="goal") + tasks = db.relationship("Task", back_populates="goal", lazy = True) def make_dict(self): """return a ditionary with all attributes of a goal""" diff --git a/app/models/task.py b/app/models/task.py index 79d20fab1..965d48de1 100644 --- a/app/models/task.py +++ b/app/models/task.py @@ -8,8 +8,10 @@ class Task(db.Model): description = db.Column(db.String) completed_at = db.Column(db.DateTime, default = None) is_complete = db.Column(db.Boolean, default = False) - goal_id = db.Column(db.Integer, db.ForeignKey('goal.id')) - goal = db.relationship("Goal", back_populates="tasks") + goal_id = db.Column(db.Integer, db.ForeignKey('goal.id'), nullable = True) + goal = db.relationship("Goal", back_populates="tasks" ) + #project instructions are recommending setting nullable to True. + #where do we do this? def make_dict(self): """given a task, return a dictionary diff --git a/app/routes/goal.py b/app/routes/goal.py index 7e802e005..ee4f31f80 100644 --- a/app/routes/goal.py +++ b/app/routes/goal.py @@ -1,6 +1,7 @@ from flask import Blueprint, jsonify, abort, make_response, request from app.models.goal import Goal from app import db +from app.routes.task import validate_task goal_bp = Blueprint("goal_bp", __name__, url_prefix = "/goals") @@ -57,7 +58,26 @@ def delete_goal(goal_id): response_body = {"details": response_str} return make_response(response_body, 200) - +#nested routes: +@goal_bp.route("//tasks", methods = ["POST"]) +def create_a_goal_with_tasks(goal_id): + new_goal = validate_goal(goal_id) + request_body = request.get_json() + task_id_list = request_body["task_ids"] + #begin helper function + #for those tasks, assign them to the goal, + for task_id in task_id_list: + task = validate_task(task_id) + task.goal_id = goal_id + + #end helper function + db.session.add(new_goal) + db.session.commit() + response_body = { + "id": int(goal_id), + "task_ids": task_id_list + } + return make_response(response_body, 200) #ideally, combine this with validate task, passing in the class as well. def validate_goal(goal_id): diff --git a/tests/test_wave_06.py b/tests/test_wave_06.py index 8afa4325e..9ee680527 100644 --- a/tests/test_wave_06.py +++ b/tests/test_wave_06.py @@ -2,7 +2,7 @@ import pytest -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_post_task_ids_to_goal(client, one_goal, three_tasks): # Act response = client.post("/goals/1/tasks", json={ From 1651a0898d3a3cade75872e2d63b506b701280b0 Mon Sep 17 00:00:00 2001 From: LP Date: Wed, 16 Nov 2022 13:16:43 -0800 Subject: [PATCH 32/34] the get route for goals is working --- app/routes/goal.py | 22 +++++++++++++++++----- tests/test_wave_06.py | 4 ++-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/app/routes/goal.py b/app/routes/goal.py index ee4f31f80..45c6a6663 100644 --- a/app/routes/goal.py +++ b/app/routes/goal.py @@ -1,7 +1,7 @@ from flask import Blueprint, jsonify, abort, make_response, request from app.models.goal import Goal from app import db -from app.routes.task import validate_task +from app.routes.task import validate_task, Task goal_bp = Blueprint("goal_bp", __name__, url_prefix = "/goals") @@ -69,16 +69,28 @@ def create_a_goal_with_tasks(goal_id): for task_id in task_id_list: task = validate_task(task_id) task.goal_id = goal_id - #end helper function db.session.add(new_goal) db.session.commit() response_body = { - "id": int(goal_id), - "task_ids": task_id_list - } + "id": int(goal_id), #there should be a better way to deal with this. + "task_ids": task_id_list} return make_response(response_body, 200) +@goal_bp.route("//tasks", methods = ["GET"]) +def get_one_goal_with_tasks(goal_id): + goal = validate_goal(goal_id) + task_list = [] + for task in goal.tasks: + task_list.append(task.make_dict()) + response = { + "id": int(goal_id), #there should be a better way to do this; + "title": goal.title, + "tasks": task_list + } + return jsonify(response), 200 + + #ideally, combine this with validate task, passing in the class as well. def validate_goal(goal_id): try: diff --git a/tests/test_wave_06.py b/tests/test_wave_06.py index 9ee680527..790373a94 100644 --- a/tests/test_wave_06.py +++ b/tests/test_wave_06.py @@ -23,7 +23,7 @@ def test_post_task_ids_to_goal(client, one_goal, three_tasks): assert len(Goal.query.get(1).tasks) == 3 -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_post_task_ids_to_goal_already_with_goals(client, one_task_belongs_to_one_goal, three_tasks): # Act response = client.post("/goals/1/tasks", json={ @@ -57,7 +57,7 @@ def test_get_tasks_for_specific_goal_no_goal(client): # ***************************************************************** -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_get_tasks_for_specific_goal_no_tasks(client, one_goal): # Act response = client.get("/goals/1/tasks") From 20f5ecc08ce10b534bf23a7f26b3caff9ee4db9d Mon Sep 17 00:00:00 2001 From: LP Date: Wed, 16 Nov 2022 13:21:26 -0800 Subject: [PATCH 33/34] more get functionality working. updated task.make_dict to influde goal_id if relevant. --- app/models/task.py | 3 +++ tests/test_wave_06.py | 11 +++-------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/app/models/task.py b/app/models/task.py index 965d48de1..c3a2cdc09 100644 --- a/app/models/task.py +++ b/app/models/task.py @@ -16,12 +16,15 @@ class Task(db.Model): def make_dict(self): """given a task, return a dictionary with all the attibutes of that task.""" + #if goal_id is empty, don't include it in dictionary. task_dict = { "id": self.task_id, "title": self.title, "description": self.description, "is_complete": self.is_complete } + if self.goal_id: + task_dict["goal_id"] = self.goal_id return task_dict @classmethod diff --git a/tests/test_wave_06.py b/tests/test_wave_06.py index 790373a94..a1eb8f8fc 100644 --- a/tests/test_wave_06.py +++ b/tests/test_wave_06.py @@ -42,7 +42,7 @@ def test_post_task_ids_to_goal_already_with_goals(client, one_task_belongs_to_on assert len(Goal.query.get(1).tasks) == 2 -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_get_tasks_for_specific_goal_no_goal(client): # Act response = client.get("/goals/1/tasks") @@ -50,12 +50,7 @@ def test_get_tasks_for_specific_goal_no_goal(client): # Assert assert response.status_code == 404 - - raise Exception("Complete test with assertion about response body") - # ***************************************************************** - # **Complete test with assertion about response body*************** - # ***************************************************************** - + assert response_body == {"message": "Goal 1 not found"} #@pytest.mark.skip(reason="No way to test this feature yet") def test_get_tasks_for_specific_goal_no_tasks(client, one_goal): @@ -74,7 +69,7 @@ def test_get_tasks_for_specific_goal_no_tasks(client, one_goal): } -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_get_tasks_for_specific_goal(client, one_task_belongs_to_one_goal): # Act response = client.get("/goals/1/tasks") From 7ad720c870aa283adcb504df7d5ba048800f71d5 Mon Sep 17 00:00:00 2001 From: LP Date: Wed, 16 Nov 2022 13:22:15 -0800 Subject: [PATCH 34/34] nested routes totally working --- tests/test_wave_06.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_wave_06.py b/tests/test_wave_06.py index a1eb8f8fc..ed8f6a97f 100644 --- a/tests/test_wave_06.py +++ b/tests/test_wave_06.py @@ -94,7 +94,7 @@ def test_get_tasks_for_specific_goal(client, one_task_belongs_to_one_goal): } -@pytest.mark.skip(reason="No way to test this feature yet") +#@pytest.mark.skip(reason="No way to test this feature yet") def test_get_task_includes_goal_id(client, one_task_belongs_to_one_goal): response = client.get("/tasks/1") response_body = response.get_json()