File size: 5,456 Bytes
54404f6
 
 
 
 
 
 
 
 
 
 
 
ef3133a
 
 
 
 
 
 
 
 
 
 
 
54404f6
 
 
 
 
d06b99c
54404f6
 
 
d06b99c
 
54404f6
 
 
d06b99c
 
 
 
 
 
54404f6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7f8f9a6
54404f6
 
 
 
7f8f9a6
 
54404f6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7f8f9a6
54404f6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ef3133a
9e903d1
54404f6
 
 
 
 
 
 
 
 
 
 
 
 
fd93c1d
54404f6
fd93c1d
54404f6
 
 
 
2a70b2f
54404f6
2a70b2f
54404f6
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
"""
Supabase client configuration and database operations for TreeTrack
"""

import os
import logging
from typing import Dict, List, Optional, Any
from supabase import create_client, Client
from pathlib import Path

logger = logging.getLogger(__name__)

# Supabase configuration - all must be provided via environment variables
SUPABASE_URL = os.getenv("SUPABASE_URL")
SUPABASE_ANON_KEY = os.getenv("SUPABASE_ANON_KEY")
SUPABASE_SERVICE_ROLE_KEY = os.getenv("SUPABASE_SERVICE_ROLE_KEY")

# Validate required environment variables (defer validation to runtime)
if not SUPABASE_URL:
    logger.warning("SUPABASE_URL not found - will cause connection failures")
if not SUPABASE_ANON_KEY:
    logger.warning("SUPABASE_ANON_KEY not found - will cause connection failures")
if not SUPABASE_SERVICE_ROLE_KEY:
    logger.warning("SUPABASE_SERVICE_ROLE_KEY not provided - admin functions will be limited")

# Initialize Supabase client
supabase: Optional[Client] = None

def get_supabase_client() -> Client:
    """Get Supabase client instance with better error handling"""
    global supabase
    
    if supabase is None:
        if not SUPABASE_URL:
            raise ValueError("SUPABASE_URL environment variable is required")
        if not SUPABASE_ANON_KEY:
            raise ValueError("SUPABASE_ANON_KEY environment variable is required")
        
        try:
            supabase = create_client(SUPABASE_URL, SUPABASE_ANON_KEY)
            logger.info("Supabase client initialized successfully")
        except Exception as e:
            logger.error(f"Failed to initialize Supabase client: {e}")
            raise ValueError(f"Supabase client initialization failed: {e}")
    
    return supabase

def get_service_client() -> Client:
    """Get Supabase client with service role (admin) permissions"""
    if not SUPABASE_SERVICE_ROLE_KEY:
        raise ValueError("SUPABASE_SERVICE_ROLE_KEY environment variable is required")
    
    return create_client(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY)


# Initialize database table (run once)
def create_trees_table():
    """Create the trees table in Supabase"""
    client = get_service_client()
    
    # SQL to create trees table
    sql = """
    CREATE TABLE IF NOT EXISTS trees (
        id BIGSERIAL PRIMARY KEY,
        latitude DECIMAL(10, 8) NOT NULL CHECK (latitude >= -90 AND latitude <= 90),
        longitude DECIMAL(11, 8) NOT NULL CHECK (longitude >= -180 AND longitude <= 180),
        location_name VARCHAR(200),
        local_name VARCHAR(200),
        scientific_name VARCHAR(200),
        common_name VARCHAR(200),
        tree_code VARCHAR(20),
        height DECIMAL(6, 2) CHECK (height > 0 AND height <= 1000), -- feet
        width DECIMAL(6, 2) CHECK (width > 0 AND width <= 200),   -- feet (girth/DBH)
        utility JSONB,
        storytelling_text TEXT CHECK (LENGTH(storytelling_text) <= 5000),
        storytelling_audio VARCHAR(500),
        phenology_stages JSONB,
        photographs JSONB,
        notes TEXT CHECK (LENGTH(notes) <= 2000),
        created_at TIMESTAMPTZ DEFAULT NOW(),
        updated_at TIMESTAMPTZ DEFAULT NOW(),
        created_by VARCHAR(100) DEFAULT 'system'
    );
    
    -- Create indexes for better performance
    CREATE INDEX IF NOT EXISTS idx_trees_location ON trees(latitude, longitude);
    CREATE INDEX IF NOT EXISTS idx_trees_scientific_name ON trees(scientific_name);
    CREATE INDEX IF NOT EXISTS idx_trees_local_name ON trees(local_name);
    CREATE INDEX IF NOT EXISTS idx_trees_location_name ON trees(location_name);
    CREATE INDEX IF NOT EXISTS idx_trees_tree_code ON trees(tree_code);
    CREATE INDEX IF NOT EXISTS idx_trees_created_at ON trees(created_at);
    
    -- Create updated_at trigger
    CREATE OR REPLACE FUNCTION update_updated_at_column()
    RETURNS TRIGGER AS $$
    BEGIN
        NEW.updated_at = NOW();
        RETURN NEW;
    END;
    $$ language 'plpgsql';
    
    DROP TRIGGER IF EXISTS update_trees_updated_at ON trees;
    CREATE TRIGGER update_trees_updated_at
        BEFORE UPDATE ON trees
        FOR EACH ROW
        EXECUTE FUNCTION update_updated_at_column();
    """
    
    try:
        client.rpc('exec_sql', {'sql': sql}).execute()
        logger.info("Trees table created successfully")
        return True
    except Exception as e:
        logger.error(f"Error creating trees table: {e}")
        return False

# Storage buckets setup
def create_storage_buckets():
    """Create storage buckets for files"""
    client = get_service_client()
    
    buckets = [
        {"name": "tree-images", "public": False},
        {"name": "tree-audios", "public": False}
    ]
    
    for bucket in buckets:
        try:
            result = client.storage.create_bucket(bucket["name"], {"public": bucket["public"]})
            logger.info(f"Created storage bucket: {bucket['name']}")
        except Exception as e:
            logger.info(f"Bucket {bucket['name']} might already exist: {e}")
    
    return True

# Test connection
def test_supabase_connection() -> bool:
    """Test Supabase connection"""
    try:
        logger.info("Testing Supabase connection...")
        client = get_supabase_client()
        result = client.table('trees').select("id").limit(1).execute()
        logger.info("Supabase connection successful")
        return True
        
    except Exception as e:
        logger.error(f"Supabase connection failed: {type(e).__name__}: {e}")
        return False