vumichien commited on
Commit
cf14e32
·
1 Parent(s): a11f31d

Upload Flask app

Browse files
Files changed (4) hide show
  1. Dockerfile +4 -9
  2. app.py +60 -98
  3. requirements.txt +4 -6
  4. templates/map.html +92 -0
Dockerfile CHANGED
@@ -3,16 +3,11 @@ FROM python:3.9-slim
3
 
4
  # Thiết lập biến môi trường để Python không tạo các file pyc
5
  ENV PYTHONDONTWRITEBYTECODE=1
6
-
7
- # Thiết lập biến môi trường để Python không buffer output
8
  ENV PYTHONUNBUFFERED=1
9
 
10
- # Tạo và đặt thư mục làm việc cho ứng dụng Flask
11
  WORKDIR /app
12
 
13
- # Tạo thư mục instance với quyền truy cập phù hợp
14
- RUN mkdir -p /app/instance && chmod -R 755 /app/instance
15
-
16
  # Sao chép file requirements.txt vào container
17
  COPY requirements.txt /app/
18
 
@@ -22,8 +17,8 @@ RUN pip install --no-cache-dir -r requirements.txt
22
  # Sao chép toàn bộ mã nguồn ứng dụng vào container
23
  COPY . /app/
24
 
25
- # Expose port 5000 for Flask
26
  EXPOSE 5000
27
 
28
- # Chạy ứng dụng Flask bằng Gunicorn
29
- CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "app:app"]
 
3
 
4
  # Thiết lập biến môi trường để Python không tạo các file pyc
5
  ENV PYTHONDONTWRITEBYTECODE=1
 
 
6
  ENV PYTHONUNBUFFERED=1
7
 
8
+ # Tạo và đặt thư mục làm việc cho ứng dụng FastAPI
9
  WORKDIR /app
10
 
 
 
 
11
  # Sao chép file requirements.txt vào container
12
  COPY requirements.txt /app/
13
 
 
17
  # Sao chép toàn bộ mã nguồn ứng dụng vào container
18
  COPY . /app/
19
 
20
+ # Expose port 5000 for FastAPI
21
  EXPOSE 5000
22
 
23
+ # Chạy ứng dụng FastAPI bằng Uvicorn
24
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "5000"]
app.py CHANGED
@@ -1,33 +1,44 @@
1
- from flask import Flask, request, jsonify, render_template
2
- from models import db, WifiSignal
 
 
 
 
 
3
  from datetime import datetime, timedelta
4
  import random
5
  import folium
6
  from folium.plugins import MarkerCluster
7
- from flasgger import Swagger
8
-
9
- # Chỉ định đường dẫn instance_path hợp lệ
10
- app = Flask(__name__, instance_path='/tmp')
11
-
12
- app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'
13
- app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
14
-
15
- # Khởi tạo Swagger
16
- swagger = Swagger(app)
17
-
18
- db.init_app(app)
19
-
20
- @app.route('/api/generate-data', methods=['POST'])
21
- def generate_data():
22
- """
23
- Generate demo data.
24
- ---
25
- tags:
26
- - Data Management
27
- responses:
28
- 201:
29
- description: Demo data generated successfully
30
- """
 
 
 
 
 
 
31
  base_latitude = 35.6837
32
  base_longitude = 139.6805
33
  start_date = datetime(2024, 8, 1)
@@ -50,82 +61,38 @@ def generate_data():
50
  signal_strength=random_signal_strength
51
  )
52
 
53
- db.session.add(wifi_signal)
54
-
55
- db.session.commit()
56
- return jsonify({"message": "Demo data generated successfully"}), 201
57
-
58
- @app.route('/api/delete-data', methods=['DELETE'])
59
- def delete_data():
60
- """
61
- Delete all data in the database.
62
- ---
63
- tags:
64
- - Data Management
65
- responses:
66
- 200:
67
- description: All data deleted successfully
68
- 500:
69
- description: An error occurred
70
- """
71
  try:
72
- num_rows_deleted = db.session.query(WifiSignal).delete()
73
- db.session.commit()
74
- return jsonify({"message": f"Deleted {num_rows_deleted} rows from the database."}), 200
75
  except Exception as e:
