Spaces:
Sleeping
Sleeping
| import os | |
| import sqlite3 | |
| import json | |
| import csv | |
| from datetime import datetime, timedelta | |
| DB_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data") | |
| DB_FILE = os.path.join(DB_DIR, "kisan.db") | |
| # Crop-stage templates derived from KVK Kanpur / UP packages | |
| CROP_TEMPLATES = { | |
| "wheat": { | |
| "display": {"en": "Wheat", "hi": "गेहूँ", "hinglish": "Gehun"}, | |
| "stages": [ | |
| { | |
| "key": "sowing", | |
| "day_offset": 0, | |
| "window": [0, 0], | |
| "action": {"en": "Sowing", "hi": "बुवाई", "hinglish": "Sowing"}, | |
| "intervention": { | |
| "en": "Apply basal fertilizer dose: 50 kg DAP and 20 kg MOP per acre. (Confirm with agriculture officer)", | |
| "hi": "बेसल उर्वरक खुराक डालें: 50 किलो डीएपी और 20 किलो एमओपी प्रति एकड़। (कृषि अधिकारी से पुष्टि करें)", | |
| "hinglish": "Basal fertilizer dose dalein: 50 kg DAP aur 20 kg MOP per acre. (Agri-officer se confirm karein)" | |
| } | |
| }, | |
| { | |
| "key": "cri", | |
| "day_offset": 21, | |
| "window": [20, 25], | |
| "action": {"en": "First Irrigation (CRI Stage)", "hi": "पहली सिंचाई (CRI अवस्था)", "hinglish": "Pehli Irrigation (CRI Stage)"}, | |
| "intervention": { | |
| "en": "Irrigate crop (21-25 days after sowing). Top-dress with 40 kg Urea per acre. (Confirm with agriculture officer)", | |
| "hi": "फसल की सिंचाई करें (बुवाई के 21-25 दिन बाद)। 40 किलोग्राम यूरिया प्रति एकड़ का छिड़काव करें। (कृषि अधिकारी से पुष्टि करें)", | |
| "hinglish": "Fasal ki irrigation karein (sowing ke 21-25 din baad). 40 kg Urea per acre top-dress karein. (Agri-officer se confirm karein)" | |
| } | |
| }, | |
| { | |
| "key": "tillering", | |
| "day_offset": 45, | |
| "window": [40, 50], | |
| "action": {"en": "Second Irrigation + Weeding", "hi": "दूसरी सिंचाई + निराई", "hinglish": "Doosri Irrigation + Weeding"}, | |
| "intervention": { | |
| "en": "Perform manual weeding or apply recommended weedicide. Apply second irrigation. (Confirm with agriculture officer)", | |
| "hi": "हाथ से निराई करें या अनुशंसित शाकनाशी का प्रयोग करें। दूसरी सिंचाई करें। (कृषि अधिकारी से पुष्टि करें)", | |
| "hinglish": "Manual weeding karein ya weedicide apply karein. Doosri irrigation karein. (Agri-officer se confirm karein)" | |
| } | |
| }, | |
| { | |
| "key": "jointing", | |
| "day_offset": 65, | |
| "window": [60, 70], | |
| "action": {"en": "Third Irrigation + Urea Top-dress", "hi": "तीसरी सिंचाई + यूरिया", "hinglish": "Teesri Irrigation + Urea Top-dress"}, | |
| "intervention": { | |
| "en": "Apply second split of Urea (40 kg/acre) and perform third irrigation. (Confirm with agriculture officer)", | |
| "hi": "यूरिया की दूसरी खुराक (40 किग्रा/एकड़) डालें और तीसरी सिंचाई करें। (कृषि अधिकारी से पुष्टि करें)", | |
| "hinglish": "Urea ki doosri split (40 kg/acre) dalein aur teesri irrigation karein. (Agri-officer se confirm karein)" | |
| } | |
| }, | |
| { | |
| "key": "flowering", | |
| "day_offset": 85, | |
| "window": [80, 90], | |
| "action": {"en": "Fourth Irrigation", "hi": "चौथी सिंचाई", "hinglish": "Chauthi Irrigation"}, | |
| "intervention": { | |
| "en": "Maintain light moisture in the field. Critical for grain yield. (Confirm with agriculture officer)", | |
| "hi": "खेत में हल्की नमी बनाए रखें। दाना बनने की प्रक्रिया के लिए महत्वपूर्ण। (कृषि अधिकारी से पुष्टि करें)", | |
| "hinglish": "Khet me halki moisture banaye rakhein. Grains ke liye zaroori hai. (Agri-officer se confirm karein)" | |
| } | |
| }, | |
| { | |
| "key": "milking", | |
| "day_offset": 105, | |
| "window": [100, 110], | |
| "action": {"en": "Fifth Irrigation", "hi": "पांचवीं सिंचाई", "hinglish": "Panchvi Irrigation"}, | |
| "intervention": { | |
| "en": "Irrigate to support grain filling and prevent premature drying. (Confirm with agriculture officer)", | |
| "hi": "दाना भरने में सहायता करने और समय से पहले सूखने से बचाने के लिए सिंचाई करें। (कृषि अधिकारी से पुष्टि करें)", | |
| "hinglish": "Irrigate karein taaki grains acche se bharein aur jaldi na sookhein. (Agri-officer se confirm karein)" | |
| } | |
| }, | |
| { | |
| "key": "harvest", | |
| "day_offset": 125, | |
| "window": [120, 135], | |
| "action": {"en": "Harvesting", "hi": "कटाई", "hinglish": "Harvesting"}, | |
| "intervention": { | |
| "en": "Harvest when grains are dry and golden (moisture level < 14%).", | |
| "hi": "फसल सुनहरी होने और दाने में 14% से कम नमी होने पर कटाई करें।", | |
| "hinglish": "Fasal sunhari hone aur daane me 14% se kam moisture hone par harvesting karein." | |
| } | |
| } | |
| ] | |
| }, | |
| "potato": { | |
| "display": {"en": "Potato", "hi": "आलू", "hinglish": "Aloo"}, | |
| "stages": [ | |
| { | |
| "key": "sowing", | |
| "day_offset": 0, | |
| "window": [0, 0], | |
| "action": {"en": "Sowing", "hi": "बुवाई", "hinglish": "Sowing"}, | |
| "intervention": { | |
| "en": "Use certified seed tubers. Spacing 60x20 cm. Apply NPK basal dose. (Confirm with agriculture officer)", | |
| "hi": "प्रमाणित बीज कंदों का प्रयोग करें। ६०x२० सेमी दूरी रखें। एनपीके बेसल खुराक डालें। (कृषि अधिकारी से पुष्टि करें)", | |
| "hinglish": "Certified seed tubers use karein. Spacing 60x20 cm. NPK basal dose dalein. (Agri-officer se confirm karein)" | |
| } | |
| }, | |
| { | |
| "key": "emergence", | |
| "day_offset": 15, | |
| "window": [12, 18], | |
| "action": {"en": "First Irrigation", "hi": "पहली सिंचाई", "hinglish": "Pehli Irrigation"}, | |
| "intervention": { | |
| "en": "Apply light irrigation 15 days post planting for uniform emergence. (Confirm with agriculture officer)", | |
| "hi": "समान अंकुरण के लिए रोपण के 15 दिन बाद हल्की सिंचाई करें। (कृषि अधिकारी से पुष्टि करें)", | |
| "hinglish": "Uniform emergence ke liye planting ke 15 din baad halki irrigation karein. (Agri-officer se confirm karein)" | |
| } | |
| }, | |
| { | |
| "key": "earthing", | |
| "day_offset": 25, | |
| "window": [20, 30], | |
| "action": {"en": "Earthing Up + Urea", "hi": "मिट्टी चढ़ाना + यूरिया", "hinglish": "Earthing Up + Urea"}, | |
| "intervention": { | |
| "en": "Weed the field. Apply Urea and mound soil around stems to prevent greening. (Confirm with agriculture officer)", | |
| "hi": "निराई करें। यूरिया डालें और तने पर मिट्टी चढ़ाएं ताकि कंद हरे न हों। (कृषि अधिकारी से पुष्टि करें)", | |
| "hinglish": "Weeding karein. Urea dalein aur stem par mitti chadhayein taaki tubers green na ho. (Agri-officer se confirm karein)" | |
| } | |
| }, | |
| { | |
| "key": "tuber_initiation", | |
| "day_offset": 45, | |
| "window": [40, 50], | |
| "action": {"en": "Second Irrigation", "hi": "दूसरी सिंचाई", "hinglish": "Doosri Irrigation"}, | |
| "intervention": { | |
| "en": "Ensure adequate soil moisture. Critical for tuber formation. (Confirm with agriculture officer)", | |
| "hi": "मिट्टी में पर्याप्त नमी सुनिश्चित करें। कंद बनने के लिए यह चरण बहुत महत्वपूर्ण है। (कृषि अधिकारी से पुष्टि करें)", | |
| "hinglish": "Mitti me moisture banaye rakhein. Tuber formation ke liye zaroori hai. (Agri-officer se confirm karein)" | |
| } | |
| }, | |
| { | |
| "key": "bulking", | |
| "day_offset": 75, | |
| "window": [70, 80], | |
| "action": {"en": "Third Irrigation + Blight Check", "hi": "तीसरी सिंचाई + झुलसा निगरानी", "hinglish": "Teesri Irrigation + Blight Check"}, | |
| "intervention": { | |
| "en": "Irrigate and check for Late Blight spots. Spray Mancozeb (2g/L) if cloudy/foggy. (Confirm with agriculture officer)", | |
| "hi": "सिंचाई करें और पिछेती झुलसा के काले धब्बों की जांच करें। बादल/कोहरा होने पर मैन्कोजेब (2 ग्राम/लीटर) छिड़कें। (कृषि अधिकारी से पुष्टि करें)", | |
| "hinglish": "Irrigation karein aur Late Blight ke spots check karein. Mancozeb (2g/L) spray karein. (Agri-officer se confirm karein)" | |
| } | |
| }, | |
| { | |
| "key": "dehaulming", | |
| "day_offset": 110, | |
| "window": [105, 115], | |
| "action": {"en": "Foliage Cutting (Dehaulming)", "hi": "डंठल कटाई (Dehaulming)", "hinglish": "Danthal Katayi (Dehaulming)"}, | |
| "intervention": { | |
| "en": "Cut foliage 10 days before harvest to thicken potato skin for storage.", | |
| "hi": "खुदाई से 10 दिन पहले डंठल काट लें ताकि भंडारण के लिए छिलका सख्त हो।", | |
| "hinglish": "Khodai se 10 din pehle danthal kaat lein taaki storage ke liye skin tight ho jaye." | |
| } | |
| }, | |
| { | |
| "key": "harvest", | |
| "day_offset": 120, | |
| "window": [115, 130], | |
| "action": {"en": "Harvesting", "hi": "खुदाई", "hinglish": "Khodai"}, | |
| "intervention": { | |
| "en": "Dig out potatoes carefully in dry weather. Allow tubers to dry in shade.", | |
| "hi": "सूखे मौसम में सावधानीपूर्वक आलू की खुदाई करें। कंदों को छाया में सुखाएं।", | |
| "hinglish": "Sookhe mausam me potato ki khodai karein. Tubers ko chhaya me sukhayein." | |
| } | |
| } | |
| ] | |
| } | |
| } | |
| def get_db(): | |
| conn = sqlite3.connect(DB_FILE) | |
| conn.row_factory = sqlite3.Row | |
| return conn | |
| def init_db(): | |
| os.makedirs(DB_DIR, exist_ok=True) | |
| conn = get_db() | |
| cursor = conn.cursor() | |
| # 1. Profile Table | |
| cursor.execute(""" | |
| CREATE TABLE IF NOT EXISTS profile ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| name TEXT, | |
| state TEXT, | |
| district TEXT, | |
| language TEXT | |
| ) | |
| """) | |
| # 2. Calendars Table | |
| cursor.execute(""" | |
| CREATE TABLE IF NOT EXISTS calendars ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| crop TEXT, | |
| variety TEXT, | |
| sow_date TEXT, | |
| location TEXT, | |
| created_at TEXT | |
| ) | |
| """) | |
| # 3. Tasks Table | |
| cursor.execute(""" | |
| CREATE TABLE IF NOT EXISTS tasks ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| calendar_id INTEGER, | |
| stage TEXT, | |
| action TEXT, | |
| intervention TEXT, | |
| target_date TEXT, | |
| window_start TEXT, | |
| window_end TEXT, | |
| status TEXT DEFAULT 'pending', | |
| notes TEXT, | |
| FOREIGN KEY(calendar_id) REFERENCES calendars(id) ON DELETE CASCADE | |
| ) | |
| """) | |
| # 4. Ledger Table | |
| cursor.execute(""" | |
| CREATE TABLE IF NOT EXISTS ledger ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| date TEXT, | |
| type TEXT, | |
| item TEXT, | |
| qty TEXT, | |
| unit TEXT, | |
| price REAL, | |
| created_at TEXT | |
| ) | |
| """) | |
| conn.commit() | |
| # --- Ingestion / Migration --- | |
| # Migration A: Profile | |
| cursor.execute("SELECT COUNT(*) FROM profile") | |
| if cursor.fetchone()[0] == 0: | |
| json_profile_path = os.path.join(DB_DIR, "user_profile.json") | |
| if os.path.exists(json_profile_path): | |
| try: | |
| with open(json_profile_path, "r", encoding="utf-8") as f: | |
| prof_data = json.load(f) | |
| cursor.execute( | |
| "INSERT INTO profile (name, state, district, language) VALUES (?, ?, ?, ?)", | |
| (prof_data.get("name"), prof_data.get("state"), prof_data.get("district"), prof_data.get("language", "hi")) | |
| ) | |
| conn.commit() | |
| print("[db.py] Migrated user profile from JSON file.") | |
| except Exception as e: | |
| print(f"[db.py] Profile migration error: {e}") | |
| # Migration B: Ledger CSV | |
| cursor.execute("SELECT COUNT(*) FROM ledger") | |
| if cursor.fetchone()[0] == 0: | |
| csv_ledger_path = os.path.join(DB_DIR, "ledger.csv") | |
| if os.path.exists(csv_ledger_path): | |
| try: | |
| with open(csv_ledger_path, "r", encoding="utf-8") as f: | |
| reader = csv.reader(f) | |
| header = next(reader, None) | |
| if header: | |
| for row in reader: | |
| if len(row) >= 5: | |
| date = row[0] | |
| item = row[1] | |
| qty = row[2] | |
| try: | |
| price = float(row[3]) | |
| except ValueError: | |
| price = 0.0 | |
| raw_type = str(row[4]).lower() | |
| trans_type = "sale" if "sale" in raw_type or "बिक्री" in raw_type else "purchase" | |
| timestamp = row[5] if len(row) > 5 else datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
| cursor.execute( | |
| "INSERT INTO ledger (date, type, item, qty, unit, price, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)", | |
| (date, trans_type, item, qty, "", price, timestamp) | |
| ) | |
| conn.commit() | |
| print("[db.py] Migrated ledger records from CSV file.") | |
| except Exception as e: | |
| print(f"[db.py] Ledger migration error: {e}") | |
| conn.close() | |
| # --- PROFILE ACTIONS --- | |
| def get_profile(): | |
| conn = get_db() | |
| cursor = conn.cursor() | |
| cursor.execute("SELECT name, state, district, language FROM profile LIMIT 1") | |
| row = cursor.fetchone() | |
| conn.close() | |
| if row: | |
| return dict(row) | |
| return None | |
| def save_profile(name, state, district, language="hi"): | |
| conn = get_db() | |
| cursor = conn.cursor() | |
| cursor.execute("SELECT id FROM profile LIMIT 1") | |
| row = cursor.fetchone() | |
| if row: | |
| cursor.execute( | |
| "UPDATE profile SET name = ?, state = ?, district = ?, language = ? WHERE id = ?", | |
| (name, state, district, language, row["id"]) | |
| ) | |
| else: | |
| cursor.execute( | |
| "INSERT INTO profile (name, state, district, language) VALUES (?, ?, ?, ?)", | |
| (name, state, district, language) | |
| ) | |
| conn.commit() | |
| conn.close() | |
| return True | |
| # --- CALENDAR & TASK ACTIONS --- | |
| def add_calendar(crop, variety, sow_date_str, location): | |
| # Verify crop template exists | |
| crop_key = crop.lower() | |
| if crop_key not in CROP_TEMPLATES: | |
| raise ValueError(f"Crop '{crop}' has no verified template.") | |
| template = CROP_TEMPLATES[crop_key] | |
| try: | |
| sow_dt = datetime.strptime(sow_date_str, "%Y-%m-%d") | |
| except ValueError: | |
| sow_dt = datetime.now() | |
| sow_date_str = sow_dt.strftime("%Y-%m-%d") | |
| created_at = datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
| conn = get_db() | |
| cursor = conn.cursor() | |
| # 1. Insert calendar | |
| cursor.execute( | |
| "INSERT INTO calendars (crop, variety, sow_date, location, created_at) VALUES (?, ?, ?, ?, ?)", | |
| (crop, variety, sow_date_str, location, created_at) | |
| ) | |
| calendar_id = cursor.lastrowid | |
| # 2. Insert calculated tasks | |
| for stage in template["stages"]: | |
| offset = stage["day_offset"] | |
| target_dt = sow_dt + timedelta(days=offset) | |
| target_date_str = target_dt.strftime("%Y-%m-%d") | |
| win_start_offset, win_end_offset = stage["window"] | |
| win_start_dt = sow_dt + timedelta(days=win_start_offset) | |
| win_end_dt = sow_dt + timedelta(days=win_end_offset) | |
| win_start_str = win_start_dt.strftime("%Y-%m-%d") | |
| win_end_str = win_end_dt.strftime("%Y-%m-%d") | |
| cursor.execute( | |
| """ | |
| INSERT INTO tasks (calendar_id, stage, action, intervention, target_date, window_start, window_end, status) | |
| VALUES (?, ?, ?, ?, ?, ?, ?, 'pending') | |
| """, | |
| ( | |
| calendar_id, | |
| stage["key"], | |
| json.dumps(stage["action"]), | |
| json.dumps(stage["intervention"]), | |
| target_date_str, | |
| win_start_str, | |
| win_end_str | |
| ) | |
| ) | |
| conn.commit() | |
| conn.close() | |
| return calendar_id | |
| def get_calendars(): | |
| conn = get_db() | |
| cursor = conn.cursor() | |
| # Load calendars | |
| cursor.execute("SELECT id, crop, variety, sow_date, location, created_at FROM calendars ORDER BY id DESC") | |
| calendars_rows = cursor.fetchall() | |
| calendars_list = [] | |
| for cal in calendars_rows: | |
| cal_dict = dict(cal) | |
| cal_id = cal["id"] | |
| # Load tasks for this calendar | |
| cursor.execute( | |
| """ | |
| SELECT id, stage, action, intervention, target_date, window_start, window_end, status, notes | |
| FROM tasks WHERE calendar_id = ? ORDER BY target_date ASC | |
| """, | |
| (cal_id,) | |
| ) | |
| tasks_rows = cursor.fetchall() | |
| tasks_list = [] | |
| for task in tasks_rows: | |
| t_dict = dict(task) | |
| # Decode action and intervention JSON | |
| try: | |
| t_dict["action"] = json.loads(t_dict["action"]) | |
| except: | |
| pass | |
| try: | |
| t_dict["intervention"] = json.loads(t_dict["intervention"]) | |
| except: | |
| pass | |
| tasks_list.append(t_dict) | |
| cal_dict["tasks"] = tasks_list | |
| calendars_list.append(cal_dict) | |
| conn.close() | |
| return calendars_list | |
| def update_calendar_sow_date(calendar_id, new_sow_date_str): | |
| try: | |
| new_sow_dt = datetime.strptime(new_sow_date_str, "%Y-%m-%d") | |
| except ValueError: | |
| raise ValueError("Invalid date format. Expected YYYY-MM-DD.") | |
| conn = get_db() | |
| cursor = conn.cursor() | |
| # Get the crop for this calendar | |
| cursor.execute("SELECT crop FROM calendars WHERE id = ?", (calendar_id,)) | |
| row = cursor.fetchone() | |
| if not row: | |
| conn.close() | |
| raise ValueError(f"Calendar with ID {calendar_id} not found.") | |
| crop = row["crop"] | |
| crop_key = crop.lower() | |
| if crop_key not in CROP_TEMPLATES: | |
| conn.close() | |
| raise ValueError(f"Crop '{crop}' has no verified template.") | |
| template = CROP_TEMPLATES[crop_key] | |
| # Update the calendar's sow_date | |
| cursor.execute("UPDATE calendars SET sow_date = ? WHERE id = ?", (new_sow_date_str, calendar_id)) | |
| # For each stage in the template, recalculate dates and update the tasks | |
| for stage in template["stages"]: | |
| offset = stage["day_offset"] | |
| target_dt = new_sow_dt + timedelta(days=offset) | |
| target_date_str = target_dt.strftime("%Y-%m-%d") | |
| win_start_offset, win_end_offset = stage["window"] | |
| win_start_dt = new_sow_dt + timedelta(days=win_start_offset) | |
| win_end_dt = new_sow_dt + timedelta(days=win_end_offset) | |
| win_start_str = win_start_dt.strftime("%Y-%m-%d") | |
| win_end_str = win_end_dt.strftime("%Y-%m-%d") | |
| cursor.execute( | |
| """ | |
| UPDATE tasks | |
| SET target_date = ?, window_start = ?, window_end = ? | |
| WHERE calendar_id = ? AND stage = ? | |
| """, | |
| (target_date_str, win_start_str, win_end_str, calendar_id, stage["key"]) | |
| ) | |
| conn.commit() | |
| conn.close() | |
| return True | |
| def delete_calendar(calendar_id): | |
| conn = get_db() | |
| cursor = conn.cursor() | |
| # Enable foreign key support first | |
| cursor.execute("PRAGMA foreign_keys = ON") | |
| cursor.execute("DELETE FROM calendars WHERE id = ?", (calendar_id,)) | |
| # Also delete associated tasks explicitly in case cascades aren't on in SQLite configuration | |
| cursor.execute("DELETE FROM tasks WHERE calendar_id = ?", (calendar_id,)) | |
| conn.commit() | |
| conn.close() | |
| return True | |
| def update_task_status(task_id, status): | |
| if status not in ["pending", "done", "skipped"]: | |
| raise ValueError("Invalid status value.") | |
| conn = get_db() | |
| cursor = conn.cursor() | |
| cursor.execute("UPDATE tasks SET status = ? WHERE id = ?", (status, task_id)) | |
| conn.commit() | |
| conn.close() | |
| return True | |
| # --- LEDGER ACTIONS --- | |
| def add_ledger_entry(date_str, trans_type, item, qty, unit="", price=0.0): | |
| timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
| conn = get_db() | |
| cursor = conn.cursor() | |
| cursor.execute( | |
| "INSERT INTO ledger (date, type, item, qty, unit, price, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)", | |
| (date_str, trans_type, item, qty, unit, price, timestamp) | |
| ) | |
| conn.commit() | |
| conn.close() | |
| return True | |
| def get_ledger_entries(): | |
| conn = get_db() | |
| cursor = conn.cursor() | |
| cursor.execute("SELECT id, date, type, item, qty, unit, price, created_at FROM ledger ORDER BY created_at DESC") | |
| rows = cursor.fetchall() | |
| entries = [] | |
| income = 0.0 | |
| expense = 0.0 | |
| for r in rows: | |
| row_dict = dict(r) | |
| entries.append(row_dict) | |
| price = row_dict["price"] or 0.0 | |
| t_type = row_dict["type"] | |
| if t_type == "sale": | |
| income += price | |
| else: | |
| expense += price | |
| conn.close() | |
| summary = { | |
| "income": int(income), | |
| "expense": int(expense), | |
| "balance": int(income - expense) | |
| } | |
| return entries, summary | |
| def delete_ledger_entry(entry_id): | |
| conn = get_db() | |
| cursor = conn.cursor() | |
| cursor.execute("DELETE FROM ledger WHERE id = ?", (entry_id,)) | |
| conn.commit() | |
| conn.close() | |
| return True | |
| def clear_ledger(): | |
| conn = get_db() | |
| cursor = conn.cursor() | |
| cursor.execute("DELETE FROM ledger") | |
| conn.commit() | |
| conn.close() | |
| return True | |
| def clear_all_data(): | |
| conn = get_db() | |
| cursor = conn.cursor() | |
| cursor.execute("DELETE FROM profile") | |
| cursor.execute("DELETE FROM calendars") | |
| cursor.execute("DELETE FROM tasks") | |
| cursor.execute("DELETE FROM ledger") | |
| conn.commit() | |
| conn.close() | |
| # Remove migration files if they exist to prevent automatic re-import | |
| json_profile_path = os.path.join(DB_DIR, "user_profile.json") | |
| if os.path.exists(json_profile_path): | |
| try: os.remove(json_profile_path) | |
| except: pass | |
| csv_ledger_path = os.path.join(DB_DIR, "ledger.csv") | |
| if os.path.exists(csv_ledger_path): | |
| try: os.remove(csv_ledger_path) | |
| except: pass | |
| return True | |