-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.py
370 lines (321 loc) · 15.2 KB
/
server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
import uvicorn
import sqlite3
from fastapi import FastAPI, File, HTTPException, Request
from linebot import LineBotApi, WebhookHandler
from bot import ImgSearchBotLine
from imageSearch import ImageSearcher
from detection import ImageProcessor
import cv2
import numpy as np
from dotenv import load_dotenv
import os
import logging
from fastapi.responses import FileResponse
import time
import warnings
from linebot import LineBotSdkDeprecatedIn30
from math import sqrt
from fastapi import BackgroundTasks
import json
import asyncio
from PIL import Image
Image.MAX_IMAGE_PIXELS = None
from fastapi.staticfiles import StaticFiles
from fastapi import Form, UploadFile
from fastapi.responses import JSONResponse
from fastapi.middleware.cors import CORSMiddleware
from fastapi.templating import Jinja2Templates
# Load .env file
load_dotenv('./.env')
ip: str = os.getenv('IP')
token: str = os.getenv('TOKEN')
chanel_secret: str = os.getenv('CHANNEL_SECRET')
ip_url: str = os.getenv('IP_URL')
print('IP:', ip)
print('IP_URL:', ip_url)
# Create a new FastAPI instance.
app = FastAPI()
app.add_middleware(
CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]
)
# กำหนด path สำหรับเก็บไฟล์และเปิดใช้ caching
app.mount("/imgsearch", StaticFiles(directory="./imgsearch", html=False), name="imgsearch")
# Create a new LineBotApi object
line_bot_api = LineBotApi(token)
# Create a new WebhookHandler object
webhook_handler = WebhookHandler(chanel_secret)
# Suppress warning messages
warnings.filterwarnings("ignore", category=LineBotSdkDeprecatedIn30)
# logging in json format file
# [{time: time, level: level, message: message}, ...]
logging.basicConfig(filename='./log.json', filemode='a', format='%(asctime)s %(levelname)s %(message)s', datefmt='%d-%b-%y %H:%M:%S', level=logging.INFO)
image_searcher = ImageSearcher()
image_searcher.set_model('clip-ViT-B-32')
logging.info('set model success')
image_searcher.load_model()
logging.info('load model success')
BotLine = ImgSearchBotLine(token, chanel_secret, ip_url)
class UserDataManager:
def __init__(self):
self.users_data = {}
self.locks = {}
self.load_users_data()
def load_users_data(self):
try:
with open('./users_data.json', 'r', encoding='utf-8') as file:
self.users_data = json.load(file)
except (FileNotFoundError, json.JSONDecodeError):
self.users_data = {}
async def update_user_phase(self, user_id, phase):
async with await self.get_lock(user_id):
self.users_data[user_id] = {'Phase': phase}
await self.save_users_data()
async def save_users_data(self):
# Write user data to file asynchronously
with open('./users_data.json', 'w', encoding='utf-8') as file:
json.dump(self.users_data, file, ensure_ascii=False, indent=4)
async def remove_user_data(self, user_id):
async with await self.get_lock(user_id):
if user_id in self.users_data:
del self.users_data[user_id]
await self.save_users_data()
async def get_lock(self, user_id):
if user_id not in self.locks:
self.locks[user_id] = asyncio.Lock()
return self.locks[user_id]
@app.get('/')
async def index():
return {'message': 'Hello, World!'}
# Create an instance of UserDataManager
user_data_manager = UserDataManager()
async def process(body):
# Get the event type
events = body['events']
# date and time in format Day month year,HH:MM:SS
current_time = time.strftime("%a %b %d %Y,%H:%M:%S", time.localtime())
print(events)
user_id = body['events'][0]['source']['userId']
if body['events'][0]['message']['type'] == 'text':
message_type = 'text'
message_body = (body['events'][0]['message']['text'])
elif body['events'][0]['message']['type'] == 'image':
message_type = 'image'
message_body = (body['events'][0]['message']['id'])
else:
message_type = 'None'
message_body = 'None'
log_message = (
f"---------Webhook Event---------\n"
f"Current Time: {current_time}\n"
f"UserID: {user_id}\n"
f"Message Type: {message_type}\n"
f"Message Text: {message_body}\n"
f"Phase: {user_data_manager.users_data.get(user_id, {}).get('Phase', 'None')}\n"
f"--------------------------------\n"
)
# Handle webhook body
try:
if body['events'][0]['message']['type'] == 'text':
user_id = body['events'][0]['source']['userId']
message = body['events'][0]['message']['text']
logging.info('Receive message from UserID: ' + user_id + ' Message: ' + message)
if message == 'ค้นหาด้วยภาพ':
# ImgSearchBotLine.push(user_id, 'ส่งภาพมาเลยจ้า')
reply_token = body['events'][0]['replyToken']
result = BotLine.reply(reply_token, 'ส่งรูปภาพที่ต้องการหามาได้เลยนะค้าบบ 😊')
# Check the result
if result == 'Success':
print("Reply sent successfully.")
user_data_manager.users_data[user_id] = {'Phase': 'Waiting for image'}
await user_data_manager.save_users_data()
logging.info(log_message)
else:
print("Reply sending failed. Error:", result)
return {'message': 'success'}
print(user_data_manager.users_data.get(user_id, {}).get('Phase', 'None'))
if message == 'คำแนะนำการใช้งาน':
BotLine.push_image(user_id, ip_url+'assets/instruction.jpg')
print('Return "คำแนะนำการใช้งาน" to user')
return {'message': 'success'}
elif body['events'][0]['message']['type'] == 'image' and user_data_manager.users_data.get(user_id, {}).get('Phase', 'None') == 'Waiting for image':
user_id = body['events'][0]['source']['userId']
image_id = body['events'][0]['message']['id']
image_content = line_bot_api.get_message_content(image_id)
logging.info('Receive image from UserID: ' + user_id + ' ImageID: ' + image_id)
starttime = time.time()
# send to crop in ImgArgumentation
imgArgumentation = ImageProcessor()
# Get the image content
image_bytes = image_content.content
# Convert the image content to a numpy array
nparr = np.frombuffer(image_bytes, np.uint8)
# Convert the numpy array to an OpenCV image object
img_np = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
# Convert the image from BGR to RGB
img_rgb = cv2.cvtColor(img_np, cv2.COLOR_BGR2RGB)
# Set the image
imgArgumentation.set_img(img_rgb)
endtime = time.time()
print('Set image time and convert to RGB:', endtime - starttime)
image_content = imgArgumentation.preprocess_and_crop_image()
endtime = time.time()
print('Preprocess and crop image time:', endtime - starttime)
try:
image_searcher.set_target(image_content)
print('set target success')
except Exception as e:
print(e)
return {'message': 'error'}
response = image_searcher.run_test()
# response is json
# in format [{'most_similar_image_path': most_similar_image_path, 'score': score}, ...]
# most_similar_image_path is path of image
# score is score of image
# get image from response and send to user
flex_data = []
for data in response:
# if some data is None replace with ''
for key in data:
if data[key] is None or data[key] == '':
data[key] = 'NONE'
print(f'Artwork ID: {data["artwork_id"]} Artwork Name: {data["artwork_name"]} Score: {data["score"]}')
flex_data.append({'artwork_id': data['artwork_id'],
'artwork_name': data['artwork_name'],
'artist_name': data['artist_name'],
'artwork_type': data['artwork_type'],
'artwork_size': data['artwork_size'],
'artwork_technique': data['artwork_technique'],
'exhibition_name': data['exhibition_name'],
'award_name': data['award_name'],
'license': data['license'],
'concept': data['concept'],
'detail': data['detail'],
'image_url': data['image_url'],
'url': data['url'],
'score': data['score']})
# print(json.dumps(flex_data, indent=4, ensure_ascii=False))
# create carousel
caraousel = BotLine.create_carousel(flex_data)
# send carousel to user
x = BotLine.push_flex(user_id, caraousel)
if x == 'Success':
print("Reply sent successfully.")
else:
print("Reply sending failed. Error:", x)
BotLine.push(user_id, f'ระบบขัดข้อง : {x} กรุณารายงานปัญหาเพื่อแก้ไขให้เร็วที่สุด')
# update user phase
await user_data_manager.update_user_phase(user_id, 'Image sent')
print('Image sent')
# ------- for debug -------
print(log_message)
logging.info(log_message)
totaltime = time.time() - starttime
print('Total Time:', totaltime)
# -------------------------
# remove user data
await user_data_manager.remove_user_data(user_id)
return {'message': 'success'}
else:
user_id = body['events'][0]['source']['userId']
message_body = (body['events'][0]['message']['text']) if body['events'][0]['message']['type'] == 'text' else 'None'
logging.info('Receive message from UserID: ' + user_id + ' Message: ' + message_body)
# push message to user
BotLine.push(user_id, 'กรุณาเลือกเมนูคำสั่งก่อนน้าา🥰')
print('Return "กรุณาเลือกเมนูคำสั่งก่อนน้าา🥰" to user')
# return status 200
return {'message': 'success'}
except Exception as e:
print(e)
return {'message': 'error'}
# return status 200
return {'message': 'success'}
# Define a route to handle webhook requests
@app.post("/webhook")
async def webhook(request: Request, background_tasks: BackgroundTasks):
# Parse the webhook event
body = await request.json()
# Get the X-Line-Signature header value
signature = request.headers['X-Line-Signature']
# Handle webhook body test
# try:
# webhook_handler.handle(body, signature)
# except Exception as e:
# print(e)
# return {'message': 'error'}
# return {'message': 'success'}
print('Have a new request!!')
background_tasks.add_task(process, body)
return {"message": "Notification sent in the background"}
# processing = await process(body)
# return processing
# Define a route to handle user requests.
@app.post('/user/{user_id}')
async def handle_user(user_id: int):
# Do some processing.
# Return a response to the user.
return {'message': 'Request received '+str(user_id)}
@app.get('/assets/{filename}')
async def assets(filename: str):
return FileResponse('./assets/'+filename)
@app.get('/imgsearch/{image_name}')
async def handle_image(image_name: str):
# Load the image
image_path = './imgsearch/' + image_name
# Return the image
return FileResponse(image_path)
# Point to the directory containing your templates
templates = Jinja2Templates(directory="templates")
# pass the ip to html
@app.get('/report')
async def report(request: Request):
return templates.TemplateResponse("report.html", {"request": request, "ip_url": ip_url})
@app.post("/submit_report")
async def submit_report(header: str = Form(...), description: str = Form(...), image: UploadFile = File(...)):
try:
upload_folder = "./uploaded_images"
os.makedirs(upload_folder, exist_ok=True)
image_path = os.path.join(upload_folder, image.filename)
with open(image_path, "wb") as buffer:
buffer.write(image.file.read())
conn = sqlite3.connect('report.db')
cursor = conn.cursor()
cursor.execute('CREATE TABLE IF NOT EXISTS reports (id INTEGER PRIMARY KEY AUTOINCREMENT, header TEXT, description TEXT, image_filename TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)')
cursor.execute('INSERT INTO reports (header, description, image_filename) VALUES (?, ?, ?)', (header, description, image.filename))
last_inserted_id = cursor.lastrowid
new_image_filename = f"{last_inserted_id}.jpg"
os.rename(image_path, os.path.join(upload_folder, new_image_filename))
cursor.execute('UPDATE reports SET image_filename = ? WHERE id = ?', (new_image_filename, last_inserted_id))
conn.commit()
conn.close()
return JSONResponse(content={"message": "Report submitted successfully"})
except Exception as e:
return JSONResponse(status_code=500, content={"message": f"Error: {str(e)}"})
# Start the FastAPI server.
if __name__ == '__main__':
# # Suppress warning messages
# warnings.filterwarnings("ignore", category=LineBotSdkDeprecatedIn30)
# image_searcher = ImageSearcher()
# image_searcher.set_model('clip-ViT-B-32')
# logging.info('set model success')
# image_searcher.load_model()
# logging.info('load model success')
# BotLine = ImgSearchBotLine(token, chanel_secret)
# # Load the image names jpg and jpeg
# image_names = list(glob.glob('./imgsearch/*.jpg') + glob.glob('./imgsearch/*.jpeg') + glob.glob('./imgsearch/*.png'))
# # Set the image names
# image_searcher.set_image_names(image_names)
# logging.info('set image_names success')
# Load the encoded images
# image_searcher.load_images()
# logging.info('load images success')
# Create a new ImgSearchBotLine object
# num_cpus = multiprocessing.cpu_count()
try:
# num_workers = num_cpus - 1 if num_cpus > 1 else 1
# print(f'Number of workers: {num_workers}')
uvicorn.run('server:app', host=ip, port=8080)
# uvicorn.run('server:app', host='localhost', port=8080,ssl_keyfile='./key.pem', ssl_certfile='./cert.pem')
# subprocess.run(["uvicorn", "server:app", "--host", "localhost", "--port", "8080", "--workers", str(num_workers)])
except Exception as e:
print('Launch server failed:', e)
exit(0)