From fd382088f83b5ecbf5ac43ab5960b7fec66e19d4 Mon Sep 17 00:00:00 2001 From: ysandler Date: Fri, 2 May 2025 13:13:33 -0500 Subject: [PATCH] init commit --- .env.example | 8 ++ .gitignore | 1 + businesses.xlsx | Bin 0 -> 8664 bytes go.mod | 30 ++++++++ go.sum | 112 +++++++++++++++++++++++++++ main.go | 199 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 350 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100755 businesses.xlsx create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..6391e73 --- /dev/null +++ b/.env.example @@ -0,0 +1,8 @@ +API_KEY= +MAX_RESULTS=10 +SHEET_NAME=businesses + +LAT= +LNG= +# needs to be valid to parse as a `uint` +RADIUS_IN_METERS= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env diff --git a/businesses.xlsx b/businesses.xlsx new file mode 100755 index 0000000000000000000000000000000000000000..bfc454d7c1201bbdbd649e43f906c904a82de756 GIT binary patch literal 8664 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 AzW@LL literal 0 HcmV?d00001 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..cee1c9b --- /dev/null +++ b/go.mod @@ -0,0 +1,30 @@ +module nearby-search + +go 1.23.2 + +require ( + github.com/jedib0t/go-pretty/v6 v6.6.7 + github.com/joho/godotenv v1.5.1 + github.com/kr/pretty v0.3.1 + github.com/xuri/excelize/v2 v2.9.0 + googlemaps.github.io/maps v1.7.0 +) + +require ( + github.com/google/uuid v1.1.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/richardlehane/mscfb v1.0.4 // indirect + github.com/richardlehane/msoleps v1.0.4 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/rogpeppe/go-internal v1.9.0 // indirect + github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d // indirect + github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect + go.opencensus.io v0.22.3 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect + golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..d034233 --- /dev/null +++ b/go.sum @@ -0,0 +1,112 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jedib0t/go-pretty/v6 v6.6.7 h1:m+LbHpm0aIAPLzLbMfn8dc3Ht8MW7lsSO4MPItz/Uuo= +github.com/jedib0t/go-pretty/v6 v6.6.7/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM= +github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= +github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= +github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00= +github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d h1:llb0neMWDQe87IzJLS4Ci7psK/lVsjIS2otl+1WyRyY= +github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= +github.com/xuri/excelize/v2 v2.9.0 h1:1tgOaEq92IOEumR1/JfYS/eR0KHOCsRv/rYXXh6YJQE= +github.com/xuri/excelize/v2 v2.9.0/go.mod h1:uqey4QBZ9gdMeWApPLdhm9x+9o2lq4iVmjiLfBS5hdE= +github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 h1:hPVCafDV85blFTabnqKgNhDCkJX25eik94Si9cTER4A= +github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= +go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= +golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +googlemaps.github.io/maps v1.7.0 h1:9yAEgaAyg6bWn+TpY8PmNJ0C+YfUBtN9KjJypjCOioo= +googlemaps.github.io/maps v1.7.0/go.mod h1:cCq0JKYAnnCRSdiaBi7Ex9CW15uxIAk7oPi8V/xEh6s= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/main.go b/main.go new file mode 100644 index 0000000..c6d1d6b --- /dev/null +++ b/main.go @@ -0,0 +1,199 @@ +package main + +import ( + "context" + "fmt" + "log" + "os" + "strconv" + "strings" + + "github.com/jedib0t/go-pretty/v6/table" + "github.com/joho/godotenv" + "github.com/kr/pretty" + "github.com/xuri/excelize/v2" + "googlemaps.github.io/maps" +) + +type Row struct { + PlaceID string `xlsx:"column(PlaceID)"` + Name string `xlsx:"column(Name)"` + Types []string `xlsx:"column(Types)"` + Address string `xlsx:"column(Address)"` + Website string `xlsx:"column(Website)"` + PhoneNumber string `xlsx:"column(PhoneNumber)"` + GoogleMapsUrl string `xlsx:"column(GoogleMapsUrl)"` +} + +func main() { + fmt.Println("Reading ENV...") + err := godotenv.Load(".env") + if err != nil { + log.Fatalf("Error loading .env file") + } + + fmt.Println("Creating maps api client...") + c, err := maps.NewClient(maps.WithAPIKey(os.Getenv("API_KEY"))) + if err != nil { + log.Fatalf("error creating maps client: %s", err) + } + + t := table.NewWriter() + t.AppendHeader(table.Row{"Name", "Address", "PhoneNumber"}) + + var rows []Row + + fmt.Println("Searching places...") + foundPlaces := searchNearby(c) + + for _, place := range foundPlaces { + details := searchDetails(c, place) + r := makeRow(place, details) + t.AppendRow(table.Row{r.Name, r.Address, r.PhoneNumber}) + rows = append(rows, r) + } + + fmt.Println(t.Render()) + pretty.Println(len(rows)) + + createExcelSheet(rows) +} + +func searchNearby(mapsClient *maps.Client) []maps.PlacesSearchResult { + mileInMeters, _ := strconv.ParseUint(os.Getenv("RADIUS_IN_METERS"), 10, strconv.IntSize) + Lat, _ := strconv.ParseFloat(os.Getenv("LAT"), 64) + Lng, _ := strconv.ParseFloat(os.Getenv("LNG"), 64) + latLon := maps.LatLng{ + Lat: Lat, + Lng: Lng, + } + + var foundPlaces []maps.PlacesSearchResult + var nextPageToken string = "" + + MAX_RESULTS, _ := strconv.Atoi(os.Getenv("MAX_RESULTS")) + for next := true; next; next = (len(foundPlaces) <= MAX_RESULTS) && nextPageToken != "" { + mapRequest := maps.NearbySearchRequest{ + Location: &latLon, + Radius: uint(mileInMeters), + PageToken: nextPageToken, + } + + searchResponse, err := mapsClient.NearbySearch(context.Background(), &mapRequest) + if err != nil { + log.Fatalf("error searching places: %s", err) + } + + nextPageToken = searchResponse.NextPageToken + for _, place := range searchResponse.Results { + if !shouldOmitPlace(place) { + foundPlaces = append(foundPlaces, place) + } + } + } + + return foundPlaces +} + +func findString(slice []string, condition func(string) bool) *string { + for _, element := range slice { + if condition(element) { + return &element + } + } + return nil +} + +func shouldOmitPlace(place maps.PlacesSearchResult) bool { + omitTypes := []string{"bar", "food", "restaurant", "store"} + requiredTypes := []string{"establishment"} + + var hasOmitType bool = false + var hasRequredType bool = false + + for _, placeType := range place.Types { + foundInOmit := findString(omitTypes, func(omitType string) bool { + return omitType == placeType + }) + + if foundInOmit != nil { + hasOmitType = true + break + } + + foundInRequired := findString(requiredTypes, func(requiredType string) bool { + return requiredType == placeType + }) + + if foundInRequired != nil { + hasRequredType = true + } + } + + return hasOmitType || !hasRequredType +} + +func makeRow(place maps.PlacesSearchResult, details maps.PlaceDetailsResult) Row { + return Row{ + PlaceID: place.PlaceID, + Name: place.Name, + Types: place.Types, + Address: details.FormattedAddress, + PhoneNumber: details.FormattedPhoneNumber, + Website: details.Website, + GoogleMapsUrl: details.URL, + } +} + +func searchDetails(mapsClient *maps.Client, place maps.PlacesSearchResult) maps.PlaceDetailsResult { + desiredDetails := []maps.PlaceDetailsFieldMask{ + "formatted_address", + "formatted_phone_number", + "website", + "url", + } + + detailsRequest := maps.PlaceDetailsRequest{ + PlaceID: place.PlaceID, + Fields: desiredDetails, + } + + detailsResponse, err := mapsClient.PlaceDetails(context.Background(), &detailsRequest) + if err != nil { + log.Fatalf("error getting details for %s: %s", place.Name, err) + } + return detailsResponse +} + +func createExcelSheet(rows []Row) { + + fmt.Println("Writing XLSX...") + + f := excelize.NewFile() + sheetName := os.Getenv("SHEET_NAME") + index, _ := f.NewSheet(sheetName) + f.SetActiveSheet(index) + + excelHeaders := []string{"PlaceID", "Name", "Types", "Address", "Website", "PhoneNumber", "GoogleMapsUrl"} + for col, header := range excelHeaders { + cell := string(rune('A'+col)) + "1" + f.SetCellValue(sheetName, cell, header) + } + + for i, row := range rows { + baseRow := i + 2 + f.SetCellValue(sheetName, "A"+strconv.Itoa(baseRow), row.PlaceID) + f.SetCellValue(sheetName, "B"+strconv.Itoa(baseRow), row.Name) + f.SetCellValue(sheetName, "C"+strconv.Itoa(baseRow), strings.Join(row.Types, ",")) + f.SetCellValue(sheetName, "D"+strconv.Itoa(baseRow), row.Address) + f.SetCellValue(sheetName, "E"+strconv.Itoa(baseRow), row.Website) + f.SetCellValue(sheetName, "F"+strconv.Itoa(baseRow), row.PhoneNumber) + f.SetCellValue(sheetName, "G"+strconv.Itoa(baseRow), row.GoogleMapsUrl) + } + + if err := f.SaveAs(sheetName + ".xlsx"); err != nil { + fmt.Println(err) + } else { + fmt.Println("Finished") + } +}