File size: 14,621 Bytes
3cd2ab5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
import os
os.environ["NUMBA_CACHE_DIR"] = "/tmp/numba_cache"
os.environ["NUMBA_DISABLE_JIT"] = "1"
from apscheduler.schedulers.background import BackgroundScheduler
import requests
import time
import logging
from flask import Flask, request, jsonify, send_file
from flask_cors import CORS
from predict import handle_predict
from database import (
    init_db, save_prediction, get_history, register_user, authenticate_user,
    get_user_profile, update_user_profile, get_farm_details_from_db, 
    update_farm_details_in_db, get_farm_detailss_from_db, get_hives_from_db, 
    get_hive_detail_from_db, add_hive_to_db, delete_hive_from_db, update_hive_health_in_db,
    add_task_to_db, get_user_tasks_from_db, get_task_detail_from_db, 
    delete_task_from_db, update_task_status_to_completed, update_hive_in_db,
    generate_reset_code, verify_reset_code, update_user_password, change_user_password, get_all_notifications, add_notification, 
    mark_notification_as_read, get_user_cities
)
from forget import forgot_password, verify_reset_code_endpoint, reset_password
from report import generate_report
from datetime import datetime

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

app = Flask(__name__)
app.secret_key = os.urandom(24)
CORS(app)

# Forgot password endpoint
@app.route('/forgot_password', methods=['POST'])
def forgot_password_endpoint():
    return forgot_password(request)

# Verify reset code endpoint
@app.route('/verify_reset_code', methods=['POST'])
def verify_reset_code_route():
    return verify_reset_code_endpoint(request)

# Reset password endpoint
@app.route('/reset_password', methods=['POST'])
def reset_password_endpoint():
    return reset_password(request)

@app.route('/change_password', methods=['POST'])
def change_password():
    data = request.json
    user_id = data.get('user_id')
    current_password = data.get('password')
    new_password = data.get('new_password')
    
    if not all([user_id, current_password, new_password]):
        return jsonify({"error": "Missing required fields"}), 400
    
    result = change_user_password(user_id, current_password, new_password)
    if result:
        return jsonify({"message": "Password changed successfully"}), 200
    return jsonify({"error": "Current password incorrect"}), 401

# Existing endpoints (unchanged)
@app.route('/predict', methods=['POST'])
def predict():
    return handle_predict(request, save_prediction)

@app.route('/signup', methods=['POST'])
def signup():
    data = request.json
    result = register_user(data['fullName'], data['email'], data['password'])
    if result == "email already exist":
        return jsonify({"message": result}), 215
    return jsonify({"message": result}), 200

@app.route('/login', methods=['POST'])
def login():
    data = request.json
    result = authenticate_user(data['email'], data['password'])
    if "error" in result:
        return jsonify(result), 415
    return jsonify(result), 200

@app.route('/profile', methods=['GET'])
def profile():
    user_id = request.args.get('user_id')
    return jsonify(get_user_profile(user_id))

@app.route('/profile/update', methods=['POST'])
def update_profile():
    data = request.json
    return jsonify(update_user_profile(
        data['user_id'], data['fullname'], data['country'], data['city'], 
        data['gender'], data['phone_number']
    ))

@app.route('/history', methods=['GET'])
def history():
    user_id = request.args.get('user_id')
    return jsonify({"history": get_history(user_id)})

@app.route('/farm', methods=['GET'])
def get_farm_details():
    user_id = request.args.get('user_id')
    if not user_id:
        return jsonify({"error": "User ID is required"}), 400
    farm = get_farm_details_from_db(user_id)
    return jsonify(farm) if farm else jsonify({"error": "Farm details not found"}), 404

@app.route('/farm/update', methods=['POST'])
def update_farm():
    data = request.json
    return jsonify(update_farm_details_in_db(
        data['user_id'], data['fullname'], data['country'], data['city'], data['zip']
    ))

@app.route('/farms', methods=['GET'])
def get_farm():
    user_id = request.args.get('user_id')
    if not user_id:
        return jsonify({"error": "User ID is required"}), 400
    farm = get_farm_details_from_db(user_id)
    return jsonify(farm) if farm else jsonify({"error": "No farm registered"}), 404

