Compare commits

..

448 Commits

Author SHA1 Message Date
dependabot[bot]
4392c34919
Bump actions/upload-artifact from 6 to 7 (#319)
Some checks failed
Deploy / Check for new spoiler (push) Has been cancelled
2026-03-13 06:17:11 +01:00
tooomm
f8cdbc9a7f
Add more info to parsing error message (#317)
Some checks failed
Deploy / Check for new spoiler (push) Has been cancelled
* Add exception to message

* Add set code

* Print warning in CI summary

* Improve error messages for card parsing
2026-01-28 22:36:33 +01:00
dependabot[bot]
4cef743e46
Bump actions/upload-artifact from 5 to 6 (#315)
Some checks failed
Deploy / Check for new spoiler (push) Has been cancelled
2026-01-07 08:15:08 +01:00
ebbit1q
2f76311165
Update README.md
Some checks failed
Deploy / Check for new spoiler (push) Has been cancelled
2025-12-12 12:46:38 +01:00
tooomm
1060f659a5
Add fallback type_line handling (#313)
Some checks failed
Deploy / Check for new spoiler (push) Has been cancelled
Handle missing type_line in spoiler cards.
2025-11-25 16:57:33 +01:00
dependabot[bot]
2f23aa93c6
Bump actions/checkout from 5 to 6 (#314)
Some checks are pending
Deploy / Check for new spoiler (push) Waiting to run
2025-11-24 19:39:30 +01:00
dependabot[bot]
34068d4d5e
Bump actions/upload-artifact from 4 to 5 (#312)
Some checks failed
Deploy / Check for new spoiler (push) Has been cancelled
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-22 10:24:50 +01:00
tooomm
daa0f09a5e
Increase timeout to 10s
Some checks failed
Deploy / Check for new spoiler (push) Has been cancelled
2025-09-13 11:17:38 +02:00
Zach H
85e4bd093b
Merge pull request #310 from Cockatrice/fix_key_error
Some checks failed
Deploy / Check for new spoiler (push) Has been cancelled
Fix error & more robust MFC handling
2025-08-20 20:04:20 -04:00
dependabot[bot]
257282374f
Bump actions/checkout from 4 to 5 (#311)
Some checks failed
Deploy / Check for new spoiler (push) Has been cancelled
2025-08-18 15:42:55 +02:00
tooomm
f3ac505dac
formatting
Use same formatting as before
2025-08-10 21:37:07 +02:00
tooomm
10aceba7a9
simplify as api guarantees existance
```
Card Face Objects
Multiface cards have a card_faces property containing at least two Card Face objects.
```
See https://scryfall.com/docs/api/cards
2025-08-10 13:42:33 +02:00
tooomm
6a14f8889e fix key error 2025-08-10 13:27:41 +02:00
tooomm
04150f692e
Python 3.10 compatibility
Some checks failed
Deploy / Check for new spoiler (push) Has been cancelled
2025-08-10 13:20:36 +02:00
tooomm
d74a762f35 datetime.datetime.utcnow() is deprecated 2025-08-10 12:15:34 +02:00
tooomm
cae671b37f
Speedup CI (#309)
Some checks failed
Deploy / Check for new spoiler (push) Has been cancelled
* speedup ci

* use req file

* remove cflag as only lxml is affected, but its never build as pip uses available wheels
2025-03-08 11:03:11 +01:00
tooomm
6d0ca400be
Fix badge 2025-03-02 23:22:30 +01:00
tooomm
0b4b3bad0f
Remove old Gitter chat 2025-03-02 23:11:27 +01:00
tooomm
d13dc6b799 Issue templates not fitting 2025-03-02 22:55:17 +01:00
tooomm
4d049b92dc
Add PEP8 note to CONTRIBUTING.md 2025-03-02 21:02:08 +01:00
J. Cameron McDonald
8459b3bfdd
feat: use set priorities (#308)
Some checks failed
Deploy / Check for new spoiler (push) Has been cancelled
* feat: use set priorities

* change FALLBACK from 1 to 0 to match Cockatrice logic
2025-01-19 14:53:35 +01:00
tooomm
b7c6f5e896
Update README.md
Some checks failed
Deploy / Check for new spoiler (push) Has been cancelled
2024-11-02 19:37:01 +01:00
tooomm
7678fadeca
Adjust badges 2024-02-10 15:06:55 +01:00
tooomm
0efd986a16
Allow to define a list of set_types to exclude (#303)
* add sets

* add comment

* No lowercase transformation
2024-02-10 15:00:13 +01:00
dependabot[bot]
c7a7a66523
Bump actions/upload-artifact from 3 to 4 (#305)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-19 00:05:59 +01:00
dependabot[bot]
c619c4ec0b
Bump actions/checkout from 3 to 4 (#304)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-19 00:04:45 +01:00
tooomm
ab0ad6f6bf
Create dependabot.yml 2023-12-19 00:02:22 +01:00
tooomm
bdb0c5367e
Remove json leftovers 2023-11-21 22:06:14 +01:00
tooomm
35c063b909
Display last update date 2023-11-08 18:51:00 +01:00
tooomm
2f5bcd39c5
cleanup requirements (#299) 2023-05-31 10:06:16 +02:00
ebbit1q
fee97dcfa9
fix #234 etc (#297) 2023-03-26 15:04:33 +02:00
tooomm
9df6be76c8
add spoiler label (#298) 2023-03-26 11:10:55 +02:00
ebbit1q
3dd54f59d8
remove writing of json files (#295)
Co-authored-by: tooomm <tooomm@users.noreply.github.com>
2023-03-25 14:34:46 +01:00
ebbit1q
5e716ecdfd
add filename to url in set header (#296) 2023-03-25 13:55:27 +01:00
tooomm
4681501866
Update content badges
add set count, set names now better readable
2023-03-05 00:48:10 +01:00
ebbit1q
9eab59de79
add missing json files to commit in action
fixes #294 again
2023-02-27 15:54:17 +01:00
ebbit1q
4692b6230e
fix missing quotes in action
fixes #294
2023-02-27 04:46:07 +01:00
ebbit1q
6a92707354
update github action (#293)
use the node-16 version of checkout
replace use of set-output with adding to GITHUB_OUTPUT
fix xml comparison using the info tags
fix deploy being triggered while only date has changed
2022-12-11 23:33:07 +01:00
tooomm
ef39de857f
temporarily add old date format in comment for old clients (#292) 2022-02-14 18:51:13 +01:00
ebbit1q
c4764f3c17 don't remove the ? from image urls 2022-02-09 18:29:28 +01:00
tooomm
e9d8f09136
CI: Exclude .json files from deployment (#288)
Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2022-02-08 17:48:50 +01:00
tooomm
8deb35f745
add <info> key (#251)
* add <info> key

* cleanup doubled

* add schemaLocation attribute

* updated date output

* space

* updated version output

* bump to v4

* escape quotes

* fix schema version

* fix

* move comment to match token

* escape

* Revert "escape"

This reverts commit 18df8eda30.

* Revert "move comment to match token"

This reverts commit f1ce0cb850.

* correct direct file link

Co-authored-by: ebbit1q <ebbit1q@gmail.com>

* Update magic_spoiler/__main__.py

* Update magic_spoiler/__main__.py

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2022-02-07 00:38:31 +01:00
tooomm
bd910e37de
Move to Cockatrice v4 db style (#255)
* cockatrice xml db v4

* bump to v4

* at least make the style consistent

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2022-02-06 02:46:29 +01:00
tooomm
bb7980bec2
Adapt script to require UTF-8 (#267)
* properly set encoding

* Update magic_spoiler/__main__.py

Co-authored-by: ebbit1q <ebbit1q@gmail.com>

* fixup code suggestion

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2022-02-06 02:18:09 +01:00
ebbit1q
f17023cf47
Merge pull request #286 from Cockatrice/tooomm-xml_declaration
Fix XML declaration
2022-02-06 01:55:48 +01:00
tooomm
bb0e3b89ea
cleanup 2022-02-05 21:05:47 +01:00
tooomm
c0d726a9e2 add xml declaration 2022-02-05 20:58:45 +01:00
tooomm
a49a417e02 Revert "Merge pull request #277 from Cockatrice/tooomm-patch-5"
This reverts commit 32fc4ef4fc, reversing
changes made to c8b5317293.
2022-02-05 20:54:56 +01:00
ebbit1q
32fc4ef4fc
Merge pull request #277 from Cockatrice/tooomm-patch-5
Add XML declaration
2022-02-05 19:10:51 +01:00
tooomm
5d94c7a3d5 Merge branch 'master' into tooomm-patch-5 2022-02-05 12:19:22 +01:00
ebbit1q
c8b5317293
Merge pull request #284 from Cockatrice/tooomm-patch-8
CI: No new data found notice
2022-02-04 23:56:48 +01:00
ebbit1q
5e6d9ad5f7
Update magic_spoiler/__main__.py 2022-02-04 23:56:11 +01:00
tooomm
a33e90021b
cleanup 2022-02-04 16:54:35 +01:00
tooomm
d234a9ccef
Update __main__.py 2022-02-04 15:19:15 +01:00
tooomm
13023af780
wording 2022-02-04 15:17:32 +01:00
tooomm
433f469140
Merge branch 'master' into tooomm-patch-8 2022-02-04 15:12:56 +01:00
tooomm
b6b518b1d9
title 2022-02-04 15:09:17 +01:00
ebbit1q
9697c74d08
Merge pull request #285 from Cockatrice/tooomm-fix_ci
CI: fix if condition
2022-01-31 23:52:44 +01:00
tooomm
bf02be35c8
fix if condition 2022-01-31 14:27:06 +01:00
tooomm
0610f60c9f
fix quote 2022-01-24 17:31:24 +01:00
tooomm
8afb4cee34
Update __main__.py 2022-01-24 15:43:55 +01:00
tooomm
d54f412793
Update __main__.py 2022-01-24 15:38:34 +01:00
tooomm
1adfed5fc6
Update __main__.py 2022-01-24 15:35:56 +01:00
tooomm
ef0ced8d69
Update __main__.py 2022-01-24 15:27:22 +01:00
tooomm
929a04c915
Update __main__.py 2022-01-24 15:14:30 +01:00
tooomm
8bceceae6e
Update __main__.py 2022-01-24 15:09:11 +01:00
tooomm
bcfb1038c7
Update __main__.py 2022-01-24 15:00:05 +01:00
tooomm
47c3b43963
Update __main__.py 2022-01-24 14:48:05 +01:00
tooomm
764ff7203c
Update __main__.py 2022-01-24 14:44:42 +01:00
tooomm
6fd325b176
tweaks 2022-01-24 14:29:33 +01:00
tooomm
4791c9e2d2
CI: Add link to deploy commit (#279) 2021-07-27 18:46:06 +02:00
tooomm
b18bc002d9
Rework README a bit (#280) 2021-07-24 11:34:04 +02:00
tooomm
4db482a3c8 Merge branch 'master' into tooomm-patch-5 2021-07-21 14:33:23 +02:00
tooomm
8a048f0770
CI: fix syntax (#278) 2021-07-21 14:23:21 +02:00
tooomm
4dc0fb8761
CI: upload artifacts for easy reviewing (#276) 2021-07-21 13:44:03 +02:00
tooomm
15ae2677cd
+ 2021-07-15 20:34:57 +02:00
tooomm
ff54e754c8
re-add xml version and encoding information 2021-07-15 20:27:58 +02:00
ebbit1q
d365ca0375
move settings info to a more visible location (#274)
* move settings info to a more visible location

* put in quotes

Co-authored-by: tooomm <tooomm@users.noreply.github.com>
2021-07-14 12:05:00 +02:00
Zach H
bb2d56df14
Merge pull request #273 from ebbit1q/patch-1
use tilde (`~`) as the SPOILER_MARKER instead
2021-05-27 18:48:44 -04:00
ebbit1q
26e1e22ad0
use tilde (~) as the SPOILER_MARKER instead
* is apparently not allowed as a filename on windows
2021-05-28 00:43:26 +02:00
Zach H
4ea02869be
Merge pull request #272 from ebbit1q/fixes
quick improvements to spoilers
2021-05-27 17:39:09 -04:00
ebbit1q
31ba735d95 add asterisk to spoiler set codes
this will make them not use the same location on disk anymore so people
will complain less about keeping spoiler card art when they don't clear
their image cache

people should still clear their image cache

this is a workaround, a nicer solution should be implemented in
cockatrice sometime
2021-05-27 23:23:34 +02:00
ebbit1q
0f4ec17eb6 remove control characters from xml 2021-05-27 23:23:10 +02:00
ebbit1q
103279671d add include/ to gitignore 2021-05-27 23:22:36 +02:00
Zach H
64c7ba430f
Merge pull request #270 from ebbit1q/inactive_spoilers
fix exception when spoiler season is inactive
2021-04-30 02:14:41 -04:00
ebbit1q
4b25618657 fix exception when spoiler season is inactive 2021-04-30 00:32:43 +02:00
Zach H
c24ab7574d
Merge pull request #265 from ebbit1q/xml_escapes
escape some xml values
2021-01-16 22:47:18 -05:00
ebbit1q
b5d1d938a8 add -p to all mkdirs 2021-01-17 04:44:52 +01:00
ebbit1q
a242ebb1d1 escape some xml values 2021-01-17 04:39:36 +01:00
Zach H
a73195f022
Merge pull request #264 from ebbit1q/languageisaverytrickythingsomesentencescanbeinterpretedindifferentways
correctly compare the output of the python script in the github workflow
2021-01-16 21:29:47 -05:00
ebbit1q
fd65e743cc the suggested changes 2021-01-17 01:27:31 +01:00
Zach H
5dd5d37680
Merge pull request #260 from Cockatrice/tooomm-patch-3
More GH Actions stuff
2021-01-16 12:05:02 -05:00
tooomm
7c791d4f01
step names 2020-12-04 17:48:59 +01:00
Zach H
9e73420094
Merge pull request #263 from ebbit1q/tooomm-patch-3
remove deploy.sh use output instead of exit code
2020-12-04 01:12:07 -05:00
ebbit1q
9481e5ff24 correctly check output
true != 'true'
2020-12-04 03:09:55 +01:00
ebbit1q
91a615f77d do not run the scheduled run on forks 2020-12-04 01:44:55 +01:00
ebbit1q
37f2d8e19b remove deploy.sh use output instead of exit code 2020-12-03 23:41:19 +01:00
tooomm
db458c19b7
last travis pieces 2020-12-03 21:43:52 +01:00
tooomm
7287cb5fbc
format 2020-12-03 21:37:10 +01:00
tooomm
7a138354f4
remove warning 2020-12-03 21:11:55 +01:00
tooomm
da60525795
fix 2020-12-02 22:48:07 +01:00
tooomm
a4bdf439ec
one more warning 2020-12-02 20:39:05 +01:00
tooomm
aec3dd7618 remove deploy key
See #258
2020-12-02 20:24:18 +01:00
tooomm
8cfbdb7d87
add more context to warning 2020-12-01 11:37:45 +01:00
tooomm
7fb119e52e
add ability to run manually from gui 2020-12-01 11:24:40 +01:00
tooomm
9f3d1839e9
Update README.md 2020-12-01 11:22:51 +01:00
tooomm
ce18105f1f
add GH Actions badge 2020-12-01 11:20:48 +01:00
Zach H
e49f6a46cd
Merge pull request #259 from ebbit1q/github_actions
forgot double colons
2020-12-01 00:41:59 -05:00
ebbit1q
a1e2ac1893 forgot double colons 2020-12-01 06:39:10 +01:00
Zach H
eeb03ff0cd
Merge pull request #258 from ebbit1q/github_actions
move to github actions
2020-12-01 00:33:16 -05:00
ebbit1q
b29407d718 move to github actions 2020-12-01 06:26:27 +01:00
Zach H
f34decf10a
Merge pull request #254 from Cockatrice/tooomm-patch-1
README: add discord badge
2020-10-14 11:18:13 -04:00
tooomm
33896b5a78
add discord badge 2020-10-14 16:55:57 +02:00
ZeldaZach
54fc3aae83 Minor fix for image
Signed-off-by: ZeldaZach <zahalpern+github@gmail.com>
2020-09-08 14:07:15 -04:00
ZeldaZach
69fac381f9 Fix Zendikar issues
Signed-off-by: ZeldaZach <zahalpern+github@gmail.com>
2020-09-03 02:11:17 -04:00
tooomm
a54349b037
Update README.md 2020-02-07 02:37:44 +01:00
tooomm
4458316d62
add badge with card count (#248) 2019-10-24 12:49:33 +02:00
tooomm
c136e4122e
adjust hint phrasing 2019-09-11 13:39:11 +02:00
tooomm
f4e6d80e89
show release dates in readme 2019-09-11 13:36:13 +02:00
Zach H
66b2ff35cc
Merge pull request #244 from Cockatrice/tooomm-patch-3
Add warning hint
2019-07-01 16:45:03 -04:00
tooomm
65d4d5afdb
add warning hint 2019-07-01 22:41:19 +02:00
tooomm
c0a584bfbd
remove spoiler tag again 2019-07-01 21:57:13 +02:00
tooomm
c28882d0fd
#242 got lost, reapply 2019-07-01 21:31:41 +02:00
Zach H
d3c20d34d1
Merge pull request #243 from Cockatrice/tooomm-patch-5
set_type: improved handling
2019-07-01 13:46:08 -04:00
tooomm
9762c04041
Merge branch 'master' into tooomm-patch-5 2019-06-28 19:29:05 +02:00
tooomm
a2865b2de0 Revert "add spoiler tag"
This reverts commit c7da69bfab.
2019-06-28 19:27:57 +02:00
tooomm
557fd2a4dc more cases, more capital 2019-06-28 19:24:07 +02:00
tooomm
c09a073fe1 Update __main__.py 2019-06-27 02:13:31 +02:00
Zach H
d57d0c9278
Merge pull request #239 from Cockatrice/tooomm-patch-2
fix indentation in header comment
2019-06-26 19:55:48 -04:00
tooomm
ed895ebf39 fix 2019-06-27 01:53:51 +02:00
Zach H
b2891a2808
Merge pull request #241 from Cockatrice/tooomm-patch-4
fix #240: use set_type from scryfall api
2019-06-26 17:33:09 -04:00
tooomm
968e8fff47 Revert "remove capitalize()"
This reverts commit e148d8d186.
2019-06-26 23:14:26 +02:00
tooomm
e148d8d186
remove capitalize() 2019-06-24 16:22:55 +02:00
tooomm
1d6dcc0e07
try capitalization for set type 2019-06-24 00:20:55 +02:00
tooomm
b6d3875527
use correct set type 2019-06-23 22:56:11 +02:00
tooomm
c7da69bfab
add spoiler tag 2019-06-23 22:47:01 +02:00
tooomm
5dff95b3df
fix indentation in header comment 2019-05-18 17:56:45 +02:00
tooomm
0f794b243e
log wording (#235) 2019-05-18 15:34:09 +02:00
Zach Halpern
5028791018 handle case of no image
Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com>
2019-04-21 11:49:25 -04:00
Zach Halpern
7b2c9801c7 Merge branch 'master' of github.com:Cockatrice/Magic-Spoiler 2019-04-21 11:48:15 -04:00
Zach Halpern
5c646eb10b Prevent cache URL content
Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com>
2019-04-21 11:48:08 -04:00
tooomm
871d39d56b
tweaks 2019-04-13 11:11:09 +02:00
tooomm
a497d7e81a
Update CONTRIBUTING.md 2019-04-04 11:14:08 +02:00
tooomm
297408ca83
cleanup 2019-04-04 10:40:26 +02:00
tooomm
ad229b3227
Update README.md 2019-04-04 10:06:09 +02:00
tooomm
c7a64d89e6
Update README.md 2019-04-04 09:38:21 +02:00
Zach Halpern
83d04f0242 Merge branch 'master' of github.com:Cockatrice/Magic-Spoiler 2019-04-04 03:21:56 -04:00
Zach Halpern
3eb796cbff Add auto removal/creation for SSE file
Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com>
2019-04-04 03:21:53 -04:00
tooomm
9025ac85ee
fix "cockatrice_carddatabase version" 2019-04-04 09:04:41 +02:00
tooomm
50f9a0f421
scryfall only 2019-04-03 13:08:49 +02:00
Zach Halpern
d983fab920 Another shot
Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com>
2019-04-02 22:56:57 -04:00
Zach Halpern
b9d2034948 Add .git check
Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com>
2019-04-02 22:52:39 -04:00
Zach Halpern
cb88104849 checker
Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com>
2019-04-02 22:48:53 -04:00
Zach Halpern
c9fc1b2447 update gitignore
Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com>
2019-04-02 22:46:50 -04:00
Zach Halpern
f9951aedb9 add readme
Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com>
2019-04-02 22:45:19 -04:00
Zach Halpern
5629d02f3c fix up glob
Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com>
2019-04-02 22:44:46 -04:00
Zach Halpern
1a7f698e58 Add JSON
Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com>
2019-04-02 22:41:16 -04:00
Zach Halpern
84b24026e5 changes to meet requests
Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com>
2019-04-02 22:17:11 -04:00
Zach Halpern
68c7150ac1 minor cleanup
Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com>
2019-04-02 13:14:40 -04:00
Zach Halpern
b2c858f4c6 test out without backup
Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com>
2019-04-02 13:07:26 -04:00
Zach Halpern
e882cd6500 More cleanups
Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com>
2019-04-02 12:58:31 -04:00
Zach Halpern
77744522b3 requirements
Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com>
2019-04-01 16:16:59 -04:00
Zach Halpern
3538edc9cd Rebuild to use SF exclusively and publish
Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com>
2019-04-01 16:12:43 -04:00
tooomm
9cefe75576
Update set_info.yml (#228) 2019-03-31 23:21:38 +02:00
Zach Halpern
510fb05931 gitignore 2019-02-13 04:27:43 -05:00
tooomm
15ca5a4721
test if that fixes the proper deletion from spoiler because set is released and data is in mtgjson already 2019-01-30 22:13:32 +01:00
Zach H
0da1b24cdc Bandaid script again to allow travis to succeed 2018-12-30 03:07:00 -05:00
Zach H
68ef367ea9
Merge pull request #226 from Cockatrice/python3_json4
Python3 conversion and Update for MTGJSONv4
2018-12-25 15:06:09 -05:00
Zach Halpern
2f06dcc95d travis 2018-12-25 15:01:32 -05:00
Zach Halpern
20212b176f travis 2018-12-25 14:59:14 -05:00
Zach Halpern
b45f57f5c6 travis 2018-12-25 14:57:25 -05:00
Zach Halpern
3b01ffbec8 travis 2018-12-25 14:55:03 -05:00
Zach Halpern
6480752ede depoy text 2018-12-21 16:11:15 -05:00
Zach Halpern
0b021b377f Python3 conversion and Update for MTGJSONv4 2018-12-21 16:08:26 -05:00
tooomm
aad0479ca1
add spoiler tag
easier differentiation in consuming apps possible
2018-12-08 22:01:05 +01:00
tooomm
9c1ace825f
add gnt
https://mtg.gamepedia.com/Game_Night
2018-11-25 19:12:51 +01:00
tooomm
667b49019f
enable UMA 2018-11-21 21:07:19 +01:00
Dave
34c84e0b16
Merge pull request #222 from Cockatrice/split-cards
split card handling
2018-09-20 00:20:53 -05:00
Dave
3292ebc731
Update scryfall_scraper.py 2018-09-20 00:08:04 -05:00
Dave
def50915bc
split card handling 2018-09-20 00:02:55 -05:00
tooomm
7c0cec2749
remove c18, enable grn 2018-09-09 15:02:26 +02:00
tooomm
6ed303f75e
disable automatic deploy to gh releases
needs a revision
2018-07-24 00:33:37 +02:00
tooomm
f6af154852
Update .travis.yml 2018-07-23 20:36:24 +02:00
tooomm
be008917ca
enable c18 2018-07-23 20:14:44 +02:00
tooomm
12a2b48a90
cleanup 2018-07-19 11:07:16 +02:00
tooomm
5cda79a339
enable m19 2018-06-11 19:48:00 +02:00
tooomm
0f6766945d
remove bbd 2018-06-05 09:17:34 +02:00
tooomm
8e65cfe357
prettify2 2018-05-31 10:22:32 +02:00
tooomm
28f19a2071
prettify 2018-05-31 10:13:53 +02:00
tooomm
73b7c933a3 Update issue templates 2018-05-31 10:13:04 +02:00
tooomm
91cf650082
fix line breaks 2018-05-30 20:03:49 +02:00
tooomm
a896fef12e
add badge to release notes 2018-05-30 19:59:57 +02:00
tooomm
075dd7a40d
switch to xml as data source 2018-05-30 19:55:06 +02:00
tooomm
4767cdffc9
quick deploy fix to skip "untagged-" 2018-05-29 10:06:20 +02:00
tooomm
41011824b6
deployment adjustments 2018-05-29 09:18:35 +02:00
tooomm
4b11546a8a Revert "test regarding error.yml"
This reverts commit 0cb9bb1948.
2018-05-24 20:22:04 +02:00
tooomm
d83827b4de
Update CONTRIBUTING.md (#212) 2018-05-24 20:16:28 +02:00
tooomm
0cb9bb1948
test regarding error.yml 2018-05-24 19:09:24 +02:00
tooomm
4a79afc0de
hint about set size [skip ci] 2018-05-24 19:08:28 +02:00
tooomm
b419569752
Update set_info.yml 2018-05-24 19:03:42 +02:00
tooomm
00c4a45663
add cm2 and c18 2018-05-24 19:00:46 +02:00
tooomm
e2906c31da
deploy only as draft to gh releases 2018-05-24 16:20:04 +02:00
tooomm
039a179b41
typo + wording 2018-05-24 15:43:38 +02:00
tooomm
69cbce049c
Update README.md 2018-05-24 15:41:17 +02:00
tooomm
b2c33879cb
add GRN
https://mtg.gamepedia.com/Guilds_of_Ravnica
2018-05-24 15:19:27 +02:00
tooomm
2826ce15b9
more bbd 2018-05-24 14:21:48 +02:00
tooomm
0a7ad363f6
add bbd (enabled) + rna 2018-05-24 14:13:06 +02:00
tooomm
250dcc10a6
remove blocks (#207)
* concept of blocks deprecated

* fix space from be97d7d
2018-05-17 10:22:05 +02:00
tooomm
d089a398d3 Update issue templates
weird gui xD
2018-05-17 10:10:21 +02:00
tooomm
5364e9fb95 Introduce issue templates
first try
2018-05-17 10:08:55 +02:00
tooomm
bd81ece2dc
Revert "deploy to gh releases (#210)" (#211)
This reverts commit 0a7561970e.
2018-04-21 12:41:49 +02:00
tooomm
0a7561970e
deploy to gh releases (#210) 2018-04-21 12:03:43 +02:00
Dave
1a6dbebdb5
Fix Scryfall Basic Land Rarity to match mtgjson 2018-04-14 00:04:59 -05:00
Zach H
36c4eb7540
Merge pull request #209 from Cockatrice/tooomm-rename
rename .travis folder
2018-03-19 04:07:51 -04:00
tooomm
292c7fc432 add .enc file back 2018-03-18 18:02:12 +01:00
tooomm
711a349e9e temp gitignore update 2018-03-18 18:01:35 +01:00
tooomm
f1bb2c2d36 folder rename 2018-03-18 17:59:04 +01:00
Dave
be97d7d075
Enabled DOM via Scryfall 2018-03-09 16:13:49 -06:00
tooomm
32f2cc48dc
enable A25 (scryfall only) (#206)
* enable scryfall for A25

* readd xml dump
2018-02-07 14:48:33 +01:00
tooomm
e31191b7e0
remove rix (2/2) (#203) 2018-01-23 21:42:04 +01:00
Dave
050711b393
Merge pull request #193 from Cockatrice/tooomm-content_badge
readme: add spoiler content badge
2018-01-11 23:47:45 -06:00
tooomm
7cf711f8f1
Merge branch 'master' into tooomm-content_badge 2018-01-11 14:22:42 +01:00
Zach H
c6fcc11cdd
Merge pull request #197 from Cockatrice/tooomm-patch-1
add card count to dom and m19
2018-01-09 03:56:53 -05:00
tooomm
f31f5f665c
add card count
https://magic.wizards.com/en/products/core-2019
https://magic.wizards.com/en/products/dominaria
2018-01-09 05:18:28 +01:00
tooomm
6259101c3c
fix link 2018-01-06 19:35:13 +01:00
Dave
624b586014
Update deploy.sh 2018-01-05 22:30:01 -06:00
Dave
85eb8ae9cc
Update set_info.yml 2018-01-05 22:23:12 -06:00
Dave
9e5419fb46
Update set_info.yml 2018-01-05 22:09:54 -06:00
Zach H
b65c812c02
Merge pull request #194 from Cheldra/patch-2
RIX first batch of corrections
2018-01-04 15:46:18 -05:00
Cheldra
875884fc9b
RIX first batch of corrections
All planeswalker loyalties needed to be fixed.
These are the errors currently in errors.yml.
2018-01-04 20:35:28 +00:00
tooomm
dec0718b83
enable RIX 2018-01-02 17:11:00 +01:00
tooomm
b82a91f1e7
include link to spoiler.xml file 2017-12-06 21:54:33 +01:00
tooomm
18ce0e2062
add spoiler content badge
dynamic badge which pulls data from our own spoiler.json file
2017-12-06 21:16:01 +01:00
tooomm
fdb26636a2
remove ust + prework for rix season (#190)
* remove ust + prework for rix

https://magic.wizards.com/en/products/rivals-ixalan
2017-12-06 16:36:02 +01:00
tooomm
b4f829a9e7
add sleep timer doc (#187) 2017-12-06 16:35:19 +01:00
Dave
626457372b
Let's try scryfall UST 2017-11-24 08:04:38 -06:00
tooomm
19de03adce
remove ima (part2) + enable ust (#182)
* remove ima

* add ust
https://magic.wizards.com/en/products/unstable
2017-11-13 20:51:12 +01:00
tooomm
f95b4bfd84
print running hint in travis log (#175)
* "running script" hint

* Combine duplicate prints
2017-11-03 14:19:35 +01:00
tooomm
21f66c65b6
remove unneeded time stamp in console log (#179)
* Remove debug/console timestamp
2017-11-02 18:55:10 +01:00
Dave
5b20149518
Update scryfall_scraper.py 2017-11-01 16:28:35 -05:00
Dave
0ab2e3ebce
Handle new Scryfall Image keys
Scryfall went from a single 'image_uri' to a variety of options inside 'image_uris'
2017-11-01 15:38:03 -05:00
Dave
2957b1adc5 Merge pull request #177 from Cockatrice/tooomm-remove_xln2
remove xln from set_info
2017-09-21 09:30:00 -05:00
tooomm
38ab12a93b remove xln from set_info 2017-09-20 22:42:50 +02:00
Dave
be76183d14 Merge pull request #173 from Cockatrice/dev-id-dfc-xml-writer
Let xml writer handle back sides of DFC
2017-09-18 12:46:12 -05:00
Dave
432ba1d028 Let xml writer handle back sides of DFC 2017-09-18 12:38:39 -05:00
Dave
3279ea743e Merge pull request #171 from Cockatrice/tooomm_xln-scryfall
switch xln to scryfall as only source
2017-09-18 11:36:56 -05:00
tooomm
c90940d91a switch xln to scryfall as only source 2017-09-18 17:47:20 +02:00
Cheldra
f81be4e592 Rowdy Crew rarity fix (#169)
Mythic -> Mythic Rare
2017-09-18 12:55:09 +02:00
Dave
512ea06646 fix Scryfall scraper (#168)
Add all pages from Scryfall
2017-09-17 18:59:50 +02:00
tooomm
e07a61781e fix azcanta 2017-09-17 17:01:41 +02:00
tooomm
b997e04b4c Revert "azcanta is present in our source"
This reverts commit 8433cf900b.
2017-09-17 17:00:00 +02:00
tooomm
8433cf900b azcanta is present in our source
no need for manual card
2017-09-17 16:53:10 +02:00
tooomm
09044c54ee fix spaces 2017-09-17 16:41:37 +02:00
Cheldra
10b5ce3698 3 last missing cards, back faces + documentation update (#166)
Added a line in the documentation about newlines
    Arcane Adaptation
    Sorcerous Spyglass
    Daring Saboteur
    All 10 back faces of transform cards
2017-09-17 15:25:13 +02:00
tooomm
275cedf42b update for rowdy crew [skip ci] 2017-09-16 19:49:35 +02:00
Cheldra
027493584a Adding Rowdy Crew (#165) 2017-09-16 19:39:18 +02:00
Cheldra
4799e92600 Adding missing cards + improving documentation (#160)
* Adding Bloodcrazed Paladin
* Adding Captain Lannery Storm
* Adding Vanquisher's Banner
* Adding Dragonskull Summit
* Adding remaining checklands
* description tweaks [skip ci]
2017-09-16 19:20:08 +02:00
tooomm
ab6d495839 Gilded Sentinel: add p/t (#157) 2017-09-15 22:11:40 +02:00
Cheldra
85f5d4043d Dark Nourishment manacost fix (#156) 2017-09-15 22:01:29 +02:00
Dave
a4a71e9779 Force Travis 2017-09-15 14:55:01 -05:00
Dave
d90d05b942 Merge pull request #154 from dev-id/error-for-manaCost
Error for manaCost == ""
2017-09-15 12:42:59 -05:00
Dave
93b65146ec Double space to single space after period in card text. (#152) 2017-09-12 11:38:54 +02:00
dev-id
7243182be3 Error for manaCost == "" 2017-09-11 12:22:10 -05:00
tooomm
2f5ccb4887 typo [skip ci] 2017-09-08 22:04:12 +02:00
Dave
30d2ce5d82 Update cards_corrections.yml 2017-09-08 13:14:45 -05:00
Dave
a791946828 Update cards_corrections.yml 2017-09-08 13:11:18 -05:00
Dave
075faa71ac Update spoilers.py 2017-09-08 12:49:45 -05:00
Dave
0a258379fb Update spoilers.py 2017-09-08 12:40:36 -05:00
Dave
2f54837d88 Update cards_corrections.yml 2017-09-08 12:24:54 -05:00
Dave
d09248b837 Update cards_corrections.yml 2017-09-08 12:09:57 -05:00
Dave
0bc5aca633 Update cards_corrections.yml 2017-09-08 12:03:04 -05:00
Dave
3849e977a0 Update cards_corrections.yml 2017-09-08 11:37:50 -05:00
Dave
66871a12f2 Merge pull request #146 from dev-id/future-sets-cleanup
Improved no-cards set debug print
2017-09-08 10:16:09 -05:00
dev-id
78f5dd2df9 Improved no-cards set debug print 2017-09-08 10:12:15 -05:00
Dave
dea584cb35 [WIP] Double-faced card handling (#144)
* Double-faced card handling

Remove duplicate debug print (no image shows up in error file)

* Handle DFC different than Split

* Don't DFC if no number

Cards with ? for a card number shouldn't be attempted to match for DFC
2017-09-07 21:08:06 -05:00
Dave
b1904667e4 Merge pull request #145 from dev-id/future-sets-cleanup
Don't make set files for sets without cards.
2017-09-07 11:28:09 -05:00
dev-id
fa10639000 Don't make set files for sets without cards.
Don't put them in spoiler.json
2017-09-07 11:21:06 -05:00
tooomm
0fd2ead70d more future sets info (#143)
Add future sets to setinfo with any available information completed and the rest commented.

noRSS enabled for each, spoiler.rss is current-set-only. As MTGS spoiler.rss gets updated, noRSS should be removed/falsed for the current set. 

* updated with basic info for future sets

source: http://www.mtgsalvation.com/forums/magic-fundamentals/the-rumor-mill/673776-schedule-of-upcoming-releases-and-spoiler-seasons

* added mythicCode to optional keys

* Update set_info.yml

http://magic.wizards.com/en/products/iconic-masters

http://markrosewater.tumblr.com/post/163779974563/wait-so-the-unstable-set-code-is-ust-i-thought

http://magic.wizards.com/en/products/rivals-ixalan

http://magic.wizards.com/en/products/masters-25

http://magic.wizards.com/en/products/dominaria

http://magic.wizards.com/en/products/core-2019

* Update set_info.yml

* Update set_info.yml

* Update mtgs_scraper.py

* Update mtgs_scraper.py

* Update set_info.yml

* more documentation

* Only print set stats for sets with cards

* Move set has cards check for debug print

* Print line if set has no cards
2017-09-07 10:32:42 -05:00
tooomm
78776169ab add huatli loyalty 2017-09-06 19:46:33 +02:00
Dave
50294c8f30 Merge pull request #142 from dev-id/new-wotc-url2
New WOTC card gallery URL
2017-09-06 12:26:35 -05:00
dev-id
da4a8ba28b New WOTC card gallery URL 2017-09-06 12:20:19 -05:00
Dave
16bbda2c22 Test Travis no-change 2017-09-05 15:13:40 -05:00
Dave
9545f30b99 Oops true-->false 2017-09-05 15:09:11 -05:00
Dave
5368ea3368 Travis debug 2017-09-05 15:03:50 -05:00
Dave
fd228f72e0 Update deploy.sh 2017-09-05 14:56:38 -05:00
Dave
9e8c20fd06 Merge pull request #141 from dev-id/ignore-date-only-commit
Don't commit travis only spoiler datetime change
2017-09-05 14:49:34 -05:00
Dave
37d12eb5d9 Merge branch 'master' into ignore-date-only-commit 2017-09-05 14:47:30 -05:00
Dave
1d443142ac Don't commit travis only spoiler datetime change
If only the date time in spoiler.xml has changed, don't commit.
2017-09-05 14:39:33 -05:00
Dave
01fcaec7c9 No empty commits from Travis 2017-09-04 13:49:53 -05:00
Dave
1b549638dc Merge pull request #136 from dev-id/small-getimage-fx-refactor
Refactor get_image_urls function to use only `setinfo` as input.
2017-09-04 10:46:14 -05:00
Dave
1f0f3129a0 Merge pull request #138 from dev-id/strip_card_name
Strip leading/trailing spaces from card names.
2017-09-04 10:46:03 -05:00
Dave
9d00b4f05a Error log to YAML (#137)
* Error log to YAML

* Also dump debug print to YAML
2017-09-04 10:45:50 -05:00
dev-id
00dd79bf14 Strip leading/trailing spaces from card names.
May prevent certain image sources from working (mtgs?)
2017-09-02 21:51:04 -05:00
dev-id
9693dad628 Refactor get_image_urls function to use only setinfo as input. 2017-09-02 21:21:29 -05:00
tooomm
c036cefbf7 remove old card names
cards doesn't show up in mtgs spoiler.rss (anymore)

wrong names on unconfirmed cards most likely anyway:
what used to be "Join the Party" is called "Call to the Feast" now for example
2017-09-02 20:02:18 +02:00
Cheldra
d042f71009 New set_info value mythicCode (#135)
* New set_info value mythicCode

Mythicspoiler is using IXA rather than XLN for some reason. Creating a new parameter to account for this.

* mythicCode

* mythicCode

* Mtgs name fixes

* Mtgs name fixes

* Set mythicCode to code if not present in setinfo
2017-09-02 10:25:22 -05:00
tooomm
bbf48cf077 more loyalty fixes
please include loyalty information in your feed, mtgs guys...
2017-09-01 20:37:37 +02:00
tooomm
588072b9b2 loyalty fix 2017-08-28 20:58:15 +02:00
tooomm
4dd80ac308 fix pics (#134) 2017-08-28 20:45:40 +02:00
tooomm
77ec60570e remove old sets and better correction files (#133)
* remove old sets and better correction files

* debug manual_sets

* Update main.py

* little tweaks
2017-08-26 06:23:03 +02:00
tooomm
ff26d45bd0 move utc tag in xml file 2017-08-23 11:33:11 +02:00
tooomm
0293efcea0 add timezone to runtime 2017-08-23 11:26:20 +02:00
tooomm
0c6b894f07 enable XLN parsing (#131)
* enable xln
* clarify dashes in comment
2017-08-23 08:55:45 +02:00
Dave
c98dd74e48 Convert HOU headers to comments as example 2017-08-22 12:59:17 -05:00
tooomm
d64c1f7c48 correct c17 date
http://magic.wizards.com/en/articles/archive/feature/commander-2017-edition-release-notes-2017-08-11
2017-08-14 11:05:51 +02:00
tritoch
9d5f723fc8 Use scryfall data for C17 (disables all other sources) (#127)
* Use scryfall data for C17 (disables all other sources)

* Allow individual sets to be scryfall-only sourced.

Remove prototyped variables (and add one...)
2017-08-12 23:20:24 -05:00
Lee Matos
e4a5b51307 refactor no cards found exit case to be more pythonic (#123)
replace counting loop with len()
2017-07-14 22:32:53 -05:00
tritoch
1e52497bc3 Update deploy.sh 2017-07-13 21:11:28 -05:00
tritoch
39a6ba1464 Travis debug (#122)
Kill background process hanging Travis-CI
2017-07-13 21:07:30 -05:00
tritoch
f561e483aa Debug travis timeout (#121)
* travis debug

* Update .travis.yml
2017-07-13 15:37:57 -05:00
tooomm
0b0c19ca06 folder for all Travis CI files and scripts (#117)
* add .travis folder

- gather all travis related files in one folder
- update location calls

* Move verify_files back out

* Update .travis.yml

* Move deploy_key to .travis folder
2017-07-11 14:30:14 +02:00
Zach H
2b41643255 Merge pull request #109 from tritoch/replace-typeline-dash
Replace `-` in type line with `—`
2017-07-11 00:17:52 -04:00
Zach H
b1693f3c30 Merge pull request #119 from tritoch/exit-on-xsd-fail
Exit with failure on XSD invalid
2017-07-11 00:16:37 -04:00
tritoch
a58e8fac01 Exit with failure on XSD invalid 2017-07-10 22:59:27 -05:00
tooomm
a3a3fc74e3 update date/time format (#114)
Remove invalid and redundant datetime tags
2017-07-10 21:21:07 -05:00
tritoch
2457a5bd34 Merge pull request #115 from Cockatrice/tooomm-print_log 2017-07-10 20:46:25 -05:00
tooomm
88c93c013d adjust print in log
grammar
2017-07-10 15:09:13 +02:00
tritoch
529a4da3d9 Remove cards from MTGS RSS that aren't in gallery (#112) 2017-07-07 16:32:02 -05:00
tritoch
f74bd35c8d Eliminate 'split_cards' array. Use 'names' key to determine split cards. (#111) 2017-07-07 16:27:50 -05:00
tritoch
39f6243846 Prettify set xml files (#110) 2017-07-07 16:27:36 -05:00
tritoch
846a41d2e2 Error on type not in valid type list (#108) 2017-07-07 16:27:22 -05:00
tritoch
4d62dcf946 Fix costs in text (#107)
* Replace anything found in `{}` with uppercase, single character versions

* Remove debug print
2017-07-07 16:27:00 -05:00
tritoch
291efdb19e Replace - in type line with 2017-07-07 00:20:44 -05:00
tritoch
c13a719944 Remove open parenthesis deletion
Fixes #81
2017-07-06 23:18:17 -05:00
tritoch
9004f6d285 formatting 2017-07-06 22:35:53 -05:00
tritoch
04e6a1892f Verify spoiler.xml against Cockatrice's XSD file (#105)
Verifies spoiler.xml against Cockatrice's XSD file

verify_xml currently takes an xml file and XSD as a string

Prints a pass/fail above XML dump

* Re-order xml writing to pass XSD

* Improved XSD verification

Now prints error

Now handles malformed XML or XSD
2017-07-06 21:37:11 -05:00
tritoch
3ee6aa3842 Merge pull request #106 from tritoch/build-date
Build date in XML
2017-07-06 21:36:16 -05:00
tritoch
dc9b9b7a48 Refactor set_info, download_images to scraper sub
Refactor set_info to align with mtgjson keys.

Move download_images to wizards_scraper
2017-07-06 19:46:26 -05:00
tritoch
599aaee733 Less gitter notifications from Travis
Fixes #93
2017-07-06 17:56:56 -05:00
tritoch
c40355f0fb Build date in XML 2017-07-06 17:49:36 -05:00
tritoch
5b987d28cf Change input files to YAML (#99)
Input files to yaml

Deduplicate file verification, move it out to module.

Remove commentjson requirement
2017-07-06 14:25:10 -05:00
tritoch
10aed51df3 Merge pull request #102 from leematos/lm-remove-urllib
Remove urllib requirement and replace with requests
2017-07-06 09:37:13 -05:00
tooomm
146db4f741 Travis: run on ubuntu trusty container-based images (#101)
Use trusty (Ubuntu 14.04) for travis. Don't version lock requirements. This should not affect non-travis users because the bug in requests has been fixed to my knowledge.
2017-07-06 09:26:33 -05:00
Lee Matos
8900c1f8af Remove urllib requirement and replace with requests 2017-07-05 22:43:19 -04:00
Lee Matos
1dd538d5a1 First pass refactoring scrapers into separate modules (#98)
Splits off the respective scrapers into submodules (mtgs_scraper.py, scryfall_scraper.py, mythic_scraper.py, wizards_scraper.py)
2017-07-05 20:44:45 -05:00
tooomm
2af17727a4 enable pip cache & non-optimizing cmake for travis (#92)
pip cache & non-optimizing compiling for travis speedups
2017-07-05 00:12:24 -05:00
tritoch
fc916a58f4 Merge pull request #89 from Cockatrice/tooomm-card_fix
not only fix type, but also types
2017-07-03 06:39:27 -05:00
tritoch
d99d2f7c91 Merge pull request #84 from tritoch/post-mtgjson-wipe
After MTGJSON adds the sets, we don't want them anymore
2017-07-03 06:39:12 -05:00
tooomm
9862add338 not only fix type, but also types 2017-07-03 01:31:13 +02:00
tritoch
80c09b1e88 After MTGJSON adds the sets, we don't want them anymore. Goodbye spoiler season! 2017-07-02 11:07:16 -05:00
Lee Matos
91cc9559e5 refactor loading json (#77)
* refactor loading json
2017-07-02 10:57:23 -05:00
Cheldra
626fa6be9d Fixing a bunch of ability costs and a typeline (#79)
* Fixing a bunch of ability costs and a typeline

* Visage of Bolas and Cinder Barrens fix
2017-07-02 10:56:53 -05:00
tritoch
324fcb6bbb Merge pull request #76 from Cockatrice/tooomm-remove_split_card
remove split card definition from script start
2017-07-02 10:53:41 -05:00
tooomm
7bde6d0ea8 sifter wurm: correct rarity (#82) 2017-07-02 15:07:16 +02:00
tooomm
f320660a22 remove split card 2017-07-01 01:32:12 +02:00
tritoch
5c5699d776 Regular set masterpieces should have regular set image 2017-06-30 12:59:35 -05:00
tritoch
cebc4ffd45 Update cards_delete 2017-06-30 11:40:41 -05:00
tritoch
86bfa1f0de Crumbling Necropolis not in HOU 2017-06-30 11:25:21 -05:00
tritoch
d6853982e9 Merge pull request #73 from tritoch/scryfall-fixes-write-allsets-toggle-errorlog
Write AllSets.json

Don't scrape scryfall if we disable comparison

Toggle for Dumping Error log
2017-06-30 11:04:48 -05:00
tritoch
fe50a05225 Card corrections
Marauding Boneslasher namefix
Graven Abomination & Wall of Forgotten Pharaohs P/T fix
2017-06-30 11:00:41 -05:00
tritoch
4e43b90156 Write AllSets.json
Don't scrape scryfall if we disable comparison

Toggle for Dumping Error log
2017-06-30 09:42:36 -05:00
tritoch
d8d31f4aab Scrape mana symbols from WOTC card gallery (#68) 2017-06-30 09:05:43 -05:00
tooomm
35f5891253 remove no longer needed pic corrections (#58) 2017-06-29 07:17:02 -05:00
tritoch
07d5a8a57a Nicol Bolas, the Deceiver Loyalty
Loyalty now known http://media.wizards.com/2017/images/daily/en_iSHCrCgmya.png
2017-06-28 20:15:48 -05:00
tritoch
876d3a800f Merge pull request #71 from tritoch/split-aftermath-image-match
Better split/aftermath image matching
2017-06-28 20:04:32 -05:00
tritoch
82186fdf2e Better split/aftermath image matching 2017-06-28 19:43:59 -05:00
tritoch
9f896e1a0a Forget me not 2017-06-28 16:09:42 -05:00
tritoch
6b440d6565 Merge pull request #69 from Cockatrice/tooomm-fix_hollowone2
card fix: hollow one
2017-06-28 15:41:02 -05:00
tritoch
5b0ef041ab Merge pull request #70 from tritoch/x-manacost
X manacost, split card improvements, error for nonland with blank manacost
2017-06-28 15:40:32 -05:00
tritoch
e40a7063b8 Handle X in card costs.
Slightly improved split card handling.

Error for Blank mana cost on nonland. If a nonland card with no mana cost (`0` is a mana cost) is printed, adding it to card_corrections will prevent it from appearing in the error log.
2017-06-28 12:27:09 -05:00
tritoch
3caffc6420 Document Travis Settings [skip ci] 2017-06-28 08:19:25 -05:00
tooomm
4043729cc2 fix hollow one 2017-06-28 10:18:49 +02:00
tooomm
d14320b07d readme: add output file explanations (#62) [skip ci]
* [ci skip] add output file explanations

* [ci skip] change presentation

* Naming & Links [skip ci]
2017-06-27 23:45:27 -05:00
tritoch
88501f8ce2 Add sleep to print entire log 2017-06-27 23:32:37 -05:00
tooomm
883af49ac8 delete double card (#65)
Remove erroneous card `Endless `
2017-06-27 14:19:13 -05:00
tritoch
813ded7802 Pre flight (#64)
* Pre-flight - verify input files before running main script.

* Pre-flight - verify input files before running main script.
2017-06-27 13:03:35 -05:00
tritoch
ea7c9228be Merge pull request #63 from tritoch/full-spoil-prep
Full Spoil preparations
2017-06-27 13:03:15 -05:00
tritoch
c32b280391 Pass set name to fullspoil for automatic URL creation 2017-06-27 12:45:42 -05:00
tritoch
fe96df161d Full Spoil preparations
Handle both WOTC gallery formats
2017-06-27 12:07:02 -05:00
tritoch
869ba84a19 Remove card exemption (#57)
* Remove card exemption

Mistakenly added thinking it was not a valid card.
2017-06-26 08:44:38 -05:00
tooomm
ef744d82ed readme update (#59) [skip ci]
* [ci skip] readme update

- outdated requirements are now always linked to up-to-date file
- added spoiler files to output list
- rephrases

* small tweak
2017-06-26 08:12:30 -05:00
skwerlman
0f77039fe7 fix list markdown for requirements (#60)
* fix list markdown for requirements

also fix unmatched bracket

* List additional requirements
2017-06-26 08:09:17 -05:00
tritoch
780a7c7715 WotC Image Gallery Regex Fix (#56)
WOTC changed the format for their card gallery, this will find the images with the new format.
2017-06-23 16:14:47 -05:00
tritoch
ec7270c524 Log Cleanup
* Scryfall debug log off by default

* Scryfall logging disabled by default.

Manual corrections print on one line.
2017-06-23 09:20:40 -05:00
tooomm
ee61c68cba [ci skip] add comment, no delete from mps 2017-06-23 14:12:39 +02:00
tritoch
1ad19e2d30 Log fix (#49)
* Don't show error log if none.
2017-06-22 23:53:58 -05:00
tritoch
c5ca8dd879 Fix Uncage the Menagerie manaCost
http://mythicspoiler.com/hou/cards/uncagethemenagerie.jpg
2017-06-22 14:24:09 -05:00
tritoch
be0d113435 Ignore blank images from MTGS (#48) 2017-06-22 12:55:44 -05:00
tritoch
248e09219e More aftermath improvements. (#47)
"layout" now is "aftermath".

"colors" and "colorIdentity" will check both sides of card.
2017-06-22 12:54:59 -05:00
tritoch
3efba33849 Remove Thoughtseize from main HOU set 2017-06-22 12:54:42 -05:00
tritoch
55fe07b63b Prevent Print Error from Halt
If a unicode error happens in a debug print, program halted.
2017-06-22 12:19:20 -05:00
tooomm
5f56b74952 Update README.md with Files branch history (#45) [skip ci] 2017-06-22 11:28:16 -05:00
tooomm
498cfffe42 deploy from master branch only (#42) 2017-06-22 10:01:17 -05:00
tritoch
60279ab3f9 Error Fix (#43)
* Errors with loading input files now cause CI to fail

* Misc syntax corrections

* Images for Reason // Believe
2017-06-22 08:55:27 -05:00
tooomm
ba4fdfc9f2 missing comma (#41) 2017-06-22 08:29:11 -05:00
tritoch
82a74439b8 Syntax Fix 2017-06-22 06:28:51 -05:00
Cheldra
82ae195d31 Update cards_corrections (#38)
Planeswalker Deck Cards image source to WOTC
2017-06-21 13:34:45 -05:00
tritoch
6d21eb5ca8 Scrape MTGS for images. Fix WOTC URL (#36)
* Scrape MTGS for images

* Fix WOTC card gallery URL.

* Additional Aftermath Fixes
2017-06-21 12:44:39 -05:00
tritoch
ab75dd2104 Merge pull request #35 from tritoch/aftermath-mtgs-handling
Aftermath Handling
2017-06-21 10:13:35 -05:00
tritoch
be0a51267c Aftermath Handling
Split MTGS-sourced aftermath card text on two newlines instead of three, which will catch both
2017-06-21 10:08:03 -05:00
tritoch
82836d7141 Update old branch value 2017-06-21 09:33:13 -05:00
tritoch
3f83451020 Don't deploy non-master branches to Files branch 2017-06-21 09:25:04 -05:00
Cheldra
09b8db64aa Non-english & low-quality pic fixes (#32) 2017-06-20 16:56:50 -05:00
Cheldra
1611302326 Crook of Condemnation pic added (#30)
* Crook of Condemnation pic added

* Syntax
2017-06-20 15:00:30 -05:00
tritoch
eba81f48d7 Add try/except blocks for file loading. (#31) 2017-06-20 14:52:53 -05:00
tritoch
839ce66d3a Syntax fix 2017-06-20 13:03:07 -05:00
tritoch
64c9a15bc3 Cardfix Crook of Comdemnation --> Crook of Condemnation
http://www.mtgsalvation.com/cards/hour-of-devastation/28947-crook-of-comdemnation
2017-06-20 12:58:46 -05:00
Cheldra
6e9e0b4afa Update cards_corrections (#28)
* Update cards_corrections

* Syntax & Extra Bracket
2017-06-20 12:46:21 -05:00
tritoch
0f2d772821 Return of Corrections (#29) 2017-06-20 12:33:32 -05:00
tritoch
b471f1073d Fix Typing
Type for printing
2017-06-20 11:40:25 -05:00
tritoch
3572607c47 Merge branch 'argument-parser' into master 2017-06-20 11:35:25 -05:00
tritoch
5d48d567c3 Add command line argument parser.
Can change a preset by adding `preset=newvalue` to command line execution.
2017-06-20 11:33:07 -05:00
tritoch
75421ddd2d Add command line argument parser.
Can change a preset by adding `preset=newvalue` to command line execution.
2017-06-20 11:29:03 -05:00
tritoch
c68da7ccab Types and errorlog improvements (#26)
* Ignore corrected files in Error.
Card Type/Types detection improvement.

* Merge fix

Bad Merge Conflict Resolution
2017-06-20 09:50:16 -05:00
Cheldra
45de69d330 Using mtgs over mythic when they differ (#25)
Mtgs updates their images as higher quality pics are found
2017-06-20 09:50:01 -05:00
tritoch
47234b17d2 Corrections Post Scrape, Unicode JSON
Corrections should be after all scraping. Output files should be Unicode.
2017-06-19 20:38:12 -05:00
tritoch
45d2233058 Merge pull request #4 from Cockatrice/master
Synchronize Upstream
2017-06-19 18:57:06 -05:00
tooomm
f7e71f4ca1 full list of supported services + rephrasing (#11) [skip ci]
* full list of supported services + rephrasing

* Update README.md

* [skip ci] final wording
2017-06-19 16:23:38 -05:00
tritoch
a70b518f30 Merge pull request #19 from tritoch/split-cards-and-xml-dump
Split Card fix and XML dump
2017-06-19 16:03:50 -05:00
tritoch
4f49559751 Missed file extension 2017-06-19 15:57:11 -05:00
tritoch
8d96f7070f Merge pull request #3 from Cockatrice/master
Synchronize Upstream
2017-06-19 15:51:49 -05:00
tritoch
ba87735c6a Merge branch 'master' into split-cards-and-xml-dump 2017-06-19 15:31:35 -05:00
tritoch
9f51dce940 Merge pull request #17 from tritoch/comment-json
Allow comments in JSON files
2017-06-19 15:30:30 -05:00
tritoch
fbdee30d65 Improved Split Card Handling. Dump XML to log. 2017-06-19 15:20:48 -05:00
tritoch
ef4f2c7377 Improved Split Card Handling. Dump XML to log. 2017-06-19 14:49:00 -05:00
tritoch
4ee8c132eb Improved Split Card Handling. Dump XML to log. 2017-06-19 14:45:32 -05:00
tritoch
9fbd0c37f8 Merge branch 'master' into comment-json 2017-06-19 12:23:48 -05:00
tritoch
3084417241 Merge pull request #2 from Cockatrice/master
Synchronize Upstream
2017-06-19 12:17:24 -05:00
tritoch
6a9ef7bfd0 Merge pull request #16 from tritoch/master
Aftermath handling for MTGS
2017-06-19 12:16:01 -05:00
tritoch
094ca85029 Allow comments in JSON files 2017-06-19 12:12:55 -05:00
tritoch
aac7664caf Revert "Allow comments in JSON files"
This reverts commit 9f1b096e0c.
2017-06-19 13:06:40 -04:00
tritoch
b426dc2662 Merge pull request #13 from Cheldra/patch-1
Fixing 3 Mythicspoiler typos
2017-06-19 12:03:58 -05:00
tritoch
f8c97d551e Lowercase Key 2017-06-19 11:59:25 -05:00
tritoch
9f1b096e0c Allow comments in JSON files 2017-06-19 11:55:09 -05:00
Cheldra
8cdcfa6b51 Fixing a stray comma 2017-06-19 17:48:25 +01:00
tritoch
b914c445d1 Aftermath handling for MTGS 2017-06-19 11:35:13 -05:00
Cheldra
f03a49b2bf Using English & higher quality art 2017-06-19 17:34:46 +01:00
Cheldra
b170d6292e Update cards_corrections.json 2017-06-19 17:28:54 +01:00
tooomm
d793ac5bbe Merge pull request #14 from Cheldra/patch-2
Sunset Pyramid duplicate
2017-06-19 18:05:26 +02:00
Cheldra
5e07f55a2c Update cards_corrections.json 2017-06-19 16:08:30 +01:00
Cheldra
1bf91f9424 Update cards_corrections.json 2017-06-19 16:08:06 +01:00
Cheldra
258384990f Update cards_corrections.json 2017-06-19 16:01:04 +01:00
Cheldra
d83adfe084 Update cards_delete.json 2017-06-19 15:50:06 +01:00
Cheldra
71663e2adc Fixing 3 Mythicspoiler typos 2017-06-19 15:48:55 +01:00
tritoch
edd0afdf77 Merge pull request #12 from tritoch/master
Remove html tag from card text
2017-06-19 08:05:50 -05:00
tritoch
d4ca443300 Remove html tag from card text 2017-06-18 22:07:59 -05:00
tritoch
3aa5061712 Merge pull request #10 from tritoch/master
Return of Split Cards. MPS set starting. Sanitize Scryfall.
2017-06-17 00:12:25 -05:00
tritoch
aa7ea2270b Return of Split Cards. MPS set starting. Sanitize Scryfall. 2017-06-17 00:10:52 -05:00
tritoch
b6a1204c79 Travis Run 2017-06-16 23:38:39 -05:00
tritoch
534f197f18 Run Travis 2017-06-16 22:43:39 -05:00
tritoch
52b0b42cb3 Merge pull request #9 from tritoch/mythic-scraper
Mythic scraper
2017-06-16 21:35:03 -05:00
tritoch
96da921b98 Merge remote-tracking branch 'origin/master' 2017-06-16 15:35:25 -05:00
tritoch
74f6583dd5 Merge pull request #1 from Cockatrice/master
Synchronize with upstream
2017-06-16 15:35:10 -05:00
tritoch
45f55a8a28 Add mythicspoiler scraper 2017-06-16 15:32:45 -05:00
tritoch
3f976fe9d5 Merge pull request #8 from Cockatrice/multi-set
Multiple Set Support
2017-06-16 15:30:47 -05:00
tritoch
f7d1f54784 Multiple Set Support 2017-06-16 09:55:45 -05:00
tritoch
4c882f3597 Nicol Bolas, the Deceiver
http://mythicspoiler.com/hou/cards/nicolbolasthedeceiver.html

Planeswalker Deck Card. Loyalty Obscured currently.
2017-06-15 11:49:12 -05:00
tritoch
eb4ef2a0c2 Drop Old Masterpieces 2017-06-13 16:30:28 -05:00
22 changed files with 1007 additions and 1360 deletions

View File

@ -1,17 +1,25 @@
# Contributing to SpoilerSeason #
Thank you for your interest in contributing to SpoilerSeason!
This project is an attempt to create a central source for new Magic: the Gathering spoilers and provide data files for miscellaneous projects including our friends over at [Cockatrice](https://github.com/Cockatrice/Cockatrice)
# Contributing to Magic-Spoiler #
Thank you for your interest in contributing to Magic-Spoiler!<br>
This project is an attempt to create a central source for new Magic: the Gathering spoilers and provide data files for miscellaneous projects like [Cockatrice](https://github.com/Cockatrice/Cockatrice).
## How can I help? ##
SpoilerSeason grabs its data from many sources, but those sources often contain errors. If you just want to improve the card data and fix errors, you can start in the [errors.json](https://github.com/Cockatrice/Magic-Spoiler/blob/files/errors.json) file in the [files branch](https://github.com/Cockatrice/Magic-Spoiler/tree/files) or our [issue tracker.](https://github.com/Cockatrice/Magic-Spoiler/issues)
Once you've found an error, whether it be in the errors.json file or from using the data files, make sure that error hasn't already been fixed in the appropriate file on the [files branch.](https://github.com/Cockatrice/Magic-Spoiler/tree/files) If it's still present, let's get it fixed!
- If the error is with one of the fields in a card, check our [cards_corrections.json](https://github.com/Cockatrice/Magic-Spoiler/blob/master/cards_corrections.json) file. The syntax for this file is `"cardname": { "field to correct": "new value" }` If you're fixing the card name, you'd put the bad card name as `cardname`
- If the card shouldn't exist at all, check the [cards_delete.json](https://github.com/Cockatrice/Magic-Spoiler/blob/master/cards_delete.json) file. This file is just an array of cards to delete. Card name is case sensitive!
- If the card is a legitimate spoiler and it isn't showing up yet, you can manually add it. The file you want is [cards_manual.json](https://github.com/Cockatrice/Magic-Spoiler/blob/master/cards_manual.json) Make sure you link the spoil source in your Push Request.
Magic-Spoiler grabs its data from [Scryfall](https://scryfall.com/), but there can be errors of course.
If you want to improve the card data and fix errors for all users, you simply have to report them directly to Scryfall.
Once you've found a mistake in our data files, make sure that error hasn't already been fixed at the Scryfall webpage in betweeen. If it's still present there, let's get it fixed!
- If the error is with one of the fields in a card (e.g. a spelling error or missing cmc) search for that card on the Scryfall webpage. Below the card art on the left, there are some links. Choose the botton one (`Report card issue`) and provide the information in the form. Once their team check & fixes the errors, it'll show up in our spoiler files, too.<br>
It only takes a few days - be patient.
- If the card is a legitimate spoiler and it isn't showing up yet, you can request it by [contacting the Scryfall support](https://scryfall.com/contact) and let them know. Make sure to link the official spoiler source in your report.
- If the card shouldn't exist at all, let the Scryfall team know as well, please.
What you should **NOT** do however, is to submit PR's to our files branch and fix the xml files there directly.<br>
You have to provide updates to Scryfall as all other changes would get overridden again.
All Push requests for card fixes should have the name of the card being fixed and the type of fix (fix/correction, delete, or manual). In the details of the PR, you **MUST INCLUDE A VALID LINK** to the page the spoiler is located at. For minor fixes, a link to the card image is OK. And of course link the issue you're fixing if there is one!
## Anything else? ##
If you notice errors, please file an [issue](https://github.com/Cockatrice/Magic-Spoiler/issues)
If you notice any other errors or have suggestions to the code, please [file an issue](https://github.com/Cockatrice/Magic-Spoiler/issues) in our repository.
We try to follow [PEP8 Style Guide](https://peps.python.org/pep-0008/).
Code improvement PRs are always welcome!
<br>
**Code improvement PRs are always welcome!**

13
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,13 @@
# Configuration options: https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
# Enable version updates for GitHub Actions
- package-ecosystem: "github-actions"
# Directory must be set to "/" to check for workflow files in .github/workflows
directory: "/"
# Check for updates to GitHub Actions once a week
schedule:
interval: "weekly"
# Limit the amout of open PR's (default = 5, disabled = 0, security updates are not impacted)
open-pull-requests-limit: 2

73
.github/workflows/deploy.yml vendored Normal file
View File

@ -0,0 +1,73 @@
name: Deploy
on:
workflow_dispatch:
push:
branches:
- master
paths-ignore:
- '**.md'
pull_request:
branches:
- master
paths-ignore:
- '**.md'
schedule:
# Every 8 hours = 3 times a day
- cron: '0 */8 * * *'
jobs:
deploy:
# Do not run the scheduled workflow on forks
if: github.event_name != 'schedule' || github.repository_owner == 'Cockatrice'
name: Check for new spoiler
runs-on: ubuntu-latest
env:
DEPLOY: ${{github.ref == 'refs/heads/master'}}
OUTPUT_PATH: out
steps:
- name: Checkout repo
uses: actions/checkout@v6
- name: Checkout output branch
# Run only when triggered from master
if: env.DEPLOY == 'true'
uses: actions/checkout@v6
with:
ref: files
path: ${{env.OUTPUT_PATH}}
- name: Install requirements using pip
shell: bash
run: python3 -m pip install --requirement requirements.txt
- name: Run script
id: run
shell: bash
run: python3 -m magic_spoiler
- name: Upload artifacts
# Run only when triggered from a PR
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@v7
with:
name: spoiler-output
path: ${{github.workspace}}/${{env.OUTPUT_PATH}}
if-no-files-found: error
- name: Deploy changes
# Run only when triggered from master and changes are available
if: env.DEPLOY == 'true' && steps.run.outputs.deploy == 'true'
shell: bash
working-directory: ${{env.OUTPUT_PATH}}
run: |
git config user.name github-actions
git config user.email github-actions@github.com
git add "*.xml" SpoilerSeasonEnabled
git commit -m "Deploy: $GITHUB_SHA"
git push
deploy_commit=`git rev-parse HEAD`
echo "::notice title=New data uploaded::See deployment: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/commit/$deploy_commit"

40
.gitignore vendored
View File

@ -1,7 +1,6 @@
# Project specific
out/
AllSets.pre.json
deploy_key.enc
# Byte-compiled / optimized / DLL files
__pycache__/
@ -13,19 +12,19 @@ __pycache__/
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
env
build
develop-eggs
dist
downloads
eggs
.eggs
lib
lib64
parts
sdist
var
*.egg-info
.installed.cfg
*.egg
@ -84,8 +83,11 @@ celerybeat-schedule
.env
# virtualenv
venv/
ENV/
venv
ENV
bin
include
pyvenv.cfg
# Spyder project settings
.spyderproject
@ -95,3 +97,9 @@ ENV/
# JetBrains
.idea
#Mac Stuff
.DS_Store
*.sqlite
.*_cache

68
.pylintrc Normal file
View File

@ -0,0 +1,68 @@
[MASTER]
# Pickle collected data for later comparisons.
persistent=yes
[MESSAGES CONTROL]
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once).You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
disable=
bad-continuation,
fixme,
line-too-long,
localled-enabled,
locally-disabled,
logging-format-interpolation,
too-few-public-methods,
too-many-statements,
wrong-import-order,
too-many-branches,
import-error
[REPORTS]
# Set the output format. Available formats are text, parseable, colorized, json
# and msvs (visual studio).You can also give a reporter class, eg
# mypackage.mymodule.MyReporterClass.
output-format=colorized
[BASIC]
# Good variable names which should always be accepted, separated by a comma.
good-names=
f,
i,
j,
k,
_,
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=__.*__|test_.*
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=
FIXME,
XXX,
TODO,
[VARIABLES]
# A regular expression matching the name of dummy variables (i.e. expectedly
# not used).
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)

View File

@ -1,26 +0,0 @@
language: python
install:
- pip install -r requirements.txt
script: bash ./deploy.sh
notifications:
email: false
webhooks:
urls:
- https://webhooks.gitter.im/e/691b9acffe1def5f9d6b
on_success: always # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: always # options: [always|never|change] default: always
deploy:
provider: releases
api_key:
secure: Bin220gU9Re176T/1bIaX/rhGB+uEaw13aoB2/ir0ePHQB0ihasEJcsgmlN8kz93KSN6vp4y2HwMLnz3t7Pn0amTV8QVL/AlOOzjbq8m/1kYTbXdPlYnMsosZPVFLHRan4LEGFsovRia6LO4p9fqC8BDgQl89W/88PlYAMWzao5jTyKKHp8o+sioYhKj9D+86lxLYspQ+6SN0HOCnF2MZ/vZtxbY32ituswAc/sJK1MtZ/PExoMe1nSI2iKCaatXyKA+FVCUNLHRAu4LgB1GfJCLpmlPbvjud8A6WAKNF6poNCvFck+Ox56tt4bw3ggR5W9kTEhvX74l6AEeC7Qz6bHjh1CEngrqFjyaHy25CcygWgagf0DUsvyGRS0RqEx4bz9psD09d+oWihdkJMfa5kRzXtVQD8sxDgsBqEz/DjsMIlf/L5ISSa7lAYiqq65ELpezBFOlvEZ9avOYLcZc7m5/5ZhtcA4HPSqzfn2nhkPpeggBKufMdyc8JIDkvs/JlFsNu46QVvugjbdGvtb4SlQK310py0TOA6nYt7WntDhX3SukKAeh6oHjZaL5aeoSBhnlQRgJfDBqI3+7anLatD30uEKCMp5sWcLrjB1HO9ZH5nceWBg4cMKJvI/zT77h96fCy7uMkPNt867GP8O9KkWVWzxGBkpIdstigNWfT5g=
file_glob: true
file: out/*.xml
skip_cleanup: true
overwrite: true
on:
tags: true
env:
global:
- ENCRYPTION_LABEL: ec68c19ba263
- COMMIT_AUTHOR_EMAIL: you@example.com

View File

@ -1,23 +1,36 @@
# Magic-Spoiler [![Gitter Chat](https://img.shields.io/gitter/room/Cockatrice/Magic-Spoiler.svg)](https://gitter.im/Cockatrice/Magic-Spoiler) #
![](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2FCockatrice%2FMagic-Spoiler%2Ffiles%2Fspoiler.xml&query=%2F%2Finfo%2FcreatedAt&label=Last%20update)
Magic-Spoiler is a python script to scrape MTGS, Scryfall, and Wizards.com to compile a cockatrice-friendly XML file as well as json files.
[![](https://img.shields.io/badge/dynamic/xml.svg?label=Included%20sets&colorB=lightgrey&url=https%3A%2F%2Fraw.githubusercontent.com%2FCockatrice%2FMagic-Spoiler%2Ffiles%2Fspoiler.xml&query=count(%2F%2Flongname))](https://github.com/Cockatrice/Magic-Spoiler/tree/files) [![](https://img.shields.io/badge/dynamic/xml.svg?label=&colorB=lightgrey&url=https%3A%2F%2Fraw.githubusercontent.com%2FCockatrice%2FMagic-Spoiler%2Ffiles%2Fspoiler.xml&query=%2F%2Flongname)](https://github.com/Cockatrice/Magic-Spoiler/blob/files/spoiler.xml)<br>
[![](https://img.shields.io/badge/dynamic/xml.svg?label=Release%20dates&colorB=lightgrey&url=https%3A%2F%2Fraw.githubusercontent.com%2FCockatrice%2FMagic-Spoiler%2Ffiles%2Fspoiler.xml&query=%2F%2Freleasedate)](https://github.com/Cockatrice/Magic-Spoiler/blob/files/spoiler.xml)<br>
[![](https://img.shields.io/badge/dynamic/xml.svg?label=Included%20cards&colorB=lightgrey&url=https%3A%2F%2Fraw.githubusercontent.com%2FCockatrice%2FMagic-Spoiler%2Ffiles%2Fspoiler.xml&query=count(%2F%2Fcard))](https://github.com/Cockatrice/Magic-Spoiler/blob/files/spoiler.xml)
## Output [![Build Status](https://travis-ci.org/Cockatrice/Magic-Spoiler.svg?branch=master)](https://travis-ci.org/Cockatrice/Magic-Spoiler) ##
Just looking for XML or JSON files? [They're in our files branch!](https://github.com/Cockatrice/Magic-Spoiler/tree/files)
<br>
## Errors ##
Noticed an error? Check out our [Contributing file](https://github.com/Cockatrice/Magic-Spoiler/blob/master/.github/CONTRIBUTING.md) for information on how to help!
# Magic-Spoiler [![Discord](https://img.shields.io/discord/314987288398659595?label=Discord&logo=discord&logoColor=white)](https://discord.gg/3Z9yzmA) #
Magic-Spoiler is a Python script to query the [Scryfall](https://scryfall.com) API to compile XML files (Cockatrice formatted) with information about spoiled cards from upcoming sets.
## Output [![Build Status](https://github.com/Cockatrice/Magic-Spoiler/actions/workflows/deploy.yml/badge.svg?branch=master)](https://github.com/Cockatrice/Magic-Spoiler/actions/workflows/deploy.yml?query=branch%3Amaster) ##
>[!TIP]
>**Enable "Download Spoilers Automatically" in `Cockatrice → Settings → Card Sources → Spoilers` to get updates automatically pushed to your client!**<br>
You can also [add the desired <b>.xml</b> file(s) to your <i>customsets</i> folder manually](https://github.com/Cockatrice/Cockatrice/wiki/Custom-Cards-&-Sets#to-add-custom-sets-follow-these-steps) to make Cockatrice use them.
Just looking for XML files? [They are in our `files` branch!](https://github.com/Cockatrice/Magic-Spoiler/tree/files)
When run by our CI, the script automatically updates the files and uploads new versions to this branch. ([History of changes](https://github.com/Cockatrice/Magic-Spoiler/commits/files))<br>
GitHub Actions are scheduled to automatically run three times a day.
## Contributing ##
Noticed an error in the card data? Check out our [Contributing file](https://github.com/Cockatrice/Magic-Spoiler/blob/master/.github/CONTRIBUTING.md) for information on how to help fixing it!
We do happily accept PR's that improve our script as well!
## Running ##
### Requirements ###
* Python 2.7
* Python Modules:
requests==2.13.0
feedparser
lxml
Pillow
datetime
* Python 3.6
* several Python Modules (see [requirements.txt](https://github.com/Cockatrice/Magic-Spoiler/blob/master/requirements.txt))
```
pip install -r requirements.txt
@ -26,13 +39,14 @@ pip install -r requirements.txt
### Usage ###
```
$> python main.py
$> python -m magic_spoiler
```
Outputs out/{SETCODE}.xml, out/MPS\_{SETCODE}.xml, out/{SETCODE}.json, out/{MPS\_{SETCODE}.json
### Output ###
errors are logged to out/errors.json
All spoiler files are written to the `out/` directory:
Add the set xml file to your `customsets` folder for Cockatrice.
When run by travis, uploads all files to [files branch](https://github.com/Cockatrice/Magic-Spoiler/tree/files)
| File Name | Content |
|:--|:--|
| `spoiler.xml` | file contains **all** currently available spoilers from different **sets** |
| `{SET_CODE}.xml` | files contain just the spoiler available for this **single set** |

View File

@ -1,2 +0,0 @@
{
}

View File

@ -1 +0,0 @@
[]

View File

@ -1,95 +0,0 @@
{
"meta": {
"instructions": "If you would like to add a card manually, use the 'blank card' template below.",
"instructions2": "Check example_card_details for more information",
"instructions3": "add the card to the 'cards' array below meta",
"example card (do not include this key, just the object)": {
"cmc": 7,
"colorIdentity": [
"W",
"B",
"G"
],
"colors": [
"White",
"Black"
],
"layout": "normal",
"manaCost": "{X}{5}{W}{B}",
"name": "Example Card",
"number": "55",
"power": "*",
"rarity": "Mythic Rare",
"subtypes": [
"Zombie"
],
"text": "Shadow, Flying, Horsemanship\nExample Card's power is equal to something.\n{G}, {T}: Unfloop target pig you control.",
"toughness": "2",
"type": "Legendary Creature - Zombie",
"types": [
"Creature"
]
},
"example card details":{
"meta": {
"values": "All fields (including loyalty) are string or array of strings except CMC which is int",
"required fields (all cards)": [
"name",
"number",
"rarity",
"type",
"url"
]
}
},
"blank card (do not use this key, just the object) - remove unneeded keys":
{
"name": "",
"manaCost": "",
"number": "",
"rarity": "",
"type": "",
"url": "",
"text": "",
"loyalty": "",
"cmc": 0,
"layout": "",
"power": "",
"toughness": ""
}
},
"cards": [
{
"name": "Nicol Bolas, God-Pharaoh",
"manaCost": "4UBR",
"number": "140",
"rarity": "Mythic Rare",
"type": "Planeswalker - Bolas",
"url": "https://i.imgur.com/DTNXG2z.png",
"text": "+2: Target opponent exiles cards from the top of his or her library until he or she exiles a nonland card. Until end of turn, you may cast that card without paying its mana cost.\n+1: Each opponent exiles two cards from his or her hand.\n-4: Nicol Bolas, God-Pharaoh deals 7 damage to target opponent or creature an opponent controls.\n-12: Exile each nonland permanent your opponents control.",
"loyalty": "7",
"cmc": 7
},
{
"name": "Bontu's Last Reckoning",
"manaCost": "1BB",
"number": "60",
"rarity": "Rare",
"type": "Sorcery",
"url": "https://i.imgur.com/HOjh3pE.png",
"text": "Destroy all creatures. Lands you control don't untap during your next untap step.",
"cmc": 3
},
{
"name": "Samut, the Tested",
"manaCost": "2RG",
"number": "144",
"rarity": "Mythic Rare",
"type": "Planeswalker - Samut",
"url": "https://i.imgur.com/4tRYs5z.png",
"text": "+1: Up to one target creature gains double strike until end of turn.\n-2: Samut, the Tested deals 2 damage divided as you choose among one or two target creatures and/or players.\n-7: Search your library for up to two creature and/or planeswalker cards, put them onto the battlefield, then shuffle your library.",
"loyalty": "4",
"cmc": 4
}
]
}

View File

@ -1,76 +0,0 @@
#!/bin/bash
set -e # Exit with nonzero exit code if anything fails
SOURCE_BRANCH="deploy-to-SpoilerSeasonFiles"
TARGET_BRANCH="files"
function doCompile {
python main.py
}
# Pull requests and commits to other branches shouldn't try to deploy, just build to verify
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
echo "Skipping deploy; just doing a build."
doCompile
exit 0
fi
# Save some useful information
REPO=`git config remote.origin.url`
SSH_REPO=${REPO/https:\/\/github.com\//git@github.com:}
SHA=`git rev-parse --verify HEAD`
# Clone the existing gh-pages for this repo into out/
# Create a new empty branch if gh-pages doesn't exist yet (should only happen on first deply)
git clone $REPO out
cd out
git checkout $TARGET_BRANCH || git checkout --orphan $TARGET_BRANCH
cd ..
# Clean out existing contents
rm -rf out/**/* || exit 0
# Run our compile script
doCompile
echo TRAVIS_PULL_REQUEST ${TRAVIS_PULL_REQUEST}
echo TRAVIS_SECURE_ENV_VARS ${TRAVIS_SECURE_ENV_VARS}
echo TRAVIS_EVENT_TYPE ${TRAVIS_EVENT_TYPE}
# Don't push to our branch for PRs.
#if [ "${ghToken:-false}" != "false" ]; then
# doCompile
#else
# doCompile
# exit 0
#fi
# Now let's go have some fun with the cloned repo
cd out
ls
git config user.name "Travis CI"
git config user.email "$COMMIT_AUTHOR_EMAIL"
# If there are no changes to the compiled out (e.g. this is a README update) then just bail.
#if git diff --quiet; then
# echo "No changes to the output on this push; exiting."
# exit 0
#fi
# Commit the "changes", i.e. the new version.
# The delta will show diffs between new and old versions.
git add -A .
git commit --allow-empty -m "Deploy to GitHub: ${SHA}"
# Get the deploy key by using Travis's stored variables to decrypt deploy_key.enc
ENCRYPTED_KEY_VAR="encrypted_${ENCRYPTION_LABEL}_key"
ENCRYPTED_IV_VAR="encrypted_${ENCRYPTION_LABEL}_iv"
ENCRYPTED_KEY=${!ENCRYPTED_KEY_VAR}
ENCRYPTED_IV=${!ENCRYPTED_IV_VAR}
openssl aes-256-cbc -K $ENCRYPTED_KEY -iv $ENCRYPTED_IV -in ../deploy_key.enc -out ../deploy_key -d
chmod 600 ../deploy_key
eval `ssh-agent -s`
ssh-add ../deploy_key
# Now that we're all set up, we can push.
git push $SSH_REPO $TARGET_BRANCH

Binary file not shown.

View File

@ -0,0 +1 @@
"""Magic Spoiler Program"""

669
magic_spoiler/__main__.py Normal file
View File

@ -0,0 +1,669 @@
"""
Handle Scryfall Spoilers
"""
import contextvars
import datetime
import hashlib
import json
import os
import pathlib
import shutil
import time
from enum import Enum
from typing import IO, Any, Dict, List, Tuple, Union
import requests
import requests_cache
from lxml import etree
SCRYFALL_SET_URL: str = "https://api.scryfall.com/sets/{}"
SESSION: contextvars.ContextVar = contextvars.ContextVar("SESSION_SCRYFALL")
SPOILER_SETS: contextvars.ContextVar = contextvars.ContextVar("SPOILER_SETS")
SPOILER_MARK = "~"
OUTPUT_DIR = pathlib.Path("out")
OUTPUT_TMP_DIR = OUTPUT_DIR.joinpath("tmp")
XML_ESCAPE_TRANSLATE_MAP = str.maketrans(
{"&": "&amp;", '"': "&quot;", "<": "&lt;", ">": "&gt;"}
)
# remove any control characters outright
XML_ESCAPE_TRANSLATE_MAP.update({i: "" for i in range(ord(" "))})
# don't remove whitespace characters in the sub " " range
del XML_ESCAPE_TRANSLATE_MAP[ord("\n")]
del XML_ESCAPE_TRANSLATE_MAP[ord("\t")]
# copied from Cockatrice/oracle/src/oracleimporter.h OracleImporter::mainCardTypes
MAINTYPES = (
"Planeswalker",
"Creature",
"Land",
"Sorcery",
"Instant",
"Artifact",
"Enchantment"
)
class Priority(Enum):
FALLBACK = 0
PRIMARY = 10
SECONDARY = 20
REPRINT = 30
OTHER = 40
SET_TYPE_PRIORITY_MAP = {
"core": Priority.PRIMARY,
"expansion": Priority.PRIMARY,
"commander": Priority.SECONDARY,
"starter": Priority.SECONDARY,
"draft_innovation": Priority.SECONDARY,
"duel_deck": Priority.SECONDARY,
"archenemy": Priority.REPRINT,
"arsenal": Priority.REPRINT,
"box": Priority.REPRINT,
"from_the_vault": Priority.REPRINT,
"masterpiece": Priority.REPRINT,
"masters": Priority.REPRINT,
"memorabilia": Priority.REPRINT,
"planechase": Priority.REPRINT,
"premium_deck": Priority.REPRINT,
"promo": Priority.REPRINT,
"spellbook": Priority.REPRINT,
"token": Priority.REPRINT,
"treasure_chest": Priority.REPRINT,
"alchemy": Priority.OTHER,
"funny": Priority.OTHER,
"minigame": Priority.OTHER,
"vanguard": Priority.OTHER,
}
def __get_session() -> Union[requests.Session, Any]:
"""
Get the session for downloading content
:return: Session
"""
requests_cache.install_cache(
cache_name="scryfall_cache", backend="sqlite", expire_after=7200 # 2 hours
)
if not SESSION.get(None):
SESSION.set(requests.Session())
return SESSION.get()
def json_download(scryfall_url: str) -> Dict[str, Any]:
"""
Get the data from Scryfall in JSON format using our secret keys
:param scryfall_url: URL to json_download JSON data from
:return: JSON object of the Scryfall data
"""
session = __get_session()
response: Any = session.get(url=scryfall_url, timeout=10.0)
request_api_json: Dict[str, Any] = response.json()
print("Downloaded: {} (Cache = {})".format(scryfall_url, response.from_cache))
return request_api_json
def download_scryfall_set(set_code: str) -> List[Dict[str, Any]]:
"""
Download a set from scryfall in entirety
:param set_code: Set code
:return: Card list
"""
set_content: Dict[str, Any] = json_download(SCRYFALL_SET_URL.format(set_code))
if set_content["object"] == "error":
print("API download failed for {}: {}".format(set_code, set_content))
return []
spoiler_cards = []
download_url = set_content["search_uri"]
page_downloaded: int = 1
while download_url:
page_downloaded += 1
cards = json_download(download_url)
if cards["object"] == "error":
print("Set {} has no cards, skipping".format(set_code))
break
for card in cards["data"]:
spoiler_cards.append(card)
if not cards.get("has_more"):
break
download_url = cards["next_page"]
return sorted(spoiler_cards, key=lambda c: (c["name"], c["collector_number"]))
def build_types(sf_card: Dict[str, Any]) -> Tuple[List[str], str, List[str]]:
"""
Build the super, type, and sub-types of a given card
:param sf_card: Scryfall card
:return: Tuple of types
"""
all_super_types = ["Legendary", "Snow", "Elite", "Basic", "World", "Ongoing"]
# return values
super_types: List[str] = []
sub_types: List[str] = []
# Spoiler cards do not always include a type_line
type_line = sf_card.get("type_line", "")
if not type_line:
type_line = "Unknown"
if "" in type_line:
card_subs = type_line.split("")[1].strip()
sub_types = card_subs.split(" ") if " " in card_subs else [card_subs]
for card_type in all_super_types:
if card_type in type_line:
super_types.append(card_type)
types: str = type_line.split("")[0]
for card_type in all_super_types:
types = types.replace(card_type, "")
return super_types, types, sub_types
def scryfall2mtgjson(scryfall_cards: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""
Convert SF cards to MTGJSON v4 format for dispatching
:param scryfall_cards: List of Scryfall cards
:return: MTGJSON card list
"""
trice_cards = []
composed_sf_cards = []
# Handle split/transform cards
for sf_card in scryfall_cards:
if "layout" in sf_card.keys():
if sf_card["layout"] in ["transform", "split"]:
# Make a copy for zoning
combined_sides = sf_card.copy()
del combined_sides["card_faces"]
# Quick pointers
face_0 = sf_card["card_faces"][0]
face_1 = sf_card["card_faces"][1]
# Update data for the combined
combined_sides["layout"] = "double-faced"
combined_sides["names"] = [face_0["name"], face_1["name"]]
# Re-structure two cards into singletons
front_side = {**combined_sides, **face_0}
back_side = {**combined_sides, **face_1}
# Uniquify them
front_side["collector_number"] += "a"
back_side["collector_number"] += "b"
# And continue on our journey
composed_sf_cards.extend([front_side, back_side])
else:
composed_sf_cards.append(sf_card)
# Build trice cards from SF cards
for sf_card in composed_sf_cards:
super_types, types, sub_types = build_types(sf_card)
if "card_faces" in sf_card:
image = (
sf_card["card_faces"][0]
.get("image_uris", {})
.get("normal", "")
)
else:
image = sf_card.get("image_uris", {}).get("normal", "")
try:
trice_card = {
"cmc": sf_card["cmc"],
"names": sf_card.get("names", None),
"mana_cost": sf_card.get("mana_cost", ""),
"name": sf_card["name"],
"number": sf_card["collector_number"],
"rarity": sf_card["rarity"].replace("mythic", "mythic rare").title(),
"text": sf_card.get("oracle_text", ""),
"url": image,
"type": sf_card.get("type_line", "Unknown"),
"colorIdentity": sf_card.get("color_identity", None),
"colors": sf_card.get("colors", []),
"power": sf_card.get("power", None),
"toughness": sf_card.get("toughness", None),
"layout": sf_card["layout"].replace("normal", ""),
"loyalty": sf_card.get("loyalty", None),
"artist": sf_card.get("artist", ""),
"flavor": sf_card.get("flavor_text", None),
"multiverseId": sf_card.get("multiverse_id", None),
"superTypes": super_types,
"types": types,
"subTypes": sub_types,
}
trice_cards.append(trice_card)
except Exception as e:
# If running in GitHub Actions CI, print the message as a warning
if 'GITHUB_ACTION' in os.environ:
print(f'::warning::Unable to parse "{sf_card.get("name")}" ({sf_card.get("set").upper()}): {str(e)}')
else:
print(f'Unable to parse "{sf_card.get("name")}" ({sf_card.get("set").upper()}): {str(e)}')
return trice_cards
def open_header(card_xml_file: IO[Any], filename: str) -> None:
"""
Add the header data to the XML file
:param card_xml_file: Card file path
"""
card_xml_file.write(
"<cockatrice_carddatabase version='4' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='https://raw.githubusercontent.com/Cockatrice/Cockatrice/master/doc/carddatabase_v4/cards.xsd'>\n"
+ " <!--\n"
+ " Created At: " + datetime.datetime.now(datetime.timezone.utc).strftime("%a, %b %d %Y, %H:%M:%S") + " (UTC)\n"
+ " \n"
+ " THIS FILE IS AUTOMATICALLY GENERATED & ALL EDITS WILL BE OVERRIDDEN.\n"
+ " -->\n"
+ "<info>\n"
+ " <author>Cockatrice/Magic-Spoiler</author>\n"
+ " <createdAt>" + datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%d %H:%M:%S") + " (UTC)</createdAt>\n"
+ " <sourceUrl>https://raw.githubusercontent.com/Cockatrice/Magic-Spoiler/files/" + filename + "</sourceUrl>\n"
+ " <sourceVersion></sourceVersion>\n"
+ "</info>\n"
+ "<sets>\n"
)
def fill_header_sets(card_xml_file: IO[Any], set_obj: Dict[str, str]) -> None:
"""
Add header data for set files
:param card_xml_file: Card file path
:param set_obj: Set object
"""
priority = SET_TYPE_PRIORITY_MAP.get(set_obj["set_type"].lower(), Priority.FALLBACK)
card_xml_file.write(
"<set>\n"
"<name>" + set_obj["code"] + "</name>\n"
"<longname>" + set_obj["name"] + " (Spoiler)</longname>\n"
"<settype>" + set_obj["set_type"].replace("_", " ").title() + "</settype>\n"
"<releasedate>" + set_obj["released_at"] + "</releasedate>\n"
"<priority>" + str(priority.value) + "</priority>\n"
"</set>\n"
)
def close_header(card_xml_file: IO[Any]) -> None:
"""
Add closing data to files
:param card_xml_file: Card file path
"""
card_xml_file.write("</sets>\n<cards>\n")
def close_xml_file(card_xml_file: IO[Any]) -> None:
"""
Add final touch to files to validate them,
then pretty them
:param card_xml_file: Card file path
"""
card_xml_file.write("</cards>\n</cockatrice_carddatabase>\n")
card_xml_file.close()
# Make the files pretty and add xml declaration
parser = etree.XMLParser(remove_blank_text=True)
root = etree.parse(card_xml_file.name, parser).getroot()
with pathlib.Path(card_xml_file.name).open("wb") as f:
f.write(etree.tostring(root, encoding="UTF-8", xml_declaration=True, pretty_print=True))
def xml_escape(text):
return text.translate(XML_ESCAPE_TRANSLATE_MAP)
def write_cards(
card_xml_file: Any, trice_dict: List[Dict[str, Any]], set_code: str
) -> None:
"""
Given a list of cards, write the cards to an output file
:param card_xml_file: Output file to write to
:param trice_dict: List of cards
:param set_code: Set code
"""
for card in trice_dict:
if "names" in card.keys() and card["names"]:
if "layout" in card and card["layout"] != "double-faced":
if card["name"] == card["names"][1]:
continue
set_name = card["name"]
if "mana_cost" in card.keys():
mana_cost = card["mana_cost"].replace("{", "").replace("}", "")
else:
mana_cost = ""
if "power" in card.keys() or "toughness" in card.keys():
if card["power"]:
pow_tough = str(card["power"]) + "/" + str(card["toughness"])
else:
pow_tough = ""
else:
pow_tough = ""
if "loyalty" in card.keys() and card["loyalty"]:
loyalty = str(card["loyalty"])
else:
loyalty = ""
if "text" in card.keys():
text = card["text"]
else:
text = ""
card_cmc = str(card["cmc"])
if card_cmc.endswith(".0"):
card_cmc = card_cmc[:-2]
card_type = card["type"]
table_row = "1"
if "Land" in card_type:
table_row = "0"
elif "Sorcery" in card_type:
table_row = "3"
elif "Instant" in card_type:
table_row = "3"
elif "Creature" in card_type:
table_row = "2"
for maintype in MAINTYPES:
if maintype in card_type:
break
else:
maintype = None
if "names" in card.keys():
if "layout" in card:
if card["layout"] == "split" or card["layout"] == "aftermath":
if "names" in card:
if card["name"] == card["names"][0]:
for json_card in trice_dict:
if json_card["name"] == card["names"][1]:
card_type += " // " + json_card["type"]
new_mc = ""
if "mana_cost" in json_card:
new_mc = json_card["mana_cost"]
mana_cost += " // " + new_mc.replace(
"{", ""
).replace("}", "")
card_cmc += " // " + str(json_card["cmc"])
text += "\n---\n" + json_card["text"]
set_name += " // " + json_card["name"]
elif card["layout"] == "double-faced":
if "names" not in card.keys():
print(card["name"] + ' is double-faced but no "names" key')
else:
pass
else:
print(card["name"] + " has multiple names and no 'layout' key")
if "number" in card:
if "b" in str(card["number"]):
if "layout" in card:
if card["layout"] == "split" or card["layout"] == "aftermath":
continue
set_name, mana_cost, card_cmc, card_type, pow_tough, table_row, text, loyalty = map(
xml_escape,
[set_name, mana_cost, card_cmc, card_type, pow_tough, table_row, text, loyalty],
)
card_xml_file.write("<card>\n")
card_xml_file.write("<name>" + set_name + "</name>\n")
card_xml_file.write("<text>" + text + "</text>\n")
card_xml_file.write("<prop>\n")
if "colors" in card.keys() and card["colors"]:
card_xml_file.write("<colors>" + "".join(card["colors"]) + "</colors>\n")
card_xml_file.write("<type>" + card_type + "</type>\n")
if maintype:
card_xml_file.write("<maintype>" + maintype + "</maintype>\n")
card_xml_file.write("<cmc>" + card_cmc + "</cmc>\n")
if mana_cost:
card_xml_file.write("<manacost>" + mana_cost + "</manacost>\n")
if pow_tough:
card_xml_file.write("<pt>" + pow_tough + "</pt>\n")
if loyalty:
card_xml_file.write("<loyalty>" + loyalty + "</loyalty>\n")
card_xml_file.write("</prop>\n")
card_xml_file.write(
'<set rarity="'
+ str(card["rarity"])
+ '" picURL="'
+ str(card["url"])
+ '">'
+ str(set_code)
+ "</set>\n"
)
if set_name + " enters the battlefield tapped" in text:
card_xml_file.write("<cipt>1</cipt>\n")
card_xml_file.write("<tablerow>" + table_row + "</tablerow>\n")
card_xml_file.write("</card>\n")
def write_spoilers_xml(trice_dicts: Dict[str, List[Dict[str, Any]]]) -> bool:
"""
Write the spoiler.xml file
:param trice_dicts: Dict of dict entries
:return: Written successfully
"""
output_file_name = "spoiler.xml"
pathlib.Path("out").mkdir(parents=True, exist_ok=True)
card_xml_file = OUTPUT_TMP_DIR.joinpath(output_file_name).open("w", encoding="utf-8")
# Fill in set headers
open_header(card_xml_file, output_file_name)
for value in SPOILER_SETS.get():
fill_header_sets(card_xml_file, {key: (value_ + SPOILER_MARK if key == "code" else value_) for key, value_ in value.items()})
close_header(card_xml_file)
# Write in all the cards
for value in SPOILER_SETS.get():
try:
write_cards(card_xml_file, trice_dicts[value["code"]], value["code"] + SPOILER_MARK)
except KeyError:
print("Skipping " + value["code"])
close_xml_file(card_xml_file)
old_xml_location = str(OUTPUT_DIR.joinpath(output_file_name))
if compare_xml_content(card_xml_file.name, old_xml_location):
print("No new data in spoiler.xml, skipping replacement")
return False
# Move new version to old location
print("Changes detected, replacing spoiler.xml with updated version")
shutil.move(card_xml_file.name, old_xml_location)
return True
def compare_xml_content(a: str, b: str) -> bool:
"""
Compare the contents of two XML files and report
if the contents are the same, minus the info part and comments
:param a: File a
:param b: File b
:return: Is file content, minus info and comments, the same?
"""
files = [pathlib.Path(file_n) for file_n in (a, b)]
if all([filepath.is_file() for filepath in files]):
hashes = []
for filepath in files:
parser = etree.XMLParser(remove_blank_text=True)
root = etree.parse(str(filepath), parser).getroot()
etree.strip_elements(root, "info", etree.Comment)
digest = hashlib.sha512(etree.tostring(root)).hexdigest()
hashes.append(digest)
return hashes[0] == hashes[1]
return False
def write_set_xml(trice_dict: List[Dict[str, Any]], set_obj: Dict[str, str]) -> bool:
"""
Write out a single magic set to XML format
:param trice_dict: Cards to print
:param set_obj: Set object
:return: Written successfully
"""
if not trice_dict:
return False
OUTPUT_TMP_DIR.mkdir(parents=True, exist_ok=True)
set_code = set_obj["code"]
file_path = OUTPUT_TMP_DIR.joinpath(f"{set_code}.xml")
card_xml_file = file_path.open("w", encoding="utf-8")
open_header(card_xml_file, file_path.name)
fill_header_sets(card_xml_file, set_obj)
close_header(card_xml_file)
write_cards(card_xml_file, trice_dict, set_obj["code"])
close_xml_file(card_xml_file)
# If content didn't change, discard newest creation
old_xml_location = str(OUTPUT_DIR.joinpath("{}.xml".format(set_obj["code"])))
if compare_xml_content(card_xml_file.name, old_xml_location):
print("No new data in {}.xml, skipping replacement".format(set_obj["code"]))
return False
# Move new version to old location
print(
"Changes detected, replacing {}.xml with updated version".format(
set_obj["code"]
)
)
shutil.move(card_xml_file.name, old_xml_location)
return True
def get_spoiler_sets() -> List[Dict[str, str]]:
"""
Download Sf sets and mark spoiler sets
:return: Spoiler sets
"""
sf_sets = json_download(SCRYFALL_SET_URL.format(""))
if sf_sets["object"] == "error":
print("Unable to download SF correctly: {}".format(sf_sets))
return []
spoiler_sets = []
# Find list of possible Set Types to exclude here: https://scryfall.com/docs/api/sets
excluded_set_types = ["alchemy", "masterpiece", "arsenal", "from_the_vault", "spellbook", "premium_deck", "duel_deck",
"draft_innovation", "treasure_chest", "planechase", "archenemy", "vanguard", "box", "promo",
"token", "memorabilia", "minigame"]
for sf_set in sf_sets["data"]:
if (
sf_set["released_at"] >= time.strftime("%Y-%m-%d %H:%M:%S")
and sf_set["set_type"] not in excluded_set_types
and sf_set["card_count"]
):
sf_set["code"] = sf_set["code"].upper()
spoiler_sets.append(sf_set)
return spoiler_sets
def delete_old_files() -> bool:
"""
Delete files that are no longer necessary within the program
:return: Files were deleted
"""
valid_files = [x["code"].upper() for x in SPOILER_SETS.get()] + [
"spoiler",
"SpoilerSeasonEnabled",
"README",
]
deleted = False
for output_file in OUTPUT_DIR.glob("*"):
if not output_file.is_file():
continue
if output_file.stem not in valid_files:
output_file.unlink()
deleted = True
if OUTPUT_TMP_DIR.is_dir():
shutil.rmtree(OUTPUT_TMP_DIR)
enabled_path = OUTPUT_DIR.joinpath("SpoilerSeasonEnabled")
if not SPOILER_SETS.get():
enabled_path.unlink(missing_ok=True)
else:
enabled_path.open("w", encoding="utf-8").write(" ")
return deleted
def main() -> None:
"""
Main dispatch thread
"""
# Determine what sets have spoiler data
SPOILER_SETS.set(get_spoiler_sets())
spoiler_xml = {}
changed = False
for set_info in SPOILER_SETS.get():
print("Handling {}".format(set_info["code"]))
cards = download_scryfall_set(set_info["code"])
trice_dict = scryfall2mtgjson(cards)
# Write SET.xml
changed |= write_set_xml(trice_dict, set_info)
# Save for spoiler.xml
spoiler_xml[set_info["code"]] = trice_dict
if spoiler_xml:
# Write out the spoiler.xml file
changed |= write_spoilers_xml(spoiler_xml)
# Cleanup outdated stuff that's not necessary
changed |= delete_old_files()
# Enable deployment on changes (used in CI)
try:
github_output = os.environ["GITHUB_OUTPUT"]
except KeyError:
print(f"not in ci but deploy={str(changed).lower()}")
else:
with open(github_output, "a") as fp:
print(f"deploy={str(changed).lower()}", file=fp)
if not changed:
print("::notice title=No updates available::"
"No new spoiler cards found for deployment")
if __name__ == "__main__":
main()

101
main.py
View File

@ -1,101 +0,0 @@
# -*- coding: utf-8 -*-
import spoilers
import sys
import os
import shutil
#import configparser
import json
#import urllib
presets = {
"isfullspoil": False, #when full spoil comes around, we only want to use WOTC images
"includeMasterpieces": True, #if the set has masterpieces, let's get those too
"oldRSS": False #maybe MTGS hasn't updated their spoiler.rss but new cards have leaked
}
with open('set_info.json') as data_file:
setinfos = json.load(data_file)
with open('cards_manual.json') as data_file:
manual_cards = json.load(data_file)
manual_cards = manual_cards['cards']
with open('cards_corrections.json') as data_file:
card_corrections = json.load(data_file)
with open('cards_delete.json') as data_file:
delete_cards = json.load(data_file)
errorlog = []
#TODO insert configparser to add config.ini file
for argument in sys.argv:
#we can modify any of the variables from the set infos file or the presets above at runtime
#works only for first-level variables currently (editing masterpieces
#syntax is variable="new value"
for setinfo in setinfos:
if setinfo in argument.split("=")[0]:
setinfos[setinfo] = argument.split("=")[1]
for preset in presets:
if preset in argument.split("=")[0]:
presets[preset] = argument.split("=")[1]
def save_allsets(AllSets):
#TODO Create AllSets.json for Oracle
print "Saving AllSets"
def save_masterpieces(masterpieces):
with open('out/' + setinfos['masterpieces']['setname'] + '.json', 'w') as outfile:
json.dump(masterpieces, outfile, sort_keys=True, indent=2, separators=(',', ': '))
def save_setjson(mtgs):
with open('out/' + setinfos['setname'] + '.json', 'w') as outfile:
json.dump(mtgs, outfile, sort_keys=True, indent=2, separators=(',', ': '))
def save_errorlog(errorlog):
fixederrors = []
unfixederrors = []
for error in errorlog:
if 'fixed' in error:
fixederrors.append(error)
else:
unfixederrors.append(error)
errorlog = {"unfixed": unfixederrors, "fixed": fixederrors}
with open('out/errors.json', 'w') as outfile:
json.dump(errorlog, outfile, sort_keys=True, indent=2, separators=(',', ': '))
def save_xml(xmlstring, outfile):
with open(outfile,'w+') as xmlfile:
xmlfile.write(xmlstring.encode('utf-8'))
if __name__ == '__main__':
AllSets = spoilers.get_allsets() #get AllSets from mtgjson
if presets['oldRSS']:
mtgs = {"cards":[]}
else:
mtgs = spoilers.scrape_mtgs('http://www.mtgsalvation.com/spoilers.rss') #scrape mtgs rss feed
mtgs = spoilers.parse_mtgs(mtgs) #parse spoilers into mtgjson format
mtgs = spoilers.correct_cards(mtgs, manual_cards, card_corrections, delete_cards) #fix using the fixfiles
scryfall = spoilers.get_scryfall('https://api.scryfall.com/cards/search?q=++e:' + setinfos['setname'].lower())
mtgs = spoilers.get_image_urls(mtgs, presets['isfullspoil'], setinfos['setname'], setinfos['setlongname'], setinfos['setsize']) #get images
mtgjson = spoilers.smash_mtgs_scryfall(mtgs, scryfall)
[mtgjson, errors] = spoilers.errorcheck(mtgjson) #check for errors where possible
errorlog += errors
spoilers.write_xml(mtgjson, setinfos['setname'], setinfos['setlongname'], setinfos['setreleasedate'])
save_xml(spoilers.pretty_xml(setinfos['setname']), 'out/spoiler.xml')
mtgs = spoilers.add_headers(mtgjson, setinfos)
AllSets = spoilers.make_allsets(AllSets, mtgjson, setinfos['setname'])
if 'masterpieces' in setinfos: #repeat all of the above for masterpieces
#masterpieces aren't in the rss feed, so for the new cards, we'll go to their individual pages on mtgs
#old cards will get their infos copied from mtgjson (including fields that may not apply like 'artist')
#the images will still come from mtgs
masterpieces = spoilers.make_masterpieces(setinfos['masterpieces'], AllSets, mtgjson)
[masterpieces, errors] = spoilers.errorcheck(masterpieces)
errorlog += errors
spoilers.write_xml(masterpieces, setinfos['masterpieces']['setname'], setinfos['masterpieces']['setlongname'], setinfos['masterpieces']['setreleasedate'])
AllSets = spoilers.make_allsets(AllSets, masterpieces, setinfos['masterpieces']['setname'])
save_masterpieces(masterpieces)
save_errorlog(errorlog)
save_allsets(AllSets)
save_setjson(mtgjson)

18
mypy.ini Normal file
View File

@ -0,0 +1,18 @@
[mypy]
python_version = 3.7
check_untyped_defs = True
disallow_untyped_calls = True
disallow_untyped_defs = True
disallow_subclassing_any = True
follow_imports = normal
incremental = True
ignore_missing_imports = True
strict_optional = True
warn_no_return = True
warn_redundant_casts = True
warn_return_any = True
warn_unused_ignores = True
[mypy-pkg/generated_code/*]
ignore_errors = True

View File

@ -1,5 +1,3 @@
requests==2.13.0
feedparser
lxml
Pillow
datetime
requests
requests_cache

7
requirements_test.txt Normal file
View File

@ -0,0 +1,7 @@
black
isort
mypy
pylint
pytest
pytest-cov
tox

View File

@ -1,18 +0,0 @@
{
"setname": "HOU",
"setlongname": "Hour of Devastation",
"blockname": "Amonkhet",
"setsize": 199,
"setreleasedate": "2017-07-14",
"settype": "expansion",
"masterpieces": {
"setname": "MPS_AKH",
"setlongname": "Masterpiece Series: Amonkhet Invocations",
"setreleasedate": "2017-04-28",
"alternativeNames": ["Amonkhet Invocations"],
"galleryURL": "http://magic.wizards.com/en/articles/archive/card-preview/masterpiece-series-amonkhet-invocations-2017-03-29",
"additionalCardNames": [],
"mtgsurl": "http://www.mtgsalvation.com/spoilers/181-amonkhet-invocations",
"mtgscardpath": "http://www.mtgsalvation.com/cards/amonkhet-invocations/"
}
}

24
setup.py Normal file
View File

@ -0,0 +1,24 @@
"""Installation setup for Magic-Spoiler."""
import setuptools
# Necessary for TOX
setuptools.setup(
name="Magic-Spoiler",
version="0.1.0",
author="Zach Halpern",
author_email="zach@cockatrice.us",
url="https://github.com/Cockatrice/Magic-Spoiler/",
description="Build XML files for distribution of MTG spoiler cards",
long_description=open("README.md", "r").read(),
long_description_content_type="text/markdown",
license="GPL-3.0",
classifiers=[
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
],
keywords="Magic: The Gathering, MTG, XML, Card Games, Collectible, Trading Cards",
packages=setuptools.find_packages(),
)

View File

@ -1,991 +0,0 @@
# -*- coding: utf-8 -*-
import requests
import feedparser
import re
import sys
import os
import shutil
import time
from lxml import html, etree
from PIL import Image
import datetime
import urllib
import json
import xml.dom.minidom
def scrape_mtgs(url):
return requests.get(url, headers={'Cache-Control':'no-cache', 'Pragma':'no-cache', 'Expires': 'Thu, 01 Jan 1970 00:00:00 GMT'}).text
def parse_mtgs(mtgs, manual_cards=[], card_corrections=[], delete_cards=[], split_cards=[], related_cards=[]):
mtgs = mtgs.replace('utf-16','utf-8')
patterns = ['<b>Name:</b> <b>(?P<name>.*?)<',
'Cost: (?P<cost>\d{0,2}[WUBRGC]*?)<',
'Type: (?P<type>.*?)<',
'Pow/Tgh: (?P<pow>.*?)<',
'Rules Text: (?P<rules>.*?)<br /',
'Rarity: (?P<rarity>.*?)<',
'Set Number: #(?P<setnumber>.*?)/'
]
d = feedparser.parse(mtgs)
cards = []
for entry in d.items()[5][1]:
card = dict(cost='',cmc='',img='',pow='',name='',rules='',type='',
color='', altname='', colorIdentity='', colorArray=[], colorIdentityArray=[], setnumber='', rarity='')
summary = entry['summary']
for pattern in patterns:
match = re.search(pattern, summary, re.MULTILINE|re.DOTALL)
if match:
dg = match.groupdict()
card[dg.items()[0][0]] = dg.items()[0][1]
cards.append(card)
#if we didn't find any cards, let's bail out to prevent overwriting good data
count = 0
for card in cards:
count = count + 1
if count < 1:
sys.exit("No cards found, exiting to prevent file overwrite")
#for manual_card in manual_cards:
#initialize some keys
#manual_card['colorArray'] = []
#manual_card['colorIdentityArray'] = []
#manual_card['color'] = ''
#manual_card['colorIdentity'] = ''
#if not manual_card.has_key('rules'):
# manual_card['rules'] = ''
#if not manual_card.has_key('pow'):
# manual_card['pow'] = ''
#if not manual_card.has_key('setnumber'):
# manual_card['setnumber'] = '0'
#if not manual_card.has_key('type'):
# manual_card['type'] = ''
#see if this is a dupe
#and remove the spoiler version
#i trust my manual cards over their data
#for card in cards:
# if card['name'] == manual_card['name']:
# cards.remove(card)
#cards.append(manual_card)
for card in cards:
card['name'] = card['name'].replace('&#x27;', '\'')
card['rules'] = card['rules'].replace('&#x27;', '\'') \
.replace('&lt;i&gt;', '') \
.replace('&lt;/i&gt;', '') \
.replace('&quot;', '"') \
.replace('blkocking', 'blocking')\
.replace('&amp;bull;','*')\
.replace('comes into the','enters the')\
.replace('threeor', 'three or')\
.replace('[i]','')\
.replace('[/i]','')\
.replace('Lawlwss','Lawless')\
.replace('Costner',"Counter")
card['type'] = card['type'].replace(' ',' ')\
.replace('Crature', 'Creature')
if card['type'][-1] == ' ':
card['type'] = card['type'][:-1]
#if card['name'] in card_corrections:
# for correction in card_corrections[card['name']]:
# if correction != 'name':
# card[correction] = card_corrections[card['name']][correction]
# for correction in card_corrections[card['name']]:
# if correction == 'name':
# oldname = card['name']
# card['name'] = card_corrections[oldname]['name']
# card['rules'] = card['rules'].replace(oldname, card_corrections[oldname][correction])
if 'cost' in card and len(card['cost']) > 0:
workingCMC = 0
stripCost = card['cost'].replace('{','').replace('}','')
for manaSymbol in stripCost:
if manaSymbol.isdigit():
workingCMC += int(manaSymbol)
elif not manaSymbol == 'X':
workingCMC += 1
card['cmc'] = workingCMC
# figure out color
for c in 'WUBRG':
if c not in card['colorIdentity']:
if c in card['cost']:
card['color'] += c
card['colorIdentity'] += c
if (c + '}') in card['rules'] or (str.lower(c) + '}') in card['rules']:
if not (c in card['colorIdentity']):
card['colorIdentity'] += c
cleanedcards = []
#let's remove any cards that are named in delete_cards array
for card in cards:
if not card['name'] in delete_cards:
cleanedcards.append(card)
cards = cleanedcards
cardlist = []
cardarray = []
for card in cards:
dupe = False
for dupecheck in cardarray:
if dupecheck['name'] == card['name']:
dupe = True
if dupe == True:
continue
#if 'draft' in card['rules']:
# continue
for cid in card['colorIdentity']:
card['colorIdentityArray'].append(cid)
if 'W' in card['color']:
card['colorArray'].append('White')
if 'U' in card['color']:
card['colorArray'].append('Blue')
if 'B' in card['color']:
card['colorArray'].append('Black')
if 'R' in card['color']:
card['colorArray'].append('Red')
if 'G' in card['color']:
card['colorArray'].append('Green')
cardpower = ''
cardtoughness = ''
if len(card['pow'].split('/')) > 1:
cardpower = card['pow'].split('/')[0]
cardtoughness = card['pow'].split('/')[1]
cardnames = []
cardnumber = card['setnumber'].lstrip('0')
if card['name'] in related_cards:
cardnames.append(card['name'])
cardnames.append(related_cards[card['name']])
cardnumber += 'a'
card['layout'] = 'double-faced'
for namematch in related_cards:
if card['name'] == related_cards[namematch]:
card['layout'] = 'double-faced'
cardnames.append(namematch)
if not card['name'] in cardnames:
cardnames.append(card['name'])
cardnumber += 'b'
cardnames = []
if card['name'] in split_cards:
cardnames.append(card['name'])
cardnames.append(split_cards[card['name']])
cardnumber = cardnumber.replace('b','').replace('a','') + 'a'
card['layout'] = 'split'
for namematch in split_cards:
if card['name'] == split_cards[namematch]:
card['layout'] = 'split'
cardnames.append(namematch)
if not card['name'] in cardnames:
cardnames.append(card['name'])
cardnumber = cardnumber.replace('b','').replace('a','') + 'b'
if 'number' in card:
if 'b' in card['number'] or 'a' in card['number']:
if not 'layout' in card:
print card['name'] + " has a a/b number but no 'layout'"
cardtypes = []
if not '-' in card['type']:
card['type'] = card['type'].replace('instant','Instant').replace('sorcery','Sorcery').replace('creature','Creature')
cardtypes.append(card['type'].replace('instant','Instant'))
else:
cardtypes = card['type'].replace('Legendary ','').split(' - ')[0].split(' ')[:-1]
if '-' in card['type']:
subtype = card['type'].split(' - ')[1].strip()
#if u"—" in card['type']:
# subtype = card['type'].split(' — ')[1].strip()
if subtype:
subtypes = subtype.split(' ')
if card['cmc'] == '':
card['cmc'] = 0
cardjson = {}
#cardjson["id"] = hashlib.sha1(setname + card['name'] + str(card['name']).lower()).hexdigest()
cardjson["cmc"] = card['cmc']
cardjson["manaCost"] = card['cost']
cardjson["name"] = card['name']
cardjson["number"] = cardnumber
#not sure if mtgjson has a list of acceptable rarities, but my application does
#so we'll warn me but continue to write a non-standard rarity (timeshifted?)
#may force 'special' in the future
if card['rarity'] not in ['Mythic Rare','Rare','Uncommon','Common','Special']:
#errors.append({"name": card['name'], "key": "rarity", "value": card['rarity']})
print card['name'] + ' has rarity = ' + card['rarity']
if subtypes:
cardjson['subtypes'] = subtypes
cardjson["rarity"] = card['rarity']
cardjson["text"] = card['rules']
cardjson["type"] = card['type']
cardjson["url"] = card['img']
cardjson["types"] = cardtypes
#optional fields
if len(card['colorIdentityArray']) > 0:
cardjson["colorIdentity"] = card['colorIdentityArray']
if len(card['colorArray']) > 0:
cardjson["colors"] = card['colorArray']
if len(cardnames) > 1:
cardjson["names"] = cardnames
if cardpower or cardpower == '0':
cardjson["power"] = cardpower
cardjson["toughness"] = cardtoughness
if card.has_key('loyalty'):
cardjson["loyalty"] = card['loyalty']
if card.has_key('layout'):
cardjson["layout"] = card['layout']
cardarray.append(cardjson)
return {"cards": cardarray}
def correct_cards(mtgjson, manual_cards=[], card_corrections=[], delete_cards=[]):
mtgjson2 = []
for card in manual_cards:
if 'cmc' not in card:
workingCMC = 0
stripCost = card['manaCost'].replace('{','').replace('}','')
for manaSymbol in stripCost:
if manaSymbol.isdigit():
workingCMC += int(manaSymbol)
elif not manaSymbol == 'X':
workingCMC += 1
if 'types' not in card:
card['types'] = []
# if '—' in card['type']:
# workingTypes = card['type'].split('—')[0].strip()
# else:
workingTypes = card['type'].split('-')[0].strip()
workingTypes.replace('Legendary ','').replace('Snow ','')\
.replace('Elite ','').replace('Basic ','').replace('World ','').replace('Ongoing ','')
card['types'] += workingTypes.split(' ')
if 'subtypes' not in card:
# if '—' in card['type']:
# workingSubtypes = card['type'].split('—')[1].strip()
if '-' in card['type']:
workingSubtypes = card['type'].split('-')[1].strip()
if workingSubtypes:
card['subtypes'] = workingSubtypes.split(' ')
colorMap = {
"W": "White",
"U": "Blue",
"B": "Black",
"R": "Red",
"G": "Green"
}
if 'manaCost' in card:
if 'text' in card and not 'Devoid' in card['text']:
for letter in card['manaCost']:
if not letter.isdigit() and not letter == 'X':
if 'colorIdentity' in card:
if not letter in card['colorIdentity']:
card['colorIdentity'] += letter
else:
card['colorIdentity'] = [letter]
if 'colors' in card:
if not colorMap[letter] in card['colors']:
card['colors'].append(colorMap[letter])
else:
card['colors'] = [colorMap[letter]]
if 'text' in card:
for CID in colorMap:
if '{' + CID + '}' in card['text']:
if 'colorIdentity' in card:
if not CID in card['colorIdentity']:
card['colorIdentity'] += CID
else:
card['colorIdentity'] = [CID]
for card in mtgjson['cards']:
isManual = False
for manualCard in manual_cards:
if card['name'] == manualCard['name']:
mtgjson2.append(manualCard)
print 'overwriting card ' + card['name']
isManual = True
if not isManual and not card['name'] in delete_cards:
mtgjson2.append(card)
for manualCard in manual_cards:
addManual = True
for card in mtgjson['cards']:
if manualCard['name'] == card['name']:
addManual = False
if addManual:
mtgjson2.append(manualCard)
print 'inserting manual card ' + manualCard['name']
mtgjson = {"cards": mtgjson2}
for card in mtgjson['cards']:
for cardCorrection in card_corrections:
if card['name'] == cardCorrection:
for correctionType in card_corrections[cardCorrection]:
if not correctionType == 'name':
card[correctionType] = card_corrections[cardCorrection][correctionType]
if 'name' in card_corrections[cardCorrection]:
card['name'] = card_corrections[cardCorrection]['name']
return mtgjson
def errorcheck(mtgjson):
errors = []
for card in mtgjson['cards']:
for key in card:
if key == "":
errors.append({"name": card['name'], "key": key, "value": ""})
requiredKeys = ['name','type']
for requiredKey in requiredKeys:
if not requiredKey in card:
errors.append({"name": card['name'], "key": key, "missing": True})
if 'text' in card:
#foo = 1
card['text'] = card['text'].replace('<i>','').replace('</i>','').replace('<em>','').replace('</em','').replace('(','')
if 'type' in card:
if 'Planeswalker' in card['type']:
if not 'loyalty' in card:
errors.append({"name": card['name'], "key": "loyalty", "value": ""})
if not card['rarity'] == 'Mythic Rare':
errors.append({"name": card['name'], "key": "rarity", "value": card['rarity']})
if not 'subtypes' in card:
errors.append({"name": card['name'], "key": "subtypes", "oldvalue": "", "newvalue": card['name'].split(" ")[0], "fixed": True})
if not card['name'].split(' ')[0] == 'Ob' and not card['name'].split(' ') == 'Nicol':
card["subtypes"] = card['name'].split(" ")[0]
else:
card["subtypes"] = card['name'].split(" ")[1]
if not 'types' in card:
#errors.append({"name": card['name'], "key": "types", "fixed": True, "oldvalue": "", "newvalue": ["Planeswalker"]})
card['types'] = ["Planeswalker"]
elif not "Planeswalker" in card['types']:
#errors.append({"name": card['name'], "key": "types", "fixed": True, "oldvalue": card['types'], "newvalue": card['types'] + ["Planeswalker"]})
card['types'].append("Planeswalker")
if 'Creature' in card['type']:
if not 'power' in card:
errors.append({"name": card['name'], "key": "power", "value": ""})
if not 'toughness' in card:
errors.append({"name": card['name'], "key": "toughness", "value": ""})
if not 'subtypes' in card:
errors.append({"name": card['name'], "key": "subtypes", "value": ""})
if 'manaCost' in card:
workingCMC = 0
stripCost = card['manaCost'].replace('{','').replace('}','')
for manaSymbol in stripCost:
if manaSymbol.isdigit():
workingCMC += int(manaSymbol)
elif not manaSymbol == 'X':
workingCMC += 1
if not 'cmc' in card:
errors.append({"name": card['name'], "key": "cmc", "value": ""})
elif not card['cmc'] == workingCMC:
errors.append({"name": card['name'], "key": "cmc", "oldvalue": card['cmc'], "newvalue": workingCMC, "fixed": True, "match": card['manaCost']})
card['cmc'] = workingCMC
if not 'cmc' in card:
errors.append({"name": card['name'], "key": "cmc", "value": ""})
else:
if not isinstance(card['cmc'], int):
errors.append({"name": card['name'], "key": "cmc", "oldvalue": card['cmc'], "newvalue": int(card['cmc']), "fixed": True})
card['cmc'] = int(card['cmc'])
else:
if card['cmc'] > 0:
if not 'manaCost' in card:
errors.append({"name": card['name'], "key": "manaCost", "value": "", "match": card['cmc']})
else:
if 'manaCost' in card:
errors.append({"name": card['name'], "key": "manaCost", "oldvalue": card['manaCost'], "fixed": True})
del card["manaCost"]
if 'colors' in card:
if not 'colorIdentity' in card:
if 'text' in card:
if not 'devoid' in card['text'].lower():
errors.append({"name": card['name'], "key": "colorIdentity", "value": ""})
else:
errors.append({"name": card['name'], "key": "colorIdentity", "value": ""})
if 'colorIdentity' in card:
if not 'colors' in card:
#this one will false positive on emerge cards
if not 'Land' in card['type'] and not 'Artifact' in card['type'] and not 'Eldrazi' in card['type']:
if 'text' in card:
if not 'emerge' in card['text'].lower() and not 'devoid' in card['text'].lower():
errors.append({"name": card['name'], "key": "colors", "value": ""})
else:
errors.append({"name": card['name'], "key": "colors", "value": ""})
#if not 'Land' in card['type'] and not 'Artifact' in card['type'] and not 'Eldrazi' in card['type']:
# errors.append({"name": card['name'], "key": "colors", "value": ""})
if not 'url' in card:
errors.append({"name": card['name'], "key": "url", "value": ""})
elif len(card['url']) < 10:
errors.append({"name": card['name'], "key": "url", "value": ""})
if 'layout' in card:
if card['layout'] == 'split' or card['layout'] == 'meld' or card['layout'] == 'aftermath':
if not 'names' in card:
errors.append({"name": card['name'], "key": "names", "value": ""})
if 'number' in card:
if not 'a' in card['number'] and not 'b' in card['number'] and not 'c' in card['number']:
errors.append({"name": card['name'], "key": "number", "value": card['number']})
if not 'number' in card:
errors.append({"name": card['name'], "key": "number", "value": ""})
if not 'types' in card:
errors.append({"name": card['name'], "key": "types", "value": ""})
#print errors
return [mtgjson, errors]
def get_scryfall(setUrl):
#getUrl = 'https://api.scryfall.com/cards/search?q=++e:'
#setUrl = getUrl + setname.lower()
setDone = False
scryfall = []
#firstPass = True
while setDone == False:
setcards = requests.get(setUrl)
setcards = setcards.json()
if setcards.has_key('data'):
#if firstPass:
# cards[set]["cards"] = []
# firstPass = False
scryfall.append(setcards['data'])
#for setkey in mtgjson[set]:
# if 'card' not in setkey:
# if set != 'NMS':
# cards[set][setkey] = mtgjson[set][setkey]
else:
setDone = True
print setUrl
print setcards
print 'No data - ' + set
#noset.append(set)
time.sleep(.1)
if setcards.has_key('has_more'):
if setcards['has_more'] == True:
#print 'Going to extra page of ' + set
setUrl = setcards['next_page']
else:
setDone = True
else:
setDone = True
scryfall = convert_scryfall(scryfall[0])
return {'cards': scryfall}
print
def convert_scryfall(scryfall):
cards2 = []
for card in scryfall:
card2 = {}
card2['cmc'] = int((card['cmc']).split('.')[0])
if card.has_key('mana_cost'):
card2['manaCost'] = card['mana_cost'].replace('{','').replace('}','')
else:
card2['manaCost'] = ''
card2['name'] = card['name']
card2['number'] = card['collector_number']
card2['rarity'] = card['rarity'].replace('mythic','mythic rare').title()
if card.has_key('oracle_text'):
card2['text'] = card['oracle_text'].replace(u"\u2022 ", u'*').replace(u"\u2014",'-').replace(u"\u2212","-")
else:
card2['text'] = ''
card2['url'] = card['image_uri']
card2['type'] = card['type_line'].replace(u'','-')
cardtypes = card['type_line'].split(u'')[0].replace('Legendary ','').replace('Snow ','')\
.replace('Elite ','').replace('Basic ','').replace('World ','').replace('Ongoing ','')
cardtypes = cardtypes.split(' ')
if u'' in card['type_line']:
cardsubtypes = card['type_line'].split(u'')[1]
if ' ' in cardsubtypes:
card2['subtypes'] = cardsubtypes.split(' ')
else:
card2['subtypes'] = [cardsubtypes]
if 'Legendary' in card['type_line']:
if card2.has_key('supertypes'):
card2['supertypes'].append('Legendary')
else:
card2['supertypes'] = ['Legendary']
if 'Snow' in card['type_line']:
if card2.has_key('supertypes'):
card2['supertypes'].append('Snow')
else:
card2['supertypes'] = ['Snow']
if 'Elite' in card['type_line']:
if card2.has_key('supertypes'):
card2['supertypes'].append('Elite')
else:
card2['supertypes'] = ['Elite']
if 'Basic' in card['type_line']:
if card2.has_key('supertypes'):
card2['supertypes'].append('Basic')
else:
card2['supertypes'] = ['Basic']
if 'World' in card['type_line']:
if card2.has_key('supertypes'):
card2['supertypes'].append('World')
else:
card2['supertypes'] = ['World']
if 'Ongoing' in card['type_line']:
if card2.has_key('supertypes'):
card2['supertypes'].append('Ongoing')
else:
card2['supertypes'] = ['Ongoing']
card2['types'] = cardtypes
if card.has_key('color_identity'):
card2['colorIdentity'] = card['color_identity']
if card.has_key('colors'):
if not card['colors'] == []:
card2['colors'] = []
if 'W' in card['colors']:
card2['colors'].append("White")
if 'U' in card['colors']:
card2['colors'].append("Blue")
if 'B' in card['colors']:
card2['colors'].append("Black")
if 'R' in card['colors']:
card2['colors'].append("Red")
if 'G' in card['colors']:
card2['colors'].append("Green")
#card2['colors'] = card['colors']
if card.has_key('all_parts'):
card2['names'] = []
for partname in card['all_parts']:
card2['names'].append(partname['name'])
if card.has_key('power'):
card2['power'] = card['power']
if card.has_key('toughness'):
card2['toughness'] = card['toughness']
if card.has_key('layout'):
if card['layout'] != 'normal':
card2['layout'] = card['layout']
if card.has_key('loyalty'):
card2['loyalty'] = card['loyalty']
if card.has_key('artist'):
card2['artist'] = card['artist']
#if card.has_key('source'):
# card2['source'] = card['source']
#if card.has_key('rulings'):
# card2['rulings'] = card['rulings']
if card.has_key('flavor_text'):
card2['flavor'] = card['flavor_text']
if card.has_key('multiverse_id'):
card2['multiverseid'] = card['multiverse_id']
cards2.append(card2)
return cards2
print
def smash_mtgs_scryfall(mtgs, scryfall):
for mtgscard in mtgs['cards']:
cardFound = False
for scryfallcard in scryfall['cards']:
if scryfallcard['name'] == mtgscard['name']:
for key in scryfallcard:
if key in mtgscard:
if not mtgscard[key] == scryfallcard[key]:
print "%s's key %s\nMTGS : %s\nScryfall: %s" % (mtgscard['name'], key, mtgscard[key], scryfallcard[key])
cardFound = True
if not cardFound:
print "MTGS has card %s and Scryfall does not." % mtgscard['name']
for scryfallcard in scryfall['cards']:
cardFound = False
for mtgscard in mtgs['cards']:
if scryfallcard['name'] == mtgscard['name']:
cardFound = True
if not cardFound:
print "Scryfall has card %s and MTGS does not." % scryfallcard['name']
return mtgs
def scrape_fullspoil(url, showRarityColors=False, showFrameColors=False, manual_cards=[], delete_cards=[], split_cards=[]):
page = requests.get(url)
tree = html.fromstring(page.content)
cards = []
cardtree = tree.xpath('//*[@id="content-detail-page-of-an-article"]')
for child in cardtree:
cardElements = child.xpath('//*/p/img')
cardcount = 0
for cardElement in cardElements:
card = {
"name": cardElement.attrib['alt'].replace(u"\u2019",'\'').split(' /// ')[0],
"img": cardElement.attrib['src']
}
card["url"] = card["img"]
card["cmc"] = 0
card["manaCost"] = ""
card["type"] = "Land"
card["types"] = ["Land"]
card["text"] = ""
#card["colorIdentity"] = [""]
if card['name'] in split_cards:
card["names"] = [card['name'], split_cards[card['name']]]
card["layout"] = "split"
notSplit = True
for backsplit in split_cards:
if card['name'] == split_cards[backsplit]:
notSplit = False
if notSplit and not card['name'] in delete_cards:
cards.append(card)
cardcount += 1
print "Spoil Gallery has " + str(cardcount) + " cards."
#print mtgjson
#print cards
#return cards
get_rarities_by_symbol(fullspoil, showRarityColors)
get_colors_by_frame(fullspoil, showFrameColors)
return cards
def get_rarities_by_symbol(fullspoil, split_cards=[]):
symbolPixels = (234, 215, 236, 218)
highVariance = 15
colorAverages = {
"Common": [225, 224, 225],
"Uncommon": [194, 228, 240],
"Rare": [225, 201, 134],
"Mythic Rare": [249, 163, 15]
}
#symbolCount = 0
for card in fullspoil:
cardImage = Image.open('images/' + card['name'] + '.png')
if card['name'] in split_cards:
setSymbol = cardImage.crop((234, 134, 236, 137))
else:
setSymbol = cardImage.crop(symbolPixels)
cardHistogram = setSymbol.histogram()
reds = cardHistogram[0:256]
greens = cardHistogram[256:256 * 2]
blues = cardHistogram[256 * 2: 256 * 3]
reds = sum(i * w for i, w in enumerate(reds)) / sum(reds)
greens = sum(i * w for i, w in enumerate(greens)) / sum(greens)
blues = sum(i * w for i, w in enumerate(blues)) / sum(blues)
variance = 768
for color in colorAverages:
colorVariance = 0
colorVariance = colorVariance + abs(colorAverages[color][0] - reds)
colorVariance = colorVariance + abs(colorAverages[color][1] - greens)
colorVariance = colorVariance + abs(colorAverages[color][2] - blues)
if colorVariance < variance:
variance = colorVariance
card['rarity'] = color
if variance > highVariance:
# if a card isn't close to any of the colors, it's probably a planeswalker? make it mythic.
print card['name'], 'has high variance of', variance, ', closest rarity is', card['rarity']
card['rarity'] = "Mythic Rare"
print card['name'], '$', reds, greens, blues
#if symbolCount < 10:
#setSymbol.save('images/' + card['name'] + '.symbol.jpg')
# symbolCount += 1
return fullspoil
print
def get_colors_by_frame(fullspoil, split_cards=[]):
framePixels = (20, 11, 76, 16)
highVariance = 10
colorAverages = {
"White": [231,225,200],
"Blue": [103,193,230],
"Black": [58, 61, 54],
"Red": [221, 122, 101],
"Green": [118, 165, 131],
"Multicolor": [219, 200, 138],
"Artifact": [141, 165, 173],
"Colorless": [216, 197, 176],
}
#symbolCount = 0
for card in fullspoil:
cardImage = Image.open('images/' + card['name'] + '.png')
if card['name'] in split_cards:
continue
#setSymbol = cardImage.crop((234, 134, 236, 137))
#else:
cardColor = cardImage.crop(framePixels)
cardHistogram = cardColor.histogram()
reds = cardHistogram[0:256]
greens = cardHistogram[256:256 * 2]
blues = cardHistogram[256 * 2: 256 * 3]
reds = sum(i * w for i, w in enumerate(reds)) / sum(reds)
greens = sum(i * w for i, w in enumerate(greens)) / sum(greens)
blues = sum(i * w for i, w in enumerate(blues)) / sum(blues)
variance = 768
for color in colorAverages:
colorVariance = 0
colorVariance = colorVariance + abs(colorAverages[color][0] - reds)
colorVariance = colorVariance + abs(colorAverages[color][1] - greens)
colorVariance = colorVariance + abs(colorAverages[color][2] - blues)
if colorVariance < variance:
variance = colorVariance
card['colors'] = [color]
if variance > highVariance:
# if a card isn't close to any of the colors, it's probably a planeswalker? make it mythic.
#print card['name'], 'has high variance of', variance, ', closest rarity is', card['color']
print card['name'], '$ colors $', reds, greens, blues
#if 'Multicolor' in card['colors'] or 'Colorless' in card['colors'] or 'Artifact' in card['colors']:
# card['colors'] = []
#if symbolCount < 10:
#cardColor.save('images/' + card['name'] + '.symbol.jpg')
# symbolCount += 1
return fullspoil
def get_image_urls(mtgjson, isfullspoil, setname, setlongname, setSize=269):
IMAGES = 'http://magic.wizards.com/en/content/' + setlongname.lower().replace(' ', '-') + '-cards'
IMAGES2 = 'http://mythicspoiler.com/newspoilers.html'
IMAGES3 = 'http://magic.wizards.com/en/articles/archive/card-image-gallery/' + setlongname.lower().replace(' ', '-')
text = requests.get(IMAGES).text
text2 = requests.get(IMAGES2).text
text3 = requests.get(IMAGES3).text
wotcpattern = r'<img alt="{}.*?" src="(?P<img>.*?\.png)"'
mythicspoilerpattern = r' src="' + setname.lower() + '/cards/{}.*?.jpg">'
for c in mtgjson['cards']:
match = re.search(wotcpattern.format(c['name'].replace('\'','&rsquo;')), text, re.DOTALL)
if match:
c['url'] = match.groupdict()['img']
else:
match3 = re.search(wotcpattern.format(c['name'].replace('\'','&rsquo;')), text3, re.DOTALL)
if match3:
c['url'] = match3.groupdict()['img']
else:
match2 = re.search(mythicspoilerpattern.format((c['name']).lower().replace(' ', '').replace('&#x27;', '').replace('-', '').replace('\'','').replace(',', '')), text2, re.DOTALL)
if match2 and not isfullspoil:
c['url'] = match2.group(0).replace(' src="', 'http://mythicspoiler.com/').replace('">', '')
pass
#if ('Creature' in c['type'] and not c.has_key('power')) or ('Vehicle' in c['type'] and not c.has_key('power')):
# print(c['name'] + ' is a creature w/o p/t img: ' + c['url'])
if len(str(c['url'])) < 10:
print(c['name'] + ' has no image.')
return mtgjson
def write_xml(mtgjson, setname, setlongname, setreleasedate, split_cards=[]):
if not os.path.isdir('out/'):
os.makedirs('out/')
cardsxml = open('out/' + setname + '.xml', 'w+')
cardsxml.truncate()
count = 0
dfccount = 0
newest = ''
related = 0
cardsxml.write("<?xml version='1.0' encoding='UTF-8'?>\n"
"<cockatrice_carddatabase version='3'>\n"
"<sets>\n<set>\n<name>"
+ setname +
"</name>\n"
"<longname>"
+ setlongname +
"</longname>\n"
"<settype>Expansion</settype>\n"
"<releasedate>"
+ setreleasedate +
"</releasedate>\n"
"</set>\n"
"</sets>\n"
"<cards>\n")
#print mtgjson
for card in mtgjson["cards"]:
for carda in split_cards:
if card["name"] == split_cards[carda]:
continue
if count == 0:
newest = card["name"]
count += 1
name = card["name"]
if card.has_key("manaCost"):
manacost = card["manaCost"].replace('{', '').replace('}', '')
else:
manacost = ""
if card.has_key("power") or card.has_key("toughness"):
if card["power"]:
pt = str(card["power"]) + "/" + str(card["toughness"])
else:
pt = 0
else:
pt = 0
if card.has_key("text"):
text = card["text"]
else:
text = ""
cardcmc = str(card['cmc'])
cardtype = card["type"]
if card.has_key("names"):
if "layout" in card:
if card["layout"] != 'split':
if len(card["names"]) > 1:
if card["names"][0] == card["name"]:
related = card["names"][1]
text += '\n\n(Related: ' + card["names"][1] + ')'
dfccount += 1
elif card['names'][1] == card['name']:
related = card["names"][0]
text += '\n\n(Related: ' + card["names"][0] + ')'
else:
for carda in split_cards:
if card["name"] == carda:
cardb = split_cards[carda]
for jsoncard in mtgjson["cards"]:
if cardb == jsoncard["name"]:
cardtype += " // " + jsoncard["type"]
manacost += " // " + (jsoncard["manaCost"]).replace('{', '').replace('}', '')
cardcmc += " // " + str(jsoncard["cmc"])
text += "\n---\n" + jsoncard["text"]
name += " // " + cardb
else:
print card["name"] + " has multiple names and no 'layout' key"
tablerow = "1"
if "Land" in cardtype:
tablerow = "0"
elif "Sorcery" in cardtype:
tablerow = "3"
elif "Instant" in cardtype:
tablerow = "3"
elif "Creature" in cardtype:
tablerow = "2"
if 'number' in card:
if 'b' in card['number']:
if 'layout' in card:
if card['layout'] == 'split':
#print "We're skipping " + card['name'] + " because it's the right side of a split card"
continue
cardsxml.write("<card>\n")
cardsxml.write("<name>" + name.encode('utf-8') + "</name>\n")
cardsxml.write('<set rarity="' + card['rarity'] + '" picURL="' + card["url"] + '">' + setname + '</set>\n')
cardsxml.write("<manacost>" + manacost.encode('utf-8') + "</manacost>\n")
cardsxml.write("<cmc>" + cardcmc + "</cmc>\n")
if card.has_key('colors'):
colorTranslate = {
"White": "W",
"Blue": "U",
"Black": "B",
"Red": "R",
"Green": "G"
}
for color in card['colors']:
cardsxml.write('<color>' + colorTranslate[color] + '</color>\n')
if name + ' enters the battlefield tapped' in text:
cardsxml.write("<cipt>1</cipt>\n")
cardsxml.write("<type>" + cardtype.encode('utf-8') + "</type>\n")
if pt:
cardsxml.write("<pt>" + pt + "</pt>\n")
if card.has_key('loyalty'):
cardsxml.write("<loyalty>" + str(card['loyalty']) + "</loyalty>\n")
cardsxml.write("<tablerow>" + tablerow + "</tablerow>\n")
cardsxml.write("<text>" + text.encode('utf-8') + "</text>\n")
if related:
# for relatedname in related:
cardsxml.write("<related>" + related.encode('utf-8') + "</related>\n")
related = ''
cardsxml.write("</card>\n")
cardsxml.write("</cards>\n</cockatrice_carddatabase>")
print 'XML STATS'
print 'Total cards: ' + str(count)
if dfccount > 0:
print 'DFC: ' + str(dfccount)
print 'Newest: ' + str(newest)
print 'Runtime: ' + str(datetime.datetime.today().strftime('%H:%M')) + ' on ' + str(datetime.date.today())
def pretty_xml(setcode):
prettyxml = xml.dom.minidom.parse('out/' + setcode + '.xml') # or xml.dom.minidom.parseString(xml_string)
pretty_xml_as_string = prettyxml.toprettyxml(newl='')
return pretty_xml_as_string
def make_allsets(AllSets, mtgjson, setname):
AllSets[setname] = mtgjson
return AllSets
def scrape_masterpieces(url='http://www.mtgsalvation.com/spoilers/181-amonkhet-invocations', cardurl='http://www.mtgsalvation.com/cards/amonkhet-invocations/'):
page = requests.get(url)
tree = html.fromstring(page.content)
cards = []
cardstree = tree.xpath('//*[contains(@class, "log-card")]')
for child in cardstree:
#print child.text
cardpage = requests.get(cardurl + child.attrib['data-card-id'] + '-' + child.text.replace(' ','-'))
tree = html.fromstring(cardpage.content)
cardtree = tree.xpath('//img[contains(@class, "card-spoiler-image")]')
#print cardtree[0]
card = {
"name": child.text,
"url": cardtree[0].attrib['src']
}
cards.append(card)
return cards
def make_masterpieces(headers, AllSets, spoil):
masterpieces = scrape_masterpieces(headers['mtgsurl'], headers['mtgscardpath'])
masterpieces2 = []
for masterpiece in masterpieces:
matched = False
for set in AllSets:
if not matched:
for oldcard in AllSets[set]['cards']:
if oldcard['name'] == masterpiece['name'] and not matched:
mixcard = oldcard
mixcard['url'] = masterpiece['url']
mixcard['rarity'] = 'Mythic Rare'
masterpieces2.append(mixcard)
matched = True
break
for spoilcard in spoil['cards']:
if not matched:
if spoilcard['name'] == masterpiece['name']:
mixcard = spoilcard
mixcard['rarity'] = 'Mythic Rare'
mixcard['url'] = masterpiece['url']
masterpieces2.append(mixcard)
matched = True
break
if not matched:
print "We couldn't find a card object to assign the data to for masterpiece " + masterpiece['name']
masterpieces2.append(masterpiece)
mpsjson = {
"name": headers['setlongname'],
"alternativeNames": headers['alternativeNames'],
"code": "MPS_AKH",
"releaseDate": headers['setreleasedate'],
"border": "black",
"type": "masterpiece",
"cards": masterpieces2
}
return mpsjson
def get_allsets():
class MyOpener(urllib.FancyURLopener):
version = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8.1.11) Gecko / 20071127 Firefox / 2.0.0.11'
opener = MyOpener()
opener.retrieve('http://mtgjson.com/json/AllSets.json', 'AllSets.pre.json')
with open('AllSets.pre.json') as data_file:
AllSets = json.load(data_file)
return AllSets
def add_headers(mtgjson, setinfos):
mtgjson2 = {
"block": setinfos['blockname'],
"border": "black",
"code": setinfos['setname'],
"magicCardsInfoCode": setinfos['setname'].lower(),
"name": setinfos['setlongname'],
"releaseDate": setinfos['setreleasedate'],
"type": setinfos['settype'],
"booster": [
[
"rare",
"mythic rare"
],
"uncommon",
"uncommon",
"uncommon",
"common",
"common",
"common",
"common",
"common",
"common",
"common",
"common",
"common",
"common",
"land",
"marketing"
],
"cards": mtgjson['cards']
}
return mtgjson2

56
tox.ini Normal file
View File

@ -0,0 +1,56 @@
[tox]
envlist = isort-inplace, black-inplace, mypy, lint
[testenv]
basepython = python3.7
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/requirements_test.txt
setenv = PYTHONPATH = {toxinidir}
passenv = PYTHONPATH = {toxinidir}
[testenv:black-inplace]
description = Run black and edit all files in place
skip_install = True
deps = black
commands = black magic_spoiler/
# Active Tests
[testenv:yapf-inplace]
description = Run yapf and edit all files in place
skip_install = True
deps = yapf
commands = yapf --in-place --recursive --parallel magic_spoiler/
[testenv:mypy]
description = mypy static type checking only
deps = mypy
commands = mypy {posargs:magic_spoiler/}
[testenv:lint]
description = Run linting tools
deps = pylint
commands = pylint magic_spoiler/ --rcfile=.pylintrc
# Inactive Tests
[testenv:yapf-check]
description = Dry-run yapf to see if reformatting is needed
skip_install = True
deps = yapf
# TODO make it error exit if there's a diff
commands = yapf --diff --recursive --parallel magic_spoiler/
[testenv:isort-check]
description = dry-run isort to see if imports need resorting
deps = isort
commands = isort --check-only
[testenv:isort-inplace]
description = Sort imports
deps = isort
commands = isort -rc magic_spoiler/
[testenv:unit]
description = Run unit tests with coverage and mypy type checking
extras = dev
deps = pytest
commands = pytest --cov=magic_spoiler {posargs:tests/}