turash/models/cli/budget.go
Damir Mukimov cdca1ea1a7
Reorganize funding programs and create application documents
- Reorganized funding program structure: moved standalone .md files into organized directories with PROGRAM_INFO.md and APPLICATION_SCOPE.md
- Created budget files for selected priority programs:
  * EIC Accelerator: budget_breakdown.csv and budget_justification.md (€1.815M)
  * ZIM: budget_breakdown.csv and budget_justification.md (€465k)
  * DBU: budget_breakdown.csv and budget_justification.md (€152.5k)
  * LIFE Programme: budget_breakdown.csv and budget_justification.md (€1.815M)
- Created team CV files for all selected programs (CV_Damir_Mukimov.md)
- Added application documents structure:
  * funding/applications/budget/ - Master budget files and templates
  * funding/applications/team_cvs/ - Master CV and team expertise summary
- Updated FUNDING_GAP_ANALYSIS.md to reflect completed documents
- Added APPLICATION_REQUIREMENTS_REPORT.md with detailed requirements
- Added PREPARATION_PRIORITY.md for application preparation workflow
2025-11-01 09:59:48 +01:00

229 lines
8.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package cli
import (
"fmt"
"github.com/damirmukimov/city_resource_graph/models/cost"
"github.com/damirmukimov/city_resource_graph/models/params"
"github.com/spf13/cobra"
)
// NewBudgetCmd creates a budget calculation command for funding applications
func NewBudgetCmd() *cobra.Command {
var durationMonths int
var phase string
var outputFormat string
cmd := &cobra.Command{
Use: "budget [params.yaml]",
Short: "Calculate detailed budget breakdown for funding applications",
Long: `Calculate detailed budget breakdown by category (personnel, infrastructure, etc.)
for funding applications. Supports different phases (MVP Foundation, MVP+, Full Program).
Examples:
# Calculate full program budget (18 months)
go run ./cmd budget params.yaml --duration 18
# Calculate MVP+ budget (6 months)
go run ./cmd budget params.yaml --duration 6 --phase MVP+
# Output as CSV
go run ./cmd budget params.yaml --duration 6 --format csv > budget.csv`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
paramsFile := args[0]
return calculateBudget(paramsFile, durationMonths, phase, outputFormat)
},
}
cmd.Flags().IntVar(&durationMonths, "duration", 18, "Budget duration in months")
cmd.Flags().StringVar(&phase, "phase", "Full", "Budget phase: MVP-Foundation, MVP+, Full")
cmd.Flags().StringVar(&outputFormat, "format", "table", "Output format: table, csv, json")
return cmd
}
func calculateBudget(paramsFile string, durationMonths int, phase string, outputFormat string) error {
// Load parameters
p, err := params.LoadFromFile(paramsFile)
if err != nil {
return fmt.Errorf("failed to load params: %w", err)
}
// Use first year's parameters for calculation
year := 1
if year > len(p.Time.Years) {
year = len(p.Time.Years)
}
// Calculate costs using the cost package (convert months to year fraction)
monthlyMultiplier := float64(durationMonths) / 12.0
engineering := float64(p.Costs.Engineers.GetYear(year)) * p.Costs.EngineerSalary * monthlyMultiplier
infrastructure := p.Costs.Infrastructure.GetYear(year) * monthlyMultiplier
marketingSales := p.Costs.MarketingSales.GetYear(year) * monthlyMultiplier
operations := p.Costs.Operations.GetYear(year) * monthlyMultiplier
// Create base cost breakdown
costBreakdown := cost.CostBreakdown{
Engineering: engineering,
Infrastructure: infrastructure,
MarketingSales: marketingSales,
Operations: operations,
Total: engineering + infrastructure + marketingSales + operations,
}
// Apply phase-specific adjustments
adjustedBudget := adjustBudgetForPhase(costBreakdown, phase, durationMonths, p)
// Output based on format
switch outputFormat {
case "json":
return outputBudgetJSON(adjustedBudget, phase, durationMonths)
case "csv":
return outputBudgetCSV(adjustedBudget, phase, durationMonths)
default:
return outputBudgetTable(adjustedBudget, phase, durationMonths, p)
}
}
func adjustBudgetForPhase(base cost.CostBreakdown, phase string, months int, p *params.Params) BudgetBreakdown {
// Base adjustments for different phases
switch phase {
case "MVP-Foundation":
// MVP Foundation: Smaller team, reduced infrastructure
return BudgetBreakdown{
Personnel: base.Engineering * 0.35, // Smaller team
Infrastructure: base.Infrastructure * 0.15, // MVP-scale infrastructure
Subcontracting: 25000, // Legal, accounting
Travel: 10000, // Essential travel
Marketing: 25000, // Basic marketing
Other: 15000, // Tools, communication
Total: 0,
}
case "MVP+":
// MVP+: Medium team, expanded infrastructure
return BudgetBreakdown{
Personnel: base.Engineering * 0.69, // Medium team
Infrastructure: base.Infrastructure * 0.32, // MVP+ infrastructure
Subcontracting: 45000,
Travel: 25000,
Marketing: 66000,
Other: 21000,
Total: 0,
}
default: // "Full"
// Full Program: Complete team, full infrastructure
// Add detailed breakdown
personnelTotal := base.Engineering + base.MarketingSales*0.3 // Add BD/domain experts
return BudgetBreakdown{
Personnel: personnelTotal,
Infrastructure: base.Infrastructure,
Subcontracting: 50000,
Travel: 40000,
Marketing: 85000,
Other: 5000,
Total: 0,
}
}
}
type BudgetBreakdown struct {
Personnel float64 `json:"personnel"`
Infrastructure float64 `json:"infrastructure"`
Subcontracting float64 `json:"subcontracting"`
Travel float64 `json:"travel"`
Marketing float64 `json:"marketing"`
Other float64 `json:"other"`
Total float64 `json:"total"`
}
func outputBudgetTable(budget BudgetBreakdown, phase string, months int, p *params.Params) error {
budget.Total = budget.Personnel + budget.Infrastructure + budget.Subcontracting +
budget.Travel + budget.Marketing + budget.Other
fmt.Printf("\nBudget Breakdown for %s Phase (%d months)\n", phase, months)
fmt.Println(string(make([]byte, 60)))
fmt.Printf("\nCategory Breakdown:\n\n")
fmt.Printf(" Personnel: €%12.2f (%5.1f%%)\n",
budget.Personnel, (budget.Personnel/budget.Total)*100)
fmt.Printf(" Infrastructure: €%12.2f (%5.1f%%)\n",
budget.Infrastructure, (budget.Infrastructure/budget.Total)*100)
fmt.Printf(" Subcontracting: €%12.2f (%5.1f%%)\n",
budget.Subcontracting, (budget.Subcontracting/budget.Total)*100)
fmt.Printf(" Travel: €%12.2f (%5.1f%%)\n",
budget.Travel, (budget.Travel/budget.Total)*100)
fmt.Printf(" Marketing: €%12.2f (%5.1f%%)\n",
budget.Marketing, (budget.Marketing/budget.Total)*100)
fmt.Printf(" Other: €%12.2f (%5.1f%%)\n",
budget.Other, (budget.Other/budget.Total)*100)
fmt.Printf("\n TOTAL: €%12.2f (100.0%%)\n", budget.Total)
fmt.Printf("\nModel Validation (from params.yaml):\n")
fmt.Printf(" Engineers (Year 1): %d × €%.0f = €%.0f/year\n",
p.Costs.Engineers.GetYear(1), p.Costs.EngineerSalary,
float64(p.Costs.Engineers.GetYear(1))*p.Costs.EngineerSalary)
fmt.Printf(" Adjusted for %d months: €%.0f\n",
months, float64(p.Costs.Engineers.GetYear(1))*p.Costs.EngineerSalary*float64(months)/12.0)
return nil
}
func outputBudgetCSV(budget BudgetBreakdown, phase string, months int) error {
budget.Total = budget.Personnel + budget.Infrastructure + budget.Subcontracting +
budget.Travel + budget.Marketing + budget.Other
fmt.Println("Category,Amount (EUR),Percentage")
fmt.Printf("Personnel,%.2f,%.2f%%\n", budget.Personnel, (budget.Personnel/budget.Total)*100)
fmt.Printf("Infrastructure,%.2f,%.2f%%\n", budget.Infrastructure, (budget.Infrastructure/budget.Total)*100)
fmt.Printf("Subcontracting,%.2f,%.2f%%\n", budget.Subcontracting, (budget.Subcontracting/budget.Total)*100)
fmt.Printf("Travel,%.2f,%.2f%%\n", budget.Travel, (budget.Travel/budget.Total)*100)
fmt.Printf("Marketing,%.2f,%.2f%%\n", budget.Marketing, (budget.Marketing/budget.Total)*100)
fmt.Printf("Other,%.2f,%.2f%%\n", budget.Other, (budget.Other/budget.Total)*100)
fmt.Printf("TOTAL,%.2f,100.00%%\n", budget.Total)
return nil
}
func outputBudgetJSON(budget BudgetBreakdown, phase string, months int) error {
budget.Total = budget.Personnel + budget.Infrastructure + budget.Subcontracting +
budget.Travel + budget.Marketing + budget.Other
jsonOutput := fmt.Sprintf(`{
"phase": "%s",
"duration_months": %d,
"budget": {
"personnel": %.2f,
"infrastructure": %.2f,
"subcontracting": %.2f,
"travel": %.2f,
"marketing": %.2f,
"other": %.2f,
"total": %.2f
},
"percentages": {
"personnel": %.2f,
"infrastructure": %.2f,
"subcontracting": %.2f,
"travel": %.2f,
"marketing": %.2f,
"other": %.2f
}
}
`,
phase, months,
budget.Personnel, budget.Infrastructure, budget.Subcontracting,
budget.Travel, budget.Marketing, budget.Other, budget.Total,
(budget.Personnel/budget.Total)*100,
(budget.Infrastructure/budget.Total)*100,
(budget.Subcontracting/budget.Total)*100,
(budget.Travel/budget.Total)*100,
(budget.Marketing/budget.Total)*100,
(budget.Other/budget.Total)*100,
)
fmt.Print(jsonOutput)
return nil
}