[#21] locode: Introduce locode overriding

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
This commit is contained in:
Airat Arifullin 2025-01-22 22:03:36 +03:00
parent 16ff77e35c
commit f2113a9c02
4 changed files with 71 additions and 4 deletions

View file

@ -51,6 +51,7 @@ locode_db: in/unlocode-CodeList.csv in/unlocode-SubdivisionCodes.csv in/continen
--continents in/continents.geojson \ --continents in/continents.geojson \
--countries in/countries.dat \ --countries in/countries.dat \
--in in/unlocode-CodeList.csv \ --in in/unlocode-CodeList.csv \
--overrides in/unlocode-CodeList_overrides.csv \
--subdiv in/unlocode-SubdivisionCodes.csv \ --subdiv in/unlocode-SubdivisionCodes.csv \
--out locode_db --out locode_db
chmod 644 locode_db chmod 644 locode_db

View file

@ -16,6 +16,7 @@ type namesDB struct {
const ( const (
locodeGenerateInputFlag = "in" locodeGenerateInputFlag = "in"
locodeGenerateOverridesFlag = "overrides"
locodeGenerateSubDivFlag = "subdiv" locodeGenerateSubDivFlag = "subdiv"
locodeGenerateAirportsFlag = "airports" locodeGenerateAirportsFlag = "airports"
locodeGenerateCountriesFlag = "countries" locodeGenerateCountriesFlag = "countries"
@ -25,6 +26,7 @@ const (
var ( var (
locodeGenerateInPaths []string locodeGenerateInPaths []string
locodeGenerateOverridesPath string
locodeGenerateSubDivPath string locodeGenerateSubDivPath string
locodeGenerateAirportsPath string locodeGenerateAirportsPath string
locodeGenerateCountriesPath string locodeGenerateCountriesPath string
@ -35,11 +37,11 @@ var (
Use: "generate", Use: "generate",
Short: "Generate UN/LOCODE database for FrostFS", Short: "Generate UN/LOCODE database for FrostFS",
Run: func(cmd *cobra.Command, _ []string) { Run: func(cmd *cobra.Command, _ []string) {
locodeDB := csvlocode.New( locodeDB := csvlocode.New(
csvlocode.Prm{ csvlocode.Prm{
Path: locodeGenerateInPaths[0], Path: locodeGenerateInPaths[0],
SubDivPath: locodeGenerateSubDivPath, SubDivPath: locodeGenerateSubDivPath,
OverridesPath: locodeGenerateOverridesPath,
}, },
csvlocode.WithExtraPaths(locodeGenerateInPaths[1:]...), csvlocode.WithExtraPaths(locodeGenerateInPaths[1:]...),
) )
@ -79,6 +81,8 @@ func initUtilLocodeGenerateCmd() {
flags.StringSliceVar(&locodeGenerateInPaths, locodeGenerateInputFlag, nil, "List of paths to UN/LOCODE tables (csv)") flags.StringSliceVar(&locodeGenerateInPaths, locodeGenerateInputFlag, nil, "List of paths to UN/LOCODE tables (csv)")
_ = locodeGenerateCmd.MarkFlagRequired(locodeGenerateInputFlag) _ = locodeGenerateCmd.MarkFlagRequired(locodeGenerateInputFlag)
flags.StringVar(&locodeGenerateOverridesPath, locodeGenerateOverridesFlag, "", "Path to UN/LOCODE override tables (csv)")
flags.StringVar(&locodeGenerateSubDivPath, locodeGenerateSubDivFlag, "", "Path to UN/LOCODE subdivision database (csv)") flags.StringVar(&locodeGenerateSubDivPath, locodeGenerateSubDivFlag, "", "Path to UN/LOCODE subdivision database (csv)")
_ = locodeGenerateCmd.MarkFlagRequired(locodeGenerateSubDivFlag) _ = locodeGenerateCmd.MarkFlagRequired(locodeGenerateSubDivFlag)

View file

@ -3,6 +3,7 @@ package csvlocode
import ( import (
"encoding/csv" "encoding/csv"
"errors" "errors"
"fmt"
"io" "io"
"os" "os"
"strings" "strings"
@ -40,6 +41,10 @@ func (t *Table) IterateAll(f func(locode.Record) error) error {
Remarks: words[11], Remarks: words[11],
} }
if err := t.Override(&record); err != nil {
return fmt.Errorf("override: %w", err)
}
return f(record) return f(record)
}) })
} }
@ -84,6 +89,14 @@ func (t *Table) SubDivName(countryCode *locodedb.CountryCode, code string) (stri
return rec.name, nil return rec.name, nil
} }
func (t *Table) Override(record *locode.Record) error {
override, found := t.overrides[record.LOCODE]
if found {
*record = override
}
return nil
}
func (t *Table) initSubDiv() (err error) { func (t *Table) initSubDiv() (err error) {
t.subDivOnce.Do(func() { t.subDivOnce.Do(func() {
t.mSubDiv = make(map[subDivKey]subDivRecord) t.mSubDiv = make(map[subDivKey]subDivRecord)
@ -103,6 +116,40 @@ func (t *Table) initSubDiv() (err error) {
return return
} }
func (t *Table) initOverrides(overridesPath string) error {
const wordsPerRecord = 12
t.overrides = make(map[locode.LOCODE]locode.Record)
if overridesPath == "" {
return nil
}
return t.scanWords([]string{overridesPath}, wordsPerRecord, func(words []string) error {
lc, err := locode.FromString(strings.Join(words[1:3], " "))
if err != nil {
return err
}
record := locode.Record{
Ch: words[0],
LOCODE: *lc,
Name: words[3],
NameWoDiacritics: words[4],
SubDiv: words[5],
Function: words[6],
Status: words[7],
Date: words[8],
IATA: words[9],
Coordinates: words[10],
Remarks: words[11],
}
t.overrides[record.LOCODE] = record
return nil
})
}
var errScanInt = errors.New("interrupt scan") var errScanInt = errors.New("interrupt scan")
func (t *Table) scanWords(paths []string, fpr int, wordsHandler func([]string) error) error { func (t *Table) scanWords(paths []string, fpr int, wordsHandler func([]string) error) error {

View file

@ -4,6 +4,8 @@ import (
"fmt" "fmt"
"io/fs" "io/fs"
"sync" "sync"
"git.frostfs.info/TrueCloudLab/frostfs-locode-db/pkg/locode"
) )
// Prm groups the required parameters of the Table's constructor. // Prm groups the required parameters of the Table's constructor.
@ -21,6 +23,11 @@ type Prm struct {
// //
// Must not be empty. // Must not be empty.
SubDivPath string SubDivPath string
// Path to a csv table with UN/LOCODE overrides.
//
// Optional.
OverridesPath string
} }
// Table is a descriptor of the UN/LOCODE table in csv format. // Table is a descriptor of the UN/LOCODE table in csv format.
@ -39,6 +46,8 @@ type Table struct {
subDivOnce sync.Once subDivOnce sync.Once
mSubDiv map[subDivKey]subDivRecord mSubDiv map[subDivKey]subDivRecord
overrides map[locode.LOCODE]locode.Record
} }
const invalidPrmValFmt = "invalid parameter %s (%T):%v" const invalidPrmValFmt = "invalid parameter %s (%T):%v"
@ -67,9 +76,15 @@ func New(prm Prm, opts ...Option) *Table {
opts[i](o) opts[i](o)
} }
return &Table{ t := &Table{
paths: append(o.extraPaths, prm.Path), paths: append(o.extraPaths, prm.Path),
mode: o.mode, mode: o.mode,
subDivPath: prm.SubDivPath, subDivPath: prm.SubDivPath,
} }
if err := t.initOverrides(prm.OverridesPath); err != nil {
panic(fmt.Errorf("init overrides: %w", err))
}
return t
} }