← Back to Docs Index

Developer Guide

Development Environment Setup

Prerequisites

  • Python 3.11+
  • PostgreSQL (for development)
  • Git
  • Code editor (VS Code recommended)

Quick Setup

# Clone repository
git clone https://github.com/your-org/race-management-console.git
cd race-management-console

# Setup virtual environment
python3.11 -m venv venv
source venv/bin/activate

# Install dependencies
pip install -r requirements.txt

# Setup environment variables
cp .env.example .env
# Edit .env with your configuration

# Initialize database
python -c "from app import app, db; app.app_context().push(); db.create_all()"

# Run development server
flask run --debug --host=0.0.0.0 --port=5000

Project Structure

race-management-console/
├── app.py                  # Flask application factory
├── main.py                 # Application entry point
├── models.py               # Database models
├── routes.py               # API routes and handlers
├── config/                 # Configuration files
│   ├── app_config.json     # Application settings
│   └── system_tags.json    # System tag definitions
├── services/               # Business logic services
│   ├── ai_conversation.py  # AI conversation handling
│   ├── aveva_client.py     # CONNECT API client
│   ├── config_manager.py   # Configuration management
│   ├── context_extractor.py # MES context extraction
│   ├── event_enrichment.py # Event data enrichment
│   ├── function_calling.py # AI function calling
│   ├── monitoring_engine.py # Background monitoring
│   ├── placeholder_resolver.py # Template placeholders
│   ├── rule_engine.py      # Rule evaluation engine
│   └── system_tags.py      # System tag management
├── templates/              # Jinja2 templates
│   ├── base.html           # Base template
│   ├── index.html          # Dashboard
│   ├── configuration/      # Configuration pages
│   ├── rules/              # Rule management pages
│   ├── events/             # Event monitoring pages
│   └── cognition/          # AI interface pages
├── static/                 # Static assets
│   ├── css/                # Stylesheets
│   ├── js/                 # JavaScript files
│   └── images/             # Images and icons
├── docs/                   # Documentation
├── tests/                  # Test suite
├── requirements.txt        # Python dependencies
└── .env.example           # Environment template

Database Models

Core Models

Package

