From 1c7212c081c58953cfa7c462657342dcaf9632ec Mon Sep 17 00:00:00 2001 From: Clemens Zagler Date: Wed, 5 Jun 2024 16:19:05 +0200 Subject: [PATCH] - Implemented configurable Operators (for Parking) - Parking map new metadatafields --- .reuse/dep5 | 2 +- docker-compose.yml | 4 +- src/go.mod | 2 + src/go.sum | 2 + src/netex/operators.go | 92 +++++++++++++++++++++++++++++++ src/netex/operators_test.go | 32 +++++++++++ src/netex/parking/parking.go | 63 +++++++++------------ src/netex/parking/parking_test.go | 14 ++--- src/resources/operators.csv | 12 ++++ 9 files changed, 176 insertions(+), 47 deletions(-) create mode 100644 src/netex/operators.go create mode 100644 src/netex/operators_test.go create mode 100644 src/resources/operators.csv diff --git a/.reuse/dep5 b/.reuse/dep5 index cd5a502..4a135d8 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -4,6 +4,6 @@ Upstream-Contact: NOI Techpark Source: https://github.com/noi-techpark/sta-nap-export Files: .github/* infrastructure .gitignore .gitmodules .pre-commit-config.yaml LICENSE.md .env.example mvnw mvnw.cmd .mvn/* - *.properties src/main/resources/db/migration/* src/go.mod src/go.sum calls.http + *.properties src/main/resources/db/migration/* src/go.mod src/go.sum calls.http src/resources/*.csv Copyright: (c) NOI Techpark License: CC0-1.0 diff --git a/docker-compose.yml b/docker-compose.yml index a6fec08..0b50a65 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,9 +11,9 @@ services: env_file: - .env volumes: - - ./src:/code + - ./src:/src - pkg:/go/pkg/mod - working_dir: /code + working_dir: /src healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] interval: 5s diff --git a/src/go.mod b/src/go.mod index 8465e01..1fa6851 100644 --- a/src/go.mod +++ b/src/go.mod @@ -8,6 +8,8 @@ require ( gotest.tools/v3 v3.5.1 ) +require github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1 // indirect + require ( github.com/bytedance/sonic v1.11.3 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect diff --git a/src/go.sum b/src/go.sum index 1b4aaea..7b74ed5 100644 --- a/src/go.sum +++ b/src/go.sum @@ -26,6 +26,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1 h1:FWNFq4fM1wPfcK40yHE5UO3RUdSNPaBC+j3PokzA6OQ= +github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= diff --git a/src/netex/operators.go b/src/netex/operators.go new file mode 100644 index 0000000..ef34693 --- /dev/null +++ b/src/netex/operators.go @@ -0,0 +1,92 @@ +// SPDX-FileCopyrightText: 2024 NOI Techpark +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +package netex + +import ( + "log" + "os" + "path/filepath" + "runtime" + + "github.com/gocarina/gocsv" +) + +type operatorCfg struct { + Origin string `csv:"origin"` + Email string `csv:"email"` + Phone string `csv:"phone"` + Url string `csv:"url"` + Street string `csv:"street"` + Town string `csv:"town"` + Postcode string `csv:"postcode"` + Country string `csv:"country"` +} + +func readOps(path string) []operatorCfg { + f, err := os.Open(path) + if err != nil { + wd, _ := os.Getwd() + log.Panicln("Cannot open Operators csv.", wd, err) + } + defer f.Close() + + ops := []operatorCfg{} + if err := gocsv.UnmarshalFile(f, &ops); err != nil { + log.Panic("Cannot unmarshal Operators csv", err) + } + return ops +} + +var ops []operatorCfg + +func getCsvPath() string { + // https://stackoverflow.com/questions/31873396/is-it-possible-to-get-the-current-root-of-package-structure-as-a-string-in-golan + // Relative paths are a pain in the butt because unit tests always execute from the directory they are in + // This is a hack to always start from root folder and compose the full "absolute" path + + _, b, _, _ := runtime.Caller(0) + root := filepath.Join(filepath.Dir(b), "../..") + return filepath.Join(root, "src", "resources", "operators.csv") +} + +func mapByOrigin(p []operatorCfg) map[string]operatorCfg { + ret := make(map[string]operatorCfg) + for _, o := range p { + ret[o.Origin] = o + } + return ret +} +func opsByOrigin() map[string]operatorCfg { + if ops == nil { + ops = readOps(getCsvPath()) + } + return mapByOrigin(ops) +} + +func GetOperator(id string) Operator { + cfg, found := opsByOrigin()[id] + if !found { + log.Panic("Unable to map operator. Probably got some origin that we shouldn't have?") + } + + o := Operator{} + o.Id = CreateID("Operator", id) + o.Version = "1" + o.PrivateCode = id + o.Name = id + o.ShortName = id + o.LegalName = id + o.TradingName = id + o.ContactDetails.Email = cfg.Email + o.ContactDetails.Phone = cfg.Phone + o.ContactDetails.Url = cfg.Url + o.OrganisationType = "operator" + o.Address.Id = CreateID("Address", id) + o.Address.CountryName = cfg.Country + o.Address.Street = cfg.Street + o.Address.Town = cfg.Town + o.Address.PostCode = cfg.Postcode + return o +} diff --git a/src/netex/operators_test.go b/src/netex/operators_test.go new file mode 100644 index 0000000..673ddbb --- /dev/null +++ b/src/netex/operators_test.go @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2024 NOI Techpark +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +package netex + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestReadOps(t *testing.T) { + ops := readOps(getCsvPath()) + if len(ops) == 0 { + t.Log("Operators loaded empty") + t.Fail() + } +} + +func TestOpsContent(t *testing.T) { + ops := readOps(getCsvPath()) + mapped := mapByOrigin(ops) + + bsb := mapped["BIKE_SHARING_BOLZANO"] + assert.Equal(t, "urp@comune.bolzano.it", bsb.Email) + assert.Equal(t, "0471997111", bsb.Phone) + + hal := mapped["HAL-API"] + assert.Equal(t, "https://www.carsharing.bz.it/it/", hal.Url) + assert.Equal(t, "Via Beda Weber 1", hal.Street) +} diff --git a/src/netex/parking/parking.go b/src/netex/parking/parking.go index 8a4af8e..8ff6852 100644 --- a/src/netex/parking/parking.go +++ b/src/netex/parking/parking.go @@ -55,15 +55,18 @@ type OdhParking struct { Srid uint32 `json:"srid"` } `json:"scoordinate"` Smeta struct { - StandardName string `json:"standard_name"` - ParkingType string `json:"parkingtype"` - ParkingVehicleTypes string `json:"parkingvehicletypes"` - ParkingLayout string `json:"parkinglayout"` - Capacity int32 `json:"capacity"` - ParkingProhibitions bool `json:"parkingprohibitions"` - ParkingCharging bool `json:"parkingcharging"` - ParkingSurveillance bool `json:"parkingsurveillance"` - ParkingReservation string `json:"parkingreservation"` + StandardName string `json:"standard_name"` + Capacity int32 `json:"capacity"` + Municipality string `json:"municipality"` + Netex struct { + Type string `json:"type"` + VehicleTypes string `json:"vehicletypes"` + Layout string `json:"layout"` + HazardProhibited bool `json:"hazard_prohibited"` + Charging bool `json:"charging"` + Surveillance bool `json:"surveillance"` + Reservation string `json:"reservation"` + } `json:"netex_parking"` } `json:"smetadata"` } @@ -86,26 +89,12 @@ func defEmpty(s string, d string) string { } } -func getOperator(id string) netex.Operator { - o := netex.Operator{} - o.Id = netex.CreateID("Operator", id) - o.Version = "1" - o.PrivateCode = id - o.Name = id - o.ShortName = id - o.LegalName = id - o.TradingName = id - o.ContactDetails.Email = fmt.Sprintf("info@%s.it", id) - o.ContactDetails.Phone = "1234567890" - o.ContactDetails.Url = fmt.Sprintf("https://%s.it", id) - o.OrganisationType = "operator" - o.Address.Id = netex.CreateID("Address", id) - o.Address.CountryName = "Italia" - o.Address.Street = "Via A. Volta 13A" - o.Address.Town = "Bolzano" - o.Address.PostCode = "39100" - return o - +func originButWithHacks(p OdhParking) string { + // There are two different Operators that both have origin FBK (Trento and Rovereto) + if p.Sorigin == "FBK" { + return fmt.Sprintf("%s-%s", p.Sorigin, p.Smeta.Municipality) + } + return p.Sorigin } func mapToNetex(os []OdhParking) ([]Parking, []netex.Operator) { @@ -123,20 +112,20 @@ func mapToNetex(os []OdhParking) ([]Parking, []netex.Operator) { p.Centroid.Location.Longitude = o.Scoord.X p.Centroid.Location.Latitude = o.Scoord.Y p.GmlPolygon = nil - op := getOperator(o.Sorigin) + op := netex.GetOperator(originButWithHacks(o)) ops[op.Id] = op p.OperatorRef = netex.MkRef("Operator", op.Id) p.Entrances = nil - p.ParkingType = defEmpty(o.Smeta.ParkingType, "undefined") - p.ParkingVehicleTypes = o.Smeta.ParkingVehicleTypes - p.ParkingLayout = defEmpty(o.Smeta.ParkingLayout, "undefined") + p.ParkingType = defEmpty(o.Smeta.Netex.Type, "undefined") + p.ParkingVehicleTypes = o.Smeta.Netex.VehicleTypes + p.ParkingLayout = defEmpty(o.Smeta.Netex.Layout, "undefined") p.PrincipalCapacity = o.Smeta.Capacity p.TotalCapacity = o.Smeta.Capacity - p.ProhibitedForHazardousMaterials = o.Smeta.ParkingProhibitions - p.RechargingAvailable = o.Smeta.ParkingProhibitions - p.Secure = o.Smeta.ParkingSurveillance - p.ParkingReservation = defEmpty(o.Smeta.ParkingReservation, "noReservations") + p.ProhibitedForHazardousMaterials = o.Smeta.Netex.HazardProhibited + p.RechargingAvailable = o.Smeta.Netex.Charging + p.Secure = o.Smeta.Netex.Surveillance + p.ParkingReservation = defEmpty(o.Smeta.Netex.Reservation, "noReservations") p.ParkingProperties = nil ps = append(ps, p) diff --git a/src/netex/parking/parking_test.go b/src/netex/parking/parking_test.go index fcd63f1..800ddb8 100644 --- a/src/netex/parking/parking_test.go +++ b/src/netex/parking/parking_test.go @@ -24,13 +24,13 @@ func bzCentro() OdhParking { o.Scoord.Y = 46.432132 o.Smeta.Capacity = 10 o.Smeta.StandardName = "Bolzano Centro 1" - o.Smeta.ParkingType = "parkingZone" - o.Smeta.ParkingVehicleTypes = "all" - o.Smeta.ParkingLayout = "covered" - o.Smeta.ParkingProhibitions = true - o.Smeta.ParkingCharging = true - o.Smeta.ParkingSurveillance = false - o.Smeta.ParkingReservation = "noReservations" + o.Smeta.Netex.Type = "parkingZone" + o.Smeta.Netex.VehicleTypes = "all" + o.Smeta.Netex.Layout = "covered" + o.Smeta.Netex.HazardProhibited = true + o.Smeta.Netex.Charging = true + o.Smeta.Netex.Surveillance = false + o.Smeta.Netex.Reservation = "noReservations" return o } diff --git a/src/resources/operators.csv b/src/resources/operators.csv new file mode 100644 index 0000000..35e6292 --- /dev/null +++ b/src/resources/operators.csv @@ -0,0 +1,12 @@ +origin,email,phone,url,street,town,postcode,country +BIKE_SHARING_BOLZANO,urp@comune.bolzano.it,0471997111,https://opencity.comune.bolzano.it/Servizi/Bike-Sharing-Bici-Bolzano,Vicolo Gumer 7,Bolzano,39100,Italy +BIKE_SHARING_MERANO,info@comune.merano.bz.it,0473250111,https://www.comune.merano.bz.it/it/Bikesharing,Via Portici 192,Merano,39012,Italy +BIKE_SHARING_PAPIN,info@papinsport.com,0474913450,https://www.papinsport.com/rent-a-bike/,Via Freising 9,San Candido,39038,Italy +HAL-API,info@carsharing.bz.it,0471061319,https://www.carsharing.bz.it/it/,Via Beda Weber 1,Bolzano,39100,Italy +FAMAS,urp@comune.bolzano.it,0471997111,https://opencity.comune.bolzano.it/Documenti-e-dati/Documenti-tecnici-di-supporto/Parcheggiare-in-citta,Vicolo Gumer 7,Bolzano,39100,Italy +Municipality Merano,info@comune.merano.bz.it,0473250111,https://www.comune.merano.bz.it/it/Parcheggi,Via Portici 192,Merano,39012,Italy +FBK-Trento,comurp@comune.trento.it,0461884111,https://www.comune.trento.it/Citta/Come-orientarsi/Parcheggi,Via Belenzani 19,Trento,38122,Italy +FBK-Rovereto,urp@comune.rovereto.tn.it,0464452111,https://www.comune.rovereto.tn.it/Servizi/Sosta-e-parcheggi,Piazza Podestà 11,Rovereto,38068,Italy +A22,a22@autobrennero.it,0461212611,https://www.autobrennero.it/it/in-viaggio/sosta-e-servizi/aree-di-servizio/,Via Berlino 10,Trento,38121,Italy +skidata,info@sta.bz.it,0471312888,https://www.sta.bz.it,Via dei Conciapelli 60,Bolzano,39100,Italy +bicincitta,info@sta.bz.it,0471312888,https://www.sta.bz.it,Via dei Conciapelli 60,Bolzano,39100,Italy