Local Data Architecture Guide for Small Businesses

For Companies < $2M Revenue

Executive Summary

This guide proposes a lightweight data architecture based on local static report generation. The approach focuses on:

Key Characteristics

1. Data Storage & Access

| Category | Tool | Features | Best For | |———-|——|———–|———-| | Local Storage | DuckDB | • SQLite for analytics
• Zero configuration
• Python integration | • Local analysis
• File querying
• Fast processing | | File Format | Parquet | • Efficient compression
• Column format
• Wide support | • Data storage
• Efficient querying
• Version control | | Version Control | Git LFS | • Large file handling
• Version tracking
• Team collaboration | • Data versioning
• File tracking
• Distribution |

Project Structure Example:

my-reports/
├── data/
│   ├── sales.parquet
│   └── customers.parquet
├── config/
│   ├── reports/
│   │   ├── sales.yaml
│   │   └── customers.yaml
│   └── app.yaml
├── templates/
│   ├── components/
│   │   └── metric_card.html
│   └── pages/
│       └── sales.html
└── static/
    └── generated/
        └── reports/

Configuration Example:

# config/app.yaml
title: "Company Analytics"
theme: "light"
update_frequency: "daily"
data_path: "./data"
reports:
  - id: "sales"
    title: "Sales Dashboard"
    template: "sales.html"
    data_source: "sales.parquet"
  - id: "customers"
    title: "Customer Analysis"
    template: "customers.html"
    data_source: "customers.parquet"

# config/reports/sales.yaml
title: "Sales Analysis"
description: "Monthly sales performance"
metrics:
  - name: "Total Revenue"
    query: "SELECT sum(amount) FROM sales"
    format: "currency"
  - name: "Customer Count"
    query: "SELECT count(distinct customer_id) FROM sales"
    format: "number"
charts:
  - name: "Monthly Sales"
    type: "line"
    query: """
      SELECT
        date_trunc('month', date) as month,
        sum(amount) as revenue
      FROM sales
      GROUP BY 1
      ORDER BY 1
    """
    params:
      x: "month"
      y: "revenue"
      title: "Monthly Revenue"

2. Report Generation Engine

| Category | Tool | Features | Best For | |———-|——|———–|———-| | Web Framework | Streamlit | • Python-native
• Auto-refresh
• Interactive | • Local dashboards
• Quick deployment
• Rapid development | | Data Processing | Polars | • Fast processing
• Memory efficient
• Parquet support | • Data transformation
• Large files
• Quick analysis | | Visualization | Plotly | • Interactive charts
• HTML export
• Wide variety | • Rich visualizations
• Custom charts
• Exports |

Main Application Code:

import streamlit as st
import yaml
import duckdb
import plotly.express as px
from pathlib import Path
from typing import Dict, Any

class LocalReportGenerator:
    def __init__(self, config_path: str = "config/app.yaml"):
        self.config = self.load_config(config_path)
        self.db = duckdb.connect(":memory:")

    def load_config(self, path: str) -> Dict[str, Any]:
        with open(path) as f:
            return yaml.safe_load(f)

    def get_report_config(self, report_id: str) -> Dict[str, Any]:
        with open(f"config/reports/{report_id}.yaml") as f:
            return yaml.safe_load(f)

    def execute_query(self, query: str) -> pd.DataFrame:
        return self.db.execute(query).fetchdf()

    def create_chart(self, chart_config: Dict[str, Any]) -> go.Figure:
        data = self.execute_query(chart_config['query'])
        return px.line(
            data,
            x=chart_config['params']['x'],
            y=chart_config['params']['y'],
            title=chart_config['params']['title']
        )

def main():
    st.set_page_config(page_title="Company Analytics", layout="wide")

    # Initialize generator
    generator = LocalReportGenerator()

    # Sidebar navigation
    st.sidebar.title("Navigation")
    selected_report = st.sidebar.selectbox(
        "Select Report",
        options=[r['id'] for r in generator.config['reports']],
        format_func=lambda x: next(r['title'] for r in generator.config['reports'] if r['id'] == x)
    )

    # Load report config
    report_config = generator.get_report_config(selected_report)

    # Display report
    st.title(report_config['title'])

    # Display metrics
    cols = st.columns(len(report_config['metrics']))
    for col, metric in zip(cols, report_config['metrics']):
        with col:
            value = generator.execute_query(metric['query']).iloc[0, 0]
            st.metric(metric['name'], value)

    # Display charts
    for chart_config in report_config['charts']:
        fig = generator.create_chart(chart_config)
        st.plotly_chart(fig, use_container_width=True)

if __name__ == "__main__":
    main()

Architecture

flowchart TB
    subgraph "Remote Object Storage"
        S3[("S3 Storage")]
        subgraph "Configuration"
            C1[app_structure.yaml]
            C2[page_configs/*.yaml]
        end
        subgraph "Data"
            D1[data/*.parquet]
        end
        C1 --> S3
        C2 --> S3
        D1 --> S3
    end

    subgraph "Local Application"
        ST[Streamlit App]
        DB[(DuckDB)]
        subgraph "Local Cache"
            LC[Streamlit Cache]
            DC[DuckDB Cache]
        end
    end

    %% Data flow
    S3 -->|1. Load app config| ST
    S3 -->|2. Load page configs| ST
    S3 -->|3. Read data| DB
    DB -->|4. Filtered data| ST

    %% Cache
    ST -->|Cache configs| LC
    DB -->|Cache data| DC

    %% Interface utilisateur
    subgraph "Interface Utilisateur"
        P1[Page d'accueil]
        P2[Pages Rapports]
    end

    ST -->|Affichage| P1
    ST -->|Visualisations| P2

    style S3 fill:#f9f,stroke:#333
    style ST fill:#bbf,stroke:#333
    style DB fill:#bfb,stroke:#333

Implementation Guide

  1. Setup (Day 1)
    # Create virtual environment
    python -m venv venv
    source venv/bin/activate
    
    # Install requirements
    pip install streamlit duckdb plotly pyyaml polars
    
    # Create project structure
    mkdir -p {data,config/{reports},templates/{components,pages},static/generated/reports}
    
  2. Development (Day 2-3)
    • Create base configurations
    • Develop report templates
    • Set up data processing
  3. Team Usage (Day 4+)
    • Share repository
    • Document processes
    • Train team members

Best Practices

  1. Use Git for version control
  2. Document configurations
  3. Regular data backups
  4. Consistent naming conventions
  5. Regular code reviews

Advantages

Limitations

Resources

Next Steps

  1. Clone repository
  2. Configure reports
  3. Add data sources
  4. Create visualizations
  5. Share with team

Would you like me to elaborate on any specific aspect or add more examples?