From 1d353fa5c37a6a4c0c421f9fe8464d03da7f9fe3 Mon Sep 17 00:00:00 2001 From: Dan H Date: Fri, 1 Dec 2023 13:12:41 -0800 Subject: [PATCH 01/10] plan is to run this in debian bookworm, which uses Python 3.11 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 30d0c95..6bcef05 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8 +FROM python:3.11 COPY . /app WORKDIR /app RUN mkdir /var/log/donate && \ From fed2fe7e3c02b12591685da134adee59f6a44368 Mon Sep 17 00:00:00 2001 From: Dan H Date: Fri, 1 Dec 2023 13:13:17 -0800 Subject: [PATCH 02/10] docker-compose.yaml file for local development --- docker-compose.yaml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 docker-compose.yaml diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..1203343 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,7 @@ +services: + web: + build: . + ports: + - "8000:5000" + redis: + image: "redis:alpine" From f99732513803f7ea518b8fb72875d5caff95bfa2 Mon Sep 17 00:00:00 2001 From: Dan H Date: Fri, 1 Dec 2023 13:17:19 -0800 Subject: [PATCH 03/10] not sure why 'from flask import Markup' no longer works. this works. it is possible this is the wrong solution --- donate/routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/donate/routes.py b/donate/routes.py index d442d34..219b0b4 100644 --- a/donate/routes.py +++ b/donate/routes.py @@ -4,12 +4,12 @@ from flask import ( current_app as app, flash, - Markup, redirect, render_template, request, Blueprint, ) +from markupsafe import Markup from sqlalchemy.orm.exc import ( NoResultFound, ) From 2cc0fc65ee8da595207eccb8759c71fc5bef39d2 Mon Sep 17 00:00:00 2001 From: Dan H Date: Fri, 1 Dec 2023 13:22:24 -0800 Subject: [PATCH 04/10] flask loads vars from .env without this line. flask fails if this line is not commented out. not sure whats going on. seems like the right thing to do --- autoapp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoapp.py b/autoapp.py index 8810d88..09895ec 100644 --- a/autoapp.py +++ b/autoapp.py @@ -1,6 +1,6 @@ from dotenv import load_dotenv import os -load_dotenv(os.environ['DONATE_DOTENV']) +#load_dotenv(os.environ['DONATE_DOTENV']) from donate.app import create_app from donate.database import db From 7b502f0045d45db2b80554cf7e2df6cd060307fa Mon Sep 17 00:00:00 2001 From: Dan H Date: Fri, 1 Dec 2023 13:24:22 -0800 Subject: [PATCH 05/10] there is no db module in donate.database to import. not sure how this worked before. there is a db module available for import in donate.extensions. I hope this is the right thing to do, not sure --- autoapp.py | 2 +- donate/routes.py | 2 +- donate/util.py | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/autoapp.py b/autoapp.py index 09895ec..62144ff 100644 --- a/autoapp.py +++ b/autoapp.py @@ -3,7 +3,7 @@ #load_dotenv(os.environ['DONATE_DOTENV']) from donate.app import create_app -from donate.database import db +from donate.extensions import db from donate.log_utils import start_timer, log_request from donate.models import DonateConfiguration from donate.settings import DevConfig, ProdConfig, TestConfig diff --git a/donate/routes.py b/donate/routes.py index 219b0b4..8aa5083 100644 --- a/donate/routes.py +++ b/donate/routes.py @@ -14,7 +14,7 @@ NoResultFound, ) from donate.util import get_one -from donate.database import db +from donate.extensions import db from donate.models import ( Account, Project, diff --git a/donate/util.py b/donate/util.py index 4bc1b8b..9716ad5 100644 --- a/donate/util.py +++ b/donate/util.py @@ -4,7 +4,8 @@ ) from flask import current_app as app -from donate.database import db +from donate.extensions import db + def get_one(cls, criteria): From 8c8f63430df010525e481a505e3778f9c00262f7 Mon Sep 17 00:00:00 2001 From: Dan H Date: Fri, 1 Dec 2023 13:25:14 -0800 Subject: [PATCH 06/10] replace use of flask-validator with sqlalchemy.orm --- donate/models.py | 100 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 91 insertions(+), 9 deletions(-) diff --git a/donate/models.py b/donate/models.py index d1d3073..39b7749 100644 --- a/donate/models.py +++ b/donate/models.py @@ -1,11 +1,14 @@ from donate.extensions import db from datetime import datetime -from flask_validator import ( - ValidateInteger, - ValidateString, - ValidateNumeric, -) - +from flask_sqlalchemy import SQLAlchemy +from sqlalchemy.orm import validates +## replaced by ripping off +# https://github.com/learn-co-curriculum/python-p4-validation-flask-sqlalchemy-validations +#from flask_validator import ( +# ValidateInteger, +# ValidateString, +# ValidateNumeric, +#) class TimestampMixin(): ''' Most objects (but not all) need a creation and updated timestamp ''' @@ -41,13 +44,28 @@ class User(db.Model): # donations = db.relationship('Donation') # subscriptions = db.relationship('StripeSubscription') +## replaced by ripping off +# https://github.com/learn-co-curriculum/python-p4-validation-flask-sqlalchemy-validations + """ @classmethod def __declare_last__(cls): ValidateString(User.username, False, True) ValidateString(User.email, False, - True, + True,sqlalchemy.orm "not a valid email address") + """ + @validates('username', 'name_first', 'name_last') + def validate_string(self, key, s): + if not isinstance(s, str): + raise ValueError("Failed string test") + return s + + @validates('email') + def validate_email(self, key, address): + if '@' not in address: + raise ValueError("Failed simple email test") + return address class Currency(db.Model, TimestampMixin): @@ -61,10 +79,17 @@ class Currency(db.Model, TimestampMixin): unique=True, nullable=False) + """ @classmethod def __declare_last__(cls): ValidateString(Currency.name, False, True) ValidateString(Currency.code, False, True) + """ + @validates('name', 'code') + def validate_string(self, key, s): + if not isinstance(s, str): + raise ValueError("Failed string test") + return s class Account(db.Model, TimestampMixin): @@ -87,10 +112,24 @@ class Account(db.Model, TimestampMixin): ccy_id = db.Column(db.Integer, db.ForeignKey('currency.id')) ccy = db.relationship('Currency') + """ @classmethod def __declare_last__(cls): ValidateString(Account.name, False, True) ValidateInteger(Account.ccy_id, False, True) + """ + @validates('name') + def validate_string(self, key, s): + if not isinstance(s, str): + raise ValueError("Failed string test") + return s + + @validates('ccy_id') + def validate_integer(self, key, i): + if not isinstance(i, int): + raise ValueError("Failed integer test") + return i + class Project(db.Model, TimestampMixin): @@ -117,10 +156,23 @@ class Project(db.Model, TimestampMixin): accounts = db.relationship('Account') + """ @classmethod def __declare_last__(cls): ValidateString(Project.name, False, True) ValidateNumeric(Project.goal, False, True) + """ + @validates('name') + def validate_string(self, key, s): + if not isinstance(s, str): + raise ValueError("Failed string test") + return s + + @validates('goal') + def validate_number(self, key, g): + if not g.isnumeric(): + raise ValueError("Failed numeric test") + return g class Donation(db.Model, TimestampMixin): @@ -219,6 +271,7 @@ class Transaction(db.Model, TimestampMixin): # approver = db.relationship("User", # foreign_keys=[approver_id]) + """ @classmethod def __declare_last__(cls): ValidateInteger(Transaction.ccy_id, False, True) @@ -229,6 +282,18 @@ def __declare_last__(cls): # ValidateInteger(Transaction.approver_id, False, True) # validate that the transaction is between two accounts with the # same ccy + """ + @validates('ccy_id','payer_id','recvr_id') + def validate_integer(self, key, i): + if not isinstance(i, int): + raise ValueError("Failed integer test") + return i + + @validates('amount') + def validate_number(self, key, g): + if not g.isnumeric(): + raise ValueError("Failed numeric test") + return g class StripeSubscription(db.Model, TimestampMixin): @@ -273,14 +338,31 @@ class StripePlan(db.Model): desc = db.Column(db.String(64), nullable=False) subscriptions = db.relationship('StripeSubscription') - + """ @classmethod def __declare_last__(cls): ValidateNumeric(StripePlan.amount, False, True) ValidateString(StripePlan.name, False, True) ValidateInteger(StripePlan.ccy_id, False, True) ValidateString(StripePlan.interval, False, True) - + """ + @validates('name','interval') + def validate_string(self, key, s): + if not isinstance(s, str): + raise ValueError("Failed string test") + return s + + @validates('ccy_id') + def validate_integer(self, key, i): + if not isinstance(i, int): + raise ValueError("Failed integer test") + return i + + @validates('amount') + def validate_number(self, key, g): + if not g.isnumeric(): + raise ValueError("Failed numeric test") + return g class DonateConfiguration(db.Model, TimestampMixin): __tablename__ = 'donate_configuration' From cced2022fed8343df1925999c7f86e9d9610ce74 Mon Sep 17 00:00:00 2001 From: Dan H Date: Fri, 1 Dec 2023 13:37:00 -0800 Subject: [PATCH 07/10] update requirements.txt with what I used with python 3.11 and Flask 3.0.0 --- requirements.txt | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/requirements.txt b/requirements.txt index a274357..8794fa8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,22 +1,27 @@ -Babel==2.6.0 -Flask==1.1.4 -Flask-Migrate==2.3.1 -Flask-SQLAlchemy==2.5.1 -SQLAlchemy==1.4.50 -GitPython==3.0.9 -git+https://github.com/xeBuz/Flask-Validator.git@v1.4#egg=Flask-Validator -pdoc==0.3.2 -pytest==5.3.5 -pytest-cov==2.7.1 -python-dotenv==0.10.1 -sadisplay==0.4.9 -Werkzeug==1.0.1 -stripe==2.17.0 -rfc3339==6.0 +alembic==1.13.0 ansicolors==1.1.8 -mysqlclient==1.4.2 -prometheus_client==0.7.1 -requests==2.24.0 -Jinja2==2.11.3 -itsdangerous==1.1.0 -MarkupSafe==1.1.1 +blinker==1.7.0 +certifi==2023.11.17 +charset-normalizer==3.3.2 +click==8.1.7 +Flask==3.0.0 +flask-marshmallow==0.15.0 +Flask-Migrate==4.0.5 +Flask-SQLAlchemy==3.1.1 +greenlet==3.0.1 +idna==3.6 +itsdangerous==2.1.2 +Jinja2==3.1.2 +Mako==1.3.0 +MarkupSafe==2.1.3 +packaging==23.2 +prometheus-client==0.19.0 +python-dotenv==1.0.0 +requests==2.31.0 +rfc3339==6.2 +sadisplay==0.4.9 +SQLAlchemy==2.0.23 +stripe==7.7.0 +typing_extensions==4.8.0 +urllib3==2.1.0 +Werkzeug==3.0.1 From 678f7fc376cd8fbabe7738ae4a77d2a6d83dc186 Mon Sep 17 00:00:00 2001 From: Dan H Date: Fri, 1 Dec 2023 13:55:43 -0800 Subject: [PATCH 08/10] updated requirements.txt file with what im using in dev. not guaranteed to work with anything or everything in prod. --- requirements.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8794fa8..711dba5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,18 +4,25 @@ blinker==1.7.0 certifi==2023.11.17 charset-normalizer==3.3.2 click==8.1.7 +coverage==7.3.2 Flask==3.0.0 -flask-marshmallow==0.15.0 Flask-Migrate==4.0.5 Flask-SQLAlchemy==3.1.1 greenlet==3.0.1 idna==3.6 +iniconfig==2.0.0 itsdangerous==2.1.2 Jinja2==3.1.2 Mako==1.3.0 MarkupSafe==2.1.3 +mysqlclient==2.2.0 packaging==23.2 +pdoc==14.1.0 +pluggy==1.3.0 prometheus-client==0.19.0 +Pygments==2.17.2 +pytest==7.4.3 +pytest-cov==4.1.0 python-dotenv==1.0.0 requests==2.31.0 rfc3339==6.2 From 046e00a67326646287e92c240417e4f0b327a489 Mon Sep 17 00:00:00 2001 From: Dan H Date: Fri, 1 Dec 2023 23:12:21 -0800 Subject: [PATCH 09/10] fix validate_number test, make it match https://flask-validator.readthedocs.io/en/latest/validators.html\#in-nan --- donate/models.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/donate/models.py b/donate/models.py index 39b7749..249a774 100644 --- a/donate/models.py +++ b/donate/models.py @@ -170,11 +170,10 @@ def validate_string(self, key, s): @validates('goal') def validate_number(self, key, g): - if not g.isnumeric(): + if (type(g) != int) and (type(g) != long) and (type(g) != float) and (type(g) != complex): raise ValueError("Failed numeric test") return g - class Donation(db.Model, TimestampMixin): ''' An amount of currency donated by a user, possibly anonymous. id: unique ID of domnation @@ -291,7 +290,7 @@ def validate_integer(self, key, i): @validates('amount') def validate_number(self, key, g): - if not g.isnumeric(): + if (type(g) != int) and (type(g) != long) and (type(g) != float) and (type(g) != complex): raise ValueError("Failed numeric test") return g @@ -360,7 +359,7 @@ def validate_integer(self, key, i): @validates('amount') def validate_number(self, key, g): - if not g.isnumeric(): + if (type(g) != int) and (type(g) != long) and (type(g) != float) and (type(g) != complex): raise ValueError("Failed numeric test") return g From 8d8b06992e447a9bf0005ea394b79ae44e608ebc Mon Sep 17 00:00:00 2001 From: Dan H Date: Fri, 1 Dec 2023 23:28:59 -0800 Subject: [PATCH 10/10] ignore file test_donate - used for sqlite3 db in dev --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index c295e68..9d28dca 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ logs/ migrations/versions/* diagrams/*.png + +test_donate