class Package(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    description = db.Column(db.Text)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    # Relationships
    templates = db.relationship('RuleTemplate', backref='package', lazy=True)

RuleTemplate

class RuleTemplate(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    category = db.Column(db.String(50), nullable=False)
    color = db.Column(db.String(7), default='#007bff')
    description = db.Column(db.Text)
    package_id = db.Column(db.Integer, db.ForeignKey('package.id'), nullable=False)

    # Relationships
    rules = db.relationship('Rule', backref='template', lazy=True)
    instances = db.relationship('TemplateInstance', backref='template', lazy=True)

TemplateInstance

class TemplateInstance(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    instance_name = db.Column(db.String(100), nullable=False)
    template_id = db.Column(db.Integer, db.ForeignKey('rule_template.id'), nullable=False)
    plant_node_floc_id = db.Column(db.String(100), nullable=False)
    is_deployed = db.Column(db.Boolean, default=False)

    # Relationships
    placeholders = db.relationship('TemplatePlaceholder', backref='instance', lazy=True)
    events = db.relationship('RuleEvent', backref='template_instance', lazy=True)

Adding New Models

  1. Define Model Class
class NewModel(db.Model):
    __tablename__ = 'new_models'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    def to_dict(self):
        return {
            'id': self.id,
            'name': self.name,
            'created_at': self.created_at.isoformat() if self.created_at else None
        }
  1. Create Migration
# In flask shell or migration script
from app import app, db
with app.app_context():
    db.create_all()
  1. Add to models.py imports
# Update routes.py import statement
from models import NewModel, ...

Service Layer Development

Creating New Services

  1. Service Structure
# services/new_service.py
import logging
from typing import Dict, List, Optional

logger = logging.getLogger(__name__)

class NewService:
    def __init__(self):
        self.config = {}

    def process_data(self, data: Dict) -> Dict:
        """Process data with error handling and logging"""
        try:
            logger.info(f"Processing data: {data}")
            # Implementation here
            result = self._internal_processing(data)
            logger.info("Processing completed successfully")
            return result
        except Exception as e:
            logger.error(f"Error processing data: {str(e)}")
            raise

    def _internal_processing(self, data: Dict) -> Dict:
        """Private method for internal processing"""
        return {"processed": True, "data": data}
  1. Service Registration
# In app.py or main.py
from services.new_service import NewService

# Initialize service
new_service = NewService()
  1. Using in Routes
# In routes.py
from services.new_service import NewService

@app.route('/api/new-endpoint')
def new_endpoint():
    try:
        service = NewService()
        result = service.process_data(request.json)
        return jsonify({'success': True, 'data': result})
    except Exception as e:
        return jsonify({'success': False, 'error': str(e)}), 500

AI Service Integration

Adding New AI Providers

  1. Extend AIConversationService
# services/ai_conversation.py
class AIConversationService:
    def _call_new_provider(self, provider: AIProvider, messages: List[Dict], functions: List[Dict] = None) -> Dict:
        """Add support for new AI provider"""
        try:
            # Initialize provider client
            client = NewProviderClient(api_key=provider.api_key)

            # Format messages for provider
            formatted_messages = self._format_messages_for_new_provider(messages)

            # Make API call
            response = client.chat.completions.create(
                model=provider.model_name,
                messages=formatted_messages,
                max_tokens=provider.max_tokens,
                temperature=provider.temperature,
                functions=functions if provider.supports_function_calling else None
            )

            return self._process_new_provider_response(response)
        except Exception as e:
            logger.error(f"Error calling new provider: {str(e)}")
            raise
  1. Update Provider Type Handling
def call_ai_provider(self, provider: AIProvider, messages: List[Dict], functions: List[Dict] = None) -> Dict:
    """Route to appropriate provider implementation"""
    if provider.provider_type == 'openai':
        return self._call_openai(provider, messages, functions)
    elif provider.provider_type == 'anthropic':
        return self._call_anthropic(provider, messages, functions)
    elif provider.provider_type == 'new_provider':
        return self._call_new_provider(provider, messages, functions)
    else:
        raise ValueError(f"Unsupported provider type: {provider.provider_type}")

Function Calling Development

Adding New Functions

  1. Define Function Schema
# services/function_calling.py
def get_equipment_diagnostics(equipment_id: str, diagnostic_type: str = "all") -> Dict:
    """
    Get detailed diagnostic information for specific equipment

    Args:
        equipment_id: Equipment identifier (e.g., 'Roaster022')
        diagnostic_type: Type of diagnostics ('performance', 'health', 'all')

    Returns:
        Dict containing diagnostic data
    """
    function_schema = {
        "name": "get_equipment_diagnostics",
        "description": "Get detailed diagnostic information for specific equipment",
        "parameters": {
            "type": "object",
            "properties": {
                "equipment_id": {
                    "type": "string",
                    "description": "Equipment identifier"
                },
                "diagnostic_type": {
                    "type": "string",
                    "enum": ["performance", "health", "all"],
                    "description": "Type of diagnostics to retrieve"
                }
            },
            "required": ["equipment_id"]
        }
    }
    return function_schema
  1. Implement Function Logic
class FunctionCallingService:
    def execute_function(self, function_name: str, arguments: Dict) -> Dict:
        """Execute function with error handling"""
        try:
            if function_name == "get_equipment_diagnostics":
                return self._get_equipment_diagnostics(**arguments)
            else:
                raise ValueError(f"Unknown function: {function_name}")
        except Exception as e:
            logger.error(f"Function execution error: {str(e)}")
            return {"error": str(e)}

    def _get_equipment_diagnostics(self, equipment_id: str, diagnostic_type: str = "all") -> Dict:
        """Implementation of equipment diagnostics function"""
        # Query database for equipment data
        streams = MonitoredStream.query.filter(
            MonitoredStream.stream_name.like(f"%{equipment_id}%")
        ).all()

        # Gather diagnostic data
        diagnostics = {
            "equipment_id": equipment_id,
            "diagnostic_type": diagnostic_type,
            "timestamp": datetime.utcnow().isoformat(),
            "data": {}
        }

        # Add specific diagnostic logic here
        return diagnostics

Frontend Development

JavaScript Structure

Component Pattern

// static/js/components/event-wall.js
class EventWall {
    constructor(containerId, options = {}) {
        this.container = document.getElementById(containerId);
        this.options = {
            refreshInterval: 30000,
            maxEvents: 100,
            ...options
        };
        this.eventData = [];
        this.init();
    }

    init() {
        this.setupEventListeners();
        this.loadEvents();
        this.startAutoRefresh();
    }

    setupEventListeners() {
        // Event listeners for UI interactions
    }

    async loadEvents() {
        try {
            const response = await fetch('/api/event-wall-data');
            const data = await response.json();
            if (data.success) {
                this.eventData = data.data;
                this.render();
            }
        } catch (error) {
            console.error('Error loading events:', error);
        }
    }

    render() {
        // Render event wall visualization
    }
}

Usage Pattern

// In template or main.js
document.addEventListener('DOMContentLoaded', function() {
    const eventWall = new EventWall('event-wall-container', {
        refreshInterval: 15000,
        layout: 'template'
    });
});

CSS Organization

Component Styles

/* static/css/components/event-wall.css */
.event-wall {
    --primary-color: #007bff;
    --warning-color: #ffc107;
    --error-color: #dc3545;
    --success-color: #28a745;
}

.event-wall__container {
    display: flex;
    flex-direction: column;
    height: 100%;
}

.event-wall__timeline {
    flex: 1;
    overflow-y: auto;
}

.event-wall__event {
    padding: 0.75rem;
    margin-bottom: 0.5rem;
    border-radius: 0.25rem;
    transition: all 0.2s ease;
}

.event-wall__event--active {
    border-left: 4px solid var(--primary-color);
    background-color: rgba(0, 123, 255, 0.1);
}

.event-wall__event--warning {
    border-left: 4px solid var(--warning-color);
    background-color: rgba(255, 193, 7, 0.1);
}

Template Development

Base Template Structure

<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en" data-bs-theme="dark">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}RACE Management Console{% endblock %}</title>

    <!-- Bootstrap CSS -->
    <link href="https://cdn.replit.com/agent/bootstrap-agent-dark-theme.min.css" rel="stylesheet">

    <!-- Custom CSS -->
    <link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}?v={{ cache_buster }}">
    {% block extra_css %}{% endblock %}
</head>
<body>
    <!-- Navigation -->
    {% include 'partials/navigation.html' %}

    <!-- Main Content -->
    <main class="container-fluid py-4">
        {% block content %}{% endblock %}
    </main>

    <!-- JavaScript -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
    <script src="https://unpkg.com/feather-icons"></script>
    <script>feather.replace();</script>

    {% block extra_js %}{% endblock %}
</body>
</html>

Page Template Pattern

<!-- templates/new-page.html -->
{% extends "base.html" %}

{% block title %}New Page - RACE Console{% endblock %}

{% block extra_css %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/new-page.css') }}?v={{ cache_buster }}">
{% endblock %}

{% block content %}
<div class="row">
    <div class="col-12">
        <div class="card">
            <div class="card-header">
                <h5 class="card-title mb-0">
                    <i data-feather="icon-name" class="me-2"></i>Page Title
                </h5>
            </div>
            <div class="card-body">
                <!-- Page content -->
            </div>
        </div>
    </div>
</div>
{% endblock %}

{% block extra_js %}
<script src="{{ url_for('static', filename='js/new-page.js') }}?v={{ cache_buster }}"></script>
{% endblock %}

Form Builder Development

Architecture Overview

The Dynamic Form Builder follows a modular service-oriented architecture with clear separation between AI generation, form management, and runtime execution.

Core Components

Database Models:

# Form registry for metadata
class FormRegistry(db.Model):
    form_id = db.Column(db.String(200), primary_key=True)
    latest_version = db.Column(db.String(20))
    created_by = db.Column(db.String(200))

# Individual form versions with content
class FormVersion(db.Model):
    id = db.Column(db.String(36), primary_key=True)
    form_id = db.Column(db.String(200))
    version = db.Column(db.String(20))  # major.minor.patch.build
    status = db.Column(db.String(20))   # draft, published
    content = db.Column(JSON)           # Complete form JSON
    grid_layout = db.Column(JSON)       # Drag & drop layout
    checksum = db.Column(db.String(64)) # Content integrity

Service Layer: - ai_form_authoring.py: AI conversation management and JSON generation - widget_renderer.py: Runtime widget rendering with grid layout support - form_data_handler.py: Form data validation and processing - form_schema.py: JSON schema validation with RACE-specific rules

Database Models (Complete Schema):

class FormRegistry(db.Model):
    form_id = db.Column(db.String(200), primary_key=True)
    latest_version = db.Column(db.String(20))  # e.g. "2.1.0.5"
    created_by = db.Column(db.String(200))
    created_at = db.Column(db.DateTime)
    updated_at = db.Column(db.DateTime)

class FormVersion(db.Model):
    id = db.Column(db.String(36), primary_key=True)
    form_id = db.Column(db.String(200))
    version = db.Column(db.String(20))     # "major.minor.patch.build"
    status = db.Column(db.String(20))      # "draft" | "published"
    content = db.Column(JSON)              # Complete form JSON
    grid_layout = db.Column(JSON)          # Version-specific layout
    checksum = db.Column(db.String(64))    # Content integrity
    conversation_id = db.Column(db.String(36))  # AI conversation link

class FormPermission(db.Model):
    id = db.Column(db.String(36), primary_key=True)
    form_id = db.Column(db.String(200))
    user_role = db.Column(db.String(100))  # RBAC integration
    permission_level = db.Column(db.String(50))

4-Digit Versioning System

Version Structure: major.minor.patch.build - User controls: Major, Minor, Patch via UI buttons - System controls: Build auto-increment via checkbox - Independent status management per version

Grid Layout System

Version-Specific Storage: - Each FormVersion has independent grid_layout JSON - Version selection clears localStorage to force database reload - CSS Grid positioning with exact cell placement

API Endpoints

Version Management APIs

# Save grid layout with version targeting and auto-INSERT
POST /form-builder/api/save-grid-layout/{form_id}
Body: {
  "grid_layout": {
    "rows": 5, "cols": 7,
    "cells": [{"id": "cell_0_0", "row": 0, "col": 0, "widget_id": "name_123"}]
  },
  "version": "2.1.0.3",      # X.X.X.X format
  "status": "draft"          # draft/published
}
Response: {
  "success": true,
  "message": "Grid layout saved and version created",
  "version_created": true
}

# Update version status only (no content modification)
POST /form-builder/api/update-version-status/{form_id}  
Body: {
  "version": "2.1.0.3",
  "status": "published"
}
Response: {
  "success": true,
  "message": "Version status updated",
  "old_status": "draft",
  "new_status": "published"
}

# Load specific version with ver parameter
GET /form-builder/forms/{form_id}?ver=2.1.0.3
Response: FormVersion object with version-specific content and grid_layout

# Combined runtime access
GET /form-builder/runtime/{form_id}?ver=2.1.0.3&status=draft
# Loads specific version with status filtering

Runtime System

# Unified runtime with version support
GET /form-builder/runtime/{form_id}?ver=X.X.X.X&status=draft

# Version parameter examples
GET /form-builder/runtime/maintenance_form?ver=2.1.0.3&status=draft
GET /form-builder/runtime/inspection_checklist?ver=1.0.0.1&status=published

Frontend Architecture

Version Management

// Global version state
let currentVersionData = {
    major: 1, minor: 0, patch: 0, build: 0
};

// Version selection with cache management
function loadVersion(selectedVersion) {
    // Clear localStorage to force DB reload
    const layoutKey = `gridLayout_${formId}`;
    localStorage.removeItem(layoutKey);

    // Reload with version parameter
    const url = new URL(window.location);
    url.searchParams.set('version', selectedVersion);
    window.location.href = url.toString();
}

Build Increment Control

// Conditional build increment
async function saveFormChanges() {
    const autoIncrementCheckbox = document.getElementById('autoIncrementBuild');
    let buildWasIncremented = false;

    if (autoIncrementCheckbox?.checked) {
        currentVersionData.build++;
        updateVersionDisplay();
        buildWasIncremented = true;
    }
    // Save logic with error rollback
}

API Development

Route Structure

Standard Route Pattern

@app.route('/api/resource', methods=['GET', 'POST'])
def handle_resource():
    """Handle resource operations with proper error handling"""
    try:
        if request.method == 'GET':
            return get_resource()
        elif request.method == 'POST':
            return create_resource()
    except Exception as e:
        logger.error(f"Error in handle_resource: {str(e)}")
        return jsonify({'success': False, 'error': str(e)}), 500

def get_resource():
    """Get resource with pagination and filtering"""
    # Pagination
    page = request.args.get('page', 1, type=int)
    per_page = request.args.get('per_page', 20, type=int)

    # Filtering
    filter_param = request.args.get('filter')

    # Query building
    query = Resource.query
    if filter_param:
        query = query.filter(Resource.name.contains(filter_param))

    # Execute query with pagination
    resources = query.paginate(
        page=page, 
        per_page=per_page, 
        error_out=False
    )

    return jsonify({
        'success': True,
        'data': [r.to_dict() for r in resources.items],
        'pagination': {
            'page': page,
            'per_page': per_page,
            'total': resources.total,
            'pages': resources.pages,
            'has_next': resources.has_next,
            'has_prev': resources.has_prev
        }
    })

def create_resource():
    """Create new resource with validation"""
    data = request.get_json()

    # Validation
    if not data or 'name' not in data:
        return jsonify({'success': False, 'error': 'Name is required'}), 400

    # Create resource
    resource = Resource(name=data['name'])
    db.session.add(resource)
    db.session.commit()

    return jsonify({
        'success': True,
        'data': resource.to_dict(),
        'message': 'Resource created successfully'
    }), 201

Error Handling

Custom Exception Classes

# exceptions.py
class RaceConsoleException(Exception):
    """Base exception for RACE Console"""
    pass

class ConfigurationError(RaceConsoleException):
    """Configuration-related errors"""
    pass

class APIConnectionError(RaceConsoleException):
    """External API connection errors"""
    pass

class RuleEvaluationError(RaceConsoleException):
    """Rule evaluation errors"""
    pass

Error Handler Registration

# app.py
@app.errorhandler(APIConnectionError)
def handle_api_connection_error(e):
    logger.error(f"API Connection Error: {str(e)}")
    return jsonify({
        'success': False,
        'error': 'External API connection failed',
        'details': str(e)
    }), 503

@app.errorhandler(ValidationError)
def handle_validation_error(e):
    return jsonify({
        'success': False,
        'error': 'Validation failed',
        'details': e.messages
    }), 400

Testing

Unit Tests

Test Structure

# tests/test_rule_engine.py
import pytest
from unittest.mock import Mock, patch
from services.rule_engine import RuleEngine
from models import Rule, TemplateInstance

class TestRuleEngine:
    def setup_method(self):
        """Setup test environment"""
        self.rule_engine = RuleEngine()
        self.mock_rule = Mock(spec=Rule)
        self.mock_instance = Mock(spec=TemplateInstance)

    def test_evaluate_condition_equals(self):
        """Test equality condition evaluation"""
        # Arrange
        condition = {
            'attribute': 'Status',
            'operator': 'equals',
            'value': 'Running'
        }
        stream_value = 'Running'

        # Act
        result = self.rule_engine._evaluate_condition(condition, stream_value)

        # Assert
        assert result is True

    @patch('services.rule_engine.RuleEvent')
    def test_trigger_rule_creates_event(self, mock_rule_event):
        """Test that rule triggering creates events"""
        # Arrange
        self.mock_rule.event_name = 'Test Event'
        self.mock_rule.severity = 'info'

        # Act
        self.rule_engine.trigger_rule(self.mock_rule, self.mock_instance, 'test_value')

        # Assert
        mock_rule_event.assert_called_once()

Running Tests

# Install test dependencies
pip install pytest pytest-cov pytest-mock

# Run all tests
pytest

# Run with coverage
pytest --cov=. --cov-report=html

# Run specific test file
pytest tests/test_rule_engine.py

# Run specific test
pytest tests/test_rule_engine.py::TestRuleEngine::test_evaluate_condition_equals

Integration Tests

API Testing

# tests/test_api.py
import pytest
from app import app, db

@pytest.fixture
def client():
    """Test client fixture"""
    app.config['TESTING'] = True
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'

    with app.test_client() as client:
        with app.app_context():
            db.create_all()
            yield client
            db.drop_all()

def test_get_packages(client):
    """Test packages API endpoint"""
    response = client.get('/api/packages')
    assert response.status_code == 200

    data = response.get_json()
    assert data['success'] is True
    assert 'data' in data

def test_create_package(client):
    """Test package creation"""
    package_data = {
        'name': 'Test Package',
        'description': 'Test description'
    }

    response = client.post('/api/packages', json=package_data)
    assert response.status_code == 201

    data = response.get_json()
    assert data['success'] is True
    assert data['data']['name'] == 'Test Package'

Debugging

Logging Configuration

Setup Structured Logging

# config/logging.py
import logging
import logging.config

LOGGING_CONFIG = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
        },
        'detailed': {
            'format': '%(asctime)s [%(levelname)s] %(name)s:%(lineno)d: %(message)s'
        }
    },
    'handlers': {
        'console': {
            'level': 'INFO',
            'class': 'logging.StreamHandler',
            'formatter': 'standard'
        },
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': 'app.log',
            'formatter': 'detailed'
        }
    },
    'loggers': {
        '': {
            'handlers': ['console', 'file'],
            'level': 'DEBUG',
            'propagate': False
        },
        'services': {
            'handlers': ['console', 'file'],
            'level': 'DEBUG',
            'propagate': False
        }
    }
}

