diff --git a/.gitignore b/.gitignore index 4ecac7d..4875ad3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ -.ipynb_checkpoints -__pycache__ -.DS_Store \ No newline at end of file +venv +*.pyc +staticfiles +.env +db.sqlite3 +getting-started/* \ No newline at end of file diff --git a/Procfile b/Procfile index 5593837..36fc594 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web gunicorn run:app \ No newline at end of file +web: uvicorn src.main:app --host=0.0.0.0 --port=${PORT:-5000} \ No newline at end of file diff --git a/README.md b/README.md index 3052507..3b12464 100644 --- a/README.md +++ b/README.md @@ -1,16 +1 @@ -# Heroku 部署機器學習 API -此範例使用鳶尾花朵資料集進行 `XGBoost` 分類器模型訓練。將模型儲存起來,並使用 Flask 建置 API 介面提供輸入值預測。最後並部署到 Heroku 雲端伺服器平台。 - -## Getting Started -### Fork Project -你可以直接 Fork 此專案到你自己的 GitHub 帳號中,並採用 Heroku 連動 GitHub 專案的方式部署此 API 範例。 - -### Fork 專案到自己的 GitHub 帳號中 - -![](https://i.imgur.com/CEURaEi.png) - -### 建立一個 Heroku 帳號並新增一個專案 - -[傳送門](https://dashboard.heroku.com/apps) - -![](https://i.imgur.com/QYjg4JR.png) \ No newline at end of file +TEST \ No newline at end of file diff --git a/app.json b/app.json new file mode 100644 index 0000000..9315228 --- /dev/null +++ b/app.json @@ -0,0 +1,14 @@ +{ + "name": "Start on Heroku: Python", + "description": "A barebones Python app, which can easily be deployed to Heroku.", + "image": "heroku/python", + "repository": "https://github.com/Hedde/fastapi-heroku-test", + "keywords": ["python", "fastapi" ], + "env": { + }, + "environments": { + "test": { + "scripts": {} + } + } + } \ No newline at end of file diff --git a/app/__init__.py b/app/__init__.py deleted file mode 100644 index c8f3a7d..0000000 --- a/app/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: UTF-8 -*- -import app.model as model -import numpy as np - -from flask import Flask, request, jsonify -from flask_cors import CORS - -app = Flask(__name__) -CORS(app) - -@app.route('/test', methods=['GET']) -def getResult(): - input = np.array([[5.5, 2.4, 2.7, 1.]]) - result = model.predict(input) - return jsonify({'result': str(result)}) - -@app.route('/predict', methods=['POST']) -def postInput(): - # 取得前端傳過來的數值 - insertValues = request.get_json() - x1=insertValues['sepalLengthCm'] - x2=insertValues['sepalWidthCm'] - x3=insertValues['petalLengthCm'] - x4=insertValues['petalWidthCm'] - input = np.array([[x1, x2, x3, x4]]) - # 進行預測 - result = model.predict(input) - - return jsonify({'result': str(result)}) \ No newline at end of file diff --git a/app/model.py b/app/model.py deleted file mode 100644 index b7c6f93..0000000 --- a/app/model.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: UTF-8 -*- -import pickle -import gzip - -# 載入Model -with gzip.open('app/model/xgboost-iris.pgz', 'r') as f: - xgboostModel = pickle.load(f) - - -def predict(input): - pred=xgboostModel.predict(input)[0] - print(pred) - return pred \ No newline at end of file diff --git a/app/model/xgboost-iris.pgz b/app/model/xgboost-iris.pgz deleted file mode 100644 index 886cb75..0000000 Binary files a/app/model/xgboost-iris.pgz and /dev/null differ diff --git a/requirements.txt b/requirements.txt index 5f1a947..878f5d0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,11 @@ -Flask -Flask-Cors +Click +fastapi gunicorn -numpy -scikit-learn==0.24.2 -xgboost==0.90 \ No newline at end of file +h11 +httptools +pydantic +starlette +uvicorn +uvloop +websockets +requests diff --git a/run.py b/run.py deleted file mode 100644 index 1967b5e..0000000 --- a/run.py +++ /dev/null @@ -1,9 +0,0 @@ -# -*- coding: UTF-8 -*- -from app import app - -@app.route('/') -def index(): - return 'Flask API started' - -if __name__ == '__main__': - app.run(host='0.0.0.0', port=3000, debug=False) \ No newline at end of file diff --git a/runtime.txt b/runtime.txt new file mode 100644 index 0000000..cd6f130 --- /dev/null +++ b/runtime.txt @@ -0,0 +1 @@ +python-3.11.1 \ No newline at end of file diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..dfff851 --- /dev/null +++ b/src/main.py @@ -0,0 +1,346 @@ +from fastapi import FastAPI +from pydantic import BaseModel +from typing import Optional +from fastapi.encoders import jsonable_encoder +from fastapi.responses import JSONResponse +import requests +import re +import json + +app = FastAPI() +def deque(userid): + sendData = {'userid': "multiUser"} + response = requests.post('https://us-central1-fortesting-c54ba.cloudfunctions.net/post/accessflag', data=sendData) + getData = response.json() + userQueue = eval(getData['result']['flagforuser']) + + userQueue.remove(userid) + postData = {"userid": "multiUser", + "flagforuser": json.dumps(userQueue), + "flagforsymptom":getData['result']['flagforsymptom'], + "flagfordaily":getData['result']['flagfordaily']} + response = requests.post('https://us-central1-fortesting-c54ba.cloudfunctions.net/post/flag', data=postData) + print(response) + +class multiUserModel(BaseModel): + userID: str +@app.post("/testMultiUser") +def postForMultiUser(multiUser: multiUserModel): + userid = multiUser.userID + sendData = {'userid': "multiUser"} + response = requests.post('https://us-central1-fortesting-c54ba.cloudfunctions.net/post/accessflag', data=sendData) + getData = response.json() + userQueue = eval(getData['result']['flagforuser']) + + userQueue.append(userid) + postData = {"userid": "multiUser", + "flagforuser": json.dumps(userQueue), + "flagforsymptom":getData['result']['flagforsymptom'], + "flagfordaily":getData['result']['flagfordaily']} + response = requests.post('https://us-central1-fortesting-c54ba.cloudfunctions.net/post/flag', data=postData) + if response.status_code == 200: + return "OK" + else: + return "fail" + + +@app.get("/basicInfo") +def get_for_basic_info(): + + sendData = {'userid': "multiUser"} + response = requests.post('https://us-central1-fortesting-c54ba.cloudfunctions.net/post/accessflag', data=sendData) + getData = response.json() + userQueue = eval(getData['result']['flagforuser']) + userid = userQueue[0] + + # Prepare the data + data = {'userid': userid} + print("user",userid) + # Send a POST request + response = requests.post('https://us-central1-fortesting-c54ba.cloudfunctions.net/post/accessbasic', data=data) + # Extract data from the response + if response.status_code == 200: + user_data = response.json() + else: + user_data = {} + + # Prepare the final result + result = { + "userID":userid, + "result": user_data['result'] + } + + deque(userid) + ################ dequeue + + # sendData = {'userid': "multiUser"} + # response = requests.post('https://us-central1-fortesting-c54ba.cloudfunctions.net/post/accessflag', data=sendData) + # getData = response.json() + # userQueue = eval(getData['result']['flagforuser']) + + # userQueue.pop(0) + # postData = {"userid": "multiUser", + # "flagforuser": json.dumps(userQueue), + # "flagforsymptom":getData['result']['flagforsymptom'], + # "flagfordaily":getData['result']['flagfordaily']} + # response = requests.post('https://us-central1-fortesting-c54ba.cloudfunctions.net/post/flag', data=postData) + # print(response) + + return jsonable_encoder(result) + + +class SymptomsModel(BaseModel): + userID: str +@app.post("/symptoms") +def post_for_symptoms(symptoms: SymptomsModel): + + userid = symptoms.userID + # Prepare the data + data = {'userid': userid} + # Send a POST request + response = requests.post('https://us-central1-fortesting-c54ba.cloudfunctions.net/post/accesssymptoms', data=data) + # Extract data from the response + if response.status_code == 200: + user_data = response.json() + else: + user_data = {} + # Prepare the final result + result = user_data['result'] + + # Prepare the data + data1 = {'userid': userid, + 'returns': "", + 'diagnosis': ""} + # Send a POST request + response = requests.post('https://us-central1-fortesting-c54ba.cloudfunctions.net/post/diagnosis', data=data1) + if response.status_code == 200: + data1 = {} + else: + return "post_diagnoss_error" + + # Prepare the data + data2 = { + 'userid': userid, + "urineprotein": "- ", + "urineob": "-", + "urineglucose": "-", + "bloodhb": "15 g/dL", + "bloodht": "45 %", + "bloodplt": "120 x 10^3/µL", + "bloodpressure": "舒張壓:89 mmHg, 收縮壓:132 mmHg", + "bloodrbc": "500 x 10^6/µL", + "bloodwbc": "12.3 x 10^3/µL", + "cholesterol": "150 mg/dL", + "hbeag": "-", + "hbsab": "-", + "hbsag": "-", + "kidneybun": "15 mg/dL", + "liversgot": "25 U/L", + "liversgpt": "30 U/L", + "kidneycre": "0.7 mg/dL" + } + # Send a POST request + response = requests.post('https://us-central1-fortesting-c54ba.cloudfunctions.net/post/testinfo', data=data2) + if response.status_code == 200: + data2 = {} + else: + return "post_testinfo_error" + + + + return jsonable_encoder(result) + + + +class ClinicModel(BaseModel): + userID: str +@app.post("/forClinic") +def post_for_clinic(clinic: ClinicModel): + userid = clinic.userID + # Prepare the data + data = {'userid': userid} + # Send a POST request + response = requests.post('https://us-central1-fortesting-c54ba.cloudfunctions.net/post/accessdiagnosis', data=data) + # Extract data from the response + if response.status_code == 200: + user_data = response.json() + user_data["result"]["userID"]=userid + result = { + "result":user_data["result"] + } + + return jsonable_encoder(result) + else: + user_data = {} + return "R" + # Prepare the final result + + + +class RecordsModel(BaseModel): + userID: str +@app.post("/records") +def post_for_records(records: RecordsModel): + userid = records.userID + + # Prepare the data + data = {'userid': userid} + + # Send a POST request + response = requests.post('https://us-central1-fortesting-c54ba.cloudfunctions.net/post/accesstestinfo', data=data) + # Extract data from the response + if response.status_code == 200: + user_data = response.json() + else: + user_data = {} + + # Prepare the final result + user_data['result']['userID']=userid + + result = { + "result":user_data['result'] + } + + + return jsonable_encoder(result) + + +class NextStepModel(BaseModel): + nextStep: str +@app.post("/nextStep") +def post_next_step(next_step: NextStepModel): + return "OK" + +class waitModel(BaseModel): + userID : str + +@app.post("/wait") +def wait(wait: waitModel): + + + + return jsonable_encoder({ + "result": { + "userID": wait.userID + } + }) + +class updateDataModel(BaseModel): + clinicData: str + userID: str + +@app.post("/updateClinic") +def post_for_update_clinic(updateData: updateDataModel): + + data = {'userid': updateData.userID} + # Send a POST request + response = requests.post('https://us-central1-fortesting-c54ba.cloudfunctions.net/post/accessbasic', data=data) + # Extract data from the response + if response.status_code == 200: + user_data = response.json() + else: + user_data = {} + return updateData.userID + info_key_mapping = {"姓名":"name", "性別":"gender", "年齡": "age", "身高": "height", "體重": "weight", "家族病史": "family", "個人病史": "record"} + + send_user_data = {} + + for i in user_data['result']: + send_user_data[info_key_mapping[i]] = user_data['result'][i] + send_user_data['userid'] = updateData.userID + send_user_data['record'] = updateData.clinicData + + response2 = requests.post('https://us-central1-fortesting-c54ba.cloudfunctions.net/post/basic', data=send_user_data) + # Extract data from the response + if response.status_code == 200: + return send_user_data + else: + user_data = {} + return "update fail" + +class isReturnModel(BaseModel): + message: str + userID: str + +@app.post("/isReturn") +def post_for_isReturn(isReturn: isReturnModel): + data = isReturn.message + userid = isReturn.userID + + score = [int(i) for i in re.findall(r'\d+', data)] + # deque(userid) + + if score[0] > 7: + result = {'message':'請您立即回診', + 'userID': userid} + requests.post('https://i-care-te-st-21770a966fd0.herokuapp.com/external_api', json=result) + + postData = {"userid": userid, + "flagforuser": "", + "flagforsymptom":"2", + "flagfordaily":"0"} + response = requests.post('https://us-central1-fortesting-c54ba.cloudfunctions.net/post/flag', data=postData) + print(response) + return "1" + else: + result = {'message':'您的狀況良好!請繼續保持!OvO', + 'userID': userid} + requests.post('https://i-care-te-st-21770a966fd0.herokuapp.com/external_api', json=result) + postData = {"userid": userid, + "flagforuser": "", + "flagforsymptom":"2", + "flagfordaily":"0"} + response = requests.post('https://us-central1-fortesting-c54ba.cloudfunctions.net/post/flag', data=postData) + print(response) + return "0" + + +class messageModel(BaseModel): + userID: str +@app.post("/messages") +def post_for_records(data_messages: messageModel): + userid = data_messages.userID + # Prepare the data + data = {'userid': userid} + + # Send a POST request + response = requests.post('https://us-central1-fortesting-c54ba.cloudfunctions.net/post/accessmessage', data=data) + # Extract data from the response + if response.status_code == 200: + user_data = response.json() + else: + user_data = {} + + # Prepare the final result + user_data['result']['userID']=userid + + result = { + "result":user_data['result'] + } + + + return jsonable_encoder(result) + + + + + +# class changeFlagModel(BaseModel): +# userid: str +# flagforuser: str +# flagforsymptom: str +# flagfordaily: str + +# @app.post("/changeFlag") +# def changeFlag(flagForChange: changeFlagModel): +# userid = flagForChange.userid +# flagforuser=flagForChange.flagforuser +# flagforsymptom=flagForChange.flagforsymptom +# flagfordaily=flagForChange.flagfordaily + +# postData = {"userid": userid, +# "flagforuser": flagforuser, +# "flagforsymptom":flagforsymptom, +# "flagfordaily":flagfordaily} +# response = requests.post('https://us-central1-fortesting-c54ba.cloudfunctions.net/post/flag', data=postData) +# print(response) \ No newline at end of file