76
- db.session.rollback()
77
- return jsonify({"message": f"An error occurred: {str(e)}"}), 500
78
-
79
- @app.route('/api/upload', methods=['POST'])
80
- def upload_data():
81
- """
82
- Upload WiFi signal data.
83
- ---
84
- tags:
85
- - Data Management
86
- parameters:
87
- - name: body
88
- in: body
89
- required: true
90
- schema:
91
- type: object
92
- properties:
93
- latitude:
94
- type: number
95
- example: 35.6895
96
- longitude:
97
- type: number
98
- example: 139.6917
99
- timestamp:
100
- type: string
101
- example: "2024-08-29 14:30:00"
102
- signal_strength:
103
- type: integer
104
- example: 42
105
- responses:
106
- 201:
107
- description: Data uploaded successfully
108
- 400:
109
- description: Invalid input
110
- """
111
- data = request.json
112
  latitude = data.get('latitude')
113
  longitude = data.get('longitude')
114
  timestamp = datetime.strptime(data.get('timestamp'), '%Y-%m-%d %H:%M:%S')
115
  signal_strength = data.get('signal_strength')
116
 
117
  wifi_signal = WifiSignal(latitude=latitude, longitude=longitude, timestamp=timestamp, signal_strength=signal_strength)
118
- db.session.add(wifi_signal)
119
- db.session.commit()
120
 
121
- return jsonify({"message": "Data uploaded successfully"}), 201
122
-
123
- @app.route('/', methods=['GET'])
124
- def show_map():
125
- start_date = request.args.get('start_date')
126
- end_date = request.args.get('end_date')
127
 
128
- query = WifiSignal.query
 
 
129
 
130
  if start_date and end_date:
131
  start_datetime = datetime.strptime(start_date, '%Y-%m-%d')
@@ -159,9 +126,4 @@ def show_map():
159
  ).add_to(marker_cluster)
160
 
161
  map_html = m._repr_html_()
162
- return render_template('map.html', map_html=map_html)
163
-
164
- if __name__ == '__main__':
165
- with app.app_context():
166
- db.create_all()
167
- app.run(debug=True)
 
1
+ from fastapi import FastAPI, HTTPException, Depends, Request
2
+ from fastapi.responses import HTMLResponse
3
+ from fastapi.templating import Jinja2Templates
4
+ from sqlalchemy.orm import Session
5
+ from sqlalchemy import create_engine, Column, Integer, Float, String, DateTime
6
+ from sqlalchemy.ext.declarative import declarative_base
7
+ from sqlalchemy.orm import sessionmaker
8
  from datetime import datetime, timedelta
9
  import random
10
  import folium
11
  from folium.plugins import MarkerCluster
12
+
13
+ DATABASE_URL = "sqlite:///./database.db"
14
+
15
+ engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
16
+ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
17
+
18
+ Base = declarative_base()
19
+
20
+ class WifiSignal(Base):
21
+ __tablename__ = "wifi_signal"
22
+ id = Column(Integer, primary_key=True, index=True)
23
+ latitude = Column(Float, index=True)
24
+ longitude = Column(Float, index=True)
25
+ timestamp = Column(DateTime, index=True)
26
+ signal_strength = Column(Integer)
27
+
28
+ Base.metadata.create_all(bind=engine)
29
+
30
+ app = FastAPI()
31
+ templates = Jinja2Templates(directory="templates")
32
+
33
+ def get_db():
34
+ db = SessionLocal()
35
+ try:
36
+ yield db
37
+ finally:
38
+ db.close()
39
+
40
+ @app.post("/api/generate-data")
41
+ def generate_data(db: Session = Depends(get_db)):
42
  base_latitude = 35.6837
43
  base_longitude = 139.6805
44
  start_date = datetime(2024, 8, 1)
 
61
  signal_strength=random_signal_strength
62
  )
63
 
64
+ db.add(wifi_signal)
65
+
66
+ db.commit()
67
+ return {"message": "Demo data generated successfully"}
68
+
69
+ @app.delete("/api/delete-data")
70
+ def delete_data(db: Session = Depends(get_db)):
 
 
 
 
 
 
 
 
 
 
 
71
  try:
72
+ num_rows_deleted = db.query(WifiSignal).delete()
73
+ db.commit()
74
+ return {"message": f"Deleted {num_rows_deleted} rows from the database."}
75
  except Exception as e:
76
+ db.rollback()
77
+ raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}")
78
+
79
+ @app.post("/api/upload")
80
+ def upload_data(request: Request, db: Session = Depends(get_db)):
81
+ data = request.json()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  latitude = data.get('latitude')
83
  longitude = data.get('longitude')
84
  timestamp = datetime.strptime(data.get('timestamp'), '%Y-%m-%d %H:%M:%S')
