Technical Architecture - ayothedoc3/Nordic-Explorer GitHub Wiki

πŸ› οΈ Technical Architecture

System Overview

Nordic Explorer is built as a modern, scalable web application using Streamlit framework with a focus on performance, multilingual support, and user experience optimization for adventure travel planning.

πŸ—οΈ Architecture Diagram

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Frontend      β”‚    β”‚   Backend       β”‚    β”‚   Data Layer    β”‚
β”‚   (Streamlit)   │◄──►│   (Python)      │◄──►│   (Pandas)      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚                       β”‚                       β”‚
         β–Ό                       β–Ό                       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   UI Components β”‚    β”‚   API Services  β”‚    β”‚   File Storage  β”‚
β”‚   CSS Styling   β”‚    β”‚   Session Mgmt  β”‚    β”‚   Cache Layer   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ–₯️ Frontend Architecture

Streamlit Framework

# Main application structure
import streamlit as st
import pandas as pd
import plotly.express as px

# Page configuration
st.set_page_config(
    page_title="Nordic Explorer",
    page_icon="πŸ”οΈ",
    layout="wide",
    initial_sidebar_state="expanded"
)

Component Structure

nordic_explorer.py
β”œβ”€β”€ Language System
β”‚   β”œβ”€β”€ Multi-language dictionary
β”‚   β”œβ”€β”€ Dynamic text rendering
β”‚   └── RTL support preparation
β”œβ”€β”€ Navigation System
β”‚   β”œβ”€β”€ Sidebar navigation
β”‚   β”œβ”€β”€ Page routing
β”‚   └── State management
β”œβ”€β”€ Core Pages
β”‚   β”œβ”€β”€ Home Dashboard
β”‚   β”œβ”€β”€ Trip Planner
β”‚   β”œβ”€β”€ Adventure Catalog
β”‚   β”œβ”€β”€ Accommodation Booking
β”‚   └── Analytics Dashboard
└── Utility Functions
    β”œβ”€β”€ Data processing
    β”œβ”€β”€ Chart generation
    └── User interaction handlers

Custom CSS Integration

/* Nordic Theme Variables */
:root {
    --nordic-blue: #2E86AB;
    --aurora-purple: #A23B72;
    --ice-white: #F8F9FA;
    --forest-green: #4ECDC4;
    --deep-grey: #333333;
}

/* Responsive Design */
@media (max-width: 768px) {
    .main-header { font-size: 2.5rem; }
    .adventure-card { margin: 0.5rem 0; }
}

πŸ”§ Backend Services

Session Management

# Streamlit session state management
if 'user_preferences' not in st.session_state:
    st.session_state.user_preferences = {
        'language': 'en',
        'currency': 'EUR',
        'trip_data': {},
        'booking_cart': []
    }

Data Processing Pipeline

def process_adventure_data():
    """
    Adventure data processing workflow
    """
    # Load adventure catalog
    adventures_df = load_adventure_catalog()
    
    # Apply filters and sorting
    filtered_adventures = apply_user_filters(adventures_df)
    
    # Calculate recommendations
    recommendations = generate_recommendations(filtered_adventures)
    
    return recommendations

API Integration Architecture

# Weather API integration
async def get_weather_data(location: str) -> dict:
    """
    Fetch real-time weather data for adventure planning
    """
    weather_api_key = os.getenv('WEATHER_API_KEY')
    # Implementation details...

# Booking API integration  
async def process_booking(booking_data: dict) -> dict:
    """
    Process adventure booking through partner APIs
    """
    booking_response = await booking_api.create_reservation(booking_data)
    return booking_response

πŸ“Š Data Architecture

Data Models

# Adventure data model
class Adventure:
    id: str
    name: str
    category: str
    difficulty: str
    duration: int
    price: float
    location: str
    coordinates: tuple
    rating: float
    description: str
    includes: list
    requirements: list