@app.route('/hives', methods=['GET'])
def get_hives():
    farm_id = request.args.get('farm_id')
    if not farm_id:
        return jsonify({"error": "Farm ID is required"}), 400
    hives = get_hives_from_db(farm_id)
    return jsonify(hives)

@app.route('/hives', methods=['POST'])
def add_hive():
    data = request.json
    if not all(key in data for key in ['farm_id', 'hive_number', 'bee_type', 'number_of_frames', 'health_status']):
        return jsonify({"error": "Missing required fields"}), 400
    try:
        hive_id = add_hive_to_db(
            data['farm_id'],
            data['hive_number'],
            data['bee_type'],
            data['number_of_frames'],
            data['health_status'],
            data.get('notes', '')
        )
        return jsonify({"success": True, "hive_id": hive_id}), 201
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@app.route('/hives', methods=['DELETE'])
def delete_hive():
    hive_id = request.args.get('hive_id')
    if not hive_id:
        return jsonify({"error": "Hive ID is required"}), 400
    try:
        deleted = delete_hive_from_db(hive_id)
        if deleted:
            return jsonify({"success": True}), 200
        else:
            return jsonify({"error": "Hive not found"}), 404
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@app.route('/hive_detail', methods=['GET'])
def get_hives_detail():
    hive_id = request.args.get('hive_id')
    if not hive_id:
        return jsonify({"error": "Hive Id is required"}), 400
    hivess = get_hive_detail_from_db(hive_id)
    return jsonify(hivess)

@app.route('/manual_health_update', methods=['POST'])
def update_hive_health():
    data = request.json
    if not all(key in data for key in ['hive_id', 'health_status']):
        return jsonify({"error": "Missing required fields"}), 400
    try:
        update_hive_health_in_db(data['hive_id'], data['health_status'])
        return jsonify({"success": True, "message": "Health status updated"}), 200
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@app.route('/edit_hive', methods=['POST'])
def update_hive():
    data = request.json
    if 'hive_id' not in data:
        return jsonify({"error": "hive_id is required"}), 400
    update_data = {k: v for k, v in data.items() if k in {'hive_id', 'number_of_frames', 'bee_type', 'notes'}}
    if len(update_data) <= 1:
        return jsonify({"error": "No valid fields provided"}), 400
    try:
        result = update_hive_in_db(
            update_data['hive_id'],
            update_data.get('number_of_frames'),
            update_data.get('bee_type'),
            update_data.get('notes')
        )
        if not result:
            return jsonify({"error": "Hive not found"}), 404
        return jsonify({"success": True, "hive_id": update_data['hive_id']}), 200
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@app.route('/report', methods=['GET'])
def get_report():
    user_id = request.args.get('user_id')
    if not user_id:
        return jsonify({"error": "User ID is required"}), 400
    try:
        buffer = generate_report(user_id)
        buffer.seek(0)
        return send_file(
            buffer,
            as_attachment=True,
            download_name=f"bee_hive_report_{user_id}_{datetime.now().strftime('%Y%m%d')}.pdf",
            mimetype='application/pdf'
        )
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@app.route('/addtask', methods=['POST'])
def add_task():
    data = request.json
    required_fields = ['user_id', 'task_name', 'description', 'deadline_date', 'status']
    if not all(key in data for key in required_fields):
        return jsonify({"error": "Missing required fields"}), 400
    try:
        task_id = add_task_to_db(
            data['user_id'],
            data['task_name'],
            data.get('hive_id'),
            data['description'],
            data['deadline_date'],
            data['status']
        )
        return jsonify({"success": True, "task_id": task_id}), 201
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@app.route('/showtasks', methods=['GET'])
def get_user_tasks():
    user_id = request.args.get('user_id')
    if not user_id:
        return jsonify({"error": "User ID is required"}), 400
    try:
        tasks = get_user_tasks_from_db(user_id)
        return jsonify({"tasks": tasks}), 200
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@app.route('/task_detail', methods=['GET'])
def get_task_detail():
    task_id = request.args.get('task_id')
    if not task_id:
        return jsonify({"error": "Task ID is required"}), 400
    try:
        task = get_task_detail_from_db(task_id)
        if task:
            return jsonify(task), 200
        return jsonify({"error": "Task not found"}), 404
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@app.route('/task_complete', methods=['POST'])
def mark_task_complete():
    data = request.json
    task_id = data.get('task_id')
    if not task_id:
        return jsonify({"error": "Task ID is required"}), 400
    try:
        updated = update_task_status_to_completed(task_id)
        if updated:
            return jsonify({"success": True, "message": "Task marked as completed"}), 200
        return jsonify({"error": "Task not found"}), 404
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@app.route('/delete_task', methods=['DELETE'])
def delete_task():
    data = request.get_json()
    task_id = data.get("task_id") if data else None
    if not task_id:
        return jsonify({"error": "Task ID is required"}), 400
    try:
        deleted = delete_task_from_db(task_id)
        if deleted:
            return jsonify({"success": True}), 200
        else:
            return jsonify({"error": "Task not found"}), 404
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@app.route('/notifications', methods=['GET'])
def get_notifications():
    user_id = request.args.get('user_id')
    if not user_id:
        return jsonify({"error": "User ID is required"}), 400
    try:
        notifications = get_all_notifications(user_id)
        return jsonify({"notifications": notifications}), 200
    except TypeError as e:
        return jsonify({"error": str(e)}), 500