85
  signal_strength = data.get('signal_strength')
86
 
87
  wifi_signal = WifiSignal(latitude=latitude, longitude=longitude, timestamp=timestamp, signal_strength=signal_strength)
88
+ db.add(wifi_signal)
89
+ db.commit()
90
 
91
+ return {"message": "Data uploaded successfully"}
 
 
 
 
 
92
 
93
+ @app.get("/", response_class=HTMLResponse)
94
+ def show_map(request: Request, start_date: str = None, end_date: str = None, db: Session = Depends(get_db)):
95
+ query = db.query(WifiSignal)
96
 
97
  if start_date and end_date:
98
  start_datetime = datetime.strptime(start_date, '%Y-%m-%d')
 
126
  ).add_to(marker_cluster)
127
 
128
  map_html = m._repr_html_()
129
+ return templates.TemplateResponse("map.html", {"request": request, "map_html": map_html, "start_date": start_date, "end_date": end_date})
 
 
 
 
 
requirements.txt CHANGED
@@ -1,7 +1,5 @@
1
- Flask==2.2.5
2
- Flask-SQLAlchemy==3.0.3
3
- Flasgger==0.9.5
4
  folium==0.13.0
5
- gunicorn==20.1.0
6
-
7
-
 
1
+ fastapi==0.95.1
2
+ uvicorn==0.22.0
3
+ sqlalchemy==1.4.22
4
  folium==0.13.0
5
+ jinja2==3.1.2
 
 
templates/map.html ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Signal Tracker Map</title>
7
+ <!-- Bootstrap CSS -->
8
+ <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
9
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.css" />
10
+ <style>
11
+ body {
12
+ display: flex;
13
+ justify-content: center;
14
+ align-items: center;
15
+ height: 100vh;
16
+ margin: 0;
17
+ background-color: #f0f0f0;
18
+ }
19
+ .container {
20
+ display: flex;
21
+ flex-direction: column;
22
+ align-items: center;
23
+ justify-content: center;
24
+ height: 100%;
25
+ }
26
+ #map {
27
+ width: 80%;
28
+ height: 500px;
29
+ box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.3);
30
+ margin-bottom: 150px; /* Thêm khoảng cách phía dưới bản đồ */
31
+ }
32
+ .filter-form {
33
+ margin-bottom: 20px;
34
+ background: white;
35
+ padding: 15px;
36
+ border-radius: 5px;
37
+ box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
38
+ width: 80%;
39
+ display: flex;
40
+ justify-content: center;
41
+ }
42
+ .title {
43
+ margin-bottom: 20px;
44
+ text-align: center;
45
+ }
46
+ </style>
47
+ </head>
48
+ <body>
49
+ <div class="container">
50
+ <h1 class="title">Signal Tracker Map</h1>
51
+ <form class="filter-form" id="date-form" method="GET" action="/">
52
+ <div class="row g-3 align-items-center">
53
+ <div class="col-auto">
54
+ <label for="start_date" class="col-form-label">Start Date:</label>
55
+ </div>
56
+ <div class="col-auto">
57
+ <input type="date" id="start_date" name="start_date" class="form-control"
58
+ value="{{ start_date or '' }}">
59
+ </div>
60
+ <div class="col-auto">
61
+ <label for="end_date" class="col-form-label">End Date:</label>
62
+ </div>
63
+ <div class="col-auto">
64
+ <input type="date" id="end_date" name="end_date" class="form-control"
65
+ value="{{ end_date or '' }}">
66
+ </div>
67
+ </div>
68
+ </form>
69
+ <div id="map">
70
+ {{ map_html|safe }}
71
+ </div>
72
+ </div>
73
+
74
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.js"></script>
75
+ <!-- Bootstrap JS (Optional) -->
76
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
77
+ <script>
78
+ // JavaScript để tự động submit form khi thay đổi ngày nếu cả hai ngày đều có giá trị
79
+ function updateMapIfDatesFilled() {
80
+ const startDate = document.getElementById('start_date').value;
81
+ const endDate = document.getElementById('end_date').value;
82
+
83
+ if (startDate && endDate) {
84
+ document.getElementById('date-form').submit();
85
+ }
86
+ }
87
+
88
+ document.getElementById('start_date').addEventListener('change', updateMapIfDatesFilled);
89
+ document.getElementById('end_date').addEventListener('change', updateMapIfDatesFilled);
90
+ </script>
91
+ </body>
92
+ </html>