├── .gitignore ├── LICENSE ├── README.md ├── assets ├── arch.png ├── dockerps.png ├── prepconfig.png ├── pyconfig.png ├── roles.png └── video-thumbnail.png ├── src ├── Dockerfile ├── deploy │ ├── .gitignore │ ├── Pulumi.yaml │ ├── README.md │ ├── index.js │ ├── package-lock.json │ └── package.json ├── geocode.py ├── prepForward.py ├── prepReverse.py ├── startup.sh └── tabpy.conf └── test.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | .DS_Store 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | *.pid.lock 14 | 15 | # Directory for instrumented libs generated by jscoverage/JSCover 16 | lib-cov 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # nyc test coverage 22 | .nyc_output 23 | 24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # Bower dependency directory (https://bower.io/) 28 | bower_components 29 | 30 | # node-waf configuration 31 | .lock-wscript 32 | 33 | # Compiled binary addons (https://nodejs.org/api/addons.html) 34 | build/Release 35 | 36 | # Dependency directories 37 | node_modules/ 38 | jspm_packages/ 39 | 40 | # TypeScript v1 declaration files 41 | typings/ 42 | 43 | # Optional npm cache directory 44 | .npm 45 | 46 | # Optional eslint cache 47 | .eslintcache 48 | 49 | # Optional REPL history 50 | .node_repl_history 51 | 52 | # Output of 'npm pack' 53 | *.tgz 54 | 55 | # Yarn Integrity file 56 | .yarn-integrity 57 | 58 | # dotenv environment variables file 59 | .env 60 | 61 | # next.js build output 62 | .next 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, Mapbox 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tableau Geocoding with Mapbox 2 | 3 | 4 | 5 | This solution allows you to use Tableau Prep for performing forward and reverse geocoding, integrated via [TabPy](https://github.com/tableau/TabPy). 6 | 7 | *You will need to have Permanent Geocoding enabled on your Mapbox account in order to use this solution. Contact [Mapbox](https://www.mapbox.com/) to get this enabled for your account.* 8 | 9 | This repository also includes instructions for launching your own TabPy instance, there is a sample script for deploying this on AWS for testing and use at scale. 10 | 11 | 12 | 13 | - [Tableau Geocoding with Mapbox](#tableau-geocoding-with-mapbox) 14 | - [Geocoding](#geocoding) 15 | - [Usage](#usage) 16 | - [Prerequisites](#prerequisites) 17 | - [Geocoding in Prep](#geocoding-in-prep) 18 | - [Forward Geocoding Requirements](#forward-geocoding-requirements) 19 | - [Reverse Geocoding Requirements](#reverse-geocoding-requirements) 20 | - [Customization](#customization) 21 | - [Geocoding Customizations](#geocoding-customizations) 22 | - [Build and Connect](#build-and-connect) 23 | - [Customization Example](#customization-example) 24 | - [Rate Limiting](#rate-limiting) 25 | - [Production](#production) 26 | 27 | 28 | 29 | ## Geocoding 30 | 31 | Tableau Desktop and Prep already geocode for a specific set of geographies (known as Geographic Roles). 32 | 33 | ![roles](assets/roles.png) 34 | 35 | There are times users require either more granular data, or additional context about a location-related data point. You can enrich your data through geocoding: 36 | 37 | - Forward: Query with text and receive a coordinate pair 38 | - Reverse: Query with a coordinate pair and receive a location. 39 | 40 | The Mapbox Geocoding API provides global scale and data coverage. It is also available to all users (no Enterprise Agreement required). For more information, consult the [Mapbox pricing page](https://www.mapbox.com/pricing/#geocode). 41 | 42 | ## Usage 43 | 44 | To simplify setup and deployment, this solution wraps [TabPy](https://github.com/tableau/TabPy) in Docker. This enables cross-platform usage with minimal setup, while also reducing the amount of code a Tableau Desktop user must write. 45 | 46 | The architecture is displayed below 47 | 48 | ![arch](assets/arch.png) 49 | 50 | ### Prerequisites 51 | 52 | You need to have Docker installed on your platform: [Mac](https://docs.docker.com/docker-for-mac/install/) / [Windows](https://docs.docker.com/docker-for-windows/) 53 | 54 | ### Geocoding in Prep 55 | 56 | To get started run the following command 57 | 58 | ```bash 59 | docker run -d -p 80:80 mbxsolutions/tableaugeocode 60 | ``` 61 | 62 | This will bring down the [fully built image from Docker Hub](https://hub.docker.com/r/mbxsolutions/tableaugeocode) and start it running on port 80. 63 | 64 | The next step is to configure your geocoding script with your Mapbox token. 65 | 66 | There are a sample scripts for forward and reverse geocoding found in this repository. You can access after cloning, or simply copy and paste from the snippet below. 67 | 68 | `prepForward.py` is for forward geocoding while `prepReverse.py` is for reverse geocoding. Before you begin, if you would like more details about the response object read the [Geocoding API documentation](https://docs.mapbox.com/api/search/#geocoding-response-object). 69 | 70 | Paste your Mapbox token to replace "token" and save. 71 | 72 | ```python 73 | # This allows your script to access to published function 74 | import sys 75 | sys.path.append('/TabPy') 76 | from geocode import geocodeForward 77 | 78 | # This is the function you reference in Tableau Prep 79 | def prepGeo(input): 80 | # YOUR TOKEN GOES HERE 81 | prepData = geocodeForward(input,"token") 82 | return prepData 83 | 84 | # This is the output schema that is passed to Tableau Prep when geocoding is complete 85 | def get_output_schema(): 86 | return pd.DataFrame( 87 | { 88 | "InitialQuery": prep_string(), 89 | "ReturnAddress": prep_string(), 90 | "Accuracy": prep_string(), 91 | "Relevance": prep_decimal(), 92 | "Longitude": prep_decimal(), 93 | "Latitude": prep_decimal(), 94 | } 95 | ) 96 | ``` 97 | 98 | After configuring your Python script, it is time to connect Tableau Prep. 99 | 100 | - Server: localhost 101 | - Port: 80 102 | - File Name: prepForward.py (if that is what you called it) 103 | - Function Name: prepGeo 104 | 105 | This configuation configuration should look like this: 106 | 107 | ![prepconfig](assets/prepconfig.png) 108 | 109 | Once input, your geocoding process will complete and return new data to your flow. 110 | 111 | ### Forward Geocoding Requirements 112 | 113 | Your data must contain a column titled `Address`. For the most accurate geocode, you should include as much geographic information (City, State, ZIP) in your Address. 114 | 115 | ### Reverse Geocoding Requirements 116 | 117 | Your data contain two columns, `Longitude` and `Latitude`. These columns can be of type float or string. 118 | 119 | ## Customization 120 | 121 | If you wish to alter the scripts or Docker image in any way, you can use the files found in `src`. 122 | 123 | ### Geocoding Customizations 124 | 125 | The forward and reverse scripts default to a global search and a limit of one. This means that the geocoder will find the *optimal single result* for your query. 126 | 127 | If you would like to refine your results, the Mapbox Geocoding API supports the following set of options. Edits to the default geocoding can be made at `src/geocode.py`. After edits, you must [rebuild the image](#build-and-connect) to validate. 128 | 129 | | Type | Filter | Description | 130 | |---------------------|-------------------|------------------------------------------------------------------------| 131 | | Forward and Reverse | Country Filtering | Specify country or list of countries to restrict results | 132 | | Forward and Reverse | Limit | Upper bound on number of results (max 10, default 1) | 133 | | Reverse | Type | Specify the type of result to return (country, postcode, address, etc) | 134 | | Forward | Bbox | Include only results that are within a given bounding box | 135 | | Forward | Proximity | Bias results that are closer to a given location | 136 | 137 | For more details on these options, consult the [Geocoding API documentation](https://docs.mapbox.com/api/search/). 138 | 139 | ### Build and Connect 140 | 141 | Once you have made your updates, run the following commands from within `src`. 142 | 143 | ```bash 144 | docker build --tag tabpy . 145 | docker run -d -p 80:80 tabpy 146 | ``` 147 | 148 | - docker build --tag tabpy: Build and tag your image as `tabpy` for running 149 | - docker run -d -p 80:80 tabpy: Run the imaged `tabpy` in detached (`d`) mode, mapping the container port 80 to localhost 80 (`p 80:80`). 150 | 151 | This will build and run the TabPy instance on your machine. To confirm that the image is running, either run `curl localhost/info` from your terminal or: 152 | 153 | ```bash 154 | docker ps 155 | ``` 156 | 157 | ![ps](assets/dockerps.png) 158 | 159 | Once you have confirmed that your image is running, open Tableau Prep 2019.3 (and above) and add a [Script node](https://help.tableau.com/current/prep/en-us/prep_scripts_TabPy.htm#add-a-script-to-your-flow). 160 | 161 | This will connect Prep to TabPy as indicated above. 162 | 163 | ### Customization Example 164 | 165 | For reverse geocoding, if you wish to filter to specific countries, you will either need to include a column in your data called "countries" (or similar) or hard code it into your script. All edits would need to be made to the [core geocoding script](src/geocode.py). 166 | 167 | **Column-based approach** 168 | 169 | ```python 170 | def geocodeReverse(input,token): 171 | url = "https://api.mapbox.com/geocoding/v5/mapbox.places-permanent/%s.json?access_token=%s&types=address&country=%s&limit=1" 172 | access_token = token 173 | input['coords'] = input['longitude'].map(str)+","+input['latitude'].map(str) 174 | coordinates = list(input["coords"]) 175 | countries = list(input["countries"]) 176 | urls = [url % (quote(list_item), access_token,countries) for list_item in coordinates] 177 | ``` 178 | 179 | **Hard code approach** 180 | 181 | ```python 182 | def geocodeReverse(input,token): 183 | url = "https://api.mapbox.com/geocoding/v5/mapbox.places-permanent/%s.json?access_token=%s&types=address&country=%s&limit=1" 184 | access_token = token 185 | input['coords'] = input['longitude'].map(str)+","+input['latitude'].map(str) 186 | coordinates = list(input["coords"]) 187 | countries = 'us' 188 | urls = [url % (quote(list_item), access_token,countries) for list_item in coordinates] 189 | ``` 190 | 191 | ### Rate Limiting 192 | 193 | The default rate limit for Geocoding API requsts is 600/minute. The included [Python scripts](src/geocode.py) enforce this limit with the [ratelimit](https://github.com/tomasbasham/ratelimit) package. If you exceed your rate limit, your results will fail. If you would like to know more about rate limiting, read the [Mapbox API documentation](https://docs.mapbox.com/api/#rate-limits). 194 | 195 | ```python 196 | # This is the rate limit period in seconds 197 | # Mapbox rate limits on a per-minute basis, with geocoding set at 600 requests per minute 198 | RATE_LIMIT = 600 199 | LIMIT_DURATION = 60 200 | 201 | # Set up rate limiting for API calls 202 | # If rate limit is exceeded, sleep the thread and wait until the LIMIT_DURATION has lapsed 203 | # If you request an increased rate limit, update RATE_LIMIT above 204 | @sleep_and_retry 205 | @limits(calls=RATE_LIMIT, period=LIMIT_DURATION) 206 | def req(url): 207 | try: 208 | with urlopen(url, context=ctx) as conn: 209 | return conn.read() 210 | except error.HTTPError as e: 211 | print(url) 212 | ``` 213 | 214 | `RATE_LIMIT` is the number of requests. `LIMIT_DURATION` is the amount of time that `RATE_LIMIT` is measured against. 215 | 216 | Our estimate is that a single thread will approach `1200` records per minute, meaning that any `RATE_LIMIT` above that value will be redundant as the script cannot process that much data. You can achieve better performance with threading. This is achieved in the following line of code: 217 | 218 | ```python 219 | with ThreadPoolExecutor(max_workers=1) as executor: 220 | ``` 221 | 222 | The parameter `max_workers=1` is how to control scale. So if you have a `RATE_LIMIT` of `2400`, you can set `max_workers` to 2 split the work across two parallel threads leading to roughly half the time to completion. Parallelism will scale linearly, but there are diminishing returns as thread values increase. 223 | 224 | ## Production 225 | 226 | If you will need geocoding at a scale beyond a single machine - you can deploy this solution into production. We provide you with a script to do this in `src/deploy`. 227 | 228 | ```bash 229 | cd src/deploy 230 | npm ci 231 | pulumi up -y 232 | ``` 233 | 234 | We use [Pulumi](https://www.pulumi.com/docs/index.html) to deploy our version of TabPy into AWS and wrap it behind a load balancer. This will give you a stable URL for sharing in your organization, and the ability to scale it up and down based on load. 235 | 236 | When deploying, Pulumi will build from the Dockerfile included in this repository, not the published one. 237 | 238 | **WARNING**: Mapbox rate limits are applied at the token level, so if you have a rate limit of 600 and deploy 4 instances of TabPy, you could quickly go over your limit without knowing. 239 | -------------------------------------------------------------------------------- /assets/arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/tableauGeocode/67f35dc8ea36e4a59a98cf43fae262e47b6e2ba3/assets/arch.png -------------------------------------------------------------------------------- /assets/dockerps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/tableauGeocode/67f35dc8ea36e4a59a98cf43fae262e47b6e2ba3/assets/dockerps.png -------------------------------------------------------------------------------- /assets/prepconfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/tableauGeocode/67f35dc8ea36e4a59a98cf43fae262e47b6e2ba3/assets/prepconfig.png -------------------------------------------------------------------------------- /assets/pyconfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/tableauGeocode/67f35dc8ea36e4a59a98cf43fae262e47b6e2ba3/assets/pyconfig.png -------------------------------------------------------------------------------- /assets/roles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/tableauGeocode/67f35dc8ea36e4a59a98cf43fae262e47b6e2ba3/assets/roles.png -------------------------------------------------------------------------------- /assets/video-thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/tableauGeocode/67f35dc8ea36e4a59a98cf43fae262e47b6e2ba3/assets/video-thumbnail.png -------------------------------------------------------------------------------- /src/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for TabPy 2 | # Setup 3 | FROM centos:7.6.1810 4 | RUN yum -y update 5 | RUN yum install git yum-utils groupinstall development which https://centos7.iuscommunity.org/ius-release.rpm -y 6 | RUN yum install python36u python36u-pip tmux -y 7 | 8 | # Python dependencies 9 | RUN python3 -m pip install --upgrade pip 10 | RUN python3 -m pip install pandas tabpy ratelimit 11 | 12 | # Where everything lives 13 | WORKDIR /TabPy 14 | COPY ./geocode.py ./geocode.py 15 | COPY ./startup.sh ./startup.sh 16 | COPY ./tabpy.conf ./tabpy.conf 17 | 18 | # Expose port 9004 19 | EXPOSE 80 20 | # Start TabPy 21 | CMD [ "./startup.sh" ] -------------------------------------------------------------------------------- /src/deploy/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /src/deploy/Pulumi.yaml: -------------------------------------------------------------------------------- 1 | name: deployGeocode 2 | runtime: nodejs 3 | description: A small script to deploy TabPy with Geocoding to AWS 4 | -------------------------------------------------------------------------------- /src/deploy/README.md: -------------------------------------------------------------------------------- 1 | # Deploying TabPy to AWS 2 | 3 | The code in this sub-folder will deploy TabPy to Amazon Web Services Elastic Container Service (ECS). It will also place a load balancer in front of your instances to provide users with a single URL for use. 4 | 5 | ## Install 6 | 7 | You must have Docker and Pulumi installed on your machine in order to use this script. You must also have an [AWS account](https://aws.amazon.com/getting-started/) with credentials available to your environment. 8 | 9 | To install: 10 | 11 | Mac 12 | 13 | ```bash 14 | brew install pulumi 15 | ``` 16 | 17 | PC 18 | 19 | ```bash 20 | @"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; iex ((New-Object System.Net.WebClient).DownloadString('https://get.pulumi.com/install.ps1'))" && SET "PATH=%PATH%;%USERPROFILE%\.pulumi\bin" 21 | ``` 22 | 23 | To install Docker, follow their [Getting Started](https://docs.docker.com/install/) guide. 24 | 25 | ## Deploy 26 | 27 | After cloning the repository 28 | 29 | ```bash 30 | cd src/deploy 31 | npm ci 32 | pulumi config set aws:region 33 | pulumi up -y 34 | ``` 35 | 36 | This will begin a Docker build and deployment to AWS. At the end of the process you will be shown a URL. That is the URL you will use in Tableau for geocoding (or any other TabPy needs). 37 | 38 | ## Details 39 | 40 | This script works as follows 41 | 42 | 1. Create AWS Fargate cluster. This is a cluster to place your TabPy container. Fargate manages scale and size for you, so you do not have to manage core resources. 43 | 2. Create Load Balancer. Your cluster will have between 1-N containers running on it, and the ALB will route requests to those containers as needed. 44 | 3. TabPy Service. The normal TabPy application is wrapped in Docker to make it simpler to deploy. It is configured to use port 80 for all communications. Fargate allows you to run as many replicas of your container as needed for scale. 45 | 46 | ```javascript 47 | const appService = new awsx.ecs.FargateService("tabpy-service", { 48 | cluster, 49 | taskDefinitionArgs: { 50 | container: { 51 | image: img, 52 | cpu: 1024 /*100% of 1024*/, 53 | memory: 2048 /*MB*/, 54 | portMappings: [web] 55 | } 56 | }, 57 | ///This is where you define how many replicas you want running at all times. 58 | desiredCount: 5 59 | }); 60 | ``` 61 | 62 | ## Teardown 63 | 64 | If you wish to remove the stack, run `pulumi destroy -y`. 65 | -------------------------------------------------------------------------------- /src/deploy/index.js: -------------------------------------------------------------------------------- 1 | const awsx = require("@pulumi/awsx"); 2 | // Step 1: Create an ECS Fargate cluster. 3 | const cluster = new awsx.ecs.Cluster("cluster"); 4 | // Step 2: Define the Networking for our service. 5 | const alb = new awsx.lb.ApplicationLoadBalancer("tabPyLb", { 6 | external: true, 7 | securityGroup: cluster.securityGroup 8 | }); 9 | const web = alb.createListener("web", { port: 80, external: true }); 10 | // Step 3: Build and publish a Docker image to a private ECR registry. 11 | const img = awsx.ecs.Image.fromPath("tabpy", "../"); 12 | // Step 4: Create a Fargate service task that can scale out. 13 | const appService = new awsx.ecs.FargateService("tabpy-service", { 14 | cluster, 15 | taskDefinitionArgs: { 16 | container: { 17 | image: img, 18 | cpu: 1024 /*100% of 1024*/, 19 | memory: 2048 /*MB*/, 20 | portMappings: [web] 21 | } 22 | }, 23 | desiredCount: 5 24 | }); 25 | // Step 5: Export the Internet address for the service. 26 | exports.url = web.endpoint.hostname; 27 | -------------------------------------------------------------------------------- /src/deploy/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-javascript", 3 | "requires": true, 4 | "lockfileVersion": 1, 5 | "dependencies": { 6 | "@protobufjs/aspromise": { 7 | "version": "1.1.2", 8 | "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", 9 | "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" 10 | }, 11 | "@protobufjs/base64": { 12 | "version": "1.1.2", 13 | "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", 14 | "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" 15 | }, 16 | "@protobufjs/codegen": { 17 | "version": "2.0.4", 18 | "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", 19 | "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" 20 | }, 21 | "@protobufjs/eventemitter": { 22 | "version": "1.1.0", 23 | "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", 24 | "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" 25 | }, 26 | "@protobufjs/fetch": { 27 | "version": "1.1.0", 28 | "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", 29 | "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", 30 | "requires": { 31 | "@protobufjs/aspromise": "^1.1.1", 32 | "@protobufjs/inquire": "^1.1.0" 33 | } 34 | }, 35 | "@protobufjs/float": { 36 | "version": "1.0.2", 37 | "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", 38 | "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" 39 | }, 40 | "@protobufjs/inquire": { 41 | "version": "1.1.0", 42 | "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", 43 | "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" 44 | }, 45 | "@protobufjs/path": { 46 | "version": "1.1.2", 47 | "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", 48 | "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" 49 | }, 50 | "@protobufjs/pool": { 51 | "version": "1.1.0", 52 | "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", 53 | "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" 54 | }, 55 | "@protobufjs/utf8": { 56 | "version": "1.1.0", 57 | "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", 58 | "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" 59 | }, 60 | "@pulumi/aws": { 61 | "version": "1.7.0", 62 | "resolved": "https://registry.npmjs.org/@pulumi/aws/-/aws-1.7.0.tgz", 63 | "integrity": "sha512-mDgGsQeS8YxP6RFmbgtCNdM+dyq/aFzEz54NAyICCW41nCzyx5R7Lpp3kPGKMqgpmDRkdfq60/75h8+SGJGnUQ==", 64 | "requires": { 65 | "@pulumi/pulumi": "^1.0.0", 66 | "aws-sdk": "^2.0.0", 67 | "builtin-modules": "3.0.0", 68 | "mime": "^2.0.0", 69 | "read-package-tree": "^5.2.1", 70 | "resolve": "^1.7.1" 71 | } 72 | }, 73 | "@pulumi/awsx": { 74 | "version": "0.18.12", 75 | "resolved": "https://registry.npmjs.org/@pulumi/awsx/-/awsx-0.18.12.tgz", 76 | "integrity": "sha512-Do45Ort9d0RryJSADRtUtQqaF+4cbcLkeDA+bCwXdQBZaJqbFFVqoJIQHWYhU00P6CVPU0linV+ADqsUoCy3FQ==", 77 | "requires": { 78 | "@pulumi/aws": "^1.0.0", 79 | "@pulumi/docker": "^0.17.3", 80 | "@pulumi/pulumi": "^1.0.0", 81 | "@types/aws-lambda": "^8.10.23", 82 | "deasync": "^0.1.15", 83 | "mime": "^2.0.0" 84 | } 85 | }, 86 | "@pulumi/docker": { 87 | "version": "0.17.4", 88 | "resolved": "https://registry.npmjs.org/@pulumi/docker/-/docker-0.17.4.tgz", 89 | "integrity": "sha512-uGnt9VEGXxCFTpIkCrULP2XTbSEJMH3vQrOq9Q+ATTnIxswlG0z/LkpuktEB6YmvnQqEUtTPPEM+p/RmLkqLrA==", 90 | "requires": { 91 | "@pulumi/pulumi": "^1.0.0", 92 | "semver": "^5.4.0" 93 | }, 94 | "dependencies": { 95 | "semver": { 96 | "version": "5.7.1", 97 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 98 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 99 | } 100 | } 101 | }, 102 | "@pulumi/pulumi": { 103 | "version": "1.4.0", 104 | "resolved": "https://registry.npmjs.org/@pulumi/pulumi/-/pulumi-1.4.0.tgz", 105 | "integrity": "sha512-BGFIwjMCaJCXLwZDB4PoCtkTc40cGvL99pgNg1KRmr6GccYw98nA70vZzqktYsyFuZOX6CzzFRo8Tgigx8+awQ==", 106 | "requires": { 107 | "@pulumi/query": "^0.3.0", 108 | "deasync": "^0.1.15", 109 | "google-protobuf": "^3.5.0", 110 | "grpc": "1.21.1", 111 | "minimist": "^1.2.0", 112 | "normalize-package-data": "^2.4.0", 113 | "protobufjs": "^6.8.6", 114 | "read-package-tree": "^5.3.1", 115 | "require-from-string": "^2.0.1", 116 | "semver": "^6.1.0", 117 | "source-map-support": "^0.4.16", 118 | "ts-node": "^7.0.0", 119 | "typescript": "^3.0.0", 120 | "upath": "^1.1.0" 121 | } 122 | }, 123 | "@pulumi/query": { 124 | "version": "0.3.0", 125 | "resolved": "https://registry.npmjs.org/@pulumi/query/-/query-0.3.0.tgz", 126 | "integrity": "sha512-xfo+yLRM2zVjVEA4p23IjQWzyWl1ZhWOGobsBqRpIarzLvwNH/RAGaoehdxlhx4X92302DrpdIFgTICMN4P38w==" 127 | }, 128 | "@types/aws-lambda": { 129 | "version": "8.10.34", 130 | "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.34.tgz", 131 | "integrity": "sha512-ewsBa0SjQYywUpDwck+4n89T0pXcRtIfIXi0aK/3qIQJNBoGvX7AKqFya9FWKdoG7ZcK3Iarl4QjlJiAW7jgPg==" 132 | }, 133 | "@types/long": { 134 | "version": "4.0.0", 135 | "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz", 136 | "integrity": "sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==" 137 | }, 138 | "@types/node": { 139 | "version": "10.17.0", 140 | "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.0.tgz", 141 | "integrity": "sha512-wuJwN2KV4tIRz1bu9vq5kSPasJ8IsEjZaP1ZR7KlmdUZvGF/rXy8DmXOVwUD0kAtvtJ7aqMKPqUXC0NUTDbrDg==" 142 | }, 143 | "ansi-regex": { 144 | "version": "2.1.1", 145 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 146 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 147 | }, 148 | "arrify": { 149 | "version": "1.0.1", 150 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", 151 | "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" 152 | }, 153 | "asap": { 154 | "version": "2.0.6", 155 | "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", 156 | "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" 157 | }, 158 | "ascli": { 159 | "version": "1.0.1", 160 | "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz", 161 | "integrity": "sha1-vPpZdKYvGOgcq660lzKrSoj5Brw=", 162 | "requires": { 163 | "colour": "~0.7.1", 164 | "optjs": "~3.2.2" 165 | } 166 | }, 167 | "aws-sdk": { 168 | "version": "2.558.0", 169 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.558.0.tgz", 170 | "integrity": "sha512-L1cqcOLuL4FG93JKQIh/5u/ks9zj3ErahzfAb7vjuSLVZgEXx8mNgdiyIgcWj5VDOBEa6ygzwV1f72rDig359A==", 171 | "requires": { 172 | "buffer": "4.9.1", 173 | "events": "1.1.1", 174 | "ieee754": "1.1.13", 175 | "jmespath": "0.15.0", 176 | "querystring": "0.2.0", 177 | "sax": "1.2.1", 178 | "url": "0.10.3", 179 | "uuid": "3.3.2", 180 | "xml2js": "0.4.19" 181 | } 182 | }, 183 | "balanced-match": { 184 | "version": "1.0.0", 185 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 186 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 187 | }, 188 | "base64-js": { 189 | "version": "1.3.1", 190 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", 191 | "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" 192 | }, 193 | "bindings": { 194 | "version": "1.2.1", 195 | "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", 196 | "integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE=" 197 | }, 198 | "brace-expansion": { 199 | "version": "1.1.11", 200 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 201 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 202 | "requires": { 203 | "balanced-match": "^1.0.0", 204 | "concat-map": "0.0.1" 205 | } 206 | }, 207 | "buffer": { 208 | "version": "4.9.1", 209 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", 210 | "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", 211 | "requires": { 212 | "base64-js": "^1.0.2", 213 | "ieee754": "^1.1.4", 214 | "isarray": "^1.0.0" 215 | } 216 | }, 217 | "buffer-from": { 218 | "version": "1.1.1", 219 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 220 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" 221 | }, 222 | "builtin-modules": { 223 | "version": "3.0.0", 224 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.0.0.tgz", 225 | "integrity": "sha512-hMIeU4K2ilbXV6Uv93ZZ0Avg/M91RaKXucQ+4me2Do1txxBDyDZWCBa5bJSLqoNTRpXTLwEzIk1KmloenDDjhg==" 226 | }, 227 | "bytebuffer": { 228 | "version": "5.0.1", 229 | "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", 230 | "integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=", 231 | "requires": { 232 | "long": "~3" 233 | } 234 | }, 235 | "camelcase": { 236 | "version": "2.1.1", 237 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", 238 | "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" 239 | }, 240 | "cliui": { 241 | "version": "3.2.0", 242 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", 243 | "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", 244 | "requires": { 245 | "string-width": "^1.0.1", 246 | "strip-ansi": "^3.0.1", 247 | "wrap-ansi": "^2.0.0" 248 | } 249 | }, 250 | "code-point-at": { 251 | "version": "1.1.0", 252 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 253 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" 254 | }, 255 | "colour": { 256 | "version": "0.7.1", 257 | "resolved": "https://registry.npmjs.org/colour/-/colour-0.7.1.tgz", 258 | "integrity": "sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g=" 259 | }, 260 | "concat-map": { 261 | "version": "0.0.1", 262 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 263 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 264 | }, 265 | "deasync": { 266 | "version": "0.1.15", 267 | "resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.15.tgz", 268 | "integrity": "sha512-pxMaCYu8cQIbGkA4Y1R0PLSooPIpH1WgFBLeJ+zLxQgHfkZG86ViJSmZmONSjZJ/R3NjwkMcIWZAzpLB2G9/CA==", 269 | "requires": { 270 | "bindings": "~1.2.1", 271 | "node-addon-api": "^1.6.0" 272 | } 273 | }, 274 | "debuglog": { 275 | "version": "1.0.1", 276 | "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", 277 | "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=" 278 | }, 279 | "decamelize": { 280 | "version": "1.2.0", 281 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 282 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" 283 | }, 284 | "define-properties": { 285 | "version": "1.1.3", 286 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 287 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", 288 | "requires": { 289 | "object-keys": "^1.0.12" 290 | } 291 | }, 292 | "dezalgo": { 293 | "version": "1.0.3", 294 | "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", 295 | "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", 296 | "requires": { 297 | "asap": "^2.0.0", 298 | "wrappy": "1" 299 | } 300 | }, 301 | "diff": { 302 | "version": "3.5.0", 303 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 304 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" 305 | }, 306 | "es-abstract": { 307 | "version": "1.16.0", 308 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.0.tgz", 309 | "integrity": "sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==", 310 | "requires": { 311 | "es-to-primitive": "^1.2.0", 312 | "function-bind": "^1.1.1", 313 | "has": "^1.0.3", 314 | "has-symbols": "^1.0.0", 315 | "is-callable": "^1.1.4", 316 | "is-regex": "^1.0.4", 317 | "object-inspect": "^1.6.0", 318 | "object-keys": "^1.1.1", 319 | "string.prototype.trimleft": "^2.1.0", 320 | "string.prototype.trimright": "^2.1.0" 321 | } 322 | }, 323 | "es-to-primitive": { 324 | "version": "1.2.0", 325 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", 326 | "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", 327 | "requires": { 328 | "is-callable": "^1.1.4", 329 | "is-date-object": "^1.0.1", 330 | "is-symbol": "^1.0.2" 331 | } 332 | }, 333 | "events": { 334 | "version": "1.1.1", 335 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", 336 | "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" 337 | }, 338 | "fs.realpath": { 339 | "version": "1.0.0", 340 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 341 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 342 | }, 343 | "function-bind": { 344 | "version": "1.1.1", 345 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 346 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 347 | }, 348 | "glob": { 349 | "version": "7.1.5", 350 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz", 351 | "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==", 352 | "requires": { 353 | "fs.realpath": "^1.0.0", 354 | "inflight": "^1.0.4", 355 | "inherits": "2", 356 | "minimatch": "^3.0.4", 357 | "once": "^1.3.0", 358 | "path-is-absolute": "^1.0.0" 359 | } 360 | }, 361 | "google-protobuf": { 362 | "version": "3.10.0", 363 | "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.10.0.tgz", 364 | "integrity": "sha512-d0cMO8TJ6xtB/WrVHCv5U81L2ulX+aCD58IljyAN6mHwdHHJ2jbcauX5glvivi3s3hx7EYEo7eUA9WftzamMnw==" 365 | }, 366 | "graceful-fs": { 367 | "version": "4.2.3", 368 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", 369 | "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" 370 | }, 371 | "grpc": { 372 | "version": "1.21.1", 373 | "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.21.1.tgz", 374 | "integrity": "sha512-PFsZQazf62nP05a0xm23mlImMuw5oVlqF/0zakmsdqJgvbABe+d6VThY2PfhqJmWEL/FhQ6QNYsxS5EAM6++7g==", 375 | "requires": { 376 | "lodash.camelcase": "^4.3.0", 377 | "lodash.clone": "^4.5.0", 378 | "nan": "^2.13.2", 379 | "node-pre-gyp": "^0.13.0", 380 | "protobufjs": "^5.0.3" 381 | }, 382 | "dependencies": { 383 | "abbrev": { 384 | "version": "1.1.1", 385 | "bundled": true 386 | }, 387 | "ansi-regex": { 388 | "version": "2.1.1", 389 | "bundled": true 390 | }, 391 | "aproba": { 392 | "version": "1.2.0", 393 | "bundled": true 394 | }, 395 | "are-we-there-yet": { 396 | "version": "1.1.5", 397 | "bundled": true, 398 | "requires": { 399 | "delegates": "^1.0.0", 400 | "readable-stream": "^2.0.6" 401 | } 402 | }, 403 | "balanced-match": { 404 | "version": "1.0.0", 405 | "bundled": true 406 | }, 407 | "brace-expansion": { 408 | "version": "1.1.11", 409 | "bundled": true, 410 | "requires": { 411 | "balanced-match": "^1.0.0", 412 | "concat-map": "0.0.1" 413 | } 414 | }, 415 | "chownr": { 416 | "version": "1.1.1", 417 | "bundled": true 418 | }, 419 | "code-point-at": { 420 | "version": "1.1.0", 421 | "bundled": true 422 | }, 423 | "concat-map": { 424 | "version": "0.0.1", 425 | "bundled": true 426 | }, 427 | "console-control-strings": { 428 | "version": "1.1.0", 429 | "bundled": true 430 | }, 431 | "core-util-is": { 432 | "version": "1.0.2", 433 | "bundled": true 434 | }, 435 | "deep-extend": { 436 | "version": "0.6.0", 437 | "bundled": true 438 | }, 439 | "delegates": { 440 | "version": "1.0.0", 441 | "bundled": true 442 | }, 443 | "detect-libc": { 444 | "version": "1.0.3", 445 | "bundled": true 446 | }, 447 | "fs-minipass": { 448 | "version": "1.2.5", 449 | "bundled": true, 450 | "requires": { 451 | "minipass": "^2.2.1" 452 | } 453 | }, 454 | "fs.realpath": { 455 | "version": "1.0.0", 456 | "bundled": true 457 | }, 458 | "gauge": { 459 | "version": "2.7.4", 460 | "bundled": true, 461 | "requires": { 462 | "aproba": "^1.0.3", 463 | "console-control-strings": "^1.0.0", 464 | "has-unicode": "^2.0.0", 465 | "object-assign": "^4.1.0", 466 | "signal-exit": "^3.0.0", 467 | "string-width": "^1.0.1", 468 | "strip-ansi": "^3.0.1", 469 | "wide-align": "^1.1.0" 470 | } 471 | }, 472 | "has-unicode": { 473 | "version": "2.0.1", 474 | "bundled": true 475 | }, 476 | "iconv-lite": { 477 | "version": "0.4.23", 478 | "bundled": true, 479 | "requires": { 480 | "safer-buffer": ">= 2.1.2 < 3" 481 | } 482 | }, 483 | "ignore-walk": { 484 | "version": "3.0.1", 485 | "bundled": true, 486 | "requires": { 487 | "minimatch": "^3.0.4" 488 | } 489 | }, 490 | "inflight": { 491 | "version": "1.0.6", 492 | "bundled": true, 493 | "requires": { 494 | "once": "^1.3.0", 495 | "wrappy": "1" 496 | } 497 | }, 498 | "inherits": { 499 | "version": "2.0.3", 500 | "bundled": true 501 | }, 502 | "ini": { 503 | "version": "1.3.5", 504 | "bundled": true 505 | }, 506 | "is-fullwidth-code-point": { 507 | "version": "1.0.0", 508 | "bundled": true, 509 | "requires": { 510 | "number-is-nan": "^1.0.0" 511 | } 512 | }, 513 | "isarray": { 514 | "version": "1.0.0", 515 | "bundled": true 516 | }, 517 | "minimatch": { 518 | "version": "3.0.4", 519 | "bundled": true, 520 | "requires": { 521 | "brace-expansion": "^1.1.7" 522 | } 523 | }, 524 | "minimist": { 525 | "version": "1.2.0", 526 | "bundled": true 527 | }, 528 | "minipass": { 529 | "version": "2.3.5", 530 | "bundled": true, 531 | "requires": { 532 | "safe-buffer": "^5.1.2", 533 | "yallist": "^3.0.0" 534 | } 535 | }, 536 | "minizlib": { 537 | "version": "1.2.1", 538 | "bundled": true, 539 | "requires": { 540 | "minipass": "^2.2.1" 541 | } 542 | }, 543 | "mkdirp": { 544 | "version": "0.5.1", 545 | "bundled": true, 546 | "requires": { 547 | "minimist": "0.0.8" 548 | }, 549 | "dependencies": { 550 | "minimist": { 551 | "version": "0.0.8", 552 | "bundled": true 553 | } 554 | } 555 | }, 556 | "needle": { 557 | "version": "2.3.1", 558 | "bundled": true, 559 | "requires": { 560 | "debug": "^4.1.0", 561 | "iconv-lite": "^0.4.4", 562 | "sax": "^1.2.4" 563 | }, 564 | "dependencies": { 565 | "debug": { 566 | "version": "4.1.1", 567 | "bundled": true, 568 | "requires": { 569 | "ms": "^2.1.1" 570 | } 571 | }, 572 | "ms": { 573 | "version": "2.1.1", 574 | "bundled": true 575 | } 576 | } 577 | }, 578 | "node-pre-gyp": { 579 | "version": "0.13.0", 580 | "bundled": true, 581 | "requires": { 582 | "detect-libc": "^1.0.2", 583 | "mkdirp": "^0.5.1", 584 | "needle": "^2.2.1", 585 | "nopt": "^4.0.1", 586 | "npm-packlist": "^1.1.6", 587 | "npmlog": "^4.0.2", 588 | "rc": "^1.2.7", 589 | "rimraf": "^2.6.1", 590 | "semver": "^5.3.0", 591 | "tar": "^4" 592 | } 593 | }, 594 | "nopt": { 595 | "version": "4.0.1", 596 | "bundled": true, 597 | "requires": { 598 | "abbrev": "1", 599 | "osenv": "^0.1.4" 600 | } 601 | }, 602 | "npm-bundled": { 603 | "version": "1.0.6", 604 | "bundled": true 605 | }, 606 | "npm-packlist": { 607 | "version": "1.4.1", 608 | "bundled": true, 609 | "requires": { 610 | "ignore-walk": "^3.0.1", 611 | "npm-bundled": "^1.0.1" 612 | } 613 | }, 614 | "npmlog": { 615 | "version": "4.1.2", 616 | "bundled": true, 617 | "requires": { 618 | "are-we-there-yet": "~1.1.2", 619 | "console-control-strings": "~1.1.0", 620 | "gauge": "~2.7.3", 621 | "set-blocking": "~2.0.0" 622 | } 623 | }, 624 | "number-is-nan": { 625 | "version": "1.0.1", 626 | "bundled": true 627 | }, 628 | "object-assign": { 629 | "version": "4.1.1", 630 | "bundled": true 631 | }, 632 | "once": { 633 | "version": "1.4.0", 634 | "bundled": true, 635 | "requires": { 636 | "wrappy": "1" 637 | } 638 | }, 639 | "os-homedir": { 640 | "version": "1.0.2", 641 | "bundled": true 642 | }, 643 | "os-tmpdir": { 644 | "version": "1.0.2", 645 | "bundled": true 646 | }, 647 | "osenv": { 648 | "version": "0.1.5", 649 | "bundled": true, 650 | "requires": { 651 | "os-homedir": "^1.0.0", 652 | "os-tmpdir": "^1.0.0" 653 | } 654 | }, 655 | "path-is-absolute": { 656 | "version": "1.0.1", 657 | "bundled": true 658 | }, 659 | "process-nextick-args": { 660 | "version": "2.0.0", 661 | "bundled": true 662 | }, 663 | "protobufjs": { 664 | "version": "5.0.3", 665 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.3.tgz", 666 | "integrity": "sha512-55Kcx1MhPZX0zTbVosMQEO5R6/rikNXd9b6RQK4KSPcrSIIwoXTtebIczUrXlwaSrbz4x8XUVThGPob1n8I4QA==", 667 | "requires": { 668 | "ascli": "~1", 669 | "bytebuffer": "~5", 670 | "glob": "^7.0.5", 671 | "yargs": "^3.10.0" 672 | } 673 | }, 674 | "rc": { 675 | "version": "1.2.8", 676 | "bundled": true, 677 | "requires": { 678 | "deep-extend": "^0.6.0", 679 | "ini": "~1.3.0", 680 | "minimist": "^1.2.0", 681 | "strip-json-comments": "~2.0.1" 682 | } 683 | }, 684 | "readable-stream": { 685 | "version": "2.3.6", 686 | "bundled": true, 687 | "requires": { 688 | "core-util-is": "~1.0.0", 689 | "inherits": "~2.0.3", 690 | "isarray": "~1.0.0", 691 | "process-nextick-args": "~2.0.0", 692 | "safe-buffer": "~5.1.1", 693 | "string_decoder": "~1.1.1", 694 | "util-deprecate": "~1.0.1" 695 | } 696 | }, 697 | "rimraf": { 698 | "version": "2.6.3", 699 | "bundled": true, 700 | "requires": { 701 | "glob": "^7.1.3" 702 | }, 703 | "dependencies": { 704 | "glob": { 705 | "version": "7.1.4", 706 | "bundled": true, 707 | "requires": { 708 | "fs.realpath": "^1.0.0", 709 | "inflight": "^1.0.4", 710 | "inherits": "2", 711 | "minimatch": "^3.0.4", 712 | "once": "^1.3.0", 713 | "path-is-absolute": "^1.0.0" 714 | } 715 | } 716 | } 717 | }, 718 | "safe-buffer": { 719 | "version": "5.1.2", 720 | "bundled": true 721 | }, 722 | "safer-buffer": { 723 | "version": "2.1.2", 724 | "bundled": true 725 | }, 726 | "sax": { 727 | "version": "1.2.4", 728 | "bundled": true 729 | }, 730 | "semver": { 731 | "version": "5.7.0", 732 | "bundled": true 733 | }, 734 | "set-blocking": { 735 | "version": "2.0.0", 736 | "bundled": true 737 | }, 738 | "signal-exit": { 739 | "version": "3.0.2", 740 | "bundled": true 741 | }, 742 | "string-width": { 743 | "version": "1.0.2", 744 | "bundled": true, 745 | "requires": { 746 | "code-point-at": "^1.0.0", 747 | "is-fullwidth-code-point": "^1.0.0", 748 | "strip-ansi": "^3.0.0" 749 | } 750 | }, 751 | "string_decoder": { 752 | "version": "1.1.1", 753 | "bundled": true, 754 | "requires": { 755 | "safe-buffer": "~5.1.0" 756 | } 757 | }, 758 | "strip-ansi": { 759 | "version": "3.0.1", 760 | "bundled": true, 761 | "requires": { 762 | "ansi-regex": "^2.0.0" 763 | } 764 | }, 765 | "strip-json-comments": { 766 | "version": "2.0.1", 767 | "bundled": true 768 | }, 769 | "tar": { 770 | "version": "4.4.8", 771 | "bundled": true, 772 | "requires": { 773 | "chownr": "^1.1.1", 774 | "fs-minipass": "^1.2.5", 775 | "minipass": "^2.3.4", 776 | "minizlib": "^1.1.1", 777 | "mkdirp": "^0.5.0", 778 | "safe-buffer": "^5.1.2", 779 | "yallist": "^3.0.2" 780 | } 781 | }, 782 | "util-deprecate": { 783 | "version": "1.0.2", 784 | "bundled": true 785 | }, 786 | "wide-align": { 787 | "version": "1.1.3", 788 | "bundled": true, 789 | "requires": { 790 | "string-width": "^1.0.2 || 2" 791 | } 792 | }, 793 | "wrappy": { 794 | "version": "1.0.2", 795 | "bundled": true 796 | }, 797 | "yallist": { 798 | "version": "3.0.3", 799 | "bundled": true 800 | } 801 | } 802 | }, 803 | "has": { 804 | "version": "1.0.3", 805 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 806 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 807 | "requires": { 808 | "function-bind": "^1.1.1" 809 | } 810 | }, 811 | "has-symbols": { 812 | "version": "1.0.0", 813 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", 814 | "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" 815 | }, 816 | "hosted-git-info": { 817 | "version": "2.8.5", 818 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", 819 | "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==" 820 | }, 821 | "ieee754": { 822 | "version": "1.1.13", 823 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", 824 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" 825 | }, 826 | "inflight": { 827 | "version": "1.0.6", 828 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 829 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 830 | "requires": { 831 | "once": "^1.3.0", 832 | "wrappy": "1" 833 | } 834 | }, 835 | "inherits": { 836 | "version": "2.0.4", 837 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 838 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 839 | }, 840 | "invert-kv": { 841 | "version": "1.0.0", 842 | "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", 843 | "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" 844 | }, 845 | "is-callable": { 846 | "version": "1.1.4", 847 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", 848 | "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" 849 | }, 850 | "is-date-object": { 851 | "version": "1.0.1", 852 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", 853 | "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" 854 | }, 855 | "is-fullwidth-code-point": { 856 | "version": "1.0.0", 857 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 858 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 859 | "requires": { 860 | "number-is-nan": "^1.0.0" 861 | } 862 | }, 863 | "is-regex": { 864 | "version": "1.0.4", 865 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", 866 | "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", 867 | "requires": { 868 | "has": "^1.0.1" 869 | } 870 | }, 871 | "is-symbol": { 872 | "version": "1.0.2", 873 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", 874 | "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", 875 | "requires": { 876 | "has-symbols": "^1.0.0" 877 | } 878 | }, 879 | "isarray": { 880 | "version": "1.0.0", 881 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 882 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 883 | }, 884 | "jmespath": { 885 | "version": "0.15.0", 886 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", 887 | "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" 888 | }, 889 | "json-parse-better-errors": { 890 | "version": "1.0.2", 891 | "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", 892 | "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" 893 | }, 894 | "lcid": { 895 | "version": "1.0.0", 896 | "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", 897 | "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", 898 | "requires": { 899 | "invert-kv": "^1.0.0" 900 | } 901 | }, 902 | "lodash.camelcase": { 903 | "version": "4.3.0", 904 | "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", 905 | "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" 906 | }, 907 | "lodash.clone": { 908 | "version": "4.5.0", 909 | "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", 910 | "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=" 911 | }, 912 | "long": { 913 | "version": "3.2.0", 914 | "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", 915 | "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=" 916 | }, 917 | "make-error": { 918 | "version": "1.3.5", 919 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", 920 | "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==" 921 | }, 922 | "mime": { 923 | "version": "2.4.4", 924 | "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", 925 | "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" 926 | }, 927 | "minimatch": { 928 | "version": "3.0.4", 929 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 930 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 931 | "requires": { 932 | "brace-expansion": "^1.1.7" 933 | } 934 | }, 935 | "minimist": { 936 | "version": "1.2.0", 937 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 938 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" 939 | }, 940 | "mkdirp": { 941 | "version": "0.5.1", 942 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 943 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 944 | "requires": { 945 | "minimist": "0.0.8" 946 | }, 947 | "dependencies": { 948 | "minimist": { 949 | "version": "0.0.8", 950 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 951 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 952 | } 953 | } 954 | }, 955 | "nan": { 956 | "version": "2.14.0", 957 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", 958 | "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" 959 | }, 960 | "node-addon-api": { 961 | "version": "1.7.1", 962 | "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.1.tgz", 963 | "integrity": "sha512-2+DuKodWvwRTrCfKOeR24KIc5unKjOh8mz17NCzVnHWfjAdDqbfbjqh7gUT+BkXBRQM52+xCHciKWonJ3CbJMQ==" 964 | }, 965 | "normalize-package-data": { 966 | "version": "2.5.0", 967 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", 968 | "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", 969 | "requires": { 970 | "hosted-git-info": "^2.1.4", 971 | "resolve": "^1.10.0", 972 | "semver": "2 || 3 || 4 || 5", 973 | "validate-npm-package-license": "^3.0.1" 974 | }, 975 | "dependencies": { 976 | "semver": { 977 | "version": "5.7.1", 978 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 979 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 980 | } 981 | } 982 | }, 983 | "number-is-nan": { 984 | "version": "1.0.1", 985 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 986 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" 987 | }, 988 | "object-inspect": { 989 | "version": "1.6.0", 990 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", 991 | "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==" 992 | }, 993 | "object-keys": { 994 | "version": "1.1.1", 995 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 996 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" 997 | }, 998 | "object.getownpropertydescriptors": { 999 | "version": "2.0.3", 1000 | "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", 1001 | "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", 1002 | "requires": { 1003 | "define-properties": "^1.1.2", 1004 | "es-abstract": "^1.5.1" 1005 | } 1006 | }, 1007 | "once": { 1008 | "version": "1.4.0", 1009 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1010 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1011 | "requires": { 1012 | "wrappy": "1" 1013 | } 1014 | }, 1015 | "optjs": { 1016 | "version": "3.2.2", 1017 | "resolved": "https://registry.npmjs.org/optjs/-/optjs-3.2.2.tgz", 1018 | "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4=" 1019 | }, 1020 | "os-locale": { 1021 | "version": "1.4.0", 1022 | "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", 1023 | "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", 1024 | "requires": { 1025 | "lcid": "^1.0.0" 1026 | } 1027 | }, 1028 | "path-is-absolute": { 1029 | "version": "1.0.1", 1030 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1031 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 1032 | }, 1033 | "path-parse": { 1034 | "version": "1.0.6", 1035 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 1036 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" 1037 | }, 1038 | "protobufjs": { 1039 | "version": "6.8.8", 1040 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", 1041 | "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", 1042 | "requires": { 1043 | "@protobufjs/aspromise": "^1.1.2", 1044 | "@protobufjs/base64": "^1.1.2", 1045 | "@protobufjs/codegen": "^2.0.4", 1046 | "@protobufjs/eventemitter": "^1.1.0", 1047 | "@protobufjs/fetch": "^1.1.0", 1048 | "@protobufjs/float": "^1.0.2", 1049 | "@protobufjs/inquire": "^1.1.0", 1050 | "@protobufjs/path": "^1.1.2", 1051 | "@protobufjs/pool": "^1.1.0", 1052 | "@protobufjs/utf8": "^1.1.0", 1053 | "@types/long": "^4.0.0", 1054 | "@types/node": "^10.1.0", 1055 | "long": "^4.0.0" 1056 | }, 1057 | "dependencies": { 1058 | "long": { 1059 | "version": "4.0.0", 1060 | "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", 1061 | "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" 1062 | } 1063 | } 1064 | }, 1065 | "punycode": { 1066 | "version": "1.3.2", 1067 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", 1068 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" 1069 | }, 1070 | "querystring": { 1071 | "version": "0.2.0", 1072 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", 1073 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" 1074 | }, 1075 | "read-package-json": { 1076 | "version": "2.1.0", 1077 | "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.0.tgz", 1078 | "integrity": "sha512-KLhu8M1ZZNkMcrq1+0UJbR8Dii8KZUqB0Sha4mOx/bknfKI/fyrQVrG/YIt2UOtG667sD8+ee4EXMM91W9dC+A==", 1079 | "requires": { 1080 | "glob": "^7.1.1", 1081 | "graceful-fs": "^4.1.2", 1082 | "json-parse-better-errors": "^1.0.1", 1083 | "normalize-package-data": "^2.0.0", 1084 | "slash": "^1.0.0" 1085 | } 1086 | }, 1087 | "read-package-tree": { 1088 | "version": "5.3.1", 1089 | "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz", 1090 | "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==", 1091 | "requires": { 1092 | "read-package-json": "^2.0.0", 1093 | "readdir-scoped-modules": "^1.0.0", 1094 | "util-promisify": "^2.1.0" 1095 | } 1096 | }, 1097 | "readdir-scoped-modules": { 1098 | "version": "1.1.0", 1099 | "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", 1100 | "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", 1101 | "requires": { 1102 | "debuglog": "^1.0.1", 1103 | "dezalgo": "^1.0.0", 1104 | "graceful-fs": "^4.1.2", 1105 | "once": "^1.3.0" 1106 | } 1107 | }, 1108 | "require-from-string": { 1109 | "version": "2.0.2", 1110 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", 1111 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" 1112 | }, 1113 | "resolve": { 1114 | "version": "1.12.0", 1115 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", 1116 | "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", 1117 | "requires": { 1118 | "path-parse": "^1.0.6" 1119 | } 1120 | }, 1121 | "sax": { 1122 | "version": "1.2.1", 1123 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", 1124 | "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" 1125 | }, 1126 | "semver": { 1127 | "version": "6.3.0", 1128 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 1129 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" 1130 | }, 1131 | "slash": { 1132 | "version": "1.0.0", 1133 | "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", 1134 | "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=" 1135 | }, 1136 | "source-map": { 1137 | "version": "0.5.7", 1138 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 1139 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" 1140 | }, 1141 | "source-map-support": { 1142 | "version": "0.4.18", 1143 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", 1144 | "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", 1145 | "requires": { 1146 | "source-map": "^0.5.6" 1147 | } 1148 | }, 1149 | "spdx-correct": { 1150 | "version": "3.1.0", 1151 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", 1152 | "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", 1153 | "requires": { 1154 | "spdx-expression-parse": "^3.0.0", 1155 | "spdx-license-ids": "^3.0.0" 1156 | } 1157 | }, 1158 | "spdx-exceptions": { 1159 | "version": "2.2.0", 1160 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", 1161 | "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" 1162 | }, 1163 | "spdx-expression-parse": { 1164 | "version": "3.0.0", 1165 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", 1166 | "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", 1167 | "requires": { 1168 | "spdx-exceptions": "^2.1.0", 1169 | "spdx-license-ids": "^3.0.0" 1170 | } 1171 | }, 1172 | "spdx-license-ids": { 1173 | "version": "3.0.5", 1174 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", 1175 | "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" 1176 | }, 1177 | "string-width": { 1178 | "version": "1.0.2", 1179 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 1180 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 1181 | "requires": { 1182 | "code-point-at": "^1.0.0", 1183 | "is-fullwidth-code-point": "^1.0.0", 1184 | "strip-ansi": "^3.0.0" 1185 | } 1186 | }, 1187 | "string.prototype.trimleft": { 1188 | "version": "2.1.0", 1189 | "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", 1190 | "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", 1191 | "requires": { 1192 | "define-properties": "^1.1.3", 1193 | "function-bind": "^1.1.1" 1194 | } 1195 | }, 1196 | "string.prototype.trimright": { 1197 | "version": "2.1.0", 1198 | "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", 1199 | "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", 1200 | "requires": { 1201 | "define-properties": "^1.1.3", 1202 | "function-bind": "^1.1.1" 1203 | } 1204 | }, 1205 | "strip-ansi": { 1206 | "version": "3.0.1", 1207 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 1208 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 1209 | "requires": { 1210 | "ansi-regex": "^2.0.0" 1211 | } 1212 | }, 1213 | "ts-node": { 1214 | "version": "7.0.1", 1215 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", 1216 | "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", 1217 | "requires": { 1218 | "arrify": "^1.0.0", 1219 | "buffer-from": "^1.1.0", 1220 | "diff": "^3.1.0", 1221 | "make-error": "^1.1.1", 1222 | "minimist": "^1.2.0", 1223 | "mkdirp": "^0.5.1", 1224 | "source-map-support": "^0.5.6", 1225 | "yn": "^2.0.0" 1226 | }, 1227 | "dependencies": { 1228 | "source-map": { 1229 | "version": "0.6.1", 1230 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1231 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" 1232 | }, 1233 | "source-map-support": { 1234 | "version": "0.5.15", 1235 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.15.tgz", 1236 | "integrity": "sha512-wYF5aX1J0+V51BDT3Om7uXNn0ct2FWiV4bvwiGVefxkm+1S1o5jsecE5lb2U28DDblzxzxeIDbTVpXHI9D/9hA==", 1237 | "requires": { 1238 | "buffer-from": "^1.0.0", 1239 | "source-map": "^0.6.0" 1240 | } 1241 | } 1242 | } 1243 | }, 1244 | "typescript": { 1245 | "version": "3.6.4", 1246 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz", 1247 | "integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==" 1248 | }, 1249 | "upath": { 1250 | "version": "1.2.0", 1251 | "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", 1252 | "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" 1253 | }, 1254 | "url": { 1255 | "version": "0.10.3", 1256 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", 1257 | "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", 1258 | "requires": { 1259 | "punycode": "1.3.2", 1260 | "querystring": "0.2.0" 1261 | } 1262 | }, 1263 | "util-promisify": { 1264 | "version": "2.1.0", 1265 | "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz", 1266 | "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=", 1267 | "requires": { 1268 | "object.getownpropertydescriptors": "^2.0.3" 1269 | } 1270 | }, 1271 | "uuid": { 1272 | "version": "3.3.2", 1273 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 1274 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 1275 | }, 1276 | "validate-npm-package-license": { 1277 | "version": "3.0.4", 1278 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", 1279 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", 1280 | "requires": { 1281 | "spdx-correct": "^3.0.0", 1282 | "spdx-expression-parse": "^3.0.0" 1283 | } 1284 | }, 1285 | "window-size": { 1286 | "version": "0.1.4", 1287 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", 1288 | "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" 1289 | }, 1290 | "wrap-ansi": { 1291 | "version": "2.1.0", 1292 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", 1293 | "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", 1294 | "requires": { 1295 | "string-width": "^1.0.1", 1296 | "strip-ansi": "^3.0.1" 1297 | } 1298 | }, 1299 | "wrappy": { 1300 | "version": "1.0.2", 1301 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1302 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1303 | }, 1304 | "xml2js": { 1305 | "version": "0.4.19", 1306 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", 1307 | "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", 1308 | "requires": { 1309 | "sax": ">=0.6.0", 1310 | "xmlbuilder": "~9.0.1" 1311 | } 1312 | }, 1313 | "xmlbuilder": { 1314 | "version": "9.0.7", 1315 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", 1316 | "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" 1317 | }, 1318 | "y18n": { 1319 | "version": "3.2.1", 1320 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", 1321 | "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" 1322 | }, 1323 | "yargs": { 1324 | "version": "3.32.0", 1325 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", 1326 | "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", 1327 | "requires": { 1328 | "camelcase": "^2.0.1", 1329 | "cliui": "^3.0.3", 1330 | "decamelize": "^1.1.1", 1331 | "os-locale": "^1.4.0", 1332 | "string-width": "^1.0.1", 1333 | "window-size": "^0.1.4", 1334 | "y18n": "^3.2.0" 1335 | } 1336 | }, 1337 | "yn": { 1338 | "version": "2.0.0", 1339 | "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", 1340 | "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=" 1341 | } 1342 | } 1343 | } 1344 | -------------------------------------------------------------------------------- /src/deploy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-javascript", 3 | "main": "index.js", 4 | "dependencies": { 5 | "@pulumi/pulumi": "^1.0.0", 6 | "@pulumi/aws": "^1.0.0", 7 | "@pulumi/awsx": "^0.18.10" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/geocode.py: -------------------------------------------------------------------------------- 1 | from concurrent.futures import ThreadPoolExecutor 2 | from urllib.request import urlopen 3 | from urllib.parse import quote 4 | import urllib.error as error 5 | import pandas as pd 6 | import ssl 7 | import json 8 | import os 9 | from ratelimit import limits, sleep_and_retry 10 | 11 | ctx = ssl.create_default_context() 12 | ctx.check_hostname = False 13 | ctx.verify_mode = ssl.CERT_NONE 14 | 15 | 16 | # This is the rate limit period in seconds 17 | # Mapbox rate limits on a per-minute basis, with geocoding set at 600 requests per minute 18 | RATE_LIMIT = 600 19 | LIMIT_DURATION = 60 20 | 21 | errReturn = { 22 | "features": [] 23 | } 24 | 25 | # Set up rate limiting for API calls 26 | # If rate limit is exceeded, sleep the thread and wait until the LIMIT_DURATION has lapsed 27 | # If you request an increased rate limit, update RATE_LIMIT above 28 | @sleep_and_retry 29 | @limits(calls=RATE_LIMIT, period=LIMIT_DURATION) 30 | def req(url): 31 | try: 32 | with urlopen(url, context=ctx) as conn: 33 | return conn.read() 34 | except error.HTTPError as e: 35 | return json.dumps(errReturn).encode("utf-8") 36 | 37 | def geocodeForward(input,token): 38 | url = "https://api.mapbox.com/geocoding/v5/mapbox.places-permanent/%s.json?pluginName=tableauGeocoder&access_token=%s&types=address&limit=1" 39 | access_token = token 40 | addr = list(input["Address"]) 41 | urls = [url % (quote(list_item), access_token) for list_item in addr] 42 | # Once you exceed 1200 req/minute, then consider using more threads to achieve performance 43 | with ThreadPoolExecutor(max_workers=1) as executor: 44 | results = (executor.map(req, urls)) 45 | parsed_results = [json.loads(result.decode("utf-8")) for result in results] 46 | long,lat = zip(*list(result["features"][0]["geometry"]["coordinates"] 47 | if len(result["features"]) > 0 48 | else [None,None] 49 | for result in parsed_results)) 50 | address = [ 51 | result["features"][0].get("place_name", None) 52 | if len(result["features"]) > 0 53 | else None 54 | for result in parsed_results 55 | ] 56 | relevance = [ 57 | result["features"][0].get("relevance", None) 58 | if len(result["features"]) > 0 59 | else None 60 | for result in parsed_results 61 | ] 62 | accuracy = [ 63 | result["features"][0]["properties"].get("accuracy", None) 64 | if len(result["features"]) > 0 65 | else None 66 | for result in parsed_results 67 | ] 68 | d = { 69 | "InitialQuery": addr, 70 | "ReturnAddress": address, 71 | "Accuracy": accuracy, 72 | "Relevance": relevance, 73 | "Longitude": long, 74 | "Latitude": lat, 75 | } 76 | columns = list(d.keys()) 77 | dataOut = pd.DataFrame(d, columns=columns) 78 | return dataOut 79 | 80 | def geocodeReverse(input,token): 81 | url = "https://api.mapbox.com/geocoding/v5/mapbox.places-permanent/%s.json?pluginName=tableauGeocoder&access_token=%s&types=address&limit=1" 82 | access_token = token 83 | input['coords'] = input['Longitude'].map(str)+","+input['Latitude'].map(str) 84 | coordinates = list(input["coords"]) 85 | urls = [url % (quote(str(list_item)), access_token) for list_item in coordinates] 86 | with ThreadPoolExecutor(max_workers=1) as executor: 87 | results = (executor.map(req, urls)) 88 | parsed_results = (json.loads(result.decode("utf-8")) for result in results) 89 | address = [ 90 | result["features"][0].get("place_name", None) 91 | if len(result["features"]) > 0 92 | else None 93 | for result in parsed_results 94 | ] 95 | relevance = [ 96 | result["features"][0].get("relevance", None) 97 | if len(result["features"]) > 0 98 | else None 99 | for result in parsed_results 100 | ] 101 | accuracy = [ 102 | result["features"][0]["properties"].get("accuracy", None) 103 | if len(result["features"]) > 0 104 | else None 105 | for result in parsed_results 106 | ] 107 | d = { 108 | "InitialQuery": coordinates, 109 | "ReturnAddress": address, 110 | "Accuracy": accuracy, 111 | "Relevance": relevance 112 | } 113 | columns = list(d.keys()) 114 | dataOut = pd.DataFrame(d, columns=columns) 115 | return dataOut -------------------------------------------------------------------------------- /src/prepForward.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append('/TabPy') 3 | from geocode import geocodeForward 4 | 5 | def prepGeo(input): 6 | prepData = geocodeForward(input,"token") 7 | return prepData 8 | 9 | def get_output_schema(): 10 | return pd.DataFrame( 11 | { 12 | "InitialQuery": prep_string(), 13 | "ReturnAddress": prep_string(), 14 | "Accuracy": prep_string(), 15 | "Relevance": prep_decimal(), 16 | "Longitude": prep_decimal(), 17 | "Latitude": prep_decimal(), 18 | } 19 | ) -------------------------------------------------------------------------------- /src/prepReverse.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append('/TabPy') 3 | from geocode import geocodeReverse 4 | 5 | def prepGeo(input): 6 | prepData = geocodeReverse(input,"token") 7 | return prepData 8 | 9 | def get_output_schema(): 10 | return pd.DataFrame( 11 | { 12 | "InitialQuery": prep_string(), 13 | "ReturnAddress": prep_string(), 14 | "Accuracy": prep_string(), 15 | "Relevance": prep_decimal() 16 | } 17 | ) -------------------------------------------------------------------------------- /src/startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | tabpy --config=./tabpy.conf -------------------------------------------------------------------------------- /src/tabpy.conf: -------------------------------------------------------------------------------- 1 | [TabPy] 2 | # TABPY_QUERY_OBJECT_PATH = /tmp/query_objects 3 | TABPY_PORT = 80 4 | # TABPY_STATE_PATH = /tabpy/tabpy_server 5 | 6 | # Where static pages live 7 | # TABPY_STATIC_PATH = /tabpy/tabpy_server/static 8 | 9 | # For how to configure TabPy authentication read 10 | # docs/server-config.md. 11 | # TABPY_PWD_FILE = /path/to/password/file.txt 12 | 13 | # To set up secure TabPy uncomment and modify the following lines. 14 | # Note only PEM-encoded x509 certificates are supported. 15 | # TABPY_TRANSFER_PROTOCOL = https 16 | # TABPY_CERTIFICATE_FILE = /path/to/certificate/file.crt 17 | # TABPY_KEY_FILE = /path/to/key/file.key 18 | 19 | # Log additional request details including caller IP, full URL, client 20 | # end user info if provided. 21 | # TABPY_LOG_DETAILS = true 22 | 23 | # Limit request size (in Mb) - any request which size exceeds 24 | # specified amount will be rejected by TabPy. 25 | # Default value is 100 Mb. 26 | # TABPY_MAX_REQUEST_SIZE_MB = 100 27 | 28 | # Configure how long a custom script provided to the /evaluate method 29 | # will run before throwing a TimeoutError. 30 | # The value should be a float representing the timeout time in seconds. 31 | # Default here is 10 minutes. At default Mapbox Rate limits this is good for 32 | # 6000 geocodes. 33 | TABPY_EVALUATE_TIMEOUT = 600 34 | 35 | [loggers] 36 | keys=root 37 | 38 | [handlers] 39 | keys=rootHandler,rotatingFileHandler 40 | 41 | [formatters] 42 | keys=rootFormatter 43 | 44 | [logger_root] 45 | level=DEBUG 46 | handlers=rootHandler,rotatingFileHandler 47 | qualname=root 48 | propagete=0 49 | 50 | [handler_rootHandler] 51 | class=StreamHandler 52 | level=DEBUG 53 | formatter=rootFormatter 54 | args=(sys.stdout,) 55 | 56 | [handler_rotatingFileHandler] 57 | class=handlers.RotatingFileHandler 58 | level=DEBUG 59 | formatter=rootFormatter 60 | args=('tabpy_log.log', 'a', 1000000, 5) 61 | 62 | [formatter_rootFormatter] 63 | format=%(asctime)s [%(levelname)s] (%(filename)s:%(module)s:%(lineno)d): %(message)s 64 | datefmt=%Y-%m-%d,%H:%M:%S -------------------------------------------------------------------------------- /test.txt: -------------------------------------------------------------------------------- 1 | Address 2 | 50 Beale Street San Francisco CA 94105 3 | 740 15th Street NW Washington DC 20005 --------------------------------------------------------------------------------