turash/models/calc.go
Damir Mukimov 4a2fda96cd
Initial commit: Repository setup with .gitignore, golangci-lint v2.6.0, and code quality checks
- Initialize git repository
- Add comprehensive .gitignore for Go projects
- Install golangci-lint v2.6.0 (latest v2) globally
- Configure .golangci.yml with appropriate linters and formatters
- Fix all formatting issues (gofmt)
- Fix all errcheck issues (unchecked errors)
- Adjust complexity threshold for validation functions
- All checks passing: build, test, vet, lint
2025-11-01 07:36:22 +01:00

151 lines
4.9 KiB
Go

package models
import (
"fmt"
"github.com/damirmukimov/city_resource_graph/models/cost"
"github.com/damirmukimov/city_resource_graph/models/customer"
"github.com/damirmukimov/city_resource_graph/models/impact"
"github.com/damirmukimov/city_resource_graph/models/params"
"github.com/damirmukimov/city_resource_graph/models/profitability"
"github.com/damirmukimov/city_resource_graph/models/revenue"
"github.com/damirmukimov/city_resource_graph/models/unit"
"github.com/damirmukimov/city_resource_graph/models/validator"
)
// YearResult contains all calculated metrics for a single year.
type YearResult struct {
Year int `json:"year"`
Customer customer.CustomerMetrics `json:"customer"`
TierDist customer.TierDistribution `json:"tier_distribution"`
Revenue revenue.RevenueBreakdown `json:"revenue"`
Costs cost.CostBreakdown `json:"costs"`
Impact impact.ImpactMetrics `json:"impact"`
UnitEconomics unit.UnitEconomics `json:"unit_economics"`
Validation validator.ValidationResult `json:"validation"`
Profit float64 `json:"profit"`
Margin float64 `json:"margin"` // Percentage
ARPU float64 `json:"arpu"` // Average revenue per user
}
// ModelResult contains results for all years.
type ModelResult struct {
Years []YearResult `json:"years"`
Summary Summary `json:"summary"`
Profitability profitability.ProfitabilityMetrics `json:"profitability"`
}
// Summary contains aggregated metrics across all years.
type Summary struct {
TotalRevenue float64 `json:"total_revenue"`
TotalCosts float64 `json:"total_costs"`
TotalProfit float64 `json:"total_profit"`
TotalCO2Avoided float64 `json:"total_co2_avoided"`
TotalWaterReused float64 `json:"total_water_reused"`
TotalWasteDiverted float64 `json:"total_waste_diverted"`
}
// Calculate runs the complete mathematical model for all years specified in params.
func Calculate(p *params.Params) (*ModelResult, error) {
if err := p.Validate(); err != nil {
return nil, fmt.Errorf("invalid params: %w", err)
}
var yearResults []YearResult
for _, year := range p.Time.Years {
result, err := CalculateYear(year, p)
if err != nil {
return nil, fmt.Errorf("year %d: %w", year, err)
}
yearResults = append(yearResults, *result)
}
summary := calculateSummary(yearResults)
// Calculate profitability metrics (IRR, NPV, payback period)
cashFlows := make([]float64, len(yearResults))
for i, year := range yearResults {
cashFlows[i] = year.Profit
}
profitabilityMetrics := profitability.CalculateProfitability(cashFlows, p.Profitability.DiscountRate)
return &ModelResult{
Years: yearResults,
Summary: summary,
Profitability: profitabilityMetrics,
}, nil
}
// CalculateYear computes all metrics for a single year.
func CalculateYear(year int, p *params.Params) (*YearResult, error) {
// 1. Customer metrics
custMetrics := customer.CalculateCustomerMetrics(year, p)
tierDist := customer.CalculateTierDistribution(year, custMetrics.PayingOrgs, p)
// 2. Revenue
revBreakdown := revenue.CalculateRevenue(year, custMetrics, tierDist, p)
// 3. Costs
costBreakdown := cost.CalculateCosts(year, p)
// 4. Impact
impactMetrics := impact.CalculateImpact(year, custMetrics, p)
// 5. Unit Economics
unitEconomics := unit.CalculateUnitEconomics(year, custMetrics, tierDist, revBreakdown, p)
// 6. Validation
validationResult := validator.Validate(year, custMetrics, revBreakdown, costBreakdown, impactMetrics)
// 7. Derived metrics
profit := revBreakdown.Total - costBreakdown.Total
var margin float64
if revBreakdown.Total > 0 {
margin = (profit / revBreakdown.Total) * 100
}
var arpu float64
if custMetrics.PayingOrgs > 0 {
arpu = revBreakdown.Total / float64(custMetrics.PayingOrgs)
}
return &YearResult{
Year: year,
Customer: custMetrics,
TierDist: tierDist,
Revenue: revBreakdown,
Costs: costBreakdown,
Impact: impactMetrics,
UnitEconomics: unitEconomics,
Validation: validationResult,
Profit: profit,
Margin: margin,
ARPU: arpu,
}, nil
}
// calculateSummary aggregates metrics across all years.
func calculateSummary(results []YearResult) Summary {
var totalRevenue, totalCosts, totalProfit float64
var totalCO2, totalWater, totalWaste float64
for _, result := range results {
totalRevenue += result.Revenue.Total
totalCosts += result.Costs.Total
totalProfit += result.Profit
totalCO2 += result.Impact.CO2Avoided
totalWater += result.Impact.WaterReused
totalWaste += result.Impact.WasteDiverted
}
return Summary{
TotalRevenue: totalRevenue,
TotalCosts: totalCosts,
TotalProfit: totalProfit,
TotalCO2Avoided: totalCO2,
TotalWaterReused: totalWater,
TotalWasteDiverted: totalWaste,
}
}