File size: 8,868 Bytes
53f2234
 
 
 
 
 
 
 
 
 
 
 
978c7aa
53f2234
 
 
978c7aa
2e28bbb
978c7aa
4624c23
53f2234
 
b686751
53f2234
 
b686751
 
 
 
53f2234
b686751
fa31ff5
53f2234
fa31ff5
 
 
 
53f2234
fa31ff5
 
53f2234
b686751
fa31ff5
53f2234
fa31ff5
 
 
 
b686751
fa31ff5
 
 
 
 
 
 
 
 
 
 
 
b686751
fa31ff5
 
 
 
 
 
 
 
b686751
fa31ff5
 
 
 
 
 
 
978c7aa
 
fa31ff5
 
b686751
 
fa31ff5
 
b686751
 
 
 
 
53f2234
 
 
978c7aa
 
 
 
 
 
 
 
fa31ff5
 
b686751
978c7aa
53f2234
b686751
53f2234
 
978c7aa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53f2234
 
 
 
 
 
 
 
 
 
 
 
 
 
 
978c7aa
53f2234
 
 
 
 
 
978c7aa
53f2234
 
 
 
 
978c7aa
 
 
4624c23
978c7aa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4624c23
978c7aa
 
 
 
 
 
 
 
 
bb92058
978c7aa
bb92058
978c7aa
 
 
 
 
bb92058
978c7aa
 
 
 
 
bb92058
978c7aa
e9f978c
53f2234
 
 
 
b686751
978c7aa
 
 
 
 
 
 
 
53f2234
 
 
978c7aa
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
import os
import re
import psycopg2
from flask import Flask, request, jsonify
import google.generativeai as genai
from flask import Response
import json

# --- إعدادات Flask ---
app = Flask(__name__)

# --- إعدادات Gemini ---
GEMINI_API_KEY = "AIzaSyCWukRy76nPgkrMflCTWh_s4gEU--wSVr8"  # يفضل استخدام متغيرات البيئة
genai.configure(api_key=GEMINI_API_KEY)
model = genai.GenerativeModel('gemini-2.0-flash')

# --- إعدادات Supabase ---
SUPABASE_DB_URL = "postgresql://postgres.dkscikruiwdmrsocggkr:76UaJhlvbM9Q0HXP@aws-1-eu-central-1.pooler.supabase.com:5432/postgres"
                  

# --- سكيمة قاعدة البيانات ---
DB_SCHEMA = """
CREATE TABLE public.section (
  id bigint GENERATED ALWAYS AS IDENTITY NOT NULL,
  created_at timestamp with time zone NOT NULL DEFAULT now(),
  name_en text,
  name_ar text,
  image_url text,
  CONSTRAINT section_pkey PRIMARY KEY (id)
);

CREATE TABLE public.category (
  id bigint GENERATED ALWAYS AS IDENTITY NOT NULL,
  created_at timestamp with time zone NOT NULL DEFAULT now(),
  section_id bigint NOT NULL,
  name_en text,
  name_ar text,
  image_url text,
  CONSTRAINT category_pkey PRIMARY KEY (id),
  CONSTRAINT category_section_id_fkey FOREIGN KEY (section_id) REFERENCES public.section(id)
);

CREATE TABLE public.offer (
  id bigint GENERATED ALWAYS AS IDENTITY NOT NULL,
  created_at timestamp with time zone NOT NULL DEFAULT now(),
  image_url text,
  CONSTRAINT offer_pkey PRIMARY KEY (id)
);

CREATE TABLE public.product (
  id bigint GENERATED ALWAYS AS IDENTITY NOT NULL,
  created_at timestamp with time zone NOT NULL DEFAULT now(),
  category_id bigint,
  name_en text,
  name_ar text,
  description text,
  quantity bigint,
  price double precision,
  CONSTRAINT product_pkey PRIMARY KEY (id),
  CONSTRAINT product_category_id_fkey FOREIGN KEY (category_id) REFERENCES public.category(id)
);

CREATE TABLE public.product_image (
  id bigint GENERATED ALWAYS AS IDENTITY NOT NULL,
  created_at timestamp with time zone NOT NULL DEFAULT now(),
  product_id bigint,
  image_url text,
  CONSTRAINT product_image_pkey PRIMARY KEY (id),
  CONSTRAINT product_image_product_id_fkey FOREIGN KEY (product_id) REFERENCES public.product(id)
);

CREATE TABLE public.promotion (
  id bigint GENERATED ALWAYS AS IDENTITY NOT NULL,
  created_at timestamp with time zone NOT NULL DEFAULT now(),
  name_en text,
  name_ar text,
  description text,
  discount_rate double precision,
  start_date timestamp without time zone,
  end_date timestamp without time zone,
  CONSTRAINT promotion_pkey PRIMARY KEY (id)
);

CREATE TABLE public.product_promotion (
  id bigint GENERATED ALWAYS AS IDENTITY NOT NULL,
  created_at timestamp with time zone NOT NULL DEFAULT now(),
  product_id bigint,
  promotion_id bigint,
  CONSTRAINT product_promotion_pkey PRIMARY KEY (id),
  CONSTRAINT product_promotion_product_id_fkey FOREIGN KEY (product_id) REFERENCES public.product(id),
  CONSTRAINT product_promotion_promotion_id_fkey FOREIGN KEY (promotion_id) REFERENCES public.promotion(id)
);
"""