@app.route('/notifications/mark_read', methods=['POST'])
def mark_notifications_read():
    data = request.json
    notification_id = data.get('notification_id')
    if not notification_id:
        return jsonify({"error": "Notification ID is required"}), 400
    try:
        mark_notification_as_read(notification_id)
        return jsonify({"message": "Notification marked as read"}), 200
    except Exception as e:
        return jsonify({"error)": str(e)}), 500

@app.route('/add_notifications', methods=['POST'])
def create_notification():
    data = request.json
    required_fields = ['user_id', 'textt']
    if not all(key in data for key in required_fields):
        return jsonify({"error": "Missing required fields"}), 400
    try:
        notification_id = add_notification(
            data['user_id'],
            data['textt']
        )
        return jsonify({"success": True, "notification_id": notification_id}), 201
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@app.route('/check_weather', methods=['POST'])
def trigger_weather_check():
    try:
        check_weather_conditions()
        return jsonify({"message": "Weather check triggered successfully"}), 200
    except Exception as e:
        logger.error(f"Error triggering weather check: {str(e)}")
        return jsonify({"error": str(e)}), 500

def check_weather_conditions():
    logger.info("Checking weather conditions for users")
    # Wake up the database
    try:
        response = requests.get("https://bilalhasanniazi-bee-notbee.hf.space/profile?user_id=0")
        if response.status_code == 200:
            logger.info("Database wake-up API called successfully")
        else:
            logger.warning(f"Database wake-up API failed with status {response.status_code}")
    except Exception as e:
        logger.error(f"Error calling database wake-up API: {str(e)}")
    
    # Wait for 10 seconds
    time.sleep(10)
    
    user_cities = get_user_cities()
    weather_api_key = "9fb23a5a66764b61a43163911251605"
    base_url = "http://api.weatherapi.com/v1/forecast.json"

    for user_id, city in user_cities.items():
        try:
            # Fetch weather data
            response = requests.get(f"{base_url}?key={weather_api_key}&q={city}")
            if response.status_code != 200:
                logger.warning(f"Failed to fetch weather for city {city}: {response.status_code}")
                continue

            data = response.json()
            if "error" in data:
                logger.warning(f"Invalid city {city} for user {user_id}: {data['error']['message']}")
                continue

            temp_c = data["current"]["temp_c"]
            if temp_c > 45:
                notification_text = "Temperature is too hot, take safety precautions to protect bees"
                add_notification(user_id, notification_text)
                logger.info(f"Notification added for user {user_id} in {city}: {notification_text}")

        except Exception as e:
            logger.error(f"Error processing weather for user {user_id}, city {city}: {str(e)}")

# Initialize scheduler
scheduler = BackgroundScheduler()
scheduler.add_job(check_weather_conditions, 'interval', hours=24)
scheduler.start()

if __name__ == '__main__':
    logger.info("Starting Flask application")
    app.run(debug=False, host='0.0.0.0', port=7860)