mtg-card-puller 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ notebooks/
2
+ data/
3
+ tests/
@@ -0,0 +1,112 @@
1
+ # Template Specific
2
+ data/*
3
+ !data/README.md
4
+
5
+ # IDE
6
+ .vscode/
7
+
8
+ # Byte-compiled / optimized / DLL files
9
+ __pycache__/
10
+ *.py[cod]
11
+ *$py.class
12
+
13
+ # C extensions
14
+ *.so
15
+
16
+ # Distribution / packaging
17
+ .Python
18
+ env/
19
+ build/
20
+ develop-eggs/
21
+ dist/
22
+ downloads/
23
+ eggs/
24
+ .eggs/
25
+ lib/
26
+ lib64/
27
+ parts/
28
+ sdist/
29
+ var/
30
+ wheels/
31
+ *.egg-info/
32
+ .installed.cfg
33
+ *.egg
34
+
35
+ # PyInstaller
36
+ # Usually these files are written by a python script from a template
37
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
38
+ *.manifest
39
+ *.spec
40
+
41
+ # Installer logs
42
+ pip-log.txt
43
+ pip-delete-this-directory.txt
44
+
45
+ # Unit test / coverage reports
46
+ htmlcov/
47
+ .tox/
48
+ .coverage
49
+ .coverage.*
50
+ .cache
51
+ nosetests.xml
52
+ coverage.xml
53
+ *.cover
54
+ .hypothesis/
55
+ .pytest_cache/
56
+
57
+ # Translations
58
+ *.mo
59
+ *.pot
60
+
61
+ # Django stuff:
62
+ *.log
63
+ local_settings.py
64
+
65
+ # Flask stuff:
66
+ instance/
67
+ .webassets-cache
68
+
69
+ # Scrapy stuff:
70
+ .scrapy
71
+
72
+ # Sphinx documentation
73
+ docs/_build/
74
+
75
+ # PyBuilder
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # pyenv
82
+ .python-version
83
+
84
+ # celery beat schedule file
85
+ celerybeat-schedule
86
+
87
+ # SageMath parsed files
88
+ *.sage.py
89
+
90
+ # dotenv
91
+ .env
92
+
93
+ # virtualenv
94
+ .venv
95
+ venv/
96
+ ENV/
97
+
98
+ # Spyder project settings
99
+ .spyderproject
100
+ .spyproject
101
+
102
+ # Rope project settings
103
+ .ropeproject
104
+
105
+ # mkdocs documentation
106
+ /site
107
+
108
+ # mypy
109
+ .mypy_cache/
110
+
111
+ # IDE settings
112
+ .vscode/
@@ -0,0 +1,9 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.6.7
4
+ hooks:
5
+ - id: ruff
6
+ name: Ruff Check
7
+ - id: ruff-format
8
+ name: Ruff Format
9
+ args: [--check]
@@ -0,0 +1,16 @@
1
+ Apache Software License 2.0
2
+
3
+ Copyright (c) 2025, Rob Fletcher
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License");
6
+ you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
16
+
@@ -0,0 +1,128 @@
1
+ .PHONY: clean clean-test clean-pyc clean-build docs help
2
+ .DEFAULT_GOAL := help
3
+
4
+ define BROWSER_PYSCRIPT
5
+ import os, webbrowser, sys
6
+
7
+ from urllib.request import pathname2url
8
+
9
+ webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1])))
10
+ endef
11
+ export BROWSER_PYSCRIPT
12
+
13
+ define PRINT_HELP_PYSCRIPT
14
+ import re, sys
15
+
16
+ for line in sys.stdin:
17
+ match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line)
18
+ if match:
19
+ target, help = match.groups()
20
+ print("%-20s %s" % (target, help))
21
+ endef
22
+ export PRINT_HELP_PYSCRIPT
23
+
24
+ BROWSER := python -c "$$BROWSER_PYSCRIPT"
25
+
26
+ help:
27
+ @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST)
28
+
29
+ clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts
30
+
31
+ clean-build: ## remove build artifacts
32
+ rm -fr build/
33
+ rm -fr dist/
34
+ rm -fr .eggs/
35
+ find . -name '*.egg-info' -exec rm -fr {} +
36
+ find . -name '*.egg' -exec rm -f {} +
37
+
38
+ clean-pyc: ## remove Python file artifacts
39
+ find . -name '*.pyc' -exec rm -f {} +
40
+ find . -name '*.pyo' -exec rm -f {} +
41
+ find . -name '*~' -exec rm -f {} +
42
+ find . -name '__pycache__' -exec rm -fr {} +
43
+
44
+ clean-test: ## remove test and coverage artifacts
45
+ rm -fr .tox/
46
+ rm -f .coverage
47
+ rm -fr htmlcov/
48
+ rm -fr .pytest_cache
49
+
50
+ lint: ## check style with flake8
51
+ flake8 mtg_card_puller tests
52
+
53
+ test: ## run tests quickly with the default Python
54
+ pytest
55
+
56
+ test-all: ## run tests on every Python version with tox
57
+ tox
58
+
59
+ coverage: ## check code coverage quickly with the default Python
60
+ coverage run --source mtg_card_puller -m pytest
61
+ coverage report -m
62
+ coverage html
63
+ $(BROWSER) htmlcov/index.html
64
+
65
+ docs: ## generate Sphinx HTML documentation, including API docs
66
+ rm -f docs/mtg_card_puller.rst
67
+ rm -f docs/modules.rst
68
+ sphinx-apidoc -o docs/ mtg_card_puller
69
+ $(MAKE) -C docs clean
70
+ $(MAKE) -C docs html
71
+ $(BROWSER) docs/_build/html/index.html
72
+
73
+ servedocs: docs ## compile the docs watching for changes
74
+ watchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D .
75
+
76
+ release: dist ## package and upload a release
77
+ twine upload dist/*
78
+
79
+ dist: clean ## builds source and wheel package
80
+ python setup.py sdist
81
+ python setup.py bdist_wheel
82
+ ls -l dist
83
+
84
+ install: clean ## install the package to the active Python's site-packages
85
+ python setup.py install
86
+
87
+ SHELL:=/bin/bash
88
+ model_dir := models/$(model)
89
+ step_dir := models/$(model)/$(step)
90
+ image_name := $(model).$(step):latest
91
+ dockerfile := $(step_dir)/Dockerfile
92
+
93
+ model-build:
94
+ @if [ -z "$(model)" ]; then \
95
+ echo "must specify a 'model' and 'step'"; \
96
+ exit 1; \
97
+ fi
98
+
99
+ docker build \
100
+ --progress=plain \
101
+ --ssh=default \
102
+ -t $(image_name) \
103
+ -f $(dockerfile) \
104
+ $(step_dir)
105
+
106
+ model-run:
107
+ @if [ -z "$(model)" ]; then \
108
+ echo "must specify a 'model' and 'step'"; \
109
+ exit 1; \
110
+ fi
111
+
112
+ cd $(step_dir) && docker compose up --force-recreate run-local
113
+
114
+ model-shell:
115
+ @if [ -z "$(model)" ]; then \
116
+ echo "must specify a 'model' and 'step'"; \
117
+ exit 1; \
118
+ fi
119
+
120
+ cd $(step_dir) && docker compose run --entrypoint bash run-local
121
+
122
+ model-dev:
123
+ @if [ -z "$(model)" ]; then \
124
+ echo "must specify a 'model' and 'step'"; \
125
+ exit 1; \
126
+ fi
127
+
128
+ cd $(step_dir) && docker compose up --force-recreate dev
@@ -0,0 +1,118 @@
1
+ Metadata-Version: 2.4
2
+ Name: mtg_card_puller
3
+ Version: 0.0.1
4
+ Summary: Pull MTG card images in b
5
+ Project-URL: Homepage, https://github.com/seerai/mtg_card_puller
6
+ Author-email: Rob Fletcher <robroy.fletcher@gmail.com>
7
+ License: Apache Software License 2.0
8
+
9
+ Copyright (c) 2025, Rob Fletcher
10
+
11
+ Licensed under the Apache License, Version 2.0 (the "License");
12
+ you may not use this file except in compliance with the License.
13
+ You may obtain a copy of the License at
14
+
15
+ http://www.apache.org/licenses/LICENSE-2.0
16
+
17
+ Unless required by applicable law or agreed to in writing, software
18
+ distributed under the License is distributed on an "AS IS" BASIS,
19
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
+ See the License for the specific language governing permissions and
21
+ limitations under the License.
22
+
23
+ License-File: LICENSE
24
+ Classifier: Development Status :: 2 - Pre-Alpha
25
+ Classifier: Intended Audience :: Developers
26
+ Classifier: License :: OSI Approved :: Apache Software License
27
+ Classifier: Natural Language :: English
28
+ Classifier: Programming Language :: Python :: 3
29
+ Classifier: Programming Language :: Python :: 3.10
30
+ Requires-Python: >=3.10
31
+ Requires-Dist: click>=7.0
32
+ Requires-Dist: geodesic-api
33
+ Provides-Extra: dev
34
+ Requires-Dist: coverage; extra == 'dev'
35
+ Requires-Dist: jupyterlab; extra == 'dev'
36
+ Requires-Dist: pre-commit; extra == 'dev'
37
+ Requires-Dist: pytest; extra == 'dev'
38
+ Requires-Dist: ruff; extra == 'dev'
39
+ Description-Content-Type: text/markdown
40
+
41
+ # mtg_card_puller
42
+
43
+ Pull MTG card images in b
44
+
45
+ ## Installation
46
+ To setup mtg_card_puller create a conda environment with the generated `environment.yaml`.
47
+ This sets up a development environment with all the required packages. This is what is used when building
48
+ out the functionality of this project.
49
+ ```bash
50
+ conda env create -f environment.yaml
51
+ conda activate mtg_puller
52
+ ```
53
+
54
+ Then install the python module. This installs the module the same way as if someone just wanting
55
+ to use this package would install it. This is what is used when using this package as a dependency.
56
+ **Note:** This installs the `dev` extra dependencies. When installing this to use as a standalone package,
57
+ you should leave off the `[dev]` part.
58
+ ```bash
59
+ python -m pip install .[dev]
60
+ ```
61
+
62
+ If functionality from this repo is needed in another project, you can install this package as a dependency
63
+ directly from the GitHub repo.
64
+ ```bash
65
+ python -m pip install git+https://github.com/seerai/mtg_card_puller.git
66
+ ```
67
+
68
+ You can then test that the docker image builds and runs correctly.
69
+ ```bash
70
+ docker build -t mtg_card_puller:latest .
71
+ ```
72
+ Once the build is complete, test the image:
73
+ ```bash
74
+ docker run --rm mtg_card_puller:latest
75
+ ```
76
+ You should see a message printed:
77
+ ```
78
+ Replace this message by putting your code into mtg_card_puller.cli.main
79
+ See click documentation at https://click.palletsprojects.com/
80
+ ```
81
+
82
+ ## Development
83
+
84
+ ### READMEs and Notes
85
+ Every folder in this project other than `mtg_card_puller`` should have a README.md
86
+ file (the template does this automatically for all folders). Each of these READMEs should be kept
87
+ up to date and should describe the contents of the folder. This is especially important for the
88
+ the data folder and should include all data files, their sources and how to download them. This
89
+ is also a good place to put any notes about the contents of the folder. For example, if you are
90
+ working on a notebook and want to save some notes about what you are doing, you can put them in
91
+ the README for that folder. **You should always be asking yourself** "If someone else were to look at
92
+ this folder, would they know what is going on?". If the answer is no, then you should add more
93
+ information to the README.
94
+
95
+ ### Dependency Management
96
+ When developing on this project, **make sure that you never directly `pip install` any packages**. This
97
+ leads to situations where peoples environemnts are different and the code will not run correctly
98
+ when another person tries to install the package. Instead, always add the dependency to the
99
+ `pyproject.toml` file and reinstall the package. This ensures that all dependencies are tracked and
100
+ others can use this package without issue. When possible, you should also pin a version or version range
101
+ of the dependency. i.e. `numpy==1.19.2` or `numpy>=1.19.2,<1.20.0`.
102
+
103
+ ### Testing
104
+ Any functions implemened in this package should be covered by tests if possible. This doesnt mean that
105
+ every line of code needs to be tested, but that the functionality of the code is tested. Anything
106
+ that is used only for development purposes can sometimes be excluded from testing. However, any
107
+ function/class you expect someone to use after installing this as a package should be tested.
108
+ **You should always be asking yourself** "If someone else were to use this code, would they know that it
109
+ works correctly?". If the answer is no, then you should add tests.
110
+
111
+ This project uses `pytest` for testing. To run the tests, cd to the root of the project and run:
112
+ ```bash
113
+ coverage run -m pytest
114
+ ```
115
+ This will run all the tests and generate a coverage report. To view the coverage report, run:
116
+ ```bash
117
+ coverage report
118
+ ```
@@ -0,0 +1,78 @@
1
+ # mtg_card_puller
2
+
3
+ Pull MTG card images in b
4
+
5
+ ## Installation
6
+ To setup mtg_card_puller create a conda environment with the generated `environment.yaml`.
7
+ This sets up a development environment with all the required packages. This is what is used when building
8
+ out the functionality of this project.
9
+ ```bash
10
+ conda env create -f environment.yaml
11
+ conda activate mtg_puller
12
+ ```
13
+
14
+ Then install the python module. This installs the module the same way as if someone just wanting
15
+ to use this package would install it. This is what is used when using this package as a dependency.
16
+ **Note:** This installs the `dev` extra dependencies. When installing this to use as a standalone package,
17
+ you should leave off the `[dev]` part.
18
+ ```bash
19
+ python -m pip install .[dev]
20
+ ```
21
+
22
+ If functionality from this repo is needed in another project, you can install this package as a dependency
23
+ directly from the GitHub repo.
24
+ ```bash
25
+ python -m pip install git+https://github.com/seerai/mtg_card_puller.git
26
+ ```
27
+
28
+ You can then test that the docker image builds and runs correctly.
29
+ ```bash
30
+ docker build -t mtg_card_puller:latest .
31
+ ```
32
+ Once the build is complete, test the image:
33
+ ```bash
34
+ docker run --rm mtg_card_puller:latest
35
+ ```
36
+ You should see a message printed:
37
+ ```
38
+ Replace this message by putting your code into mtg_card_puller.cli.main
39
+ See click documentation at https://click.palletsprojects.com/
40
+ ```
41
+
42
+ ## Development
43
+
44
+ ### READMEs and Notes
45
+ Every folder in this project other than `mtg_card_puller`` should have a README.md
46
+ file (the template does this automatically for all folders). Each of these READMEs should be kept
47
+ up to date and should describe the contents of the folder. This is especially important for the
48
+ the data folder and should include all data files, their sources and how to download them. This
49
+ is also a good place to put any notes about the contents of the folder. For example, if you are
50
+ working on a notebook and want to save some notes about what you are doing, you can put them in
51
+ the README for that folder. **You should always be asking yourself** "If someone else were to look at
52
+ this folder, would they know what is going on?". If the answer is no, then you should add more
53
+ information to the README.
54
+
55
+ ### Dependency Management
56
+ When developing on this project, **make sure that you never directly `pip install` any packages**. This
57
+ leads to situations where peoples environemnts are different and the code will not run correctly
58
+ when another person tries to install the package. Instead, always add the dependency to the
59
+ `pyproject.toml` file and reinstall the package. This ensures that all dependencies are tracked and
60
+ others can use this package without issue. When possible, you should also pin a version or version range
61
+ of the dependency. i.e. `numpy==1.19.2` or `numpy>=1.19.2,<1.20.0`.
62
+
63
+ ### Testing
64
+ Any functions implemened in this package should be covered by tests if possible. This doesnt mean that
65
+ every line of code needs to be tested, but that the functionality of the code is tested. Anything
66
+ that is used only for development purposes can sometimes be excluded from testing. However, any
67
+ function/class you expect someone to use after installing this as a package should be tested.
68
+ **You should always be asking yourself** "If someone else were to use this code, would they know that it
69
+ works correctly?". If the answer is no, then you should add tests.
70
+
71
+ This project uses `pytest` for testing. To run the tests, cd to the root of the project and run:
72
+ ```bash
73
+ coverage run -m pytest
74
+ ```
75
+ This will run all the tests and generate a coverage report. To view the coverage report, run:
76
+ ```bash
77
+ coverage report
78
+ ```
@@ -0,0 +1,17 @@
1
+ FROM python:3.10-slim AS builder
2
+
3
+ RUN mkdir /install
4
+ WORKDIR /app
5
+ COPY mtg_card_puller/ /app/mtg_card_puller
6
+ COPY pyproject.toml LICENSE README.md /app
7
+
8
+ RUN python -m pip install . --prefix=/install
9
+
10
+ FROM python:3.10-slim
11
+ WORKDIR /app
12
+ COPY --from=builder /install /usr/local
13
+
14
+ ENV PATH="/usr/local/bin:${PATH}"
15
+ ENV PYTHONPATH="${PYTHONPATH}:/usr/local/"
16
+
17
+ CMD ["mtg_card_puller"]
@@ -0,0 +1,8 @@
1
+ name: "mtg_puller"
2
+ channels:
3
+ - defaults
4
+ dependencies:
5
+ - python=3.10
6
+ - pip
7
+ variables:
8
+ DATA_PATH: /home/rob/projects/mtg_card_puller/data
@@ -0,0 +1,5 @@
1
+ """Top-level package for mtg_card_puller."""
2
+
3
+ __author__ = """Rob Fletcher"""
4
+ __email__ = 'robroy.fletcher@gmail.com'
5
+ __version__ = '0.0.1'
@@ -0,0 +1,23 @@
1
+ """Console script for mtg_card_puller."""
2
+
3
+ import sys
4
+ import click
5
+ from mtg_card_puller import mtg_card_puller
6
+
7
+ CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
8
+
9
+ @click.command(context_settings=CONTEXT_SETTINGS)
10
+ @click.option(
11
+ "-v", "--verbose", is_flag=True, help="Enable verbose output for debugging"
12
+ )
13
+ @click.option(
14
+ "-f", "--file", type=str, required=True, help="Path to the file containing card names"
15
+ )
16
+ def main(verbose, file):
17
+ """Console script for mtg_card_puller."""
18
+ mtg_card_puller.main(deck_file=file)
19
+ return 0
20
+
21
+
22
+ if __name__ == "__main__":
23
+ sys.exit(main()) # pragma: no cover
@@ -0,0 +1,100 @@
1
+ """Main module."""
2
+
3
+ import re
4
+ import tqdm
5
+ import requests
6
+ import time
7
+ from pathlib import Path
8
+ import logging
9
+ from typing import Tuple
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ def extract_card_id(mox_full_name: str) -> Tuple[str, str, int]:
14
+ """Extracts the card ID from the full name of a card.
15
+
16
+ Args:
17
+ mox_full_name (str): The full name of the card.
18
+
19
+ Returns:
20
+ Tuple[str, str, int]: A tuple containing the card name, card series, and card number.
21
+ """
22
+ # Extract the card ID using regex
23
+ card_name = ""
24
+ card_series = ""
25
+ card_number = ""
26
+ match = re.search(r'\d (.*) \(([A-Z0-9]{3})\) (\d{1,3})', mox_full_name)
27
+ if match:
28
+ card_name = match.group(1)
29
+ card_series = match.group(2)
30
+ card_number = int(match.group(3))
31
+
32
+ logger.debug(f"Extracted card series: {card_series}, card number: {card_number} from full name: {mox_full_name}")
33
+ else:
34
+ raise ValueError("Problem matching card ID from full name: " + mox_full_name)
35
+
36
+ return card_name, card_series, card_number
37
+
38
+ def execute_request(card_name: str, card_series: str, card_number: int) -> None:
39
+ """Executes a request to the MTG API to fetch card details.
40
+
41
+ Args:
42
+ card_name (str): The name of the card.
43
+ card_series (str): The card series.
44
+ card_number (int): The card number.
45
+ """
46
+ normalized_name = card_name.strip(".'").replace(" ", "_")
47
+ headers = {'User-Agent': 'mtg_card_puller', 'Accept': '*/*'}
48
+
49
+ # Check if the image already exists
50
+ img_path = Path(f"{normalized_name}.png")
51
+ if img_path.is_file():
52
+ logger.info(f"Image for {card_name} already exists, skipping download.")
53
+ return
54
+
55
+ time.sleep(0.1) # Avoid hitting the API too fast
56
+ r = requests.get(f"https://api.scryfall.com/cards/{card_series}/{card_number}", headers=headers)
57
+ if r.status_code != 200:
58
+ logger.error(f"Failed to fetch card details for {card_name} ({card_series} {card_number})")
59
+ return
60
+
61
+ img_url = r.json().get('image_uris', {}).get('png')
62
+
63
+ time.sleep(0.1) # Avoid hitting the API too fast
64
+ r = requests.get(img_url, stream=True, headers=headers)
65
+ if r.status_code != 200:
66
+ logger.error(f"Failed to fetch image for {card_name} ({card_series} {card_number})")
67
+ return
68
+
69
+ # Save the image to a file
70
+ with open(f"{normalized_name}.png", 'wb') as f:
71
+ for chunk in r.iter_content(1024):
72
+ f.write(chunk)
73
+
74
+
75
+ def main(deck_file: str, verbose: bool = False) -> None:
76
+ """Main entrypoint for the script
77
+
78
+ Args:
79
+ deck_file (str): Path to the file containing card names.
80
+ verbose (bool): Enable verbose output for debugging.
81
+ """
82
+ logger.setLevel(logging.DEBUG if verbose else logging.INFO)
83
+ with open(deck_file, 'r') as f:
84
+ pbar = tqdm.tqdm(f.readlines(), desc="Processing cards", colour="green", unit="card")
85
+ for line in pbar:
86
+ try:
87
+ card_name, card_series, card_number = extract_card_id(line.strip())
88
+ pbar.set_description(f"Fetching {card_name[:20]:20}")
89
+ except ValueError as e:
90
+ logger.error(e)
91
+
92
+ try:
93
+ execute_request(card_name, card_series, card_number)
94
+ # time.sleep(1)
95
+
96
+ except Exception as e:
97
+ logger.error(f"Error fetching card details: {e}")
98
+ continue
99
+
100
+
@@ -0,0 +1,79 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 1,
6
+ "metadata": {},
7
+ "outputs": [],
8
+ "source": [
9
+ "import requests"
10
+ ]
11
+ },
12
+ {
13
+ "cell_type": "code",
14
+ "execution_count": 4,
15
+ "metadata": {},
16
+ "outputs": [
17
+ {
18
+ "name": "stdout",
19
+ "output_type": "stream",
20
+ "text": [
21
+ "https://cards.scryfall.io/png/front/2/3/23eb3cf7-c90d-4bfa-b125-4fbcb5614468.png?1710673416\n"
22
+ ]
23
+ }
24
+ ],
25
+ "source": [
26
+ "headers = {'User-Agent': 'mtg_card_puller', 'Accept': '*/*'}\n",
27
+ "r = requests.get(\"https://api.scryfall.com/cards/pip/7\", headers=headers)\n",
28
+ "if r.status_code == 200:\n",
29
+ " img_uri = r.json()['image_uris']['png']\n",
30
+ " print(r.json()['image_uris']['png'])\n",
31
+ "else:\n",
32
+ " print(\"Error: \", r.status_code)"
33
+ ]
34
+ },
35
+ {
36
+ "cell_type": "code",
37
+ "execution_count": 7,
38
+ "metadata": {},
39
+ "outputs": [],
40
+ "source": [
41
+ "r = requests.get(img_uri, headers=headers, stream=True)\n",
42
+ "if r.status_code == 200:\n",
43
+ " with open('card.png', 'wb') as f:\n",
44
+ " for chunk in r.iter_content(1024):\n",
45
+ " f.write(chunk)\n",
46
+ "else:\n",
47
+ " print(\"Error: \", r.status_code)"
48
+ ]
49
+ },
50
+ {
51
+ "cell_type": "code",
52
+ "execution_count": null,
53
+ "metadata": {},
54
+ "outputs": [],
55
+ "source": []
56
+ }
57
+ ],
58
+ "metadata": {
59
+ "kernelspec": {
60
+ "display_name": "mtg_puller",
61
+ "language": "python",
62
+ "name": "python3"
63
+ },
64
+ "language_info": {
65
+ "codemirror_mode": {
66
+ "name": "ipython",
67
+ "version": 3
68
+ },
69
+ "file_extension": ".py",
70
+ "mimetype": "text/x-python",
71
+ "name": "python",
72
+ "nbconvert_exporter": "python",
73
+ "pygments_lexer": "ipython3",
74
+ "version": "3.10.16"
75
+ }
76
+ },
77
+ "nbformat": 4,
78
+ "nbformat_minor": 2
79
+ }
@@ -0,0 +1,52 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.21.0"]
3
+ build-backend = "hatchling.build"
4
+
5
+
6
+
7
+ [project]
8
+ name = "mtg_card_puller"
9
+ description = "Pull MTG card images in b"
10
+ readme = "README.md"
11
+ license.file = "LICENSE"
12
+ authors = [
13
+ { name = "Rob Fletcher", email = "robroy.fletcher@gmail.com" }
14
+ ]
15
+ maintaners = [
16
+ { name = "Rob Fletcher", email = "robroy.fletcher@gmail.com" }
17
+ ]
18
+ requires-python = ">=3.10"
19
+ classifiers=[
20
+ 'Development Status :: 2 - Pre-Alpha',
21
+ 'Intended Audience :: Developers',
22
+ 'License :: OSI Approved :: Apache Software License',
23
+ 'Natural Language :: English',
24
+ 'Programming Language :: Python :: 3',
25
+ 'Programming Language :: Python :: 3.10',
26
+ ]
27
+ dependencies = [
28
+ "click>=7.0",
29
+ "geodesic-api"
30
+ ]
31
+ dynamic = ["version"]
32
+
33
+ [project.scripts]
34
+ mtg_card_puller = "mtg_card_puller.cli:main"
35
+
36
+ [project.optional-dependencies]
37
+ dev = [
38
+ "ruff",
39
+ "pytest",
40
+ "coverage",
41
+ "pre-commit",
42
+ "jupyterlab",
43
+ ]
44
+
45
+ [project.urls]
46
+ Homepage = "https://github.com/seerai/mtg_card_puller"
47
+
48
+ [tool.hatch.version]
49
+ path = "mtg_card_puller/__init__.py"
50
+
51
+ [tool.hatch.build.targets.sdist]
52
+ exclude = [".github", "data/", "docs/"]
@@ -0,0 +1,3 @@
1
+ # Scripts
2
+
3
+ Any scripts that get run as standalone things (not to be imported as packages).
@@ -0,0 +1 @@
1
+ """Unit test package for mtg_card_puller."""
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env python
2
+
3
+ """Tests for `mtg_card_puller` package."""
4
+
5
+ from mtg_card_puller import mtg_card_puller
6
+ from mtg_card_puller import cli
7
+ from click.testing import CliRunner
8
+ import pytest
9
+
10
+
11
+ @pytest.fixture
12
+ def test_deck_data():
13
+ """Load sample data
14
+
15
+ Load the sample data file in the data folder to test with
16
+ """
17
+ with open('tests/data/sample_deck.txt', 'r') as f:
18
+ yield f
19
+
20
+ card_id_cases = [
21
+ ("1 Mr. House, President and CEO (PIP) 7 *F*", "PIP", 7),
22
+ ("1 Arcane Signet (M3C) 283", "M3C", 283),
23
+ ("1 Arid Mesa (MH2) 244", "MH2", 244),
24
+ ("1 Attempted Murder (UNF) 66 *F*", "UNF", 66),
25
+ ("1 Automated Artificer (NEO) 239 *F*", "NEO", 239),
26
+ ("1 Bag of Devouring (AFC) 21", "AFC", 21),
27
+ ("1 Barbarian Class (AFR) 131", "AFR", 131),
28
+ ("1 Battlefield Forge (C21) 278", "C21", 278),
29
+ ("1 Bennie Bracks, Zoologist (NCC) 86", "NCC", 86),
30
+ ("1 Berserker's Frenzy (AFC) 29", "AFC", 29),
31
+ ("1 Big Score (SNC) 102 *F*", "SNC", 102),
32
+ ("1 Blood Crypt (RNA) 245", "RNA", 245),
33
+ ("1 Bloodstained Mire (KTK) 230", "KTK", 230),
34
+ ("1 Boros Signet (SLD) 291 *E*", "SLD", 291),
35
+ ("1 Brazen Dwarf (AFR) 134", "AFR", 134),
36
+ ]
37
+
38
+ def test_command_line_interface():
39
+ """Test the CLI."""
40
+ runner = CliRunner()
41
+ result = runner.invoke(cli.main)
42
+ assert result.exit_code == 2
43
+ assert "Missing option '-f'" in result.output
44
+ help_result = runner.invoke(cli.main, ['--help'])
45
+ assert help_result.exit_code == 0
46
+ assert 'Console script for mtg_card_puller' in help_result.output
47
+
48
+ @pytest.mark.parametrize("card_line, expected_series, expected_number", card_id_cases)
49
+ def test_extract_card_id(card_line, expected_series, expected_number):
50
+ assert mtg_card_puller.extract_card_id(card_line)[1:] == (expected_series, expected_number)
51
+