|
import os |
|
import re |
|
from datetime import datetime |
|
from flask import Flask, request, jsonify |
|
from flask_cors import CORS |
|
import psycopg2 |
|
from psycopg2 import pool |
|
from typing import Optional, Dict, List |
|
|
|
app = Flask(__name__) |
|
CORS(app) |
|
|
|
|
|
DB_URL = os.getenv('SUPABASE_DB_URL') |
|
connection_pool = psycopg2.pool.SimpleConnectionPool( |
|
minconn=1, |
|
maxconn=5, |
|
dsn=DB_URL |
|
) |
|
|
|
class QueryBuilder: |
|
"""فئة مسؤولة عن بناء استعلامات SQL الآمنة""" |
|
|
|
@staticmethod |
|
def get_last_office_visit(cam_mac: str) -> str: |
|
"""استعلام عن آخر زيارة للمكتب""" |
|
return f""" |
|
SELECT caption, created_at, image_url |
|
FROM data |
|
WHERE cam_mac = '{cam_mac}' |
|
AND (caption LIKE '%مكتب%' OR caption LIKE '%عمل%') |
|
ORDER BY created_at DESC |
|
LIMIT 1 |
|
""" |
|
|
|
@staticmethod |
|
def get_last_five_places(cam_mac: str) -> str: |
|
"""استعلام عن آخر 5 أماكن مميزة""" |
|
return f""" |
|
SELECT DISTINCT ON (caption) caption, created_at, latitude, longitude, image_url |
|
FROM data |
|
WHERE cam_mac = '{cam_mac}' |
|
AND caption IS NOT NULL |
|
ORDER BY caption, created_at DESC |
|
LIMIT 5 |
|
""" |
|
|
|
@staticmethod |
|
def check_garden_visit(cam_mac: str) -> str: |
|
"""استعلام عن زيارة الحديقة""" |
|
return f""" |
|
SELECT caption, created_at, image_url |
|
FROM data |
|
WHERE cam_mac = '{cam_mac}' |
|
AND (caption LIKE '%حديقة%' OR caption LIKE '%حدائق%' OR caption LIKE '%نباتات%') |
|
ORDER BY created_at DESC |
|
LIMIT 1 |
|
""" |
|
|
|
@staticmethod |
|
def check_classroom_visit(cam_mac: str) -> str: |
|
"""استعلام عن زيارة صف دراسي""" |
|
return f""" |
|
SELECT caption, created_at, image_url |
|
FROM data |
|
WHERE cam_mac = '{cam_mac}' |
|
AND (caption LIKE '%صف%' OR caption LIKE '%قاعة%' OR caption LIKE '%محاضرة%' |
|
OR caption LIKE '%جامعة%' OR caption LIKE '%مدرسة%') |
|
ORDER BY created_at DESC |
|
LIMIT 1 |
|
""" |
|
|
|
class DatabaseExecutor: |
|
"""فئة مسؤولة عن تنفيذ الاستعلامات وإدارة الاتصالات""" |
|
|
|
@staticmethod |
|
def execute_query(sql: str) -> List[Dict]: |
|
"""تنفيذ استعلام SQL وإرجاع النتائج""" |
|
conn = None |
|
try: |
|
conn = connection_pool.getconn() |
|
cursor = conn.cursor() |
|
cursor.execute(sql) |
|
|
|
if cursor.description: |
|
columns = [desc[0] for desc in cursor.description] |
|
return [dict(zip(columns, row)) for row in cursor.fetchall()] |
|
return [] |
|
finally: |
|
if conn: |
|
connection_pool.putconn(conn) |
|
|
|
@app.route('/query', methods=['POST']) |
|
def handle_query(): |
|
try: |
|
data = request.get_json() |
|
cam_mac = data.get('cam_mac') |
|
question = data.get('question') |
|
|
|
if not cam_mac or not question: |
|
return jsonify({"error": "يجب تقديم cam_mac و question"}), 400 |
|
|
|
|
|
cam_mac = re.sub(r'[^a-fA-F0-9:]', '', cam_mac) |
|
question = question.strip().lower() |
|
|
|
|
|
sql = None |
|
|
|
if 'مكتب' in question or 'عمل' in question: |
|
sql = QueryBuilder.get_last_office_visit(cam_mac) |
|
elif 'خمس أماكن' in question or 'آخر أماكن' in question: |
|
sql = QueryBuilder.get_last_five_places(cam_mac) |
|
elif 'حديقة' in question or 'حدائق' in question: |
|
sql = QueryBuilder.check_garden_visit(cam_mac) |
|
elif 'صف دراسي' in question or 'محاضرة' in question or 'قاعة' in question: |
|
sql = QueryBuilder.check_classroom_visit(cam_mac) |
|
|
|
if not sql: |
|
return jsonify({"error": "نوع السؤال غير معروف"}), 400 |
|
|
|
|
|
results = DatabaseExecutor.execute_query(sql) |
|
|
|
return jsonify({ |
|
"question": question, |
|
"results": results, |
|
"timestamp": datetime.now().isoformat() |
|
}) |
|
|
|
except psycopg2.Error as e: |
|
return jsonify({"error": "خطأ في قاعدة البيانات", "details": str(e)}), 500 |
|
except Exception as e: |
|
return jsonify({"error": "خطأ غير متوقع", "details": str(e)}), 500 |
|
|
|
@app.route('/health', methods=['GET']) |
|
def health_check(): |
|
try: |
|
conn = connection_pool.getconn() |
|
conn.close() |
|
return jsonify({"status": "يعمل بنجاح"}) |
|
except Exception as e: |
|
return jsonify({"status": "غير متصل", "error": str(e)}), 500 |
|
|
|
if __name__ == '__main__': |
|
app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 5000))) |