mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
Repository Structure:
- Move files from cluttered root directory into organized structure
- Create archive/ for archived data and scraper results
- Create bugulma/ for the complete application (frontend + backend)
- Create data/ for sample datasets and reference materials
- Create docs/ for comprehensive documentation structure
- Create scripts/ for utility scripts and API tools
Backend Implementation:
- Implement 3 missing backend endpoints identified in gap analysis:
* GET /api/v1/organizations/{id}/matching/direct - Direct symbiosis matches
* GET /api/v1/users/me/organizations - User organizations
* POST /api/v1/proposals/{id}/status - Update proposal status
- Add complete proposal domain model, repository, and service layers
- Create database migration for proposals table
- Fix CLI server command registration issue
API Documentation:
- Add comprehensive proposals.md API documentation
- Update README.md with Users and Proposals API sections
- Document all request/response formats, error codes, and business rules
Code Quality:
- Follow existing Go backend architecture patterns
- Add proper error handling and validation
- Match frontend expected response schemas
- Maintain clean separation of concerns (handler -> service -> repository)
218 lines
6.2 KiB
Go
218 lines
6.2 KiB
Go
package plugins
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"bugulma/backend/internal/domain"
|
|
"bugulma/backend/internal/matching/engine"
|
|
)
|
|
|
|
// Manager handles plugin-based matching logic integration
|
|
type Manager struct {
|
|
registry PluginRegistry
|
|
engine *engine.Engine
|
|
}
|
|
|
|
// NewManager creates a new plugin manager
|
|
func NewManager(registry PluginRegistry, engine *engine.Engine) *Manager {
|
|
return &Manager{
|
|
registry: registry,
|
|
engine: engine,
|
|
}
|
|
}
|
|
|
|
// EnhanceCandidate uses plugins to enhance candidate evaluation
|
|
func (pm *Manager) EnhanceCandidate(candidate *engine.Candidate) error {
|
|
sourceFlow := candidate.SourceFlow
|
|
targetFlow := candidate.TargetFlow
|
|
|
|
// Get plugins for this resource type
|
|
plugins := pm.registry.GetByResourceType(sourceFlow.Type)
|
|
|
|
if len(plugins) == 0 {
|
|
// No plugins available, use default engine logic
|
|
return nil
|
|
}
|
|
|
|
// Use the first available plugin (in production, you might want plugin selection logic)
|
|
plugin := plugins[0]
|
|
|
|
// Calculate distance (simplified - should use geospatial calculator)
|
|
distance := candidate.DistanceKm
|
|
if distance == 0 {
|
|
distance = 10.0 // Placeholder distance
|
|
}
|
|
|
|
// Apply plugin enhancements
|
|
if plugin.SupportsQualityCheck() {
|
|
if qualityScore, err := plugin.CheckQualityCompatibility(sourceFlow, targetFlow); err != nil {
|
|
return fmt.Errorf("quality check failed: %w", err)
|
|
} else {
|
|
// Blend plugin score with existing score
|
|
candidate.QualityScore = (candidate.QualityScore + qualityScore) / 2
|
|
}
|
|
}
|
|
|
|
if plugin.SupportsEconomicCalculation() {
|
|
if economicValue, err := plugin.CalculateEconomicImpact(sourceFlow, targetFlow, distance); err != nil {
|
|
// Log warning but don't fail - use existing calculation
|
|
fmt.Printf("Warning: Plugin economic calculation failed: %v\n", err)
|
|
} else {
|
|
// Use plugin's economic calculation if available
|
|
candidate.EstimatedAnnualSavings = economicValue
|
|
}
|
|
}
|
|
|
|
if plugin.SupportsTemporalCheck() {
|
|
if temporalScore, err := plugin.CheckTemporalCompatibility(sourceFlow, targetFlow); err != nil {
|
|
return fmt.Errorf("temporal check failed: %w", err)
|
|
} else {
|
|
// Blend plugin score with existing score
|
|
candidate.TemporalScore = (candidate.TemporalScore + temporalScore) / 2
|
|
}
|
|
}
|
|
|
|
// Recalculate overall score with enhanced components
|
|
scoring := &engine.ScoringResult{
|
|
Compatibility: candidate.CompatibilityScore,
|
|
Economic: pm.normalizeEconomicScore(candidate.EstimatedAnnualSavings),
|
|
Temporal: candidate.TemporalScore,
|
|
Quality: candidate.QualityScore,
|
|
}
|
|
|
|
// Simple weighted calculation (should match engine logic)
|
|
weights := map[string]float64{
|
|
"compatibility": 0.3,
|
|
"economic": 0.25,
|
|
"temporal": 0.25,
|
|
"quality": 0.2,
|
|
}
|
|
|
|
candidate.OverallScore = scoring.Compatibility*weights["compatibility"] +
|
|
scoring.Economic*weights["economic"] +
|
|
scoring.Temporal*weights["temporal"] +
|
|
scoring.Quality*weights["quality"]
|
|
|
|
candidate.OverallScore = max(0, min(1, candidate.OverallScore))
|
|
|
|
return nil
|
|
}
|
|
|
|
// ValidateWithPlugins validates a candidate using plugin validation logic
|
|
func (pm *Manager) ValidateWithPlugins(candidate *engine.Candidate) (bool, string) {
|
|
plugins := pm.registry.GetByResourceType(candidate.SourceFlow.Type)
|
|
|
|
if len(plugins) == 0 {
|
|
// No plugins available, use default validation
|
|
return pm.defaultValidation(candidate)
|
|
}
|
|
|
|
plugin := plugins[0]
|
|
|
|
// Use plugin validation
|
|
valid, reason := plugin.ValidateCandidate(candidate)
|
|
if !valid {
|
|
return false, fmt.Sprintf("Plugin validation failed: %s", reason)
|
|
}
|
|
|
|
// Also run default validation
|
|
valid, defaultReason := pm.defaultValidation(candidate)
|
|
if !valid {
|
|
return false, fmt.Sprintf("Default validation failed: %s", defaultReason)
|
|
}
|
|
|
|
return true, "All validations passed"
|
|
}
|
|
|
|
// GetEnhancedMatchingCriteria returns enhanced criteria based on plugins
|
|
func (pm *Manager) GetEnhancedMatchingCriteria(resourceType domain.ResourceType) *engine.Criteria {
|
|
baseCriteria := &engine.Criteria{
|
|
ResourceType: resourceType,
|
|
MaxDistanceKm: 50.0,
|
|
MinCompatibility: 0.5,
|
|
MinEconomicScore: 0.3,
|
|
MinTemporalScore: 0.4,
|
|
MinQualityScore: 0.4,
|
|
ExcludeSameOrg: true,
|
|
Limit: 50,
|
|
}
|
|
|
|
// Enhance criteria based on resource type
|
|
switch resourceType {
|
|
case domain.TypeHeat:
|
|
baseCriteria.MaxDistanceKm = 30.0 // Heat has shorter transport distance
|
|
baseCriteria.MinEconomicScore = 0.4 // Higher economic threshold for heat
|
|
case domain.TypeBiowaste:
|
|
baseCriteria.MaxDistanceKm = 100.0 // Biowaste can travel farther
|
|
baseCriteria.MinQualityScore = 0.6 // Higher quality threshold for biowaste
|
|
case domain.TypeWater:
|
|
baseCriteria.MaxDistanceKm = 25.0 // Water transport is expensive
|
|
baseCriteria.MinCompatibility = 0.6 // Water requires better compatibility
|
|
}
|
|
|
|
return baseCriteria
|
|
}
|
|
|
|
// normalizeEconomicScore converts economic value to a 0-1 score
|
|
func (pm *Manager) normalizeEconomicScore(annualSavings float64) float64 {
|
|
if annualSavings <= 0 {
|
|
return 0
|
|
}
|
|
|
|
// Simple normalization: cap at €100,000 annual savings = score of 1.0
|
|
maxSavings := 100000.0
|
|
score := min(annualSavings/maxSavings, 1.0)
|
|
|
|
return score
|
|
}
|
|
|
|
// defaultValidation provides basic validation when no plugins are available
|
|
func (pm *Manager) defaultValidation(candidate *engine.Candidate) (bool, string) {
|
|
// Basic validation rules
|
|
if candidate.OverallScore < 0.3 {
|
|
return false, "Overall score below minimum threshold"
|
|
}
|
|
|
|
if candidate.DistanceKm > 100.0 {
|
|
return false, "Distance exceeds maximum transport limit"
|
|
}
|
|
|
|
if candidate.EstimatedAnnualSavings < 1000.0 {
|
|
return false, "Annual savings below minimum threshold"
|
|
}
|
|
|
|
return true, "Default validation passed"
|
|
}
|
|
|
|
// RegisterDefaultPlugins registers the built-in plugins
|
|
func RegisterDefaultPlugins(registry PluginRegistry) error {
|
|
// Register heat plugin
|
|
heatPlugin := NewHeatPlugin(nil)
|
|
if err := registry.Register(heatPlugin); err != nil {
|
|
return fmt.Errorf("failed to register heat plugin: %w", err)
|
|
}
|
|
|
|
// Register biowaste plugin
|
|
biowastePlugin := NewBiowastePlugin(nil)
|
|
if err := registry.Register(biowastePlugin); err != nil {
|
|
return fmt.Errorf("failed to register biowaste plugin: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Helper functions
|
|
func min(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
func max(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|