def setup_logging():
    logging.config.dictConfig(LOGGING_CONFIG)

Performance Profiling

Flask-Profiler Integration

# app.py
from flask_profiler import Profiler

# Add to application factory
if app.config.get('PROFILING_ENABLED'):
    app.config['flask_profiler'] = {
        'enabled': True,
        'storage': {
            'engine': 'sqlite'
        },
        'basicAuth': {
            'enabled': True,
            'username': 'admin',
            'password': 'admin'
        }
    }
    profiler = Profiler()
    profiler.init_app(app)

Database Debugging

Query Logging

# Enable SQL query logging
import logging
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)

# Or in app configuration
app.config['SQLALCHEMY_ECHO'] = True

Database Connection Debugging

# services/database_debug.py
def debug_database_connection():
    """Debug database connection issues"""
    try:
        from app import db
        result = db.session.execute('SELECT 1')
        logger.info("Database connection successful")
        return True
    except Exception as e:
        logger.error(f"Database connection failed: {str(e)}")
        return False

Deployment

Production Checklist

Code Quality

  • [ ] All tests passing
  • [ ] Code coverage > 80%
  • [ ] No debug statements in production code
  • [ ] Proper error handling
  • [ ] Security review completed

Configuration

  • [ ] Environment variables set
  • [ ] Database migrations applied
  • [ ] SSL certificates configured
  • [ ] Firewall rules configured
  • [ ] Monitoring setup

