-
Notifications
You must be signed in to change notification settings - Fork 44
Sphinx C22 - Aleida V #33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d6ba97c
19a1d4b
d0678a5
3f5d293
a8b1a3a
f071a28
1353080
6eb7bcb
2184a9a
136c02c
bab0ce1
7af8ae8
5e5caab
cd408b5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,22 +1,33 @@ | ||
| from flask import Flask | ||
| from .db import db, migrate | ||
| from .models import task, goal | ||
| from app.routes.task_routes import bp as tasks_bp | ||
| from app.routes.goal_routes import bp as goals_bp | ||
| from dotenv import load_dotenv | ||
| from flask_cors import CORS | ||
| import os | ||
|
|
||
| def create_app(config=None): | ||
| load_dotenv() | ||
|
|
||
| def create_app(test_config=None): | ||
| app = Flask(__name__) | ||
| CORS(app) | ||
| app.config['CORS_HEADERS'] = 'Content-Type' | ||
|
|
||
| app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False | ||
| app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('SQLALCHEMY_DATABASE_URI') | ||
|
|
||
| if config: | ||
| if test_config: | ||
| # Merge `config` into the app's configuration | ||
| # to override the app's default settings for testing | ||
| app.config.update(config) | ||
| app.config.update(test_config) | ||
|
|
||
| # Initialize app with SQLAlchemy and Migrate | ||
| db.init_app(app) | ||
| migrate.init_app(app, db) | ||
|
|
||
| # Register Blueprints here | ||
| app.register_blueprint(tasks_bp) | ||
| app.register_blueprint(goals_bp) | ||
|
|
||
| return app |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,38 @@ | ||
| from sqlalchemy.orm import Mapped, mapped_column | ||
| from sqlalchemy.orm import Mapped, mapped_column, relationship # type: ignore | ||
| from ..db import db | ||
| from ..routes.task_routes import validate_task | ||
| from typing import TYPE_CHECKING | ||
| if TYPE_CHECKING: | ||
| from .task import Task | ||
|
|
||
| class Goal(db.Model): | ||
| id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) | ||
| title: Mapped[str] | ||
| tasks: Mapped[list["Task"]] = relationship(back_populates="goal") | ||
|
|
||
|
|
||
| def check_goal_tasks(self): | ||
| tasks_assigned = [] | ||
| if self.tasks: | ||
| for task in self.tasks: | ||
| task = validate_task(task.id) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If a task is found on |
||
| tasks_assigned.append(task.to_nested_dict()) | ||
|
|
||
| return tasks_assigned | ||
|
|
||
| def to_dict(self): | ||
| goal_to_dict = {} | ||
| goal_to_dict["id"] = self.id | ||
| goal_to_dict["title"] = self.title | ||
| return goal_to_dict | ||
|
|
||
| def to_nested_dict(self): | ||
| goal_to_dict = {} | ||
| goal_to_dict["id"] = self.id | ||
| goal_to_dict["title"] = self.title | ||
| if not self.check_goal_tasks(): | ||
| goal_to_dict["tasks"] = [] | ||
| else: | ||
| goal_to_dict["tasks"] = self.check_goal_tasks() | ||
|
Comment on lines
+29
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have an endpoint that will provide us with the same data that |
||
|
|
||
| return goal_to_dict | ||
|
Comment on lines
+29
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Notice that this method and the |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,55 @@ | ||
| from sqlalchemy.orm import Mapped, mapped_column | ||
| from sqlalchemy.orm import Mapped, mapped_column, relationship # type: ignore | ||
| from ..db import db | ||
| from datetime import datetime | ||
| from sqlalchemy import ForeignKey # type: ignore | ||
| from typing import Optional | ||
|
|
||
| from typing import TYPE_CHECKING | ||
| if TYPE_CHECKING: | ||
| from .goal import Goal | ||
|
|
||
|
|
||
| class Task(db.Model): | ||
| id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) | ||
| title: Mapped[str] | ||
| description: Mapped[str] | ||
| completed_at: Mapped[Optional[datetime]] # = completed_at if completed_at is not None | ||
|
|
||
| goal_id: Mapped[Optional[int]] = mapped_column(ForeignKey("goal.id")) | ||
| goal: Mapped[Optional["Goal"]] = relationship(back_populates="tasks") | ||
|
Comment on lines
+18
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice work establishing this relationship and attribute of the |
||
|
|
||
|
|
||
| def to_dict(self): | ||
| if not self.completed_at: | ||
| is_complete = False | ||
| else: | ||
| is_complete = True | ||
|
|
||
| return dict( | ||
| id=self.id, | ||
| title=self.title, | ||
| description=self.description, | ||
| is_complete=is_complete | ||
| ) | ||
|
|
||
| def to_nested_dict(self): | ||
| if not self.completed_at: | ||
| is_complete = False | ||
| else: | ||
| is_complete = True | ||
|
|
||
| task_dictionary = dict( | ||
| id=self.id, | ||
| title=self.title, | ||
| description=self.description, | ||
| is_complete=is_complete | ||
| ) | ||
|
|
||
| if self.goal_id: | ||
| task_dictionary["goal_id"] = self.goal_id | ||
|
|
||
| return task_dictionary | ||
|
|
||
|
|
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,120 @@ | ||
| from flask import Blueprint | ||
| from flask import Blueprint, abort, jsonify, make_response, request | ||
| from app.db import db | ||
| from ..models.goal import Goal | ||
| import requests | ||
| # from .route_utilities import validate_model, create_model, validate_task, validate_goal | ||
| from app.models.task import Task | ||
| from ..routes.task_routes import validate_task | ||
|
|
||
|
|
||
| bp = Blueprint("goals_bp", __name__, url_prefix="/goals") | ||
|
|
||
| @bp.post("") | ||
| def create_goal(): | ||
|
|
||
| try: | ||
| request_body = request.get_json() | ||
| title = request_body["title"] | ||
|
|
||
| new_goal = Goal(title=title) | ||
|
|
||
| db.session.add(new_goal) | ||
| db.session.commit() | ||
|
|
||
| return {"goal": new_goal.to_dict()}, 201 | ||
|
|
||
| except KeyError as error: | ||
| response = {"details": f"Invalid data"} | ||
| abort(make_response(response, 400)) | ||
|
Comment on lines
+15
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This logic looks very similar to the logic we wrote in our create routes, what refactoring could be done here to move this code out of our routes into a helper function to support maintainability and scalability? (Hint: We did a refactor like this in Flasky). |
||
|
|
||
| @bp.get("") | ||
| def get_all_goals(): | ||
|
|
||
| query = db.select(Goal).order_by(Goal.id) | ||
| goals = db.session.scalars(query) | ||
|
|
||
| goals_response = [goal.to_dict() for goal in goals] | ||
|
|
||
| return goals_response | ||
|
Comment on lines
+33
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The refactor I mention below about getting all of our tasks would allow us condense this to one line of code. |
||
|
|
||
| @bp.get("/<goal_id>") | ||
| def get_one_goal(goal_id): | ||
| goal = validate_goal(goal_id) | ||
| response = {"goal": goal.to_dict()} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another place where our nested dictionary logic could be implemented. |
||
| return response | ||
|
|
||
|
|
||
| @bp.get("/<goal_id>/tasks") | ||
| def get_tasks_by_goal(goal_id): | ||
| goal = validate_goal(goal_id) | ||
| response = goal.to_nested_dict() | ||
| return response | ||
|
|
||
| # @bp.get("/<goal_id>/tasks") | ||
| # def get_tasks_for_one_goal(goal_id): | ||
| # goal = validate_goal(goal_id) | ||
| # return goal.to_nested_dict() | ||
|
|
||
|
|
||
|
|
||
| @bp.post("/<goal_id>/tasks") | ||
| def create_new_tasks_for_goal(goal_id): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would name this something different, mainly because it gives the impression that you will create a new |
||
|
|
||
| current_goal = validate_goal(goal_id) | ||
| request_body = request.get_json() | ||
|
|
||
|
|
||
| current_goal_tasks = request_body["task_ids"] | ||
|
|
||
| for task in current_goal_tasks: | ||
| current_task = validate_task(task) | ||
| # query = db.select(Task).where(current_task.goal_id == goal_id) | ||
| current_task.goal_id = goal_id | ||
|
|
||
| db.session.commit() | ||
|
|
||
| return {"id": current_goal.id, | ||
| "task_ids": current_goal_tasks | ||
| } | ||
|
|
||
|
|
||
| @bp.delete("/<goal_id>") | ||
| def delete_goal(goal_id): | ||
|
|
||
| goal = validate_goal(goal_id) | ||
|
|
||
| db.session.delete(goal) | ||
| db.session.commit() | ||
|
|
||
| response = {"details": f'Goal {goal_id} "{goal.title}" successfully deleted'} | ||
| return jsonify(response) | ||
|
Comment on lines
+81
to
+90
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ⭐️ |
||
|
|
||
|
|
||
| @bp.put("/<goal_id>") | ||
| def update_goal(goal_id): | ||
| goal = validate_goal(goal_id) | ||
| request_body = request.get_json() | ||
|
|
||
| goal.title = request_body["title"] | ||
|
|
||
| db.session.commit() | ||
|
|
||
| return {"goal": goal.to_dict()} | ||
|
Comment on lines
+93
to
+102
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this code similar to our other |
||
|
|
||
|
|
||
| def validate_goal(goal_id): | ||
| try: | ||
| goal_id = int(goal_id) | ||
|
|
||
| except: | ||
| response = {"details": f"Invalid data"} | ||
| abort(make_response(response, 400)) | ||
|
|
||
| query = db.select(Goal).where(Goal.id == goal_id) | ||
| goal = db.session.scalar(query) | ||
|
|
||
| if not goal: | ||
| response = {"details": f"Goal {goal_id} not found"} | ||
| abort(make_response(response, 404)) | ||
|
|
||
| return goal | ||
|
Comment on lines
+105
to
+120
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could move this into it's own helper file now so that we could clear up this file since it is not a route itself. We could even DRY this up a bit to where it could written to check against any model instead of just the |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
⭐️