From 0685853bf52329816c67943fc3bf2e544e7b8d52 Mon Sep 17 00:00:00 2001 From: amir-esfahani Date: Sat, 6 Sep 2025 12:08:09 +0330 Subject: [PATCH 1/2] Fix NumPy deprecation warning by replacing np.float with float --- .DS_Store | Bin 0 -> 6148 bytes calculators.py | 2 +- gail/.DS_Store | Bin 0 -> 6148 bytes gail/gail.py | 6 +++--- gail/gail_api.py | 4 ++-- utils.py | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 .DS_Store create mode 100644 gail/.DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..2f40f05b5521f5fc5f9a60d7830caeafe02651a0 GIT binary patch literal 6148 zcmeHKu};H44E2>rq?Dl&3t~VK3v(CdP{J2fVl6bSTBJmY1PpAE_z+emRwg8v*!na4 z0MF-BnkZ=n3qr`2eDC73bI!XYz9S+tyqgY)`b6YI89QAxEyClh4e2<`F3{L}#B@b5 zEoesLhPN92A_Kg3J*?K4a=ORbt=4ax%m<@5OLIwk`)Ivqnnlqh$tLjdU(TLpdnX^K zo4lt#@Xi<2EciVokLJkpnvzd=Vk8tOp~f9CjN8f>3CDQo z{0l(|Cu3*Eeca5(oluOO9rnPwlL-a2RR)xSIs;pB+2{IyzW)4Q57H}TKpFT~3>YsO zMMFH2tF4WP<60X)FQF{#R|u9N=-5&WUoOS_P%E$p+yRCT3qe>Qx)HE6Xrm1LDg$4I C>TxRo literal 0 HcmV?d00001 diff --git a/calculators.py b/calculators.py index 17dab61..43c9fd7 100644 --- a/calculators.py +++ b/calculators.py @@ -82,7 +82,7 @@ def doGailCalc(): if request.json['ihyp'] == 0: rhyp = np.float64(0.93) elif request.json['ihyp'] == 1: - rhyp = np.float(1.82) + rhyp = float(1.82) fiveYearABS = calc.CalculateAbsoluteRisk(request.json['age'], request.json['age'] + 5, diff --git a/gail/.DS_Store b/gail/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..7721a6aa758aae9cd003cbb7ff79c8c9d4dbf06a GIT binary patch literal 6148 zcmeHKK~BOz6n%pvqOf4$!ey^OOo+=$YTOwU+#^&1CSV(pL^q~4@Dg6ZrE6E-#RItT z{h0~0R)}tl(f^Y_Z#sQ{`gj8!27u|z&s#tfK#eI_*<$la$bHFDRQyPTXzUy@`iL>c z5WNyx2Y#aivUYW@Ru3s|xpvF->rFJGJ4 z2h;&|VBvtg9|ERe=rMC>Uma}h5rA0bunqQ`(jf*OLywt5-eDM5N_3^hA2Ez8=Xl_G zp~uXjD~Iui596O~{0YU_r*rciEKv|8VvBzgVPK>VP`%ryMZV zs2g?ok^J6T`f#$>2Fx?2gzz$lixPJ1R!pvJ#k))!91o;J3_WHJIl|CC0@em?)PWy$ F;2ld*dNTk3 literal 0 HcmV?d00001 diff --git a/gail/gail.py b/gail/gail.py index e26edc3..3b736b8 100644 --- a/gail/gail.py +++ b/gail/gail.py @@ -952,7 +952,7 @@ def CalculateRisk(self, for k in range(108): # really is NumCovPattInGailModel / 2 # /* col2: indicator for age */ r8iTox2[k, 1] = np.float64(0.0) - r8iTox2[108 + k, 1] = np.float(1.0) + r8iTox2[108 + k, 1] = float(1.0) for j in range(1, 3): # note, since we always subtract 1 from j and k, we could zero base these # /* col3: age menarchy cate */ @@ -1200,7 +1200,7 @@ def CalculateRisk(self, if __name__ == '__main__': gailMod = GailRiskCalculator() gailMod.Initialize() - print gailMod.CalculateRisk(1, # riskIndex int [1 = Abs, 2 = Ave] + print(gailMod.CalculateRisk(1, # riskIndex int [1 = Abs, 2 = Ave] 35, # CurrentAge int [t1] 40, # ProjectionAge int [t2] 0, # AgeIndicator int [i0] @@ -1212,4 +1212,4 @@ def CalculateRisk(self, 1, # int [ihyp] HyperPlasia np.float64(1.82), # double [rhyp] RHyperPlasia 1 # irace int [race] - ) + )) diff --git a/gail/gail_api.py b/gail/gail_api.py index 4f0bc95..6f4506e 100644 --- a/gail/gail_api.py +++ b/gail/gail_api.py @@ -151,7 +151,7 @@ def validate_inputs(self, inputs): def run(self, inputs): errors = self.validate_inputs(inputs) - print len(errors.keys()) + print(len(errors.keys())) return_obj = {"code": 200} ra_obj = None # print "out_of_bounds" not in errors.keys() @@ -174,7 +174,7 @@ def do_calculation(self, inputs): if inputs['ihyp'] == 0: rhyp = np.float64(0.93) elif inputs['ihyp'] == 1: - rhyp = np.float(1.82) + rhyp = float(1.82) # The rhyp computed here doesn't get used by the following functions - SRM: 3-23-2017 # The age_indicator doesn't get used by the following either. - SRM: 3-23-2017 fiveYearABS = self.calc.CalculateAbsoluteRisk(inputs['age'], diff --git a/utils.py b/utils.py index 93c3695..2b70d28 100644 --- a/utils.py +++ b/utils.py @@ -3,8 +3,8 @@ def roundLikeNCI(np_float64): outval = np.float64(np_float64) * np.float64(1000.0) - if outval - outval.astype(np.int) >= np.float(0.5): + if outval - outval.astype(np.int) >= float(0.5): outval = outval.astype(np.int) + 1 else: outval = outval.astype(np.int) - return np.float(outval) / np.float(1000) + return float(outval) / float(1000) From 5e344fd8efae6b98bb8002abba6bea9708ef1aa5 Mon Sep 17 00:00:00 2001 From: amir-esfahani Date: Mon, 15 Sep 2025 16:23:54 +0330 Subject: [PATCH 2/2] minor fix --- Dockerfile | 49 +++++++++++++--- calculators.py | 137 +++++++++++++++++++++++++++++++++++++++++-- calculators_tests.py | 10 ++-- docker-compose.yml | 37 ++++++++++++ gail/__init__.py | 2 + gail/gail.py | 1 + gail/gail_api.py | 1 + 7 files changed, 220 insertions(+), 17 deletions(-) create mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile index 573d740..04ea67d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,44 @@ -FROM python:2.7-slim +# Use slim Python 3.10 image +FROM python:3.10.12-slim -RUN mkdir -p /usr/src/app -WORKDIR /usr/src/app -COPY requirements.txt /usr/src/app/ -RUN pip install --no-cache-dir -r requirements.txt -COPY . /usr/src/app +# Set Flask environment variables +ENV FLASK_ENV=development +ENV FLASK_APP=calculators.py +ENV FLASK_DEBUG=1 + +WORKDIR /app +RUN mkdir /uwsgi_log + +# Copy project files +COPY . /app + +# Install system dependencies for building packages (numpy, scipy, uwsgi) +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + gfortran \ + python3-dev \ + libatlas-base-dev \ + libopenblas-dev \ + && rm -rf /var/lib/apt/lists/* + +# Upgrade pip, setuptools, wheel +RUN pip install --upgrade pip setuptools wheel + +# Install prebuilt wheels for numpy/scipy to avoid build errors +RUN pip install numpy==1.25.2 scipy==1.11.1 + +# Install Flask, Jinja2, MarkupSafe compatible versions +RUN pip install Flask>=2.2.5 Jinja2>=3.1.2 MarkupSafe>=2.1.2 + +# Install remaining dependencies from requirements.txt excluding numpy/scipy/Flask/Jinja2/MarkupSafe +RUN pip install --use-pep517 --no-build-isolation $(grep -vE "numpy|scipy|Flask|Jinja2|MarkupSafe" requirements.txt) + +# Install uwsgi if needed +RUN apt-get update && apt-get install -y uwsgi \ + && rm -rf /var/lib/apt/lists/* + +# Expose Flask port EXPOSE 5000 -CMD ["gunicorn", "--workers=4", "-b 0.0.0.0:5000","wsgi:app"] + +# Run the Flask app +CMD ["flask", "run", "--host=0.0.0.0"] diff --git a/calculators.py b/calculators.py index 43c9fd7..33dfb2c 100644 --- a/calculators.py +++ b/calculators.py @@ -1,10 +1,131 @@ from flask import Flask, jsonify, request from flask import abort, render_template from flask_bootstrap import Bootstrap -from gail import gail, gail_api + +# DEBUG: Add debugging information before imports +import os +import sys + +print("=== DEBUGGING IMPORT ISSUES ===") +print("Current working directory:", os.getcwd()) +print("Python path:") +for path in sys.path: + print(f" {path}") + +print("\nContents of current directory:") +for item in os.listdir('.'): + print(f" {item}") + +print("\nLooking for gail directory...") +if os.path.exists('./gail'): + print("✓ gail directory found at ./gail") + print("Contents of gail directory:") + for item in os.listdir('./gail'): + print(f" {item}") + + # Check if __init__.py exists + if os.path.exists('./gail/__init__.py'): + print("✓ __init__.py found in gail directory") + else: + print("✗ __init__.py NOT found in gail directory") + + # Check for the specific files we need + if os.path.exists('./gail/gail.py'): + print("✓ gail.py found") + else: + print("✗ gail.py NOT found") + + if os.path.exists('./gail/gail_api.py'): + print("✓ gail_api.py found") + else: + print("✗ gail_api.py NOT found") + +else: + print("✗ gail directory NOT found at ./gail") + +# Also check absolute path +if os.path.exists('/app/gail'): + print("✓ gail directory found at /app/gail") + print("Contents of /app/gail directory:") + for item in os.listdir('/app/gail'): + print(f" {item}") +else: + print("✗ gail directory NOT found at /app/gail") + +print("=== END DEBUGGING INFO ===\n") + +# Try different import approaches +print("=== TESTING IMPORTS ===") + +try: + print("Attempting: import gail.gail_api as gail_api") + import gail.gail_api as gail_api + print("✓ SUCCESS: gail.gail_api imported successfully") +except ImportError as e: + print(f"✗ FAILED: {e}") + + # Try alternative approach + try: + print("Attempting: sys.path.insert approach") + gail_path = os.path.join(os.path.dirname(__file__), 'gail') + if gail_path not in sys.path: + sys.path.insert(0, gail_path) + import gail_api + print("✓ SUCCESS: gail_api imported with sys.path.insert") + except ImportError as e2: + print(f"✗ FAILED: {e2}") + + # Try direct file import + try: + print("Attempting: direct file path import") + import importlib.util + spec = importlib.util.spec_from_file_location("gail_api", "./gail/gail_api.py") + gail_api = importlib.util.module_from_spec(spec) + spec.loader.exec_module(gail_api) + print("✓ SUCCESS: gail_api imported directly from file") + except Exception as e3: + print(f"✗ FAILED: {e3}") + print("All import attempts failed!") + gail_api = None + +try: + print("Attempting: from gail.gail import GailRiskCalculator") + from gail.gail import GailRiskCalculator + print("✓ SUCCESS: GailRiskCalculator imported successfully") +except ImportError as e: + print(f"✗ FAILED: {e}") + + # Try alternative approach + try: + print("Attempting: GailRiskCalculator with sys.path.insert") + if 'gail' not in sys.modules: + gail_path = os.path.join(os.path.dirname(__file__), 'gail') + if gail_path not in sys.path: + sys.path.insert(0, gail_path) + import gail + from gail import GailRiskCalculator + print("✓ SUCCESS: GailRiskCalculator imported with sys.path.insert") + except ImportError as e2: + print(f"✗ FAILED: {e2}") + + # Try direct file import + try: + print("Attempting: GailRiskCalculator direct file path import") + import importlib.util + spec = importlib.util.spec_from_file_location("gail", "./gail/gail.py") + gail_module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(gail_module) + GailRiskCalculator = gail_module.GailRiskCalculator + print("✓ SUCCESS: GailRiskCalculator imported directly from file") + except Exception as e3: + print(f"✗ FAILED: {e3}") + print("All GailRiskCalculator import attempts failed!") + GailRiskCalculator = None + import numpy as np from RiskAssessment import BasicRiskAssessment as assessment +print("=== END TESTING IMPORTS ===\n") app = Flask(__name__) Bootstrap(app) @@ -12,7 +133,11 @@ def get_calculator(calculator_name): if calculator_name.lower() == "gail": - return gail_api.GailAPI() + if gail_api is not None: + return gail_api.GailAPI() + else: + print("ERROR: gail_api is None, cannot create GailAPI instance") + return None return None @app.route('/') @@ -53,7 +178,6 @@ def getFieldDescrptions(calculator): # gailapi = gail_api.GailAPI() return render_template("api_doc.html", name=calc.get_name(), version="2.0", apidef=calc.get_input_fields_json()) - @app.route('/api/v1.0/gail', methods=['POST']) def doGailCalc(): """ @@ -72,7 +196,10 @@ def doGailCalc(): abort(400) else: # print request.json - calc = gail.GailRiskCalculator() + if GailRiskCalculator is None: + return jsonify({"error": "GailRiskCalculator not available due to import issues"}), 500 + + calc = GailRiskCalculator() calc.Initialize() # TODO: look into moving this into the instantion of the object # TODO: move the rhyp and age indicator logic into the gail calculator, all it's logic should live in there. @@ -136,4 +263,4 @@ def doGailCalc(): if __name__ == "__main__": - app.run(debug=False) + app.run(debug=False, host="0.0.0.0", port=5070) \ No newline at end of file diff --git a/calculators_tests.py b/calculators_tests.py index caff2ba..fb6f4a8 100644 --- a/calculators_tests.py +++ b/calculators_tests.py @@ -85,7 +85,7 @@ def get_NCI(self, utils.roundLikeNCI(cgvLifetimeRiskAVE)) def test_bad_calc_name(self): - print "Testing Unknown Calculator Name (For v2.0)" + print("Testing Unknown Calculator Name (For v2.0)") # test getting the docs for a bad calc rv = self.app.get('/api/v2.0/bad_calc') assert rv.status_code == 501 @@ -95,7 +95,7 @@ def test_bad_calc_name(self): assert rv.status_code == 501 def test_gail_calc_doc(self): - print "Testing GAIL Doc URLs (For v2.0)" + print("Testing GAIL Doc URLs (For v2.0)") rv = self.app.get('/api/v2.0/gail') assert rv.status_code == 200 assert "VisExcell API For GAIL - version 2.0" in rv.get_data() @@ -104,7 +104,7 @@ def test_gail_calc_doc(self): assert rv.mimetype == "application/json" def test_gail_calc_post(self): - print "Testing getting GAIL results for (v2.0)" + print("Testing getting GAIL results for (v2.0)") post_data = { "age": 48, "menarch_age": 2, @@ -132,7 +132,7 @@ def test_gail_calc_post(self): # TODO: Add bad post data to check error responses def test_unknown_biopsies(self): - print "Testing for unknown biopsy bug (using v1.0)" + print("Testing for unknown biopsy bug (using v1.0)") post_data = { "age": 40, "menarch_age": 2, @@ -176,7 +176,7 @@ def test_unknown_biopsies(self): self.assertEqual((fyr, fyra, ltr, ltra),(nci_fyr,nci_fyra,nci_ltr,nci_ltra), "Inputs: %r" % post_data) def test_calculations_valid_inputs(self): - print "Testing for valid inputs (using v1.0)" + print("Testing for valid inputs (using v1.0)") test_inputs = [{"age": 42, "menarch_age": 2, "live_birth_age": 0, "ever_had_biopsy": 0, "num_biopsy": 0, "first_deg_relatives": 0, "ihyp": 99, "race": 1}, {"age": 50, "menarch_age": 2, "live_birth_age": 0, "ever_had_biopsy": 0, "num_biopsy": 0, "first_deg_relatives": 0, "ihyp": 99, "race": 1}, {"age": 68, "menarch_age": 2, "live_birth_age": 0, "ever_had_biopsy": 0, "num_biopsy": 0, "first_deg_relatives": 0, "ihyp": 99, "race": 1}, diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..4476830 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,37 @@ +version: '3.8' + +services: + riskmodels-api: + container_name: riskmodels-api + build: . + restart: always +# command: uwsgi --http 0.0.0.0:5000 --master -p 1 -w app:app + environment: + SECRET_KEY: f502c8e1-f4b2-486a-aeb4-85e427a46613 + JWT_SECRET_KEY: RbXAh3yIaUBJfDh=wtYs-cTPK?EnwG!JGrFgGhYOdqsy/!h2RXZ1PPaAkIGmrA7F + CACHE_ENABLED: 1 + CACHE_TYPE: RedisCache + # DATABASE_TYPE: postgresql + # DATABASE_HOST: landa-db # Connect to root-level Postgres + # DATABASE_PORT: 5432 + # DATABASE_USER: postgres + # DATABASE_PASS: gofuckYourselfAUG!2024 + # DATABASE_DB: riskmodels-db # Project-specific DB in shared container + FLASK_DEBUG: 1 + FLASK_CONFIG: development + FLASK_APP: calculators.py + LISTEN_HOST: 0.0.0.0 + LISTEN_PORT: 5000 + LOG_LEVEL: INFO + UPDATE_DAYS_TOKEN: zdMBOJi9f4WlkHuPai4RRwkM6pryEp + FILE_UPLOAD_TOKEN: P5boWPJXxrRDlGvtBuP7a5GOoXY8wo + ports: + - "5030:5000" + networks: + - landa_network + +# Optional Redis section is removed since you don’t use it yet + +networks: + landa_network: + external: true \ No newline at end of file diff --git a/gail/__init__.py b/gail/__init__.py index e69de29..10202fd 100644 --- a/gail/__init__.py +++ b/gail/__init__.py @@ -0,0 +1,2 @@ +from . import gail +from . import gail_api \ No newline at end of file diff --git a/gail/gail.py b/gail/gail.py index 3b736b8..81970af 100644 --- a/gail/gail.py +++ b/gail/gail.py @@ -1,3 +1,4 @@ +# Gail model risk calculator import numpy as np class GailRiskCalculator: diff --git a/gail/gail_api.py b/gail/gail_api.py index 6f4506e..2d302b2 100644 --- a/gail/gail_api.py +++ b/gail/gail_api.py @@ -1,3 +1,4 @@ +# Gail model risk calculator API import gail as gail_calc from RiskAssessment import BasicRiskAssessment as assessment import numpy as np