Performance

  • [ ] Database indexes optimized
  • [ ] Static file caching configured
  • [ ] Application performance tested
  • [ ] Load testing completed

Continuous Integration

GitHub Actions Example

# .github/workflows/test.yml
name: Test Suite

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:13
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: test_db
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
    - uses: actions/checkout@v2

    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: 3.11

    - name: Install dependencies
      run: |
        pip install -r requirements.txt
        pip install pytest pytest-cov

    - name: Run tests
      run: pytest --cov=. --cov-report=xml
      env:
        DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db

    - name: Upload coverage
      uses: codecov/codecov-action@v1

Contributing

Code Style

Python Style Guide

  • Follow PEP 8
  • Use type hints where appropriate
  • Maximum line length: 120 characters
  • Use descriptive variable names
  • Document complex functions

JavaScript Style Guide

  • Use ES6+ features
  • Consistent indentation (2 spaces)
  • Use meaningful variable names
  • Comment complex logic

Git Workflow

  1. Create feature branch from main
  2. Make commits with descriptive messages
  3. Create pull request
  4. Code review and approval
  5. Merge to main

Pull Request Template

## Description
Brief description of changes

## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update

## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests added/updated
- [ ] Manual testing completed

## Checklist
- [ ] Code follows style guidelines
- [ ] Self-review completed
- [ ] Documentation updated
- [ ] Tests pass locally

Version: beta

On this page