# Accommodation data model
class Accommodation:
    id: str
    name: str
    type: str  # hotel, cabin, ice_hotel, treehouse
    location: str
    price_per_night: float
    amenities: list
    rating: float
    availability: dict
    unique_features: list

Database Schema (Conceptual)

-- Adventures table
CREATE TABLE adventures (
    id VARCHAR(50) PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    category VARCHAR(50),
    difficulty ENUM('Easy', 'Moderate', 'Challenging', 'Expert'),
    duration INT, -- in hours
    price DECIMAL(10,2),
    location VARCHAR(100),
    latitude DECIMAL(10,8),
    longitude DECIMAL(11,8),
    rating DECIMAL(3,2),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Bookings table
CREATE TABLE bookings (
    id VARCHAR(50) PRIMARY KEY,
    user_id VARCHAR(50),
    adventure_id VARCHAR(50),
    booking_date DATE,
    participants INT,
    total_cost DECIMAL(10,2),
    status ENUM('pending', 'confirmed', 'cancelled'),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (adventure_id) REFERENCES adventures(id)
);

🌐 Internationalization (i18n)

Language Support Implementation

# Language dictionary structure
languages = {
    "en": {
        "title": "Nordic Explorer",
        "subtitle": "Discover Nordic Adventures",
        "navigation": {
            "home": "Home",
            "planner": "Trip Planner", 
            "adventures": "Adventures",
            "accommodations": "Stays"
        }
    },
    "sv": {
        "title": "Nordic Explorer", 
        "subtitle": "UpptΓ€ck nordiska Γ€ventyr",
        "navigation": {
            "home": "Hem",
            "planner": "Reseplanerare",
            "adventures": "Γ„ventyr", 
            "accommodations": "Boende"
        }
    }
}

Dynamic Text Rendering

def get_text(key: str, lang: str = "en") -> str:
    """
    Retrieve localized text based on language selection
    """
    keys = key.split('.')
    text_dict = languages.get(lang, languages["en"])
    
    for k in keys:
        text_dict = text_dict.get(k, key)
    
    return text_dict if isinstance(text_dict, str) else key

πŸ“± Mobile Optimization

Responsive Design Strategy

/* Mobile-first approach */
.container {
    width: 100%;
    padding: 0 1rem;
}

@media (min-width: 768px) {
    .container {
        max-width: 750px;
        margin: 0 auto;
    }
}

@media (min-width: 1200px) {
    .container {
        max-width: 1140px;
    }
}

Touch-Friendly Interactions

# Streamlit mobile optimizations
def create_mobile_friendly_filters():
    """
    Create touch-friendly filter interface
    """
    with st.expander("πŸ” Adventure Filters", expanded=False):
        difficulty = st.selectbox("Difficulty Level", options)
        category = st.multiselect("Activity Type", categories)
        price_range = st.slider("Price Range (EUR)", 50, 500, (100, 300))
    
    return difficulty, category, price_range

πŸ”’ Security Implementation

Data Validation

import re
from typing import Union

def validate_email(email: str) -> bool:
    """Validate email format"""
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return re.match(pattern, email) is not None

def sanitize_input(user_input: str) -> str:
    """Sanitize user input to prevent XSS"""
    # Remove potentially dangerous characters
    sanitized = re.sub(r'[<>"\']', '', user_input)
    return sanitized.strip()

Session Security

def generate_session_token() -> str:
    """Generate secure session token"""
    import secrets
    return secrets.token_urlsafe(32)

def validate_session(token: str) -> bool:
    """Validate session token"""
    # Implementation for session validation
    return True  # Placeholder

πŸ“ˆ Performance Optimization

Caching Strategy

@st.cache_data(ttl=3600)  # Cache for 1 hour
def load_adventure_catalog():
    """Load and cache adventure data"""
    # Expensive data loading operation
    return pd.read_csv('adventures.csv')

@st.cache_data(ttl=1800)  # Cache for 30 minutes  
def generate_weather_data(location: str):
    """Cache weather data to reduce API calls"""
    # Weather API call
    return weather_data

Lazy Loading Implementation

def lazy_load_images():
    """Implement lazy loading for adventure images"""
    placeholder = st.empty()
    
    if st.button("Load More Adventures"):
        with placeholder.container():
            for adventure in next_batch_adventures:
                display_adventure_card(adventure)

πŸ”„ API Integrations

External Service Integration

class WeatherService:
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = "https://api.openweathermap.org/data/2.5"
    
    async def get_forecast(self, lat: float, lon: float) -> dict:
        """Get weather forecast for adventure location"""
        # API implementation
        pass

class BookingService:
    def __init__(self, partner_api_key: str):
        self.api_key = partner_api_key
    
    async def check_availability(self, adventure_id: str, date: str) -> bool:
        """Check real-time availability"""
        # Booking API implementation
        pass

Error Handling

def handle_api_error(func):
    """Decorator for API error handling"""
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except requests.RequestException as e:
            st.error(f"Service temporarily unavailable: {str(e)}")
            return None
        except Exception as e:
            st.error(f"An unexpected error occurred: {str(e)}")
            return None
    return wrapper

πŸš€ Deployment Architecture

Streamlit Cloud Configuration

# .streamlit/config.toml
[server]
port = 8501
enableCORS = false
enableXsrfProtection = false
maxUploadSize = 50

[browser]
gatherUsageStats = false
serverAddress = "0.0.0.0"

[theme]
primaryColor = "#2E86AB"
backgroundColor = "#F8F9FA"
secondaryBackgroundColor = "#FFFFFF"
textColor = "#333333"
font = "sans serif"

CI/CD Pipeline

# .github/workflows/deploy.yml
name: Deploy to Streamlit Cloud
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: 3.8
    - name: Install dependencies
      run: |
        pip install -r requirements.txt
    - name: Run tests
      run: |
        python -m pytest tests/

πŸ“Š Monitoring & Analytics

Performance Metrics

import time
from functools import wraps

def monitor_performance(func):
    """Monitor function execution time"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        execution_time = time.time() - start_time
        
        # Log performance metrics
        st.sidebar.metric("Page Load Time", f"{execution_time:.2f}s")
        return result
    return wrapper

User Analytics

def track_user_interaction(action: str, details: dict = None):
    """Track user interactions for analytics"""
    analytics_data = {
        'timestamp': datetime.now(),
        'action': action,
        'details': details or {},
        'session_id': st.session_state.get('session_id'),
        'user_agent': st.context.headers.get('User-Agent', 'Unknown')
    }
    
    # Send to analytics service
    # analytics_service.track(analytics_data)

πŸ§ͺ Testing Strategy

Unit Testing

import pytest
import pandas as pd
from nordic_explorer import process_adventure_data, filter_by_difficulty

def test_adventure_data_processing():
    """Test adventure data processing function"""
    # Mock data
    test_data = pd.DataFrame({
        'name': ['Ice Climbing', 'Northern Lights'],
        'difficulty': ['Expert', 'Easy'],
        'price': [299, 89]
    })
    
    result = process_adventure_data(test_data)
    assert len(result) == 2
    assert result['name'].iloc[0] == 'Ice Climbing'

def test_difficulty_filter():
    """Test difficulty filtering"""
    adventures = create_test_adventures()
    filtered = filter_by_difficulty(adventures, 'Easy')
    assert all(adv['difficulty'] == 'Easy' for adv in filtered)

Integration Testing

def test_booking_workflow():
    """Test complete booking workflow"""
    # Test data
    booking_data = {
        'adventure_id': 'ice-climbing-001',
        'participants': 2,
        'date': '2025-02-15'
    }
    
    # Test booking process
    result = process_booking(booking_data)
    assert result['status'] == 'confirmed'
    assert result['total_cost'] > 0

πŸ“š Related Documentation: