BuybackJanice
This article explores the development of an optimized desktop application that solves this problem by interfacing with EVE Online’s ESI (EVE Swagger Interface) API to provide real-time transportation analysis.
Building an Optimized EVE Online Transportation Calculator with Python and PyQt5 Link to heading
Introduction Link to heading
EVE Online, the massive multiplayer space simulation game, presents players with complex logistics challenges. One fundamental question every industrialist and hauler faces is: “How do I transport these items efficiently?” This seemingly simple question involves calculating cargo volumes, determining appropriate ship types, and considering the economic value density of items being transported.
The Problem Space Link to heading
Transportation Types in EVE Online Link to heading
EVE Online features several categories of industrial ships, each with different cargo capacities:
- Jump Freighters (JF): 350,000 m³ capacity - expensive but can traverse nullsec space via cyno jumps
- Freighters: 1,139,969 m³ capacity - larger cargo holds but limited to high-security routes
- Items exceeding freighter capacity: Require multiple trips or alternative logistics solutions
Beyond simple volume calculations, sophisticated haulers also consider ISK density (ISK per cubic meter). High-value, low-volume items are priority targets for gankers, making this metric critical for risk assessment.
System Architecture Link to heading
Technology Stack Link to heading
The application leverages:
- PyQt5: Cross-platform GUI framework for desktop applications
- ESI API: CCP Games’ official API for EVE Online data
- Pandas: Data manipulation for Excel/CSV import/export
- ThreadPoolExecutor: Concurrent API calls for performance optimization
- QThread: Non-blocking UI operations during network requests
Key Design Decisions Link to heading
1. Persistent Type Caching Link to heading
The most significant optimization is a class-level cache for item type data:
class DataFetcherThread(QThread):
type_cache = {} # Persists across multiple runs
Since item metadata (volume, type_id) is immutable, we cache it at the class level. This means:
- First lookup of “Tritanium” requires 2 API calls
- Subsequent lookups across the entire session are instant
- Users processing similar cargo lists see dramatic speedups
2. Parallel API Request Processing Link to heading
Instead of sequential processing, we use concurrent futures:
with ThreadPoolExecutor(max_workers=5) as executor:
future_to_item = {
executor.submit(self.process_item, name, qty): (name, qty)
for name, qty in self.items
}
This parallelizes independent API calls, reducing total processing time from O(n × latency) to approximately O(latency) for most cargo manifests.
3. Single Market Price Fetch Link to heading
Rather than individual price lookups per item, we fetch the entire market price database once:
p_url = f"{ESI_BASE}/markets/prices/?datasource=tranquility"
p_resp = requests.get(p_url, timeout=30)
This single ~300KB download provides adjusted/average prices for all ~33,000 item types in EVE, enabling O(1) price lookups.
Implementation Deep Dive Link to heading
Data Flow Architecture Link to heading
User Input → Parser → Thread Pool → ESI API → Results Table
↓
Type Cache (persistent)
↓
Price Database (one-time fetch)
ESI API Integration Link to heading
The application makes three types of ESI calls:
- Item Search:
/universe/ids/(POST with item names) - Type Metadata:
/universe/types/{type_id}/(volume data) - Market Prices:
/markets/prices/(one bulk fetch)
Each has appropriate error handling and timeout settings to handle EVE’s occasionally unstable API.
Performance Optimization Results Link to heading
Before Optimization Link to heading
- Sequential processing: ~2 seconds per item (2 API calls × 500ms + 1s processing)
- No caching: Repeated items processed fully each time
- Individual price lookups: 50 items = 50 additional API calls
Example: 50-item cargo manifest = ~120 seconds
After Optimization Link to heading
- Parallel processing: 5 concurrent workers
- Type caching: Instant lookups for repeated items
- Bulk price fetch: Single 300KB download
Example: 50-item cargo manifest = ~15 seconds (8× faster)
Subsequent runs with cached items: <5 seconds (24× faster)
Aggregate Summary Calculation Link to heading
The system calculates total volume and provides intelligent recommendations:
if total_volume <= JF_CAPACITY:
ships_text = "1 Jump Freighter"
elif total_volume <= FREIGHTER_CAPACITY:
ships_text = "1 Freighter"
else:
num_freighters = math.ceil(total_volume / FREIGHTER_CAPACITY)
ships_text = f"{num_freighters} Freighters"
ISK Density Analysis Link to heading
High-value items are flagged automatically:
if isk_per_m3 >= 1_000_000: # 1M ISK/m³ threshold
note = f"High ISK/m³ ({int(isk_per_m3):,})"
This warns haulers about gank-worthy cargo requiring additional escort or lower-profile shipping methods.
Error Handling Strategy Link to heading
The application implements defense-in-depth error handling:
- API Level: Try-catch around all network calls with timeouts
- Data Level: Validation of ESI responses and type conversions
- UI Level: User-friendly error messages via QMessageBox
- Thread Level: Error signals from worker thread to UI thread
Example error handling pattern:
try:
result = future.result()
results.append(result)
except Exception as e:
results.append({
"item": name,
"transport_type": f"Error: {e}"
})
Failed items don’t crash the application; they appear in results with error descriptions.
Color-Coded Ship Requirements Link to heading
Visual feedback uses color psychology:
- 🟢 Green: Single JF (safe, easy)
- 🔵 Blue: Single Freighter (routine)
- 🔴 Red: Multiple ships required (complex logistics)
Lessons Learned Link to heading
1. Cache Aggressively for Immutable Data Link to heading
EVE item metadata doesn’t change. Class-level caching provided the single largest performance improvement.
2. Batch Network Operations Link to heading
Fetching 33,000 prices in one call beats 50 individual lookups every time, despite downloading “extra” data.
3. Parallel Processing Has Overhead Link to heading
Beyond 5-7 concurrent workers, we hit diminishing returns due to ESI rate limiting and connection overhead.
Future Enhancements Link to heading
Potential improvements:
- Route Planning: Integrate ESI’s route API for jump calculations
- Contract Parsing: Auto-import from in-game contracts via ESI authentication
Conclusion Link to heading
This EVE Online transport calculator demonstrates how thoughtful architectural decisions—persistent caching, parallel processing, and bulk data fetching—can transform a sluggish application into a responsive tool. By leveraging PyQt5’s threading model and ESI’s comprehensive API, we created a desktop application that handles real-world logistics challenges faced by EVE industrialists daily.
The complete source code is available, and the techniques demonstrated here apply broadly to any application interfacing with rate-limited APIs while maintaining responsive UIs.
- Dependencies: requests, pandas, openpyxl
- Performance: Processes 50 items in ~15 seconds
Links:

