Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
628 changes: 628 additions & 0 deletions ARCHITECTURE.md

Large diffs are not rendered by default.

246 changes: 246 additions & 0 deletions README_CLIENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
# Neo4j Aircraft Data Python Client

A clean, well-structured Python client library for working with aircraft data in Neo4j databases. This library provides a simple starting point with Python best practices, Pydantic models, and a repository pattern for organizing queries.

## Features

✅ **Pydantic Models** - Type-safe data models for all entities
✅ **Repository Pattern** - Clean separation of data access logic
✅ **Parameterized Queries** - Secure, injection-safe Cypher queries
✅ **Type Hints** - Full type annotations throughout
✅ **Integration Tests** - Working tests with testcontainers
✅ **Modern Packaging** - PEP 621 compliant pyproject.toml

## Installation

```bash
pip install -e .
```

For development with testing dependencies:

```bash
pip install -e ".[dev]"
```

## Quick Start

### Basic Usage

```python
from neo4j_client import Neo4jConnection, AircraftRepository

# Create connection
with Neo4jConnection(
uri="bolt://localhost:7687",
username="neo4j",
password="password"
) as conn:
# Initialize repository
repo = AircraftRepository(conn)

# Find an aircraft by ID
aircraft = repo.find_by_id("AC001")
if aircraft:
print(f"Found: {aircraft.model} ({aircraft.tail_number})")

# Find all aircraft for an operator
aircraft_list = repo.find_by_operator("American Airlines")
for ac in aircraft_list:
print(f"{ac.tail_number}: {ac.model}")
```

### Working with Flights

```python
from neo4j_client import FlightRepository

with Neo4jConnection(uri, username, password) as conn:
flight_repo = FlightRepository(conn)

# Get all flights for an aircraft
flights = flight_repo.find_by_aircraft_id("AC001")
for flight in flights:
print(f"{flight.flight_number}: {flight.origin} → {flight.destination}")
```

### Maintenance Events

```python
from neo4j_client import MaintenanceEventRepository

with Neo4jConnection(uri, username, password) as conn:
maint_repo = MaintenanceEventRepository(conn)

# Find critical maintenance events
critical_events = maint_repo.find_by_severity("CRITICAL")
for event in critical_events:
print(f"Event {event.event_id}: {event.fault}")

# Get maintenance history for an aircraft
events = maint_repo.find_by_aircraft_id("AC001")
for event in events:
print(f"{event.reported_at}: {event.fault} - {event.corrective_action}")
```

### Creating and Updating Aircraft

```python
from neo4j_client import Aircraft, AircraftRepository

with Neo4jConnection(uri, username, password) as conn:
repo = AircraftRepository(conn)

# Create new aircraft
aircraft = Aircraft(
aircraft_id="AC999",
tail_number="N12345",
icao24="ABC123",
model="Boeing 737-800",
operator="Test Airlines",
manufacturer="Boeing"
)
repo.create(aircraft)

# Update existing aircraft
aircraft.operator = "New Operator"
repo.update(aircraft)
```

## Available Models

The library includes Pydantic models for all major entities:

- **Aircraft** - Commercial aircraft with tail numbers, models, operators
- **Airport** - Airports with IATA/ICAO codes and coordinates
- **Flight** - Scheduled flight operations
- **System** - Aircraft systems (hydraulics, avionics, engines, etc.)
- **Component** - System components
- **Sensor** - Monitoring sensors
- **Reading** - Time-series sensor readings
- **MaintenanceEvent** - Maintenance events and fault reports
- **Delay** - Flight delay incidents

## Available Repositories

### AircraftRepository

- `create(aircraft)` - Create or merge aircraft
- `find_by_id(aircraft_id)` - Find by unique ID
- `find_by_tail_number(tail_number)` - Find by tail number
- `find_all(limit)` - Get all aircraft
- `find_by_operator(operator, limit)` - Filter by operator
- `update(aircraft)` - Update existing aircraft
- `delete(aircraft_id)` - Delete aircraft

