
+++ .gitignore
... | ... | @@ -0,0 +1,3 @@ |
1 | +/database/keys/ | |
2 | +/database/db_config.json | |
3 | +/database/db_config1.json |
--- action.py
+++ action.py
... | ... | @@ -1,11 +1,8 @@ |
1 | 1 |
|
2 |
-from flask_restx import Resource, Api, Namespace, fields,reqparse |
|
3 |
-from flask import request,jsonify |
|
2 |
+from flask_restx import Resource, Namespace, fields |
|
4 | 3 |
from flask import Flask, request |
5 | 4 |
import os |
6 |
-from haversine import haversine |
|
7 | 5 |
from database.database import DB |
8 |
-from datetime import datetime |
|
9 | 6 |
import pandas as pd |
10 | 7 |
import jwt |
11 | 8 |
|
... | ... | @@ -17,102 +14,156 @@ |
17 | 14 |
description="노드 분석을 위해 사용하는 api.", |
18 | 15 |
) |
19 | 16 |
|
17 |
+trip_log_model = Action.model('TripLog', { |
|
18 |
+ 'trip_id': fields.String(required=True, description='The ID of the trip (64 characters)'), |
|
19 |
+ 'trip_distance_m': fields.Float(required=True, description='Total distance traveled in meters'), |
|
20 |
+ 'trip_time_s': fields.Float(required=True, description='Total time of the trip in seconds'), |
|
21 |
+ 'abrupt_start_count': fields.Integer(required=True, description='Count of abrupt starts'), |
|
22 |
+ 'abrupt_stop_count': fields.Integer(required=True, description='Count of abrupt stops'), |
|
23 |
+ 'abrupt_acceleration_count': fields.Integer(required=True, description='Count of abrupt accelerations'), |
|
24 |
+ 'abrupt_deceleration_count': fields.Integer(required=True, description='Count of abrupt decelerations'), |
|
25 |
+ 'helmet_on': fields.Integer(required=True, description='Whether the helmet was worn during the trip, must be 0 or 1 with 1 is the helmet on.'), |
|
26 |
+ 'final_score': fields.Float(required=True, description='The final safety score for the trip') |
|
27 |
+}) |
|
20 | 28 |
|
21 |
- |
|
22 |
-def find_node(gps_address_y,gps_address_x): |
|
23 |
- db=DB() |
|
24 |
- nn_end = None |
|
25 |
- end_delta = float("inf") |
|
26 |
- value=0.0001 |
|
27 |
- near_nodes=[] |
|
28 |
- while near_nodes==[]: |
|
29 |
- value=value*10 |
|
30 |
- near_nodes=db.db_get_near_node(gps_address_y,gps_address_x,value) |
|
31 |
- |
|
32 |
- for n in near_nodes: |
|
33 |
- e_dist = haversine((gps_address_x,gps_address_y), n) |
|
34 |
- if e_dist < end_delta : |
|
35 |
- nn_end = n |
|
36 |
- end_delta = e_dist |
|
37 |
- return nn_end |
|
38 |
- |
|
39 |
- |
|
40 |
-@Action.route('/image_summit') |
|
41 |
-class fileUpload(Resource): |
|
42 |
- @Action.doc(responses={200: 'Success'}) |
|
43 |
- @Action.doc(responses={500: 'Register Failed'}) |
|
44 |
- def post(self): |
|
45 |
- if request.method == 'POST': |
|
46 |
- current_time = datetime.now() |
|
47 |
- timestamp = current_time.strftime("%Y%m%d%H%M%S") |
|
48 |
- # 시간을 원하는 형식으로 포맷팅하기 (예: 년-월-일 시:분:초) |
|
49 |
- formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S") |
|
50 |
- # 포맷팅된 현재 시간 출력 |
|
51 |
- token = request.headers.get('Authorization') |
|
52 |
- print(token) |
|
53 |
- if not token: |
|
54 |
- return jsonify({'result': 'fail', 'msg': '토큰이 없습니다.'}) |
|
55 |
- else: |
|
56 |
- # Decode the token to verify it |
|
57 |
- decoded_token = jwt.decode(token, "secret", algorithms=['HS256']) |
|
58 |
- print(decoded_token) |
|
59 |
- user_id = decoded_token['id'] |
|
60 |
- print("현재 시간,저장요청:", formatted_time) |
|
61 |
- f = request.files['file'] |
|
62 |
- save_path = f"images/{timestamp}/{user_id}" |
|
63 |
- os.makedirs(save_path, exist_ok=True) |
|
64 |
- new_filename = f"{save_path}/{user_id}_{timestamp}.jpg" |
|
65 |
- print(f) |
|
66 |
- f.save(new_filename) |
|
67 |
- print("저장완료", formatted_time) |
|
68 |
- |
|
69 |
- return { |
|
70 |
- 'save': 'done' # str으로 반환하여 return |
|
71 |
- }, 200 |
|
29 |
+history_request_model = Action.model( |
|
30 |
+ 'history_request', { |
|
31 |
+ 'user_id' : fields.String(required=True, description = 'The user ID that you want to query history') |
|
32 |
+ } |
|
33 |
+) |
|
72 | 34 |
|
73 | 35 |
|
74 | 36 |
@Action.route('/gps_update') |
75 |
-class fileUpload(Resource): |
|
37 |
+ |
|
38 |
+class GPS_update(Resource): |
|
39 |
+ @Action.doc(responses={200: 'Success'}) |
|
40 |
+ @Action.doc(responses={401: 'Unauthorized'}) |
|
41 |
+ @Action.doc(responses={500: 'Internal Error'}) |
|
76 | 42 |
def post(self): |
77 |
- db = DB() |
|
78 | 43 |
token = request.headers.get('Authorization') |
79 | 44 |
if not token: |
80 |
- return jsonify({'result': 'fail', 'msg': '토큰이 없습니다.'}) |
|
81 |
- else: |
|
45 |
+ return {'result': 'fail', 'msg': '토큰이 없습니다.'} |
|
46 |
+ try: |
|
82 | 47 |
# Decode the token to verify it |
83 | 48 |
decoded_token = jwt.decode(token, "secret", algorithms=['HS256']) |
84 |
- #print(decoded_token) |
|
85 | 49 |
user_id = decoded_token['id'] |
86 |
- |
|
50 |
+ except jwt.ExpiredSignatureError: |
|
51 |
+ return {'result': 'fail', 'msg': '토큰이 만료되었습니다.'}, 401 |
|
52 |
+ except jwt.InvalidTokenError: |
|
53 |
+ return {'result': 'fail', 'msg': '유효하지 않은 토큰입니다.'}, 401 |
|
54 |
+ |
|
55 |
+ db = DB() |
|
87 | 56 |
|
88 | 57 |
data = request.get_json() |
89 | 58 |
if len(data["trip_id"]) !=64: |
90 |
- return jsonify({500 :"ERROR! INVALID TRIP_ID!"}) |
|
59 |
+ return {500 :"ERROR! INVALID TRIP_ID!"} |
|
91 | 60 |
|
92 | 61 |
if len(data["trip_log"]["timestamp"]) == 0: |
93 |
- return jsonify({500 :"ERROR! 'trip_log' is empty!"}) |
|
62 |
+ return {500 :"ERROR! 'trip_log' is empty!"} |
|
94 | 63 |
|
95 | 64 |
time_stamp_len = len(data["trip_log"]["timestamp"]) |
96 | 65 |
latitude_len = len(data["trip_log"]["latitude"]) |
97 | 66 |
longitude_len = len(data["trip_log"]["longitude"]) |
98 | 67 |
|
99 | 68 |
if time_stamp_len != latitude_len or latitude_len != longitude_len: |
100 |
- return jsonify( |
|
101 |
- { |
|
69 |
+ return { |
|
102 | 70 |
500: f"ERROR! Mismatching length of data in trip_log! \n timestamp : {time_stamp_len} \n latitude : {latitude_len} \n longitude : {longitude_len}" |
103 |
- } |
|
104 |
- ) |
|
105 |
- |
|
71 |
+ } |
|
72 |
+ |
|
106 | 73 |
df = pd.DataFrame(data["trip_log"]) |
107 | 74 |
df["user_id"] = data["user_id"] |
108 | 75 |
df["trip_id"] = data["trip_id"] |
109 |
- |
|
110 |
- |
|
76 |
+ |
|
111 | 77 |
columns = df.columns |
112 | 78 |
data_csv_block = df.to_csv(header=False, index=False) |
113 | 79 |
print(f"recieved : {data}") |
114 | 80 |
# GPS 데이터베이스에 삽입 |
115 | 81 |
db.insert_gps_data(data_csv_block, columns) |
116 |
- return jsonify({'result': f'success'}) |
|
117 |
- |
|
82 |
+ return {'result': f'success'} |
|
118 | 83 |
|
84 |
+ |
|
85 |
+@Action.route('/trip_and_score_update') |
|
86 |
+class TRIP_insert(Resource): |
|
87 |
+ @Action.expect(trip_log_model) |
|
88 |
+ @Action.doc(responses={200: 'Success'}) |
|
89 |
+ @Action.doc(responses={401: 'Unauthorized'}) |
|
90 |
+ @Action.doc(responses={500: 'Internal Error'}) |
|
91 |
+ def post(self): |
|
92 |
+ token = request.headers.get('Authorization') |
|
93 |
+ |
|
94 |
+ # Check if token is provided |
|
95 |
+ if not token: |
|
96 |
+ return {'result': 'fail', 'msg': '토큰이 없습니다.'}, 401 |
|
97 |
+ |
|
98 |
+ try: |
|
99 |
+ # Decode the token to verify it |
|
100 |
+ decoded_token = jwt.decode(token, "secret", algorithms=['HS256']) |
|
101 |
+ user_id = decoded_token['id'] |
|
102 |
+ except jwt.ExpiredSignatureError: |
|
103 |
+ return {'result': 'fail', 'msg': '토큰이 만료되었습니다.'}, 401 |
|
104 |
+ except jwt.InvalidTokenError: |
|
105 |
+ return {'result': 'fail', 'msg': '유효하지 않은 토큰입니다.'}, 401 |
|
106 |
+ |
|
107 |
+ db = DB() |
|
108 |
+ data = request.get_json() |
|
109 |
+ if len(data["trip_id"]) != 64: |
|
110 |
+ return {"result" : "ERROR! INVALID TRIP_ID!"}, 500 |
|
111 |
+ |
|
112 |
+ trip_id = data["trip_id"] |
|
113 |
+ trip_distance_m = data["total_distance_m"] |
|
114 |
+ trip_time_s = data["total_time_s"] |
|
115 |
+ abrupt_start_count = data["abrupt_start_count"] |
|
116 |
+ abrupt_stop_count = data["abrupt_stop_count"] |
|
117 |
+ abrupt_acceleration_count = data["abrupt_acceleration_count"] |
|
118 |
+ abrupt_deceleration_count = data["abrupt_deceleration_count"] |
|
119 |
+ helmet_on = int(data["helmet_on"]) |
|
120 |
+ final_score = data["final_score"] |
|
121 |
+ |
|
122 |
+ if (helmet_on != 1) and (helmet_on != 0): |
|
123 |
+ return {"result" : f"ERROR! INVALID 'helmet_on'! \n helmet_on : {helmet_on}"}, 500 |
|
124 |
+ db.insert_trip_data( |
|
125 |
+ user_id, |
|
126 |
+ trip_id, |
|
127 |
+ trip_distance_m, |
|
128 |
+ trip_time_s, |
|
129 |
+ abrupt_start_count, |
|
130 |
+ abrupt_stop_count, |
|
131 |
+ abrupt_acceleration_count, |
|
132 |
+ abrupt_deceleration_count, |
|
133 |
+ helmet_on, |
|
134 |
+ final_score |
|
135 |
+ ) |
|
136 |
+ return {'result': f'success'} |
|
137 |
+ |
|
138 |
+@Action.route('/get_history') |
|
139 |
+class Get_history(Resource): |
|
140 |
+ @Action.expect(history_request_model) |
|
141 |
+ @Action.doc(responses={401: 'Unauthorized'}) |
|
142 |
+ @Action.doc(responses={500: 'Internal Error'}) |
|
143 |
+ def post(self): |
|
144 |
+ token = request.headers.get('Authorization') |
|
145 |
+ |
|
146 |
+ # Check if token is provided |
|
147 |
+ if not token: |
|
148 |
+ return {'result': 'fail', 'msg': '토큰이 없습니다.'}, 401 |
|
149 |
+ |
|
150 |
+ try: |
|
151 |
+ # Decode the token to verify it |
|
152 |
+ decoded_token = jwt.decode(token, "secret", algorithms=['HS256']) |
|
153 |
+ user_id = decoded_token['id'] |
|
154 |
+ except jwt.ExpiredSignatureError: |
|
155 |
+ return {'result': 'fail', 'msg': '토큰이 만료되었습니다.'}, 401 |
|
156 |
+ except jwt.InvalidTokenError: |
|
157 |
+ return {'result': 'fail', 'msg': '유효하지 않은 토큰입니다.'}, 401 |
|
158 |
+ |
|
159 |
+ # Interact with the DB to get user history |
|
160 |
+ |
|
161 |
+ data = request.get_json() |
|
162 |
+ user_id = data["user_id"] |
|
163 |
+ try: |
|
164 |
+ db = DB() |
|
165 |
+ result, status_code = db.get_history(user_name=user_id) |
|
166 |
+ return {'result': 'success', 'data': result}, status_code |
|
167 |
+ except Exception as e: |
|
168 |
+ print(str(e)) |
|
169 |
+ return {'result': 'fail', 'msg': str(e)}, 500 |
--- app.py
+++ app.py
... | ... | @@ -2,7 +2,6 @@ |
2 | 2 |
from flask_restx import Api |
3 | 3 |
from flask_cors import CORS |
4 | 4 |
from auth import Auth |
5 |
-from trip import Trip |
|
6 | 5 |
from action import Action |
7 | 6 |
from flask_caching import Cache |
8 | 7 |
import json |
... | ... | @@ -21,8 +20,6 @@ |
21 | 20 |
license="") |
22 | 21 |
|
23 | 22 |
print("Api Started") |
24 |
-api.add_namespace(Trip, '/trip') |
|
25 |
-print("Api Add Trip") |
|
26 | 23 |
|
27 | 24 |
api.add_namespace(Auth, '/auth') |
28 | 25 |
print("Api Add Auth") |
... | ... | @@ -30,5 +27,5 @@ |
30 | 27 |
api.add_namespace(Action, '/action') |
31 | 28 |
|
32 | 29 |
if __name__ == "__main__": |
33 |
- app.run(debug=True, host='192.168.0.195', port=15857) |
|
30 |
+ app.run(debug=True, host='0.0.0.0', port=27461) |
|
34 | 31 |
print("Flask Start") |
--- auth.py
+++ auth.py
... | ... | @@ -1,12 +1,8 @@ |
1 |
-import hashlib |
|
2 |
-from flask import request,jsonify,render_template,redirect,url_for |
|
3 |
-from flask_restx import Resource, Api, Namespace, fields |
|
1 |
+from flask import request,jsonify |
|
2 |
+from flask_restx import Resource, Namespace, fields |
|
4 | 3 |
from database.database import DB |
5 | 4 |
import datetime |
6 | 5 |
import jwt |
7 |
- |
|
8 |
- |
|
9 |
- |
|
10 | 6 |
|
11 | 7 |
|
12 | 8 |
users = {} |
... | ... | @@ -27,8 +23,18 @@ |
27 | 23 |
|
28 | 24 |
}) |
29 | 25 |
|
26 |
+ |
|
27 |
+get_phone_number = Auth.inherit('get a phone number of an user', { |
|
28 |
+ 'id' : fields.String(description="user id", required=True) |
|
29 |
+}) |
|
30 |
+ |
|
31 |
+ |
|
32 |
+get_email = Auth.inherit('get an email of an user', { |
|
33 |
+ 'id' : fields.String(description="user id", required=True) |
|
34 |
+}) |
|
35 |
+ |
|
30 | 36 |
user_fields_register = Auth.inherit('User reigster', user_fields, { |
31 |
- 'password': fields.String(description='Password', required=True),'email': fields.String(description='email', required=True),'user_sex': fields.String(description='sex', required=True),'phone': fields.String(description='phone', required=True) |
|
37 |
+ 'password': fields.String(description='Password', required=True),'email': fields.String(description='email', required=True),'phone': fields.String(description='phone', required=True) |
|
32 | 38 |
|
33 | 39 |
}) |
34 | 40 |
|
... | ... | @@ -52,70 +58,121 @@ |
52 | 58 |
}, 200 |
53 | 59 |
|
54 | 60 |
|
55 |
- |
|
56 |
- |
|
57 | 61 |
@Auth.route('/register') |
58 | 62 |
class AuthRegister(Resource): |
59 | 63 |
@Auth.expect(user_fields_register) |
60 | 64 |
@Auth.doc(responses={200: 'Success'}) |
61 | 65 |
@Auth.doc(responses={500: 'Register Failed'}) |
62 | 66 |
def post(self): |
63 |
- db=DB() |
|
64 |
- id_ = request.json['id'] |
|
65 |
- password = request.json['password'] |
|
66 |
- user_email = request.json['email'] |
|
67 |
- sex = request.json['sex'] |
|
68 |
- phone = request.json['phone'] |
|
69 |
- pw_has = hashlib.sha256(password.encode('utf-8')).hexdigest() |
|
70 |
- db_check_duplicate = db.db_check_id(id_) |
|
71 |
- if db_check_duplicate != None: |
|
72 |
- return { |
|
73 |
- "message" : "Register Failed : duplicate ID" |
|
74 |
- }, 500 |
|
75 |
- db_login_success_flag=db.db_login(id_,password) |
|
76 |
- if db_login_success_flag != None: |
|
77 |
- return { |
|
78 |
- "message": "Register Failed" |
|
79 |
- }, 500 |
|
67 |
+ user_manager = DB() |
|
68 |
+ # Extract data from the request |
|
69 |
+ data = request.json |
|
70 |
+ id_ = data['id'] |
|
71 |
+ password = data['password'] |
|
72 |
+ user_email = data['email'] |
|
73 |
+ # sex = data['user_sex'] |
|
74 |
+ phone = data['phone'] |
|
75 |
+ |
|
76 |
+ # Prepare data for registration |
|
77 |
+ user_data = { |
|
78 |
+ 'username': id_, |
|
79 |
+ 'password': password, |
|
80 |
+ 'email': user_email, |
|
81 |
+ # 'sex': sex, |
|
82 |
+ 'phone': phone |
|
83 |
+ } |
|
84 |
+ |
|
85 |
+ # Call the register_user method from the UserManager instance |
|
86 |
+ result, status_code = user_manager.register_user(user_data) |
|
87 |
+ |
|
88 |
+ # Return the appropriate response based on the result from UserManager |
|
89 |
+ if status_code == 200: |
|
90 |
+ return result, 200 |
|
80 | 91 |
else: |
81 |
- db.db_add_id(id_,pw_has,user_email,sex,phone) |
|
82 |
- return { |
|
83 |
- 'Authorization': id_ # str으로 반환하여 return |
|
84 |
- }, 200 |
|
92 |
+ return result, 500 |
|
93 |
+ |
|
94 |
+@Auth.route('/retrive_phone_number') |
|
95 |
+class AuthRegister(Resource): |
|
96 |
+ @Auth.expect(get_phone_number) |
|
97 |
+ @Auth.doc(responses={200: 'Success'}) |
|
98 |
+ @Auth.doc(responses={500: 'Register Failed'}) |
|
99 |
+ def post(self): |
|
100 |
+ user_manager = DB() |
|
101 |
+ data = request.json |
|
102 |
+ id_ = data['id'] |
|
103 |
+ query_input = { |
|
104 |
+ "username" : id_ |
|
105 |
+ } |
|
106 |
+ result, status_code = user_manager.get_phone_number(query_input) |
|
107 |
+ |
|
108 |
+ if status_code == 200: |
|
109 |
+ return result, 200 |
|
110 |
+ else: |
|
111 |
+ return result, 500 |
|
112 |
+ |
|
113 |
+ |
|
114 |
+@Auth.route('/retrive_email') |
|
115 |
+class AuthRegister(Resource): |
|
116 |
+ @Auth.expect(get_email) |
|
117 |
+ @Auth.doc(responses={200: 'Success'}) |
|
118 |
+ @Auth.doc(responses={500: 'Register Failed'}) |
|
119 |
+ def post(self): |
|
120 |
+ user_manager = DB() |
|
121 |
+ data = request.json |
|
122 |
+ id_ = data['id'] |
|
123 |
+ query_input = { |
|
124 |
+ "username" : id_ |
|
125 |
+ } |
|
126 |
+ result, status_code = user_manager.get_email(query_input) |
|
127 |
+ |
|
128 |
+ if status_code == 200: |
|
129 |
+ return result, 200 |
|
130 |
+ else: |
|
131 |
+ return result, 500 |
|
132 |
+ |
|
133 |
+ |
|
85 | 134 |
|
86 | 135 |
@Auth.route('/login') |
87 | 136 |
class AuthLogin(Resource): |
88 | 137 |
@Auth.expect(user_fields_auth) |
89 |
- @Auth.doc(responses={200: 'Success'}) |
|
90 |
- @Auth.doc(responses={404: 'User Not Found'}) |
|
91 |
- @Auth.doc(responses={500: 'Auth Failed'}) |
|
138 |
+ @Auth.doc(responses={200: 'Login Successful'}) |
|
139 |
+ @Auth.doc(responses={401: 'Unauthorized'}) |
|
140 |
+ @Auth.doc(responses={500: 'Login Failed'}) |
|
92 | 141 |
def post(self): |
93 |
- db=DB() |
|
94 |
- id = request.json['id'] |
|
95 |
- password = request.json['password'] |
|
96 |
- #CRITICAL ... WHY? WHY? |
|
97 |
- # ... hashing should be handled at client, not server... |
|
98 |
- pw_hash = hashlib.sha256(password.encode('utf-8')).hexdigest() |
|
99 |
- result = db.db_login(id,pw_hash) |
|
100 |
- if result is not None: |
|
142 |
+ user_manager = DB() |
|
143 |
+ # Extract data from the request |
|
144 |
+ data = request.json |
|
145 |
+ id_ = data['id'] |
|
146 |
+ password = data['password'] |
|
147 |
+ |
|
148 |
+ # Prepare data for authentication |
|
149 |
+ user_data = { |
|
150 |
+ 'username': id_, |
|
151 |
+ 'password': password |
|
152 |
+ } |
|
153 |
+ |
|
154 |
+ # Call the login_user method from the UserManager instance |
|
155 |
+ result, status_code = user_manager.login_user(user_data) |
|
156 |
+ |
|
157 |
+ if result['status'] == 'success': |
|
101 | 158 |
payload = { |
102 |
- 'id' : id, |
|
103 |
- 'exp' : datetime.datetime.utcnow() + datetime.timedelta(days=14) |
|
159 |
+ 'id': id_, |
|
160 |
+ 'exp': datetime.datetime.utcnow() + datetime.timedelta(days=14) |
|
104 | 161 |
} |
105 | 162 |
token = jwt.encode(payload, "secret", algorithm='HS256') |
106 |
- return jsonify({'result': 'success', 'token': token}) |
|
107 |
- else: |
|
108 |
- return jsonify({'result': 'fail', 'msg': '아이디/비밀번호가 일치하지 않습니다.'}) |
|
163 |
+ return {'result': 'success', 'token': token}, 200 |
|
164 |
+ else : |
|
165 |
+ return {'result': 'fail', 'msg': '아이디/비밀번호가 일치하지 않습니다.'}, 401 |
|
109 | 166 |
|
110 | 167 |
|
111 |
-@Auth.route('/secession') |
|
112 |
-class AuthSecession(Resource): |
|
168 |
+@Auth.route('/withdraw') |
|
169 |
+class AuthWithdraw(Resource): |
|
113 | 170 |
def post(self): |
114 | 171 |
db=DB() |
115 | 172 |
id = request.json['token'] |
116 | 173 |
payload = jwt.decode(id, "secret", algorithms=['HS256']) |
117 | 174 |
db.db_delete_id(payload['id']) |
118 |
- return {'secession':'success'} |
|
175 |
+ return {'secession':'success'}, 200 |
|
119 | 176 |
|
120 | 177 |
|
121 | 178 |
|
--- database/database.py
+++ database/database.py
... | ... | @@ -1,87 +1,267 @@ |
1 | 1 |
import psycopg2 # driver 임포트 |
2 |
-import time |
|
2 |
+import json |
|
3 |
+import bcrypt |
|
4 |
+from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes |
|
5 |
+from cryptography.hazmat.primitives import padding |
|
6 |
+from cryptography.hazmat.backends import default_backend |
|
7 |
+import re |
|
8 |
+import os |
|
3 | 9 |
from io import StringIO |
4 |
-from datetime import datetime, timedelta |
|
10 |
+from datetime import datetime |
|
5 | 11 |
|
12 |
+config_file_path = "database/db_config.json" |
|
6 | 13 |
|
7 | 14 |
class DB(): |
8 | 15 |
def __init__(self): |
9 |
- self.conn=psycopg2.connect( |
|
10 |
- host='210.180.118.83', |
|
11 |
- dbname='TRAFFICAGNET', |
|
12 |
- user='takensoft', |
|
13 |
- password='', |
|
14 |
- port='5432', |
|
15 |
- options=f'-c search_path=trafficagent' |
|
16 |
- ) # db에 접속 |
|
16 |
+ |
|
17 |
+ # Load the database configuration from the JSON file |
|
18 |
+ self.db_config = self.load_db_config(config_file_path) |
|
19 |
+ |
|
20 |
+ # Initialize database connection |
|
21 |
+ self.conn = psycopg2.connect( |
|
22 |
+ host=self.db_config['host'], |
|
23 |
+ dbname=self.db_config['dbname'], |
|
24 |
+ user=self.db_config['user'], |
|
25 |
+ password=self.db_config['password'], |
|
26 |
+ port=self.db_config['port'], |
|
27 |
+ # options=self.db_config['options'] |
|
28 |
+ ) |
|
29 |
+ self.schema = self.db_config["schema"] |
|
30 |
+ |
|
17 | 31 |
self.conn.autocommit=True |
18 |
- self.schema = 'trafficagent' |
|
19 |
- ''' |
|
20 |
- def __init__(self): |
|
21 |
- self.conn=psycopg2.connect( |
|
22 |
- host='165.229.169.113', |
|
23 |
- dbname='traffic_agent', |
|
24 |
- user='takensoft', |
|
25 |
- password='ts44301236!@', |
|
26 |
- port='5432', |
|
27 |
- options="-c search_path=traffic_agent_v1") # db에 접속 |
|
28 |
- self.conn.autocommit=True |
|
29 |
- ''' |
|
30 |
- |
|
31 |
- |
|
32 |
- |
|
33 |
- def db_check_id(self,id): |
|
34 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
32 |
+ self.cur = self.conn.cursor() |
|
33 |
+ # yeah, that double quotation is absolutely needed (to distinguish capital letters) |
|
34 |
+ self.cur.execute("SET search_path TO " + f'"{self.schema}"') |
|
35 |
+ with open("database/keys/encryption_key2024-09-05_14:27:02", "rb") as f: |
|
36 |
+ self.encryption_key = f.read() |
|
35 | 37 |
|
36 |
- cur.execute(f''' |
|
37 |
- SELECT user_id |
|
38 |
- FROM "{self.schema}".user_id |
|
39 |
- Where user_id = '{id}'; |
|
40 |
- ''') |
|
41 |
- result=cur.fetchone() |
|
38 |
+ def load_db_config(self, config_file_path): |
|
39 |
+ """ |
|
40 |
+ Loads database configuration from a JSON file. |
|
41 |
+ """ |
|
42 |
+ with open(config_file_path, 'r') as config_file: |
|
43 |
+ return json.load(config_file) |
|
44 |
+ |
|
45 |
+ def encrypt_aes(self, plain_text): |
|
46 |
+ iv = os.urandom(16) # AES block size is 16 bytes |
|
47 |
+ cipher = Cipher(algorithms.AES(self.encryption_key), modes.CBC(iv), backend=default_backend()) |
|
48 |
+ encryptor = cipher.encryptor() |
|
49 |
+ |
|
50 |
+ # Pad the plaintext to be a multiple of 16 bytes |
|
51 |
+ padder = padding.PKCS7(algorithms.AES.block_size).padder() |
|
52 |
+ padded_data = padder.update(plain_text.encode('utf-8')) + padder.finalize() |
|
53 |
+ |
|
54 |
+ encrypted_data = encryptor.update(padded_data) + encryptor.finalize() |
|
55 |
+ return encrypted_data, iv |
|
56 |
+ |
|
57 |
+ def decrypt_aes(self, encrypted_data, iv): |
|
58 |
+ cipher = Cipher(algorithms.AES(self.encryption_key), modes.CBC(iv), backend=default_backend()) |
|
59 |
+ decryptor = cipher.decryptor() |
|
60 |
+ |
|
61 |
+ decrypted_data = decryptor.update(encrypted_data) + decryptor.finalize() |
|
62 |
+ |
|
63 |
+ # Remove padding after decryption |
|
64 |
+ unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() |
|
65 |
+ unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize() |
|
66 |
+ |
|
67 |
+ return unpadded_data.decode('utf-8') |
|
68 |
+ |
|
69 |
+ def cleanse_and_validate_input(self, data): |
|
70 |
+ """ |
|
71 |
+ Cleanses input by removing leading/trailing spaces and validates the data. |
|
72 |
+ Returns cleansed data and an error message if validation fails. |
|
73 |
+ """ |
|
74 |
+ username = data.get('username', '').strip() |
|
75 |
+ password = data.get('password', '').strip() |
|
76 |
+ email = data.get('email', '').strip() |
|
77 |
+ phone = data.get('phone', '').strip() |
|
78 |
+ phone = phone.replace("-","") |
|
79 |
+ sex = data.get('sex', '').strip() |
|
80 |
+ |
|
81 |
+ # Validate username |
|
82 |
+ if not username: |
|
83 |
+ return None, "Username is required." |
|
84 |
+ if len(username) > 26: |
|
85 |
+ return None, "Username must not exceed 26 characters." |
|
86 |
+ |
|
87 |
+ # Validate password |
|
88 |
+ if not password: |
|
89 |
+ return None, "Password is required." |
|
90 |
+ if len(password) < 8: |
|
91 |
+ return None, "Password must be at least 8 characters long." |
|
92 |
+ |
|
93 |
+ # Validate email format |
|
94 |
+ if not email or not re.fullmatch(r"[^@]+@[^@]+\.[^@]+", email): |
|
95 |
+ return None, "Invalid email address." |
|
96 |
+ |
|
97 |
+ # Validate phone number format |
|
98 |
+ if not re.fullmatch(r'010\d{8}', phone): |
|
99 |
+ return None, "Phone number must be in the format 010XXXXXXXX where X are digits." |
|
100 |
+ |
|
101 |
+ # Validate sex input |
|
102 |
+ # if not sex: |
|
103 |
+ # return None, "Sex is required." |
|
104 |
+ # if sex not in ['Male', 'Female', 'Non-binary', 'Other']: |
|
105 |
+ # return None, "Invalid value for sex." |
|
106 |
+ sex = "WHATEVER" |
|
107 |
+ return { |
|
108 |
+ 'username': username, |
|
109 |
+ 'password': password, |
|
110 |
+ 'email': email, |
|
111 |
+ 'phone': phone, |
|
112 |
+ 'sex': sex |
|
113 |
+ }, None |
|
114 |
+ |
|
115 |
+ def register_user(self, data): |
|
116 |
+ data, error = self.cleanse_and_validate_input(data) |
|
117 |
+ if error: |
|
118 |
+ return {'status': 'error', 'message': error}, 400 |
|
119 |
+ |
|
120 |
+ username = data['username'] |
|
121 |
+ password = data['password'] |
|
122 |
+ email = data['email'] |
|
123 |
+ phone = data['phone'] |
|
124 |
+ sex = data['sex'] |
|
125 |
+ |
|
126 |
+ # Hash the password with bcrypt, which automatically handles the salt |
|
127 |
+ hashed_pw = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()) |
|
128 |
+ |
|
129 |
+ # Encrypt email, phone, and sex with AES |
|
130 |
+ encrypted_email, email_iv = self.encrypt_aes(email) |
|
131 |
+ encrypted_phone, phone_iv = self.encrypt_aes(phone) |
|
132 |
+ encrypted_sex, sex_iv = self.encrypt_aes(sex) |
|
133 |
+ |
|
134 |
+ # Insert the user into the database |
|
135 |
+ try: |
|
136 |
+ self.cur.execute(f""" |
|
137 |
+ INSERT INTO users (username, user_pw, user_email, email_iv, user_phone, phone_iv, user_sex, user_time_stamp) |
|
138 |
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s) |
|
139 |
+ """, ( |
|
140 |
+ username, |
|
141 |
+ psycopg2.Binary(hashed_pw), |
|
142 |
+ psycopg2.Binary(encrypted_email), |
|
143 |
+ psycopg2.Binary(email_iv), |
|
144 |
+ psycopg2.Binary(encrypted_phone), |
|
145 |
+ psycopg2.Binary(phone_iv), |
|
146 |
+ psycopg2.Binary(encrypted_sex), |
|
147 |
+ datetime.now() # Correct way to insert current timestamp with timezone |
|
148 |
+ ) |
|
149 |
+ ) |
|
150 |
+ self.conn.commit() |
|
151 |
+ return {'status': 'success', 'message': f'user {username} registered successfully'}, 200 |
|
152 |
+ except psycopg2.Error as e: |
|
153 |
+ self.conn.rollback() |
|
154 |
+ return {'status': 'error', 'message': str(e)}, 400 |
|
155 |
+ |
|
156 |
+ def login_user(self, data): |
|
157 |
+ username = data.get('username', '').strip() |
|
158 |
+ password = data.get('password', '').strip() |
|
159 |
+ |
|
160 |
+ # Validate input |
|
161 |
+ if not username or not password: |
|
162 |
+ return {'status': 'error', 'message': 'Username and password are required.'}, 400 |
|
163 |
+ |
|
164 |
+ # Retrieve the user's hashed password from the database |
|
165 |
+ self.cur.execute("SELECT user_pw FROM users WHERE username = %s", (username,)) |
|
166 |
+ user = self.cur.fetchone() |
|
167 |
+ |
|
168 |
+ if user is None: |
|
169 |
+ return {'status': 'error', 'message': 'Invalid username or password'}, 401 |
|
170 |
+ |
|
171 |
+ hashed_pw = bytes(user[0]) # Convert the retrieved hashed password to bytes |
|
172 |
+ |
|
173 |
+ # Check if the provided password matches the stored hashed password |
|
174 |
+ if bcrypt.checkpw(password.encode('utf-8'), hashed_pw): |
|
175 |
+ return {'status': 'success', 'message': 'Logged in successfully'}, 200 |
|
176 |
+ else: |
|
177 |
+ return {'status': 'error', 'message': 'Invalid username or password'}, 401 |
|
178 |
+ |
|
179 |
+ def get_phone_number(self, data): |
|
180 |
+ username = data.get('username', '').strip() |
|
181 |
+ |
|
182 |
+ if not username: |
|
183 |
+ return {'status': 'error', 'message': 'Username is required.'}, 400 |
|
184 |
+ |
|
185 |
+ # Retrieve the encrypted phone number and IV from the database |
|
186 |
+ self.cur.execute("SELECT user_phone, phone_iv FROM users WHERE username = %s", (username,)) |
|
187 |
+ user = self.cur.fetchone() |
|
188 |
+ |
|
189 |
+ if user is None: |
|
190 |
+ return {'status': 'error', 'message': 'User not found'}, 404 |
|
191 |
+ |
|
192 |
+ encrypted_phone, phone_iv = user |
|
193 |
+ |
|
194 |
+ # Decrypt the phone number |
|
195 |
+ decrypted_phone = self.decrypt_aes(encrypted_phone, phone_iv) |
|
196 |
+ |
|
197 |
+ return {'status': 'success', 'phone_number': decrypted_phone}, 200 |
|
198 |
+ |
|
199 |
+ |
|
200 |
+ def get_email(self, data): |
|
201 |
+ username = data.get('username', '').strip() |
|
202 |
+ |
|
203 |
+ if not username: |
|
204 |
+ return {'status': 'error', 'message': 'Username is required.'}, 400 |
|
205 |
+ |
|
206 |
+ # Retrieve the encrypted phone number and IV from the database |
|
207 |
+ self.cur.execute("SELECT user_email, email_iv FROM users WHERE username = %s", (username,)) |
|
208 |
+ user = self.cur.fetchone() |
|
209 |
+ |
|
210 |
+ if user is None: |
|
211 |
+ return {'status': 'error', 'message': 'User not found'}, 404 |
|
212 |
+ |
|
213 |
+ encrypted_phone, phone_iv = user |
|
214 |
+ |
|
215 |
+ # Decrypt the phone number |
|
216 |
+ decrypted_phone = self.decrypt_aes(encrypted_phone, phone_iv) |
|
217 |
+ |
|
218 |
+ return {'status': 'success', 'phone_number': decrypted_phone}, 200 |
|
219 |
+ |
|
220 |
+ def insert_gps_data(self, csv_block, columns): |
|
221 |
+ cur = self.conn.cursor() |
|
222 |
+ data = StringIO(csv_block) |
|
223 |
+ |
|
224 |
+ # using COPY instead of INSERT to do even less operation per data. |
|
225 |
+ cur.copy_from(data, 'gps_data', sep=',', columns=columns) |
|
226 |
+ self.conn.commit() |
|
42 | 227 |
cur.close() |
228 |
+ return True |
|
43 | 229 |
|
44 |
- return result |
|
230 |
+ def insert_trip_data( |
|
231 |
+ self, |
|
232 |
+ username, |
|
233 |
+ trip_id, |
|
234 |
+ total_distance_m, |
|
235 |
+ total_time_s, |
|
236 |
+ abrupt_start_count, |
|
237 |
+ abrupt_stop_count, |
|
238 |
+ abrupt_acceleration_count, |
|
239 |
+ abrupt_deceleration_count, |
|
240 |
+ helmet_on, |
|
241 |
+ final_score |
|
242 |
+ ): |
|
45 | 243 |
|
46 |
- def db_login(self,id,pw): |
|
47 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
244 |
+ self.cur.execute(f""" |
|
245 |
+ INSERT INTO trip_log (user_id, trip_id, timestamp, total_distance_m, total_time_s, abrupt_start_count, abrupt_stop_count, |
|
246 |
+ abrupt_acceleration_count, abrupt_deceleration_count, helmet_on, final_score) |
|
247 |
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) |
|
248 |
+ """, ( |
|
249 |
+ username, |
|
250 |
+ trip_id, |
|
251 |
+ datetime.now(), |
|
252 |
+ total_distance_m, |
|
253 |
+ total_time_s, |
|
254 |
+ abrupt_start_count, |
|
255 |
+ abrupt_stop_count, |
|
256 |
+ abrupt_acceleration_count, |
|
257 |
+ abrupt_deceleration_count, |
|
258 |
+ helmet_on, |
|
259 |
+ final_score |
|
260 |
+ ) |
|
261 |
+ ) |
|
48 | 262 |
|
49 |
- cur.execute(f''' |
|
50 |
- SELECT user_id, user_pw, user_email, user_sex, user_phone, user_time_stamp |
|
51 |
- FROM "{self.schema}".user_id |
|
52 |
- Where user_id = '{id}' and user_pw='{pw}'; |
|
53 |
- ''') |
|
54 |
- result=cur.fetchone() |
|
55 |
- |
|
56 |
- |
|
57 |
- cur.close() |
|
58 |
- |
|
59 |
- return result |
|
60 |
- |
|
61 |
- def db_add_id(self,user_id,user_pw,user_email,user_sex,user_phone) : |
|
62 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
63 |
- recievingtime=datetime.now() |
|
64 |
- d = recievingtime.isoformat(sep=' ', timespec='milliseconds') |
|
65 |
- # d=recievingtime.strftime("%Y-%m-%d %H:%M:%S.%f") |
|
66 |
- cur.execute(f''' |
|
67 |
- insert into "{self.schema}".user_id (user_id,user_pw,user_email,user_sex,user_phone,user_time_stamp) |
|
68 |
- values ('{user_id}','{user_pw}','{user_email}','{user_sex}','{user_phone}','{d}') |
|
69 |
- ''') |
|
70 |
- |
|
71 |
- def db_add_action(self,user_id,user_pw,user_email,user_sex,user_phone) : |
|
72 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
73 |
- recievingtime=datetime.now() |
|
74 |
- d = recievingtime.isoformat(sep=' ', timespec='milliseconds') |
|
75 |
- # d=recievingtime.strftime("%Y-%m-%d %H:%M:%S.%f") |
|
76 |
- cur.execute(f''' |
|
77 |
- insert into "{self.schema}".user_id (user_id,user_pw,user_email,user_sex,user_phone,user_time_stamp) |
|
78 |
- values ('{user_id}','{user_pw}','{user_email}','{user_sex}','{user_phone}','{d}') |
|
79 |
- ''') |
|
80 |
- |
|
81 |
- |
|
82 |
- cur.close() |
|
83 | 263 |
def db_delete_id(self,user_id) : |
84 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
264 |
+ cur = self.conn.cursor() |
|
85 | 265 |
cur.execute(f''' |
86 | 266 |
delete |
87 | 267 |
from "{self.schema}".user_id ui |
... | ... | @@ -89,128 +269,77 @@ |
89 | 269 |
''') |
90 | 270 |
cur.close() |
91 | 271 |
|
92 |
- def db_get_node(self): |
|
93 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
272 |
+ def get_history(self, user_name): |
|
273 |
+ """ |
|
274 |
+ Retrieves all trip logs for the specified user within the last month and returns them in JSON format. |
|
275 |
+ [ |
|
276 |
+ { |
|
277 |
+ "trip_id": "trip_001", |
|
278 |
+ "timestamp": "2024-09-01 12:45:00", |
|
279 |
+ "total_distance_m": 1000.5, |
|
280 |
+ "total_time_s": 600, |
|
281 |
+ "abrupt_start_count": 3, |
|
282 |
+ "abrupt_stop_count": 2, |
|
283 |
+ "abrupt_acceleration_count": 1, |
|
284 |
+ "abrupt_deceleration_count": 1, |
|
285 |
+ "helmet_on": true, |
|
286 |
+ "final_score": 85.5 |
|
287 |
+ }, |
|
288 |
+ { |
|
289 |
+ "trip_id": "trip_002", |
|
290 |
+ "timestamp": "2024-09-02 14:30:00", |
|
291 |
+ "total_distance_m": 1500.0, |
|
292 |
+ "total_time_s": 720, |
|
293 |
+ "abrupt_start_count": 2, |
|
294 |
+ "abrupt_stop_count": 3, |
|
295 |
+ "abrupt_acceleration_count": 1, |
|
296 |
+ "abrupt_deceleration_count": 2, |
|
297 |
+ "helmet_on": false, |
|
298 |
+ "final_score": 90.0 |
|
299 |
+ } |
|
300 |
+ ] |
|
301 |
+ """ |
|
302 |
+ try: |
|
303 |
+ # Execute the query to retrieve logs within the last month |
|
304 |
+ self.cur.execute(""" |
|
305 |
+ SELECT trip_id, timestamp, total_distance_m, total_time_s, abrupt_start_count, |
|
306 |
+ abrupt_stop_count, abrupt_acceleration_count, abrupt_deceleration_count, |
|
307 |
+ helmet_on, final_score |
|
308 |
+ FROM trip_log |
|
309 |
+ WHERE user_id = %s |
|
310 |
+ AND timestamp >= NOW() - INTERVAL '1 month' |
|
311 |
+ """, (user_name,)) |
|
94 | 312 |
|
95 |
- cur.execute(''' |
|
96 |
- select "index",source_x ,source_y,target_x,target_y," dist " |
|
97 |
- from "{self.schema}".node n |
|
98 |
- where flcass != 'pedstrian' |
|
99 |
- ''') |
|
100 |
- result=cur.fetchall() |
|
313 |
+ # Fetch all results |
|
314 |
+ rows = self.cur.fetchall() |
|
101 | 315 |
|
102 |
- return result |
|
103 |
- |
|
104 |
- def db_get_dest(self,dest1): |
|
105 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
316 |
+ # Format the results into a list of dictionaries |
|
317 |
+ result = [] |
|
318 |
+ for row in rows: |
|
319 |
+ trip_log = { |
|
320 |
+ "trip_id": row[0], |
|
321 |
+ "timestamp": row[1].strftime("%Y-%m-%d %H:%M:%S"), # Format timestamp |
|
322 |
+ "total_distance_m": row[2], |
|
323 |
+ "total_time_s": row[3], |
|
324 |
+ "abrupt_start_count": row[4], |
|
325 |
+ "abrupt_stop_count": row[5], |
|
326 |
+ "abrupt_acceleration_count": row[6], |
|
327 |
+ "abrupt_deceleration_count": row[7], |
|
328 |
+ "helmet_on": bool(row[8]), # Convert INT to Boolean |
|
329 |
+ "final_score": row[9] |
|
330 |
+ } |
|
331 |
+ result.append(trip_log) |
|
106 | 332 |
|
107 |
- cur.execute(f''' |
|
108 |
- select j.q,li.q ,li.location_cen_x_4623,li.location_cen_y_4623,li.location_exit_x_4623 ,li.location_exit_y_4623 |
|
109 |
- from "{self.schema}".jibun j, "{self.schema}".location_info li |
|
110 |
- where j.build_num =li.build_code and (j.q='{dest1}' or li.q='{dest1}' or li.build_name_city like '{dest1}') |
|
111 |
- ''') |
|
112 |
- result=cur.fetchone() |
|
333 |
+ # Convert the result list to JSON format |
|
334 |
+ return json.dumps(result), 200 |
|
113 | 335 |
|
114 |
- return (float(result[4]),float(result[5])) |
|
115 |
- |
|
116 |
- def db_get_near_node(self,dest_x,dest_y,value): |
|
117 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
118 |
- |
|
119 |
- cur.execute(f''' |
|
120 |
- select source_x, source_y |
|
121 |
- from "{self.schema}".node n |
|
122 |
- where {dest_x} > source_y - {value} and {dest_x} <= source_y + {value} |
|
123 |
- and {dest_y} > source_x - {value} and {dest_y} <= source_x + {value} |
|
124 |
- |
|
125 |
- ''') |
|
126 |
- result=cur.fetchall() |
|
127 |
- return result |
|
128 |
- |
|
129 |
- def db_get_address(self,dest1): |
|
130 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
131 |
- |
|
132 |
- cur.execute(f''' |
|
133 |
- select j.q,li.q ,li.location_cen_x_4623,li.location_cen_y_4623,li.location_exit_x_4623 ,li.location_exit_y_4623 |
|
134 |
- from "{self.schema}".jibun j, "{self.schema}".location_info li |
|
135 |
- where j.build_num =li.build_code and (j.q='{dest1}' or li.q='{dest1}' or li.build_name_city like '{dest1}') |
|
136 |
- ''') |
|
137 |
- result=cur.fetchone() |
|
138 |
- print( (float(result[2]),float(result[3]))) |
|
139 |
- |
|
140 |
- return (float(result[2]),float(result[3])) |
|
141 |
- |
|
142 |
- def db_add_report(self,report_id,report_x,report_y) : |
|
143 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
144 |
- now=time.localtime() |
|
145 |
- d=time.strftime('%Y-%m-%d %X', now) |
|
146 |
- cur.execute(f''' |
|
147 |
- insert into "{self.schema}".report (report_id,report_x,report_y,timestamp) |
|
148 |
- values ('{report_id}','{report_x}','{report_y}','{d}') |
|
149 |
- ''') |
|
150 |
- |
|
151 |
- def db_get_near_point(self,dest_x,dest_y): |
|
152 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
153 |
- now=datetime.now() |
|
154 |
- d_plus=now +timedelta(hours=1) |
|
155 |
- d_plus=str("'"+d_plus.strftime('%Y-%m-%d %X')+"'") |
|
156 |
- d_minus=now -timedelta(hours=1) |
|
157 |
- d_minus=str("'"+d_minus.strftime('%Y-%m-%d %X')+"'") |
|
158 |
- cur.execute(f''' |
|
159 |
- select report_x, report_y |
|
160 |
- from "{self.schema}".report |
|
161 |
- where {dest_y} > report_y - 0.000498 and {dest_y} <= report_y + 0.000498 |
|
162 |
- and {dest_x} > report_x - 0.000498 and {dest_x} <= report_x + 0.000498 |
|
163 |
- and timestamp between {d_minus} and {d_plus}; |
|
336 |
+ except psycopg2.Error as e: |
|
337 |
+ self.conn.rollback() |
|
338 |
+ return {'status': 'error', 'message': str(e)}, 500 |
|
164 | 339 |
|
165 | 340 |
|
166 |
- ''') |
|
167 |
- result=cur.fetchall() |
|
168 |
- return result |
|
169 |
- |
|
170 |
- |
|
171 |
- def db_add_pothole(self,pothole_id,pothole_location_x,pothole_location_y) : |
|
172 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
173 |
- now=datetime.now() |
|
174 |
- d=now.strftime('%Y-%m-%d %X') |
|
175 |
- cur.execute(f''' |
|
176 |
- insert into "{self.schema}".pothole (pothole_id,pothole_location_x,pothole_location_y,timestamp) |
|
177 |
- values ('{pothole_id}','{pothole_location_x}','{pothole_location_y}','{d}') |
|
178 |
- ''') |
|
179 |
- |
|
180 |
- def db_delete_pothole(self,dest_x,dest_y) : |
|
181 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
182 |
- now=datetime.now() |
|
183 |
- d_plus=now +timedelta(hours=1) |
|
184 |
- d_plus=str("'"+d_plus.strftime('%Y-%m-%d %X')+"'") |
|
185 |
- d_minus=now -timedelta(hours=1) |
|
186 |
- d_minus=str("'"+d_minus.strftime('%Y-%m-%d %X')+"'") |
|
187 |
- cur.execute(f''' |
|
188 |
- delete from "{self.schema}".pothole |
|
189 |
- where {dest_y} > pothole_location_y - 0.000498 and {dest_y} <= pothole_location_y + 0.000498 |
|
190 |
- and {dest_x} > pothole_location_x - 0.000498 and {dest_x} <= pothole_location_x + 0.000498 |
|
191 |
- and timestamp between {d_minus} and {d_plus}; |
|
192 |
- ''') |
|
193 |
- |
|
194 |
- def db_display_pothole(self) : |
|
195 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
196 |
- cur.execute(f''' |
|
197 |
- select report_x,report_y from "{self.schema}".report |
|
198 |
- ''') |
|
199 |
- result=cur.fetchall() |
|
200 |
- return result |
|
201 |
- |
|
202 |
- def insert_gps_data(self, csv_block, columns): |
|
203 |
- cur = self.conn.cursor() |
|
204 |
- data = StringIO(csv_block) |
|
205 |
- |
|
206 |
- # using COPY instead of INSERT to do even less operation per data. |
|
207 |
- cur.copy_from(data, 'gps_data', sep=',', columns = columns) |
|
208 |
- self.conn.commit() |
|
209 |
- cur.close() |
|
210 |
- return True |
|
211 |
- |
|
212 | 341 |
def close_connection(self): |
213 |
- cur = self.conn.cursor() |
|
342 |
+ cur = self.cur |
|
214 | 343 |
cur.close() |
215 | 344 |
return True |
216 | 345 |
|
+++ database/key_gen.py
... | ... | @@ -0,0 +1,18 @@ |
1 | +import os | |
2 | + | |
3 | +# NEVER be the part of server script, THIS SHOULD NEVER run with server. | |
4 | +# ALSO, remember to BACKUP the key | |
5 | + | |
6 | +def create_and_save_key(key_file_path): | |
7 | + """ | |
8 | + Generates a new AES encryption key and saves it to a file. | |
9 | + """ | |
10 | + key = os.urandom(32) # AES-256 requires a 32-byte key | |
11 | + with open(key_file_path, 'wb') as key_file: | |
12 | + key_file.write(key) | |
13 | + print(f"Encryption key created and saved to {key_file_path}") | |
14 | + return key | |
15 | + | |
16 | +if __name__ == "__main__": | |
17 | + from datetime import datetime | |
18 | + create_and_save_key(f"keys/encryption_key{datetime.now().strftime('%Y-%m-%d_%H:%M:%S')}")(파일 끝에 줄바꿈 문자 없음) |
--- gcity.py
... | ... | @@ -1,153 +0,0 @@ |
1 | -import networkx as nx | |
2 | -import math | |
3 | -from itertools import tee | |
4 | -from numpy import Inf, Infinity, inf | |
5 | -from database.database import DB | |
6 | -import pandas as pd | |
7 | -from haversine import haversine | |
8 | -import time | |
9 | -import pandas as pd | |
10 | -import os | |
11 | - | |
12 | -paths= os.getcwd() | |
13 | - | |
14 | -def dist(a, b): | |
15 | - (x1, y1) = a | |
16 | - (x2, y2) = b | |
17 | - return ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5 | |
18 | - | |
19 | -def swith_xy(tuples): | |
20 | - x,y=tuples | |
21 | - return (y,x) | |
22 | - | |
23 | -def pairwise( iterable ): | |
24 | - """Returns an iterable access binary tuple | |
25 | - s -> (s0,s1), (s1,s2), (s2, s3), ...""" | |
26 | - a, b = tee( iterable ) | |
27 | - next(b, None) | |
28 | - return zip(a, b) | |
29 | - | |
30 | - | |
31 | - | |
32 | -class path_finder(): | |
33 | - def __init__(self): | |
34 | - start_time=time.time() | |
35 | - self.db=DB() | |
36 | - self.G=nx.read_gpickle(paths + '\\navigation_model\\OSM_gpickle.gpickle') | |
37 | - | |
38 | - print("done") | |
39 | - print(time.time()-start_time) | |
40 | - | |
41 | - | |
42 | - | |
43 | - def get_trip(self,dest1,dest2): | |
44 | - | |
45 | - start_time=time.time() | |
46 | - dest1=swith_xy(self.db.db_get_dest(dest1)) | |
47 | - dest2=swith_xy(self.db.db_get_dest(dest2)) | |
48 | - value=0.0001 | |
49 | - | |
50 | - start_near_nodes=[] | |
51 | - while start_near_nodes == []: | |
52 | - value=value*10 | |
53 | - start_near_nodes=self.db.db_get_near_node(dest1[1],dest1[0],value) | |
54 | - else: | |
55 | - start_near_nodes=self.db.db_get_near_node(dest1[1],dest1[0],value) | |
56 | - | |
57 | - nn_start = None | |
58 | - nn_end = None | |
59 | - start_delta = float("inf") | |
60 | - end_delta = float("inf") | |
61 | - | |
62 | - | |
63 | - | |
64 | - for n in start_near_nodes: | |
65 | - s_dist = haversine(dest1, n) | |
66 | - if s_dist < start_delta : | |
67 | - nn_start = n | |
68 | - start_delta = s_dist | |
69 | - value=0.0001 | |
70 | - end_near_nodes=[] | |
71 | - while end_near_nodes==[]: | |
72 | - value=value*10 | |
73 | - self.db.db_get_near_node(dest2[1],dest2[0],value) | |
74 | - end_near_nodes=self.db.db_get_near_node(dest2[1],dest2[0],value) | |
75 | - | |
76 | - for n in end_near_nodes: | |
77 | - e_dist = haversine(dest2, n) | |
78 | - if e_dist < end_delta : | |
79 | - nn_end = n | |
80 | - end_delta = e_dist | |
81 | - | |
82 | - path = list(nx.astar_path(self.G,nn_start,nn_end,heuristic=dist,weight='length')) | |
83 | - return path | |
84 | - | |
85 | - def get_dest(self, dest1): | |
86 | - dest1=swith_xy(self.db.db_get_address(dest1)) | |
87 | - return dest1 | |
88 | - | |
89 | -db=DB() | |
90 | -df = pd.read_csv('D:\\takensoft\\project2\\경산 길찾기\\경산시_체크.csv',encoding='euc-kr') | |
91 | -li_start=[] | |
92 | -li_dest1=[] | |
93 | -for i in range(len(df)): | |
94 | - try: | |
95 | - print(i) | |
96 | - dest1=df['start'][i] | |
97 | - li_dest1.append(dest1) | |
98 | - dest1=swith_xy(db.db_get_dest(dest1)) | |
99 | - value=0.0001 | |
100 | - start_near_nodes=[] | |
101 | - while start_near_nodes == []: | |
102 | - value=value*10 | |
103 | - start_near_nodes=db.db_get_near_node(dest1[1],dest1[0],value) | |
104 | - nn_start = None | |
105 | - start_delta = float("inf") | |
106 | - for n in start_near_nodes: | |
107 | - s_dist = haversine(dest1, n) | |
108 | - if s_dist < start_delta : | |
109 | - nn_start = n | |
110 | - start_delta = s_dist | |
111 | - li_start.append(nn_start) | |
112 | - except: | |
113 | - continue | |
114 | - | |
115 | -df_check=pd.DataFrame({'start':li_dest1,'시작지점':li_start}) | |
116 | - | |
117 | -df_check.to_csv('test.csv',encoding='euc-kr') | |
118 | - | |
119 | - | |
120 | -''' | |
121 | -df=pd.read_csv('D:\\takensoft\\project2\\경산 길찾기\\경산시.csv',encoding='euc-kr') | |
122 | - | |
123 | -p=path_finder() | |
124 | -li_path=[] | |
125 | -for i in range(len(df)): | |
126 | - try: | |
127 | - if i%100 ==0: | |
128 | - print(i) | |
129 | - df2=pd.DataFrame(li_path) | |
130 | - df2.to_csv(f'D:\\takensoft\\project2\\경산 길찾기\\길찾기 결과{i}.csv',encoding='euc-kr') | |
131 | - li_path=[] | |
132 | - start=df['start'][i] | |
133 | - end=df['end'][i] | |
134 | - li_path.append(p.get_trip(start,end)) | |
135 | - except: | |
136 | - continue | |
137 | -li_start_x = [] | |
138 | -li_start_y = [] | |
139 | -li_end_x = [] | |
140 | -li_end_y = [] | |
141 | - | |
142 | -db=DB() | |
143 | -#df.to_csv('D:\\takensoft\\project2\\경산 길찾기\\길찾기 결과.csv',encoding='euc-kr') | |
144 | - | |
145 | -df=pd.read_csv('D:\\takensoft\\project2\\경산 길찾기\\경산시.csv',encoding='euc-kr') | |
146 | -for i in range(len(df)): | |
147 | - li_start_x.append(db.db_get_dest(df['start'][i])[0]) | |
148 | - li_start_y.append(db.db_get_dest(df['start'][i])[1]) | |
149 | - li_end_x.append(db.db_get_dest(df['end'][i])[0]) | |
150 | - li_end_y.append([db.db_get_dest(df['end'][i])[1]]) | |
151 | -df2 = pd.DataFrame({'start_point_x':li_start_x,'start_point_y':li_start_y,'end_point_x':li_end_x,'end_point_y':li_end_y}) | |
152 | -df2.to_csv('D:\\takensoft\\project2\\경산 길찾기\\출발지도착지좌표.csv',encoding='euc-kr') | |
153 | -'''(파일 끝에 줄바꿈 문자 없음) |
--- requirements.txt
... | ... | @@ -1,41 +0,0 @@ |
1 | -aniso8601==9.0.1 | |
2 | -attrs==23.2.0 | |
3 | -blinker==1.8.2 | |
4 | -cachelib==0.9.0 | |
5 | -click==8.1.7 | |
6 | -Flask==3.0.3 | |
7 | -Flask-Caching==2.3.0 | |
8 | -flask-restx==1.3.0 | |
9 | -haversine==2.8.1 | |
10 | -imageio==2.34.1 | |
11 | -importlib_resources==6.4.0 | |
12 | -itsdangerous==2.2.0 | |
13 | -Jinja2==3.1.4 | |
14 | -joblib==1.4.2 | |
15 | -jsonschema==4.22.0 | |
16 | -jsonschema-specifications==2023.12.1 | |
17 | -lazy_loader==0.4 | |
18 | -MarkupSafe==2.1.5 | |
19 | -networkx==3.3 | |
20 | -numpy==1.23.3 | |
21 | -opencv-python==4.10.0.82 | |
22 | -packaging==24.1 | |
23 | -pandas==2.2.2 | |
24 | -pillow==10.3.0 | |
25 | -psycopg2-binary==2.9.9 | |
26 | -PyJWT==2.8.0 | |
27 | -python-dateutil==2.9.0.post0 | |
28 | -pytz==2024.1 | |
29 | -referencing==0.35.1 | |
30 | -rpds-py==0.18.1 | |
31 | -scikit-image==0.23.2 | |
32 | -scikit-learn==1.5.0 | |
33 | -scipy==1.13.1 | |
34 | -six==1.16.0 | |
35 | -threadpoolctl==3.5.0 | |
36 | -tifffile==2024.5.22 | |
37 | -torch==1.12.1 | |
38 | -typing_extensions==4.12.2 | |
39 | -tzdata==2024.1 | |
40 | -Werkzeug==3.0.3 | |
41 | - |
--- trip.py
... | ... | @@ -1,78 +0,0 @@ |
1 | -from flask import request | |
2 | -from flask_restx import Resource, Api, Namespace, fields | |
3 | -from navigation_model.model_trip import path_finder | |
4 | -import time | |
5 | -import numpy as np | |
6 | -import networkx | |
7 | - | |
8 | -todos = {} | |
9 | -count = 1 | |
10 | - | |
11 | -not_in_list=[] | |
12 | - | |
13 | - | |
14 | -#trip = path_finder() | |
15 | - | |
16 | - | |
17 | - | |
18 | -Trip = Namespace( | |
19 | - name="trip", | |
20 | - description="경로 노드를 받기위한 사용하는 API.", | |
21 | -) | |
22 | - | |
23 | -trip_fields = Trip.model('Trip', { # Model 객체 생성 | |
24 | - 'path_start': fields.String(description='a Todo', required=True),'path_end' : fields.String(description='a Todo', required=True) | |
25 | -}) | |
26 | - | |
27 | - | |
28 | - | |
29 | -@Trip.route('/trip') | |
30 | -class TripPost(Resource): | |
31 | - @Trip.expect(trip_fields) | |
32 | - @Trip.response(201, 'Success', trip_fields) | |
33 | - def post(self): | |
34 | - """경로를 받습니다""" | |
35 | - start_time=time.time() | |
36 | - start = request.json['path_start'] | |
37 | - end = request.json['path_end'] | |
38 | - | |
39 | - return { | |
40 | - 'nodes' : trip.get_trip(start,end), | |
41 | - 'start_point' : trip.get_dest(start), | |
42 | - 'end_point' : trip.get_dest(end) | |
43 | - | |
44 | - | |
45 | - }, 201 | |
46 | -@Trip.route('/trip2') | |
47 | -class TripPost(Resource): | |
48 | - @Trip.expect(trip_fields) | |
49 | - @Trip.response(201, 'Success', trip_fields) | |
50 | - def post(self): | |
51 | - """경로를 받습니다""" | |
52 | - start_time=time.time() | |
53 | - start_x = request.json['dest1_x'] | |
54 | - start_y = request.json['dest1_y'] | |
55 | - end = request.json['path_end'] | |
56 | - return { | |
57 | - 'nodes' : trip.get_trip_2(start_x,start_y,end,not_in_list), | |
58 | - 'start_point' : (start_x,start_y), | |
59 | - 'end_point' : trip.get_dest(end) | |
60 | - | |
61 | - | |
62 | - }, 201 | |
63 | - | |
64 | - | |
65 | -@Trip.route('/remove') | |
66 | -class removenodePost(Resource): | |
67 | - def post(self): | |
68 | - """경로를 받습니다""" | |
69 | - gps_x = request.json['gps_x'] | |
70 | - gps_y = request.json['gps_y'] | |
71 | - trip.G.remove_node((float(gps_x),float(gps_y))) | |
72 | - not_in_list.append((float(gps_x),float(gps_y))) | |
73 | - | |
74 | - return { | |
75 | - "done" : "done" | |
76 | - }, 201 | |
77 | - | |
78 | - |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?