From 2ac86f5e606c746cb308985519fe18b9f0021008 Mon Sep 17 00:00:00 2001 From: ChristopherDiesch Date: Thu, 27 Mar 2025 16:12:01 -0600 Subject: [PATCH 01/50] Fix typo --- secrets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/secrets b/secrets index ccaaa2f..a3c7a4a 100644 --- a/secrets +++ b/secrets @@ -1,6 +1,6 @@ # Secrets which are encrypted using git-crypt before being sent to git-crypt FLASK_SECRET_KEY="" POSTGRES_USER="" -POSTGRES_PASSWD="" +POSTGRES_PASSWORD="" REDIS_USER="" REDIS_PASSWD="" \ No newline at end of file From d43e847f9012f595a02ca69ca01f747cef69982c Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 01:17:15 -0600 Subject: [PATCH 02/50] Add git-crypt to .gitattributes --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index d95a7f8..9a30271 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -/app/secret_key filter=git-crypt diff=git-crypt \ No newline at end of file +./secrets filter=git-crypt diff=git-crypt From ebc8103cda935fdeb3e36aaa3a078d4b99e2fc65 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 01:17:55 -0600 Subject: [PATCH 03/50] formatting --- app/MRE/api/controller.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/MRE/api/controller.py b/app/MRE/api/controller.py index f511f6b..811cdae 100644 --- a/app/MRE/api/controller.py +++ b/app/MRE/api/controller.py @@ -3,5 +3,7 @@ import cmath ''' TODO: Actually implement this ''' -def compute_mre(consumer_fico: int, consumer_income: float, home_price: float) -> float: +def compute_mre(consumer_fico: int, + consumer_income: float, + home_price: float) -> float: return None From 3e5524934f3a7da70264f9d8a74247a84cc704ef Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 01:18:29 -0600 Subject: [PATCH 04/50] Add secrets post git-crypt --- secrets | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/secrets b/secrets index a3c7a4a..b7aee75 100644 --- a/secrets +++ b/secrets @@ -1,6 +1,6 @@ # Secrets which are encrypted using git-crypt before being sent to git-crypt FLASK_SECRET_KEY="" -POSTGRES_USER="" -POSTGRES_PASSWORD="" -REDIS_USER="" -REDIS_PASSWD="" \ No newline at end of file +POSTGRES_USER="mre" +POSTGRES_PASSWORD="NH,EJ5dg3GAnu`;D}Qh&$w" +REDIS_USER="mre" +REDIS_PASSWD="P`+h:GgW8'.QfHL)[C-*3s" From f21ccc5ae7902543de9a244e568a38dd58ea6f4e Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 11:17:17 -0600 Subject: [PATCH 05/50] Temporarily removed secrets --- secrets | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 secrets diff --git a/secrets b/secrets deleted file mode 100644 index b7aee75..0000000 --- a/secrets +++ /dev/null @@ -1,6 +0,0 @@ -# Secrets which are encrypted using git-crypt before being sent to git-crypt -FLASK_SECRET_KEY="" -POSTGRES_USER="mre" -POSTGRES_PASSWORD="NH,EJ5dg3GAnu`;D}Qh&$w" -REDIS_USER="mre" -REDIS_PASSWD="P`+h:GgW8'.QfHL)[C-*3s" From 5c72a28f580fcbfd3531ead73f7967f4f863d9a4 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 11:18:33 -0600 Subject: [PATCH 06/50] secrets added to git-crypt --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 9a30271..a8776de 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -./secrets filter=git-crypt diff=git-crypt +secrets filter=git-crypt diff=git-crypt From f4d8f98bf9e9bacbd62bed4dc7d3f086bdcceb64 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 11:19:11 -0600 Subject: [PATCH 07/50] Have git-crypt run on secrets --- secrets | Bin 0 -> 73 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 secrets diff --git a/secrets b/secrets new file mode 100644 index 0000000000000000000000000000000000000000..dcfc8caa0c61ddac6c1473a906889037d21dcc0d GIT binary patch literal 73 zcmV-P0Ji@CM@dveQdv+`0I?(l&<*NX{_$c3Jb$z1)=iD3=So2sE}SR6oOF^@#Sb-( fvwt2c3Duco)~jIyMMG^WG$!OzUZ!aTOjDHkSg#(u literal 0 HcmV?d00001 From de2c8a702cf811a779597818c2cd26682351741d Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 11:20:48 -0600 Subject: [PATCH 08/50] Added boiler-plate secrets --- secrets | Bin 73 -> 160 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/secrets b/secrets index dcfc8caa0c61ddac6c1473a906889037d21dcc0d..93420158ee072632267e0ae1592afe0e2745deb6 100644 GIT binary patch literal 160 zcmV;R0AK$AM@dveQdv+`08cBOSz{JYL06W=qK$Z?Flad0!%ShUU) zx^^wtT8YcX?`!opn$hUNN6v2kxlb OW!=>dwzQ2VqUda~#!LMG literal 73 zcmV-P0Ji@CM@dveQdv+`0I?(l&<*NX{_$c3Jb$z1)=iD3=So2sE}SR6oOF^@#Sb-( fvwt2c3Duco)~jIyMMG^WG$!OzUZ!aTOjDHkSg#(u From 5211e70d039a5860d56b8df0507bb8bff4f380d8 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 11:26:29 -0600 Subject: [PATCH 09/50] Added default API key --- secrets | Bin 160 -> 215 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/secrets b/secrets index 93420158ee072632267e0ae1592afe0e2745deb6..5b703f09432434c8a83378bf01208f7c61203efb 100644 GIT binary patch literal 215 zcmV;|04Vz5S>@Ket;CKLEoT;gR1w)aD{Ph?#8OheDC%*GySLY&%@?={ZfaqOa`WC9x5fV z6-ena>+XC)#ydlh!b=~XNlB^_@!8$`ZTc0(;(SaI#XR?{K1^_^pHd0(ZzG-wEwKTb RgrldDm;&h`UDBf3wwYq;Yi$4k literal 160 zcmV;R0AK$AM@dveQdv+`08cBOSz{JYL06W=qK$Z?Flad0!%ShUU) zx^^wtT8YcX?`!opn$hUNN6v2kxlb OW!=>dwzQ2VqUda~#!LMG From 461e7573e8f602dd1595714a2995e5f105caa337 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 11:32:43 -0600 Subject: [PATCH 10/50] Added usernames and passwords for redis/postgres --- secrets | Bin 215 -> 297 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/secrets b/secrets index 5b703f09432434c8a83378bf01208f7c61203efb..f1a59559ce2c2360230721d09719f4d0c3b3cfef 100644 GIT binary patch literal 297 zcmV+^0oMKiM@dveQdv+`0C6*hzEK$4oFPmfz!F^DR!^ba`sghybYheNR&PMNOx#84 zShkBviFp9cpntD8r!v~CL?G{w?@IR+#A9Y41Y&_)eeJ4U+=k-&RDn&s6*c!Jbc4X~ zCcj`maC6gF-G>`FH#oh|`Mj5pE2v)n0V1@s*jzfaE0N99cf>a5nDE;{akEBPTKRGo zD0WTlAsB{JXd>Af1|GHV>pUHgmNAkD_Z4a}4B1n=veA5Yf+Q-&Ci;N@%&Yr&jfZBL zo|Ttd1%Kd>@6AteL8^7(E~APJeC3A`n^QShovNhhi@$m~!T${qtUi53Pswi%l`r$n vkBjmRF0QUqw~+t&(1_yHa_m4=%Bg$WDk-Rn8*vV?%DXk-eh}l9y%1YRYYdVX literal 215 zcmV;|04Vz5S>@Ket;CKLEoT;gR1w)aD{Ph?#8OheDC%*GySLY&%@?={ZfaqOa`WC9x5fV z6-ena>+XC)#ydlh!b=~XNlB^_@!8$`ZTc0(;(SaI#XR?{K1^_^pHd0(ZzG-wEwKTb RgrldDm;&h`UDBf3wwYq;Yi$4k From 8e23799e3f78efad2a6ccf76cbc263fd4dd7c9a2 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 11:39:48 -0600 Subject: [PATCH 11/50] Added some skeleton functions to the mre controller --- app/MRE/api/controller.py | 12 ++++++++++++ app/__init__.py | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/MRE/api/controller.py b/app/MRE/api/controller.py index 811cdae..748ff15 100644 --- a/app/MRE/api/controller.py +++ b/app/MRE/api/controller.py @@ -7,3 +7,15 @@ def compute_mre(consumer_fico: int, consumer_income: float, home_price: float) -> float: return None + +''' +TODO: Implement this. +''' +def compute_tic_fee(home_price: float) -> float: + return None + +def compute_max_tic_fee(consumer_income: float, + consumer_fico: int) -> float: + return None + + diff --git a/app/__init__.py b/app/__init__.py index 2d4d059..c616fa9 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,7 +1,8 @@ from flask import Flask import os from app.MRE import mre_blueprint -from logging import Logger, Formatter, getLogger, DEBUG, INFO, FileHandler +from logging import Logger, Formatter, getLogger, DEBUG, INFO, + FileHandler def load_config_from_environ(app: Flask) -> Flask: From 3d98e4c23ef718e4ad9895311796f461d2377078 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 11:48:54 -0600 Subject: [PATCH 12/50] Added boiler-plate files for docker and entrypoint --- docker/entrypoint.sh | 2 ++ docker/mre.Dockerfile | 0 2 files changed, 2 insertions(+) create mode 100644 docker/entrypoint.sh create mode 100644 docker/mre.Dockerfile diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 0000000..75c9a20 --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,2 @@ +#! /usr/bin/bash + diff --git a/docker/mre.Dockerfile b/docker/mre.Dockerfile new file mode 100644 index 0000000..e69de29 From 4e9b4c726c5d2f16d5c90e9f53cece5ff2649fc5 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 11:58:01 -0600 Subject: [PATCH 13/50] Fleshed out Dockerfile --- docker/mre.Dockerfile | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docker/mre.Dockerfile b/docker/mre.Dockerfile index e69de29..069a07a 100644 --- a/docker/mre.Dockerfile +++ b/docker/mre.Dockerfile @@ -0,0 +1,10 @@ +From python:3.13.2-bullseye + +# set environment variables +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED + +copy .. . + +CMD ['.', 'venv/bin/activate', ';', + 'pip', 'install', '-r', 'requirements.txt'] From c63cab39aaef6693ba40127269626eb25658ec5f Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 11:59:20 -0600 Subject: [PATCH 14/50] Typo fix --- docker/mre.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/mre.Dockerfile b/docker/mre.Dockerfile index 069a07a..1c24368 100644 --- a/docker/mre.Dockerfile +++ b/docker/mre.Dockerfile @@ -4,7 +4,7 @@ From python:3.13.2-bullseye ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED -copy .. . +COPY .. . CMD ['.', 'venv/bin/activate', ';', 'pip', 'install', '-r', 'requirements.txt'] From 268b31fcc7f23cd38bc25e407032ae1c0332f041 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 11:59:41 -0600 Subject: [PATCH 15/50] Typo fix --- docker/mre.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/mre.Dockerfile b/docker/mre.Dockerfile index 1c24368..5a97c3f 100644 --- a/docker/mre.Dockerfile +++ b/docker/mre.Dockerfile @@ -1,4 +1,4 @@ -From python:3.13.2-bullseye +FROM python:3.13.2-bullseye # set environment variables ENV PYTHONDONTWRITEBYTECODE 1 From b74dc0668a51a92fcb94eddcd1abd344c576c5a5 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 12:02:58 -0600 Subject: [PATCH 16/50] Added code to entrypoint --- docker/entrypoint.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 75c9a20..8ee4158 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -1,2 +1,3 @@ #! /usr/bin/bash +uwsgi --ini /var/www/run.py From 6f746848d9601edcd19cf3bf817f93d87c724b1c Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 12:11:09 -0600 Subject: [PATCH 17/50] Added to the dockerfile --- docker/mre.Dockerfile | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/docker/mre.Dockerfile b/docker/mre.Dockerfile index 5a97c3f..59453a4 100644 --- a/docker/mre.Dockerfile +++ b/docker/mre.Dockerfile @@ -1,10 +1,27 @@ FROM python:3.13.2-bullseye +LABEL MAINTAINER="Chris Diesch " + +# make sure everything it up-to-date +RUN "apt-get -y update" + +# Add the entrypoint and make it executable +ADD ./entrypoint.sh /var/docker-entrypoint.sh +RUN "chmod +x /var/docker-entrypoint.sh" + +# add the requirements and install them +ADD ../requirements.txt /var/www/requirements.txt +RUN "pip install -Ur /var/www/requirements.txt" + +# add the uwsgi runner +ADD ../run.py /var/www/run.py + # set environment variables ENV PYTHONDONTWRITEBYTECODE 1 -ENV PYTHONUNBUFFERED +ENV PYTHONUNBUFFERED 1 -COPY .. . +# Copy the app directory +COPY ../app /var/www/app -CMD ['.', 'venv/bin/activate', ';', - 'pip', 'install', '-r', 'requirements.txt'] +# define the entrypoint +ENTRYPOINT ["/var/docker-entrypoint.sh"] From 05bc670e9a01dd0951b6ddf98bf6475f999d779f Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 12:12:50 -0600 Subject: [PATCH 18/50] Added definition of workdir to the dockerfile --- docker/mre.Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/mre.Dockerfile b/docker/mre.Dockerfile index 59453a4..5730d5f 100644 --- a/docker/mre.Dockerfile +++ b/docker/mre.Dockerfile @@ -2,6 +2,7 @@ FROM python:3.13.2-bullseye LABEL MAINTAINER="Chris Diesch " +WORKDIR /var/www # make sure everything it up-to-date RUN "apt-get -y update" From 24901135cb2261e076732ca96260e84804372a77 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 12:34:25 -0600 Subject: [PATCH 19/50] Use a docker-compose template instead of a defined yaml file so that the secrets can be loaded at runtime --- .../docker-compose.yaml.tmpl | 39 +++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) rename docker-compose.yaml => docker/docker-compose.yaml.tmpl (57%) diff --git a/docker-compose.yaml b/docker/docker-compose.yaml.tmpl similarity index 57% rename from docker-compose.yaml rename to docker/docker-compose.yaml.tmpl index 3db6620..ca54e36 100644 --- a/docker-compose.yaml +++ b/docker/docker-compose.yaml.tmpl @@ -2,6 +2,8 @@ version: "3.8" services: nginx: + networks: + - frontend image: nginx:latest ports: - "80:80" @@ -12,10 +14,15 @@ services: depends_on: - uwsgi - uwsgi: - image: python:3.9-slim-buster # Use a slim Python image + mre_api: + build: + context: . + docker-file: docker/mre.docker-file + networks: + - frontend + - backend volumes: - - ./app:/app # Mount your Flask app directory + - ./app:/var/www/app # Mount your Flask app directory - ./uwsgi.ini:/etc/uwsgi.ini # UWSGI configuration command: uwsgi --ini /etc/uwsgi.ini expose: @@ -23,27 +30,43 @@ services: environment: - FLASK_APP=run.py # Adjust this based on your Flask app's entry point - FLASK_ENV=dev # Dev environment + - MRE_POSTGRES_PASSWORD={{ POSTGRES_PASSWORD }} + - MRE_POSTGRES_USER={{ POSTGRES_USER }} + - MRE_REDIS_PASSWORD={{ REDIS_PASSWORD }} depends_on: - - redis # Ensure Redis is running before UWSGI starts - + - redis + - postgres + redis: + networks: + - backend image: redis:latest ports: - "6379:6379" # Expose Redis port (for debugging/accessing from outside) volumes: - redis_data:/data # Persist Redis data + command: redis-server --save 20 1 --loglevel {{ LOG_LEGEL }} --requirepass {{ REDIS_PASSWORD }} postgres: + networks: + -backend image: postgres:latest ports: - "5432:5432" # Expose PostgreSQL port (for debugging/admin) environment: - - POSTGRES_USER=youruser # Replace with your desired username - - POSTGRES_PASSWORD=yourpassword # Replace with your desired password + - POSTGRES_USER={{ POSTGRES_USER }} # Replace with your desired username + - POSTGRES_PASSWORD={{ POSTGRES_PASSWORD }} # Replace with your desired password - POSTGRES_DB=yourdb # Replace with your desired database name volumes: - postgres_data:/var/lib/postgresql/data # Persist PostgreSQL data volumes: postgres_data: - redis_data: \ No newline at end of file + redis_data: + +networks: + frontend: + driver: bridge + backend: + driver: bridge + internal: true From 247b5adb1f722ca43cd300d14a72f1ddcd6b9265 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 12:52:14 -0600 Subject: [PATCH 20/50] Added scripts directory --- scripts/render_templates.py | 36 ++++++++++++++++++++++++++++++++++++ start.sh => scripts/start.sh | 0 stop.sh => scripts/stop.sh | 0 3 files changed, 36 insertions(+) create mode 100644 scripts/render_templates.py rename start.sh => scripts/start.sh (100%) rename stop.sh => scripts/stop.sh (100%) diff --git a/scripts/render_templates.py b/scripts/render_templates.py new file mode 100644 index 0000000..47db22b --- /dev/null +++ b/scripts/render_templates.py @@ -0,0 +1,36 @@ +import os +import jinja2 +import argparse + +def render_template(template_file: str, + out_path: str, + **kwargs) -> None: + folder, file = os.path.split(template_file) + template_path = os.path.abspath(folder) + env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_path), + trim_blocks=True, + lstrip_blocks=True) + template = env.get_template(file) + with open(out_path, 'w+') as save: + save.write(template.render(**kwargs)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(prog='render_templates.py', + description='A script to render the docker jinja templates.', + add_help=True) + parser.add_argument('template_path', + help='The path to the template to render.') + parser.add_argument('out_path', + help='The path to write the rendered template.') + parser.add_argument('-k', + '--kwargs', + required=False, + type=str, + default='', + help='Additional kwargs to include if needed (format: key1=value1,key2=value2...).') + parser.add_argument('-o', + '--overwrite', + action='store_true', + help='If set the previous template will be overwritten.') + diff --git a/start.sh b/scripts/start.sh similarity index 100% rename from start.sh rename to scripts/start.sh diff --git a/stop.sh b/scripts/stop.sh similarity index 100% rename from stop.sh rename to scripts/stop.sh From 12b7673b6e3b7cefc86ced8e48576653f845b266 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 13:39:34 -0600 Subject: [PATCH 21/50] Work on render templates.py added argument for variable file --- requirements.txt | 3 ++- scripts/render_templates.py | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 96ede90..03d7fbe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ flask==3.1.0 flask-restx==1.3.0 pyopenssl==25.0.0 -werkzeug==3.1.3 \ No newline at end of file +werkzeug==3.1.3 +jinja2==3.1.6 diff --git a/scripts/render_templates.py b/scripts/render_templates.py index 47db22b..7a758be 100644 --- a/scripts/render_templates.py +++ b/scripts/render_templates.py @@ -21,6 +21,8 @@ if __name__ == '__main__': add_help=True) parser.add_argument('template_path', help='The path to the template to render.') + parser.add_argument('var_file_path', + help='The path to the file containing the variables to use for rendering the tempalte(s).') parser.add_argument('out_path', help='The path to write the rendered template.') parser.add_argument('-k', @@ -34,3 +36,13 @@ if __name__ == '__main__': action='store_true', help='If set the previous template will be overwritten.') + args = parser.parse_args() + + if not os.path.exists(args.template_path): + print(f'The given template path {args.template_path} does not exist.') + exit(1) + + if not os.path.exists(args.var_file_path): + print(f'The given template path {args.var_file_path} does not exist.') + exit(1) + From 096f363fd2797e008a6020d736d540b2429511a5 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 14:08:58 -0600 Subject: [PATCH 22/50] Finished intitial script for rendering templates --- scripts/render_templates.py | 55 +++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/scripts/render_templates.py b/scripts/render_templates.py index 7a758be..ba6c745 100644 --- a/scripts/render_templates.py +++ b/scripts/render_templates.py @@ -1,6 +1,7 @@ import os import jinja2 import argparse +import traceback def render_template(template_file: str, out_path: str, @@ -15,16 +16,26 @@ def render_template(template_file: str, save.write(template.render(**kwargs)) +def load_template_vars_file(var_file_path: str) -> dict: + result = {} + with open(var_file_path) as reader: + for line in reader: + key, value = line.split('=', 1) + result[key] = value + + return result + + if __name__ == '__main__': parser = argparse.ArgumentParser(prog='render_templates.py', description='A script to render the docker jinja templates.', add_help=True) parser.add_argument('template_path', - help='The path to the template to render.') + help='The path to the template(s) to render (Can be either a directory or a file).') parser.add_argument('var_file_path', help='The path to the file containing the variables to use for rendering the tempalte(s).') parser.add_argument('out_path', - help='The path to write the rendered template.') + help='The path to write the rendered template (MUST be a directory).') parser.add_argument('-k', '--kwargs', required=False, @@ -38,6 +49,7 @@ if __name__ == '__main__': args = parser.parse_args() + # make sure we have a template file and a variable file. if not os.path.exists(args.template_path): print(f'The given template path {args.template_path} does not exist.') exit(1) @@ -46,3 +58,42 @@ if __name__ == '__main__': print(f'The given template path {args.var_file_path} does not exist.') exit(1) + run_on_dir = False + # if the template path is a directory, make sure output path exists + if os.path.isdir(args.template_path): + run_on_dir = True + if not os.path.exists(args.out_path): + os.makedirs(args.out_path) + else: + print('The output path must be a directory since the template path is.') + exit(1) + + args.template_path = [os.path.join(args.template_path, p) + for p in filter(lambda x: x.ends_with('.tmpl'), + os.listdir(args.template_path))] + # if the tempalte path is a file + else: + # wrap it in a list + args.template_path = [args.template_path] + + # load the variables from the variable file + kwargs = load_template_vars_file(args.var_file_path) + args.kwargs = {a.split('=')[0]: a.split('=')[1] for a in args.kwargs.split(',')} if args.kwargs else {} + for k in kwargs: + # The assumption made here is that anything passed on the commandline should be prioritized + # over values that appear in the template variables file. + if k not in args.kwargs: + args.kwargs[k] = kwargs[k] + + for template in args.template_path: + out_file_name = os.path.join(args.out_path, + os.path.split(template.remove('.tmpl'))) + # if the output exists and we aren't overwritting files, then move on. + if os.path.exists(out_file_name) and not args.overwrite: + continue + + try: + render_template(template, out_file_name, **args.kwargs) + except Exception as ex: + print(f'Failed to render template "{template}":') + print(f'Error: {str(ex)}\n{traceback.format_exec()}') From 96364f4cd782c9ffead19c51cc6ae9b6aa991bb2 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 15:32:01 -0600 Subject: [PATCH 23/50] Got the render_templates script working properly --- docker/docker-compose.yaml.tmpl | 8 ++++---- scripts/render_templates.py | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/docker/docker-compose.yaml.tmpl b/docker/docker-compose.yaml.tmpl index ca54e36..29ee7d0 100644 --- a/docker/docker-compose.yaml.tmpl +++ b/docker/docker-compose.yaml.tmpl @@ -30,9 +30,9 @@ services: environment: - FLASK_APP=run.py # Adjust this based on your Flask app's entry point - FLASK_ENV=dev # Dev environment - - MRE_POSTGRES_PASSWORD={{ POSTGRES_PASSWORD }} + - MRE_POSTGRES_PASSWORD={{ POSTGRES_PASSWD }} - MRE_POSTGRES_USER={{ POSTGRES_USER }} - - MRE_REDIS_PASSWORD={{ REDIS_PASSWORD }} + - MRE_REDIS_PASSWORD={{ REDIS_PASSWD }} depends_on: - redis - postgres @@ -45,7 +45,7 @@ services: - "6379:6379" # Expose Redis port (for debugging/accessing from outside) volumes: - redis_data:/data # Persist Redis data - command: redis-server --save 20 1 --loglevel {{ LOG_LEGEL }} --requirepass {{ REDIS_PASSWORD }} + command: redis-server --save 20 1 --loglevel {{ LOG_LEGEL }} --requirepass {{ REDIS_PASSWD }} postgres: networks: @@ -55,7 +55,7 @@ services: - "5432:5432" # Expose PostgreSQL port (for debugging/admin) environment: - POSTGRES_USER={{ POSTGRES_USER }} # Replace with your desired username - - POSTGRES_PASSWORD={{ POSTGRES_PASSWORD }} # Replace with your desired password + - POSTGRES_PASSWORD={{ POSTGRES_PASSWD }} # Replace with your desired password - POSTGRES_DB=yourdb # Replace with your desired database name volumes: - postgres_data:/var/lib/postgresql/data # Persist PostgreSQL data diff --git a/scripts/render_templates.py b/scripts/render_templates.py index ba6c745..7ad37b5 100644 --- a/scripts/render_templates.py +++ b/scripts/render_templates.py @@ -20,6 +20,9 @@ def load_template_vars_file(var_file_path: str) -> dict: result = {} with open(var_file_path) as reader: for line in reader: + line = line.strip() + if line.startswith('#') or '=' not in line: + continue key, value = line.split('=', 1) result[key] = value @@ -87,7 +90,7 @@ if __name__ == '__main__': for template in args.template_path: out_file_name = os.path.join(args.out_path, - os.path.split(template.remove('.tmpl'))) + os.path.split(template.replace('.tmpl', ''))[1]) # if the output exists and we aren't overwritting files, then move on. if os.path.exists(out_file_name) and not args.overwrite: continue From b5004f0a4c589aa524f99dc79e32f6edcbecd30f Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Fri, 28 Mar 2025 16:15:41 -0600 Subject: [PATCH 24/50] Add skeleton function to the controller. Add pytest to requirements.txt --- app/MRE/api/controller.py | 3 +++ requirements.txt | 1 + 2 files changed, 4 insertions(+) diff --git a/app/MRE/api/controller.py b/app/MRE/api/controller.py index 748ff15..436b3ed 100644 --- a/app/MRE/api/controller.py +++ b/app/MRE/api/controller.py @@ -14,6 +14,9 @@ TODO: Implement this. def compute_tic_fee(home_price: float) -> float: return None +''' +TODO: Implement this +''' def compute_max_tic_fee(consumer_income: float, consumer_fico: int) -> float: return None diff --git a/requirements.txt b/requirements.txt index 03d7fbe..ee9b6e3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ flask-restx==1.3.0 pyopenssl==25.0.0 werkzeug==3.1.3 jinja2==3.1.6 +pytest==8.3.5 From 1d87e245f43b4a4450ae9121a3ff817440145395 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 30 Mar 2025 00:50:02 -0600 Subject: [PATCH 25/50] Fixed issues with the docker-compose template --- docker/docker-compose.yaml.tmpl | 6 +++--- docker/mre.Dockerfile | 15 +++++++-------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/docker/docker-compose.yaml.tmpl b/docker/docker-compose.yaml.tmpl index 29ee7d0..32d9a43 100644 --- a/docker/docker-compose.yaml.tmpl +++ b/docker/docker-compose.yaml.tmpl @@ -12,12 +12,12 @@ services: - ./nginx/conf.d:/etc/nginx/conf.d # Mount Nginx configuration - ./static:/var/www/static # Serve static files depends_on: - - uwsgi + - mre_api mre_api: build: context: . - docker-file: docker/mre.docker-file + dockerfile: docker/mre.Dockerfile networks: - frontend - backend @@ -49,7 +49,7 @@ services: postgres: networks: - -backend + - backend image: postgres:latest ports: - "5432:5432" # Expose PostgreSQL port (for debugging/admin) diff --git a/docker/mre.Dockerfile b/docker/mre.Dockerfile index 5730d5f..9d7b992 100644 --- a/docker/mre.Dockerfile +++ b/docker/mre.Dockerfile @@ -3,26 +3,25 @@ FROM python:3.13.2-bullseye LABEL MAINTAINER="Chris Diesch " WORKDIR /var/www -# make sure everything it up-to-date -RUN "apt-get -y update" # Add the entrypoint and make it executable -ADD ./entrypoint.sh /var/docker-entrypoint.sh -RUN "chmod +x /var/docker-entrypoint.sh" +ADD ./docker/entrypoint.sh /var/docker-entrypoint.sh +RUN chmod +x /var/docker-entrypoint.sh # add the requirements and install them -ADD ../requirements.txt /var/www/requirements.txt -RUN "pip install -Ur /var/www/requirements.txt" + +ADD ./requirements.txt /var/www/requirements.txt +RUN pip install -Ur /var/www/requirements.txt # add the uwsgi runner -ADD ../run.py /var/www/run.py +ADD ./run.py /var/www/run.py # set environment variables ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 # Copy the app directory -COPY ../app /var/www/app +COPY ./app /var/www/app # define the entrypoint ENTRYPOINT ["/var/docker-entrypoint.sh"] From c96c8897916d041a3736841eec4897fe23438fa4 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 14:51:55 -0600 Subject: [PATCH 26/50] Add mre submodule --- .gitmodules | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e7f93ea --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "MRE-module"] + path: mre_module + uri: http://10.0.50.3:3002/Quarter/MRE-module.git From 7d06b308ba60afd15ef245e7fd61438e28046bb5 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 14:53:12 -0600 Subject: [PATCH 27/50] Add mre submodule --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index e7f93ea..5e6a754 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "MRE-module"] - path: mre_module - uri: http://10.0.50.3:3002/Quarter/MRE-module.git + path = mre_module + uri = http://10.0.50.3:3002/Quarter/MRE-module.git From df161a9edbb8bc0bfcb7847276f6c4edf33c8ec2 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 14:57:33 -0600 Subject: [PATCH 28/50] Added MRE submodule --- .gitmodules | 4 ++-- MRE-module | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) create mode 160000 MRE-module diff --git a/.gitmodules b/.gitmodules index 5e6a754..9e70945 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "MRE-module"] - path = mre_module - uri = http://10.0.50.3:3002/Quarter/MRE-module.git + path = MRE-module + url = http://10.0.50.3:3002/Quarter/MRE-module.git diff --git a/MRE-module b/MRE-module new file mode 160000 index 0000000..27cfe80 --- /dev/null +++ b/MRE-module @@ -0,0 +1 @@ +Subproject commit 27cfe80cbdba084ab3a18fad6b4d288db30d9a32 From 56b79a573a588dfda2a8fdf08599fd8b3f84deb8 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 15:17:34 -0600 Subject: [PATCH 29/50] refactor API and add request parsing --- app/MRE/api/controller.py | 24 ---------------------- app/MRE/api/routes.py | 43 +++++++++++++++++++++++++++------------ 2 files changed, 30 insertions(+), 37 deletions(-) delete mode 100644 app/MRE/api/controller.py diff --git a/app/MRE/api/controller.py b/app/MRE/api/controller.py deleted file mode 100644 index 436b3ed..0000000 --- a/app/MRE/api/controller.py +++ /dev/null @@ -1,24 +0,0 @@ -import cmath - -''' -TODO: Actually implement this -''' -def compute_mre(consumer_fico: int, - consumer_income: float, - home_price: float) -> float: - return None - -''' -TODO: Implement this. -''' -def compute_tic_fee(home_price: float) -> float: - return None - -''' -TODO: Implement this -''' -def compute_max_tic_fee(consumer_income: float, - consumer_fico: int) -> float: - return None - - diff --git a/app/MRE/api/routes.py b/app/MRE/api/routes.py index aee5e4b..cefdcf4 100644 --- a/app/MRE/api/routes.py +++ b/app/MRE/api/routes.py @@ -1,17 +1,34 @@ from flask import current_app -from flask_restx import Api, fields, apidoc, Model -from . import api_blueprint +from flask_restx import Api, fields, apidoc, Model, Namespace, Resource +from flask_restx.reqparse import RequestParser +from MRE_module import compute_mre -api = Api(api_blueprint) +namespace = Namespace('mre', + description='Endpoints for MRE calculations') -mre_from_home_price_model = Model( - { - 'consumer_income': fields.Float(attribute='consumer_income'), - 'home_price': fields.Float(attribute='home_price'), - 'consumer_fico': fields.Integer(attribute='consumer_fico') - } -) +MRE_request_parser = RequestParser() +MRE_request_parser.add_argument('consumer_fico', + required=True, + type=int, + help='The consumer\'s fico score (int between [300, 850])') +MRE_request_parser.add_argument('home_price', + required=True, + type=float, + help='The price of the home the consumer wants to buy (float)') +MRE_request_parser.add_argument('down_payment', + required=True, + type=float, + help='The down payment (as a percentage) for purchasing the home (float between [0, 1]).') + +@namespace.route('compute_mre_from_home_price') +@namespace.doc(params={ + 'consumer_fico': 'The consumer\'s fico score (int).', + 'home_price': 'The price of the home (float).', + 'down_payment': 'The down_payment percentage (float).' +}) +class ComputeMRE(Resource): + @namespace.doc(description='Computes and returns the MRE.') + def get(self): + args = MRE_request_parser.parse_args() + mre = compute_mre(args.home_price, args.down_payment, args.consumer_fico) -@api.route('compute_mre_from_home_price') -def compute_mre_from_home_price(): - pass \ No newline at end of file From 0b122774569b5205f9dbe4a78b603ce7c8d92fe9 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 15:19:39 -0600 Subject: [PATCH 30/50] Move where the submodule is loaded --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 9e70945..a6ad123 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "MRE-module"] - path = MRE-module + path = app/MRE/api/MRE_module url = http://10.0.50.3:3002/Quarter/MRE-module.git From 29011a141a8b806520bdf4db25f59ed43c142b13 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 15:21:24 -0600 Subject: [PATCH 31/50] Fixed issue with submodule --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index a6ad123..cf4c5af 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "MRE-module"] - path = app/MRE/api/MRE_module + path = mre url = http://10.0.50.3:3002/Quarter/MRE-module.git From e7ae192382099f22a34e866d8f338d7b87362698 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 15:23:13 -0600 Subject: [PATCH 32/50] Added submodule --- .gitmodules | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index cf4c5af..5827bbb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ [submodule "MRE-module"] - path = mre - url = http://10.0.50.3:3002/Quarter/MRE-module.git + path = "mre" + url = "http://10.0.50.3:3002/Quarter/MRE-module.git" + From 36f86a0d6ce8a551eca737baa9e5e981603a88f1 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 15:24:45 -0600 Subject: [PATCH 33/50] Moved mre module --- .gitmodules | 4 ++-- MRE-module | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) delete mode 160000 MRE-module diff --git a/.gitmodules b/.gitmodules index 5827bbb..f3fabcc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "MRE-module"] - path = "mre" - url = "http://10.0.50.3:3002/Quarter/MRE-module.git" + path = mre + url = http://10.0.50.3:3002/Quarter/MRE-module.git diff --git a/MRE-module b/MRE-module deleted file mode 160000 index 27cfe80..0000000 --- a/MRE-module +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 27cfe80cbdba084ab3a18fad6b4d288db30d9a32 From 979b9e471dc85b452d41d6b5e060d0796f38ccc9 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 15:28:15 -0600 Subject: [PATCH 34/50] resolving submodule issue --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index f3fabcc..2ae6d80 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "MRE-module"] path = mre - url = http://10.0.50.3:3002/Quarter/MRE-module.git + url = http://10.0.50.3:3002/Quarter/MRE-module From deca20e5f6188a8c65bb3961132e1c191b790708 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 15:31:56 -0600 Subject: [PATCH 35/50] rename submodule --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 2ae6d80..4cc0444 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ -[submodule "MRE-module"] +[submodule "mre"] path = mre url = http://10.0.50.3:3002/Quarter/MRE-module From 2220d933e4cd7c439f2e74ff56a4201038aa0016 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 15:37:41 -0600 Subject: [PATCH 36/50] mre submodule --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 4cc0444..bb2bb77 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "mre"] path = mre - url = http://10.0.50.3:3002/Quarter/MRE-module + url = http://10.0.50.3:3002/Quarter/MRE-module.git From 4b34feb22d0e6efe1ae787e29fa6bf7c13b31cd4 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 15:43:36 -0600 Subject: [PATCH 37/50] Submodule issues --- .gitmodules | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index bb2bb77..759556b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,3 @@ [submodule "mre"] path = mre - url = http://10.0.50.3:3002/Quarter/MRE-module.git - + url = http://10.0.50.3:3002/Quarter/MRE-module.git From 495cf994cb30d83fc109b54330978338af139a27 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 15:47:22 -0600 Subject: [PATCH 38/50] Resolved submodule issues --- .gitmodules | 2 +- mre | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 160000 mre diff --git a/.gitmodules b/.gitmodules index 759556b..3321215 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "mre"] - path = mre + path = mre url = http://10.0.50.3:3002/Quarter/MRE-module.git diff --git a/mre b/mre new file mode 160000 index 0000000..27cfe80 --- /dev/null +++ b/mre @@ -0,0 +1 @@ +Subproject commit 27cfe80cbdba084ab3a18fad6b4d288db30d9a32 From 8c5a6d9743fae88b7e4d203f7b08be21a0f38508 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 16:05:40 -0600 Subject: [PATCH 39/50] Refactor blueprints and namespaces --- api.py | 12 ++++++++++++ app/MRE/__init__.py | 9 +++------ app/MRE/api/__init__.py | 5 ----- app/MRE/api/routes.py | 4 +--- app/__init__.py | 5 +++-- 5 files changed, 19 insertions(+), 16 deletions(-) create mode 100644 api.py diff --git a/api.py b/api.py new file mode 100644 index 0000000..e4af6e5 --- /dev/null +++ b/api.py @@ -0,0 +1,12 @@ +from flask_restx import api +from flask import Blueprint +from .MRE.api import namespace as mre_namespace + +blueprint = Blueprint('api', __name__) +api = Api( + blueprint, + doc='/doc/', + title='Quarter API documentatoin' +) +api.add_namespace(mre_namespace, '/mre') + diff --git a/app/MRE/__init__.py b/app/MRE/__init__.py index 0e56b53..6a1020d 100644 --- a/app/MRE/__init__.py +++ b/app/MRE/__init__.py @@ -1,7 +1,4 @@ -from flask import Blueprint -from .api import api_blueprint +from flask_restx import Namespace -mre_blueprint = Blueprint('mre_blueprint', - __name__, - url_prefix='mre') -mre_blueprint.register_blueprint(api_blueprint) +namespace = Namespace('mre', + description='Endpoints for MRE calculations') diff --git a/app/MRE/api/__init__.py b/app/MRE/api/__init__.py index 065b810..e69de29 100644 --- a/app/MRE/api/__init__.py +++ b/app/MRE/api/__init__.py @@ -1,5 +0,0 @@ -from flask import Blueprint - -api_blueprint = Blueprint('api_blueprint', - __name__, - url_prefix='api') \ No newline at end of file diff --git a/app/MRE/api/routes.py b/app/MRE/api/routes.py index cefdcf4..3a748ce 100644 --- a/app/MRE/api/routes.py +++ b/app/MRE/api/routes.py @@ -2,9 +2,7 @@ from flask import current_app from flask_restx import Api, fields, apidoc, Model, Namespace, Resource from flask_restx.reqparse import RequestParser from MRE_module import compute_mre - -namespace = Namespace('mre', - description='Endpoints for MRE calculations') +from . import namespace MRE_request_parser = RequestParser() MRE_request_parser.add_argument('consumer_fico', diff --git a/app/__init__.py b/app/__init__.py index c616fa9..371dbd7 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,8 +1,8 @@ from flask import Flask import os from app.MRE import mre_blueprint -from logging import Logger, Formatter, getLogger, DEBUG, INFO, - FileHandler +from .api import blueprint as api_blueprint +from logging import Logger, Formatter, getLogger, DEBUG, INFO, FileHandler def load_config_from_environ(app: Flask) -> Flask: @@ -31,5 +31,6 @@ def create_app(app_name: str='MRE') -> Flask: app = init_logger(app) app.register_blueprint(mre_blueprint) + app.register_blueprint(api_blueprint) return app From 1d5016c176fab1cd24219068b6bbe2227153fafb Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 16:19:05 -0600 Subject: [PATCH 40/50] Refactoring of namespace and blueprints --- app/MRE/__init__.py | 5 ++--- app/MRE/api/__init__.py | 6 ++++++ app/__init__.py | 2 +- app/api.py | 12 ++++++++++++ 4 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 app/api.py diff --git a/app/MRE/__init__.py b/app/MRE/__init__.py index 6a1020d..146e692 100644 --- a/app/MRE/__init__.py +++ b/app/MRE/__init__.py @@ -1,4 +1,3 @@ -from flask_restx import Namespace +from flask import Blueprint -namespace = Namespace('mre', - description='Endpoints for MRE calculations') +blueprint = Blueprint('mre', __name__) diff --git a/app/MRE/api/__init__.py b/app/MRE/api/__init__.py index e69de29..c928b48 100644 --- a/app/MRE/api/__init__.py +++ b/app/MRE/api/__init__.py @@ -0,0 +1,6 @@ +from flask_restx import Namespace + +namespace = Namespace( + 'mre', + description='API endpoints for MRE calculations.' +) diff --git a/app/__init__.py b/app/__init__.py index 371dbd7..117de4e 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,6 +1,6 @@ from flask import Flask import os -from app.MRE import mre_blueprint +from app.MRE import blueprint as mre_blueprint from .api import blueprint as api_blueprint from logging import Logger, Formatter, getLogger, DEBUG, INFO, FileHandler diff --git a/app/api.py b/app/api.py new file mode 100644 index 0000000..7860b06 --- /dev/null +++ b/app/api.py @@ -0,0 +1,12 @@ +from flask_restx import Api +from flask import Blueprint +from .MRE.api import namespace as mre_namespace + +blueprint = Blueprint('api', __name__) +api = Api( + blueprint, + doc='/doc/', + title='Quarter API documentatoin' +) +api.add_namespace(mre_namespace, '/mre') + From 7c8fcf665e39c30683eef12b59f19ce3e7013b37 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 16:20:57 -0600 Subject: [PATCH 41/50] Update submodule --- api.py | 12 ------------ mre | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) delete mode 100644 api.py diff --git a/api.py b/api.py deleted file mode 100644 index e4af6e5..0000000 --- a/api.py +++ /dev/null @@ -1,12 +0,0 @@ -from flask_restx import api -from flask import Blueprint -from .MRE.api import namespace as mre_namespace - -blueprint = Blueprint('api', __name__) -api = Api( - blueprint, - doc='/doc/', - title='Quarter API documentatoin' -) -api.add_namespace(mre_namespace, '/mre') - diff --git a/mre b/mre index 27cfe80..ce94312 160000 --- a/mre +++ b/mre @@ -1 +1 @@ -Subproject commit 27cfe80cbdba084ab3a18fad6b4d288db30d9a32 +Subproject commit ce94312099a10d3cf273e42d4b315ac17a2f187f From b5e5c14239b9153efb7b0486d062ae3f5344982d Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 16:29:00 -0600 Subject: [PATCH 42/50] Added logging for http responses --- app/__init__.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/__init__.py b/app/__init__.py index 117de4e..443de60 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,4 +1,5 @@ -from flask import Flask +from flask import Flask, g, request +import time import os from app.MRE import blueprint as mre_blueprint from .api import blueprint as api_blueprint @@ -32,5 +33,15 @@ def create_app(app_name: str='MRE') -> Flask: app.register_blueprint(mre_blueprint) app.register_blueprint(api_blueprint) + + @app.before_request + def before_request(): + g.start = time.time() + + @app.after_request + def after_request(response): + request_time = time.time() - g.start + app.logger.info(f'HTTP request completed (method={request.method}, path={request.path}, request_time={request_time}, status_code={response.status_code}).') + return response return app From d0529495170045886ea9f742fc3c534cbf94a814 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 16:29:35 -0600 Subject: [PATCH 43/50] formatting --- app/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/__init__.py b/app/__init__.py index 443de60..69e68db 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -21,8 +21,9 @@ def init_logger(app: Flask, log_level=INFO) -> Flask: to_file = FileHandler(os.environ.get('MRE_LOG_PATH', f'./{app.name}.log')) to_file.setFormatter(formatter) logger.addHandler(to_file) - + app.logger = logger + return app From 9a53e2529ab0aeeb856292845cc1e9037e8425d6 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 16:33:05 -0600 Subject: [PATCH 44/50] Move the mre-module --- .gitmodules | 2 +- app/MRE/api/routes.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 3321215..32b3b59 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "mre"] - path = mre + path = app/mre_module url = http://10.0.50.3:3002/Quarter/MRE-module.git diff --git a/app/MRE/api/routes.py b/app/MRE/api/routes.py index 3a748ce..4d9e3ac 100644 --- a/app/MRE/api/routes.py +++ b/app/MRE/api/routes.py @@ -1,7 +1,7 @@ from flask import current_app from flask_restx import Api, fields, apidoc, Model, Namespace, Resource from flask_restx.reqparse import RequestParser -from MRE_module import compute_mre +from mre import compute_mre from . import namespace MRE_request_parser = RequestParser() From 4b541b62f0a819e70444f724ea903631ce5bdcf7 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 16:37:07 -0600 Subject: [PATCH 45/50] Resolved submodule relocation --- .gitmodules | 3 +++ app/MRE/api/routes.py | 2 +- app/mre_module | 1 + mre | 1 - 4 files changed, 5 insertions(+), 2 deletions(-) create mode 160000 app/mre_module delete mode 160000 mre diff --git a/.gitmodules b/.gitmodules index 32b3b59..3f5c14d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "mre"] path = app/mre_module url = http://10.0.50.3:3002/Quarter/MRE-module.git +[submodule "app/mre_module"] + path = app/mre_module + url = http://10.0.50.3:3002/Quarter/MRE.git diff --git a/app/MRE/api/routes.py b/app/MRE/api/routes.py index 4d9e3ac..22a894f 100644 --- a/app/MRE/api/routes.py +++ b/app/MRE/api/routes.py @@ -1,7 +1,7 @@ from flask import current_app from flask_restx import Api, fields, apidoc, Model, Namespace, Resource from flask_restx.reqparse import RequestParser -from mre import compute_mre +from app.mre_module import compute_mre from . import namespace MRE_request_parser = RequestParser() diff --git a/app/mre_module b/app/mre_module new file mode 160000 index 0000000..e8dfc65 --- /dev/null +++ b/app/mre_module @@ -0,0 +1 @@ +Subproject commit e8dfc65e6dd757689fcd4d49d932b3e5f863110e diff --git a/mre b/mre deleted file mode 160000 index ce94312..0000000 --- a/mre +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ce94312099a10d3cf273e42d4b315ac17a2f187f From b069331a1e72e566a53f1b3f56318b9203409f0a Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 17:31:14 -0600 Subject: [PATCH 46/50] Get routes working --- app/MRE/{api/routes.py => api.py} | 9 +++++++-- app/MRE/api/__init__.py | 6 ------ app/__init__.py | 17 ++++++++++++++++- run.py | 2 +- 4 files changed, 24 insertions(+), 10 deletions(-) rename app/MRE/{api/routes.py => api.py} (91%) delete mode 100644 app/MRE/api/__init__.py diff --git a/app/MRE/api/routes.py b/app/MRE/api.py similarity index 91% rename from app/MRE/api/routes.py rename to app/MRE/api.py index 22a894f..79c30b3 100644 --- a/app/MRE/api/routes.py +++ b/app/MRE/api.py @@ -2,7 +2,12 @@ from flask import current_app from flask_restx import Api, fields, apidoc, Model, Namespace, Resource from flask_restx.reqparse import RequestParser from app.mre_module import compute_mre -from . import namespace + +namespace = Namespace( + 'mre', + description='API endpoints for MRE calculations.' +) + MRE_request_parser = RequestParser() MRE_request_parser.add_argument('consumer_fico', @@ -18,7 +23,7 @@ MRE_request_parser.add_argument('down_payment', type=float, help='The down payment (as a percentage) for purchasing the home (float between [0, 1]).') -@namespace.route('compute_mre_from_home_price') +@namespace.route('/compute_mre') @namespace.doc(params={ 'consumer_fico': 'The consumer\'s fico score (int).', 'home_price': 'The price of the home (float).', diff --git a/app/MRE/api/__init__.py b/app/MRE/api/__init__.py deleted file mode 100644 index c928b48..0000000 --- a/app/MRE/api/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from flask_restx import Namespace - -namespace = Namespace( - 'mre', - description='API endpoints for MRE calculations.' -) diff --git a/app/__init__.py b/app/__init__.py index 69e68db..bed2b97 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,4 +1,4 @@ -from flask import Flask, g, request +from flask import Flask, g, request, url_for import time import os from app.MRE import blueprint as mre_blueprint @@ -45,4 +45,19 @@ def create_app(app_name: str='MRE') -> Flask: app.logger.info(f'HTTP request completed (method={request.method}, path={request.path}, request_time={request_time}, status_code={response.status_code}).') return response + def has_no_empty_params(rule): + defaults = rule.defaults if rule.defaults is not None else () + arguments = rule.arguments if rule.arguments is not None else () + return len(defaults) >= len(arguments) + + @app.route('/ping') + def ping(): + links = [] + for rule in app.url_map.iter_rules(): + if "GET" in rule.methods and has_no_empty_params(rule): + url = url_for(rule.endpoint, **(rule.defaults or {})) + links.append(url) + app.logger.info(f'Endpoint: {url}') + return {'links': links}, 200 + return app diff --git a/run.py b/run.py index 050634c..561e134 100644 --- a/run.py +++ b/run.py @@ -3,4 +3,4 @@ from app import create_app app = create_app() if __name__ == '__main__': - app.run(ssl_context='adhoc') \ No newline at end of file + app.run(port=8080, host='0.0.0.0') From 3a3d2e13e75703c4a4e53e93d13ff4304d6fe0b6 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 17:35:29 -0600 Subject: [PATCH 47/50] Get the correct gitmodule --- .gitmodules | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitmodules b/.gitmodules index 3f5c14d..1b8db4a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "mre"] - path = app/mre_module - url = http://10.0.50.3:3002/Quarter/MRE-module.git [submodule "app/mre_module"] path = app/mre_module - url = http://10.0.50.3:3002/Quarter/MRE.git + url = http://10.0.50.3:3002/Quarter/MRE-module.git From a49af93922f67661a266b8488966e60c6502b7e0 Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 17:42:47 -0600 Subject: [PATCH 48/50] Get the submodule checked out --- .gitmodules | 3 --- app/mre_module | 1 - 2 files changed, 4 deletions(-) delete mode 160000 app/mre_module diff --git a/.gitmodules b/.gitmodules index 1b8db4a..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "app/mre_module"] - path = app/mre_module - url = http://10.0.50.3:3002/Quarter/MRE-module.git diff --git a/app/mre_module b/app/mre_module deleted file mode 160000 index e8dfc65..0000000 --- a/app/mre_module +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e8dfc65e6dd757689fcd4d49d932b3e5f863110e From 45aec1a7f9b6d2b66d7fb15e03d11fc61c1f6cfc Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 17:52:57 -0600 Subject: [PATCH 49/50] Resolved last few issues with MRE calculator --- .gitmodules | 3 +++ app/MRE/api.py | 2 ++ app/__init__.py | 28 ++++++++++++++-------------- app/mre_module | 1 + 4 files changed, 20 insertions(+), 14 deletions(-) create mode 160000 app/mre_module diff --git a/.gitmodules b/.gitmodules index e69de29..1b8db4a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "app/mre_module"] + path = app/mre_module + url = http://10.0.50.3:3002/Quarter/MRE-module.git diff --git a/app/MRE/api.py b/app/MRE/api.py index 79c30b3..58b7c69 100644 --- a/app/MRE/api.py +++ b/app/MRE/api.py @@ -35,3 +35,5 @@ class ComputeMRE(Resource): args = MRE_request_parser.parse_args() mre = compute_mre(args.home_price, args.down_payment, args.consumer_fico) + return {'consumer_mre': mre} + diff --git a/app/__init__.py b/app/__init__.py index bed2b97..b4b84b5 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -45,19 +45,19 @@ def create_app(app_name: str='MRE') -> Flask: app.logger.info(f'HTTP request completed (method={request.method}, path={request.path}, request_time={request_time}, status_code={response.status_code}).') return response - def has_no_empty_params(rule): - defaults = rule.defaults if rule.defaults is not None else () - arguments = rule.arguments if rule.arguments is not None else () - return len(defaults) >= len(arguments) - - @app.route('/ping') - def ping(): - links = [] - for rule in app.url_map.iter_rules(): - if "GET" in rule.methods and has_no_empty_params(rule): - url = url_for(rule.endpoint, **(rule.defaults or {})) - links.append(url) - app.logger.info(f'Endpoint: {url}') - return {'links': links}, 200 +# def has_no_empty_params(rule): +# defaults = rule.defaults if rule.defaults is not None else () +# arguments = rule.arguments if rule.arguments is not None else () +# return len(defaults) >= len(arguments) +# +# @app.route('/ping') +# def ping(): +# links = [] +# for rule in app.url_map.iter_rules(): +# if "GET" in rule.methods and has_no_empty_params(rule): +# url = url_for(rule.endpoint, **(rule.defaults or {})) +# links.append(url) +# app.logger.info(f'Endpoint: {url}') +# return {'links': links}, 200 return app diff --git a/app/mre_module b/app/mre_module new file mode 160000 index 0000000..ce94312 --- /dev/null +++ b/app/mre_module @@ -0,0 +1 @@ +Subproject commit ce94312099a10d3cf273e42d4b315ac17a2f187f From c9dcad9adadb880be75274d8bf14b33dcdd3105b Mon Sep 17 00:00:00 2001 From: Chris Diesch Date: Sun, 6 Apr 2025 17:55:47 -0600 Subject: [PATCH 50/50] Add the prefix for the API --- app/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/__init__.py b/app/__init__.py index b4b84b5..01cab12 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -33,7 +33,7 @@ def create_app(app_name: str='MRE') -> Flask: app = init_logger(app) app.register_blueprint(mre_blueprint) - app.register_blueprint(api_blueprint) + app.register_blueprint(api_blueprint, url_prefix='/api') @app.before_request def before_request():