From 549b70dd51ffbad05f98590a7c8166f465ad4e46 Mon Sep 17 00:00:00 2001 From: brettlangdon Date: Wed, 17 Dec 2014 20:43:35 -0500 Subject: [PATCH] initial commit --- .gitignore | 12 +++++ MANIFEST.in | 4 ++ README.md | 121 +++++++++++++++++++++++++++++++++++++++++++++++ bin/swuploader | 63 ++++++++++++++++++++++++ requirements.txt | 2 + setup.py | 22 +++++++++ swuploader.py | 61 ++++++++++++++++++++++++ 7 files changed, 285 insertions(+) create mode 100644 .gitignore create mode 100644 MANIFEST.in create mode 100644 README.md create mode 100755 bin/swuploader create mode 100644 requirements.txt create mode 100644 setup.py create mode 100644 swuploader.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0feff52 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +*.py[co] +*.log +build +dist +docs/_* +.env +*.egg-info +*.egg +htmlcov +.coverage +models +*.json diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..427ae37 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,4 @@ +include README.* setup.py setup.cfg swuploader.py +include bin * +global-exclude *.pyc +global-exclude *.pyo diff --git a/README.md b/README.md new file mode 100644 index 0000000..a9b0ba4 --- /dev/null +++ b/README.md @@ -0,0 +1,121 @@ +Shapeways Uploader +================== + +Shapeways API bulk uploader. + +## Install +### pip +```bash +pip install swuploader +``` +### Git +```bash +git clone git://github.com/brettlangdon/swuploader.git +cd ./swuploader +python setup.py install +``` + +## Usage +``` +Usage: + swuploader (-h | --help) + swuploader (-v | --version) + swuploader auth [--server=] + swuploader upload [--models=] [--out=] [--server=] + +Options: + -h --help Show this help text + -v --version Show version information + -m --models= Set the directory for where model ini's are available [default: ./] + -o --out= Set the directory where the model ini's are moved to after upload [default: ./] + -s --server= Set different api server url if desired. +``` + +### Setup App + +Before you can use `swuploader` you must have an application setup on +shapeways.com this can be setup/registered at +https://developers.shapeways.com/manage-apps/create. +You will also need to note your `consumer key` and `consumer secret` from your +app's settings page. + +### Authenticate +Once you have an application setup with shapeways.com you must authenticate with +the api server before you can start to upload models, to do so run the +following: + +```bash +swuploader auth ./auth.json +``` + +This will give you a url to use for authentication, once you follow that url and +authorize with your application you will be given a `verifier code` to enter to +complete the authentication process. + +As a result of running this command you will have a file `auth.json` which will +contain required keys/tokens/secrets necessary for all subsequent requests. + +### Upload +Finally you are ready to upload. In order to upload you need a folder which +contains the model files that you want to upload (see +http://www.shapeways.com/tutorials/supported-applications for a list of +supported file types). + +For each model file you must supply an additional `.ini` file containing +information about the model which is being uploaded. For example: +```ini +name=Sphere +file=./sphere.stl +``` + +For supported properties please see: +http://developers.shapeways.com/docs?li=dh_docs#POST_-models-v1 for the list of +accepted paramaters. + +*note* for materials, please provide them in the format +`material.=`. + +Your directory structure should look similar to: + +```bash +$ ls -1 +auth.json +model.obj +model.ini +sphere.ini +sphere.stl +``` + +Once you have your model files and `.ini` files ready you can run `swuploader` +like so: + +```bash +swuploader upload ./auth.json +``` + +`swuploader` will then read all the `.ini` files in the current directory and +upload the model file assigned to the `file` attribute along with the other +provided properties in the `.ini` file. + + +## License +``` +The MIT License (MIT) Copyright (c) 2014 Brett Langdon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` diff --git a/bin/swuploader b/bin/swuploader new file mode 100755 index 0000000..61fffdd --- /dev/null +++ b/bin/swuploader @@ -0,0 +1,63 @@ +#!/usr/bin/env python +import json +import shutil +import os +import urlparse + +from shapeways.client import Client +import swuploader +import docopt + +__doc__ = """ +Usage: + swuploader (-h | --help) + swuploader (-v | --version) + swuploader auth [--server=] + swuploader upload [--models=] [--out=] [--server=] + +Options: + -h --help Show this help text + -v --version Show version information + -m --models= Set the directory for where model ini's are available [default: ./] + -o --out= Set the directory where the model ini's are moved to after upload [default: ./] + -s --server= Set different api server url if desired. +""" + +args = docopt.docopt(__doc__, version="swuploader %s" % (swuploader.__version__, )) + +if args["auth"]: + client = Client(args[""], args[""]) + if args["--server"]: + client.base_url = args["--server"] + url = client.connect() + parts = urlparse.urlparse(url) + qs = urlparse.parse_qs(parts.query) + oauth_token = qs["oauth_token"][0] + print "Please Visit: %s" % (url, ) + verifier = raw_input("Paste Verifier Code: ") + client.verify(oauth_token, verifier) + auth_tokens = { + "consumer_key": client.consumer_key, + "consumer_secret": client.consumer_secret, + "oauth_token": client.oauth_token, + "oauth_secret": client.oauth_secret, + } + with open(args[""], "w") as fp: + json.dump(auth_tokens, fp) +elif args["upload"]: + auth_tokens = {} + with open(args[""]) as fp: + auth_tokens = json.load(fp) + client = Client(**auth_tokens) + if args["--server"]: + client.base_url = args["--server"] + for settings_filename, model in swuploader.get_models(args["--models"]): + print "Uploading Model From: %s" % (settings_filename, ) + results = swuploader.upload_model(model, client) + if results: + print "Visit Model At: %s" % (results["urls"]["editModelUrl"]["address"], ) + basename = os.path.basename(settings_filename) + new_location = os.path.abspath(os.path.join(args["--out"], basename)) + shutil.move(settings_filename, new_location) + else: + print "Model Upload Failed" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..aab3950 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +shapeways==1.0.0 +docopt==0.6.2 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..2bc2ad3 --- /dev/null +++ b/setup.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +from setuptools import setup, find_packages + +from swuploader import __version__ + +setup( + name='swuploader', + version=__version__, + author='Brett Langdon', + author_email='brett@blangdon.com', + packages=find_packages(), + install_requires=[ + 'shapeways==1.0.0', + ], + scripts=[ + './bin/swuploader', + ], + setup_requires=[], + description='Easy to up model uploader for Shapeways.com', + url='http://github.com/brettlangdon/swuploader', +) diff --git a/swuploader.py b/swuploader.py new file mode 100644 index 0000000..7e9f752 --- /dev/null +++ b/swuploader.py @@ -0,0 +1,61 @@ +import base64 +import glob +import os + +__version__ = "0.1.0" + + +def parse_settings(settings_filename): + settings_filename = os.path.abspath(settings_filename) + name, ext = os.path.splitext(os.path.basename(settings_filename)) + settings = { + "name": name, + "hasRightsToModel": True, + "acceptTermsAndConditions": True, + "materials": {}, + } + with open(settings_filename) as fp: + for line in fp: + name, _, value = line.strip("\r\n").partition("=") + + if value.lower() == "false": + value = False + elif value.lower() == "true": + value = True + + if name == "file": + dirname = os.path.dirname(settings_filename) + value = os.path.abspath(os.path.join(dirname, value)) + filename = os.path.basename(value) + settings["fileName"] = filename + elif name.startswith("material."): + _, _, mat_id = name.partition(".") + settings["materials"][int(mat_id)] = { + "markup": float(value), + "isActive": True, + } + elif name == "categories": + value = [int(c) for c in value.split(",")] + elif name == "tags": + value = [t for t in value.split(",")] + + settings[name] = value + return settings_filename, settings + + +def get_models(dir_name=None): + if dir_name is None: + dir_name = os.getcwd() + pattern = "%s/*.ini" % (dir_name, ) + for settings_filename in glob.iglob(pattern): + yield parse_settings(settings_filename) + + +def upload_model(settings, client): + if not settings.get("file"): + return False + + with open(settings["file"]) as fp: + settings["file"] = base64.b64encode(fp.read()) + + return client.add_model(settings)