1
- # Import FastAPI class from fastapi package
2
- from fastapi import FastAPI
1
+ # Import necessary modules from FastAPI, Pydantic, and standard libraries
2
+ from fastapi import FastAPI , Path , HTTPException , Query
3
+ from fastapi .responses import JSONResponse
4
+ from pydantic import BaseModel , Field , computed_field
5
+ from typing import Annotated , Literal , Optional
6
+ import json
3
7
import uvicorn
4
- # Create FastAPI app instance with metadata
5
- app = FastAPI (
6
- title = "FastAPI" , # Title of the API
7
- description = "A FastAPI project managed with uv" , # Description of the API
8
- version = "1.0.0" # Version of the API
9
- )
10
-
11
- # Root endpoint: returns a welcome message
8
+
9
+ # Initialize the FastAPI app
10
+ app = FastAPI ()
11
+
12
+ # --------- Pydantic Models ---------
13
+ class Patient (BaseModel ):
14
+ """
15
+ Schema for patient information.
16
+ Includes fields for ID, name, city, age, gender, height, weight, and computed properties for BMI and health verdict.
17
+ """
18
+ id : Annotated [str , Field (..., description = 'ID of the patient' , examples = ['P001' ])]
19
+ name : Annotated [str , Field (..., description = 'Name of the patient' )]
20
+ city : Annotated [str , Field (..., description = 'City where the patient is living' )]
21
+ age : Annotated [int , Field (..., gt = 0 , lt = 120 , description = 'Age of the patient' )]
22
+ gender : Annotated [Literal ['male' , 'female' , 'others' ], Field (..., description = 'Gender of the patient' )]
23
+ height : Annotated [float , Field (..., gt = 0 , description = 'Height of the patient in meters' )]
24
+ weight : Annotated [float , Field (..., gt = 0 , description = 'Weight of the patient in kilograms' )]
25
+
26
+ @computed_field
27
+ @property
28
+ def bmi (self ) -> float :
29
+ """
30
+ Calculate Body Mass Index (BMI).
31
+ BMI = weight (kg) / (height (m))^2
32
+ """
33
+ return round (self .weight / (self .height ** 2 ), 2 )
34
+
35
+ @computed_field
36
+ @property
37
+ def verdict (self ) -> str :
38
+ """
39
+ Provide a health verdict based on BMI.
40
+ """
41
+ if self .bmi < 18.5 :
42
+ return 'Underweight'
43
+ elif self .bmi < 25 :
44
+ return 'Normal'
45
+ elif self .bmi < 30 :
46
+ return 'Overweight'
47
+ return 'Obese'
48
+
49
+ class PatientUpdate (BaseModel ):
50
+ """
51
+ Schema for updating patient information.
52
+ Allows partial updates with optional fields.
53
+ """
54
+ name : Annotated [Optional [str ], Field (default = None )]
55
+ city : Annotated [Optional [str ], Field (default = None )]
56
+ age : Annotated [Optional [int ], Field (default = None , gt = 0 , lt = 120 )]
57
+ gender : Annotated [Optional [Literal ['male' , 'female' , 'others' ]], Field (default = None )]
58
+ height : Annotated [Optional [float ], Field (default = None , gt = 0 )]
59
+ weight : Annotated [Optional [float ], Field (default = None , gt = 0 )]
60
+
61
+ # --------- Utility Functions ---------
62
+ def load_data () -> dict :
63
+ """
64
+ Load patient data from a JSON file.
65
+ """
66
+ with open ('patients.json' , 'r' ) as f :
67
+ return json .load (f )
68
+
69
+ def save_data (data : dict ) -> None :
70
+ """
71
+ Save patient data to a JSON file.
72
+ """
73
+ with open ('patients.json' , 'w' ) as f :
74
+ json .dump (data , f )
75
+
76
+ # --------- API Routes ---------
12
77
@app .get ("/" )
13
- def read_root ():
14
- return {"message" : "Welcome to FastAPI with uv!" }
78
+ def hello ():
79
+ """
80
+ Root endpoint: Welcome message for the API.
81
+ """
82
+ return {'message' : 'Patient Management System API' }
15
83
16
- # Endpoint 1: Greets the user by name
17
- @app .get ("/hello" )
18
- def greet_user (name : str ):
19
- return {"message" : f"Hello, { name } !" }
84
+ @app .get ('/about' )
85
+ def about ():
86
+ """
87
+ About endpoint: Provides information about the API.
88
+ """
89
+ return {'message' : 'A fully functional API to manage your patient records' }
20
90
21
- # Endpoint 2: Returns the square of a given number
22
- @app .get ("/square/{number}" )
23
- def square_number (number : int ):
24
- return {"number" : number , "square" : number * number }
91
+ @app .get ('/view' )
92
+ def view ():
93
+ """
94
+ View all patients in the database.
95
+ """
96
+ return load_data ()
25
97
98
+ @app .get ('/patient/{patient_id}' )
99
+ def view_patient (patient_id : str = Path (..., description = 'ID of the patient in the DB' , example = 'P001' )):
100
+ """
101
+ View details of a specific patient by ID.
102
+ """
103
+ data = load_data ()
104
+ if patient_id in data :
105
+ return data [patient_id ]
106
+ raise HTTPException (status_code = 404 , detail = 'Patient not found' )
26
107
27
- # Endpoint to get the length of the text
28
- if __name__ == "__main__" :
108
+ @app .get ('/sort' )
109
+ def sort_patients (
110
+ sort_by : str = Query (..., description = 'Sort on the basis of height, weight, or BMI' ),
111
+ order : str = Query ('asc' , description = 'Sort in ascending or descending order' )
112
+ ):
113
+ """
114
+ Sort patients by a specified field and order.
115
+ """
116
+ valid_fields = ['height' , 'weight' , 'bmi' ]
117
+ if sort_by not in valid_fields :
118
+ raise HTTPException (status_code = 400 , detail = f'Invalid field. Select from { valid_fields } ' )
119
+ if order not in ['asc' , 'desc' ]:
120
+ raise HTTPException (status_code = 400 , detail = 'Invalid order. Select between asc and desc' )
121
+
122
+ data = load_data ()
123
+ sorted_list = sorted (
124
+ data .values (),
125
+ key = lambda x : x .get (sort_by , 0 ),
126
+ reverse = (order == 'desc' )
127
+ )
128
+ return sorted_list
29
129
30
- # Run the FastAPI app using uvicorn server
31
- uvicorn .run (app , host = "127.0.0.1" , port = 8000 , reload = True )
130
+ @app .post ('/create' )
131
+ def create_patient (patient : Patient ):
132
+ """
133
+ Create a new patient record.
134
+ """
135
+ data = load_data ()
136
+ if patient .id in data :
137
+ raise HTTPException (status_code = 400 , detail = 'Patient already exists' )
138
+
139
+ data [patient .id ] = patient .model_dump (exclude = ['id' ])
140
+ save_data (data )
141
+ return JSONResponse (status_code = 201 , content = {'message' : 'Patient created successfully' })
142
+
143
+ @app .put ('/edit/{patient_id}' )
144
+ def update_patient (patient_id : str , patient_update : PatientUpdate ):
145
+ """
146
+ Update an existing patient's information.
147
+ """
148
+ data = load_data ()
149
+ if patient_id not in data :
150
+ raise HTTPException (status_code = 404 , detail = 'Patient not found' )
151
+
152
+ existing_patient_info = data [patient_id ]
153
+ updated_patient_info = patient_update .model_dump (exclude_unset = True )
154
+
155
+ for key , value in updated_patient_info .items ():
156
+ existing_patient_info [key ] = value
157
+
158
+ existing_patient_info ['id' ] = patient_id
159
+ patient_pydantic_obj = Patient (** existing_patient_info )
160
+ existing_patient_info = patient_pydantic_obj .model_dump (exclude = ['id' ])
161
+
162
+ data [patient_id ] = existing_patient_info
163
+ save_data (data )
164
+ return JSONResponse (status_code = 200 , content = {'message' : 'Patient updated successfully' })
165
+
166
+ @app .delete ('/delete/{patient_id}' )
167
+ def delete_patient (patient_id : str ):
168
+ """
169
+ Delete a patient record by ID.
170
+ """
171
+ data = load_data ()
172
+ if patient_id not in data :
173
+ raise HTTPException (status_code = 404 , detail = 'Patient not found' )
174
+
175
+ del data [patient_id ]
176
+ save_data (data )
177
+ return JSONResponse (status_code = 200 , content = {'message' : 'Patient deleted successfully' })
178
+
179
+ # --------- Run the App ---------
180
+ if __name__ == "__main__" :
181
+ """
182
+ Run the FastAPI app using uvicorn.
183
+ """
184
+ uvicorn .run (app , host = "127.0.0.1" , port = 8000 , reload = True )
185
+ # To run the app, use the command: uvicorn app:app --reload
0 commit comments