├── .gitignore ├── README.md ├── app ├── main.py └── static │ ├── data.json │ └── index.html ├── dev-requirements.txt ├── requirements.txt ├── stackoverflow.py ├── startup.txt └── tests └── test_data.py /.gitignore: -------------------------------------------------------------------------------- 1 | env/ 2 | .vscode/ 3 | __pycache__ 4 | 5 | *.csv 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Prerequisites 3 | To use this demo app: 4 | 5 | - [Install Python 3](https://www.python.org/downloads/) 6 | - [Install Docker Community Edition](https://www.docker.com/community-edition) 7 | - (Optional) [Install the Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-windows?view=azure-cli-latest) 8 | 9 | ## Run locally 10 | 11 | To build and run the docker container locally: 12 | ``` 13 | docker build --rm -t stackoverflow-flask . 14 | docker run --rm -it -p 8000:8000 stackoverflow-flask 15 | ``` 16 | 17 | Open a web browser, and navigate to the sample app at ```http://localhost:8000.``` 18 | 19 | In your terminal window, press Ctrl+C to exit the web server and stop the container. 20 | 21 | If you make code changes you can re-run the docker build and run commands above to update the container. 22 | 23 | ## Deploy the container to Azure 24 | 25 | For this portion of the tutorial you will need the Azure CLI. Either install it locally, or you can run commands in the browser by navigating to the [Azure Cloud Shell](https://shell.azure.com/bash). 26 | 27 | Create a resource group: 28 | ``` 29 | az group create --name FlaskApp --location "West US" 30 | ``` 31 | 32 | Create a container registry and retrieve the password, note that `` needs to be a unique name: 33 | ``` 34 | az acr create --name --resource-group FlaskApp --location "West US" --sku Basic --admin-enabled true 35 | az acr credential show -n 36 | ``` 37 | 38 | You see two passwords. Make note of the user name and the first password. 39 | ```JSON 40 | { 41 | "passwords": [ 42 | { 43 | "name": "password", 44 | "value": "" 45 | }, 46 | { 47 | "name": "password2", 48 | "value": "" 49 | } 50 | ], 51 | "username": "" 52 | } 53 | ``` 54 | 55 | Log in to your registry. When prompted, supply the username and password shown above. 56 | ```bash 57 | docker login .azurecr.io -u 58 | ``` 59 | 60 | Tag your container and push it to the registry: 61 | ``` 62 | docker tag flask-quickstart .azurecr.io/flask-quickstart 63 | docker push .azurecr.io/flask-quickstart 64 | ``` 65 | 66 | Create the app service plan: 67 | ``` 68 | az appservice plan create --name FlaskAppPlan --resource-group FlaskApp --sku B1 --is-linux 69 | ``` 70 | 71 | Create the web app: 72 | ``` 73 | az webapp create --name --resource-group FlaskApp --plan FlaskAppPlan --deployment-container-image-name ".azurecr.io/flask-quickstart" 74 | ``` 75 | 76 | Configure it to pull from the registry: 77 | ``` 78 | az webapp config container set --name --resource-group FlaskApp --docker-custom-image-name .azurecr.io/flask-quickstart --docker-registry-server-url https://.azurecr.io --docker-registry-server-user --docker-registry-server-password 79 | ``` 80 | 81 | Run the following command to set the port number on the site and restart it: 82 | ``` 83 | az webapp config appsettings set --name --resource-group FlaskApp --settings WEBSITES_PORT=8000 84 | az webapp restart --name --resource-group FlaskApp 85 | ``` 86 | 87 | Browse to the web app at ```http://.azurewebsites.net``` 88 | 89 | ## Install additional libraries 90 | 91 | To install additional libraries, first create a virtual environment locally, install packages and then generate a requirements.txt file. 92 | 93 | On Windows: 94 | ``` 95 | py -3 -m venv env 96 | env\scripts\activate 97 | pip install flask 98 | pip freeze > requirements.txt 99 | deactivate 100 | ``` 101 | 102 | On Linux/Unix/macOS: 103 | ``` 104 | python3 -m venv env 105 | env/bin/activate 106 | pip install flask 107 | pip freeze > requirements.txt 108 | deactivate 109 | ``` 110 | 111 | Add the following code to the dockerfile so that the additional requirements are installed: 112 | ``` 113 | # Install additional requirements from a requirements.txt file 114 | COPY requirements.txt / 115 | RUN pip install --no-cache-dir -U pip 116 | RUN pip install --no-cache-dir -r /requirements.txt 117 | ``` 118 | 119 | ## Clean up 120 | 121 | To clean up your Azure resources, delete the resource group 122 | ```az group delete --name FlaskApp``` -------------------------------------------------------------------------------- /app/main.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | app = Flask(__name__) 3 | 4 | @app.route('/') 5 | def index(): 6 | return app.send_static_file('index.html') 7 | 8 | @app.route('/api/data') 9 | def get_data(): 10 | return app.send_static_file('data.json') 11 | 12 | if __name__ == '__main__': 13 | app.run() 14 | -------------------------------------------------------------------------------- /app/static/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "2011": { 3 | "count": { 4 | "C": 741.0, 5 | "C#": 1352.0, 6 | "C++": 740.0, 7 | "CSS": 1356.0, 8 | "Java": 861.0, 9 | "JavaScript": 1419.0, 10 | "None": 20.0, 11 | "PHP": 788.0, 12 | "Perl": 284.0, 13 | "Python": 575.0, 14 | "Ruby": 285.0, 15 | "SQL": 1613.0 16 | }, 17 | "percent": { 18 | "C": 29.024676850763807, 19 | "C#": 52.95730513121818, 20 | "C++": 28.985507246376812, 21 | "CSS": 53.11398354876615, 22 | "Java": 33.72502937720329, 23 | "JavaScript": 55.58166862514688, 24 | "None": 0.7833920877399139, 25 | "PHP": 30.865648256952603, 26 | "Perl": 11.124167645906777, 27 | "Python": 22.52252252252252, 28 | "Ruby": 11.163337250293772, 29 | "SQL": 63.18057187622404 30 | } 31 | }, 32 | "2012": { 33 | "count": { 34 | "C": 1484.0, 35 | "C#": 2468.0, 36 | "C++": 1526.0, 37 | "CSS": 2824.0, 38 | "HTML5": 2154.0, 39 | "Java": 2348.0, 40 | "JavaScript": 3127.0, 41 | "None": 5.0, 42 | "Objective-C": 605.0, 43 | "PHP": 1925.0, 44 | "Perl": 461.0, 45 | "Python": 1210.0, 46 | "Ruby": 577.0, 47 | "SQL": 3294.0 48 | }, 49 | "percent": { 50 | "C": 27.119883040935672, 51 | "C#": 45.10233918128655, 52 | "C++": 27.887426900584796, 53 | "CSS": 51.60818713450293, 54 | "HTML5": 39.364035087719294, 55 | "Java": 42.909356725146196, 56 | "JavaScript": 57.14546783625731, 57 | "None": 0.09137426900584794, 58 | "Objective-C": 11.056286549707602, 59 | "PHP": 35.17909356725146, 60 | "Perl": 8.424707602339181, 61 | "Python": 22.112573099415204, 62 | "Ruby": 10.544590643274853, 63 | "SQL": 60.19736842105263 64 | } 65 | }, 66 | "2013": { 67 | "count": { 68 | "C": 1436.0, 69 | "C#": 3027.0, 70 | "C++": 1696.0, 71 | "JQuery": 239.0, 72 | "Java": 3020.0, 73 | "JavaScript": 4735.0, 74 | "Node.js": 599.0, 75 | "Objective-C": 955.0, 76 | "PHP": 2324.0, 77 | "Python": 1880.0, 78 | "Ruby": 794.0, 79 | "SQL": 4593.0, 80 | "jQuery": 4109.0 81 | }, 82 | "percent": { 83 | "C": 18.02435044558805, 84 | "C#": 37.9942261830049, 85 | "C++": 21.287812225429896, 86 | "JQuery": 2.9998744822392367, 87 | "Java": 37.90636375047069, 88 | "JavaScript": 59.43265972135057, 89 | "Node.js": 7.518513869712565, 90 | "Objective-C": 11.986946152880632, 91 | "PHP": 29.170327601355595, 92 | "Python": 23.59733902347182, 93 | "Ruby": 9.966110204593951, 94 | "SQL": 57.65030751851386, 95 | "jQuery": 51.575247897577505 96 | } 97 | }, 98 | "2014": { 99 | "count": { 100 | "C": 1174.0, 101 | "C#": 2378.0, 102 | "C++": 1418.0, 103 | "Java": 2479.0, 104 | "JavaScript": 3783.0, 105 | "Node.js": 641.0, 106 | "Objective-C": 675.0, 107 | "PHP": 1710.0, 108 | "Python": 1473.0, 109 | "Ruby": 593.0, 110 | "SQL": 3467.0 111 | }, 112 | "percent": { 113 | "C": 18.22981366459627, 114 | "C#": 36.92546583850932, 115 | "C++": 22.01863354037267, 116 | "Java": 38.493788819875775, 117 | "JavaScript": 58.742236024844715, 118 | "Node.js": 9.953416149068323, 119 | "Objective-C": 10.48136645962733, 120 | "PHP": 26.552795031055897, 121 | "Python": 22.872670807453417, 122 | "Ruby": 9.20807453416149, 123 | "SQL": 53.83540372670807 124 | } 125 | }, 126 | "2015": { 127 | "count": { 128 | "Android": 4110.0, 129 | "AngularJS": 2913.0, 130 | "Arduino / Raspberry Pi": 1626.0, 131 | "C": 3612.0, 132 | "C#": 6949.0, 133 | "C++": 4529.0, 134 | "C++11": 1851.0, 135 | "Cassandra": 202.0, 136 | "Clojure": 176.0, 137 | "Cloud (AWS, GAE, Azure, etc.)": 1410.0, 138 | "CoffeeScript": 783.0, 139 | "Cordova": 628.0, 140 | "Dart": 109.0, 141 | "F#": 174.0, 142 | "Go": 462.0, 143 | "Hadoop": 342.0, 144 | "Haskell": 357.0, 145 | "Java": 8219.0, 146 | "JavaScript": 11962.0, 147 | "LAMP": 1926.0, 148 | "Matlab": 860.0, 149 | "MongoDB": 1745.0, 150 | "Node.js": 2919.0, 151 | "Objective-C": 1719.0, 152 | "PHP": 6529.0, 153 | "Perl": 738.0, 154 | "Python": 5238.0, 155 | "R": 755.0, 156 | "Redis": 873.0, 157 | "Ruby": 1765.0, 158 | "Rust": 103.0, 159 | "SQL": 9439.0, 160 | "SQL Server": 4129.0, 161 | "Salesforce": 153.0, 162 | "Scala": 538.0, 163 | "Sharepoint": 349.0, 164 | "Spark": 104.0, 165 | "Swift": 759.0, 166 | "Visual Basic": 1701.0, 167 | "Windows Phone": 570.0, 168 | "Wordpress": 2007.0, 169 | "iOS": 1956.0 170 | }, 171 | "percent": { 172 | "Android": 18.69796642554934, 173 | "AngularJS": 13.252354305991537, 174 | "Arduino / Raspberry Pi": 7.397297666166234, 175 | "C": 16.432373413402484, 176 | "C#": 31.61366634820982, 177 | "C++": 20.604158136572494, 178 | "C++11": 8.420908966834993, 179 | "Cassandra": 0.9189754788226195, 180 | "Clojure": 0.8006915063008962, 181 | "Cloud (AWS, GAE, Azure, etc.)": 6.414630817524225, 182 | "CoffeeScript": 3.5621673263272826, 183 | "Cordova": 2.8570128747554704, 184 | "Dart": 0.4958828078795323, 185 | "F#": 0.7915927391838405, 186 | "Go": 2.101815204039853, 187 | "Hadoop": 1.5558891770165142, 188 | "Haskell": 1.6241299303944314, 189 | "Java": 37.391383467540145, 190 | "JavaScript": 54.41972612710978, 191 | "LAMP": 8.762112733724582, 192 | "Matlab": 3.9124698603339247, 193 | "MongoDB": 7.938674309631046, 194 | "Node.js": 13.279650607342704, 195 | "Objective-C": 7.8203903371093215, 196 | "PHP": 29.70292525362813, 197 | "Perl": 3.357445066193531, 198 | "Python": 23.82967107956872, 199 | "R": 3.4347845866885036, 200 | "Redis": 3.9716118465947865, 201 | "Ruby": 8.0296619808016, 202 | "Rust": 0.4685865065283654, 203 | "SQL": 42.94163140894408, 204 | "SQL Server": 18.78440471316137, 205 | "Salesforce": 0.6960556844547563, 206 | "Scala": 2.4475683544879665, 207 | "Sharepoint": 1.5877348619262088, 208 | "Spark": 0.4731358900868932, 209 | "Swift": 3.4529821209226155, 210 | "Visual Basic": 7.738501433055821, 211 | "Windows Phone": 2.593148628360857, 212 | "Wordpress": 9.130612801965334, 213 | "iOS": 8.898594240480415 214 | } 215 | }, 216 | "2016": { 217 | "count": { 218 | "Android": 8601.0, 219 | "AngularJS": 8823.0, 220 | "Arduino / Raspberry Pi": 3797.0, 221 | "C": 7678.0, 222 | "C#": 15283.0, 223 | "C++": 9589.0, 224 | "Cassandra": 663.0, 225 | "Clojure": 556.0, 226 | "Cloud (AWS, GAE, Azure, etc.)": 4629.0, 227 | "CoffeeScript": 1662.0, 228 | "Cordova": 1651.0, 229 | "Dart": 222.0, 230 | "F#": 484.0, 231 | "Go": 1547.0, 232 | "Hadoop": 1012.0, 233 | "Haskell": 813.0, 234 | "Java": 17942.0, 235 | "JavaScript": 27385.0, 236 | "LAMP": 4821.0, 237 | "Matlab": 1602.0, 238 | "MongoDB": 4780.0, 239 | "Node.js": 8509.0, 240 | "Objective-C": 3202.0, 241 | "PHP": 12780.0, 242 | "Perl": 1624.0, 243 | "Python": 12282.0, 244 | "R": 1632.0, 245 | "ReactJS": 2541.0, 246 | "Redis": 2862.0, 247 | "Ruby": 4383.0, 248 | "Rust": 436.0, 249 | "SQL": 21976.0, 250 | "SQL Server": 9306.0, 251 | "Salesforce": 665.0, 252 | "Scala": 1602.0, 253 | "SharePoint": 853.0, 254 | "Spark": 707.0, 255 | "Swift": 2746.0, 256 | "Visual Basic": 3312.0, 257 | "Windows Phone": 1123.0, 258 | "WordPress": 4496.0, 259 | "iOS": 4498.0 260 | }, 261 | "percent": { 262 | "Android": 17.544110147883732, 263 | "AngularJS": 17.99694033656298, 264 | "Arduino / Raspberry Pi": 7.74502804691484, 265 | "C": 15.661397246302908, 266 | "C#": 31.17389087200408, 267 | "C++": 19.55940846506884, 268 | "Cassandra": 1.352371239163692, 269 | "Clojure": 1.1341152473227945, 270 | "Cloud (AWS, GAE, Azure, etc.)": 9.442121366649667, 271 | "CoffeeScript": 3.3901070882202955, 272 | "Cordova": 3.367669556348802, 273 | "Dart": 0.4528301886792453, 274 | "F#": 0.9872514023457419, 275 | "Go": 3.155532891381948, 276 | "Hadoop": 2.0642529321774603, 277 | "Haskell": 1.6583375828658848, 278 | "Java": 36.59765425803162, 279 | "JavaScript": 55.859255481896994, 280 | "LAMP": 9.833758286588475, 281 | "Matlab": 3.267720550739419, 282 | "MongoDB": 9.750127485976542, 283 | "Node.js": 17.356450790413056, 284 | "Objective-C": 6.531361550229475, 285 | "PHP": 26.068332483426822, 286 | "Perl": 3.3125956144824067, 287 | "Python": 25.052524222335542, 288 | "R": 3.328913819479857, 289 | "ReactJS": 5.183069862315145, 290 | "Redis": 5.837837837837838, 291 | "Ruby": 8.940336562978073, 292 | "Rust": 0.8893421723610403, 293 | "SQL": 44.82610912799592, 294 | "SQL Server": 18.98215196328404, 295 | "Salesforce": 1.3564507904130547, 296 | "Scala": 3.267720550739419, 297 | "SharePoint": 1.7399286078531362, 298 | "Spark": 1.4421213666496684, 299 | "Swift": 5.601223865374809, 300 | "Visual Basic": 6.755736868944416, 301 | "Windows Phone": 2.2906680265170833, 302 | "WordPress": 9.170831208567058, 303 | "iOS": 9.17491075981642 304 | } 305 | }, 306 | "2017": { 307 | "count": { 308 | "Assembly": 1823.0, 309 | "C": 6974.0, 310 | "C#": 12476.0, 311 | "C++": 8155.0, 312 | "Clojure": 391.0, 313 | "CoffeeScript": 1192.0, 314 | "Common Lisp": 273.0, 315 | "Dart": 145.0, 316 | "Elixir": 380.0, 317 | "Erlang": 281.0, 318 | "F#": 457.0, 319 | "Go": 1557.0, 320 | "Groovy": 1193.0, 321 | "Hack": 107.0, 322 | "Haskell": 649.0, 323 | "Java": 14524.0, 324 | "JavaScript": 22875.0, 325 | "Julia": 138.0, 326 | "Lua": 1039.0, 327 | "Matlab": 1569.0, 328 | "Objective-C": 2349.0, 329 | "PHP": 10290.0, 330 | "Perl": 1585.0, 331 | "Python": 11704.0, 332 | "R": 1634.0, 333 | "Ruby": 3324.0, 334 | "Rust": 416.0, 335 | "SQL": 18754.0, 336 | "Scala": 1309.0, 337 | "Smalltalk": 327.0, 338 | "Swift": 2368.0, 339 | "TypeScript": 3488.0, 340 | "VB.NET": 2273.0, 341 | "VBA": 1574.0, 342 | "Visual Basic 6": 1071.0 343 | }, 344 | "percent": { 345 | "Assembly": 4.977474402730375, 346 | "C": 19.041638225255973, 347 | "C#": 34.06416382252559, 348 | "C++": 22.266211604095563, 349 | "Clojure": 1.0675767918088739, 350 | "CoffeeScript": 3.2546075085324233, 351 | "Common Lisp": 0.7453924914675768, 352 | "Dart": 0.3959044368600682, 353 | "Elixir": 1.037542662116041, 354 | "Erlang": 0.7672354948805461, 355 | "F#": 1.2477815699658703, 356 | "Go": 4.251194539249147, 357 | "Groovy": 3.2573378839590448, 358 | "Hack": 0.29215017064846416, 359 | "Haskell": 1.7720136518771332, 360 | "Java": 39.65597269624573, 361 | "JavaScript": 62.45733788395904, 362 | "Julia": 0.37679180887372016, 363 | "Lua": 2.836860068259386, 364 | "Matlab": 4.283959044368601, 365 | "Objective-C": 6.4136518771331055, 366 | "PHP": 28.09556313993174, 367 | "Perl": 4.327645051194539, 368 | "Python": 31.956313993174064, 369 | "R": 4.461433447098976, 370 | "Ruby": 9.075767918088737, 371 | "Rust": 1.1358361774744026, 372 | "SQL": 51.205460750853234, 373 | "Scala": 3.574061433447099, 374 | "Smalltalk": 0.8928327645051195, 375 | "Swift": 6.465529010238908, 376 | "TypeScript": 9.523549488054607, 377 | "VB.NET": 6.206143344709898, 378 | "VBA": 4.297610921501707, 379 | "Visual Basic 6": 2.9242320819112626 380 | } 381 | } 382 | } -------------------------------------------------------------------------------- /app/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Line Chart 7 | 8 | 9 | 16 | 17 | 18 | 19 |
20 | 21 |
22 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /dev-requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qubitron/stackoverflow-flask/ffd927fd0144e6035862efa772ee458a25654c4c/dev-requirements.txt -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qubitron/stackoverflow-flask/ffd927fd0144e6035862efa772ee458a25654c4c/requirements.txt -------------------------------------------------------------------------------- /stackoverflow.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import zipfile 3 | import shutil 4 | import os 5 | import pandas as pd 6 | import json 7 | 8 | urls = { 9 | 2017: 'https://drive.google.com/uc?export=download&id=0B6ZlG_Eygdj-c1kzcmUxN05VUXM', 10 | 2016: 'https://drive.google.com/uc?export=download&id=0B0DL28AqnGsrV0VldnVIT1hyb0E', 11 | 2015: 'https://drive.google.com/uc?export=download&id=0B0DL28AqnGsra1psanV1MEdxZk0', 12 | 2014: 'https://drive.google.com/uc?export=download&id=0B0DL28AqnGsrempjMktvWFNaQzA', 13 | 2013: 'https://drive.google.com/uc?export=download&id=0B0DL28AqnGsrenpPNTc5UE1PYW8', 14 | 2012: 'https://drive.google.com/uc?export=download&id=0B0DL28AqnGsrX3JaZWVwWEpHNWM', 15 | 2011: 'https://drive.google.com/uc?export=download&id=0Bx0LyhBTBZQgUGVYaGx3SzdUQ1U', 16 | } 17 | 18 | filenames = { 19 | 2017: 'survey_results_public.csv', 20 | 2016: '2016 Stack Overflow Survey Results/2016 Stack Overflow Survey Responses.csv', 21 | 2015: '2015 Stack Overflow Developer Survey Responses.csv', 22 | 2014: '2014 Stack Overflow Survey Responses.csv', 23 | 2013: '2013 Stack Overflow Survey Responses.csv', 24 | 2012: '2012 Stack Overflow Survey Results.csv', 25 | 2011: '2011 Stack Overflow Survey Results.csv' 26 | } 27 | 28 | questionNames = { 29 | 2017: 'HaveWorkedLanguage', 30 | 2016: 'tech_do', 31 | 2015: 'Select all that apply', 32 | 2014: 'Which of the following languages or technologies have you used significantly in the past year?', 33 | 2013: 'Which of the following languages or technologies have you used significantly in the past year?', 34 | 2012: 'Which languages are you proficient in?', 35 | 2011: 'Which languages are you proficient in?', 36 | } 37 | 38 | def survey_csvname(year): 39 | return 'survey{}.csv'.format(year) 40 | 41 | def download_survey(year): 42 | print("Downloading " + str(year)) 43 | request = requests.get(urls[year]) 44 | with open("survey.zip", "wb") as file: 45 | file.write(request.content) 46 | 47 | with zipfile.ZipFile("survey.zip", "r") as file: 48 | file.extractall("data") 49 | 50 | shutil.move("data/" + filenames[year], survey_csvname(year)) 51 | shutil.rmtree("data", ignore_errors=True) 52 | os.remove("survey.zip") 53 | 54 | def languages_breakdown(year): 55 | if not os.path.exists(survey_csvname(year)): 56 | download_survey(year) 57 | print("Processing " + str(year)) 58 | data=pd.read_csv(survey_csvname(year), encoding='latin1') 59 | 60 | if year >= 2016: 61 | # Languages are semicolon separated list in a single column 62 | languages = data[questionNames[year]].str.split(';', expand=True) 63 | else: 64 | # Languages are a set of columns, one column per language + other 65 | # First get a list of column names that represent the set of languages/technology 66 | current = 0 67 | columnNames = [] 68 | 69 | # Iterate through columns until we get to the language/tech question 70 | while data.columns[current] != questionNames[year]: 71 | current += 1 72 | 73 | # Add all columns except for other (which is the last one) 74 | while data.columns[current + 1].startswith('Unnamed'): 75 | columnNames.append(data.columns[current]) 76 | current += 1 77 | 78 | # Filter the survey to just the language column names 79 | languages = data[columnNames] 80 | languages = languages.drop(0) 81 | 82 | summary = languages.apply(pd.Series.value_counts) 83 | summary = pd.DataFrame({'count': summary.sum(axis=1).groupby(lambda x: x.strip()).sum()}) 84 | 85 | 86 | total = data[data[questionNames[year]].notnull()].shape[0] 87 | 88 | # total needs to account for all columns 89 | if year < 2016: 90 | notNull = languages.apply(lambda x: pd.notnull(x)).sum(axis=1) 91 | total = notNull[notNull > 0].shape[0] 92 | 93 | summary['percent'] = summary['count']/total*100 94 | 95 | return summary 96 | 97 | if __name__ == "__main__": 98 | totals = {} 99 | for year in range(2011, 2018): 100 | totals[year] = languages_breakdown(year).to_dict() 101 | 102 | with open('app/static/data.json', 'w') as file: 103 | file.write(json.dumps(totals, indent=4, separators=(',', ': '))) 104 | -------------------------------------------------------------------------------- /startup.txt: -------------------------------------------------------------------------------- 1 | gunicorn app/main:app -------------------------------------------------------------------------------- /tests/test_data.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import stackoverflow 3 | import os 4 | 5 | class TestStackoverflow(unittest.TestCase): 6 | def test_language_percents(self): 7 | # get list of web frameworks used by Python developers 8 | languages = stackoverflow.languages_breakdown(2015).to_dict() 9 | 10 | self.assertAlmostEqual(languages['percent']['Java'], 37.4, 1) 11 | self.assertAlmostEqual(languages['percent']['C#'], 31.6, 1) 12 | self.assertAlmostEqual(languages['percent']['Python'], 23.8, 1) 13 | self.assertAlmostEqual(languages['percent']['C++'], 20.6, 1) 14 | 15 | 16 | 17 | --------------------------------------------------------------------------------