File size: 8,662 Bytes
4b4d390
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import os
import requests
import random
import logging
import json

try:
    from PyPDF2 import PdfReader
except ImportError:
    # Fallback for Docker environment
    from pypdf import PdfReader

# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Check for environment variable or use a default value for testing
def get_api_key():
    api_key = os.environ.get('DEEPSEEK_API_KEY')
    if not api_key:
        logger.warning("No DEEPSEEK_API_KEY environment variable found. Using mock responses.")
    return api_key

# Mock evaluation result for testing
def get_mock_evaluation(story_excerpt):
    # Use a short excerpt of the story in the evaluation to make it look personalized
    story_start = story_excerpt[:100].replace("\n", " ").strip()
    if len(story_start) > 50:
        story_start = story_start[:50] + "..."

    # Generate random scores but keep them reasonable
    unity_score = random.randint(6, 9)
    characters_score = random.randint(6, 9)
    decisive_moment_score = random.randint(7, 10)
    language_score = random.randint(5, 9)
    time_place_score = random.randint(6, 9)
    plot_score = random.randint(7, 10)
    ending_score = random.randint(6, 10)
    message_score = random.randint(6, 9)
    
    total_score = unity_score + characters_score + decisive_moment_score + language_score + \
                 time_place_score + plot_score + ending_score + message_score
    
    # Mock evaluation text
    return {
        "evaluation": f"""📋 التقييم:



وحدة الحدث: {unity_score}/10

القصة تدور حول حدث رئيسي بشكل جيد، وتحافظ على تماسك الأحداث.



الشخصيات المحدودة والمعرّفة: {characters_score}/10

الشخصيات محددة بوضوح ومتميزة، مع تطوير مناسب للشخصيات الرئيسية.



التركيز على لحظة حاسمة: {decisive_moment_score}/10

القصة تبني بمهارة نحو لحظة التحول الرئيسية التي تغير مسار الأحداث.



الإيجاز واقتصاد اللغة: {language_score}/10

اللغة موجزة ومعبرة، تنقل المعاني بفعالية دون إطناب غير ضروري.



وحدة الزمان والمكان: {time_place_score}/10

هناك استخدام متناسق للإطار الزماني والمكاني، مما يعزز تماسك القصة.



حبكة جيدة البناء: {plot_score}/10

الحبكة متطورة بشكل منطقي ومتماسك، مع تسلسل واضح للأحداث.



نهاية مؤثرة: {ending_score}/10

النهاية تترك انطباعًا قويًا وتوفر إغلاقًا مناسبًا للأحداث.



رسالة أو موضوع واضح: {message_score}/10

تُظهر القصة رسالة واضحة تتطور بشكل طبيعي من خلال الأحداث.



النتيجة النهائية: {total_score}/80

{"هذا عمل أدبي متميز يتسم بقوة البناء والتماسك. القصة تحقق توازنًا جيدًا بين تطوير الشخصيات وتقدم الحبكة." if total_score > 65 else "رغم وجود عناصر قوية في القصة، هناك مجال للتحسين في بعض الجوانب. ننصح بالتركيز على تعزيز تماسك الأحداث وتطوير الشخصيات بشكل أعمق."}



ملاحظة: بداية القصة "{story_start}" تظهر إمكانات جيدة وتجذب اهتمام القارئ.""",
        "fixed_story": None
    }

def review_story(pdf_path):
    """

    Review a story from a PDF file

    """
    try:
        # Load text from PDF
        text = ""
        try:
            with open(pdf_path, "rb") as f:
                reader = PdfReader(f)
                text = "\n".join([p.extract_text() for p in reader.pages if p.extract_text()])
            text = " ".join(text.split())
        except Exception as e:
            logger.error(f"PDF loading failed: {e}")
            return {"evaluation": f"Error loading PDF: {e}", "fixed_story": None}
        
        # Get evaluation
        api_key = get_api_key()
        
        if not api_key:
            # Return mock evaluation
            logger.info("Using mock evaluation for PDF")
            return get_mock_evaluation(text)
            
        # Use API for evaluation
        return call_api_for_evaluation(text)
            
    except Exception as e:
        logger.error(f"Error in review_story: {e}")
        return {"evaluation": f"Error processing story: {e}", "fixed_story": None}

def review_story_text(story_text):
    """

    Review a story provided as text directly

    """
    try:
        # Get evaluation
        api_key = get_api_key()
        
        if not api_key:
            # Return mock evaluation
            logger.info("Using mock evaluation for text")
            return get_mock_evaluation(story_text)
            
        # Use API for evaluation
        return call_api_for_evaluation(story_text)
            
    except Exception as e:
        logger.error(f"Error in review_story_text: {e}")
        return {"evaluation": f"Error processing story text: {e}", "fixed_story": None}

def call_api_for_evaluation(story):
    """

    Call the DeepSeek API for story evaluation

    """
    api_key = get_api_key()
    if not api_key:
        return get_mock_evaluation(story)
        
    evaluation_prompt = f"""

    You are a professional literary critic specializing in the art of the short story. Your task is to evaluate the following story according to 8 essential criteria used in literary criticism. You must write the full evaluation in Modern Standard Arabic, using a clear and organized style.



    🔹 For each criterion:

    Give a score out of 10.

    Write a brief explanation (one or two lines) that justifies the score.



    🔹 At the end of the evaluation:

    Add up the scores to get a final result out of 80.

    If the score is above 65: Praise the story as a successful and well-crafted literary work.

    If the score is 65 or lower: Provide constructive criticism that highlights the main weaknesses and suggests how to improve them.



    Start the evaluation with a title that includes an emoji, such as: 📋 التقييم:

    

    🔹 The evaluation criteria are:

    1. Unity of event: Does the story revolve around a single main incident or situation?

    2. Limited and defined characters: Does the story include a small number of clear and distinctive characters?

    3. Focus on a decisive moment: Does the story highlight a turning point or critical decision?

    4. Conciseness and economy of language: Is the language focused and free of unnecessary details?

    5. Unity of time and place: Does the story take place in a specific time and setting?

    6. Well-structured plot: Is there a clear logical sequence (beginning, middle, end)?

    7. Impactful ending: Does the ending leave an emotional or intellectual impact?

    8. Clear message or theme: Does the story convey a specific idea or feeling clearly?



    Evaluate the following story based on these criteria:

    {story}

    """

    url = "https://api.deepseek.com/v1/chat/completions"
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }

    payload_eval = {
        "model": "deepseek-chat",
        "messages": [
            {"role": "system", "content": "You are a formal short story evaluator."},
            {"role": "user", "content": evaluation_prompt.strip()}
        ],
        "temperature": random.uniform(0.9, 1.0),
        "max_tokens": 2500
    }

    try:
        logger.info("Sending request to DeepSeek API")
        response_eval = requests.post(url, headers=headers, json=payload_eval)
        
        if response_eval.status_code != 200:
            logger.error(f"API Error: {response_eval.status_code} - {response_eval.text}")
            return {"evaluation": f"Error from API: {response_eval.text}", "fixed_story": None}

        evaluation_result = response_eval.json()["choices"][0]["message"]["content"]
        logger.info("Successfully received evaluation from API")

        return {
            "evaluation": evaluation_result.strip(),
            "fixed_story": None
        }
    except Exception as e:
        logger.error(f"API call failed: {e}")
        return {"evaluation": f"Error calling API: {e}", "fixed_story": None}