### FlightRepository

- `find_by_aircraft_id(aircraft_id, limit)` - Get flights for aircraft
- `find_by_id(flight_id)` - Find specific flight

### AirportRepository

- `find_by_iata(iata)` - Find airport by IATA code
- `find_all(limit)` - Get all airports

### SystemRepository

- `find_by_aircraft_id(aircraft_id)` - Get systems for aircraft

### MaintenanceEventRepository

- `find_by_aircraft_id(aircraft_id, limit)` - Get maintenance events for aircraft
- `find_by_severity(severity, limit)` - Filter by severity level

## Testing

The library includes comprehensive tests using pytest and testcontainers:

```bash
pytest
```

Tests automatically spin up a Neo4j container, run integration tests, and clean up.

## Environment Configuration

Set these environment variables for production use:

```bash
export NEO4J_URI="bolt://localhost:7687"
export NEO4J_USERNAME="neo4j"
export NEO4J_PASSWORD="your-password"
export NEO4J_DATABASE="neo4j"
```

## Architecture

See [ARCHITECTURE.md](ARCHITECTURE.md) for detailed architecture documentation including:
- System design overview
- Component diagrams
- Data flow
- Query patterns

## What's Included

This is a **starting point** focused on:

- ✅ Clean, readable code with type hints
- ✅ Pydantic models for validation
- ✅ Repository pattern for queries
- ✅ Parameterized Cypher queries (security)
- ✅ Basic CRUD operations
- ✅ Working integration tests
- ✅ Modern Python packaging

## What's Not Included (Yet)

This library focuses on simplicity. Consider adding:

- Async/await support
- Connection pooling configuration
- Advanced transaction management
- Retry logic and circuit breakers
- Caching layers
- CLI tools
- Logging frameworks
- Monitoring/observability

## Next Steps

To extend this library:

1. **Add more repositories** - Create repositories for Component, Sensor, Reading, Delay entities
2. **Add complex queries** - Implement graph traversals, aggregations, recommendations
3. **Add batch operations** - Bulk insert/update methods
4. **Add relationship methods** - Link aircraft to flights, systems, etc.
5. **Add async support** - Use neo4j async driver if needed
6. **Add validation** - Enhanced Pydantic validators for business rules
7. **Add monitoring** - Integrate with your logging/metrics system

## Security

This library follows security best practices:

- ✅ **Parameterized queries** - All Cypher queries use parameters, never string formatting
- ✅ **MERGE over CREATE** - Prevents accidental duplicates
- ✅ **Pydantic validation** - Input validation before database operations
- ✅ **Error handling** - Custom exceptions for different error types

## Contributing

This is a demonstration library. For production use, consider:

- Adding comprehensive error handling
- Implementing connection pooling
- Adding query performance monitoring
- Implementing audit logging
- Adding schema validation

## License

MIT

## Support

This library is provided as a starting point for developers working with aircraft data in Neo4j. Extend and customize it for your specific needs.
148 changes: 148 additions & 0 deletions examples/usage_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
"""Example usage of the Neo4j Aircraft Data Python Client.

This script demonstrates how to use the client library to query aircraft data
from a Neo4j database. It includes examples of:
- Connecting to Neo4j
- Querying aircraft
- Finding flights for an aircraft
- Retrieving maintenance events
- Working with airports and systems
"""

import os
from neo4j_client import (
Neo4jConnection,
AircraftRepository,
FlightRepository,
AirportRepository,
SystemRepository,
MaintenanceEventRepository,
)


def main():
"""Run example queries against the Neo4j database."""

# Get connection details from environment variables
uri = os.getenv("NEO4J_URI", "bolt://localhost:7687")
username = os.getenv("NEO4J_USERNAME", "neo4j")
password = os.getenv("NEO4J_PASSWORD", "password")
database = os.getenv("NEO4J_DATABASE", "neo4j")

print("=" * 80)
print("Neo4j Aircraft Data Client - Example Usage")
print("=" * 80)
print(f"\nConnecting to Neo4j at {uri}...\n")

