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)
371 lines
10 KiB
Go
371 lines
10 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// EUFundingAPI represents the EU Funding & Tenders Portal API client
|
|
type EUFundingAPI struct {
|
|
BaseURL string
|
|
SearchAPIKey string
|
|
FAQAPIKey string
|
|
PersonAPIKey string
|
|
Client *http.Client
|
|
}
|
|
|
|
// NewEUFundingAPI creates a new API client instance
|
|
func NewEUFundingAPI() *EUFundingAPI {
|
|
return &EUFundingAPI{
|
|
BaseURL: "https://api.tech.ec.europa.eu/search-api/prod/rest",
|
|
SearchAPIKey: "SEDIA",
|
|
FAQAPIKey: "SEDIA_FAQ",
|
|
PersonAPIKey: "SEDIA_PERSON",
|
|
Client: &http.Client{
|
|
Timeout: 30 * time.Second,
|
|
},
|
|
}
|
|
}
|
|
|
|
// SearchQuery represents the Elasticsearch query structure
|
|
type SearchQuery struct {
|
|
Bool struct {
|
|
Must []interface{} `json:"must"`
|
|
} `json:"bool"`
|
|
}
|
|
|
|
// APIResponse represents the standard API response
|
|
type APIResponse struct {
|
|
Results []map[string]interface{} `json:"results"`
|
|
Total int `json:"total"`
|
|
}
|
|
|
|
// makeRequest performs HTTP requests to the API
|
|
func (api *EUFundingAPI) makeRequest(endpoint string, method string, data interface{}) (*APIResponse, error) {
|
|
var body io.Reader
|
|
if data != nil && method == "POST" {
|
|
jsonData, err := json.Marshal(data)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal request data: %v", err)
|
|
}
|
|
body = bytes.NewBuffer(jsonData)
|
|
}
|
|
|
|
req, err := http.NewRequest(method, endpoint, body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create request: %v", err)
|
|
}
|
|
|
|
req.Header.Set("User-Agent", "EU-Funding-API-Client-Go/1.0")
|
|
req.Header.Set("Accept", "application/json")
|
|
if body != nil {
|
|
req.Header.Set("Content-Type", "application/json")
|
|
}
|
|
|
|
resp, err := api.Client.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("request failed: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("API returned status %d", resp.StatusCode)
|
|
}
|
|
|
|
var response APIResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
|
|
return nil, fmt.Errorf("failed to decode response: %v", err)
|
|
}
|
|
|
|
return &response, nil
|
|
}
|
|
|
|
// SearchGrantsTenders searches for grants and tenders
|
|
func (api *EUFundingAPI) SearchGrantsTenders(query *SearchQuery) (*APIResponse, error) {
|
|
endpoint := fmt.Sprintf("%s/search?apiKey=%s&text=***", api.BaseURL, api.SearchAPIKey)
|
|
|
|
if query == nil {
|
|
// Default query to get all grants and tenders
|
|
query = &SearchQuery{}
|
|
query.Bool.Must = []interface{}{
|
|
map[string]interface{}{
|
|
"terms": map[string][]string{
|
|
"type": {"0", "1", "2", "8"}, // All types
|
|
},
|
|
},
|
|
map[string]interface{}{
|
|
"terms": map[string][]string{
|
|
"status": {"31094501", "31094502", "31094503"}, // All statuses
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
return api.makeRequest(endpoint, "POST", query)
|
|
}
|
|
|
|
// GetTopicDetails gets detailed information about a specific topic
|
|
func (api *EUFundingAPI) GetTopicDetails(topicIdentifier string) (*APIResponse, error) {
|
|
endpoint := fmt.Sprintf("%s/search?apiKey=%s&text=\"%s\"", api.BaseURL, api.SearchAPIKey, topicIdentifier)
|
|
return api.makeRequest(endpoint, "GET", nil)
|
|
}
|
|
|
|
// SearchGrantUpdates searches for grant updates
|
|
func (api *EUFundingAPI) SearchGrantUpdates(frameworkProgramme string) (*APIResponse, error) {
|
|
query := &SearchQuery{}
|
|
query.Bool.Must = []interface{}{
|
|
map[string]interface{}{
|
|
"terms": map[string][]string{
|
|
"type": {"6"}, // Grant updates
|
|
},
|
|
},
|
|
}
|
|
|
|
if frameworkProgramme != "" {
|
|
query.Bool.Must = append(query.Bool.Must, map[string]interface{}{
|
|
"terms": map[string][]string{
|
|
"frameworkProgramme": {frameworkProgramme},
|
|
},
|
|
})
|
|
}
|
|
|
|
endpoint := fmt.Sprintf("%s/search?apiKey=%s&text=***", api.BaseURL, api.SearchAPIKey)
|
|
return api.makeRequest(endpoint, "POST", query)
|
|
}
|
|
|
|
// SearchFAQs searches FAQs
|
|
func (api *EUFundingAPI) SearchFAQs(programme string) (*APIResponse, error) {
|
|
query := &SearchQuery{}
|
|
query.Bool.Must = []interface{}{
|
|
map[string]interface{}{
|
|
"terms": map[string][]string{
|
|
"type": {"0", "1"}, // FAQ types
|
|
},
|
|
},
|
|
map[string]interface{}{
|
|
"terms": map[string][]string{
|
|
"status": {"0", "1"}, // All statuses
|
|
},
|
|
},
|
|
}
|
|
|
|
if programme != "" {
|
|
query.Bool.Must = append(query.Bool.Must, map[string]interface{}{
|
|
"term": map[string]string{
|
|
"programme": programme,
|
|
},
|
|
})
|
|
}
|
|
|
|
endpoint := fmt.Sprintf("%s/search?apiKey=%s&text=***", api.BaseURL, api.FAQAPIKey)
|
|
return api.makeRequest(endpoint, "POST", query)
|
|
}
|
|
|
|
// GetOrganizationData gets organization public data
|
|
func (api *EUFundingAPI) GetOrganizationData(picCode string) (*APIResponse, error) {
|
|
endpoint := fmt.Sprintf("%s/document/%s?apiKey=%s", api.BaseURL, picCode, api.PersonAPIKey)
|
|
return api.makeRequest(endpoint, "GET", nil)
|
|
}
|
|
|
|
// SearchPartners searches for partners in a specific topic
|
|
func (api *EUFundingAPI) SearchPartners(topic string) (*APIResponse, error) {
|
|
query := &SearchQuery{}
|
|
query.Bool.Must = []interface{}{
|
|
map[string]interface{}{
|
|
"terms": map[string][]string{
|
|
"topics": {topic},
|
|
},
|
|
},
|
|
map[string]interface{}{
|
|
"terms": map[string][]string{
|
|
"type": {"ORGANISATION", "PERSON"},
|
|
},
|
|
},
|
|
}
|
|
|
|
endpoint := fmt.Sprintf("%s/search?apiKey=%s&text=***", api.BaseURL, api.SearchAPIKey)
|
|
return api.makeRequest(endpoint, "POST", query)
|
|
}
|
|
|
|
// SearchProjects searches for EU funded projects
|
|
func (api *EUFundingAPI) SearchProjects(programmeID, missionGroup string) (*APIResponse, error) {
|
|
query := &SearchQuery{}
|
|
|
|
if programmeID != "" {
|
|
query.Bool.Must = append(query.Bool.Must, map[string]interface{}{
|
|
"terms": map[string][]string{
|
|
"programId": {programmeID},
|
|
},
|
|
})
|
|
}
|
|
|
|
if missionGroup != "" {
|
|
query.Bool.Must = append(query.Bool.Must, map[string]interface{}{
|
|
"terms": map[string][]string{
|
|
"missionGroup": {missionGroup},
|
|
},
|
|
})
|
|
}
|
|
|
|
endpoint := fmt.Sprintf("%s/search?apiKey=%s&text=***", api.BaseURL, api.SearchAPIKey)
|
|
return api.makeRequest(endpoint, "POST", query)
|
|
}
|
|
|
|
func main() {
|
|
fmt.Println("EU Funding & Tenders Portal API Client (Go)")
|
|
fmt.Println("=" + strings.Repeat("=", 50))
|
|
|
|
api := NewEUFundingAPI()
|
|
|
|
// Example 1: Search for EIC Accelerator opportunities
|
|
fmt.Println("\n1. Searching for EIC Accelerator opportunities...")
|
|
|
|
eicQuery := &SearchQuery{}
|
|
eicQuery.Bool.Must = []interface{}{
|
|
map[string]interface{}{
|
|
"terms": map[string][]string{
|
|
"type": {"1", "2", "8"}, // Grants
|
|
},
|
|
},
|
|
map[string]interface{}{
|
|
"terms": map[string][]string{
|
|
"status": {"31094501", "31094502", "31094503"}, // All statuses
|
|
},
|
|
},
|
|
map[string]interface{}{
|
|
"term": map[string]string{
|
|
"callIdentifier": "HORIZON-EIC-2026-ACCELERATOR-01",
|
|
},
|
|
},
|
|
}
|
|
|
|
results, err := api.SearchGrantsTenders(eicQuery)
|
|
if err != nil {
|
|
log.Printf("Error searching grants: %v", err)
|
|
} else {
|
|
fmt.Printf("Found %d EIC Accelerator opportunities\n", len(results.Results))
|
|
for i, result := range results.Results {
|
|
if i >= 3 { // Show first 3
|
|
break
|
|
}
|
|
if title, ok := result["title"].(string); ok {
|
|
fmt.Printf("- %s\n", title)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Example 2: Get topic details for EIC Accelerator
|
|
fmt.Println("\n2. Getting EIC Accelerator topic details...")
|
|
topicDetails, err := api.GetTopicDetails("HORIZON-EIC-2026-ACCELERATOR-01")
|
|
if err != nil {
|
|
log.Printf("Error getting topic details: %v", err)
|
|
} else {
|
|
fmt.Println("Topic details retrieved successfully")
|
|
if len(topicDetails.Results) > 0 {
|
|
topic := topicDetails.Results[0]
|
|
if title, ok := topic["title"].(string); ok {
|
|
fmt.Printf("Title: %s\n", title)
|
|
}
|
|
if status, ok := topic["status"].(string); ok {
|
|
fmt.Printf("Status: %s\n", status)
|
|
}
|
|
if deadline, ok := topic["deadline"].(string); ok {
|
|
fmt.Printf("Deadline: %s\n", deadline)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Example 3: Search for grant updates
|
|
fmt.Println("\n3. Searching for recent grant updates...")
|
|
updates, err := api.SearchGrantUpdates("43108390") // Horizon Europe
|
|
if err != nil {
|
|
log.Printf("Error searching updates: %v", err)
|
|
} else {
|
|
fmt.Printf("Found %d grant updates\n", len(updates.Results))
|
|
}
|
|
|
|
// Example 4: Search FAQs
|
|
fmt.Println("\n4. Searching for FAQs...")
|
|
faqs, err := api.SearchFAQs("")
|
|
if err != nil {
|
|
log.Printf("Error searching FAQs: %v", err)
|
|
} else {
|
|
fmt.Printf("Found %d FAQs\n", len(faqs.Results))
|
|
}
|
|
|
|
fmt.Println("\nGo API client demonstration completed!")
|
|
}
|
|
|
|
// EICAcceleratorMonitor monitors EIC Accelerator opportunities
|
|
func EICAcceleratorMonitor() {
|
|
fmt.Println("EIC Accelerator Monitor")
|
|
fmt.Println("=" + strings.Repeat("=", 30))
|
|
|
|
api := NewEUFundingAPI()
|
|
|
|
// Query for EIC Accelerator opportunities
|
|
query := &SearchQuery{}
|
|
query.Bool.Must = []interface{}{
|
|
map[string]interface{}{
|
|
"terms": map[string][]string{
|
|
"type": {"1", "2", "8"},
|
|
},
|
|
},
|
|
map[string]interface{}{
|
|
"terms": map[string][]string{
|
|
"status": {"31094501", "31094502"}, // Open and forthcoming
|
|
},
|
|
},
|
|
map[string]interface{}{
|
|
"term": map[string]string{
|
|
"frameworkProgramme": "43108390", // Horizon Europe
|
|
},
|
|
},
|
|
map[string]interface{}{
|
|
"query_string": map[string]interface{}{
|
|
"query": "EIC Accelerator",
|
|
"default_field": "title",
|
|
},
|
|
},
|
|
}
|
|
|
|
results, err := api.SearchGrantsTenders(query)
|
|
if err != nil {
|
|
log.Printf("Error monitoring EIC Accelerator: %v", err)
|
|
return
|
|
}
|
|
|
|
if results != nil && len(results.Results) > 0 {
|
|
fmt.Printf("Found %d EIC Accelerator opportunities:\n", len(results.Results))
|
|
|
|
for _, opp := range results.Results {
|
|
title := getStringValue(opp, "title")
|
|
identifier := getStringValue(opp, "identifier")
|
|
status := getStringValue(opp, "status")
|
|
deadline := getStringValue(opp, "deadline")
|
|
|
|
fmt.Printf("\n📋 %s\n", title)
|
|
fmt.Printf(" ID: %s\n", identifier)
|
|
fmt.Printf(" Status: %s\n", status)
|
|
fmt.Printf(" Deadline: %s\n", deadline)
|
|
}
|
|
} else {
|
|
fmt.Println("No EIC Accelerator opportunities found")
|
|
}
|
|
}
|
|
|
|
// Helper function to safely get string values from map
|
|
func getStringValue(data map[string]interface{}, key string) string {
|
|
if value, ok := data[key].(string); ok {
|
|
return value
|
|
}
|
|
return "N/A"
|
|
}</content>
|
|
<parameter name="filePath">/Users/damirmukimov/city_resource_graph/eu_funding_api.go
|