# --- الاتصال بقاعدة البيانات ---
def get_db_connection():
    try:
        return psycopg2.connect(SUPABASE_DB_URL)
    except Exception as err:
        print(f"Database connection error: {err}")
        return None

# --- توليد SQL باستخدام Gemini ---
def generate_sql_gemini(natural_language_query):
    prompt = f"""You are a PostgreSQL expert for an e-commerce database.
Your job is to convert a natural language query into a SQL SELECT statement, based on the following database schema.

Database Schema:
{DB_SCHEMA}

Database Structure Description:

1. **section** (الأقسام الرئيسية)
   - تخزن الأقسام الرئيسية للمتجر
   - تحتوي على اسم بالإنجليزية والعربية وصورة
   - كل قسم يحتوي على عدة فئات

2. **category** (الفئات)
   - تخزن الفئات التابعة للأقسام
   - مرتبطة بالقسم الرئيسي عبر section_id
   - تحتوي على اسم بالإنجليزية والعربية وصورة

3. **product** (المنتجات)
   - تخزن معلومات المنتجات الأساسية
   - مرتبطة بالفئة عبر category_id
   - تحتوي على: الاسم، الوصف، الكمية، السعر

4. **product_image** (صور المنتجات)
   - تخزن الصور المتعددة لكل منتج
   - مرتبطة بالمنتج عبر product_id
   - كل منتج يمكن أن يحتوي على عدة صور

5. **promotion** (العروض والتخفيضات)
   - تخزن معلومات العروض والتخفيضات
   - تحتوي على: نسبة الخصم، تاريخ البداية، تاريخ النهاية

6. **product_promotion** (ربط المنتجات بالعروض)
   - جدول وسيط يربط المنتجات بالعروض
   - علاقة many-to-many بين المنتجات والعروض

7. **offer** (العروض الخاصة)
   - تخسن العروض الإعلانية الخاصة
   - تحتوي principalmente على صورة العرض

Important Relationships:
- category.section_id = section.id
- product.category_id = category.id  
- product_image.product_id = product.id
- product_promotion.product_id = product.id
- product_promotion.promotion_id = promotion.id

Query Generation Rules:
- Use ONLY SELECT statements - no INSERT, UPDATE, DELETE
- Use proper JOIN syntax with table aliases for readability
- Always use the exact table and column names from the schema
- Include relevant WHERE clauses for filtering when appropriate
- Use ORDER BY for sorting when relevant
- Use LIMIT for limiting results when appropriate
- Calculate discounted prices when promotions are involved: (price * (1 - discount_rate/100))
- Handle date filters for active promotions using CURRENT_DATE
- The output must contain ONLY the SQL query, no explanations or comments
- Always end with a semicolon

Question: "{natural_language_query}"

SQL:"""

    try:
        response = model.generate_content(prompt)
        sql = response.text.strip()

        # تنظيف الناتج
        sql = re.sub(r"^```sql\s*", "", sql, flags=re.IGNORECASE)
        sql = re.sub(r"\s*```$", "", sql)
        sql = re.sub(r"^SQL:\s*", "", sql, flags=re.IGNORECASE)

        if not sql.upper().startswith("SELECT"):
            sql = "SELECT " + sql.split("SELECT")[-1] if "SELECT" in sql else f"SELECT * FROM ({sql}) AS subquery"

        if not sql.endswith(";"):
            sql += ";"

        return sql
    except Exception as e:
        print(f"Gemini error: {e}")
        return None

# --- نقطة النهاية الرئيسية ---
@app.route('/api/query', methods=['POST'])
def handle_query():
    data = request.get_json()
    if not data or 'text' not in data:
        return jsonify({"error": "Please send 'text' in the request body"}), 400

    natural_query = data['text']
    print(f"Natural query: {natural_query}")
    
    sql_query = generate_sql_gemini(natural_query)

    if not sql_query:
        return jsonify({"error": "Failed to generate SQL query"}), 500

    print(f"Generated SQL: {sql_query}")

    if not sql_query.upper().strip().startswith("SELECT"):
        return jsonify({"error": "Only SELECT queries are allowed"}), 403

    conn = get_db_connection()
    if not conn:
        return jsonify({"error": "Database connection failed"}), 500

    cursor = None
    try:
        cursor = conn.cursor()
        cursor.execute(sql_query)
        columns = [desc[0] for desc in cursor.description]
        rows = cursor.fetchall()
        data = [dict(zip(columns, row)) for row in rows]

        response_data = {
            "data": data,
        }
        
        response_json = json.dumps(response_data, ensure_ascii=False)
        
        return Response(
            response_json,
            status=200,
            mimetype='application/json; charset=utf-8'
        )
        
    except Exception as e:
        print(f"SQL execution error: {e}")
        return jsonify({"error": str(e), "generated_sql": sql_query}), 500
    finally:
        if cursor:
            cursor.close()
        if conn:
            conn.close()

@app.route('/')
def home():
    return """
    <h1>E-Commerce Natural Language to SQL API</h1>
    <p>Use <code>/api/query</code> with POST {"text": "your question"}.</p>
    <p>Examples:</p>
    <ul>
        <li>"عرض جميع المنتجات في قسم الإلكترونيات"</li>
        <li>"أعرض المنتجات ذات التخفيضات النشطة"</li>
        <li="أعرض أحدث 10 منتجات"</li>
        <li>"أعرض الفئات التابعة للقسم الغذائي"</li>
    </ul>
    """

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=7860)