try:
# Create connection using context manager (auto cleanup)
with Neo4jConnection(uri, username, password, database) as conn:
print("✅ Connected successfully!\n")

# Example 1: List all aircraft
print("-" * 80)
print("Example 1: List Aircraft")
print("-" * 80)
aircraft_repo = AircraftRepository(conn)
aircraft_list = aircraft_repo.find_all(limit=5)
print(f"Found {len(aircraft_list)} aircraft:\n")
for ac in aircraft_list:
print(f" {ac.tail_number}")
print(f" Model: {ac.model}")
print(f" Operator: {ac.operator}")
print(f" Manufacturer: {ac.manufacturer}")
print()

if not aircraft_list:
print("No aircraft found in database. Exiting.\n")
return

# Example 2: Find aircraft by operator
print("-" * 80)
print("Example 2: Find Aircraft by Operator")
print("-" * 80)
first_operator = aircraft_list[0].operator
operator_aircraft = aircraft_repo.find_by_operator(first_operator, limit=3)
print(f"Aircraft operated by {first_operator}:\n")
for ac in operator_aircraft:
print(f" - {ac.tail_number} ({ac.model})")
print()

# Example 3: Get flights for specific aircraft
print("-" * 80)
print("Example 3: Flights for Specific Aircraft")
print("-" * 80)
first_aircraft = aircraft_list[0]
flight_repo = FlightRepository(conn)
flights = flight_repo.find_by_aircraft_id(first_aircraft.aircraft_id, limit=5)
print(f"Recent flights for {first_aircraft.tail_number}:\n")
for flight in flights:
print(f" {flight.flight_number}")
print(f" Route: {flight.origin} → {flight.destination}")
print(f" Departure: {flight.scheduled_departure}")
print(f" Arrival: {flight.scheduled_arrival}")
print()

# Example 4: Get aircraft systems
print("-" * 80)
print("Example 4: Aircraft Systems")
print("-" * 80)
system_repo = SystemRepository(conn)
systems = system_repo.find_by_aircraft_id(first_aircraft.aircraft_id)
print(f"Systems for {first_aircraft.tail_number}:\n")
for system in systems:
print(f" - {system.name} ({system.type})")
print()

# Example 5: Get maintenance events
print("-" * 80)
print("Example 5: Maintenance Events")
print("-" * 80)
maint_repo = MaintenanceEventRepository(conn)
events = maint_repo.find_by_aircraft_id(first_aircraft.aircraft_id, limit=5)
print(f"Recent maintenance events for {first_aircraft.tail_number}:\n")
for event in events:
print(f" [{event.severity}] {event.reported_at}")
print(f" Fault: {event.fault}")
print(f" Action: {event.corrective_action}")
print()

# Example 6: Find critical maintenance events
print("-" * 80)
print("Example 6: Critical Maintenance Events")
print("-" * 80)
critical_events = maint_repo.find_by_severity("CRITICAL", limit=5)
print(f"Critical maintenance events across all aircraft:\n")
for event in critical_events:
aircraft = aircraft_repo.find_by_id(event.aircraft_id)
tail = aircraft.tail_number if aircraft else "Unknown"
print(f" Aircraft: {tail}")
print(f" Reported: {event.reported_at}")
print(f" Fault: {event.fault[:60]}...")
print()

# Example 7: Airport information
print("-" * 80)
print("Example 7: Airport Information")
print("-" * 80)
airport_repo = AirportRepository(conn)
airports = airport_repo.find_all(limit=5)
print(f"Sample airports in the network:\n")
for airport in airports:
print(f" {airport.iata} - {airport.name}")
print(f" Location: {airport.city}, {airport.country}")
print(f" Coordinates: {airport.lat}, {airport.lon}")
print()

print("=" * 80)
print("✅ All examples completed successfully!")
print("=" * 80)

except Exception as e:
print(f"\n❌ Error: {e}\n")
import traceback
traceback.print_exc()


if __name__ == "__main__":
main()
Loading