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)
270 lines
8.6 KiB
Go
270 lines
8.6 KiB
Go
package handler
|
|
|
|
import (
|
|
"bugulma/backend/internal/domain"
|
|
"bugulma/backend/internal/service"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// GraphTraversalHandler handles advanced graph traversal operations
|
|
type GraphTraversalHandler struct {
|
|
traversalService *service.GraphTraversalService
|
|
}
|
|
|
|
// NewGraphTraversalHandler creates a new graph traversal handler
|
|
func NewGraphTraversalHandler(traversalService *service.GraphTraversalService) *GraphTraversalHandler {
|
|
return &GraphTraversalHandler{
|
|
traversalService: traversalService,
|
|
}
|
|
}
|
|
|
|
// FindResourceChains finds chains of resource flows from waste to reuse
|
|
// @Summary Find resource chains
|
|
// @Tags graph-traversal
|
|
// @Produce json
|
|
// @Param resourceType query string true "Resource type (e.g., biowaste, heat, water)"
|
|
// @Param maxLength query int false "Maximum chain length" default(5)
|
|
// @Param minValue query number false "Minimum economic value" default(1000)
|
|
// @Success 200 {object} map[string]interface{}
|
|
// @Router /api/graph/resource-chains [get]
|
|
func (h *GraphTraversalHandler) FindResourceChains(c *gin.Context) {
|
|
resourceTypeStr := c.Query("resourceType")
|
|
if resourceTypeStr == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "resourceType parameter is required"})
|
|
return
|
|
}
|
|
|
|
resourceType := domain.ResourceType(resourceTypeStr)
|
|
maxLengthStr := c.DefaultQuery("maxLength", "5")
|
|
minValueStr := c.DefaultQuery("minValue", "1000")
|
|
|
|
maxLength, err := strconv.Atoi(maxLengthStr)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid maxLength parameter"})
|
|
return
|
|
}
|
|
|
|
minValue, err := strconv.ParseFloat(minValueStr, 64)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid minValue parameter"})
|
|
return
|
|
}
|
|
|
|
chains, err := h.traversalService.FindResourceChains(c.Request.Context(), resourceType, maxLength, minValue)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"resource_chains": chains,
|
|
"total_chains": len(chains),
|
|
"resource_type": resourceType,
|
|
"criteria": gin.H{
|
|
"max_length": maxLength,
|
|
"min_value": minValue,
|
|
},
|
|
})
|
|
}
|
|
|
|
// FindSymbiosisNetworks identifies interconnected industrial symbiosis networks
|
|
// @Summary Find symbiosis networks
|
|
// @Tags graph-traversal
|
|
// @Produce json
|
|
// @Param minOrganizations query int false "Minimum organizations per network" default(3)
|
|
// @Param maxNetworkSize query int false "Maximum network size" default(20)
|
|
// @Success 200 {object} map[string]interface{}
|
|
// @Router /api/graph/symbiosis-networks [get]
|
|
func (h *GraphTraversalHandler) FindSymbiosisNetworks(c *gin.Context) {
|
|
minOrgsStr := c.DefaultQuery("minOrganizations", "3")
|
|
maxSizeStr := c.DefaultQuery("maxNetworkSize", "20")
|
|
|
|
minOrgs, err := strconv.Atoi(minOrgsStr)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid minOrganizations parameter"})
|
|
return
|
|
}
|
|
|
|
maxSize, err := strconv.Atoi(maxSizeStr)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid maxNetworkSize parameter"})
|
|
return
|
|
}
|
|
|
|
networks, err := h.traversalService.FindSymbiosisNetworks(c.Request.Context(), minOrgs, maxSize)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"symbiosis_networks": networks,
|
|
"total_networks": len(networks),
|
|
"criteria": gin.H{
|
|
"min_organizations": minOrgs,
|
|
"max_network_size": maxSize,
|
|
},
|
|
})
|
|
}
|
|
|
|
// FindOptimalResourcePaths finds the most cost-effective paths for resource flows
|
|
// @Summary Find optimal resource paths
|
|
// @Tags graph-traversal
|
|
// @Produce json
|
|
// @Param sourceOrgId query string true "Source organization ID"
|
|
// @Param targetOrgId query string true "Target organization ID"
|
|
// @Param resourceType query string true "Resource type"
|
|
// @Param maxHops query int false "Maximum hops in path" default(4)
|
|
// @Success 200 {object} map[string]interface{}
|
|
// @Router /api/graph/optimal-paths [get]
|
|
func (h *GraphTraversalHandler) FindOptimalResourcePaths(c *gin.Context) {
|
|
sourceOrgID := c.Query("sourceOrgId")
|
|
targetOrgID := c.Query("targetOrgId")
|
|
resourceTypeStr := c.Query("resourceType")
|
|
maxHopsStr := c.DefaultQuery("maxHops", "4")
|
|
|
|
if sourceOrgID == "" || targetOrgID == "" || resourceTypeStr == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "sourceOrgId, targetOrgId, and resourceType parameters are required"})
|
|
return
|
|
}
|
|
|
|
resourceType := domain.ResourceType(resourceTypeStr)
|
|
maxHops, err := strconv.Atoi(maxHopsStr)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid maxHops parameter"})
|
|
return
|
|
}
|
|
|
|
paths, err := h.traversalService.FindOptimalResourcePaths(c.Request.Context(), sourceOrgID, targetOrgID, resourceType, maxHops)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"optimal_paths": paths,
|
|
"total_paths": len(paths),
|
|
"route": gin.H{
|
|
"source_org": sourceOrgID,
|
|
"target_org": targetOrgID,
|
|
"resource_type": resourceType,
|
|
"max_hops": maxHops,
|
|
},
|
|
})
|
|
}
|
|
|
|
// AnalyzeNetworkCentrality calculates centrality measures for organizations
|
|
// @Summary Analyze network centrality
|
|
// @Tags graph-traversal
|
|
// @Produce json
|
|
// @Success 200 {object} map[string]interface{}
|
|
// @Router /api/graph/centrality [get]
|
|
func (h *GraphTraversalHandler) AnalyzeNetworkCentrality(c *gin.Context) {
|
|
centrality, err := h.traversalService.AnalyzeNetworkCentrality(c.Request.Context())
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// Convert to response format
|
|
var sortedOrgs []map[string]interface{}
|
|
for orgID, metrics := range centrality {
|
|
sortedOrgs = append(sortedOrgs, map[string]interface{}{
|
|
"org_id": orgID,
|
|
"degree": metrics.Degree,
|
|
"betweenness": metrics.Betweenness,
|
|
"closeness": metrics.Closeness,
|
|
"out_flows": metrics.OutFlows,
|
|
"in_flows": metrics.InFlows,
|
|
})
|
|
}
|
|
|
|
// Sort by degree centrality (descending)
|
|
for i := 0; i < len(sortedOrgs)-1; i++ {
|
|
for j := i + 1; j < len(sortedOrgs); j++ {
|
|
if sortedOrgs[i]["degree"].(float64) < sortedOrgs[j]["degree"].(float64) {
|
|
sortedOrgs[i], sortedOrgs[j] = sortedOrgs[j], sortedOrgs[i]
|
|
}
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"centrality_analysis": gin.H{
|
|
"organizations": sortedOrgs,
|
|
"total_analyzed": len(sortedOrgs),
|
|
"metrics": []string{"degree", "betweenness", "closeness"},
|
|
},
|
|
"interpretation": gin.H{
|
|
"degree_centrality": "Number of direct connections (local importance)",
|
|
"betweenness_centrality": "Control over resource flows (network influence)",
|
|
"closeness_centrality": "Average distance to other organizations (accessibility)",
|
|
},
|
|
})
|
|
}
|
|
|
|
// GetNetworkStatistics provides overall network statistics
|
|
// @Summary Get network statistics
|
|
// @Tags graph-traversal
|
|
// @Produce json
|
|
// @Success 200 {object} map[string]interface{}
|
|
// @Router /api/graph/statistics [get]
|
|
func (h *GraphTraversalHandler) GetNetworkStatistics(c *gin.Context) {
|
|
stats, err := h.traversalService.GetNetworkStatistics(c.Request.Context())
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"network_statistics": gin.H{
|
|
"total_organizations": stats.TotalOrganizations,
|
|
"total_resource_flows": stats.TotalResourceFlows,
|
|
"total_matches": stats.TotalMatches,
|
|
"total_economic_value": stats.TotalEconomicValue,
|
|
"avg_distance_km": stats.AverageDistance,
|
|
"high_value_matches": stats.HighValueMatches,
|
|
"geographic_span_km": stats.GeographicSpanKm,
|
|
},
|
|
"generated_at": time.Now().Format(time.RFC3339),
|
|
})
|
|
}
|
|
|
|
// GetCircularEconomyAnalysis analyzes circular economy potential
|
|
// @Summary Analyze circular economy potential
|
|
// @Tags graph-traversal
|
|
// @Produce json
|
|
// @Success 200 {object} map[string]interface{}
|
|
// @Router /api/graph/circular-economy [get]
|
|
func (h *GraphTraversalHandler) GetCircularEconomyAnalysis(c *gin.Context) {
|
|
cycles, err := h.traversalService.FindCircularEconomyCycles(c.Request.Context())
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
totalCircularValue := 0.0
|
|
for _, cycle := range cycles {
|
|
totalCircularValue += cycle.CycleValue
|
|
}
|
|
|
|
avgCycleValue := 0.0
|
|
if len(cycles) > 0 {
|
|
avgCycleValue = totalCircularValue / float64(len(cycles))
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"circular_economy_analysis": gin.H{
|
|
"circular_cycles": cycles,
|
|
"total_cycles": len(cycles),
|
|
"total_circular_value": totalCircularValue,
|
|
"avg_cycle_value": avgCycleValue,
|
|
"circular_efficiency": totalCircularValue / 100000.0, // Simplified metric
|
|
},
|
|
"analysis_timestamp": time.Now().Format(time.RFC3339),
|
|
})
|
|
}
|