حلول الواجهة الخلفية


إجابات اختبار المعرفة

  1. jsonify() تحوّل قاموس Python إلى استجابة JSON وتضبط ترويسة Content-Type: application/json الصحيحة. وبدونها، قد تُرجع Flask صيغًا أو أخطاء غير متوقعة.

  2. البيئة الافتراضية تعزل تبعيات مشروعك عن Python النظام، فتمنع تعارض الإصدارات بين المشاريع.

  3. db.session.commit() تحفظ كل التغييرات المعلّقة في قاعدة البيانات. وبدونها تضيع التغييرات (الإضافات والتحديثات وعمليات الحذف) عند انتهاء الجلسة.

  4. nullable=False يعني أن العمود لا يمكن أن يكون NULL أو فارغًا في قاعدة البيانات. ويُرفع خطأ إذا حاولت حفظ سجل دون تقديم قيمة.

  5. ملفات .env تحتوي على أسرار (مفاتيح API وكلمات المرور). إذا أُدرجت في commit ثم دُفعت إلى مستودع عام، استطاع أي شخص رؤية مفاتيحك وسرقتها.


حل التحدي 1

أضف إلى routes.py:

@api.route("/messages", methods=["DELETE"])
def delete_all_messages():
    count = Message.query.delete()
    db.session.commit()
    return jsonify({"deleted": count})

اختبار:

curl -X DELETE http://localhost:5000/api/messages
# → {"deleted": 5}

حل التحدي 2

النهج في الذاكرة (بسيط):

أضف قرب أعلى routes.py:

CURRENT_SYSTEM_PROMPT = "You are a helpful assistant."

أضف مسارين جديدين:

@api.route("/system-prompt", methods=["GET"])
def get_system_prompt():
    global CURRENT_SYSTEM_PROMPT
    return jsonify({"current_prompt": CURRENT_SYSTEM_PROMPT})


@api.route("/system-prompt", methods=["POST"])
def set_system_prompt():
    global CURRENT_SYSTEM_PROMPT
    data = request.get_json()
    CURRENT_SYSTEM_PROMPT = data.get("prompt", CURRENT_SYSTEM_PROMPT)
    return jsonify({"current_prompt": CURRENT_SYSTEM_PROMPT})

ثم عدّل مسار chat ليستخدم CURRENT_SYSTEM_PROMPT:

messages = [
    {"role": "system", "content": CURRENT_SYSTEM_PROMPT},
    {"role": "user", "content": user_message},
]

اختبار:

curl -X POST http://localhost:5000/api/system-prompt \
  -H "Content-Type: application/json" \
  -d '{"prompt": "You speak like a pirate."}'

curl -X POST http://localhost:5000/api/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "Hello!"}'
# → "Ahoy, matey!"

حل التحدي 3

عدّل مسار chat ليتضمن سجل المحادثة:

@api.route("/chat", methods=["POST"])
def chat():
    data = request.get_json()
    user_message = data.get("message", "")

    if not user_message:
        return jsonify({"error": "Message is required"}), 400

    user_msg = Message(content=user_message, sender="user")
    db.session.add(user_msg)
    db.session.commit()

    try:
        # احصل على آخر 10 رسائل للسياق
        recent = Message.query.order_by(Message.timestamp.desc()).limit(10).all()
        recent.reverse()

        messages = [{"role": "system", "content": "You are a helpful assistant."}]
        for msg in recent:
            role = "user" if msg.sender == "user" else "assistant"
            messages.append({"role": role, "content": msg.content})

        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=messages,
        )

        ai_reply = response.choices[0].message.content
        ai_msg = Message(content=ai_reply, sender="ai")
        db.session.add(ai_msg)
        db.session.commit()

        return jsonify({
            "user_message": user_msg.to_dict(),
            "ai_message": ai_msg.to_dict(),
        })

    except Exception as e:
        return jsonify({"error": str(e)}), 500

مشروع مفتوح: API لقائمة المهام

models.py — إضافة نموذج Todo

class Todo(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(200), nullable=False)
    completed = db.Column(db.Boolean, default=False)
    created_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc))

    def to_dict(self):
        return {
            "id": self.id,
            "title": self.title,
            "completed": self.completed,
            "created_at": self.created_at.isoformat() if self.created_at else None,
        }

routes.py — إضافة مسارات المهام

@api.route("/todos", methods=["GET"])
def get_todos():
    completed = request.args.get("completed")
    sort = request.args.get("sort", "created_at")
    order = request.args.get("order", "desc")

    query = Todo.query
    if completed is not None:
        val = completed.lower() == "true"
        query = query.filter_by(completed=val)

    if order == "desc":
        query = query.order_by(getattr(Todo, sort).desc())
    else:
        query = query.order_by(getattr(Todo, sort).asc())

    todos = query.all()
    return jsonify([t.to_dict() for t in todos])


@api.route("/todos", methods=["POST"])
def create_todo():
    data = request.get_json()
    if not data or not data.get("title"):
        return jsonify({"error": "Title is required"}), 400

    todo = Todo(title=data["title"])
    db.session.add(todo)
    db.session.commit()
    return jsonify(todo.to_dict()), 201


@api.route("/todos/<int:todo_id>", methods=["PUT"])
def update_todo(todo_id):
    todo = Todo.query.get(todo_id)
    if not todo:
        return jsonify({"error": "Not found"}), 404

    data = request.get_json()
    if "completed" in data:
        todo.completed = data["completed"]
    if "title" in data:
        todo.title = data["title"]
    db.session.commit()
    return jsonify(todo.to_dict())


@api.route("/todos/<int:todo_id>", methods=["DELETE"])
def delete_todo(todo_id):
    todo = Todo.query.get(todo_id)
    if not todo:
        return jsonify({"error": "Not found"}), 404
    db.session.delete(todo)
    db.session.commit()
    return jsonify({"deleted": True})

اختبار المسار الكامل:

# إنشاء
curl -X POST http://localhost:5000/api/todos \
  -H "Content-Type: application/json" \
  -d '{"title": "Learn Flask"}'

# عرض
curl http://localhost:5000/api/todos

# إكمال
curl -X PUT http://localhost:5000/api/todos/1 \
  -H "Content-Type: application/json" \
  -d '{"completed": true}'

# حذف
curl -X DELETE http://localhost:5000/api/todos/1