├── .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(""+group.marketGroupName+" ");
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(""+item.marketGroupName+" ");
43 | });
44 | $.map(data.types,function(item){
45 | node.append(""+item.typeName+" ");
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 | Open excel and go to a new worksheet
88 | hit the 'New Query' button on the Data ribbon.
89 | Go to 'from other sources', and pick from Web
90 | Give it the url to get the aggregates you want. You'll need to do more research here to make it dynamic
91 | Hit ok.
92 | You'll get a list of the type ids, followed by record.
93 | on the convert ribbon, hit 'into table'
94 | 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 | 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 | Close and load. you're now done.
97 | 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 |
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 |
43 |
44 | Sell Data Buy Data
45 |
46 |
47 |
48 |
49 | Issued Volume Left Volume Entered Minimum Volume Price Duration Station Name Region Name
50 |
51 |
52 |
53 |
54 |
55 | Issued Volume Left Volume Entered Minimum Volume Price Range Duration Station Name Region Name
56 |
57 |
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 | Name Quantity Volume Percentile Buy Max Buy Percentile Sell Min Sell
12 |
13 | Totals {{total.volume|number_format(2, '.', ',')}} {{total.pbuy|number_format(2, '.', ',')}} {{total.mbuy|number_format(2, '.', ',')}}
14 | {{total.psell|number_format(2, '.', ',')}} {{total.msell|number_format(2, '.', ',')}}
15 |
16 | {% for item in appraisal%}
17 | {{item.typename}}
18 | {{item.quantity|number_format(0, '.', ',')}} {{item.volume|number_format(2, '.', ',')}}
19 | {{item.pbuy|number_format(2, '.', ',')}} {{item.mbuy|number_format(2, '.', ',')}}
20 | {{item.psell|number_format(2, '.', ',')}} {{item.msell|number_format(2, '.', ',')}}
21 | {% else %}
22 | No Items
23 | {% endfor %}
24 |
25 |
26 | Totals {{total.volume|number_format(2, '.', ',')}} {{total.pbuy|number_format(2, '.', ',')}} {{total.mbuy|number_format(2, '.', ',')}}
27 | {{total.psell|number_format(2, '.', ',')}} {{total.msell|number_format(2, '.', ',')}}
28 |
29 |
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 | OrderID OrderSet Issued Volume Left/Entered Minimum Volume Price Duration Station Name Region Name
12 |
13 |
14 | {% for order in sellorders%}
15 | {{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, '.', ',')}}
16 | {{order.duration}} {{order.stationName|escape}} {{order.regionName}}
17 | {% else %}
18 | No Orders Anywhere
19 | {% endfor %}
20 |
21 |
22 |
23 |
24 | {% endif %}
25 | {% if buyorders|length %}
26 |
27 |
28 |
29 |
30 | OrderID OrderSet Issued Volume Left/Entered Minimum Volume Price Range Duration Station Name Region Name
31 |
32 |
33 | {% for order in buyorders%}
34 | {{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}}
35 | {{order.duration}} {{order.stationName|escape}} {{order.regionName}}
36 | {% else %}
37 | No Orders Anywhere
38 | {% endfor %}
39 |
40 |
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 |
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 | Buy Sell
25 | Type
26 | Weighted Average Max Price Median Total Volume Number of Orders 5% Buy Average
27 | Weighted Average Min Price Median Total Volume Number of Orders 5% Sell Average
28 |
29 |
30 |
31 | {% for aggregate in maggs %}
32 | {{types[loop.index0]}}
33 | {{aggregate['true'][0]|number_format(2, '.', ',')}}
34 | {{aggregate['true'][1]|number_format(2, '.', ',')}}
35 | {{aggregate['true'][4]|number_format(2, '.', ',')}}
36 | {{aggregate['true'][5]|number_format(0, '.', ',')}}
37 | {{aggregate['true'][6]|number_format(0, '.', ',')}}
38 | {{aggregate['true'][7]|number_format(2, '.', ',')}}
39 | {{aggregate['false'][0]|number_format(2, '.', ',')}}
40 | {{aggregate['false'][2]|number_format(2, '.', ',')}}
41 | {{aggregate['false'][4]|number_format(2, '.', ',')}}
42 | {{aggregate['false'][5]|number_format(0, '.', ',')}}
43 | {{aggregate['false'][6]|number_format(0, '.', ',')}}
44 | {{aggregate['false'][7]|number_format(2, '.', ',')}}
45 | {% endfor %}
46 |
47 |
48 |
49 |
50 |
51 |
52 |
Top 10 Stations for Sell orders
53 |
54 | Station Number of Orders Volume
55 | {% for station in fpsell %}
56 | {{station.stationName|escape}} {{station.coun}} {{station.vol}}
57 | {% endfor %}
58 |
59 |
60 |
61 |
Top 10 Stations for Buy orders
62 |
63 | Station Number of Orders Volume
64 | {% for station in fpbuy %}
65 | {{station.stationName|escape}} {{station.coun}} {{station.vol}}
66 | {% endfor %}
67 |
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 |
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 |
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 | OrderID Issued Volume Left/Entered Minimum Volume Price Duration Station Name Region Name
13 |
14 |
15 | {% for order in sellorders%}
16 | {{order.orderID}} {{order.issued}} {{order.volume|number_format(0, '.', ',')}}/{{order.volumeEntered|number_format(0, '.', ',')}} {{order.minVolume|number_format(0, '.', ',')}} {{order.price|number_format(2, '.', ',')}}
17 | {{order.duration}} {{order.stationName|escape}} {{order.regionName}}
18 | {% else %}
19 | No Orders
20 | {% endfor %}
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
Buy Orders
29 |
30 |
31 | OrderID Issued Volume Left/Entered Minimum Volume Price Range Duration Station Name Region Name
32 |
33 |
34 | {% for order in buyorders%}
35 | {{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}}
36 | {{order.duration}} {{order.stationName|escape}} {{order.regionName}}
37 | {% else %}
38 | No Orders
39 | {% endfor %}
40 |
41 |
42 |
43 |
44 |
45 |
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 |
--------------------------------------------------------------------------------