Country profile changes : GWM
This commit is contained in:
parent
661971f9d7
commit
89f8e1c515
494
main.go
494
main.go
@ -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, ",") + ")"
|
||||
}
|
||||
|
||||
BIN
pb_data/data.db
BIN
pb_data/data.db
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
pb_data/logs.db
BIN
pb_data/logs.db
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user