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)
113 lines
3.2 KiB
Go
113 lines
3.2 KiB
Go
package graph
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"bugulma/backend/internal/domain"
|
|
|
|
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
|
|
)
|
|
|
|
// PathFinder handles path finding operations
|
|
type PathFinder struct {
|
|
sessionManager *SessionManager
|
|
queryTemplates *QueryTemplates
|
|
nodeConverter *NodeConverter
|
|
config *Config
|
|
}
|
|
|
|
// NewPathFinder creates a new path finder instance
|
|
func NewPathFinder(sessionManager *SessionManager, queryTemplates *QueryTemplates, nodeConverter *NodeConverter, config *Config) *PathFinder {
|
|
return &PathFinder{
|
|
sessionManager: sessionManager,
|
|
queryTemplates: queryTemplates,
|
|
nodeConverter: nodeConverter,
|
|
config: config,
|
|
}
|
|
}
|
|
|
|
// FindOptimalResourcePaths finds the most cost-effective paths for resource flows
|
|
func (pf *PathFinder) FindOptimalResourcePaths(
|
|
ctx context.Context,
|
|
sourceOrgID string,
|
|
targetOrgID string,
|
|
resourceType domain.ResourceType,
|
|
maxHops int,
|
|
) ([]*ResourceChain, error) {
|
|
|
|
if maxHops > pf.config.MaxPathHops {
|
|
maxHops = pf.config.MaxPathHops
|
|
}
|
|
|
|
result, err := pf.sessionManager.ExecuteRead(ctx, func(tx neo4j.ManagedTransaction) (interface{}, error) {
|
|
cypher := pf.queryTemplates.FindOptimalPathQuery(maxHops)
|
|
|
|
queryResult, err := tx.Run(ctx, cypher, map[string]interface{}{
|
|
"source_org": sourceOrgID,
|
|
"target_org": targetOrgID,
|
|
"resource_type": string(resourceType),
|
|
"max_distance": pf.config.MaxPathDistanceKm,
|
|
"limit": pf.config.DefaultResultLimit,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to execute optimal path query: %w", err)
|
|
}
|
|
|
|
var chains []*ResourceChain
|
|
for queryResult.Next(ctx) {
|
|
record := queryResult.Record()
|
|
|
|
pathNodes, _ := record.Get("pathNodes")
|
|
pathRels, _ := record.Get("pathRels")
|
|
totalCost, _ := record.Get("total_cost")
|
|
totalDistance, _ := record.Get("total_distance")
|
|
pathLength, _ := record.Get("path_length")
|
|
|
|
chain := &ResourceChain{
|
|
ChainID: fmt.Sprintf("optimal_%s_%s", sourceOrgID, targetOrgID),
|
|
ResourceType: resourceType,
|
|
TotalDistanceKm: getFloat64Value(totalDistance),
|
|
TotalCost: getFloat64Value(totalCost),
|
|
EnvironmentalImpact: getFloat64Value(totalCost) * 0.2,
|
|
Circular: false,
|
|
Steps: []ResourceChainStep{},
|
|
}
|
|
|
|
// Build steps from path
|
|
if nodesSlice, ok := pathNodes.([]interface{}); ok {
|
|
if relsSlice, ok := pathRels.([]interface{}); ok {
|
|
stepNum := 1
|
|
for i := 0; i < len(nodesSlice)-1 && i < len(relsSlice); i++ {
|
|
if node, ok := nodesSlice[i].(neo4j.Node); ok {
|
|
if len(node.Labels) > 0 && node.Labels[0] == "ResourceFlow" {
|
|
step := ResourceChainStep{
|
|
StepNumber: stepNum,
|
|
SourceFlowID: getStringProp(node.Props, "id"),
|
|
DistanceKm: getFloat64Value(totalDistance) / float64(getIntValue(pathLength)),
|
|
}
|
|
chain.Steps = append(chain.Steps, step)
|
|
stepNum++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
chains = append(chains, chain)
|
|
}
|
|
|
|
if err := queryResult.Err(); err != nil {
|
|
return nil, fmt.Errorf("failed to iterate path results: %w", err)
|
|
}
|
|
|
|
return chains, nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return result.([]*ResourceChain), nil
|
|
}
|