├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── composer.lock ├── logs └── README.md ├── public ├── 504.html ├── css │ ├── grids-responsive-min.css │ ├── main.css │ ├── marketbrowser.css │ └── pure-min.css ├── index.php └── js │ ├── marketbrowser-post.js │ └── marketbrowser.js ├── scripts ├── aggloader-esi.py ├── aggloader.py ├── citadelgetter.py └── marketgroup-parallel-session.py ├── src ├── dependencies.php ├── middleware.php ├── routes.php └── settings.php └── templates ├── about.phtml ├── aggregate.phtml ├── api.phtml ├── appraisal.phtml ├── base.phtml ├── browser.phtml ├── displayappraisal.phtml ├── history.phtml ├── hub.phtml ├── index.phtml ├── json.phtml ├── region.phtml ├── station.phtml ├── type.phtml └── types.phtml /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /logs/* 3 | !/logs/README.md 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Steve Anderson / Steve Ronuken 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #FuzzMarket 2 | 3 | A new market data website for Eve Online. 4 | 5 | 6 | (mostly done because I wanted to see if I could.) 7 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "slim/slim-skeleton", 3 | "description": "A Slim Framework skeleton application for rapid development", 4 | "keywords": ["microframework","rest","router", "psr7"], 5 | "homepage": "http://github.com/slimphp/Slim-Skeleton", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Josh Lockhart", 10 | "email": "info@joshlockhart.com", 11 | "homepage": "http://www.joshlockhart.com/" 12 | } 13 | ], 14 | "require": { 15 | "php": ">=5.5.0", 16 | "slim/slim": "^3.1", 17 | "slim/php-view": "^2.0", 18 | "monolog/monolog": "^1.17", 19 | "twig/twig": "~1.0", 20 | "slim/twig-view": "^2.1", 21 | "predis/predis": "1.1.1", 22 | "slim/http-cache": "^0.3.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "ee23e1d29a6e0e22691181cef14e89d7", 8 | "content-hash": "ee6796f33afffa39b5e2ebf9322b42d3", 9 | "packages": [ 10 | { 11 | "name": "container-interop/container-interop", 12 | "version": "1.1.0", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/container-interop/container-interop.git", 16 | "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/container-interop/container-interop/zipball/fc08354828f8fd3245f77a66b9e23a6bca48297e", 21 | "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e", 22 | "shasum": "" 23 | }, 24 | "type": "library", 25 | "autoload": { 26 | "psr-4": { 27 | "Interop\\Container\\": "src/Interop/Container/" 28 | } 29 | }, 30 | "notification-url": "https://packagist.org/downloads/", 31 | "license": [ 32 | "MIT" 33 | ], 34 | "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", 35 | "time": "2014-12-30 15:22:37" 36 | }, 37 | { 38 | "name": "monolog/monolog", 39 | "version": "1.19.0", 40 | "source": { 41 | "type": "git", 42 | "url": "https://github.com/Seldaek/monolog.git", 43 | "reference": "5f56ed5212dc509c8dc8caeba2715732abb32dbf" 44 | }, 45 | "dist": { 46 | "type": "zip", 47 | "url": "https://api.github.com/repos/Seldaek/monolog/zipball/5f56ed5212dc509c8dc8caeba2715732abb32dbf", 48 | "reference": "5f56ed5212dc509c8dc8caeba2715732abb32dbf", 49 | "shasum": "" 50 | }, 51 | "require": { 52 | "php": ">=5.3.0", 53 | "psr/log": "~1.0" 54 | }, 55 | "provide": { 56 | "psr/log-implementation": "1.0.0" 57 | }, 58 | "require-dev": { 59 | "aws/aws-sdk-php": "^2.4.9", 60 | "doctrine/couchdb": "~1.0@dev", 61 | "graylog2/gelf-php": "~1.0", 62 | "jakub-onderka/php-parallel-lint": "0.9", 63 | "php-amqplib/php-amqplib": "~2.4", 64 | "php-console/php-console": "^3.1.3", 65 | "phpunit/phpunit": "~4.5", 66 | "phpunit/phpunit-mock-objects": "2.3.0", 67 | "raven/raven": "^0.13", 68 | "ruflin/elastica": ">=0.90 <3.0", 69 | "swiftmailer/swiftmailer": "~5.3" 70 | }, 71 | "suggest": { 72 | "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", 73 | "doctrine/couchdb": "Allow sending log messages to a CouchDB server", 74 | "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", 75 | "ext-mongo": "Allow sending log messages to a MongoDB server", 76 | "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", 77 | "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", 78 | "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", 79 | "php-console/php-console": "Allow sending log messages to Google Chrome", 80 | "raven/raven": "Allow sending log messages to a Sentry server", 81 | "rollbar/rollbar": "Allow sending log messages to Rollbar", 82 | "ruflin/elastica": "Allow sending log messages to an Elastic Search server" 83 | }, 84 | "type": "library", 85 | "extra": { 86 | "branch-alias": { 87 | "dev-master": "2.0.x-dev" 88 | } 89 | }, 90 | "autoload": { 91 | "psr-4": { 92 | "Monolog\\": "src/Monolog" 93 | } 94 | }, 95 | "notification-url": "https://packagist.org/downloads/", 96 | "license": [ 97 | "MIT" 98 | ], 99 | "authors": [ 100 | { 101 | "name": "Jordi Boggiano", 102 | "email": "j.boggiano@seld.be", 103 | "homepage": "http://seld.be" 104 | } 105 | ], 106 | "description": "Sends your logs to files, sockets, inboxes, databases and various web services", 107 | "homepage": "http://github.com/Seldaek/monolog", 108 | "keywords": [ 109 | "log", 110 | "logging", 111 | "psr-3" 112 | ], 113 | "time": "2016-04-12 18:29:35" 114 | }, 115 | { 116 | "name": "nikic/fast-route", 117 | "version": "v1.0.1", 118 | "source": { 119 | "type": "git", 120 | "url": "https://github.com/nikic/FastRoute.git", 121 | "reference": "8ea928195fa9b907f0d6e48312d323c1a13cc2af" 122 | }, 123 | "dist": { 124 | "type": "zip", 125 | "url": "https://api.github.com/repos/nikic/FastRoute/zipball/8ea928195fa9b907f0d6e48312d323c1a13cc2af", 126 | "reference": "8ea928195fa9b907f0d6e48312d323c1a13cc2af", 127 | "shasum": "" 128 | }, 129 | "require": { 130 | "php": ">=5.4.0" 131 | }, 132 | "type": "library", 133 | "autoload": { 134 | "psr-4": { 135 | "FastRoute\\": "src/" 136 | }, 137 | "files": [ 138 | "src/functions.php" 139 | ] 140 | }, 141 | "notification-url": "https://packagist.org/downloads/", 142 | "license": [ 143 | "BSD-3-Clause" 144 | ], 145 | "authors": [ 146 | { 147 | "name": "Nikita Popov", 148 | "email": "nikic@php.net" 149 | } 150 | ], 151 | "description": "Fast request router for PHP", 152 | "keywords": [ 153 | "router", 154 | "routing" 155 | ], 156 | "time": "2016-06-12 19:08:51" 157 | }, 158 | { 159 | "name": "pimple/pimple", 160 | "version": "v3.0.2", 161 | "source": { 162 | "type": "git", 163 | "url": "https://github.com/silexphp/Pimple.git", 164 | "reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a" 165 | }, 166 | "dist": { 167 | "type": "zip", 168 | "url": "https://api.github.com/repos/silexphp/Pimple/zipball/a30f7d6e57565a2e1a316e1baf2a483f788b258a", 169 | "reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a", 170 | "shasum": "" 171 | }, 172 | "require": { 173 | "php": ">=5.3.0" 174 | }, 175 | "type": "library", 176 | "extra": { 177 | "branch-alias": { 178 | "dev-master": "3.0.x-dev" 179 | } 180 | }, 181 | "autoload": { 182 | "psr-0": { 183 | "Pimple": "src/" 184 | } 185 | }, 186 | "notification-url": "https://packagist.org/downloads/", 187 | "license": [ 188 | "MIT" 189 | ], 190 | "authors": [ 191 | { 192 | "name": "Fabien Potencier", 193 | "email": "fabien@symfony.com" 194 | } 195 | ], 196 | "description": "Pimple, a simple Dependency Injection Container", 197 | "homepage": "http://pimple.sensiolabs.org", 198 | "keywords": [ 199 | "container", 200 | "dependency injection" 201 | ], 202 | "time": "2015-09-11 15:10:35" 203 | }, 204 | { 205 | "name": "predis/predis", 206 | "version": "v1.1.1", 207 | "source": { 208 | "type": "git", 209 | "url": "https://github.com/nrk/predis.git", 210 | "reference": "f0210e38881631afeafb56ab43405a92cafd9fd1" 211 | }, 212 | "dist": { 213 | "type": "zip", 214 | "url": "https://api.github.com/repos/nrk/predis/zipball/f0210e38881631afeafb56ab43405a92cafd9fd1", 215 | "reference": "f0210e38881631afeafb56ab43405a92cafd9fd1", 216 | "shasum": "" 217 | }, 218 | "require": { 219 | "php": ">=5.3.9" 220 | }, 221 | "require-dev": { 222 | "phpunit/phpunit": "~4.8" 223 | }, 224 | "suggest": { 225 | "ext-curl": "Allows access to Webdis when paired with phpiredis", 226 | "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol" 227 | }, 228 | "type": "library", 229 | "autoload": { 230 | "psr-4": { 231 | "Predis\\": "src/" 232 | } 233 | }, 234 | "notification-url": "https://packagist.org/downloads/", 235 | "license": [ 236 | "MIT" 237 | ], 238 | "authors": [ 239 | { 240 | "name": "Daniele Alessandri", 241 | "email": "suppakilla@gmail.com", 242 | "homepage": "http://clorophilla.net" 243 | } 244 | ], 245 | "description": "Flexible and feature-complete Redis client for PHP and HHVM", 246 | "homepage": "http://github.com/nrk/predis", 247 | "keywords": [ 248 | "nosql", 249 | "predis", 250 | "redis" 251 | ], 252 | "time": "2016-06-16 16:22:20" 253 | }, 254 | { 255 | "name": "psr/http-message", 256 | "version": "1.0", 257 | "source": { 258 | "type": "git", 259 | "url": "https://github.com/php-fig/http-message.git", 260 | "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298" 261 | }, 262 | "dist": { 263 | "type": "zip", 264 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/85d63699f0dbedb190bbd4b0d2b9dc707ea4c298", 265 | "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298", 266 | "shasum": "" 267 | }, 268 | "require": { 269 | "php": ">=5.3.0" 270 | }, 271 | "type": "library", 272 | "extra": { 273 | "branch-alias": { 274 | "dev-master": "1.0.x-dev" 275 | } 276 | }, 277 | "autoload": { 278 | "psr-4": { 279 | "Psr\\Http\\Message\\": "src/" 280 | } 281 | }, 282 | "notification-url": "https://packagist.org/downloads/", 283 | "license": [ 284 | "MIT" 285 | ], 286 | "authors": [ 287 | { 288 | "name": "PHP-FIG", 289 | "homepage": "http://www.php-fig.org/" 290 | } 291 | ], 292 | "description": "Common interface for HTTP messages", 293 | "keywords": [ 294 | "http", 295 | "http-message", 296 | "psr", 297 | "psr-7", 298 | "request", 299 | "response" 300 | ], 301 | "time": "2015-05-04 20:22:00" 302 | }, 303 | { 304 | "name": "psr/log", 305 | "version": "1.0.0", 306 | "source": { 307 | "type": "git", 308 | "url": "https://github.com/php-fig/log.git", 309 | "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" 310 | }, 311 | "dist": { 312 | "type": "zip", 313 | "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", 314 | "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", 315 | "shasum": "" 316 | }, 317 | "type": "library", 318 | "autoload": { 319 | "psr-0": { 320 | "Psr\\Log\\": "" 321 | } 322 | }, 323 | "notification-url": "https://packagist.org/downloads/", 324 | "license": [ 325 | "MIT" 326 | ], 327 | "authors": [ 328 | { 329 | "name": "PHP-FIG", 330 | "homepage": "http://www.php-fig.org/" 331 | } 332 | ], 333 | "description": "Common interface for logging libraries", 334 | "keywords": [ 335 | "log", 336 | "psr", 337 | "psr-3" 338 | ], 339 | "time": "2012-12-21 11:40:51" 340 | }, 341 | { 342 | "name": "slim/http-cache", 343 | "version": "0.3.0", 344 | "source": { 345 | "type": "git", 346 | "url": "https://github.com/slimphp/Slim-HttpCache.git", 347 | "reference": "29d9c101dcec66ad0851d7fede5c499ed309d4af" 348 | }, 349 | "dist": { 350 | "type": "zip", 351 | "url": "https://api.github.com/repos/slimphp/Slim-HttpCache/zipball/29d9c101dcec66ad0851d7fede5c499ed309d4af", 352 | "reference": "29d9c101dcec66ad0851d7fede5c499ed309d4af", 353 | "shasum": "" 354 | }, 355 | "require": { 356 | "php": ">=5.4.0", 357 | "pimple/pimple": "~3.0", 358 | "psr/http-message": "^1.0" 359 | }, 360 | "require-dev": { 361 | "slim/slim": "3.x-dev" 362 | }, 363 | "type": "library", 364 | "autoload": { 365 | "psr-4": { 366 | "Slim\\HttpCache\\": "src" 367 | } 368 | }, 369 | "notification-url": "https://packagist.org/downloads/", 370 | "license": [ 371 | "MIT" 372 | ], 373 | "authors": [ 374 | { 375 | "name": "Josh Lockhart", 376 | "email": "hello@joshlockhart.com", 377 | "homepage": "http://joshlockhart.com" 378 | } 379 | ], 380 | "description": "Slim Framework HTTP cache middleware and service provider", 381 | "homepage": "http://slimframework.com", 382 | "keywords": [ 383 | "cache", 384 | "framework", 385 | "middleware", 386 | "slim" 387 | ], 388 | "time": "2015-08-13 13:31:25" 389 | }, 390 | { 391 | "name": "slim/php-view", 392 | "version": "2.1.0", 393 | "source": { 394 | "type": "git", 395 | "url": "https://github.com/slimphp/PHP-View.git", 396 | "reference": "8bae5b10d10c51596ef8d8113b3b63678718adcb" 397 | }, 398 | "dist": { 399 | "type": "zip", 400 | "url": "https://api.github.com/repos/slimphp/PHP-View/zipball/8bae5b10d10c51596ef8d8113b3b63678718adcb", 401 | "reference": "8bae5b10d10c51596ef8d8113b3b63678718adcb", 402 | "shasum": "" 403 | }, 404 | "require": { 405 | "psr/http-message": "^1.0" 406 | }, 407 | "require-dev": { 408 | "phpunit/phpunit": "^5.0", 409 | "slim/slim": "^3.0" 410 | }, 411 | "type": "library", 412 | "autoload": { 413 | "psr-4": { 414 | "Slim\\Views\\": "src" 415 | } 416 | }, 417 | "notification-url": "https://packagist.org/downloads/", 418 | "license": [ 419 | "MIT" 420 | ], 421 | "authors": [ 422 | { 423 | "name": "Glenn Eggleton", 424 | "email": "geggleto@gmail.com" 425 | } 426 | ], 427 | "description": "Render PHP view scripts into a PSR-7 Response object.", 428 | "keywords": [ 429 | "framework", 430 | "php", 431 | "phtml", 432 | "renderer", 433 | "slim", 434 | "template", 435 | "view" 436 | ], 437 | "time": "2016-03-04 09:48:50" 438 | }, 439 | { 440 | "name": "slim/slim", 441 | "version": "3.4.2", 442 | "source": { 443 | "type": "git", 444 | "url": "https://github.com/slimphp/Slim.git", 445 | "reference": "a132385f736063d00632b52b3f8a389fe66fe4fa" 446 | }, 447 | "dist": { 448 | "type": "zip", 449 | "url": "https://api.github.com/repos/slimphp/Slim/zipball/a132385f736063d00632b52b3f8a389fe66fe4fa", 450 | "reference": "a132385f736063d00632b52b3f8a389fe66fe4fa", 451 | "shasum": "" 452 | }, 453 | "require": { 454 | "container-interop/container-interop": "^1.1", 455 | "nikic/fast-route": "^1.0", 456 | "php": ">=5.5.0", 457 | "pimple/pimple": "^3.0", 458 | "psr/http-message": "^1.0" 459 | }, 460 | "provide": { 461 | "psr/http-message-implementation": "1.0" 462 | }, 463 | "require-dev": { 464 | "phpunit/phpunit": "^4.0", 465 | "squizlabs/php_codesniffer": "^2.5" 466 | }, 467 | "type": "library", 468 | "autoload": { 469 | "psr-4": { 470 | "Slim\\": "Slim" 471 | } 472 | }, 473 | "notification-url": "https://packagist.org/downloads/", 474 | "license": [ 475 | "MIT" 476 | ], 477 | "authors": [ 478 | { 479 | "name": "Rob Allen", 480 | "email": "rob@akrabat.com", 481 | "homepage": "http://akrabat.com" 482 | }, 483 | { 484 | "name": "Josh Lockhart", 485 | "email": "hello@joshlockhart.com", 486 | "homepage": "https://joshlockhart.com" 487 | }, 488 | { 489 | "name": "Gabriel Manricks", 490 | "email": "gmanricks@me.com", 491 | "homepage": "http://gabrielmanricks.com" 492 | }, 493 | { 494 | "name": "Andrew Smith", 495 | "email": "a.smith@silentworks.co.uk", 496 | "homepage": "http://silentworks.co.uk" 497 | } 498 | ], 499 | "description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs", 500 | "homepage": "http://slimframework.com", 501 | "keywords": [ 502 | "api", 503 | "framework", 504 | "micro", 505 | "router" 506 | ], 507 | "time": "2016-05-25 11:23:38" 508 | }, 509 | { 510 | "name": "slim/twig-view", 511 | "version": "2.1.1", 512 | "source": { 513 | "type": "git", 514 | "url": "https://github.com/slimphp/Twig-View.git", 515 | "reference": "16fded26a44b8e8e0e041f1cff32afa21daeb284" 516 | }, 517 | "dist": { 518 | "type": "zip", 519 | "url": "https://api.github.com/repos/slimphp/Twig-View/zipball/16fded26a44b8e8e0e041f1cff32afa21daeb284", 520 | "reference": "16fded26a44b8e8e0e041f1cff32afa21daeb284", 521 | "shasum": "" 522 | }, 523 | "require": { 524 | "php": ">=5.5.0", 525 | "psr/http-message": "^1.0", 526 | "twig/twig": "^1.18" 527 | }, 528 | "require-dev": { 529 | "phpunit/phpunit": "^4.8.0" 530 | }, 531 | "type": "library", 532 | "autoload": { 533 | "psr-4": { 534 | "Slim\\Views\\": "src" 535 | } 536 | }, 537 | "notification-url": "https://packagist.org/downloads/", 538 | "license": [ 539 | "MIT" 540 | ], 541 | "authors": [ 542 | { 543 | "name": "Josh Lockhart", 544 | "email": "hello@joshlockhart.com", 545 | "homepage": "http://joshlockhart.com" 546 | } 547 | ], 548 | "description": "Slim Framework 3 view helper built on top of the Twig templating component", 549 | "homepage": "http://slimframework.com", 550 | "keywords": [ 551 | "framework", 552 | "slim", 553 | "template", 554 | "twig", 555 | "view" 556 | ], 557 | "time": "2016-03-13 20:58:41" 558 | }, 559 | { 560 | "name": "twig/twig", 561 | "version": "v1.24.1", 562 | "source": { 563 | "type": "git", 564 | "url": "https://github.com/twigphp/Twig.git", 565 | "reference": "3566d311a92aae4deec6e48682dc5a4528c4a512" 566 | }, 567 | "dist": { 568 | "type": "zip", 569 | "url": "https://api.github.com/repos/twigphp/Twig/zipball/3566d311a92aae4deec6e48682dc5a4528c4a512", 570 | "reference": "3566d311a92aae4deec6e48682dc5a4528c4a512", 571 | "shasum": "" 572 | }, 573 | "require": { 574 | "php": ">=5.2.7" 575 | }, 576 | "require-dev": { 577 | "symfony/debug": "~2.7", 578 | "symfony/phpunit-bridge": "~2.7" 579 | }, 580 | "type": "library", 581 | "extra": { 582 | "branch-alias": { 583 | "dev-master": "1.24-dev" 584 | } 585 | }, 586 | "autoload": { 587 | "psr-0": { 588 | "Twig_": "lib/" 589 | } 590 | }, 591 | "notification-url": "https://packagist.org/downloads/", 592 | "license": [ 593 | "BSD-3-Clause" 594 | ], 595 | "authors": [ 596 | { 597 | "name": "Fabien Potencier", 598 | "email": "fabien@symfony.com", 599 | "homepage": "http://fabien.potencier.org", 600 | "role": "Lead Developer" 601 | }, 602 | { 603 | "name": "Armin Ronacher", 604 | "email": "armin.ronacher@active-4.com", 605 | "role": "Project Founder" 606 | }, 607 | { 608 | "name": "Twig Team", 609 | "homepage": "http://twig.sensiolabs.org/contributors", 610 | "role": "Contributors" 611 | } 612 | ], 613 | "description": "Twig, the flexible, fast, and secure template language for PHP", 614 | "homepage": "http://twig.sensiolabs.org", 615 | "keywords": [ 616 | "templating" 617 | ], 618 | "time": "2016-05-30 09:11:59" 619 | } 620 | ], 621 | "packages-dev": [], 622 | "aliases": [], 623 | "minimum-stability": "stable", 624 | "stability-flags": [], 625 | "prefer-stable": false, 626 | "prefer-lowest": false, 627 | "platform": { 628 | "php": ">=5.5.0" 629 | }, 630 | "platform-dev": [] 631 | } 632 | -------------------------------------------------------------------------------- /logs/README.md: -------------------------------------------------------------------------------- 1 | Your Slim Framework application's log files will be written to this directory. 2 | -------------------------------------------------------------------------------- /public/504.html: -------------------------------------------------------------------------------- 1 | 2 | Bad connection to gateway 3 | 4 |

Looks like something is wrong with the database. Steve will need to kick it repeatedly until it works again

5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /public/css/grids-responsive-min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Pure v0.6.0 3 | Copyright 2014 Yahoo! Inc. All rights reserved. 4 | Licensed under the BSD License. 5 | https://github.com/yahoo/pure/blob/master/LICENSE.md 6 | */ 7 | @media screen and (min-width:35.5em){.pure-u-sm-1,.pure-u-sm-1-1,.pure-u-sm-1-2,.pure-u-sm-1-3,.pure-u-sm-2-3,.pure-u-sm-1-4,.pure-u-sm-3-4,.pure-u-sm-1-5,.pure-u-sm-2-5,.pure-u-sm-3-5,.pure-u-sm-4-5,.pure-u-sm-5-5,.pure-u-sm-1-6,.pure-u-sm-5-6,.pure-u-sm-1-8,.pure-u-sm-3-8,.pure-u-sm-5-8,.pure-u-sm-7-8,.pure-u-sm-1-12,.pure-u-sm-5-12,.pure-u-sm-7-12,.pure-u-sm-11-12,.pure-u-sm-1-24,.pure-u-sm-2-24,.pure-u-sm-3-24,.pure-u-sm-4-24,.pure-u-sm-5-24,.pure-u-sm-6-24,.pure-u-sm-7-24,.pure-u-sm-8-24,.pure-u-sm-9-24,.pure-u-sm-10-24,.pure-u-sm-11-24,.pure-u-sm-12-24,.pure-u-sm-13-24,.pure-u-sm-14-24,.pure-u-sm-15-24,.pure-u-sm-16-24,.pure-u-sm-17-24,.pure-u-sm-18-24,.pure-u-sm-19-24,.pure-u-sm-20-24,.pure-u-sm-21-24,.pure-u-sm-22-24,.pure-u-sm-23-24,.pure-u-sm-24-24{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-sm-1-24{width:4.1667%;*width:4.1357%}.pure-u-sm-1-12,.pure-u-sm-2-24{width:8.3333%;*width:8.3023%}.pure-u-sm-1-8,.pure-u-sm-3-24{width:12.5%;*width:12.469%}.pure-u-sm-1-6,.pure-u-sm-4-24{width:16.6667%;*width:16.6357%}.pure-u-sm-1-5{width:20%;*width:19.969%}.pure-u-sm-5-24{width:20.8333%;*width:20.8023%}.pure-u-sm-1-4,.pure-u-sm-6-24{width:25%;*width:24.969%}.pure-u-sm-7-24{width:29.1667%;*width:29.1357%}.pure-u-sm-1-3,.pure-u-sm-8-24{width:33.3333%;*width:33.3023%}.pure-u-sm-3-8,.pure-u-sm-9-24{width:37.5%;*width:37.469%}.pure-u-sm-2-5{width:40%;*width:39.969%}.pure-u-sm-5-12,.pure-u-sm-10-24{width:41.6667%;*width:41.6357%}.pure-u-sm-11-24{width:45.8333%;*width:45.8023%}.pure-u-sm-1-2,.pure-u-sm-12-24{width:50%;*width:49.969%}.pure-u-sm-13-24{width:54.1667%;*width:54.1357%}.pure-u-sm-7-12,.pure-u-sm-14-24{width:58.3333%;*width:58.3023%}.pure-u-sm-3-5{width:60%;*width:59.969%}.pure-u-sm-5-8,.pure-u-sm-15-24{width:62.5%;*width:62.469%}.pure-u-sm-2-3,.pure-u-sm-16-24{width:66.6667%;*width:66.6357%}.pure-u-sm-17-24{width:70.8333%;*width:70.8023%}.pure-u-sm-3-4,.pure-u-sm-18-24{width:75%;*width:74.969%}.pure-u-sm-19-24{width:79.1667%;*width:79.1357%}.pure-u-sm-4-5{width:80%;*width:79.969%}.pure-u-sm-5-6,.pure-u-sm-20-24{width:83.3333%;*width:83.3023%}.pure-u-sm-7-8,.pure-u-sm-21-24{width:87.5%;*width:87.469%}.pure-u-sm-11-12,.pure-u-sm-22-24{width:91.6667%;*width:91.6357%}.pure-u-sm-23-24{width:95.8333%;*width:95.8023%}.pure-u-sm-1,.pure-u-sm-1-1,.pure-u-sm-5-5,.pure-u-sm-24-24{width:100%}}@media screen and (min-width:48em){.pure-u-md-1,.pure-u-md-1-1,.pure-u-md-1-2,.pure-u-md-1-3,.pure-u-md-2-3,.pure-u-md-1-4,.pure-u-md-3-4,.pure-u-md-1-5,.pure-u-md-2-5,.pure-u-md-3-5,.pure-u-md-4-5,.pure-u-md-5-5,.pure-u-md-1-6,.pure-u-md-5-6,.pure-u-md-1-8,.pure-u-md-3-8,.pure-u-md-5-8,.pure-u-md-7-8,.pure-u-md-1-12,.pure-u-md-5-12,.pure-u-md-7-12,.pure-u-md-11-12,.pure-u-md-1-24,.pure-u-md-2-24,.pure-u-md-3-24,.pure-u-md-4-24,.pure-u-md-5-24,.pure-u-md-6-24,.pure-u-md-7-24,.pure-u-md-8-24,.pure-u-md-9-24,.pure-u-md-10-24,.pure-u-md-11-24,.pure-u-md-12-24,.pure-u-md-13-24,.pure-u-md-14-24,.pure-u-md-15-24,.pure-u-md-16-24,.pure-u-md-17-24,.pure-u-md-18-24,.pure-u-md-19-24,.pure-u-md-20-24,.pure-u-md-21-24,.pure-u-md-22-24,.pure-u-md-23-24,.pure-u-md-24-24{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-md-1-24{width:4.1667%;*width:4.1357%}.pure-u-md-1-12,.pure-u-md-2-24{width:8.3333%;*width:8.3023%}.pure-u-md-1-8,.pure-u-md-3-24{width:12.5%;*width:12.469%}.pure-u-md-1-6,.pure-u-md-4-24{width:16.6667%;*width:16.6357%}.pure-u-md-1-5{width:20%;*width:19.969%}.pure-u-md-5-24{width:20.8333%;*width:20.8023%}.pure-u-md-1-4,.pure-u-md-6-24{width:25%;*width:24.969%}.pure-u-md-7-24{width:29.1667%;*width:29.1357%}.pure-u-md-1-3,.pure-u-md-8-24{width:33.3333%;*width:33.3023%}.pure-u-md-3-8,.pure-u-md-9-24{width:37.5%;*width:37.469%}.pure-u-md-2-5{width:40%;*width:39.969%}.pure-u-md-5-12,.pure-u-md-10-24{width:41.6667%;*width:41.6357%}.pure-u-md-11-24{width:45.8333%;*width:45.8023%}.pure-u-md-1-2,.pure-u-md-12-24{width:50%;*width:49.969%}.pure-u-md-13-24{width:54.1667%;*width:54.1357%}.pure-u-md-7-12,.pure-u-md-14-24{width:58.3333%;*width:58.3023%}.pure-u-md-3-5{width:60%;*width:59.969%}.pure-u-md-5-8,.pure-u-md-15-24{width:62.5%;*width:62.469%}.pure-u-md-2-3,.pure-u-md-16-24{width:66.6667%;*width:66.6357%}.pure-u-md-17-24{width:70.8333%;*width:70.8023%}.pure-u-md-3-4,.pure-u-md-18-24{width:75%;*width:74.969%}.pure-u-md-19-24{width:79.1667%;*width:79.1357%}.pure-u-md-4-5{width:80%;*width:79.969%}.pure-u-md-5-6,.pure-u-md-20-24{width:83.3333%;*width:83.3023%}.pure-u-md-7-8,.pure-u-md-21-24{width:87.5%;*width:87.469%}.pure-u-md-11-12,.pure-u-md-22-24{width:91.6667%;*width:91.6357%}.pure-u-md-23-24{width:95.8333%;*width:95.8023%}.pure-u-md-1,.pure-u-md-1-1,.pure-u-md-5-5,.pure-u-md-24-24{width:100%}}@media screen and (min-width:64em){.pure-u-lg-1,.pure-u-lg-1-1,.pure-u-lg-1-2,.pure-u-lg-1-3,.pure-u-lg-2-3,.pure-u-lg-1-4,.pure-u-lg-3-4,.pure-u-lg-1-5,.pure-u-lg-2-5,.pure-u-lg-3-5,.pure-u-lg-4-5,.pure-u-lg-5-5,.pure-u-lg-1-6,.pure-u-lg-5-6,.pure-u-lg-1-8,.pure-u-lg-3-8,.pure-u-lg-5-8,.pure-u-lg-7-8,.pure-u-lg-1-12,.pure-u-lg-5-12,.pure-u-lg-7-12,.pure-u-lg-11-12,.pure-u-lg-1-24,.pure-u-lg-2-24,.pure-u-lg-3-24,.pure-u-lg-4-24,.pure-u-lg-5-24,.pure-u-lg-6-24,.pure-u-lg-7-24,.pure-u-lg-8-24,.pure-u-lg-9-24,.pure-u-lg-10-24,.pure-u-lg-11-24,.pure-u-lg-12-24,.pure-u-lg-13-24,.pure-u-lg-14-24,.pure-u-lg-15-24,.pure-u-lg-16-24,.pure-u-lg-17-24,.pure-u-lg-18-24,.pure-u-lg-19-24,.pure-u-lg-20-24,.pure-u-lg-21-24,.pure-u-lg-22-24,.pure-u-lg-23-24,.pure-u-lg-24-24{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-lg-1-24{width:4.1667%;*width:4.1357%}.pure-u-lg-1-12,.pure-u-lg-2-24{width:8.3333%;*width:8.3023%}.pure-u-lg-1-8,.pure-u-lg-3-24{width:12.5%;*width:12.469%}.pure-u-lg-1-6,.pure-u-lg-4-24{width:16.6667%;*width:16.6357%}.pure-u-lg-1-5{width:20%;*width:19.969%}.pure-u-lg-5-24{width:20.8333%;*width:20.8023%}.pure-u-lg-1-4,.pure-u-lg-6-24{width:25%;*width:24.969%}.pure-u-lg-7-24{width:29.1667%;*width:29.1357%}.pure-u-lg-1-3,.pure-u-lg-8-24{width:33.3333%;*width:33.3023%}.pure-u-lg-3-8,.pure-u-lg-9-24{width:37.5%;*width:37.469%}.pure-u-lg-2-5{width:40%;*width:39.969%}.pure-u-lg-5-12,.pure-u-lg-10-24{width:41.6667%;*width:41.6357%}.pure-u-lg-11-24{width:45.8333%;*width:45.8023%}.pure-u-lg-1-2,.pure-u-lg-12-24{width:50%;*width:49.969%}.pure-u-lg-13-24{width:54.1667%;*width:54.1357%}.pure-u-lg-7-12,.pure-u-lg-14-24{width:58.3333%;*width:58.3023%}.pure-u-lg-3-5{width:60%;*width:59.969%}.pure-u-lg-5-8,.pure-u-lg-15-24{width:62.5%;*width:62.469%}.pure-u-lg-2-3,.pure-u-lg-16-24{width:66.6667%;*width:66.6357%}.pure-u-lg-17-24{width:70.8333%;*width:70.8023%}.pure-u-lg-3-4,.pure-u-lg-18-24{width:75%;*width:74.969%}.pure-u-lg-19-24{width:79.1667%;*width:79.1357%}.pure-u-lg-4-5{width:80%;*width:79.969%}.pure-u-lg-5-6,.pure-u-lg-20-24{width:83.3333%;*width:83.3023%}.pure-u-lg-7-8,.pure-u-lg-21-24{width:87.5%;*width:87.469%}.pure-u-lg-11-12,.pure-u-lg-22-24{width:91.6667%;*width:91.6357%}.pure-u-lg-23-24{width:95.8333%;*width:95.8023%}.pure-u-lg-1,.pure-u-lg-1-1,.pure-u-lg-5-5,.pure-u-lg-24-24{width:100%}}@media screen and (min-width:80em){.pure-u-xl-1,.pure-u-xl-1-1,.pure-u-xl-1-2,.pure-u-xl-1-3,.pure-u-xl-2-3,.pure-u-xl-1-4,.pure-u-xl-3-4,.pure-u-xl-1-5,.pure-u-xl-2-5,.pure-u-xl-3-5,.pure-u-xl-4-5,.pure-u-xl-5-5,.pure-u-xl-1-6,.pure-u-xl-5-6,.pure-u-xl-1-8,.pure-u-xl-3-8,.pure-u-xl-5-8,.pure-u-xl-7-8,.pure-u-xl-1-12,.pure-u-xl-5-12,.pure-u-xl-7-12,.pure-u-xl-11-12,.pure-u-xl-1-24,.pure-u-xl-2-24,.pure-u-xl-3-24,.pure-u-xl-4-24,.pure-u-xl-5-24,.pure-u-xl-6-24,.pure-u-xl-7-24,.pure-u-xl-8-24,.pure-u-xl-9-24,.pure-u-xl-10-24,.pure-u-xl-11-24,.pure-u-xl-12-24,.pure-u-xl-13-24,.pure-u-xl-14-24,.pure-u-xl-15-24,.pure-u-xl-16-24,.pure-u-xl-17-24,.pure-u-xl-18-24,.pure-u-xl-19-24,.pure-u-xl-20-24,.pure-u-xl-21-24,.pure-u-xl-22-24,.pure-u-xl-23-24,.pure-u-xl-24-24{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-xl-1-24{width:4.1667%;*width:4.1357%}.pure-u-xl-1-12,.pure-u-xl-2-24{width:8.3333%;*width:8.3023%}.pure-u-xl-1-8,.pure-u-xl-3-24{width:12.5%;*width:12.469%}.pure-u-xl-1-6,.pure-u-xl-4-24{width:16.6667%;*width:16.6357%}.pure-u-xl-1-5{width:20%;*width:19.969%}.pure-u-xl-5-24{width:20.8333%;*width:20.8023%}.pure-u-xl-1-4,.pure-u-xl-6-24{width:25%;*width:24.969%}.pure-u-xl-7-24{width:29.1667%;*width:29.1357%}.pure-u-xl-1-3,.pure-u-xl-8-24{width:33.3333%;*width:33.3023%}.pure-u-xl-3-8,.pure-u-xl-9-24{width:37.5%;*width:37.469%}.pure-u-xl-2-5{width:40%;*width:39.969%}.pure-u-xl-5-12,.pure-u-xl-10-24{width:41.6667%;*width:41.6357%}.pure-u-xl-11-24{width:45.8333%;*width:45.8023%}.pure-u-xl-1-2,.pure-u-xl-12-24{width:50%;*width:49.969%}.pure-u-xl-13-24{width:54.1667%;*width:54.1357%}.pure-u-xl-7-12,.pure-u-xl-14-24{width:58.3333%;*width:58.3023%}.pure-u-xl-3-5{width:60%;*width:59.969%}.pure-u-xl-5-8,.pure-u-xl-15-24{width:62.5%;*width:62.469%}.pure-u-xl-2-3,.pure-u-xl-16-24{width:66.6667%;*width:66.6357%}.pure-u-xl-17-24{width:70.8333%;*width:70.8023%}.pure-u-xl-3-4,.pure-u-xl-18-24{width:75%;*width:74.969%}.pure-u-xl-19-24{width:79.1667%;*width:79.1357%}.pure-u-xl-4-5{width:80%;*width:79.969%}.pure-u-xl-5-6,.pure-u-xl-20-24{width:83.3333%;*width:83.3023%}.pure-u-xl-7-8,.pure-u-xl-21-24{width:87.5%;*width:87.469%}.pure-u-xl-11-12,.pure-u-xl-22-24{width:91.6667%;*width:91.6357%}.pure-u-xl-23-24{width:95.8333%;*width:95.8023%}.pure-u-xl-1,.pure-u-xl-1-1,.pure-u-xl-5-5,.pure-u-xl-24-24{width:100%}} -------------------------------------------------------------------------------- /public/css/main.css: -------------------------------------------------------------------------------- 1 | html, button, input, select, textarea, .pure-g [class *= "pure-u"] {font-family: 'Electrolize', sans-serif !important;} 2 | body {background-color:#F5F5F5} 3 | table {font-family: 'Source Code Pro' !important;} 4 | .content { margin:auto; padding:30px;} 5 | .orders td { padding:4px; text-align:right;} 6 | .orders th { padding:4px;} 7 | .subtotal th { padding:4px; text-align:right;} 8 | .orders tbody tr:nth-child(odd) { background-color: #E5E5E5; } 9 | .orders tbody td { border-right:1px dotted #222 } 10 | .orders { width:100%} 11 | .aggregates {border-collapse:collapse; border:1px solid} 12 | .aggregates .sellorders { border:1px solid} 13 | .aggregates .buyorders { border:1px solid} 14 | .aggregates td {text-align:right; padding:1px;} 15 | .aggregates tbody tr:nth-child(odd) { background-color: #E5E5E5; } 16 | .title { text-align:center} 17 | .ui-autocomplete { max-height: 100px; overflow-y: auto;overflow-x: hidden; } 18 | .ui-helper-hidden-accessible { position: absolute; left:-999em; } 19 | .center { text-align:center;} 20 | .menu-black { background-color:#222222;color:#999999} 21 | 22 | .menu-black a:link { color: #999999; text-decoration: underline;} 23 | .menu-black a:visited { color: #999999; text-decoration: underline;} 24 | .menu-black a:hover { color: #999999; text-decoration: underline;} 25 | .menu-black a:active { color: #999999; text-decoration: underline;} 26 | 27 | .stationstat{ width:80%} 28 | .stationstat td { padding:4px;} 29 | .alignright {text-align:right;} 30 | .fixedbutton {position: fixed; top: 30px; right: 50px; background-color:lightgray;z-index:200;} 31 | footer {margin:30px;} 32 | 33 | a:link { color: #444444; text-decoration: underline;} 34 | a:visited { color: #444444; text-decoration: underline;} 35 | a:hover { color: #444444; text-decoration: underline;} 36 | a:active { color: #444444; text-decoration: underline;} 37 | 38 | .pasteblock { margin:auto;width:70%} 39 | .pasteblock textarea {width:100%} 40 | 41 | 42 | -------------------------------------------------------------------------------- /public/css/marketbrowser.css: -------------------------------------------------------------------------------- 1 | body { background-color:#202022 !important} 2 | 3 | .markettable { font-size:14px; } 4 | .marketcontent { background-color: #202022; color: white;} 5 | .selecttab { margin:5px;border:2px;border-style: groove;} 6 | 7 | .selectedtab { margin:5px;border:2px;border-style: groove; background-color: #909099;} 8 | 9 | #searchbarInput { background-color: #202022; color: white; } 10 | #regionselector { background-color: #202022; color: white; } 11 | 12 | 13 | .groupLink { 14 | list-style-type: none; 15 | margin: 0; 16 | position: relative; 17 | padding-left: 16px; 18 | } 19 | 20 | .itemLink { 21 | list-style-type: none; 22 | margin: 0; 23 | position: relative; 24 | padding-left: 20px; 25 | } 26 | 27 | 28 | .menulisttop, .subdisplay{ 29 | list-style-type: none; 30 | margin: 0; 31 | position: relative; 32 | padding-left: 5px; 33 | } 34 | 35 | #browsemenu { margin-top:20px;margin-bottom:20px} 36 | #searchbar { margin-top:20px;margin-bottom:20px;margin-left:10px} 37 | #regionselectordiv { margin-top:20px;margin-bottom:20px;margin-left:10px} 38 | #tabbar { margin-top:20px;margin-bottom:20px;margin-left:10px} 39 | 40 | #leftdata { padding: 5px } 41 | 42 | .securityClass { padding-right:5px;} 43 | .high { color:green;} 44 | .low { color:orange;} 45 | .null { color:red;} 46 | 47 | #marketcontent { position:absolute;top:0;bottom:0;} 48 | #buydata_wrapper { display:none;} 49 | 50 | 51 | /* 52 | * Table styles 53 | */ 54 | table.dataTable { 55 | width: 100%; 56 | margin: 0 auto; 57 | clear: both; 58 | border-collapse: separate; 59 | border-spacing: 0; 60 | /* 61 | * Header and footer styles 62 | */ 63 | /* 64 | * Body styles 65 | */ } 66 | table.dataTable thead th, 67 | table.dataTable tfoot th { 68 | font-weight: bold; } 69 | table.dataTable thead th, 70 | table.dataTable thead td { 71 | padding: 10px 18px; 72 | border-bottom: 1px solid #111111; } 73 | table.dataTable thead th:active, 74 | table.dataTable thead td:active { 75 | outline: none; } 76 | table.dataTable tfoot th, 77 | table.dataTable tfoot td { 78 | padding: 10px 18px 6px 18px; 79 | border-top: 1px solid #111111; } 80 | table.dataTable thead .sorting, 81 | table.dataTable thead .sorting_asc, 82 | table.dataTable thead .sorting_desc, 83 | table.dataTable thead .sorting_asc_disabled, 84 | table.dataTable thead .sorting_desc_disabled { 85 | cursor: pointer; 86 | *cursor: hand; 87 | background-repeat: no-repeat; 88 | background-position: center right; } 89 | table.dataTable thead .sorting { 90 | background-image: url("../images/sort_both.png"); } 91 | table.dataTable thead .sorting_asc { 92 | background-image: url("../images/sort_asc.png"); } 93 | table.dataTable thead .sorting_desc { 94 | background-image: url("../images/sort_desc.png"); } 95 | table.dataTable thead .sorting_asc_disabled { 96 | background-image: url("../images/sort_asc_disabled.png"); } 97 | table.dataTable thead .sorting_desc_disabled { 98 | background-image: url("../images/sort_desc_disabled.png"); } 99 | table.dataTable tbody tr { 100 | background-color: black; } 101 | table.dataTable tbody tr.selected { 102 | background-color: #545454; } 103 | table.dataTable tbody th, 104 | table.dataTable tbody td { 105 | padding: 8px 10px; } 106 | table.dataTable.row-border tbody th, table.dataTable.row-border tbody td, table.dataTable.display tbody th, table.dataTable.display tbody td { 107 | border-top: 1px solid #dddddd; } 108 | table.dataTable.row-border tbody tr:first-child th, 109 | table.dataTable.row-border tbody tr:first-child td, table.dataTable.display tbody tr:first-child th, 110 | table.dataTable.display tbody tr:first-child td { 111 | border-top: none; } 112 | table.dataTable.cell-border tbody th, table.dataTable.cell-border tbody td { 113 | border-top: 1px solid #dddddd; 114 | border-right: 1px solid #dddddd; } 115 | table.dataTable.cell-border tbody tr th:first-child, 116 | table.dataTable.cell-border tbody tr td:first-child { 117 | border-left: 1px solid #dddddd; } 118 | table.dataTable.cell-border tbody tr:first-child th, 119 | table.dataTable.cell-border tbody tr:first-child td { 120 | border-top: none; } 121 | table.dataTable.stripe tbody tr.odd, table.dataTable.display tbody tr.odd { 122 | background-color: black; } 123 | table.dataTable.stripe tbody tr.odd.selected, table.dataTable.display tbody tr.odd.selected { 124 | background-color: #525252; } 125 | table.dataTable.hover tbody tr:hover, table.dataTable.display tbody tr:hover { 126 | background-color: black; } 127 | table.dataTable.hover tbody tr:hover.selected, table.dataTable.display tbody tr:hover.selected { 128 | background-color: #505050; } 129 | table.dataTable.order-column tbody tr > .sorting_1, 130 | table.dataTable.order-column tbody tr > .sorting_2, 131 | table.dataTable.order-column tbody tr > .sorting_3, table.dataTable.display tbody tr > .sorting_1, 132 | table.dataTable.display tbody tr > .sorting_2, 133 | table.dataTable.display tbody tr > .sorting_3 { 134 | background-color: black; } 135 | table.dataTable.order-column tbody tr.selected > .sorting_1, 136 | table.dataTable.order-column tbody tr.selected > .sorting_2, 137 | table.dataTable.order-column tbody tr.selected > .sorting_3, table.dataTable.display tbody tr.selected > .sorting_1, 138 | table.dataTable.display tbody tr.selected > .sorting_2, 139 | table.dataTable.display tbody tr.selected > .sorting_3 { 140 | background-color: #525252; } 141 | table.dataTable.display tbody tr.odd > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd > .sorting_1 { 142 | background-color: black; } 143 | table.dataTable.display tbody tr.odd > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd > .sorting_2 { 144 | background-color: black; } 145 | table.dataTable.display tbody tr.odd > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd > .sorting_3 { 146 | background-color: black; } 147 | table.dataTable.display tbody tr.odd.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_1 { 148 | background-color: #4f4f4f; } 149 | table.dataTable.display tbody tr.odd.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_2 { 150 | background-color: #505050; } 151 | table.dataTable.display tbody tr.odd.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_3 { 152 | background-color: #505050; } 153 | table.dataTable.display tbody tr.even > .sorting_1, table.dataTable.order-column.stripe tbody tr.even > .sorting_1 { 154 | background-color: black; } 155 | table.dataTable.display tbody tr.even > .sorting_2, table.dataTable.order-column.stripe tbody tr.even > .sorting_2 { 156 | background-color: black; } 157 | table.dataTable.display tbody tr.even > .sorting_3, table.dataTable.order-column.stripe tbody tr.even > .sorting_3 { 158 | background-color: black; } 159 | table.dataTable.display tbody tr.even.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_1 { 160 | background-color: #525252; } 161 | table.dataTable.display tbody tr.even.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_2 { 162 | background-color: #525252; } 163 | table.dataTable.display tbody tr.even.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_3 { 164 | background-color: #535353; } 165 | table.dataTable.display tbody tr:hover > .sorting_1, table.dataTable.order-column.hover tbody tr:hover > .sorting_1 { 166 | background-color: black; } 167 | table.dataTable.display tbody tr:hover > .sorting_2, table.dataTable.order-column.hover tbody tr:hover > .sorting_2 { 168 | background-color: black; } 169 | table.dataTable.display tbody tr:hover > .sorting_3, table.dataTable.order-column.hover tbody tr:hover > .sorting_3 { 170 | background-color: black; } 171 | table.dataTable.display tbody tr:hover.selected > .sorting_1, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_1 { 172 | background-color: #4d4d4d; } 173 | table.dataTable.display tbody tr:hover.selected > .sorting_2, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_2 { 174 | background-color: #4d4d4d; } 175 | table.dataTable.display tbody tr:hover.selected > .sorting_3, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_3 { 176 | background-color: #4e4e4e; } 177 | table.dataTable.no-footer { 178 | border-bottom: 1px solid #111111; } 179 | table.dataTable.nowrap th, table.dataTable.nowrap td { 180 | white-space: nowrap; } 181 | table.dataTable.compact thead th, 182 | table.dataTable.compact thead td { 183 | padding: 4px 17px; } 184 | table.dataTable.compact tfoot th, 185 | table.dataTable.compact tfoot td { 186 | padding: 4px; } 187 | table.dataTable.compact tbody th, 188 | table.dataTable.compact tbody td { 189 | padding: 4px; } 190 | table.dataTable th.dt-left, 191 | table.dataTable td.dt-left { 192 | text-align: left; } 193 | table.dataTable th.dt-center, 194 | table.dataTable td.dt-center, 195 | table.dataTable td.dataTables_empty { 196 | text-align: center; } 197 | table.dataTable th.dt-right, 198 | table.dataTable td.dt-right { 199 | text-align: right; } 200 | table.dataTable th.dt-justify, 201 | table.dataTable td.dt-justify { 202 | text-align: justify; } 203 | table.dataTable th.dt-nowrap, 204 | table.dataTable td.dt-nowrap { 205 | white-space: nowrap; } 206 | table.dataTable thead th.dt-head-left, 207 | table.dataTable thead td.dt-head-left, 208 | table.dataTable tfoot th.dt-head-left, 209 | table.dataTable tfoot td.dt-head-left { 210 | text-align: left; } 211 | table.dataTable thead th.dt-head-center, 212 | table.dataTable thead td.dt-head-center, 213 | table.dataTable tfoot th.dt-head-center, 214 | table.dataTable tfoot td.dt-head-center { 215 | text-align: center; } 216 | table.dataTable thead th.dt-head-right, 217 | table.dataTable thead td.dt-head-right, 218 | table.dataTable tfoot th.dt-head-right, 219 | table.dataTable tfoot td.dt-head-right { 220 | text-align: right; } 221 | table.dataTable thead th.dt-head-justify, 222 | table.dataTable thead td.dt-head-justify, 223 | table.dataTable tfoot th.dt-head-justify, 224 | table.dataTable tfoot td.dt-head-justify { 225 | text-align: justify; } 226 | table.dataTable thead th.dt-head-nowrap, 227 | table.dataTable thead td.dt-head-nowrap, 228 | table.dataTable tfoot th.dt-head-nowrap, 229 | table.dataTable tfoot td.dt-head-nowrap { 230 | white-space: nowrap; } 231 | table.dataTable tbody th.dt-body-left, 232 | table.dataTable tbody td.dt-body-left { 233 | text-align: left; } 234 | table.dataTable tbody th.dt-body-center, 235 | table.dataTable tbody td.dt-body-center { 236 | text-align: center; } 237 | table.dataTable tbody th.dt-body-right, 238 | table.dataTable tbody td.dt-body-right { 239 | text-align: right; } 240 | table.dataTable tbody th.dt-body-justify, 241 | table.dataTable tbody td.dt-body-justify { 242 | text-align: justify; } 243 | table.dataTable tbody th.dt-body-nowrap, 244 | table.dataTable tbody td.dt-body-nowrap { 245 | white-space: nowrap; } 246 | 247 | table.dataTable, 248 | table.dataTable th, 249 | table.dataTable td { 250 | box-sizing: content-box; } 251 | 252 | /* 253 | * Control feature layout 254 | */ 255 | .dataTables_wrapper { 256 | position: relative; 257 | clear: both; 258 | *zoom: 1; 259 | zoom: 1; } 260 | .dataTables_wrapper .dataTables_length { 261 | float: left; } 262 | .dataTables_wrapper .dataTables_filter { 263 | float: right; 264 | text-align: right; } 265 | .dataTables_wrapper .dataTables_filter input { 266 | margin-left: 0.5em; } 267 | .dataTables_wrapper .dataTables_info { 268 | clear: both; 269 | float: left; 270 | padding-top: 0.755em; } 271 | .dataTables_wrapper .dataTables_paginate { 272 | float: right; 273 | text-align: right; 274 | padding-top: 0.25em; } 275 | .dataTables_wrapper .dataTables_paginate .paginate_button { 276 | box-sizing: border-box; 277 | display: inline-block; 278 | min-width: 1.5em; 279 | padding: 0.5em 1em; 280 | margin-left: 2px; 281 | text-align: center; 282 | text-decoration: none !important; 283 | cursor: pointer; 284 | *cursor: hand; 285 | color: white !important; 286 | border: 1px solid transparent; 287 | border-radius: 2px; } 288 | .dataTables_wrapper .dataTables_paginate .paginate_button.current, .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover { 289 | color: white !important; 290 | border: 1px solid #979797; 291 | background-color: white; 292 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, white), color-stop(100%, gainsboro)); 293 | /* Chrome,Safari4+ */ 294 | background: -webkit-linear-gradient(top, white 0%, gainsboro 100%); 295 | /* Chrome10+,Safari5.1+ */ 296 | background: -moz-linear-gradient(top, white 0%, gainsboro 100%); 297 | /* FF3.6+ */ 298 | background: -ms-linear-gradient(top, white 0%, gainsboro 100%); 299 | /* IE10+ */ 300 | background: -o-linear-gradient(top, white 0%, gainsboro 100%); 301 | /* Opera 11.10+ */ 302 | background: linear-gradient(to bottom, white 0%, gainsboro 100%); 303 | /* W3C */ } 304 | .dataTables_wrapper .dataTables_paginate .paginate_button.disabled, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active { 305 | cursor: default; 306 | color: #666 !important; 307 | border: 1px solid transparent; 308 | background: transparent; 309 | box-shadow: none; } 310 | .dataTables_wrapper .dataTables_paginate .paginate_button:hover { 311 | color: white !important; 312 | border: 1px solid #d6d6d6; 313 | background-color: white; 314 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, white), color-stop(100%, #d6d6d6)); 315 | /* Chrome,Safari4+ */ 316 | background: -webkit-linear-gradient(top, white 0%, #d6d6d6 100%); 317 | /* Chrome10+,Safari5.1+ */ 318 | background: -moz-linear-gradient(top, white 0%, #d6d6d6 100%); 319 | /* FF3.6+ */ 320 | background: -ms-linear-gradient(top, white 0%, #d6d6d6 100%); 321 | /* IE10+ */ 322 | background: -o-linear-gradient(top, white 0%, #d6d6d6 100%); 323 | /* Opera 11.10+ */ 324 | background: linear-gradient(to bottom, white 0%, #d6d6d6 100%); 325 | /* W3C */ } 326 | .dataTables_wrapper .dataTables_paginate .paginate_button:active { 327 | outline: none; 328 | background-color: #f0f0f0; 329 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #f0f0f0), color-stop(100%, #d1d1d1)); 330 | /* Chrome,Safari4+ */ 331 | background: -webkit-linear-gradient(top, #f0f0f0 0%, #d1d1d1 100%); 332 | /* Chrome10+,Safari5.1+ */ 333 | background: -moz-linear-gradient(top, #f0f0f0 0%, #d1d1d1 100%); 334 | /* FF3.6+ */ 335 | background: -ms-linear-gradient(top, #f0f0f0 0%, #d1d1d1 100%); 336 | /* IE10+ */ 337 | background: -o-linear-gradient(top, #f0f0f0 0%, #d1d1d1 100%); 338 | /* Opera 11.10+ */ 339 | background: linear-gradient(to bottom, #f0f0f0 0%, #d1d1d1 100%); 340 | /* W3C */ 341 | box-shadow: inset 0 0 3px #111; } 342 | .dataTables_wrapper .dataTables_paginate .ellipsis { 343 | padding: 0 1em; } 344 | .dataTables_wrapper .dataTables_processing { 345 | position: absolute; 346 | top: 50%; 347 | left: 50%; 348 | width: 100%; 349 | height: 40px; 350 | margin-left: -50%; 351 | margin-top: -25px; 352 | padding-top: 20px; 353 | text-align: center; 354 | font-size: 1.2em; 355 | background-color: white; 356 | background: -webkit-gradient(linear, left top, right top, color-stop(0%, transparent), color-stop(25%, rgba(0, 0, 0, 0.9)), color-stop(75%, rgba(0, 0, 0, 0.9)), color-stop(100%, rgba(255, 255, 255, 0))); 357 | background: -webkit-linear-gradient(left, transparent 0%, rgba(0, 0, 0, 0.9) 25%, rgba(0, 0, 0, 0.9) 75%, transparent 100%); 358 | background: -moz-linear-gradient(left, transparent 0%, rgba(0, 0, 0, 0.9) 25%, rgba(0, 0, 0, 0.9) 75%, transparent 100%); 359 | background: -ms-linear-gradient(left, transparent 0%, rgba(0, 0, 0, 0.9) 25%, rgba(0, 0, 0, 0.9) 75%, transparent 100%); 360 | background: -o-linear-gradient(left, transparent 0%, rgba(0, 0, 0, 0.9) 25%, rgba(0, 0, 0, 0.9) 75%, transparent 100%); 361 | background: linear-gradient(to right, transparent 0%, rgba(0, 0, 0, 0.9) 25%, rgba(0, 0, 0, 0.9) 75%, transparent 100%); } 362 | .dataTables_wrapper .dataTables_length, 363 | .dataTables_wrapper .dataTables_filter, 364 | .dataTables_wrapper .dataTables_info, 365 | .dataTables_wrapper .dataTables_processing, 366 | .dataTables_wrapper .dataTables_paginate { 367 | color: white; } 368 | .dataTables_wrapper .dataTables_scroll { 369 | clear: both; } 370 | .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody { 371 | *margin-top: -1px; 372 | -webkit-overflow-scrolling: touch; } 373 | .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td { 374 | vertical-align: middle; } 375 | .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th > div.dataTables_sizing, 376 | .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td > div.dataTables_sizing, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th > div.dataTables_sizing, 377 | .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td > div.dataTables_sizing { 378 | height: 0; 379 | overflow: hidden; 380 | margin: 0 !important; 381 | padding: 0 !important; } 382 | .dataTables_wrapper.no-footer .dataTables_scrollBody { 383 | border-bottom: 1px solid #111111; } 384 | .dataTables_wrapper.no-footer div.dataTables_scrollHead table.dataTable, 385 | .dataTables_wrapper.no-footer div.dataTables_scrollBody > table { 386 | border-bottom: none; } 387 | .dataTables_wrapper:after { 388 | visibility: hidden; 389 | display: block; 390 | content: ""; 391 | clear: both; 392 | height: 0; } 393 | 394 | @media screen and (max-width: 767px) { 395 | .dataTables_wrapper .dataTables_info, 396 | .dataTables_wrapper .dataTables_paginate { 397 | float: none; 398 | text-align: center; } 399 | .dataTables_wrapper .dataTables_paginate { 400 | margin-top: 0.5em; } } 401 | @media screen and (max-width: 640px) { 402 | .dataTables_wrapper .dataTables_length, 403 | .dataTables_wrapper .dataTables_filter { 404 | float: none; 405 | text-align: center; } 406 | .dataTables_wrapper .dataTables_filter { 407 | margin-top: 0.5em; } } 408 | -------------------------------------------------------------------------------- /public/css/pure-min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Pure v0.6.0 3 | Copyright 2014 Yahoo! Inc. All rights reserved. 4 | Licensed under the BSD License. 5 | https://github.com/yahoo/pure/blob/master/LICENSE.md 6 | */ 7 | /*! 8 | normalize.css v^3.0 | MIT License | git.io/normalize 9 | Copyright (c) Nicolas Gallagher and Jonathan Neal 10 | */ 11 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block}.pure-g{letter-spacing:-.31em;*letter-spacing:normal;*word-spacing:-.43em;text-rendering:optimizespeed;font-family:FreeSans,Arimo,"Droid Sans",Helvetica,Arial,sans-serif;display:-webkit-flex;-webkit-flex-flow:row wrap;display:-ms-flexbox;-ms-flex-flow:row wrap;-ms-align-content:flex-start;-webkit-align-content:flex-start;align-content:flex-start}.opera-only :-o-prefocus,.pure-g{word-spacing:-.43em}.pure-u{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-g [class *="pure-u"]{font-family:sans-serif}.pure-u-1,.pure-u-1-1,.pure-u-1-2,.pure-u-1-3,.pure-u-2-3,.pure-u-1-4,.pure-u-3-4,.pure-u-1-5,.pure-u-2-5,.pure-u-3-5,.pure-u-4-5,.pure-u-5-5,.pure-u-1-6,.pure-u-5-6,.pure-u-1-8,.pure-u-3-8,.pure-u-5-8,.pure-u-7-8,.pure-u-1-12,.pure-u-5-12,.pure-u-7-12,.pure-u-11-12,.pure-u-1-24,.pure-u-2-24,.pure-u-3-24,.pure-u-4-24,.pure-u-5-24,.pure-u-6-24,.pure-u-7-24,.pure-u-8-24,.pure-u-9-24,.pure-u-10-24,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-1-24{width:4.1667%;*width:4.1357%}.pure-u-1-12,.pure-u-2-24{width:8.3333%;*width:8.3023%}.pure-u-1-8,.pure-u-3-24{width:12.5%;*width:12.469%}.pure-u-1-6,.pure-u-4-24{width:16.6667%;*width:16.6357%}.pure-u-1-5{width:20%;*width:19.969%}.pure-u-5-24{width:20.8333%;*width:20.8023%}.pure-u-1-4,.pure-u-6-24{width:25%;*width:24.969%}.pure-u-7-24{width:29.1667%;*width:29.1357%}.pure-u-1-3,.pure-u-8-24{width:33.3333%;*width:33.3023%}.pure-u-3-8,.pure-u-9-24{width:37.5%;*width:37.469%}.pure-u-2-5{width:40%;*width:39.969%}.pure-u-5-12,.pure-u-10-24{width:41.6667%;*width:41.6357%}.pure-u-11-24{width:45.8333%;*width:45.8023%}.pure-u-1-2,.pure-u-12-24{width:50%;*width:49.969%}.pure-u-13-24{width:54.1667%;*width:54.1357%}.pure-u-7-12,.pure-u-14-24{width:58.3333%;*width:58.3023%}.pure-u-3-5{width:60%;*width:59.969%}.pure-u-5-8,.pure-u-15-24{width:62.5%;*width:62.469%}.pure-u-2-3,.pure-u-16-24{width:66.6667%;*width:66.6357%}.pure-u-17-24{width:70.8333%;*width:70.8023%}.pure-u-3-4,.pure-u-18-24{width:75%;*width:74.969%}.pure-u-19-24{width:79.1667%;*width:79.1357%}.pure-u-4-5{width:80%;*width:79.969%}.pure-u-5-6,.pure-u-20-24{width:83.3333%;*width:83.3023%}.pure-u-7-8,.pure-u-21-24{width:87.5%;*width:87.469%}.pure-u-11-12,.pure-u-22-24{width:91.6667%;*width:91.6357%}.pure-u-23-24{width:95.8333%;*width:95.8023%}.pure-u-1,.pure-u-1-1,.pure-u-5-5,.pure-u-24-24{width:100%}.pure-button{display:inline-block;zoom:1;line-height:normal;white-space:nowrap;vertical-align:middle;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button{font-family:inherit;font-size:100%;padding:.5em 1em;color:#444;color:rgba(0,0,0,.8);border:1px solid #999;border:0 rgba(0,0,0,0);background-color:#E6E6E6;text-decoration:none;border-radius:2px}.pure-button-hover,.pure-button:hover,.pure-button:focus{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000', GradientType=0);background-image:-webkit-gradient(linear,0 0,0 100%,from(transparent),color-stop(40%,rgba(0,0,0,.05)),to(rgba(0,0,0,.1)));background-image:-webkit-linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1));background-image:-moz-linear-gradient(top,rgba(0,0,0,.05) 0,rgba(0,0,0,.1));background-image:-o-linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1));background-image:linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1))}.pure-button:focus{outline:0}.pure-button-active,.pure-button:active{box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;border-color:#000\9}.pure-button[disabled],.pure-button-disabled,.pure-button-disabled:hover,.pure-button-disabled:focus,.pure-button-disabled:active{border:0;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);filter:alpha(opacity=40);-khtml-opacity:.4;-moz-opacity:.4;opacity:.4;cursor:not-allowed;box-shadow:none}.pure-button-hidden{display:none}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button-primary,.pure-button-selected,a.pure-button-primary,a.pure-button-selected{background-color:#0078e7;color:#fff}.pure-form input[type=text],.pure-form input[type=password],.pure-form input[type=email],.pure-form input[type=url],.pure-form input[type=date],.pure-form input[type=month],.pure-form input[type=time],.pure-form input[type=datetime],.pure-form input[type=datetime-local],.pure-form input[type=week],.pure-form input[type=number],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=color],.pure-form select,.pure-form textarea{padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;vertical-align:middle;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-form input:not([type]){padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-form input[type=color]{padding:.2em .5em}.pure-form input[type=text]:focus,.pure-form input[type=password]:focus,.pure-form input[type=email]:focus,.pure-form input[type=url]:focus,.pure-form input[type=date]:focus,.pure-form input[type=month]:focus,.pure-form input[type=time]:focus,.pure-form input[type=datetime]:focus,.pure-form input[type=datetime-local]:focus,.pure-form input[type=week]:focus,.pure-form input[type=number]:focus,.pure-form input[type=search]:focus,.pure-form input[type=tel]:focus,.pure-form input[type=color]:focus,.pure-form select:focus,.pure-form textarea:focus{outline:0;border-color:#129FEA}.pure-form input:not([type]):focus{outline:0;border-color:#129FEA}.pure-form input[type=file]:focus,.pure-form input[type=radio]:focus,.pure-form input[type=checkbox]:focus{outline:thin solid #129FEA;outline:1px auto #129FEA}.pure-form .pure-checkbox,.pure-form .pure-radio{margin:.5em 0;display:block}.pure-form input[type=text][disabled],.pure-form input[type=password][disabled],.pure-form input[type=email][disabled],.pure-form input[type=url][disabled],.pure-form input[type=date][disabled],.pure-form input[type=month][disabled],.pure-form input[type=time][disabled],.pure-form input[type=datetime][disabled],.pure-form input[type=datetime-local][disabled],.pure-form input[type=week][disabled],.pure-form input[type=number][disabled],.pure-form input[type=search][disabled],.pure-form input[type=tel][disabled],.pure-form input[type=color][disabled],.pure-form select[disabled],.pure-form textarea[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input:not([type])[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input[readonly],.pure-form select[readonly],.pure-form textarea[readonly]{background-color:#eee;color:#777;border-color:#ccc}.pure-form input:focus:invalid,.pure-form textarea:focus:invalid,.pure-form select:focus:invalid{color:#b94a48;border-color:#e9322d}.pure-form input[type=file]:focus:invalid:focus,.pure-form input[type=radio]:focus:invalid:focus,.pure-form input[type=checkbox]:focus:invalid:focus{outline-color:#e9322d}.pure-form select{height:2.25em;border:1px solid #ccc;background-color:#fff}.pure-form select[multiple]{height:auto}.pure-form label{margin:.5em 0 .2em}.pure-form fieldset{margin:0;padding:.35em 0 .75em;border:0}.pure-form legend{display:block;width:100%;padding:.3em 0;margin-bottom:.3em;color:#333;border-bottom:1px solid #e5e5e5}.pure-form-stacked input[type=text],.pure-form-stacked input[type=password],.pure-form-stacked input[type=email],.pure-form-stacked input[type=url],.pure-form-stacked input[type=date],.pure-form-stacked input[type=month],.pure-form-stacked input[type=time],.pure-form-stacked input[type=datetime],.pure-form-stacked input[type=datetime-local],.pure-form-stacked input[type=week],.pure-form-stacked input[type=number],.pure-form-stacked input[type=search],.pure-form-stacked input[type=tel],.pure-form-stacked input[type=color],.pure-form-stacked input[type=file],.pure-form-stacked select,.pure-form-stacked label,.pure-form-stacked textarea{display:block;margin:.25em 0}.pure-form-stacked input:not([type]){display:block;margin:.25em 0}.pure-form-aligned input,.pure-form-aligned textarea,.pure-form-aligned select,.pure-form-aligned .pure-help-inline,.pure-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.pure-form-aligned textarea{vertical-align:top}.pure-form-aligned .pure-control-group{margin-bottom:.5em}.pure-form-aligned .pure-control-group label{text-align:right;display:inline-block;vertical-align:middle;width:10em;margin:0 1em 0 0}.pure-form-aligned .pure-controls{margin:1.5em 0 0 11em}.pure-form input.pure-input-rounded,.pure-form .pure-input-rounded{border-radius:2em;padding:.5em 1em}.pure-form .pure-group fieldset{margin-bottom:10px}.pure-form .pure-group input,.pure-form .pure-group textarea{display:block;padding:10px;margin:0 0 -1px;border-radius:0;position:relative;top:-1px}.pure-form .pure-group input:focus,.pure-form .pure-group textarea:focus{z-index:3}.pure-form .pure-group input:first-child,.pure-form .pure-group textarea:first-child{top:1px;border-radius:4px 4px 0 0;margin:0}.pure-form .pure-group input:first-child:last-child,.pure-form .pure-group textarea:first-child:last-child{top:1px;border-radius:4px;margin:0}.pure-form .pure-group input:last-child,.pure-form .pure-group textarea:last-child{top:-2px;border-radius:0 0 4px 4px;margin:0}.pure-form .pure-group button{margin:.35em 0}.pure-form .pure-input-1{width:100%}.pure-form .pure-input-2-3{width:66%}.pure-form .pure-input-1-2{width:50%}.pure-form .pure-input-1-3{width:33%}.pure-form .pure-input-1-4{width:25%}.pure-form .pure-help-inline,.pure-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:.875em}.pure-form-message{display:block;color:#666;font-size:.875em}@media only screen and (max-width :480px){.pure-form button[type=submit]{margin:.7em 0 0}.pure-form input:not([type]),.pure-form input[type=text],.pure-form input[type=password],.pure-form input[type=email],.pure-form input[type=url],.pure-form input[type=date],.pure-form input[type=month],.pure-form input[type=time],.pure-form input[type=datetime],.pure-form input[type=datetime-local],.pure-form input[type=week],.pure-form input[type=number],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=color],.pure-form label{margin-bottom:.3em;display:block}.pure-group input:not([type]),.pure-group input[type=text],.pure-group input[type=password],.pure-group input[type=email],.pure-group input[type=url],.pure-group input[type=date],.pure-group input[type=month],.pure-group input[type=time],.pure-group input[type=datetime],.pure-group input[type=datetime-local],.pure-group input[type=week],.pure-group input[type=number],.pure-group input[type=search],.pure-group input[type=tel],.pure-group input[type=color]{margin-bottom:0}.pure-form-aligned .pure-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.pure-form-aligned .pure-controls{margin:1.5em 0 0}.pure-form .pure-help-inline,.pure-form-message-inline,.pure-form-message{display:block;font-size:.75em;padding:.2em 0 .8em}}.pure-menu{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-menu-fixed{position:fixed;left:0;top:0;z-index:3}.pure-menu-list,.pure-menu-item{position:relative}.pure-menu-list{list-style:none;margin:0;padding:0}.pure-menu-item{padding:0;margin:0;height:100%}.pure-menu-link,.pure-menu-heading{display:block;text-decoration:none;white-space:nowrap}.pure-menu-horizontal{width:100%;white-space:nowrap}.pure-menu-horizontal .pure-menu-list{display:inline-block}.pure-menu-horizontal .pure-menu-item,.pure-menu-horizontal .pure-menu-heading,.pure-menu-horizontal .pure-menu-separator{display:inline-block;*display:inline;zoom:1;vertical-align:middle}.pure-menu-item .pure-menu-item{display:block}.pure-menu-children{display:none;position:absolute;left:100%;top:0;margin:0;padding:0;z-index:3}.pure-menu-horizontal .pure-menu-children{left:0;top:auto;width:inherit}.pure-menu-allow-hover:hover>.pure-menu-children,.pure-menu-active>.pure-menu-children{display:block;position:absolute}.pure-menu-has-children>.pure-menu-link:after{padding-left:.5em;content:"\25B8";font-size:small}.pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after{content:"\25BE"}.pure-menu-scrollable{overflow-y:scroll;overflow-x:hidden}.pure-menu-scrollable .pure-menu-list{display:block}.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list{display:inline-block}.pure-menu-horizontal.pure-menu-scrollable{white-space:nowrap;overflow-y:hidden;overflow-x:auto;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;padding:.5em 0}.pure-menu-horizontal.pure-menu-scrollable::-webkit-scrollbar{display:none}.pure-menu-separator{background-color:#ccc;height:1px;margin:.3em 0}.pure-menu-horizontal .pure-menu-separator{width:1px;height:1.3em;margin:0 .3em}.pure-menu-heading{text-transform:uppercase;color:#565d64}.pure-menu-link{color:#777}.pure-menu-children{background-color:#fff}.pure-menu-link,.pure-menu-disabled,.pure-menu-heading{padding:.5em 1em}.pure-menu-disabled{opacity:.5}.pure-menu-disabled .pure-menu-link:hover{background-color:transparent}.pure-menu-active>.pure-menu-link,.pure-menu-link:hover,.pure-menu-link:focus{background-color:#eee}.pure-menu-selected .pure-menu-link,.pure-menu-selected .pure-menu-link:visited{color:#000}.pure-table{border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid #cbcbcb}.pure-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.pure-table td,.pure-table th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;font-size:inherit;margin:0;overflow:visible;padding:.5em 1em}.pure-table td:first-child,.pure-table th:first-child{border-left-width:0}.pure-table thead{background-color:#e0e0e0;color:#000;text-align:left;vertical-align:bottom}.pure-table td{background-color:transparent}.pure-table-odd td{background-color:#f2f2f2}.pure-table-striped tr:nth-child(2n-1) td{background-color:#f2f2f2}.pure-table-bordered td{border-bottom:1px solid #cbcbcb}.pure-table-bordered tbody>tr:last-child>td{border-bottom-width:0}.pure-table-horizontal td,.pure-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #cbcbcb}.pure-table-horizontal tbody>tr:last-child>td{border-bottom-width:0} -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | run(); 31 | -------------------------------------------------------------------------------- /public/js/marketbrowser-post.js: -------------------------------------------------------------------------------- 1 | $(document).ready( function () { 2 | loadMarketGroupsBase(); 3 | $('#selldata').DataTable({ 4 | paging: false, 5 | searching: false, 6 | "info": false, 7 | responsive: true, 8 | "columns": [ 9 | {type:'date'}, 10 | {type:'num-fmt',responsivePriority: 3}, 11 | {type:'num-fmt'}, 12 | {type:'num-fmt'}, 13 | {type:'num-fmt',responsivePriority: 1}, 14 | {type:'num-fmt'}, 15 | {type:'html',responsivePriority: 2}, 16 | {type:'text'}, 17 | ] 18 | }); 19 | 20 | $('#buydata').DataTable({ 21 | paging: false, 22 | searching: false, 23 | "info": false, 24 | responsive: true, 25 | "columns": [ 26 | {type:'date'}, 27 | {type:'num-fmt',responsivePriority: 3}, 28 | {type:'num-fmt'}, 29 | {type:'num-fmt'}, 30 | {type:'num-fmt',responsivePriority: 1}, 31 | {type:'num-fmt'}, 32 | {type:'num-fmt'}, 33 | {type:'html',responsivePriority: 2}, 34 | {type:'text'}, 35 | ] 36 | }); 37 | 38 | 39 | $('#selltab').click(function(event){ 40 | event.stopPropagation(); 41 | $("#buydata_wrapper").hide(); 42 | $("#selldata_wrapper").show(); 43 | $("#selldata").DataTable().columns.adjust().responsive.recalc() 44 | $('#selltab').toggleClass("selectedtab"); 45 | $('#buytab').toggleClass("selectedtab"); 46 | }); 47 | $('#buytab').click(function(event){ 48 | event.stopPropagation(); 49 | $("#selldata_wrapper").hide(); 50 | $("#buydata_wrapper").show(); 51 | $("#buydata").DataTable().columns.adjust().responsive.recalc() 52 | $('#selltab').toggleClass("selectedtab"); 53 | $('#buytab').toggleClass("selectedtab"); 54 | }); 55 | 56 | $(function() { 57 | $.getJSON( "/api/typeids", function( data ) { 58 | $( "#searchbarInput" ).autocomplete({ 59 | source: data, 60 | minLength: 2, 61 | select: function( event, ui ) { 62 | event.preventDefault(); 63 | $( "#searchbarInput" ).val(ui.item.label); 64 | loadItem(ui.item.value); 65 | } 66 | }); 67 | }); 68 | }); 69 | 70 | 71 | 72 | } ); 73 | $('#regionselector').change(function(){ 74 | 75 | regionid=$('#regionselector').val(); 76 | if (selecteditem!=0){ 77 | loadItem(selecteditem); 78 | } 79 | 80 | 81 | }); 82 | -------------------------------------------------------------------------------- /public/js/marketbrowser.js: -------------------------------------------------------------------------------- 1 | var marketGroupsBase="https://market.fuzzwork.co.uk/api/marketgroup/"; 2 | 3 | var regionid="10000002"; 4 | var selecteditem=""; 5 | 6 | $.fn.dataTable.ext.order.intl = function ( locales, options ) { 7 | if ( window.Intl ) { 8 | var collator = new window.Intl.Collator( locales, options ); 9 | var types = $.fn.dataTable.ext.type; 10 | 11 | delete types.order['string-pre']; 12 | types.order['string-asc'] = collator.compare; 13 | types.order['string-desc'] = function ( a, b ) { 14 | return collator.compare( a, b ) * -1; 15 | }; 16 | } 17 | }; 18 | 19 | 20 | 21 | function loadMarketGroupsBase() { 22 | $.getJSON(marketGroupsBase+"0/",function(data,status,xhr) { 23 | $.map(data.marketgroups,function(group){ 24 | $("#browsemenulist").append(""); 25 | } 26 | ); 27 | $('.groupLink').click(function(event){event.stopPropagation();openSubGroup(event.target);}); 28 | }); 29 | } 30 | 31 | function openSubGroup(group) 32 | { 33 | var node; 34 | var itemcount=0; 35 | if ($(group).children('ul').length>0) { 36 | $(group).children('ul').toggle(); 37 | } else { 38 | $(group).append(''); 39 | node=$(group).children('ul'); 40 | $.getJSON(marketGroupsBase+group.dataset.groupid+"/",function(data,status,xhr) { 41 | $.map(data.marketgroups,function(item){ 42 | node.append(""); 43 | }); 44 | $.map(data.types,function(item){ 45 | node.append(""); 46 | }); 47 | $('.itemLink').click(function(event){event.stopPropagation();updateInfo(event.target.dataset.type);}); 48 | }); 49 | } 50 | } 51 | 52 | function ccpRound(number,digits) { 53 | return Number.parseFloat(number).toPrecision(digits) 54 | } 55 | 56 | function updateInfo(itemid) 57 | { 58 | selecteditem=itemid 59 | loadItem(itemid); 60 | try { 61 | var stateObj = {}; 62 | // history.pushState(stateObj, itemid, "/browser/"+regionid+"/"+itemid+"/"); 63 | } catch(err) { console.log("No pushstate"); } 64 | } 65 | 66 | function loadItem(type) { 67 | selecteditem=type 68 | if (isFinite(type)) { 69 | lookupUrl="https://market.fuzzwork.co.uk/api/region/"+regionid+"/type/"+type+"/"; 70 | } else { 71 | lookupUrl=type; 72 | } 73 | $.getJSON(lookupUrl,function(data){ 74 | $.fn.dataTable.ext.order.intl(); 75 | selldata=$('#selldata').DataTable(); 76 | selldata.rows().remove(); 77 | for (var order in data.sellorders) { 78 | if (ccpRound(data.sellorders[order].security)>0.5) { 79 | secclass="high"; 80 | } else if (ccpRound(data.sellorders[order].security)>0 ) { 81 | secclass="low"; 82 | } else { 83 | secclass="null"; 84 | } 85 | newrow=selldata.row.add([data.sellorders[order].issued,new Intl.NumberFormat().format(data.sellorders[order].volume),new Intl.NumberFormat().format(data.sellorders[order].volumeEntered),new Intl.NumberFormat().format(data.sellorders[order].minVolume),new Intl.NumberFormat().format(data.sellorders[order].price),data.sellorders[order].duration,""+ccpRound(data.sellorders[order].security,1)+""+data.sellorders[order].stationName,data.sellorders[order].regionName]); 86 | } 87 | selldata.draw(); 88 | selldata.columns.adjust().draw(); 89 | buydata=$('#buydata').DataTable(); 90 | buydata.rows().remove(); 91 | for (var order in data.buyorders) { 92 | if (ccpRound(data.buyorders[order].security)>0.5) { 93 | secclass="high"; 94 | } else if (ccpRound(data.buyorders[order].security)>0 ) { 95 | secclass="low"; 96 | } else { 97 | secclass="null"; 98 | } 99 | newrow=buydata.row.add([data.buyorders[order].issued,new Intl.NumberFormat().format(data.buyorders[order].volume),new Intl.NumberFormat().format(data.buyorders[order].volumeEntered),new Intl.NumberFormat().format(data.buyorders[order].minVolume),new Intl.NumberFormat().format(data.buyorders[order].price),data.buyorders[order].range,new Intl.NumberFormat().format(data.buyorders[order].duration),""+ccpRound(data.buyorders[order].security,1)+""+data.buyorders[order].stationName,data.buyorders[order].regionName]); 100 | } 101 | buydata.draw(); 102 | buydata.columns.adjust().draw(); 103 | $("#leftdata").empty(); 104 | $("#rightdata").empty(); 105 | $("#leftdata").append("

"+data.typename+"

") 106 | 107 | 108 | }); 109 | } 110 | 111 | -------------------------------------------------------------------------------- /scripts/aggloader-esi.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine, Column, MetaData, Table, Index 2 | from sqlalchemy import Integer, String, Text, Float, Boolean, BigInteger, Numeric, SmallInteger, DateTime 3 | import time 4 | import requests 5 | from requests_futures.sessions import FuturesSession 6 | import requests_futures 7 | from concurrent.futures import as_completed 8 | import datetime 9 | import csv 10 | import time 11 | import sys 12 | import re 13 | import pandas 14 | import numpy 15 | import redis 16 | import json 17 | import os 18 | import shutil 19 | import base64 20 | 21 | import gzip 22 | from StringIO import StringIO 23 | 24 | 25 | import logging 26 | logging.basicConfig(filename='logs/aggloader-esi.log',level=logging.INFO,format='%(asctime)s %(levelname)s %(message)s') 27 | 28 | 29 | 30 | def RateLimited(maxPerSecond): 31 | minInterval = 1.0 / float(maxPerSecond) 32 | def decorate(func): 33 | lastTimeCalled = [0.0] 34 | def rateLimitedFunction(*args,**kargs): 35 | elapsed = time.clock() - lastTimeCalled[0] 36 | leftToWait = minInterval - elapsed 37 | if leftToWait>0: 38 | time.sleep(leftToWait) 39 | ret = func(*args,**kargs) 40 | lastTimeCalled[0] = time.clock() 41 | return ret 42 | return rateLimitedFunction 43 | return decorate 44 | 45 | 46 | 47 | 48 | def processData(result,orderwriter,ordersetid,connection,orderTable): 49 | 50 | try: 51 | resp=result.result() 52 | regionid=result.region 53 | logging.info('Process {} {} {} {}'.format(resp.status_code,result.url,result.retry,result.region)) 54 | if resp.status_code==200: 55 | try: 56 | orders=resp.json() 57 | logging.info('{} orders on page {} {}'.format(len(orders),result.fullurl,result.page)) 58 | for order in orders: 59 | if not result.structure and int(order['location_id'])>100000000 and order['is_buy_order']: 60 | pass 61 | else: 62 | orderwriter.writerow([order['order_id'], 63 | order['type_id'], 64 | order['issued'], 65 | order['is_buy_order'], 66 | order['volume_remain'], 67 | order['volume_total'], 68 | order['min_volume'], 69 | order['price'], 70 | order['location_id'], 71 | order['range'], 72 | order['duration'], 73 | regionid, 74 | ordersetid] 75 | ) 76 | 77 | if len(orders)>0: 78 | nextpage=result.url 79 | else: 80 | nextpage=None 81 | logging.info('{}: next page {}'.format(result.url,nextpage)) 82 | return {'retry':0,'url':nextpage,'region':result.region,'page':result.page+1,'structure':result.structure} 83 | except: 84 | logging.error("URL: {} could not be parsed".format(result.url)) 85 | file = open("logs/{}-{}.txt".format(result.region,result.page),"wb") 86 | file.write(resp.content) 87 | file.close() 88 | elif resp.status_code==403: 89 | logging.error("403 status. {} returned {}".format(resp.url,resp.status_code)) 90 | return {'retry':4} 91 | else: 92 | logging.error("Non 200 status. {} returned {} on retry {}".format(resp.url,resp.status_code,result.retry)) 93 | return {'retry':result.retry+1,'url':result.url,'region':result.region,'page':result.page,'structure':result.structure} 94 | except requests.exceptions.ConnectionError as e: 95 | logging.error(e) 96 | return {'retry':result.retry+1,'url':result.url,'region':result.region,'page':result.page,'structure':result.structure} 97 | return {'retry':result.retry+1,'url':result.url,'region':result.region,'page':result.page,'structure':result.structure} 98 | 99 | 100 | 101 | 102 | 103 | @RateLimited(150) 104 | def getData(requestsConnection,url,retry,page,region,structure): 105 | future=requestsConnection.get(url+str(page)) 106 | logging.info('getting {}#{}#{}#{}'.format(retry,page,region,url+str(page))) 107 | future.url=url 108 | future.fullurl=url+str(page) 109 | future.page=page 110 | future.retry=retry 111 | future.region=region 112 | future.structure=structure 113 | return future 114 | 115 | 116 | if __name__ == "__main__": 117 | import ConfigParser, os 118 | fileLocation = os.path.dirname(os.path.realpath(__file__)) 119 | inifile=fileLocation+'/esi.cfg' 120 | 121 | config = ConfigParser.ConfigParser() 122 | config.read(inifile) 123 | 124 | clientid=config.get('oauth','clientid') 125 | secret=config.get('oauth','secret') 126 | refreshtoken=config.get('oauth','refreshtoken') 127 | 128 | reqs_num_workers=config.getint('requests','max_workers') 129 | useragent=config.get('requests','useragent') 130 | 131 | connectionstring=config.get('database','connectionstring') 132 | 133 | engine = create_engine(connectionstring, echo=False) 134 | metadata = MetaData() 135 | connection = engine.connect() 136 | 137 | 138 | session = FuturesSession(max_workers=reqs_num_workers) 139 | session.headers.update({'UserAgent':useragent}); 140 | orderTable = Table('orders',metadata, 141 | Column('id',Integer,primary_key=True, autoincrement=True), 142 | Column('orderID',BigInteger, primary_key=False,autoincrement=False), 143 | Column('typeID',Integer), 144 | Column('issued',DateTime), 145 | Column('buy',Boolean), 146 | Column('volume',BigInteger), 147 | Column('volumeEntered',BigInteger), 148 | Column('minVolume',BigInteger), 149 | Column('price',Numeric(scale=4,precision=19)), 150 | Column('stationID',BigInteger), 151 | Column('range',String(12)), 152 | Column('duration',Integer), 153 | Column('region',Integer), 154 | Column('orderSet',BigInteger) 155 | ) 156 | 157 | Index("orders_1",orderTable.c.typeID) 158 | Index("orders_2",orderTable.c.typeID,orderTable.c.buy) 159 | Index("orders_5",orderTable.c.region,orderTable.c.typeID,orderTable.c.buy) 160 | Index("orders_6",orderTable.c.region) 161 | 162 | 163 | orderSet=Table('orderset',metadata, 164 | Column('id',BigInteger,primary_key=True, autoincrement=True), 165 | Column('downloaded',DateTime) 166 | ) 167 | 168 | 169 | 170 | #metadata.create_all(engine,checkfirst=True) 171 | 172 | urls=[] 173 | 174 | for regionid in xrange(10000001,10000070): 175 | if regionid not in (10000024,10000026): 176 | urls.append({'url':"https://esi.evetech.net/latest/markets/{}/orders/?order_type=all&datasource=tranquility&page=".format(regionid),'retry':0,'page':1,'region':regionid,'structure':0}) 177 | 178 | trans = connection.begin() 179 | 180 | connection.execute(orderSet.insert(),downloaded=datetime.datetime.now().isoformat()) 181 | 182 | result=connection.execute("select currval('orderset_id_seq')").fetchone() 183 | 184 | ordersetid=result[0] 185 | 186 | 187 | 188 | with open('/tmp/orderset-{}.csv'.format(ordersetid), 'wb') as csvfile: 189 | orderwriter = csv.writer(csvfile,quoting=csv.QUOTE_MINIMAL,delimiter="\t") 190 | # Loop through the urls in batches 191 | while len(urls)>0: 192 | futures=[] 193 | logging.warn("Loop restarting {}".format(ordersetid)); 194 | for url in urls: 195 | logging.info('URL:{} Retry:{} page:{}'.format(url['url'],url['retry'],url['page'])); 196 | futures.append(getData(session,url['url'],url['retry'],url['page'],url['region'],url['structure'])) 197 | urls=[] 198 | for result in as_completed(futures): 199 | presult=processData(result,orderwriter,ordersetid,connection,orderTable) 200 | if presult['retry']==1 or presult['retry']==2: 201 | urls.append(presult) 202 | logging.info("adding {} to retry {}".format(result.url,presult['retry'])) 203 | if presult['retry'] == 0 and presult['url'] is not None: 204 | logging.info('{} has more pages. {}'.format(result.url,presult['retry'])) 205 | urls.append(presult) 206 | 207 | 208 | # Get authorization 209 | headers = {'Authorization':'Basic '+ base64.b64encode(clientid+':'+secret),'User-Agent':useragent} 210 | query = {'grant_type':'refresh_token','refresh_token':refreshtoken} 211 | r = requests.post('https://login.eveonline.com/oauth/token',params=query,headers=headers) 212 | response = r.json() 213 | accesstoken = response['access_token'] 214 | logging.warn("Access Token {}".format(accesstoken)) 215 | 216 | 217 | 218 | 219 | session.headers.update({'Authorization':'Bearer '+accesstoken}); 220 | 221 | results=connection.execute('select "stationID",mss."regionID" from evesde."staStations" sta join evesde."mapSolarSystems" mss on mss."solarSystemID"=sta."solarSystemID" where "stationID">100000000').fetchall() 222 | for result in results: 223 | urls.append({'url':"https://esi.evetech.net/latest/markets/structures/{}/?&datasource=tranquility&page=".format(result[0]),'retry':0,'page':1,'region':result[1],'structure':1}) 224 | 225 | 226 | while len(urls)>0: 227 | futures=[] 228 | logging.warn("Loop restarting {}".format(ordersetid)); 229 | for url in urls: 230 | logging.info('URL:{} Retry:{} page:{}'.format(url['url'],url['retry'],url['page'])); 231 | futures.append(getData(session,url['url'],url['retry'],url['page'],url['region'],url['structure'])) 232 | urls=[] 233 | for result in as_completed(futures): 234 | presult=processData(result,orderwriter,ordersetid,connection,orderTable) 235 | if presult['retry']==1: 236 | urls.append(presult) 237 | logging.info("adding {} to retry {}".format(result.url,presult['retry'])) 238 | if presult['retry'] == 0 and presult['url'] is not None: 239 | logging.info('{} has more pages. {}'.format(result.url,presult['retry'])) 240 | urls.append(presult) 241 | 242 | logging.warn("Loading Data File {}".format(ordersetid)); 243 | connection.execute("""copy orders_{}("orderID","typeID",issued,buy,volume,"volumeEntered","minVolume",price,"stationID",range,duration,region,"orderSet") from '/tmp/orderset-{}.csv'""".format((int(ordersetid)/100)%10,ordersetid)) 244 | logging.warn("Complete load {}".format(ordersetid)); 245 | trans.commit() 246 | 247 | 248 | 249 | logging.warn("Pandas populating sell {}".format(ordersetid)); 250 | 251 | sell=pandas.read_sql_query("""select region||'|'||"typeID"||'|'||buy as what,price,sum(volume) volume from orders where "orderSet"={} and buy=False group by region,"typeID",buy,price order by region,"typeID",price asc""".format(ordersetid),connection); 252 | logging.warn("Pandas populating buy {}".format(ordersetid)); 253 | buy=pandas.read_sql_query("""select region||'|'||"typeID"||'|'||buy as what,price,sum(volume) volume from orders where "orderSet"={} and buy=True group by region,"typeID",buy,price order by region,"typeID",price desc""".format(ordersetid),connection); 254 | logging.warn("Pandas populated {}".format(ordersetid)); 255 | 256 | 257 | logging.warn("Sell Math running {}".format(ordersetid)); 258 | sell['min']=sell.groupby('what')['price'].transform('min') 259 | sell['volume']=sell.apply(lambda x: 0 if x['price']>x['min']*100 else x['volume'],axis=1) 260 | sell['cumsum']=sell.groupby('what')['volume'].apply(lambda x: x.cumsum()) 261 | sell['fivepercent']=sell.groupby('what')['volume'].transform('sum')/20 262 | sell['lastsum']=sell.groupby('what')['cumsum'].shift(1) 263 | sell.fillna(0,inplace=True) 264 | sell['applies']=sell.apply(lambda x: x['volume'] if x['cumsum']<=x['fivepercent'] else x['fivepercent']-x['lastsum'],axis=1) 265 | num = sell._get_numeric_data() 266 | num[num < 0] = 0 267 | logging.warn("Buy Math running {}".format(ordersetid)); 268 | buy['max']=buy.groupby('what')['price'].transform('max') 269 | buy['volume']=buy.apply(lambda x: 0 if x['price']1000: 315 | count=0 316 | pipe.execute() 317 | pipe.execute() 318 | 319 | 320 | logging.warn("Outputing to CSV {}".format(ordersetid)); 321 | agg2.to_csv(path_or_buf="/tmp/aggregatecsv.csv.gz",compression='gzip'); 322 | 323 | logging.warn("Station Aggregates {}".format(ordersetid)); 324 | 325 | logging.warn("Pandas populating sell {}".format(ordersetid)); 326 | 327 | sell=pandas.read_sql_query("""select "stationID"||'|'||"typeID"||'|'||buy as what,price,sum(volume) volume from orders where "orderSet"={} and "stationID" in (60003760,60008494,60011866,60004588,60005686) and buy=False group by "stationID","typeID",buy,price order by "stationID","typeID",price asc""".format(ordersetid),connection); 328 | logging.warn("Pandas populating buy {}".format(ordersetid)); 329 | buy=pandas.read_sql_query("""select "stationID"||'|'||"typeID"||'|'||buy as what,price,sum(volume) volume from orders where "orderSet"={} and "stationID" in (60003760,60008494,60011866,60004588,60005686) and buy=True group by "stationID","typeID",buy,price order by "stationID","typeID",price desc""".format(ordersetid),connection); 330 | logging.warn("Pandas populated {}".format(ordersetid)); 331 | 332 | 333 | logging.warn("Sell Math running {}".format(ordersetid)); 334 | sell['min']=sell.groupby('what')['price'].transform('min') 335 | sell['volume']=sell.apply(lambda x: 0 if x['price']>x['min']*100 else x['volume'],axis=1) 336 | sell['cumsum']=sell.groupby('what')['volume'].apply(lambda x: x.cumsum()) 337 | sell['fivepercent']=sell.groupby('what')['volume'].transform('sum')/20 338 | sell['lastsum']=sell.groupby('what')['cumsum'].shift(1) 339 | sell.fillna(0,inplace=True) 340 | sell['applies']=sell.apply(lambda x: x['volume'] if x['cumsum']<=x['fivepercent'] else x['fivepercent']-x['lastsum'],axis=1) 341 | num = sell._get_numeric_data() 342 | num[num < 0] = 0 343 | logging.warn("Buy Math running {}".format(ordersetid)); 344 | buy['max']=buy.groupby('what')['price'].transform('max') 345 | buy['volume']=buy.apply(lambda x: 0 if x['price']1000: 389 | count=0 390 | pipe.execute() 391 | pipe.execute() 392 | 393 | 394 | logging.warn("System Aggregates {}".format(ordersetid)); 395 | 396 | logging.warn("Pandas populating sell {}".format(ordersetid)); 397 | 398 | sell=pandas.read_sql_query("""select "solarSystemID"||'|'||"typeID"||'|'||buy as what,price,sum(volume) volume from orders join evesde."staStations" on orders."stationID"="staStations"."stationID" where "orderSet"={} and "solarSystemID" in (30000142,30000144) and buy=False group by "solarSystemID","typeID",buy,price order by "solarSystemID","typeID",price asc""".format(ordersetid),connection); 399 | logging.warn("Pandas populating buy {}".format(ordersetid)); 400 | buy=pandas.read_sql_query("""select "solarSystemID"||'|'||"typeID"||'|'||buy as what,price,sum(volume) volume from orders join evesde."staStations" on orders."stationID"="staStations"."stationID" where "orderSet"={} and "solarSystemID" in (30000142,30000144) and buy=True group by "solarSystemID","typeID",buy,price order by "solarSystemID","typeID",price asc""".format(ordersetid),connection); 401 | logging.warn("Pandas populated {}".format(ordersetid)); 402 | 403 | 404 | logging.warn("Sell Math running {}".format(ordersetid)); 405 | sell['min']=sell.groupby('what')['price'].transform('min') 406 | sell['volume']=sell.apply(lambda x: 0 if x['price']>x['min']*100 else x['volume'],axis=1) 407 | sell['cumsum']=sell.groupby('what')['volume'].apply(lambda x: x.cumsum()) 408 | sell['fivepercent']=sell.groupby('what')['volume'].transform('sum')/20 409 | sell['lastsum']=sell.groupby('what')['cumsum'].shift(1) 410 | sell.fillna(0,inplace=True) 411 | sell['applies']=sell.apply(lambda x: x['volume'] if x['cumsum']<=x['fivepercent'] else x['fivepercent']-x['lastsum'],axis=1) 412 | num = sell._get_numeric_data() 413 | num[num < 0] = 0 414 | logging.warn("Buy Math running {}".format(ordersetid)); 415 | buy['max']=buy.groupby('what')['price'].transform('max') 416 | buy['volume']=buy.apply(lambda x: 0 if x['price']1000: 460 | count=0 461 | pipe.execute() 462 | pipe.execute() 463 | 464 | 465 | 466 | 467 | logging.warn("Storing some stats for the front page {}".format(ordersetid)); 468 | result=connection.execute("""select array_to_json(array_agg(t)) from (select coun,"stationName",orders."stationID",vol from (select "stationID",count(*) coun,sum(volume) vol from orders where "orderSet"={} and buy=false group by "stationID" order by count(*)) orders join evesde."staStations" on orders."stationID"="staStations"."stationID" order by coun desc limit 10) t""".format(ordersetid)).fetchone() 469 | redisdb.set("fp-sell",json.dumps(result[0])); 470 | result=connection.execute("""select array_to_json(array_agg(t)) from (select coun,"stationName",orders."stationID",vol from (select "stationID",count(*) coun,sum(volume) vol from orders where "orderSet"={} and buy=true group by "stationID" order by count(*)) orders join evesde."staStations" on orders."stationID"="staStations"."stationID" order by coun desc limit 10) t""".format(ordersetid)).fetchone() 471 | redisdb.set("fp-buy",json.dumps(result[0])); 472 | redisdb.set("fp-lastupdate",datetime.datetime.utcnow().isoformat()) 473 | logging.warn("Complete {}".format(ordersetid)) 474 | 475 | shutil.move("""/tmp/orderset-{}.csv""".format(ordersetid),"""/opt/orderbooks/orderset-{}.csv""".format(ordersetid)) 476 | -------------------------------------------------------------------------------- /scripts/aggloader.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine, Column, MetaData, Table, Index 2 | from sqlalchemy import Integer, String, Text, Float, Boolean, BigInteger, Numeric, SmallInteger, DateTime 3 | import time 4 | import requests 5 | from requests_futures.sessions import FuturesSession 6 | import requests_futures 7 | from concurrent.futures import as_completed 8 | import datetime 9 | import csv 10 | import time 11 | import sys 12 | import re 13 | import pandas 14 | import numpy 15 | import redis 16 | import json 17 | import os 18 | 19 | 20 | import logging 21 | logging.basicConfig(filename='logs/aggloader.log',level=logging.WARN,format='%(asctime)s %(levelname)s %(message)s') 22 | 23 | 24 | 25 | def RateLimited(maxPerSecond): 26 | minInterval = 1.0 / float(maxPerSecond) 27 | def decorate(func): 28 | lastTimeCalled = [0.0] 29 | def rateLimitedFunction(*args,**kargs): 30 | elapsed = time.clock() - lastTimeCalled[0] 31 | leftToWait = minInterval - elapsed 32 | if leftToWait>0: 33 | time.sleep(leftToWait) 34 | ret = func(*args,**kargs) 35 | lastTimeCalled[0] = time.clock() 36 | return ret 37 | return rateLimitedFunction 38 | return decorate 39 | 40 | 41 | 42 | 43 | def processData(result,orderwriter,ordersetid,connection,orderTable): 44 | 45 | try: 46 | m=re.search('/(\d+)/',result.url) 47 | regionid=m.group(1) 48 | resp=result.result() 49 | logging.info('Process {} {} {}'.format(resp.status_code,result.url,result.retry)) 50 | if resp.status_code==200: 51 | orders=resp.json() 52 | logging.info('{} orders on page {}'.format(len(orders['items']),result.url)) 53 | for order in orders['items']: 54 | orderwriter.writerow([order['id'], 55 | order['type'], 56 | order['issued'], 57 | order['buy'], 58 | order['volume'], 59 | order['volumeEntered'], 60 | order['minVolume'], 61 | order['price'], 62 | order['stationID'], 63 | order['range'], 64 | order['duration'], 65 | regionid, 66 | ordersetid] 67 | ) 68 | 69 | logging.info('{}: next page {}'.format(result.url,orders.get('next',{}).get('href',None))) 70 | return {'retry':0,'url':orders.get('next',{}).get('href',None)} 71 | else: 72 | logging.error("Non 200 status. {} returned {}".format(resp.url,resp.status_code)) 73 | return {'retry':result.retry+1,'url':result.url} 74 | except requests.exceptions.ConnectionError as e: 75 | logging.error(e) 76 | return {'retry':result.retry+1,'url':result.url} 77 | 78 | 79 | 80 | 81 | 82 | 83 | @RateLimited(150) 84 | def getData(requestsConnection,url,retry): 85 | future=requestsConnection.get(url) 86 | future.url=url 87 | future.retry=retry 88 | return future 89 | 90 | 91 | if __name__ == "__main__": 92 | engine = create_engine('postgresql+psycopg2://marketdata:marketdatapass@localhost/marketdata?application_name=aggloader', echo=False) 93 | metadata = MetaData() 94 | connection = engine.connect() 95 | 96 | 97 | reqs_num_workers = 20 98 | session = FuturesSession(max_workers=reqs_num_workers) 99 | session.headers.update({'UserAgent':'Fuzzwork All Region Download'}); 100 | orderTable = Table('orders',metadata, 101 | Column('id',Integer,primary_key=True, autoincrement=True), 102 | Column('orderID',BigInteger, primary_key=False,autoincrement=False), 103 | Column('typeID',Integer), 104 | Column('issued',DateTime), 105 | Column('buy',Boolean), 106 | Column('volume',BigInteger), 107 | Column('volumeEntered',BigInteger), 108 | Column('minVolume',BigInteger), 109 | Column('price',Numeric(scale=4,precision=19)), 110 | Column('stationID',Integer), 111 | Column('range',String(12)), 112 | Column('duration',Integer), 113 | Column('region',Integer), 114 | Column('orderSet',BigInteger) 115 | ) 116 | 117 | Index("orders_1",orderTable.c.typeID) 118 | Index("orders_2",orderTable.c.typeID,orderTable.c.buy) 119 | Index("orders_5",orderTable.c.region,orderTable.c.typeID,orderTable.c.buy) 120 | Index("orders_6",orderTable.c.region) 121 | 122 | 123 | orderSet=Table('orderset',metadata, 124 | Column('id',BigInteger,primary_key=True, autoincrement=True), 125 | Column('downloaded',DateTime) 126 | ) 127 | 128 | aggregates=Table('aggregateOrders',metadata, 129 | Column('typeID',Integer), 130 | Column('region',Integer), 131 | Column('orderSet',BigInteger), 132 | Column('wavgsell',Numeric(scale=2,precision=19)), 133 | Column('wavgbuy',Numeric(scale=2,precision=19)), 134 | Column('maxsell',Numeric(scale=2,precision=19)), 135 | Column('maxbuy',Numeric(scale=2,precision=19)), 136 | Column('minsell',Numeric(scale=2,precision=19)), 137 | Column('minbuy',Numeric(scale=2,precision=19)), 138 | Column('stddevsell',Numeric(scale=2,precision=19)), 139 | Column('stddevbuy',Numeric(scale=2,precision=19)), 140 | Column('mediansell',Numeric(scale=2,precision=19)), 141 | Column('medianbuy',Numeric(scale=2,precision=19)), 142 | Column('volumesell',BigInteger), 143 | Column('volumebuy',BigInteger), 144 | Column('countsell',BigInteger), 145 | Column('countbuy',BigInteger), 146 | Column('fivepsell',Numeric(scale=2,precision=19)), 147 | Column('fivepbuy',Numeric(scale=2,precision=19)), 148 | ) 149 | Index("aggregates1",aggregates.c.region,aggregates.c.typeID,aggregates.c.orderSet) 150 | 151 | 152 | #metadata.create_all(engine,checkfirst=True) 153 | 154 | urls=[] 155 | 156 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000001/orders/all/",'retry':0}) 157 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000002/orders/all/",'retry':0}) 158 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000003/orders/all/",'retry':0}) 159 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000004/orders/all/",'retry':0}) 160 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000005/orders/all/",'retry':0}) 161 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000006/orders/all/",'retry':0}) 162 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000007/orders/all/",'retry':0}) 163 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000008/orders/all/",'retry':0}) 164 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000009/orders/all/",'retry':0}) 165 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000010/orders/all/",'retry':0}) 166 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000011/orders/all/",'retry':0}) 167 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000012/orders/all/",'retry':0}) 168 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000013/orders/all/",'retry':0}) 169 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000014/orders/all/",'retry':0}) 170 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000015/orders/all/",'retry':0}) 171 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000016/orders/all/",'retry':0}) 172 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000017/orders/all/",'retry':0}) 173 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000018/orders/all/",'retry':0}) 174 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000019/orders/all/",'retry':0}) 175 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000020/orders/all/",'retry':0}) 176 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000021/orders/all/",'retry':0}) 177 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000022/orders/all/",'retry':0}) 178 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000023/orders/all/",'retry':0}) 179 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000025/orders/all/",'retry':0}) 180 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000027/orders/all/",'retry':0}) 181 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000028/orders/all/",'retry':0}) 182 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000029/orders/all/",'retry':0}) 183 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000030/orders/all/",'retry':0}) 184 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000031/orders/all/",'retry':0}) 185 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000032/orders/all/",'retry':0}) 186 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000033/orders/all/",'retry':0}) 187 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000034/orders/all/",'retry':0}) 188 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000035/orders/all/",'retry':0}) 189 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000036/orders/all/",'retry':0}) 190 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000037/orders/all/",'retry':0}) 191 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000038/orders/all/",'retry':0}) 192 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000039/orders/all/",'retry':0}) 193 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000040/orders/all/",'retry':0}) 194 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000041/orders/all/",'retry':0}) 195 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000042/orders/all/",'retry':0}) 196 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000043/orders/all/",'retry':0}) 197 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000044/orders/all/",'retry':0}) 198 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000045/orders/all/",'retry':0}) 199 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000046/orders/all/",'retry':0}) 200 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000047/orders/all/",'retry':0}) 201 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000048/orders/all/",'retry':0}) 202 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000049/orders/all/",'retry':0}) 203 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000050/orders/all/",'retry':0}) 204 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000051/orders/all/",'retry':0}) 205 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000052/orders/all/",'retry':0}) 206 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000053/orders/all/",'retry':0}) 207 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000054/orders/all/",'retry':0}) 208 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000055/orders/all/",'retry':0}) 209 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000056/orders/all/",'retry':0}) 210 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000057/orders/all/",'retry':0}) 211 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000058/orders/all/",'retry':0}) 212 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000059/orders/all/",'retry':0}) 213 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000060/orders/all/",'retry':0}) 214 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000061/orders/all/",'retry':0}) 215 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000062/orders/all/",'retry':0}) 216 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000063/orders/all/",'retry':0}) 217 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000064/orders/all/",'retry':0}) 218 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000065/orders/all/",'retry':0}) 219 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000066/orders/all/",'retry':0}) 220 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000067/orders/all/",'retry':0}) 221 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000068/orders/all/",'retry':0}) 222 | urls.append({'url':"https://crest-tq.eveonline.com/market/10000069/orders/all/",'retry':0}) 223 | 224 | trans = connection.begin() 225 | 226 | connection.execute(orderSet.insert(),downloaded=datetime.datetime.now().isoformat()) 227 | 228 | result=connection.execute("select currval('orderset_id_seq')").fetchone() 229 | 230 | ordersetid=result[0] 231 | 232 | with open('/tmp/orderset-{}.csv'.format(ordersetid), 'wb') as csvfile: 233 | orderwriter = csv.writer(csvfile,quoting=csv.QUOTE_MINIMAL,delimiter="\t") 234 | # Loop through the urls in batches 235 | while len(urls)>0: 236 | futures=[] 237 | logging.warn("Loop restarting {}".format(ordersetid)); 238 | for url in urls: 239 | logging.info('URL:{} Retry:{}'.format(url['url'],url['retry'])); 240 | futures.append(getData(session,url['url'],url['retry'])) 241 | urls=[] 242 | for result in as_completed(futures): 243 | presult=processData(result,orderwriter,ordersetid,connection,orderTable) 244 | if presult['retry']==1: 245 | urls.append(presult) 246 | logging.info("adding {} to retry {}".format(presult.url,presult.retry)) 247 | if presult['retry'] == 0 and presult['url'] is not None: 248 | logging.info('{} has more pages. {}'.format(result.url,presult['retry'])) 249 | urls.append(presult) 250 | 251 | 252 | logging.warn("Loading Data File {}".format(ordersetid)); 253 | connection.execute("""copy orders_{}("orderID","typeID",issued,buy,volume,"volumeEntered","minVolume",price,"stationID",range,duration,region,"orderSet") from '/tmp/orderset-{}.csv'""".format((int(ordersetid)/100)%10,ordersetid)) 254 | logging.warn("Complete load {}".format(ordersetid)); 255 | trans.commit() 256 | 257 | 258 | 259 | logging.warn("Pandas populating sell {}".format(ordersetid)); 260 | 261 | sell=pandas.read_sql_query("""select region||'|'||"typeID"||'|'||buy as what,price,sum(volume) volume from orders where "orderSet"={} and buy=False group by region,"typeID",buy,price order by region,"typeID",price asc""".format(ordersetid),connection); 262 | logging.warn("Pandas populating buy {}".format(ordersetid)); 263 | buy=pandas.read_sql_query("""select region||'|'||"typeID"||'|'||buy as what,price,sum(volume) volume from orders where "orderSet"={} and buy=True group by region,"typeID",buy,price order by region,"typeID",price desc""".format(ordersetid),connection); 264 | logging.warn("Pandas populated {}".format(ordersetid)); 265 | 266 | 267 | logging.warn("Sell Math running {}".format(ordersetid)); 268 | sell['min']=sell.groupby('what')['price'].transform('min') 269 | sell['volume']=sell.apply(lambda x: 0 if x['price']>x['min']*100 else x['volume'],axis=1) 270 | sell['cumsum']=sell.groupby('what')['volume'].apply(lambda x: x.cumsum()) 271 | sell['fivepercent']=sell.groupby('what')['volume'].transform('sum')/20 272 | sell['lastsum']=sell.groupby('what')['cumsum'].shift(1) 273 | sell.fillna(0,inplace=True) 274 | sell['applies']=sell.apply(lambda x: x['volume'] if x['cumsum']<=x['fivepercent'] else x['fivepercent']-x['lastsum'],axis=1) 275 | num = sell._get_numeric_data() 276 | num[num < 0] = 0 277 | logging.warn("Buy Math running {}".format(ordersetid)); 278 | buy['max']=buy.groupby('what')['price'].transform('max') 279 | buy['volume']=buy.apply(lambda x: 0 if x['price']1000: 323 | count=0 324 | pipe.execute() 325 | pipe.execute() 326 | 327 | 328 | logging.warn("Storing some stats for the front page {}".format(ordersetid)); 329 | result=connection.execute("""select array_to_json(array_agg(t)) from (select coun,"stationName",orders."stationID",vol from (select "stationID",count(*) coun,sum(volume) vol from orders where "orderSet"={} and buy=false group by "stationID" order by count(*)) orders join evesde."staStations" on orders."stationID"="staStations"."stationID" order by coun desc limit 10) t""".format(ordersetid)).fetchone() 330 | redisdb.set("fp-sell",json.dumps(result[0])); 331 | result=connection.execute("""select array_to_json(array_agg(t)) from (select coun,"stationName",orders."stationID",vol from (select "stationID",count(*) coun,sum(volume) vol from orders where "orderSet"={} and buy=true group by "stationID" order by count(*)) orders join evesde."staStations" on orders."stationID"="staStations"."stationID" order by coun desc limit 10) t""".format(ordersetid)).fetchone() 332 | redisdb.set("fp-buy",json.dumps(result[0])); 333 | logging.warn("Complete {}".format(ordersetid)) 334 | 335 | os.rename("""/tmp/orderset-{}.csv""".format(ordersetid),"""/opt/orderbooks/orderset-{}.csv""".format(ordersetid)) 336 | -------------------------------------------------------------------------------- /scripts/citadelgetter.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from sqlalchemy import create_engine,MetaData,Table,Column,INTEGER,FLOAT,VARCHAR,BigInteger 3 | 4 | 5 | import logging 6 | logging.basicConfig(filename='logs/citadelfullloader.log',level=logging.WARN,format='%(asctime)s %(levelname)s %(message)s') 7 | 8 | 9 | engine = create_engine('postgresql+psycopg2://marketdata:marketdatapass@localhost/marketdata', echo=False) 10 | metadata = MetaData() 11 | connection = engine.connect() 12 | 13 | staStations = Table('staStations2', metadata, 14 | Column('stationID', BigInteger, primary_key=True, autoincrement=False, nullable=False), 15 | Column('security', INTEGER()), 16 | Column('dockingCostPerVolume', FLOAT(precision=53)), 17 | Column('maxShipVolumeDockable', FLOAT(precision=53)), 18 | Column('officeRentalCost', INTEGER()), 19 | Column('operationID', INTEGER(),index=True), 20 | Column('stationTypeID', INTEGER(),index=True), 21 | Column('corporationID', INTEGER(),index=True), 22 | Column('solarSystemID', INTEGER(),index=True), 23 | Column('constellationID', INTEGER(),index=True), 24 | Column('regionID', INTEGER(),index=True), 25 | Column('stationName', VARCHAR(length=100)), 26 | Column('x', FLOAT(precision=53)), 27 | Column('y', FLOAT(precision=53)), 28 | Column('z', FLOAT(precision=53)), 29 | Column('reprocessingEfficiency', FLOAT(precision=53)), 30 | Column('reprocessingStationsTake', FLOAT(precision=53)), 31 | Column('reprocessingHangarFlag', INTEGER()), 32 | schema='evesde' 33 | ) 34 | 35 | 36 | headers={'User-Agent':'Fuzzwork Market Citadel Name Getter'}; 37 | 38 | from requests_oauthlib import OAuth2Session 39 | token = {'access_token':'','refresh_token':'Refresh token goes here!','token_type': 'Bearer','expires_in': '-30'} 40 | client_id = r'Client ID goes here!' 41 | refresh_url = 'https://login.eveonline.com/oauth/token' 42 | extra = { 'client_id': client_id,'client_secret': r'Client secret goes here!'} 43 | 44 | def token_saver(token): 45 | pass 46 | 47 | 48 | client = OAuth2Session(client_id, token=token, auto_refresh_url=refresh_url,auto_refresh_kwargs=extra, token_updater=token_saver) 49 | 50 | 51 | citadellistreq=client.get('https://esi.tech.ccp.is/latest/universe/structures/?datasource=tranquility') 52 | citadellist=citadellistreq.json() 53 | 54 | 55 | 56 | 57 | for citadel in citadellist: 58 | citadeldetails=client.get("https://esi.tech.ccp.is/latest/universe/structures/{}/?datasource=tranquility".format(citadel),headers=headers) 59 | if citadeldetails.status_code==200: 60 | cjson=citadeldetails.json() 61 | if cjson['type_id'] in [35826,35827,35833,35834,40340]: 62 | citadelmarket=client.get("https://esi.tech.ccp.is/latest/markets/structures/{}/?page=1&datasource=tranquility".format(citadel)) 63 | if citadelmarket.status_code==200: 64 | connection.execute(staStations.insert(),stationID=citadel,stationName=cjson['name'],stationTypeID=cjson['type_id'],solarSystemID=cjson['solar_system_id'],x=cjson['position']['x'],y=cjson['position']['y'],z=cjson['position']['z']) 65 | logging.warn('Citadel id {} inserted'.format(citadel)) 66 | else: 67 | logging.warn('Citadel id {} not inserted, no market'.format(citadel)) 68 | else: 69 | logging.warn('Citadel id {} not inserted, no market possible'.format(citadel)) 70 | else: 71 | logging.warn('Citadel id {} failed with status code {}'.format(citadel,citadeldetails.status_code)) 72 | -------------------------------------------------------------------------------- /scripts/marketgroup-parallel-session.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from sqlalchemy import create_engine,MetaData,Table,Column,INTEGER,FLOAT,VARCHAR,UnicodeText,DECIMAL,Boolean,select,literal_column 3 | import requests_cache 4 | from requests_futures.sessions import FuturesSession 5 | import requests_futures 6 | from concurrent.futures import as_completed 7 | 8 | from tqdm import tqdm 9 | 10 | 11 | def getitems(typelist): 12 | typefuture=[] 13 | print "getitems" 14 | for typeid in typelist: 15 | if isinstance(typeid,basestring) and typeid.startswith("https"): 16 | typefuture.append(session.get(str(typeid))) 17 | else: 18 | typefuture.append(session.get(typelookupurl.format(typeid))) 19 | badlist=[] 20 | pbar = tqdm(total=len(typelist)) 21 | for typedata in as_completed(typefuture): 22 | if typedata.result().status_code==200: 23 | itemjson=typedata.result().json() 24 | item=itemjson.get('type_id') 25 | if int(item) in sdetypelist: 26 | try: 27 | connection.execute(invTypes.update().where(invTypes.c.typeID == literal_column(str(item))), 28 | typeID=item, 29 | typeName=itemjson['name'], 30 | groupID=itemjson.get('group_id',None), 31 | marketGroupID=itemjson.get('market_group_id',None), 32 | capacity=itemjson.get('capacity',None), 33 | published=itemjson.get('published',False), 34 | portionSize=itemjson.get('portion_size',None), 35 | volume=itemjson['volume']) 36 | except: 37 | pass 38 | else: 39 | connection.execute(invTypes.insert(), 40 | typeID=item, 41 | typeName=itemjson['name'], 42 | marketGroupID=itemjson.get('market_group_id',None), 43 | groupID=itemjson.get('group_id',None), 44 | published=itemjson.get('published',False), 45 | volume=itemjson.get('volume',None), 46 | capacity=itemjson.get('capacity',None), 47 | portionSize=itemjson.get('portion_size',None), 48 | mass=itemjson.get('mass',None) 49 | ) 50 | else: 51 | badlist.append(typedata.result().url) 52 | print typedata.result().url 53 | pbar.update(1) 54 | return badlist 55 | 56 | 57 | 58 | 59 | 60 | engine = create_engine('postgresql+psycopg2://marketdata:marketdatapass@localhost/marketdata', echo=False,connect_args={"application_name":"itemloader"}) 61 | metadata = MetaData() 62 | connection = engine.connect() 63 | trans = connection.begin() 64 | 65 | 66 | 67 | invTypes = Table('invTypes', metadata, 68 | Column('typeID', INTEGER(), primary_key=True, autoincrement=False, nullable=False), 69 | Column('groupID', INTEGER(),index=True), 70 | Column('typeName', VARCHAR(length=100)), 71 | Column('description',UnicodeText()), 72 | Column('mass', FLOAT(precision=53)), 73 | Column('volume', FLOAT(precision=53)), 74 | Column('capacity', FLOAT(precision=53)), 75 | Column('portionSize', INTEGER()), 76 | Column('raceID', INTEGER()), 77 | Column('basePrice', DECIMAL(precision=19, scale=4)), 78 | Column('published', Boolean), 79 | Column('marketGroupID', INTEGER()), 80 | Column('iconID', INTEGER()), 81 | Column('soundID', INTEGER()), 82 | Column('graphicID', INTEGER()), 83 | schema="evesde" 84 | ) 85 | 86 | 87 | maintypelist=[] 88 | 89 | groupurl="https://esi.evetech.net/latest/markets/groups/?datasource=tranquility" 90 | 91 | grouplookupurl="https://esi.evetech.net/latest/markets/groups/{}/?datasource=tranquility&language=en-us" 92 | 93 | 94 | typelookupurl='https://esi.evetech.net/latest/universe/types/{}/' 95 | 96 | errorcount=0 97 | requests_cache.install_cache("item_cache",expire_after=35000) 98 | 99 | lookup=select([invTypes]) 100 | result=connection.execute(lookup).fetchall() 101 | 102 | sdetypelist=[] 103 | 104 | for typedata in result: 105 | sdetypelist.append(typedata.typeID) 106 | 107 | reqs_num_workers=50 108 | 109 | session = FuturesSession(max_workers=reqs_num_workers) 110 | 111 | groups=requests.get(groupurl) 112 | 113 | groupjson=groups.json() 114 | 115 | 116 | groupfuture=[] 117 | 118 | 119 | for group in groupjson: 120 | groupfuture.append(session.get(grouplookupurl.format(group))) 121 | 122 | 123 | pbar = tqdm(total=len(groupjson)) 124 | for groupdata in as_completed(groupfuture): 125 | try: 126 | groupdatajson=groupdata.result().json() 127 | for grouptype in groupdatajson.get('types',[]): 128 | maintypelist.append(grouptype) 129 | pbar.update(1) 130 | except: 131 | print groupdata.result().url 132 | pbar.close() 133 | 134 | 135 | firstbadlist=getitems(maintypelist) 136 | print "Getting badlist" 137 | secondbadlist=getitems(firstbadlist) 138 | 139 | 140 | trans.commit() 141 | -------------------------------------------------------------------------------- /src/dependencies.php: -------------------------------------------------------------------------------- 1 | getContainer(); 5 | 6 | // view renderer 7 | $container['renderer'] = function ($c) { 8 | $settings = $c->get('settings')['renderer']; 9 | return new Slim\Views\Twig($settings['template_path']); 10 | }; 11 | 12 | $container['cache'] = function () { 13 | return new \Slim\HttpCache\CacheProvider(); 14 | }; 15 | 16 | 17 | // monolog 18 | $container['logger'] = function ($c) { 19 | $settings = $c->get('settings')['logger']; 20 | $logger = new Monolog\Logger($settings['name']); 21 | $logger->pushProcessor(new Monolog\Processor\UidProcessor()); 22 | $logger->pushHandler(new Monolog\Handler\StreamHandler($settings['path'], Monolog\Logger::DEBUG)); 23 | return $logger; 24 | }; 25 | -------------------------------------------------------------------------------- /src/middleware.php: -------------------------------------------------------------------------------- 1 | add(new \Slim\Csrf\Guard); 5 | // 6 | $app->add(new \Slim\HttpCache\Cache('public', 300)); 7 | -------------------------------------------------------------------------------- /src/routes.php: -------------------------------------------------------------------------------- 1 | get('/appraisal/',function ($request, $response, $args) { 5 | return $this->renderer->render($response, 'appraisal.phtml', $args); 6 | }); 7 | 8 | $app->post('/appraisal/',function ($request, $response, $args) { 9 | 10 | $region=$_POST['region']; 11 | if (!ctype_digit($region)) { 12 | exit; 13 | } 14 | 15 | $db = new PDO("pgsql:host=localhost;dbname=marketdata;user=marketdata;password=marketdatapass"); 16 | $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); 17 | $sql=<<prepare($sql); 21 | $stmt->execute(); 22 | $typeidlookup=array(); 23 | while ($row = $stmt->fetchObject()) { 24 | $typeidlookup[$row->typeName]=$row->typeID; 25 | } 26 | $inventory=array(); 27 | 28 | if (array_key_exists('pasteblock',$_POST)) { 29 | $entries=explode("\n", $_POST['pasteblock']); 30 | } else { 31 | exit; 32 | } 33 | 34 | foreach ($entries as $entry) { 35 | $entry=strtolower(preg_replace('/(\d),(\d)/','$1$2',$entry)); 36 | $entry=strtolower(preg_replace('/(\d)\.(\d)/','$1$2',$entry)); 37 | if (preg_match("/blueprint copy/", trim($entry), $matches)) { 38 | continue; 39 | } 40 | if (preg_match("/^(30 day pilot.*)\t(\d+)\t(.*)$/", trim($entry), $matches)) { 41 | if (isset($typeidlookup[$matches[1]])) { 42 | if (isset($inventory[$typeidlookup[$matches[1]]])) { 43 | $inventory[$typeidlookup[$matches[1]]]+=$matches[2]; 44 | } else { 45 | $inventory[$typeidlookup[$matches[1]]]=$matches[2]; 46 | } 47 | } 48 | } elseif (preg_match("/^(.*) \((\d+) units\)$/", trim($entry), $matches)) { 49 | if (isset($typeidlookup[$matches[1]])) { 50 | if (isset($inventory[$typeidlookup[$matches[1]]])) { 51 | $inventory[$typeidlookup[$matches[1]]]+=$matches[2]; 52 | } else { 53 | $inventory[$typeidlookup[$matches[1]]]=$matches[2]; 54 | } 55 | } 56 | } elseif (preg_match("/^(.*)\t.*\t(\d+)$/", trim($entry), $matches)) { 57 | if (isset($typeidlookup[$matches[1]])) { 58 | $quantity=$matches[2]; 59 | if (isset($inventory[$typeidlookup[$matches[1]]])) { 60 | $inventory[$typeidlookup[$matches[1]]]+=$quantity; 61 | } else { 62 | $inventory[$typeidlookup[$matches[1]]]=$quantity; 63 | } 64 | } 65 | } elseif (preg_match("/^(\d+) (.*)$/", trim($entry), $matches)) { 66 | if (isset($typeidlookup[$matches[2]])) { 67 | if (isset($inventory[$typeidlookup[$matches[2]]])) { 68 | $inventory[$typeidlookup[$matches[2]]]+=$matches[1]; 69 | } else { 70 | $inventory[$typeidlookup[$matches[2]]]=$matches[1]; 71 | } 72 | } 73 | } elseif (preg_match("/^(.*)\t([\d\.,]+)\t/", trim($entry), $matches)) { 74 | if (isset($typeidlookup[$matches[1]])) { 75 | $quantity=str_replace(',', '', str_replace(',', '', $matches[2])); 76 | $quantity=str_replace('\.', '', str_replace('\.', '', $matches[2])); 77 | if (isset($inventory[$typeidlookup[$matches[1]]])) { 78 | $inventory[$typeidlookup[$matches[1]]]+=$quantity; 79 | } else { 80 | $inventory[$typeidlookup[$matches[1]]]=$quantity; 81 | } 82 | } 83 | } elseif (preg_match("/^(.*):\s+([\d.,]+)/", trim($entry), $matches)) { 84 | if (isset($typeidlookup[$matches[1]])) { 85 | $quantity=str_replace(',', '', str_replace(',', '', $matches[2])); 86 | if (isset($inventory[$typeidlookup[$matches[1]]])) { 87 | $inventory[$typeidlookup[$matches[1]]]+=$quantity; 88 | } else { 89 | $inventory[$typeidlookup[$matches[1]]]=$quantity; 90 | } 91 | } 92 | } elseif (preg_match("/^(.*)\t([\d.,]+)/", trim($entry), $matches)) { 93 | if (isset($typeidlookup[$matches[1]])) { 94 | $quantity=str_replace(',', '', str_replace(',', '', $matches[2])); 95 | if (isset($inventory[$typeidlookup[$matches[1]]])) { 96 | $inventory[$typeidlookup[$matches[1]]]+=$quantity; 97 | } else { 98 | $inventory[$typeidlookup[$matches[1]]]=$quantity; 99 | } 100 | } 101 | } elseif (preg_match("/^\[(.*),.*]/", trim($entry), $matches)) { 102 | if (isset($typeidlookup[$matches[1]])) { 103 | $quantity=1; 104 | if (isset($inventory[$typeidlookup[$matches[1]]])) { 105 | $inventory[$typeidlookup[$matches[1]]]+=$quantity; 106 | } else { 107 | $inventory[$typeidlookup[$matches[1]]]=$quantity; 108 | } 109 | } 110 | } elseif (preg_match("/^(.*), Qty: (\d+)/", trim($entry), $matches)) { 111 | if (isset($typeidlookup[$matches[1]])) { 112 | $quantity=$matches[2]; 113 | if (isset($inventory[$typeidlookup[$matches[1]]])) { 114 | $inventory[$typeidlookup[$matches[1]]]+=$quantity; 115 | } else { 116 | $inventory[$typeidlookup[$matches[1]]]=$quantity; 117 | } 118 | } 119 | } elseif (preg_match("/^.*\t(.*)\t.*/", trim($entry), $matches)) { 120 | if (isset($typeidlookup[$matches[1]])) { 121 | $quantity=1; 122 | if (isset($inventory[$typeidlookup[$matches[1]]])) { 123 | $inventory[$typeidlookup[$matches[1]]]+=$quantity; 124 | } else { 125 | $inventory[$typeidlookup[$matches[1]]]=$quantity; 126 | } 127 | } 128 | } elseif (preg_match("/^(.*)/", trim($entry), $matches)) { 129 | if (isset($typeidlookup[$matches[1]])) { 130 | $quantity=1; 131 | if (isset($inventory[$typeidlookup[$matches[1]]])) { 132 | $inventory[$typeidlookup[$matches[1]]]+=$quantity; 133 | } else { 134 | $inventory[$typeidlookup[$matches[1]]]=$quantity; 135 | } 136 | } 137 | } 138 | } 139 | 140 | if (count($inventory)) { 141 | $sql="insert into appraisal (identifier,list) values (:id,:list)"; 142 | $stmt = $db->prepare($sql); 143 | $identifier=uniqid(); 144 | $stmt->execute(array(":id"=>$identifier,":list"=>json_encode($inventory))); 145 | 146 | return $response->withRedirect('/appraisal/'.$region.'/'.$identifier); 147 | } 148 | return $this->renderer->render($response, 'appraisal.phtml', $args); 149 | }); 150 | 151 | 152 | $app->get('/appraisal/{region:[0-9]+}/{identifier}[/{json}]',function ($request, $response, $args) { 153 | 154 | $db = new PDO("pgsql:host=localhost;dbname=marketdata;user=marketdata;password=marketdatapass"); 155 | $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); 156 | $sql=<<prepare($sql); 160 | $stmt->execute(); 161 | $typenamelookup=array(); 162 | while ($row = $stmt->fetchObject()) { 163 | $typenamelookup[$row->typeID]=array("typeName"=>$row->typeName,"volume"=>$row->volume); 164 | } 165 | $sql='select "regionName","regionID" from evesde."mapRegions" where "regionID"=:region'; 166 | $stmt = $db->prepare($sql); 167 | $stmt->execute(array(":region"=>$args['region'])); 168 | $row = $stmt->fetchObject(); 169 | $regionName=$row->regionName; 170 | $regionid=$row->regionID; 171 | 172 | $args['region']=$regionName; 173 | $args['regionid']=$regionid; 174 | 175 | $sql='select list from appraisal where identifier=:id'; 176 | $stmt = $db->prepare($sql); 177 | $stmt->execute(array(":id"=>$args['identifier'])); 178 | $row = $stmt->fetchObject(); 179 | $list=$row->list; 180 | 181 | $items=json_decode($list); 182 | 183 | $redis = new Predis\Client(); 184 | 185 | $appraisal=array(); 186 | $total=array(); 187 | $total['pbuy']=0; 188 | $total['mbuy']=0; 189 | $total['psell']=0; 190 | $total['msell']=0; 191 | $total['volume']=0; 192 | foreach ($items as $item=>$quantity) { 193 | $buydetails=explode("|",$redis->get($regionid.'|'.$item."|true")); 194 | $selldetails=explode("|",$redis->get($regionid.'|'.$item."|false")); 195 | $line=array(); 196 | $line['typeid']=$item; 197 | $line['typename']=$typenamelookup[$item]['typeName']; 198 | $line['quantity']=$quantity; 199 | $line['pbuy']=$quantity*$buydetails[7]; 200 | $line['psell']=$quantity*$selldetails[7]; 201 | $line['mbuy']=$quantity*$buydetails[1]; 202 | $line['msell']=$quantity*$selldetails[2]; 203 | $line['volume']=$quantity*$typenamelookup[$item]['volume']; 204 | $total['pbuy']+=$line['pbuy']; 205 | $total['mbuy']+=$line['mbuy']; 206 | $total['psell']+=$line['psell']; 207 | $total['msell']+=$line['msell']; 208 | $total['volume']+=$line['volume']; 209 | $appraisal[]=$line; 210 | } 211 | 212 | $args['appraisal']=$appraisal; 213 | $args['total']=$total; 214 | if ($args['json']) { 215 | $resWithExpires = $this->cache->withExpires($response->withJson($args), time() + 300); 216 | return $resWithExpires; 217 | } else { 218 | return $this->renderer->render($response, 'displayappraisal.phtml', $args); 219 | } 220 | 221 | }); 222 | 223 | 224 | $app->map(['GET', 'POST'],'/aggregates/',function ($request, $response, $args) { 225 | $redis = new Predis\Client(); 226 | $aggregate=array(); 227 | $allGetVars = $request->getQueryParams(); 228 | $region=0; 229 | if (isset($allGetVars['region'])){ 230 | $region=$allGetVars['region']; 231 | } elseif (isset($allGetVars['station'])){ 232 | $region=$allGetVars['station']; 233 | } elseif (isset($allGetVars['system'])){ 234 | $region=$allGetVars['system']; 235 | } 236 | $types=$allGetVars['types']; 237 | $allPostPutVars = $request->getParsedBody(); 238 | if (isset($allPostPutVars['types'])){ 239 | $types=$allPostPutVars['types']; 240 | if (isset($allPostPutVars['region'])){ 241 | $region=$allPostPutVars['region']; 242 | } elseif (isset($allPostPutVars['station'])){ 243 | $region=$allPostPutVars['station']; 244 | } elseif (isset($allPostPutVars['system'])){ 245 | $region=$allPostPutVars['system']; 246 | } 247 | } 248 | $ordertype=array("true"=>"buy","false"=>"sell"); 249 | foreach (explode(",",$types) as $type) { 250 | if ($type == '') { 251 | continue; 252 | } 253 | foreach (array("true","false") as $buy) { 254 | $values=$redis->get($region.'|'.$type."|".$buy); 255 | if ($values==''){ 256 | $values='|||||||'; 257 | } 258 | $details=explode("|",$values); 259 | #$details=explode("|",$redis->get($region.'|'.$type."|".$buy)); 260 | if ($details[0]==""){ 261 | $details[0]=null; 262 | } 263 | 264 | $aggregate[$type][$ordertype[$buy]]=array( 265 | "weightedAverage"=>$details[0]?$details[0]:0, 266 | "max"=>$details[1]?$details[1]:0, 267 | "min"=>$details[2]?$details[2]:0, 268 | "stddev"=>$details[3]?$details[3]:0, 269 | "median"=>$details[4]?$details[4]:0, 270 | "volume"=>$details[5]?$details[5]:0, 271 | "orderCount"=>$details[6]?$details[6]:0, 272 | "percentile"=>$details[7]?$details[7]:0 273 | ); 274 | } 275 | } 276 | $response=$response->withHeader('Access-Control-Allow-Origin', '*'); 277 | $resWithExpires = $this->cache->withExpires($response->withJson($aggregate), time() + 300); 278 | return $resWithExpires; 279 | }); 280 | 281 | 282 | 283 | $app->get('/', function ($request, $response, $args) { 284 | $redis = new Predis\Client(); 285 | $aggregate=array(); 286 | foreach (array(34,35,36,37,38,39,40,11399,44992,40520) as $type) { 287 | $aggregate[$type]=array(); 288 | foreach (array("true","false") as $buy) { 289 | $aggregate[$type][$buy]=explode("|",$redis->get('60003760|'.$type."|".$buy)); 290 | } 291 | } 292 | $args['fpbuy']=json_decode($redis->get('fp-buy')); 293 | $args['fpsell']=json_decode($redis->get('fp-sell')); 294 | $args['fplastupdate']=$redis->get('fp-lastupdate'); 295 | $args['types']=array("Tritanium","Pyrite","Mexallon","Isogen","Nocxium","Zydrine","Megacyte","Morphite","PLEX","Skill Injector"); 296 | $args['maggs']=$aggregate; 297 | return $this->renderer->render($response, 'index.phtml', $args); 298 | }); 299 | 300 | $app->get("/api/orderset", function ($request, $response) use ($app) { 301 | $db = new PDO("pgsql:host=localhost;dbname=marketdata;user=marketdata;password=marketdatapass"); 302 | $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); 303 | $ordersetsql="select max(id) id from orderset"; 304 | $stmt = $db->prepare($ordersetsql); 305 | $stmt->execute(); 306 | $result= $stmt->fetchAll(PDO::FETCH_ASSOC); 307 | $result=array("orderset"=>$result[0]['id']); 308 | $resWithExpires = $this->cache->withExpires($response->withJson($result), time() + 300); 309 | return $resWithExpires; 310 | }); 311 | 312 | $app->get("/api/typeids", function ($request, $response) use ($app) { 313 | $db = new PDO("pgsql:host=localhost;dbname=marketdata;user=marketdata;password=marketdatapass"); 314 | $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); 315 | $sql='select "typeID" as value,"typeName" as label from evesde."invTypes" where "marketGroupID" is not null order by "typeName"'; 316 | $stmt = $db->prepare($sql); 317 | $stmt->execute(); 318 | $result= $stmt->fetchAll(PDO::FETCH_ASSOC); 319 | $resWithExpires = $this->cache->withExpires($response->withJson($result), time() + 3600); 320 | return $resWithExpires; 321 | }); 322 | 323 | $app->get("/api/regionids", function ($request, $response) use ($app) { 324 | $db = new PDO("pgsql:host=localhost;dbname=marketdata;user=marketdata;password=marketdatapass"); 325 | $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); 326 | $sql='select "regionID" as value,"regionName" as label from evesde."mapRegions" where "regionID"<11000000 and "regionID" not in (10000004,10000017) order by "regionName"'; 327 | $stmt = $db->prepare($sql); 328 | $stmt->execute(); 329 | $result= $stmt->fetchAll(PDO::FETCH_ASSOC); 330 | $resWithExpires = $this->cache->withExpires($response->withJson($result), time() + 3600); 331 | return $resWithExpires; 332 | }); 333 | 334 | $app->get("/api/stationids", function ($request, $response) use ($app) { 335 | $db = new PDO("pgsql:host=localhost;dbname=marketdata;user=marketdata;password=marketdatapass"); 336 | $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); 337 | $sql='select "stationID" as value,"stationName" as label from evesde."staStations" order by "stationName"'; 338 | $stmt = $db->prepare($sql); 339 | $stmt->execute(); 340 | $result= $stmt->fetchAll(PDO::FETCH_ASSOC); 341 | $resWithExpires = $this->cache->withExpires($response->withJson($result), time() + 3600); 342 | return $resWithExpires; 343 | }); 344 | 345 | $app->get('/type/', function ($request, $response, $args) { 346 | return $this->renderer->render($response, 'types.phtml', $args); 347 | }); 348 | $app->get('/hub/', function ($request, $response, $args) { 349 | return $this->renderer->render($response, 'hub.phtml', $args); 350 | }); 351 | $app->get('/region/', function ($request, $response, $args) { 352 | return $this->renderer->render($response, 'region.phtml', $args); 353 | }); 354 | $app->get('/station/', function ($request, $response, $args) { 355 | return $this->renderer->render($response, 'station.phtml', $args); 356 | }); 357 | $app->get('/aggregate/', function ($request, $response, $args) { 358 | return $this->renderer->render($response, 'aggregate.phtml', $args); 359 | }); 360 | 361 | $app->get('/browser/', function ($request, $response, $args) { 362 | $db = new PDO("pgsql:host=localhost;dbname=marketdata;user=marketdata;password=marketdatapass"); 363 | $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); 364 | $sql='select "regionID" as value,"regionName" as label from evesde."mapRegions" where "regionID"<11000000 and "regionID" not in (10000004,10000017) order by "regionName"'; 365 | $stmt = $db->prepare($sql); 366 | $stmt->execute(); 367 | $result= $stmt->fetchAll(PDO::FETCH_ASSOC); 368 | $args['regions']=$result; 369 | return $this->renderer->render($response, 'browser.phtml', $args); 370 | }); 371 | 372 | 373 | $app->get("/api/marketgroup/{parent:[0-9]+}/", function ($request, $response, $args) use ($app) { 374 | $db = new PDO("pgsql:host=localhost;dbname=marketdata;user=marketdata;password=marketdatapass"); 375 | $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); 376 | $marketgroupsql='select "parentGroupID","marketGroupID","marketGroupName","iconID","hasTypes" from evesde."invMarketGroups" where "parentGroupID"=:parent or (:parent=0 and "parentGroupID" is null) order by "marketGroupName"'; 377 | $stmt = $db->prepare($marketgroupsql); 378 | $stmt->execute(array(":parent"=>$args['parent'])); 379 | $marketgroups= $stmt->fetchAll(PDO::FETCH_ASSOC); 380 | $markettypesql='select "typeID","typeName","iconID" from evesde."invTypes" where "marketGroupID"=:parent order by "typeName"'; 381 | $stmt = $db->prepare($markettypesql); 382 | $stmt->execute(array(":parent"=>$args['parent'])); 383 | $types= $stmt->fetchAll(PDO::FETCH_ASSOC); 384 | 385 | $result=array("marketgroups"=>$marketgroups,"types"=>$types,"groupid"=>$args['parent']); 386 | $resWithExpires = $this->cache->withExpires($response->withJson($result), time() + 300); 387 | return $resWithExpires; 388 | }); 389 | 390 | $app->get('/api/region/{region:[0-9]+}/type/{type:[0-9]+}/', function ($request, $response, $args) { 391 | $db = new PDO("pgsql:host=localhost;dbname=marketdata;user=marketdata;password=marketdatapass"); 392 | $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); 393 | $ordersetsql="select max(id) from orderset"; 394 | $stmt = $db->prepare($ordersetsql); 395 | $stmt->execute(); 396 | $result= $stmt->fetchAll(PDO::FETCH_ASSOC); 397 | $orderset=$result['0']['max']; 398 | 399 | $sellordersql=<<prepare($sellordersql); 426 | $stmt->execute(array(":typeid"=>$args['type'],":orderset"=>$orderset,":region"=>$args['region'])); 427 | $sellorders=$stmt->fetchAll(PDO::FETCH_ASSOC); 428 | $stmt = $db->prepare($buyordersql); 429 | $stmt->execute(array(":typeid"=>$args['type'],":orderset"=>$orderset,":region"=>$args['region'])); 430 | $buyorders=$stmt->fetchAll(PDO::FETCH_ASSOC); 431 | $stmt =$db->prepare('select "typeName" from evesde."invTypes" where "typeID"=:typeid'); 432 | $stmt->execute(array(":typeid"=>$args['type'])); 433 | $nameres=$stmt->fetchAll(PDO::FETCH_ASSOC); 434 | 435 | $result=array(); 436 | $db=null; 437 | $result['typename']=$nameres[0]['typeName']; 438 | $result['orderset']=$orderset; 439 | $result['buyorders']=$buyorders; 440 | $result['sellorders']=$sellorders; 441 | $resWithExpires = $this->cache->withExpires($response->withJson($result), time() + 300); 442 | return $resWithExpires; 443 | 444 | }); 445 | 446 | 447 | $app->get('/api/', function ($request, $response, $args) { 448 | 449 | $files=glob('/opt/orderbooks/*.csv.gz'); 450 | rsort($files, SORT_NATURAL ); 451 | $args['files']=$files; 452 | 453 | return $this->renderer->render($response, 'api.phtml', $args); 454 | }); 455 | $app->get('/about/', function ($request, $response, $args) { 456 | return $this->renderer->render($response, 'about.phtml', $args); 457 | }); 458 | 459 | $app->get('/type/{type:[0-9]+}/', function ($request, $response, $args) { 460 | $db = new PDO("pgsql:host=localhost;dbname=marketdata;user=marketdata;password=marketdatapass"); 461 | $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); 462 | $ordersetsql="select max(id) from orderset"; 463 | $stmt = $db->prepare($ordersetsql); 464 | $stmt->execute(); 465 | $result= $stmt->fetchAll(PDO::FETCH_ASSOC); 466 | $orderset=$result['0']['max']; 467 | 468 | $sellordersql=<<prepare($sellordersql); 491 | $stmt->execute(array(":typeid"=>$args['type'],":orderset"=>$orderset)); 492 | $sellorders=$stmt->fetchAll(PDO::FETCH_ASSOC); 493 | $stmt = $db->prepare($buyordersql); 494 | $stmt->execute(array(":typeid"=>$args['type'],":orderset"=>$orderset)); 495 | $buyorders=$stmt->fetchAll(PDO::FETCH_ASSOC); 496 | $stmt =$db->prepare('select "typeName" from evesde."invTypes" where "typeID"=:typeid'); 497 | $stmt->execute(array(":typeid"=>$args['type'])); 498 | $nameres=$stmt->fetchAll(PDO::FETCH_ASSOC); 499 | $db=null; 500 | $args['typename']=$nameres[0]['typeName']; 501 | $args['orderset']=$orderset; 502 | $args['buyorders']=$buyorders; 503 | $args['sellorders']=$sellorders; 504 | return $this->renderer->render($response, 'type.phtml', $args); 505 | 506 | 507 | }); 508 | 509 | 510 | $app->get('/region/{region:[0-9]+}/type/{type:[0-9]+}/', function ($request, $response, $args) { 511 | $db = new PDO("pgsql:host=localhost;dbname=marketdata;user=marketdata;password=marketdatapass"); 512 | $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); 513 | $ordersetsql="select max(id) from orderset"; 514 | $stmt = $db->prepare($ordersetsql); 515 | $stmt->execute(); 516 | $result= $stmt->fetchAll(PDO::FETCH_ASSOC); 517 | $orderset=$result['0']['max']; 518 | 519 | $sellordersql=<<prepare($sellordersql); 544 | $stmt->execute(array(":typeid"=>$args['type'],":orderset"=>$orderset,":region"=>$args['region'])); 545 | $sellorders=$stmt->fetchAll(PDO::FETCH_ASSOC); 546 | $stmt = $db->prepare($buyordersql); 547 | $stmt->execute(array(":typeid"=>$args['type'],":orderset"=>$orderset,":region"=>$args['region'])); 548 | $buyorders=$stmt->fetchAll(PDO::FETCH_ASSOC); 549 | $stmt =$db->prepare('select "typeName" from evesde."invTypes" where "typeID"=:typeid'); 550 | $stmt->execute(array(":typeid"=>$args['type'])); 551 | $nameres=$stmt->fetchAll(PDO::FETCH_ASSOC); 552 | 553 | 554 | $db=null; 555 | $args['typename']=$nameres[0]['typeName']; 556 | $args['orderset']=$orderset; 557 | $args['buyorders']=$buyorders; 558 | $args['sellorders']=$sellorders; 559 | return $this->renderer->render($response, 'type.phtml', $args); 560 | }); 561 | 562 | $app->get('/empire/type/{type:[0-9]+}/', function ($request, $response, $args) { 563 | $db = new PDO("pgsql:host=localhost;dbname=marketdata;user=marketdata;password=marketdatapass"); 564 | $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); 565 | $ordersetsql="select max(id) from orderset"; 566 | $stmt = $db->prepare($ordersetsql); 567 | $stmt->execute(); 568 | $result= $stmt->fetchAll(PDO::FETCH_ASSOC); 569 | $orderset=$result['0']['max']; 570 | 571 | $sellordersql=<<prepare($sellordersql); 598 | $stmt->execute(array(":typeid"=>$args['type'],":orderset"=>$orderset)); 599 | $sellorders=$stmt->fetchAll(PDO::FETCH_ASSOC); 600 | $stmt = $db->prepare($buyordersql); 601 | $stmt->execute(array(":typeid"=>$args['type'],":orderset"=>$orderset)); 602 | $buyorders=$stmt->fetchAll(PDO::FETCH_ASSOC); 603 | $stmt =$db->prepare('select "typeName" from evesde."invTypes" where "typeID"=:typeid'); 604 | $stmt->execute(array(":typeid"=>$args['type'])); 605 | $nameres=$stmt->fetchAll(PDO::FETCH_ASSOC); 606 | 607 | 608 | $db=null; 609 | $args['typename']=$nameres[0]['typeName']; 610 | $args['orderset']=$orderset; 611 | $args['buyorders']=$buyorders; 612 | $args['sellorders']=$sellorders; 613 | return $this->renderer->render($response, 'type.phtml', $args); 614 | }); 615 | 616 | $app->get('/hub/type/{type:[0-9]+}/', function ($request, $response, $args) { 617 | $db = new PDO("pgsql:host=localhost;dbname=marketdata;user=marketdata;password=marketdatapass"); 618 | $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); 619 | $ordersetsql="select max(id) from orderset"; 620 | $stmt = $db->prepare($ordersetsql); 621 | $stmt->execute(); 622 | $result= $stmt->fetchAll(PDO::FETCH_ASSOC); 623 | $orderset=$result['0']['max']; 624 | 625 | $sellordersql=<<prepare($sellordersql); 650 | $stmt->execute(array(":typeid"=>$args['type'],":orderset"=>$orderset)); 651 | $sellorders=$stmt->fetchAll(PDO::FETCH_ASSOC); 652 | $stmt = $db->prepare($buyordersql); 653 | $stmt->execute(array(":typeid"=>$args['type'],":orderset"=>$orderset)); 654 | $buyorders=$stmt->fetchAll(PDO::FETCH_ASSOC); 655 | $stmt =$db->prepare('select "typeName" from evesde."invTypes" where "typeID"=:typeid'); 656 | $stmt->execute(array(":typeid"=>$args['type'])); 657 | $nameres=$stmt->fetchAll(PDO::FETCH_ASSOC); 658 | 659 | 660 | $db=null; 661 | $args['typename']=$nameres[0]['typeName']; 662 | $args['orderset']=$orderset; 663 | $args['buyorders']=$buyorders; 664 | $args['sellorders']=$sellorders; 665 | return $this->renderer->render($response, 'type.phtml', $args); 666 | }); 667 | 668 | 669 | $app->get('/history/{orderid:[0-9]+}', function ($request, $response, $args) { 670 | $db = new PDO("pgsql:host=localhost;dbname=marketdata;user=marketdata;password=marketdatapass"); 671 | $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); 672 | 673 | $sellordersql=<<prepare($sellordersql); 694 | $stmt->execute(array(":orderid"=>$args['orderid'])); 695 | $sellorders=$stmt->fetchAll(PDO::FETCH_ASSOC); 696 | $stmt = $db->prepare($buyordersql); 697 | $stmt->execute(array(":orderid"=>$args['orderid'])); 698 | $buyorders=$stmt->fetchAll(PDO::FETCH_ASSOC); 699 | $args['orderset']=$orderset; 700 | $args['buyorders']=$buyorders; 701 | $args['sellorders']=$sellorders; 702 | return $this->renderer->render($response, 'history.phtml', $args); 703 | 704 | 705 | }); 706 | 707 | $app->get('/station/{station:[0-9]+}/type/{type:[0-9]+}/', function ($request, $response, $args) { 708 | $db = new PDO("pgsql:host=localhost;dbname=marketdata;user=marketdata;password=marketdatapass"); 709 | $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); 710 | $ordersetsql="select max(id) from orderset"; 711 | $stmt = $db->prepare($ordersetsql); 712 | $stmt->execute(); 713 | $result= $stmt->fetchAll(PDO::FETCH_ASSOC); 714 | $orderset=$result['0']['max']; 715 | 716 | $sellordersql=<<prepare($sellordersql); 741 | $stmt->execute(array(":typeid"=>$args['type'],":orderset"=>$orderset,":station"=>$args['station'])); 742 | $sellorders=$stmt->fetchAll(PDO::FETCH_ASSOC); 743 | $stmt = $db->prepare($buyordersql); 744 | $stmt->execute(array(":typeid"=>$args['type'],":orderset"=>$orderset,":station"=>$args['station'])); 745 | $buyorders=$stmt->fetchAll(PDO::FETCH_ASSOC); 746 | $stmt =$db->prepare('select "typeName" from evesde."invTypes" where "typeID"=:typeid'); 747 | $stmt->execute(array(":typeid"=>$args['type'])); 748 | $nameres=$stmt->fetchAll(PDO::FETCH_ASSOC); 749 | 750 | 751 | $db=null; 752 | $args['typename']=$nameres[0]['typeName']; 753 | $args['orderset']=$orderset; 754 | $args['buyorders']=$buyorders; 755 | $args['sellorders']=$sellorders; 756 | return $this->renderer->render($response, 'type.phtml', $args); 757 | }); 758 | 759 | 760 | $app->get('/authlogin', function ($request, $response, $args) { 761 | include('/opt/web/market/src/secretreal.php'); 762 | return $response->withStatus(302)->withHeader('Location', 'https://login.eveonline.com/oauth/authorize?response_type=code&redirect_uri=https%3A%2F%2Fmarket.fuzzwork.co.uk%2Fauth%2Fupdater&client_id='+$clientid+'&scope=esi-universe.read_structures.v1%20esi-markets.structure_markets.v1&state=authmebitch'); 763 | }); 764 | 765 | $app->get('/auth/updater',function ($request, $response, $args) { 766 | include('/opt/web/market/src/secretreal.php'); 767 | $code=$_GET['code']; 768 | $state=$_GET['state']; 769 | $url='https://login.eveonline.com/oauth/token'; 770 | $header='Authorization: Basic '.base64_encode($clientid.':'.$secret); 771 | $fields=array( 772 | 'grant_type' => 'authorization_code', 773 | 'code' => $code 774 | ); 775 | $fields_string=''; 776 | foreach ($fields as $key => $value) { 777 | $fields_string .= $key.'='.$value.'&'; 778 | } 779 | rtrim($fields_string, '&'); 780 | $ch = curl_init(); 781 | curl_setopt($ch, CURLOPT_URL, $url); 782 | curl_setopt($ch, CURLOPT_USERAGENT, $useragent); 783 | curl_setopt($ch, CURLOPT_HTTPHEADER, array($header)); 784 | curl_setopt($ch, CURLOPT_POST, count($fields)); 785 | curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); 786 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 787 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); 788 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); 789 | $result = curl_exec($ch); 790 | if ($result===false) { 791 | $response->getBody()->write(curl_error($ch)); 792 | } 793 | curl_close($ch); 794 | $resp=json_decode($result); 795 | $response->getBody()->write(print_r($resp,true)); 796 | return $response; 797 | }); 798 | -------------------------------------------------------------------------------- /src/settings.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'displayErrorDetails' => true, // set to false in production 5 | 6 | // Renderer settings 7 | 'renderer' => [ 8 | 'template_path' => __DIR__ . '/../templates/', 9 | ], 10 | 11 | // Monolog settings 12 | 'logger' => [ 13 | 'name' => 'slim-app', 14 | 'path' => __DIR__ . '/../logs/app.log', 15 | ], 16 | ], 17 | ]; 18 | -------------------------------------------------------------------------------- /templates/about.phtml: -------------------------------------------------------------------------------- 1 | {% extends "base.phtml" %} 2 | 3 | 4 | {% block title %}Fuzzwork Market Data - About{% endblock %} 5 | 6 | {% block content %} 7 |
8 |
9 |
10 |

Fuzzwork Market Data

11 |
12 |
13 |
14 |
15 |

This site is a market data site for EVE Online

16 |

It works by getting the full order book for New Eden, once every 30 minutes, then processing it down to produce aggregate data. The code can be found at https://github.com/fuzzysteve/FuzzMarket

17 |

Order data is retained for 2 days, so you can investigate the history of an order, to see how it changes between the 30 minute snapshots.

18 |

An api will be provided to access the aggregates at will.

19 |

The entire orderbook, for a yet to be determined time, will be available to download.

20 |

As I'm thinking of moving over to an SSD based server for this, to cut down on the slowdown when the market orders are imported, I've put up a Patreon page. You won't get anything more than a fuzzy feeling, and my thanks, but maybe that's enough?

21 |
22 |
23 |
24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /templates/aggregate.phtml: -------------------------------------------------------------------------------- 1 | {% extends "base.phtml" %} 2 | 3 | 4 | {% block title %}Fuzzwork Market Data{% endblock %} 5 | 6 | {% block content %} 7 |
8 |
9 |
10 |

Fuzzwork Market Data

11 |
12 |
13 |
14 |
15 |

Still need to write this page, and the other stuff to link off it. Probably give you historical data on the aggregates, with pretty graphs

16 |
17 |
18 |
19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /templates/api.phtml: -------------------------------------------------------------------------------- 1 | {% extends "base.phtml" %} 2 | 3 | 4 | {% block title %}Fuzzwork Market Data{% endblock %} 5 | 6 | {% block content %} 7 |
8 |
9 |
10 |

Fuzzwork Market Data

11 |
12 |
13 |
14 |
15 |

Wheee! API.

16 |

Just a simple one at the moment. Global, Regions, Systems and all station (Including player owned structures. Assuming I have data for them. I don't for private.).

17 |

Yes, all systems now

18 |
    19 |
  • Global - 0 20 |
  • Jita - 30000142 21 |
  • Perimeter - 30000144 22 |
  • Jita 4-4 CNAP - 60003760 23 |
  • Amarr VIII - 60008494 24 |
  • Dodixie - 60011866 25 |
  • Rens - 60004588 26 |
  • Hek - 60005686 27 |
28 |

Formatted one of these ways

29 |
https://market.fuzzwork.co.uk/aggregates/?region=10000002&types=34,35,36,37,38,39,40
30 |
https://market.fuzzwork.co.uk/aggregates/?station=60003760&types=34,35,36,37,38,39,40
31 |

(Due to how it works, you can give a station or a system as a region, and it will return correctly. These are all precalculated aggregates)

32 |

Returning a json dataset looking like (this is truncated) the following

33 |
 34 | {
 35 |   "34": {
 36 |     "buy": {
 37 |       "weightedAverage": "4.02878502065",
 38 |       "max": "5.95",
 39 |       "min": "0.01",
 40 |       "stddev": "1.62036217159",
 41 |       "median": "5.0",
 42 |       "volume": "10024734026.0",
 43 |       "orderCount": "52",
 44 |       "percentile": "5.50168617928"
 45 |     },
 46 |     "sell": {
 47 |       "weightedAverage": "6.60015441538",
 48 |       "max": "2201571.0",
 49 |       "min": "5.01",
 50 |       "stddev": "177420.733866",
 51 |       "median": "6.38",
 52 |       "volume": "25573930856.0",
 53 |       "orderCount": "179",
 54 |       "percentile": "5.92257900667"
 55 |     }
 56 |   },
 57 |   "35": {
 58 |     "buy": {
 59 |       "weightedAverage": "2.95108749592",
 60 |       "max": "9.32",
 61 |       "min": "0.01",
 62 |       "stddev": "2.33386568045",
 63 |       "median": "8.08",
 64 |       "volume": "3567567586.0",
 65 |       "orderCount": "43",
 66 |       "percentile": "8.93197172057"
 67 |     },
 68 |     "sell": {
 69 |       "weightedAverage": "11.8397717552",
 70 |       "max": "88.97",
 71 |       "min": "8.9",
 72 |       "stddev": "6.28077891535",
 73 |       "median": "10.49",
 74 |       "volume": "13983717157.0",
 75 |       "orderCount": "170",
 76 |       "percentile": "9.30539352676"
 77 |     }
 78 |   }
 79 | }
 80 | 
81 |

I highly recommend pulling all the aggregates into one sheet, and then using vlookup to retrieve them. It's more efficient for both you and me

82 |

An example function for use with Google Docs can be found on my github page. An example of it in use is shared from my google drive

83 |

No real example with Excel I'm afraid. I'd suggest using XLWings to pull out the bits you want. Alternatively use powerquery.

84 |

Power Query

85 |

If you have a recent version of Excel, you have powerquery available.

86 |
    87 |
  1. Open excel and go to a new worksheet 88 |
  2. hit the 'New Query' button on the Data ribbon. 89 |
  3. Go to 'from other sources', and pick from Web 90 |
  4. Give it the url to get the aggregates you want. You'll need to do more research here to make it dynamic 91 |
  5. Hit ok. 92 |
  6. You'll get a list of the type ids, followed by record. 93 |
  7. on the convert ribbon, hit 'into table' 94 |
  8. Next to value, in the resultant table, you'll see two arrows pointing apart from each other. hit it, and leave the buy and sell ticked. untick 'use original column names as prefix' 95 |
  9. You'll now have two more columns, called buy and sell. split those with that same icon. I'd suggest leaving the original name as a prefix this time. 96 |
  10. Close and load. you're now done. 97 |
  11. Just hit refresh all, when you want to update them. You can, through the connections button on the data ribbon, set the query up to refresh on open, or on a timer. just select it and go to the properties. 98 |
99 |

There's also the api https://market.fuzzwork.co.uk/api/orderset which returns the most recent orderset id. This may be of use if you're downloading the order book. Please don't do this every 30 minutes. Get the data youself, direct from CCP. It'll be fresher and more reliable.

100 |
101 |

If you're wanting all the aggregated market date, then you'll probably want to get https://market.fuzzwork.co.uk/aggregatecsv.csv.gz, which containst a full dump of the data. Though if an item isn't on the market, you'll get _nothing_ back for it. and it'll require a little manipulation to be easily usable. Unfortunately it's too big for google sheets. excel handles it.

102 | 103 |
104 |

Files

105 |
    106 |
  • latest.csv.gz - linked to the latest file
  • 107 | {% for file in files%} 108 | {% if file != "." and file != ".." %} 109 |
  • {{file|split('/')[3]}}
  • 110 | {% endif %} 111 | {% endfor %} 112 |
113 |
114 |
115 |
116 | {% endblock %} 117 | -------------------------------------------------------------------------------- /templates/appraisal.phtml: -------------------------------------------------------------------------------- 1 | {% extends "base.phtml" %} 2 | 3 | 4 | {% block title %}Fuzzwork Market Data - Appraisal{% endblock %} 5 | 6 | {% block content %} 7 |
8 |
9 |
10 |

Fuzzwork Market Data appraisal

11 |
12 |
13 |
14 |
15 |
16 |
17 | 19 |
20 | 27 | 28 |
29 |
30 |
31 |
32 | {% endblock %} 33 | -------------------------------------------------------------------------------- /templates/base.phtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% block title %}{% endblock %} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {% block extrahead %}{% endblock %} 18 | 19 | 20 | 33 | {% block content %}{% endblock %} 34 |
35 | EVE Online and the EVE logo are the registered trademarks of CCP hf. All rights are reserved worldwide. All other trademarks are the property of their respective owners. EVE Online, the EVE logo, EVE and all associated logos and designs are the intellectual property of CCP hf. All artwork, screenshots, characters, vehicles, storylines, world facts or other recognizable features of the intellectual property relating to these trademarks are likewise the intellectual property of CCP hf. CCP hf. has granted permission to market.fuzzwork.co.uk to use EVE Online and all associated logos and designs for promotional and information purposes on its website but does not endorse, and is not in any way affiliated with, market.fuzzwork.co.uk. CCP is in no way responsible for the content on or functioning of this website, nor can it be liable for any damage arising from the use of this website. 36 |
37 | {% block footercontent %}{% endblock %} 38 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /templates/browser.phtml: -------------------------------------------------------------------------------- 1 | {% extends "base.phtml" %} 2 | {% block extrahead %} 3 | 4 | 5 | 6 | 7 | 8 | {% endblock %} 9 | 10 | {% block title %}Fuzzwork Market Data Browser{% endblock %} 11 | 12 | {% block content %} 13 |
14 |
15 | 38 |
39 |
40 |
41 |
42 |
43 |
44 | Sell DataBuy Data 45 |
46 |
47 | 48 | 49 | 50 | 51 | 52 |
IssuedVolume LeftVolume EnteredMinimum VolumePriceDurationStation NameRegion Name
53 | 54 | 55 | 56 | 57 |
IssuedVolume LeftVolume EnteredMinimum VolumePriceRangeDurationStation NameRegion Name
58 |
59 |
60 |
61 | 62 |
63 | {% endblock %} 64 | -------------------------------------------------------------------------------- /templates/displayappraisal.phtml: -------------------------------------------------------------------------------- 1 | {% extends "base.phtml" %} 2 | {% block title %}Appraisal{% endblock %} 3 | {% block content %} 4 |
5 |
6 |
7 | 8 |

Appraisal - Region {{region}}

9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {% for item in appraisal%} 17 | 18 | 19 | 20 | 21 | {% else %} 22 | 23 | {% endfor %} 24 | 25 | 26 | 27 | 28 | 29 |
NameQuantityVolumePercentile BuyMax BuyPercentile SellMin Sell
Totals{{total.volume|number_format(2, '.', ',')}}{{total.pbuy|number_format(2, '.', ',')}}{{total.mbuy|number_format(2, '.', ',')}}{{total.psell|number_format(2, '.', ',')}}{{total.msell|number_format(2, '.', ',')}}
{{item.typename}}{{item.quantity|number_format(0, '.', ',')}}{{item.volume|number_format(2, '.', ',')}}{{item.pbuy|number_format(2, '.', ',')}}{{item.mbuy|number_format(2, '.', ',')}}{{item.psell|number_format(2, '.', ',')}}{{item.msell|number_format(2, '.', ',')}}
No Items
Totals{{total.volume|number_format(2, '.', ',')}}{{total.pbuy|number_format(2, '.', ',')}}{{total.mbuy|number_format(2, '.', ',')}}{{total.psell|number_format(2, '.', ',')}}{{total.msell|number_format(2, '.', ',')}}
30 |
31 |
32 |
33 | {% endblock %} 34 | {% block footercontent %} 35 | 36 | 49 | 50 | 51 | {% endblock %} 52 | -------------------------------------------------------------------------------- /templates/history.phtml: -------------------------------------------------------------------------------- 1 | {% extends "base.phtml" %} 2 | {% block title %}{{type}}{% endblock %} 3 | {% block content %} 4 |
5 |

Order History

6 | {% if sellorders|length %} 7 |
8 |
9 | 10 | 11 | 12 | 13 | 14 | {% for order in sellorders%} 15 | 16 | 17 | {% else %} 18 | 19 | {% endfor %} 20 | 21 |
OrderIDOrderSetIssuedVolume Left/EnteredMinimum VolumePriceDurationStation NameRegion Name
{{order.orderID}}{{order.orderSet}}{{order.issued}}{{order.volume|number_format(0, '.', ',')}}/{{order.volumeEntered|number_format(0, '.', ',')}}{{order.minVolume|number_format(0, '.', ',')}}{{order.price|number_format(2, '.', ',')}}{{order.duration}}{{order.stationName|escape}}{{order.regionName}}
No Orders Anywhere
22 |
23 |
24 | {% endif %} 25 | {% if buyorders|length %} 26 |
27 |
28 | 29 | 30 | 31 | 32 | 33 | {% for order in buyorders%} 34 | 35 | 36 | {% else %} 37 | 38 | {% endfor %} 39 | 40 |
OrderIDOrderSetIssuedVolume Left/EnteredMinimum VolumePriceRangeDurationStation NameRegion Name
{{order.orderID}}{{order.orderSet}}{{order.issued}}{{order.volume|number_format(0, '.', ',')}}/{{order.volumeEntered|number_format(0, '.', ',')}}{{order.minVolume|number_format(0, '.', ',')}}{{order.price|number_format(2, '.', ',')}}{{order.range}}{{order.duration}}{{order.stationName|escape}}{{order.regionName}}
No Orders Anywhere
41 |
42 |
43 | {% endif %} 44 |
45 | {% endblock %} 46 | -------------------------------------------------------------------------------- /templates/hub.phtml: -------------------------------------------------------------------------------- 1 | {% extends "base.phtml" %} 2 | 3 | 4 | {% block title %}Fuzzwork Market Data{% endblock %} 5 | 6 | {% block content %} 7 |
8 |
9 |
10 |

Fuzzwork Market Data

11 |
12 |
13 |
14 |
15 |

Please select a type to see the Orders for it in the following stations

16 |
    17 |
  • Jita IV - Moon 4 - Caldari Navy Assembly Plant
  • 18 |
  • Amarr VIII (Oris) - Emperor Family Academy
  • 19 |
  • Dodixie IX - Moon 20 - Federation Navy Assembly Plant
  • 20 |
  • Rens VI - Moon 8 - Brutor Tribe Treasure
  • 21 |
  • Hek VIII - Moon 12 - Boundless Creation Factory
  • 22 |
23 | 24 |
25 |
26 | 36 |
37 |
38 |
39 | {% endblock %} 40 | {%block footercontent %} 41 | 56 | 57 | {% endblock %} 58 | -------------------------------------------------------------------------------- /templates/index.phtml: -------------------------------------------------------------------------------- 1 | {% extends "base.phtml" %} 2 | 3 | 4 | {% block title %}Fuzzwork Market Data{% endblock %} 5 | 6 | {% block content %} 7 |
8 |
9 |
10 |

Fuzzwork Market Data

11 |

Last Update (EVE): {{ fplastupdate|date("Y-m-d H:i") }}

12 |
13 |
14 |
15 |
16 |

Forge Aggregates

17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | {% for aggregate in maggs %} 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | {% endfor %} 46 | 47 |
BuySell
TypeWeighted AverageMax PriceMedianTotal VolumeNumber of Orders5% Buy AverageWeighted AverageMin PriceMedianTotal VolumeNumber of Orders5% Sell Average
{{types[loop.index0]}}{{aggregate['true'][0]|number_format(2, '.', ',')}}{{aggregate['true'][1]|number_format(2, '.', ',')}}{{aggregate['true'][4]|number_format(2, '.', ',')}}{{aggregate['true'][5]|number_format(0, '.', ',')}}{{aggregate['true'][6]|number_format(0, '.', ',')}}{{aggregate['true'][7]|number_format(2, '.', ',')}}{{aggregate['false'][0]|number_format(2, '.', ',')}}{{aggregate['false'][2]|number_format(2, '.', ',')}}{{aggregate['false'][4]|number_format(2, '.', ',')}}{{aggregate['false'][5]|number_format(0, '.', ',')}}{{aggregate['false'][6]|number_format(0, '.', ',')}}{{aggregate['false'][7]|number_format(2, '.', ',')}}
48 |
49 |
50 |
51 |
52 |

Top 10 Stations for Sell orders

53 | 54 | 55 | {% for station in fpsell %} 56 | 57 | {% endfor %} 58 |
StationNumber of OrdersVolume
{{station.stationName|escape}}{{station.coun}}{{station.vol}}
59 |
60 |
61 |

Top 10 Stations for Buy orders

62 | 63 | 64 | {% for station in fpbuy %} 65 | 66 | {% endfor %} 67 |
StationNumber of OrdersVolume
{{station.stationName|escape}}{{station.coun}}{{station.vol}}
68 |
69 |
70 |
71 | {% endblock %} 72 | -------------------------------------------------------------------------------- /templates/json.phtml: -------------------------------------------------------------------------------- 1 | {{ data|json_encode() }} 2 | -------------------------------------------------------------------------------- /templates/region.phtml: -------------------------------------------------------------------------------- 1 | {% extends "base.phtml" %} 2 | 3 | 4 | {% block title %}Fuzzwork Market Data{% endblock %} 5 | 6 | {% block content %} 7 |
8 |
9 |
10 |

Fuzzwork Market Data

11 |
12 |
13 |
14 |
15 |

Please select a Region and a Type to see the Orders for it

16 | 17 | 18 | 19 |
20 |
21 |
22 | {% endblock %} 23 | {%block footercontent %} 24 | 58 | 59 | {% endblock %} 60 | -------------------------------------------------------------------------------- /templates/station.phtml: -------------------------------------------------------------------------------- 1 | {% extends "base.phtml" %} 2 | 3 | 4 | {% block title %}Fuzzwork Market Data{% endblock %} 5 | 6 | {% block content %} 7 |
8 |
9 |
10 |

Fuzzwork Market Data

11 |
12 |
13 |
14 |
15 |

Please select a Station and a Type to see the Orders for it

16 | 17 | 18 | 19 |
20 |
21 |
22 | {% endblock %} 23 | {%block footercontent %} 24 | 58 | 59 | {% endblock %} 60 | -------------------------------------------------------------------------------- /templates/type.phtml: -------------------------------------------------------------------------------- 1 | {% extends "base.phtml" %} 2 | {% block title %}{{typename}}{% endblock %} 3 | {% block content %} 4 |
5 |

{{typename}}

6 |
7 |
8 | 9 |

Sell Orders

10 | 11 | 12 | 13 | 14 | 15 | {% for order in sellorders%} 16 | 17 | 18 | {% else %} 19 | 20 | {% endfor %} 21 | 22 |
OrderIDIssuedVolume Left/EnteredMinimum VolumePriceDurationStation NameRegion Name
{{order.orderID}}{{order.issued}}{{order.volume|number_format(0, '.', ',')}}/{{order.volumeEntered|number_format(0, '.', ',')}}{{order.minVolume|number_format(0, '.', ',')}}{{order.price|number_format(2, '.', ',')}}{{order.duration}}{{order.stationName|escape}}{{order.regionName}}
No Orders
23 |
24 |
25 |
26 |
27 | 28 |

Buy Orders

29 | 30 | 31 | 32 | 33 | 34 | {% for order in buyorders%} 35 | 36 | 37 | {% else %} 38 | 39 | {% endfor %} 40 | 41 |
OrderIDIssuedVolume Left/EnteredMinimum VolumePriceRangeDurationStation NameRegion Name
{{order.orderID}}{{order.issued}}{{order.volume|number_format(0, '.', ',')}}/{{order.volumeEntered|number_format(0, '.', ',')}}{{order.minVolume|number_format(0, '.', ',')}}{{order.price|number_format(2, '.', ',')}}{{order.range}}{{order.duration}}{{order.stationName|escape}}{{order.regionName}}
No Orders
42 |
43 |
44 |
45 |
46 | Sell|Buy 47 |
48 | {% endblock %} 49 | -------------------------------------------------------------------------------- /templates/types.phtml: -------------------------------------------------------------------------------- 1 | {% extends "base.phtml" %} 2 | 3 | 4 | {% block title %}Fuzzwork Market Data{% endblock %} 5 | 6 | {% block content %} 7 |
8 |
9 |
10 |

Fuzzwork Market Data

11 |
12 |
13 |
14 |
15 |

Please select a type to see all the orders for it across New Eden.

16 | 17 |
18 |
19 |
20 | {% endblock %} 21 | {%block footercontent %} 22 | 37 | 38 | {% endblock %} 39 | --------------------------------------------------------------------------------