Country profile changes : GWM

This commit is contained in:
Gowtham M 2026-02-04 12:30:32 +05:30
parent 661971f9d7
commit 89f8e1c515
7 changed files with 494 additions and 0 deletions

494
main.go
View File

@ -483,6 +483,13 @@ func main() {
app = pocketbase.New()
app.OnRecordAuthRequest("users").Add(func(e *core.RecordAuthEvent) error {
e.Record.Set("lastLogin", time.Now().UTC())
return app.Dao().SaveRecord(e.Record)
})
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
// Serve privacy policy static page
e.Router.GET("/privacy/*", apis.StaticDirectoryHandler(os.DirFS("./pb_public/privacy"), false))
@ -1995,6 +2002,465 @@ func main() {
})
// ============================================
// GET ALL COUNTRIES
// ============================================
e.Router.GET("/api/getCountries", func(c echo.Context) error {
language := c.QueryParam("language")
countries := []struct {
ID string `db:"id" json:"id"`
CountryName string `db:"country_name" json:"country_name"`
CountryCode string `db:"country_code" json:"country_code"`
FlagUrl string `db:"flag_url" json:"flag_url"`
Logo string `db:"logo" json:"logo"`
}{}
countryNameColumn := "country_name_" + language
query := ` SELECT id,country_code,flag_url,logo,` + countryNameColumn + ` AS country_name FROM countries`
err := app.DB().
NewQuery(query).
All(&countries)
if err != nil {
log.Printf("Failed to fetch countries: %v", err)
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to fetch countries"})
}
if len(countries) == 0 {
return c.JSON(http.StatusNotFound, map[string]string{"error": "No countries found"})
}
// Build response with full logo URLs
result := []map[string]interface{}{}
for _, country := range countries {
var flagURL string
if country.Logo == "" {
flagURL = ""
} else {
flagURL = fmt.Sprintf("%s/api/files/countries/%s/%s",
baseUrl,
country.ID,
country.Logo,
)
}
result = append(result, map[string]interface{}{
"id": country.ID,
"country_name": country.CountryName,
"country_code": country.CountryCode,
"flag_url": flagURL,
})
}
return c.JSON(http.StatusOK, result)
})
// ============================================
// GET COUNTRY DETAILS (Country + Leaders + All Statistics)
// ============================================
e.Router.GET("/api/getCountryDetails", func(c echo.Context) error {
countryCode := c.QueryParam("country_code")
language := c.QueryParam("language")
if countryCode == "" {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "country_code is required"})
}
// Get country
country := struct {
ID string `db:"id" json:"id"`
CountryName string `db:"country_name" json:"country_name"`
CountryCode string `db:"country_code" json:"country_code"`
FlagUrl string `db:"flag_url" json:"flag_url"`
}{}
countryNameColumn := "country_name_" + language
query := ` SELECT id,country_code,flag_url,logo,` + countryNameColumn + ` AS country_name FROM countries WHERE country_code = {:country_code}`
err := app.DB().
NewQuery(query).
Bind(dbx.Params{
"country_code": countryCode,
}).
One(&country)
if err != nil {
log.Printf("Country not found: %v", err)
return c.JSON(http.StatusNotFound, map[string]string{"error": "Country not found"})
}
// Get leaders
leaders := []struct {
ID string `db:"id" json:"id"`
Name string `db:"name" json:"name"`
Position string `db:"position" json:"position"`
Photo string `db:"photo" json:"photo"`
IsCurrent bool `db:"is_current" json:"is_current"`
}{}
nameColumn := "name_" + language
positionColumn := "position_" + language
query2 := `SELECT id,` + nameColumn + ` AS name,` + positionColumn + ` AS position,photo,is_current FROM leaders WHERE country = {:country} AND is_current = true ORDER BY position ASC `
err = app.DB().
NewQuery(query2).
Bind(dbx.Params{
"country": country.ID,
}).
All(&leaders)
if err != nil {
log.Printf("Failed to fetch leaders: %v", err)
}
// Get all statistics
allStatistics := []struct {
ID string `db:"id" json:"id"`
Type string `db:"type" json:"type"`
Topic string `db:"topic" json:"topic"`
Value string `db:"value" json:"value"`
Rank float64 `db:"rank" json:"rank"`
IsRankPositive bool `db:"is_rank_positive" json:"is_rank_positive"`
RankHeading string `db:"rank_heading" json:"rank_heading"`
}{}
topicColumn := "topic_" + language
valueColumn := "value_" + language
rankHeadingColumn := "rank_heading_" + language
query3 := `SELECT id,type,` + topicColumn + ` AS topic,` + valueColumn + ` AS value,` + rankHeadingColumn + ` AS rank_heading,rank,is_rank_positive FROM country_statistics WHERE country = {:country} ORDER BY type ASC, rank ASC`
err = app.DB().
NewQuery(query3).
Bind(dbx.Params{
"country": country.ID,
}).
All(&allStatistics)
if err != nil {
log.Printf("Failed to fetch statistics: %v", err)
}
// Group statistics by type
statisticsMap := map[string]interface{}{
"demography": []interface{}{},
"gdp": []interface{}{},
"fdi": []interface{}{},
"trade": []interface{}{},
"bilateral_trade": []interface{}{},
}
for _, stat := range allStatistics {
// Remove quotes from type field if present
cleanType := stat.Type
if len(cleanType) > 2 && cleanType[0] == '"' && cleanType[len(cleanType)-1] == '"' {
cleanType = cleanType[1 : len(cleanType)-1]
}
if _, exists := statisticsMap[cleanType]; exists {
statisticsMap[cleanType] = append(statisticsMap[cleanType].([]interface{}), stat)
}
}
// Build final response
result := map[string]interface{}{
"country": country,
"leaders": leaders,
"statistics": statisticsMap,
}
return c.JSON(http.StatusOK, result)
})
// ============================================
// GET BILATERAL TRADE DATA
// ============================================
e.Router.GET("/api/getBilateralTradeData", func(c echo.Context) error {
countryCode := c.QueryParam("country_code")
statisticsId := c.QueryParam("statistics_id")
language := c.QueryParam("language")
if countryCode == "" {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "country_code is required"})
}
// Get country
country := struct {
ID string `db:"id" json:"id"`
CountryName string `db:"country_name" json:"country_name"`
CountryCode string `db:"country_code" json:"country_code"`
FlagUrl string `db:"flag_url" json:"flag_url"`
}{}
countryNameColumn := "country_name_" + language
query := ` SELECT id,country_code,flag_url,logo,` + countryNameColumn + ` AS country_name FROM countries WHERE country_code = {:country_code}`
err := app.DB().
NewQuery(query).
Bind(dbx.Params{
"country_code": countryCode,
}).
One(&country)
if err != nil {
return c.JSON(http.StatusNotFound, map[string]string{"error": "Country not found"})
}
// Get all bilateral trade statistics
allBilateralTradeData := []struct {
Topic string `db:"topic" json:"topic"`
Value string `db:"value" json:"value"`
Icon string `db:"icon" json:"icon"`
}{}
topicColumn := "topic_" + language
query2 := `SELECT ` + topicColumn + ` AS topic,value,icon FROM bilateral_trade_data WHERE country = {:country} AND statistics = {:statistics}`
err = app.DB().
NewQuery(query2).
Bind(dbx.Params{
"country": country.ID,
"statistics": statisticsId,
}).
All(&allBilateralTradeData)
if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{
"error": err.Error(),
})
}
if len(allBilateralTradeData) == 0 {
return c.JSON(http.StatusNotFound, map[string]string{
"error": "Data not found",
})
}
return c.JSON(http.StatusOK, allBilateralTradeData)
})
// ============================================
// GET ALL COMPETITIVENESS REPORTS
// ============================================
e.Router.GET("/api/getCompetitivenessReports", func(c echo.Context) error {
type CountryMini struct {
ID string `json:"id"`
CountryName string `json:"country_name"`
Flag string `json:"flag"`
}
type CompetitivenessReport struct {
ID string `json:"id"`
Year int `json:"year"`
CurrentYearRank int `json:"current_year_rank"`
PreviousYearRank int `json:"previous_year_rank"`
IsRankPositive bool `json:"is_rank_positive"`
Heading string `json:"heading"`
Content string `json:"content"`
Logo string `json:"logo"`
FirstGlobally []CountryMini `json:"first_in_globally"`
FirstGCC []CountryMini `json:"first_in_gcc"`
FirstArab []CountryMini `json:"first_in_arab_countries"`
}
language := c.QueryParam("language")
if language == "" {
language = "en"
}
headingColumn := "heading_" + language
contentColumn := "content_" + language
// Step 1: fetch competitiveness reports
rows := []struct {
ID string `db:"id"`
Year int `db:"year"`
CurrentYearRank int `db:"current_year_rank"`
PreviousYearRank int `db:"previous_year_rank"`
IsRankPositive bool `db:"is_rank_positive"`
Logo string `db:"logo"`
Heading string `db:"heading"`
Content string `db:"content"`
FirstGlobally string `db:"first_in_globally"`
FirstGCC string `db:"first_in_gcc"`
FirstArab string `db:"first_in_arab_countries"`
}{}
query := `
SELECT
id,
year,
current_year_rank,
previous_year_rank,
is_rank_positive,
logo,
` + headingColumn + ` AS heading,
` + contentColumn + ` AS content,
first_in_globally,
first_in_gcc,
first_in_arab_countries
FROM competitive_reports
`
err := app.DB().NewQuery(query).All(&rows)
if err != nil {
return c.JSON(http.StatusInternalServerError, echo.Map{
"error": err.Error(),
})
}
if len(rows) == 0 {
return c.JSON(http.StatusNotFound, echo.Map{
"error": "No competitiveness reports found",
})
}
// return c.JSON(http.StatusOK, rows)
// Step 2: loop & attach relation data
result := []CompetitivenessReport{}
for _, r := range rows {
report := CompetitivenessReport{
ID: r.ID,
Year: r.Year,
CurrentYearRank: r.CurrentYearRank,
PreviousYearRank: r.PreviousYearRank,
IsRankPositive: r.IsRankPositive,
Heading: r.Heading,
Content: r.Content,
Logo: baseUrl + "/api/files/competitive_reports/" + r.ID + "/" + r.Logo,
}
loadCountries := func(ids []string) []CountryMini {
if len(ids) == 0 {
return []CountryMini{}
}
inClause := buildSafeInClause(ids)
query := `
SELECT
id,
country_name_` + language + ` AS country_name,
flag_url AS flag
FROM countries
WHERE id IN ` + inClause
countries := []CountryMini{}
err := app.DB().
NewQuery(query).
All(&countries)
if err != nil {
log.Println("Country fetch error:", err)
}
return countries
}
report.FirstGlobally = loadCountries(parseRelationIDs(r.FirstGlobally))
report.FirstGCC = loadCountries(parseRelationIDs(r.FirstGCC))
report.FirstArab = loadCountries(parseRelationIDs(r.FirstArab))
result = append(result, report)
}
return c.JSON(http.StatusOK, result)
})
// ============================================
// SEED SAMPLE DATA (Run once to populate database)
// ============================================
e.Router.POST("/api/seedSampleData", func(c echo.Context) error {
// Create Argentina
countryID := ""
err := app.Dao().DB().
Select("id").
From("countries").
Where(dbx.HashExp{"country_code": "AR"}).
One(&struct{ ID string }{ID: countryID})
// If Argentina doesn't exist, create it
if err != nil {
_, err = app.Dao().DB().
Insert("countries", dbx.Params{
"country_name": "Argentina",
"country_code": "AR",
"flag_url": "https://flagcdn.com/ar.svg",
}).Execute()
if err != nil {
log.Printf("Failed to create Argentina: %v", err)
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to create country"})
}
// Get the newly created country ID
err = app.Dao().DB().
Select("id").
From("countries").
Where(dbx.HashExp{"country_code": "AR"}).
One(&struct{ ID string }{ID: countryID})
}
// Insert Leaders (simplified - you can add more)
leaders := []map[string]interface{}{
{"country": countryID, "name": "Javier Milei", "position": "President", "is_current": true},
{"country": countryID, "name": "Victoria Villarruel", "position": "Vice President", "is_current": true},
}
for _, leader := range leaders {
_, err = app.Dao().DB().Insert("leaders", leader).Execute()
if err != nil {
log.Printf("Failed to insert leader: %v", err)
}
}
// Insert Statistics
statistics := []map[string]interface{}{
// Demography
{"country": countryID, "type": "\"demography\"", "topic": "Capital City", "value": "Buenos Aires"},
{"country": countryID, "type": "\"demography\"", "topic": "Area", "value": "2,780,400 sq. km"},
{"country": countryID, "type": "\"demography\"", "topic": "2021 Population", "value": "45.81 M", "rank": 0.9, "is_rank_positive": true},
// GDP
{"country": countryID, "type": "\"gdp\"", "topic": "2021 Nominal (AED)", "value": "1.8 T", "rank": 2.5, "is_rank_positive": true},
{"country": countryID, "type": "\"gdp\"", "topic": "2021 Real (AED)", "value": "2.08 T", "rank": 3.2, "is_rank_positive": true},
// FDI
{"country": countryID, "type": "\"fdi\"", "topic": "2020 FDI Inflow (AED)", "value": "4.75 B", "rank": -10.41, "is_rank_positive": false},
// Trade
{"country": countryID, "type": "\"trade\"", "topic": "2021 Total Trade (AED)", "value": "603 B", "rank": 35.36, "is_rank_positive": true},
// Bilateral Trade
{"country": countryID, "type": "\"bilateral_trade\"", "topic": "Total (2022)", "value": "$4.7M"},
{"country": countryID, "type": "\"bilateral_trade\"", "topic": "Top 5 Import Commodities", "value": "Jewellery, Diamonds, Gold", "rank": 1},
}
for _, stat := range statistics {
_, err = app.Dao().DB().Insert("country_statistics", stat).Execute()
if err != nil {
log.Printf("Failed to insert statistic: %v", err)
}
}
return c.JSON(http.StatusOK, map[string]string{"message": "Sample data seeded successfully"})
})
type DownloadRequest struct {
Username string `json:"username"`
Password string `json:"password"`
@ -3428,3 +3894,31 @@ func sendStatusChangeEmail(app *pocketbase.PocketBase, record *models.Record, ne
log.Printf("Status change email sent successfully to: %s", userEmail)
return nil
}
func parseRelationIDs(value string) []string {
if value == "" {
return []string{}
}
var ids []string
_ = json.Unmarshal([]byte(value), &ids)
return ids
}
func buildSafeInClause(ids []string) string {
quoted := make([]string, 0, len(ids))
for _, id := range ids {
if id == "" {
continue
}
// PocketBase IDs are safe: lowercase letters + numbers
quoted = append(quoted, "'"+id+"'")
}
if len(quoted) == 0 {
return "('')" // never matches
}
return "(" + strings.Join(quoted, ",") + ")"
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.