Renamed OpenPhoto to Trovebox

This commit is contained in:
sneakypete81 2013-07-19 17:46:30 +01:00
parent 57b593d245
commit 6f70330994
30 changed files with 368 additions and 365 deletions

View file

@ -5,12 +5,13 @@ OpenPhoto/Trovebox Python Library
:alt: Build Status
:target: https://travis-ci.org/photo/openphoto-python
.. image:: https://pypip.in/v/openphoto/badge.png
.. image:: https://pypip.in/v/trovebox/badge.png
:alt: Python Package Index (PyPI)
:target: https://pypi.python.org/pypi/openphoto
:target: https://pypi.python.org/pypi/trovebox
This library works with any `OpenPhoto <https://github.com/photo>`__ server
(including the `Trovebox <http://trovebox.com>`__ hosted service).
This library works with any Trovebox server, either
`Self-hosted <https://github.com/photo>`__, or using the
`Trovebox hosted service<http://trovebox.com>`__).
It provides full access to your photos and metadata, via a simple
Pythonic API.
@ -18,11 +19,11 @@ Installation
============
::
pip install openphoto
pip install trovebox
Documentation
=============
See the `OpenPhoto/Trovebox API Documentation <https://trovebox.com/documentation>`__
See the `Trovebox API Documentation <https://trovebox.com/documentation>`__
for full API documentation, including Python examples.
All development takes place at the `openphoto-python GitHub site <https://github.com/photo/openphoto-python>`__.
@ -30,9 +31,9 @@ All development takes place at the `openphoto-python GitHub site <https://github
Credentials
===========
For full access to your photos, you need to create the following config
file in ``~/.config/openphoto/default``::
file in ``~/.config/trovebox/default``::
# ~/.config/openphoto/default
# ~/.config/trovebox/default
host = your.host.com
consumerKey = your_consumer_key
consumerSecret = your_consumer_secret
@ -52,14 +53,14 @@ Using the library
=================
::
from openphoto import OpenPhoto
client = OpenPhoto()
from trovebox import Trovebox
client = Trovebox()
photos = client.photos.list()
photos[0].update(tags=["tag1", "tag2"])
print(photos[0].tags)
The OpenPhoto Python class hierarchy mirrors the
`OpenPhoto/Trovebox API <https://trovebox.com/documentation>`__ endpoint layout.
The Trovebox Python class hierarchy mirrors the
`Trovebox API <https://trovebox.com/documentation>`__ endpoint layout.
For example, the calls in the example above use the following API endpoints:
* ``client.photos.list() -> /photos/list.json``
@ -72,22 +73,22 @@ You can also access the API at a lower level using GET/POST methods::
API Versioning
==============
It may be useful to lock your application to a particular version of the OpenPhoto API.
It may be useful to lock your application to a particular version of the Trovebox API.
This ensures that future API updates won't cause unexpected breakages.
To do this, add the optional ``api_version`` parameter when creating the client object::
from openphoto import OpenPhoto
client = OpenPhoto(api_version=2)
from trovebox import Trovebox
client = Trovebox(api_version=2)
Commandline Tool
================
You can run commands to the OpenPhoto API from your shell!
You can run commands to the Trovebox API from your shell!
These are the options you can pass to the shell program::
--help # Display help text
-c config_file # Either the name of a config file in ~/.config/openphoto/ or a full path to a config file
-c config_file # Either the name of a config file in ~/.config/trovebox/ or a full path to a config file
-h hostname # Overrides config_file for unauthenticated API calls
-e endpoint # [default=/photos/list.json]
-X method # [default=GET]
@ -98,9 +99,9 @@ These are the options you can pass to the shell program::
Commandline Examples
--------------------
Upload a public photo to the host specified in ```~/.config/openphoto/default```::
Upload a public photo to the host specified in ```~/.config/trovebox/default```::
openphoto -p -X POST -e /photo/upload.json -F 'photo=@/path/to/photo/jpg' -F 'permission=1'
trovebox -p -X POST -e /photo/upload.json -F 'photo=@/path/to/photo/jpg' -F 'permission=1'
{
"code":201,
"message":"Photo 1eo uploaded successfully",
@ -112,9 +113,9 @@ Upload a public photo to the host specified in ```~/.config/openphoto/default```
}
}
Get a thumbnail URL from current.openphoto.me (unauthenticated access)::
Get a thumbnail URL from current.trovebox.com (unauthenticated access)::
openphoto -h current.openphoto.me -p -e /photo/62/view.json -F 'returnSizes=20x20'
openphoto -h current.trovebox.com -p -e /photo/62/view.json -F 'returnSizes=20x20'
{
"code":200,
"message":"Photo 62",
@ -125,11 +126,11 @@ Get a thumbnail URL from current.openphoto.me (unauthenticated access)::
],
...
...
"path20x20":"http://current.openphoto.me/photo/62/create/36c0a/20x20.jpg",
"pathBase":"http://awesomeness.openphoto.me/base/201203/7ae997-Boracay-Philippines-007.jpg",
"path20x20":"http://current.trovebox.com/photo/62/create/36c0a/20x20.jpg",
"pathBase":"http://awesomeness.trovebox.com/base/201203/7ae997-Boracay-Philippines-007.jpg",
"permission":"1",
"photo20x20":[
"http://current.openphoto.me/photo/62/create/36c0a/20x20.jpg",
"http://current.trovebox.com/photo/62/create/36c0a/20x20.jpg",
13,
20
],

View file

@ -1,4 +0,0 @@
#!/usr/bin/env python
import openphoto.main
openphoto.main.main()

4
bin/trovebox Executable file
View file

@ -0,0 +1,4 @@
#!/usr/bin/env python
import trovebox.main
trovebox.main.main()

View file

@ -1,35 +0,0 @@
from openphoto.openphoto_http import OpenPhotoHttp
from openphoto.errors import *
from openphoto._version import __version__
import openphoto.api_photo
import openphoto.api_tag
import openphoto.api_album
LATEST_API_VERSION = 2
class OpenPhoto(OpenPhotoHttp):
"""
Client library for OpenPhoto
If no parameters are specified, config is loaded from the default
location (~/.config/openphoto/default).
The config_file parameter is used to specify an alternate config file.
If the host parameter is specified, no config file is loaded and
OAuth tokens (consumer*, token*) can optionally be specified.
All requests will include the api_version path, if specified.
This should be used to ensure that your application will continue to work
even if the OpenPhoto API is updated to a new revision.
"""
def __init__(self, config_file=None, host=None,
consumer_key='', consumer_secret='',
token='', token_secret='',
api_version=None):
OpenPhotoHttp.__init__(self, config_file, host,
consumer_key, consumer_secret,
token, token_secret, api_version)
self.photos = openphoto.api_photo.ApiPhotos(self)
self.photo = openphoto.api_photo.ApiPhoto(self)
self.tags = openphoto.api_tag.ApiTags(self)
self.tag = openphoto.api_tag.ApiTag(self)
self.albums = openphoto.api_album.ApiAlbums(self)
self.album = openphoto.api_album.ApiAlbum(self)

View file

@ -9,28 +9,28 @@ tput setaf 3
echo
echo "Testing latest self-hosted site..."
tput sgr0
export OPENPHOTO_TEST_CONFIG=test
unset OPENPHOTO_TEST_SERVER_API
export TROVEBOX_TEST_CONFIG=test
unset TROVEBOX_TEST_SERVER_API
python -m unittest discover --catch tests/functional
# Test server running APIv1 OpenPhoto instance
# Test server running APIv1 Trovebox instance
# Install from photo/frontend commit 660b2ab
tput setaf 3
echo
echo "Testing APIv1 self-hosted site..."
tput sgr0
export OPENPHOTO_TEST_CONFIG=test-apiv1
export OPENPHOTO_TEST_SERVER_API=1
export TROVEBOX_TEST_CONFIG=test-apiv1
export TROVEBOX_TEST_SERVER_API=1
python -m unittest discover --catch tests/functional
# Test server running v3.0.8 OpenPhoto instance
# Test server running v3.0.8 Trovebox instance
# Install from photo/frontend commit e9d81de57b
tput setaf 3
echo
echo "Testing v3.0.8 self-hosted site..."
tput sgr0
export OPENPHOTO_TEST_CONFIG=test-3.0.8
unset OPENPHOTO_TEST_SERVER_API
export TROVEBOX_TEST_CONFIG=test-3.0.8
unset TROVEBOX_TEST_SERVER_API
python -m unittest discover --catch tests/functional
# Test account on hosted trovebox.com site
@ -38,7 +38,7 @@ tput setaf 3
echo
echo "Testing latest hosted site..."
tput sgr0
export OPENPHOTO_TEST_CONFIG=test-hosted
unset OPENPHOTO_TEST_SERVER_API
export TROVEBOX_TEST_CONFIG=test-hosted
unset TROVEBOX_TEST_SERVER_API
python -m unittest discover --catch tests/functional

View file

@ -4,11 +4,11 @@ import sys
requires = ['requests', 'requests_oauthlib']
console_script = """[console_scripts]
openphoto = openphoto.main:main
trovebox = trovebox.main:main
"""
# from openphoto._version import __version__
exec(open("openphoto/_version.py").read())
# from trovebox._version import __version__
exec(open("trovebox/_version.py").read())
# Check the Python version
(major, minor) = sys.version_info[:2]
@ -23,18 +23,19 @@ try:
}
except ImportError:
from distutils.core import setup
kw = {'scripts': ['bin/openphoto'],
kw = {'scripts': ['bin/trovebox'],
'requires': requires}
setup(name='openphoto',
setup(name='trovebox',
version=__version__,
description='The official Python client library for Trovebox/OpenPhoto',
description='The official Python client library for the Trovebox photo service',
long_description=open("README.rst").read(),
author='Pete Burgers, James Walker',
url='https://github.com/photo/openphoto-python',
packages=['openphoto'],
packages=['trovebox'],
data_files=['README.rst'],
keywords=['openphoto', 'pyopenphoto', 'openphoto-python', 'trovebox'],
keywords=['openphoto', 'pyopenphoto', 'openphoto-python',
'trovebox', 'pytrovebox', 'openphoto-trovebox'],
classifiers=['Development Status :: 4 - Beta',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',

View file

@ -1,4 +1,4 @@
OpenPhoto/Trovebox Python Testing
Trovebox Python Testing
=======================
###Unit Tests
@ -26,8 +26,8 @@ To run the unit tests against all supported Python versions, use ```tox```:
###Functional Tests
The functional tests check that the openphoto-python library interoperates
correctly with a real OpenPhoto/Trovebox server.
The functional tests check that the Trovebox python library interoperates
correctly with a real Trovebox server.
They are slow to run and rely on a stable HTTP connection to a test server.

View file

@ -1,34 +1,34 @@
Functional Testing
=======================
These functional tests check that the openphoto-python library interoperates
correctly with a real OpenPhoto/Trovebox server.
These functional tests check that the Trovebox python library interoperates
correctly with a real Trovebox server.
They are slow to run, and require a stable HTTP connection to a test server.
----------------------------------------
<a name="requirements"></a>
### Requirements
A computer, Python and an empty OpenPhoto/Trovebox test host.
A computer, Python and an empty Trovebox test host.
---------------------------------------
<a name="setup"></a>
### Setting up
Create a ``~/.config/openphoto/test`` config file containing the following:
Create a ``~/.config/trovebox/test`` config file containing the following:
# ~/.config/openphoto/test
# ~/.config/trovebox/test
host = your.host.com
consumerKey = your_consumer_key
consumerSecret = your_consumer_secret
token = your_access_token
tokenSecret = your_access_token_secret
Make sure this is an empty test server, **not a production OpenPhoto server!!!**
Make sure this is an empty test server, **not a production Trovebox server!!!**
You can specify an alternate test config file with the following environment variable:
export OPENPHOTO_TEST_CONFIG=test2
export TROVEBOX_TEST_CONFIG=test2
---------------------------------------
<a name="running"></a>
@ -37,28 +37,28 @@ You can specify an alternate test config file with the following environment var
The following instructions are for Python 2.7. You can adapt them for earlier
Python versions using the ``unittest2`` package.
cd /path/to/openphoto-python
cd /path/to/trovebox-python
python -m unittest discover -c tests/functional
The "-c" lets you stop the tests gracefully with \[CTRL\]-c.
The easiest way to run a subset of the tests is with the ``nose`` package:
cd /path/to/openphoto-python
cd /path/to/trovebox-python
nosetests -v -s --nologcapture tests/functional/test_albums.py:TestAlbums.test_view
All HTTP requests and responses are recorded in the file ``tests.log``.
You can enable more verbose output to stdout with the following environment variable:
export OPENPHOTO_TEST_DEBUG=1
export TROVEBOX_TEST_DEBUG=1
---------------------------------------
<a name="test_details"></a>
### Test Details
These tests are intended to verify the openphoto-python library.
They don't provide comprehensive testing of the OpenPhoto API,
These tests are intended to verify the Trovebox python library.
They don't provide comprehensive testing of the Trovebox API,
there are PHP unit tests for that.
Each test class is run as follows:
@ -84,11 +84,11 @@ Remove all photos, tags and albums
By default, all currently supported API versions will be tested.
It's useful to test servers that only support older API versions.
To restrict the testing to a specific maximum API version, use the
``OPENPHOTO_TEST_SERVER_API`` environment variable.
``TROVEBOX_TEST_SERVER_API`` environment variable.
For example, to restrict testing to APIv1 and APIv2:
export OPENPHOTO_TEST_SERVER_API=2
export TROVEBOX_TEST_SERVER_API=2
<a name="full_regression"></a>
### Full Regression Test
@ -96,7 +96,7 @@ For example, to restrict testing to APIv1 and APIv2:
The ``run_functional_tests`` script runs all functional tests against
all supported API versions.
To use it, you must set up multiple OpenPhoto instances and create the following
To use it, you must set up multiple Trovebox instances and create the following
config files containing your credentials:
test : Latest self-hosted site (from photo/frontend master branch)

View file

@ -29,7 +29,7 @@ class TestAlbums(test_base.TestBase):
def test_update(self):
""" Test that an album can be updated """
# Update the album using the OpenPhoto class,
# Update the album using the Trovebox class,
# passing in the album object
new_name = "New Name"
self.client.album.update(self.albums[0], name=new_name)
@ -38,7 +38,7 @@ class TestAlbums(test_base.TestBase):
self.albums = self.client.albums.list()
self.assertEqual(self.albums[0].name, new_name)
# Update the album using the OpenPhoto class, passing in the album id
# Update the album using the Trovebox class, passing in the album id
new_name = "Another New Name"
self.client.album.update(self.albums[0].id, name=new_name)

View file

@ -7,11 +7,11 @@ try:
except ImportError:
import unittest
import openphoto
import trovebox
def get_test_server_api():
return int(os.getenv("OPENPHOTO_TEST_SERVER_API",
openphoto.LATEST_API_VERSION))
return int(os.getenv("TROVEBOX_TEST_SERVER_API",
trovebox.LATEST_API_VERSION))
class TestBase(unittest.TestCase):
TEST_TITLE = "Test Image - delete me!"
@ -21,8 +21,8 @@ class TestBase(unittest.TestCase):
testcase_name = "(unknown testcase)"
api_version = None
config_file = os.getenv("OPENPHOTO_TEST_CONFIG", "test")
debug = (os.getenv("OPENPHOTO_TEST_DEBUG", "0") == "1")
config_file = os.getenv("TROVEBOX_TEST_CONFIG", "test")
debug = (os.getenv("TROVEBOX_TEST_DEBUG", "0") == "1")
def __init__(self, *args, **kwds):
super(TestBase, self).__init__(*args, **kwds)
@ -42,7 +42,7 @@ class TestBase(unittest.TestCase):
else:
print("\nTesting %s v%d" % (cls.testcase_name, cls.api_version))
cls.client = openphoto.OpenPhoto(config_file=cls.config_file,
cls.client = trovebox.Trovebox(config_file=cls.config_file,
api_version=cls.api_version)
if cls.client.photos.list() != []:

View file

@ -1,6 +1,6 @@
import logging
import openphoto
import trovebox
from tests.functional import test_base
class TestFramework(test_base.TestBase):
@ -16,7 +16,7 @@ class TestFramework(test_base.TestBase):
"""
API v0 has a special hello world message
"""
client = openphoto.OpenPhoto(config_file=self.config_file,
client = trovebox.Trovebox(config_file=self.config_file,
api_version=0)
result = client.get("hello.json")
self.assertEqual(result['message'],
@ -28,7 +28,7 @@ class TestFramework(test_base.TestBase):
For all API versions >0, we get a generic hello world message
"""
for api_version in range(1, test_base.get_test_server_api() + 1):
client = openphoto.OpenPhoto(config_file=self.config_file,
client = trovebox.Trovebox(config_file=self.config_file,
api_version=api_version)
result = client.get("hello.json")
self.assertEqual(result['message'], "Hello, world!")
@ -40,7 +40,7 @@ class TestFramework(test_base.TestBase):
If the API version is unspecified,
we get a generic hello world message.
"""
client = openphoto.OpenPhoto(config_file=self.config_file,
client = trovebox.Trovebox(config_file=self.config_file,
api_version=None)
result = client.get("hello.json")
self.assertEqual(result['message'], "Hello, world!")
@ -51,7 +51,8 @@ class TestFramework(test_base.TestBase):
If the API version is unsupported, we should get an error
(ValueError, since the returned 404 HTML page is not valid JSON)
"""
client = openphoto.OpenPhoto(config_file=self.config_file,
api_version=openphoto.LATEST_API_VERSION + 1)
with self.assertRaises(openphoto.OpenPhoto404Error):
version = trovebox.LATEST_API_VERSION + 1
client = trovebox.Trovebox(config_file=self.config_file,
api_version=version)
with self.assertRaises(trovebox.Trovebox404Error):
client.get("hello.json")

View file

@ -1,6 +1,6 @@
from __future__ import unicode_literals
import openphoto
import trovebox
from tests.functional import test_base
class TestPhotos(test_base.TestBase):
@ -8,9 +8,9 @@ class TestPhotos(test_base.TestBase):
def test_delete_upload(self):
""" Test photo deletion and upload """
# Delete one photo using the OpenPhoto class, passing in the id
# Delete one photo using the Trovebox class, passing in the id
self.assertTrue(self.client.photo.delete(self.photos[0].id))
# Delete one photo using the OpenPhoto class, passing in the object
# Delete one photo using the Trovebox class, passing in the object
self.assertTrue(self.client.photo.delete(self.photos[1]))
# And another using the Photo object directly
self.assertTrue(self.photos[2].delete())
@ -49,7 +49,7 @@ class TestPhotos(test_base.TestBase):
def test_edit(self):
""" Check that the edit request returns an HTML form """
# Test using the OpenPhoto class
# Test using the Trovebox class
html = self.client.photo.edit(self.photos[0])
self.assertIn("<form", html.lower())
@ -60,7 +60,7 @@ class TestPhotos(test_base.TestBase):
def test_upload_duplicate(self):
""" Ensure that duplicate photos are rejected """
# Attempt to upload a duplicate
with self.assertRaises(openphoto.OpenPhotoDuplicateError):
with self.assertRaises(trovebox.TroveboxDuplicateError):
self.client.photo.upload("tests/data/test_photo1.jpg",
title=self.TEST_TITLE)
@ -75,7 +75,7 @@ class TestPhotos(test_base.TestBase):
photo = self.photos[0]
self.assertNotEqual(photo.title, title)
# Add the title to a photo using the OpenPhoto class
# Add the title to a photo using the Trovebox class
ret_val = self.client.photo.update(photo, title=title)
# Check that it's there
@ -117,7 +117,7 @@ class TestPhotos(test_base.TestBase):
self.assertFalse(hasattr(photo, "path9x9"))
self.assertFalse(hasattr(photo, "path19x19"))
# View at a particular size using the OpenPhoto class
# View at a particular size using the Trovebox class
photo = self.client.photo.view(photo, returnSizes="9x9")
self.assertTrue(hasattr(photo, "path9x9"))

View file

@ -52,8 +52,8 @@ class TestTags(test_base.TestBase):
"since there are no fields that can be updated")
def test_update(self):
""" Test that a tag can be updated """
# Update the tag using the OpenPhoto class, passing in the tag object
owner = "test1@openphoto.me"
# Update the tag using the Trovebox class, passing in the tag object
owner = "test1@trovebox.com"
ret_val = self.client.tag.update(self.tags[0], owner=owner)
# Check that the tag is updated
@ -61,8 +61,8 @@ class TestTags(test_base.TestBase):
self.assertEqual(self.tags[0].owner, owner)
self.assertEqual(ret_val.owner, owner)
# Update the tag using the OpenPhoto class, passing in the tag id
owner = "test2@openphoto.me"
# Update the tag using the Trovebox class, passing in the tag id
owner = "test2@trovebox.com"
ret_val = self.client.tag.update(self.TEST_TAG, owner=owner)
# Check that the tag is updated
@ -71,7 +71,7 @@ class TestTags(test_base.TestBase):
self.assertEqual(ret_val.owner, owner)
# Update the tag using the Tag object directly
owner = "test3@openphoto.me"
owner = "test3@trovebox.com"
ret_val = self.tags[0].update(owner=owner)
# Check that the tag is updated

View file

@ -5,7 +5,7 @@ try:
except ImportError:
import unittest
import openphoto
import trovebox
class TestAlbums(unittest.TestCase):
test_host = "test.example.com"
@ -18,8 +18,8 @@ class TestAlbums(unittest.TestCase):
"name": "Album 2",
"totalRows": 2}]
def setUp(self):
self.client = openphoto.OpenPhoto(host=self.test_host)
self.test_albums = [openphoto.objects.Album(self.client, album)
self.client = trovebox.Trovebox(host=self.test_host)
self.test_albums = [trovebox.objects.Album(self.client, album)
for album in self.test_albums_dict]
@staticmethod
@ -27,7 +27,7 @@ class TestAlbums(unittest.TestCase):
return {"message": message, "code": code, "result": result}
class TestAlbumsList(TestAlbums):
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_albums_list(self, mock_get):
"""Check that the album list is returned correctly"""
mock_get.return_value = self._return_value(self.test_albums_dict)
@ -39,7 +39,7 @@ class TestAlbumsList(TestAlbums):
self.assertEqual(result[1].id, "2")
self.assertEqual(result[1].name, "Album 2")
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_albums_list_returns_cover_photos(self, mock_get):
"""Check that the album list returns cover photo objects"""
mock_get.return_value = self._return_value(self.test_albums_dict)
@ -56,7 +56,7 @@ class TestAlbumsList(TestAlbums):
self.assertEqual(result[1].cover.tags, ["tag3", "tag4"])
class TestAlbumCreate(TestAlbums):
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_album_create(self, mock_post):
"""Check that an album can be created"""
mock_post.return_value = self._return_value(self.test_albums_dict[0])
@ -69,7 +69,7 @@ class TestAlbumCreate(TestAlbums):
self.assertEqual(result.cover.tags, ["tag1", "tag2"])
class TestAlbumDelete(TestAlbums):
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_album_delete(self, mock_post):
"""Check that an album can be deleted"""
mock_post.return_value = self._return_value(True)
@ -77,7 +77,7 @@ class TestAlbumDelete(TestAlbums):
mock_post.assert_called_with("/album/1/delete.json")
self.assertEqual(result, True)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_album_delete_id(self, mock_post):
"""Check that an album can be deleted using its ID"""
mock_post.return_value = self._return_value(True)
@ -85,14 +85,14 @@ class TestAlbumDelete(TestAlbums):
mock_post.assert_called_with("/album/1/delete.json")
self.assertEqual(result, True)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_album_delete_failure(self, mock_post):
"""Check that an exception is raised if an album cannot be deleted"""
mock_post.return_value = self._return_value(False)
with self.assertRaises(openphoto.OpenPhotoError):
with self.assertRaises(trovebox.TroveboxError):
self.client.album.delete(self.test_albums[0])
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_album_object_delete(self, mock_post):
"""Check that an album can be deleted using the album object directly"""
mock_post.return_value = self._return_value(True)
@ -104,76 +104,76 @@ class TestAlbumDelete(TestAlbums):
self.assertEqual(album.id, None)
self.assertEqual(album.name, None)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_album_object_delete_failure(self, mock_post):
"""
Check that an exception is raised if an album cannot be deleted
when using the album object directly
"""
mock_post.return_value = self._return_value(False)
with self.assertRaises(openphoto.OpenPhotoError):
with self.assertRaises(trovebox.TroveboxError):
self.test_albums[0].delete()
class TestAlbumForm(TestAlbums):
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_album_form(self, _):
""" If album.form gets implemented, write a test! """
with self.assertRaises(NotImplementedError):
self.client.album.form(self.test_albums[0])
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_album_form_id(self, _):
""" If album.form gets implemented, write a test! """
with self.assertRaises(NotImplementedError):
self.client.album.form("1")
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_album_object_form(self, _):
""" If album.form gets implemented, write a test! """
with self.assertRaises(NotImplementedError):
self.test_albums[0].form()
class TestAlbumAddPhotos(TestAlbums):
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_album_add_photos(self, _):
""" If album.add_photos gets implemented, write a test! """
with self.assertRaises(NotImplementedError):
self.client.album.add_photos(self.test_albums[0], ["Photo Objects"])
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_album_add_photos_id(self, _):
""" If album.add_photos gets implemented, write a test! """
with self.assertRaises(NotImplementedError):
self.client.album.add_photos("1", ["Photo Objects"])
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_album_object_add_photos(self, _):
""" If album.add_photos gets implemented, write a test! """
with self.assertRaises(NotImplementedError):
self.test_albums[0].add_photos(["Photo Objects"])
class TestAlbumRemovePhotos(TestAlbums):
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_album_remove_photos(self, _):
""" If album.remove_photos gets implemented, write a test! """
with self.assertRaises(NotImplementedError):
self.client.album.remove_photos(self.test_albums[0],
["Photo Objects"])
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_album_remove_photos_id(self, _):
""" If album.remove_photos gets implemented, write a test! """
with self.assertRaises(NotImplementedError):
self.client.album.remove_photos("1", ["Photo Objects"])
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_album_object_remove_photos(self, _):
""" If album.remove_photos gets implemented, write a test! """
with self.assertRaises(NotImplementedError):
self.test_albums[0].remove_photos(["Photo Objects"])
class TestAlbumUpdate(TestAlbums):
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_album_update(self, mock_post):
"""Check that an album can be updated"""
mock_post.return_value = self._return_value(self.test_albums_dict[1])
@ -184,7 +184,7 @@ class TestAlbumUpdate(TestAlbums):
self.assertEqual(result.cover.id, "2b")
self.assertEqual(result.cover.tags, ["tag3", "tag4"])
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_album_update_id(self, mock_post):
"""Check that an album can be updated using its ID"""
mock_post.return_value = self._return_value(self.test_albums_dict[1])
@ -195,7 +195,7 @@ class TestAlbumUpdate(TestAlbums):
self.assertEqual(result.cover.id, "2b")
self.assertEqual(result.cover.tags, ["tag3", "tag4"])
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_album_object_update(self, mock_post):
"""Check that an album can be updated using the album object directly"""
mock_post.return_value = self._return_value(self.test_albums_dict[1])
@ -208,7 +208,7 @@ class TestAlbumUpdate(TestAlbums):
self.assertEqual(album.cover.tags, ["tag3", "tag4"])
class TestAlbumView(TestAlbums):
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_album_view(self, mock_get):
"""Check that an album can be viewed"""
mock_get.return_value = self._return_value(self.test_albums_dict[1])
@ -219,7 +219,7 @@ class TestAlbumView(TestAlbums):
self.assertEqual(result.cover.id, "2b")
self.assertEqual(result.cover.tags, ["tag3", "tag4"])
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_album_view_id(self, mock_get):
"""Check that an album can be viewed using its ID"""
mock_get.return_value = self._return_value(self.test_albums_dict[1])
@ -230,7 +230,7 @@ class TestAlbumView(TestAlbums):
self.assertEqual(result.cover.id, "2b")
self.assertEqual(result.cover.tags, ["tag3", "tag4"])
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_album_object_view(self, mock_get):
"""Check that an album can be viewed using the album object directly"""
mock_get.return_value = self._return_value(self.test_albums_dict[1])

View file

@ -11,8 +11,8 @@ try:
except ImportError:
import unittest
import openphoto
from openphoto.main import main
import trovebox
from trovebox.main import main
class TestException(Exception):
pass
@ -23,53 +23,53 @@ def raise_exception(_):
class TestCli(unittest.TestCase):
test_file = os.path.join("tests", "unit", "data", "test_file.txt")
@mock.patch.object(openphoto.main.openphoto, "OpenPhoto")
@mock.patch.object(trovebox.main.trovebox, "Trovebox")
@mock.patch('sys.stdout', new_callable=io.StringIO)
def test_defaults(self, _, mock_openphoto):
def test_defaults(self, _, mock_trovebox):
"""Check that the default behaviour is correct"""
get = mock_openphoto.return_value.get
get = mock_trovebox.return_value.get
main([])
mock_openphoto.assert_called_with(config_file=None)
mock_trovebox.assert_called_with(config_file=None)
get.assert_called_with("/photos/list.json", process_response=False)
@mock.patch.object(openphoto.main.openphoto, "OpenPhoto")
@mock.patch.object(trovebox.main.trovebox, "Trovebox")
@mock.patch('sys.stdout', new_callable=io.StringIO)
def test_config(self, _, mock_openphoto):
def test_config(self, _, mock_trovebox):
"""Check that a config file can be specified"""
main(["--config=test"])
mock_openphoto.assert_called_with(config_file="test")
mock_trovebox.assert_called_with(config_file="test")
@mock.patch.object(openphoto.main.openphoto, "OpenPhoto")
@mock.patch.object(trovebox.main.trovebox, "Trovebox")
@mock.patch('sys.stdout', new_callable=io.StringIO)
def test_get(self, mock_stdout, mock_openphoto):
def test_get(self, mock_stdout, mock_trovebox):
"""Check that the get operation is working"""
get = mock_openphoto.return_value.get
get = mock_trovebox.return_value.get
get.return_value = "Result"
main(["-X", "GET", "-h", "test_host", "-e", "test_endpoint", "-F",
"field1=1", "-F", "field2=2"])
mock_openphoto.assert_called_with(host="test_host")
mock_trovebox.assert_called_with(host="test_host")
get.assert_called_with("test_endpoint", field1="1", field2="2",
process_response=False)
self.assertEqual(mock_stdout.getvalue(), "Result\n")
@mock.patch.object(openphoto.main.openphoto, "OpenPhoto")
@mock.patch.object(trovebox.main.trovebox, "Trovebox")
@mock.patch('sys.stdout', new_callable=io.StringIO)
def test_post(self, mock_stdout, mock_openphoto):
def test_post(self, mock_stdout, mock_trovebox):
"""Check that the post operation is working"""
post = mock_openphoto.return_value.post
post = mock_trovebox.return_value.post
post.return_value = "Result"
main(["-X", "POST", "-h", "test_host", "-e", "test_endpoint", "-F",
"field1=1", "-F", "field2=2"])
mock_openphoto.assert_called_with(host="test_host")
mock_trovebox.assert_called_with(host="test_host")
post.assert_called_with("test_endpoint", field1="1", field2="2",
files={}, process_response=False)
self.assertEqual(mock_stdout.getvalue(), "Result\n")
@mock.patch.object(openphoto.main.openphoto, "OpenPhoto")
@mock.patch.object(trovebox.main.trovebox, "Trovebox")
@mock.patch('sys.stdout', new_callable=io.StringIO)
def test_post_files(self, _, mock_openphoto):
def test_post_files(self, _, mock_trovebox):
"""Check that files are posted correctly"""
post = mock_openphoto.return_value.post
post = mock_trovebox.return_value.post
main(["-X", "POST", "-F", "photo=@%s" % self.test_file])
# It's not possible to directly compare the file object,
# so check it manually
@ -104,7 +104,7 @@ class TestCli(unittest.TestCase):
mock_stdout.getvalue())
self.assertIn("To get your credentials", mock_stdout.getvalue())
@mock.patch.object(openphoto.main.openphoto, "OpenPhoto")
@mock.patch.object(trovebox.main.trovebox, "Trovebox")
@mock.patch('sys.stdout', new_callable=io.StringIO)
def test_verbose(self, mock_stdout, _):
"""Check that the verbose option is working"""
@ -112,11 +112,11 @@ class TestCli(unittest.TestCase):
self.assertIn("Method: GET", mock_stdout.getvalue())
self.assertIn("Endpoint: /photos/list.json", mock_stdout.getvalue())
@mock.patch.object(openphoto.main.openphoto, "OpenPhoto")
@mock.patch.object(trovebox.main.trovebox, "Trovebox")
@mock.patch('sys.stdout', new_callable=io.StringIO)
def test_pretty_print(self, mock_stdout, mock_openphoto):
def test_pretty_print(self, mock_stdout, mock_trovebox):
"""Check that the pretty-print option is working"""
get = mock_openphoto.return_value.get
get = mock_trovebox.return_value.get
get.return_value = '{"test":1}'
main(["-p"])
self.assertEqual(mock_stdout.getvalue(), '{\n "test":1\n}\n')
@ -125,5 +125,5 @@ class TestCli(unittest.TestCase):
def test_version(self, mock_stdout):
"""Check that the version string is correctly printed"""
main(["--version"])
self.assertEqual(mock_stdout.getvalue(), openphoto.__version__ + "\n")
self.assertEqual(mock_stdout.getvalue(), trovebox.__version__ + "\n")

View file

@ -5,10 +5,10 @@ try:
except ImportError:
import unittest
from openphoto import OpenPhoto
from trovebox import Trovebox
CONFIG_HOME_PATH = os.path.join("tests", "config")
CONFIG_PATH = os.path.join(CONFIG_HOME_PATH, "openphoto")
CONFIG_PATH = os.path.join(CONFIG_HOME_PATH, "trovebox")
class TestConfig(unittest.TestCase):
def setUp(self):
@ -41,7 +41,7 @@ class TestConfig(unittest.TestCase):
def test_default_config(self):
""" Ensure the default config is loaded """
self.create_config("default", "Test Default Host")
client = OpenPhoto()
client = Trovebox()
config = client.config
self.assertEqual(client.host, "Test Default Host")
self.assertEqual(config.consumer_key, "default_consumer_key")
@ -53,7 +53,7 @@ class TestConfig(unittest.TestCase):
""" Ensure a custom config can be loaded """
self.create_config("default", "Test Default Host")
self.create_config("custom", "Test Custom Host")
client = OpenPhoto(config_file="custom")
client = Trovebox(config_file="custom")
config = client.config
self.assertEqual(client.host, "Test Custom Host")
self.assertEqual(config.consumer_key, "custom_consumer_key")
@ -65,7 +65,7 @@ class TestConfig(unittest.TestCase):
""" Ensure a full custom config path can be loaded """
self.create_config("path", "Test Path Host")
full_path = os.path.abspath(CONFIG_PATH)
client = OpenPhoto(config_file=os.path.join(full_path, "path"))
client = Trovebox(config_file=os.path.join(full_path, "path"))
config = client.config
self.assertEqual(client.host, "Test Path Host")
self.assertEqual(config.consumer_key, "path_consumer_key")
@ -76,7 +76,7 @@ class TestConfig(unittest.TestCase):
def test_host_override(self):
""" Ensure that specifying a host overrides the default config """
self.create_config("default", "Test Default Host")
client = OpenPhoto(host="host_override")
client = Trovebox(host="host_override")
config = client.config
self.assertEqual(config.host, "host_override")
self.assertEqual(config.consumer_key, "")
@ -87,14 +87,14 @@ class TestConfig(unittest.TestCase):
def test_missing_config_files(self):
""" Ensure that missing config files raise exceptions """
with self.assertRaises(IOError):
OpenPhoto()
Trovebox()
with self.assertRaises(IOError):
OpenPhoto(config_file="custom")
Trovebox(config_file="custom")
def test_host_and_config_file(self):
""" It's not valid to specify both a host and a config_file """
self.create_config("custom", "Test Custom Host")
with self.assertRaises(ValueError):
OpenPhoto(config_file="custom", host="host_override")
Trovebox(config_file="custom", host="host_override")

View file

@ -7,7 +7,7 @@ try:
except ImportError:
import unittest
import openphoto
import trovebox
class TestHttp(unittest.TestCase):
test_host = "test.example.com"
@ -24,7 +24,7 @@ class TestHttp(unittest.TestCase):
def setUp(self):
self.client = openphoto.OpenPhoto(host=self.test_host,
self.client = trovebox.Trovebox(host=self.test_host,
**self.test_oauth)
def _register_uri(self, method, uri=test_uri, data=None, body=None,
@ -78,7 +78,7 @@ class TestHttp(unittest.TestCase):
@httpretty.activate
def test_get_without_oauth(self):
"""Check that the get method works without OAuth parameters"""
self.client = openphoto.OpenPhoto(host=self.test_host)
self.client = trovebox.Trovebox(host=self.test_host)
self._register_uri(httpretty.GET)
response = self.client.get(self.test_endpoint)
self.assertNotIn("authorization", self._last_request().headers)
@ -87,9 +87,9 @@ class TestHttp(unittest.TestCase):
@httpretty.activate
def test_post_without_oauth(self):
"""Check that the post method fails without OAuth parameters"""
self.client = openphoto.OpenPhoto(host=self.test_host)
self.client = trovebox.Trovebox(host=self.test_host)
self._register_uri(httpretty.POST)
with self.assertRaises(openphoto.OpenPhotoError):
with self.assertRaises(trovebox.TroveboxError):
self.client.post(self.test_endpoint)
@httpretty.activate
@ -110,9 +110,9 @@ class TestHttp(unittest.TestCase):
def test_get_parameter_processing(self):
"""Check that the parameter processing function is working"""
self._register_uri(httpretty.GET)
photo = openphoto.objects.Photo(None, {"id": "photo_id"})
album = openphoto.objects.Album(None, {"id": "album_id"})
tag = openphoto.objects.Tag(None, {"id": "tag_id"})
photo = trovebox.objects.Photo(None, {"id": "photo_id"})
album = trovebox.objects.Album(None, {"id": "album_id"})
tag = trovebox.objects.Tag(None, {"id": "tag_id"})
self.client.get(self.test_endpoint,
photo=photo, album=album, tag=tag,
list_=[photo, album, tag],
@ -129,7 +129,7 @@ class TestHttp(unittest.TestCase):
@httpretty.activate
def test_get_with_api_version(self):
"""Check that an API version can be specified for the get method"""
self.client = openphoto.OpenPhoto(host=self.test_host, api_version=1)
self.client = trovebox.Trovebox(host=self.test_host, api_version=1)
self._register_uri(httpretty.GET,
uri="http://%s/v1/%s" % (self.test_host,
self.test_endpoint))
@ -138,7 +138,7 @@ class TestHttp(unittest.TestCase):
@httpretty.activate
def test_post_with_api_version(self):
"""Check that an API version can be specified for the post method"""
self.client = openphoto.OpenPhoto(host=self.test_host, api_version=1,
self.client = trovebox.Trovebox(host=self.test_host, api_version=1,
**self.test_oauth)
self._register_uri(httpretty.POST,
uri="http://%s/v1/%s" % (self.test_host,

View file

@ -11,7 +11,7 @@ try:
except ImportError:
import unittest
import openphoto
import trovebox
class TestHttpErrors(unittest.TestCase):
test_host = "test.example.com"
@ -26,7 +26,7 @@ class TestHttpErrors(unittest.TestCase):
"token_secret": "dummy"}
def setUp(self):
self.client = openphoto.OpenPhoto(host=self.test_host,
self.client = trovebox.Trovebox(host=self.test_host,
**self.test_oauth)
def _register_uri(self, method, uri=test_uri,
@ -48,7 +48,7 @@ class TestHttpErrors(unittest.TestCase):
to raise an exception
"""
self._register_uri(httpretty.GET, status=500)
with self.assertRaises(openphoto.OpenPhotoError):
with self.assertRaises(trovebox.TroveboxError):
self.client.get(self.test_endpoint)
@httpretty.activate
@ -58,7 +58,7 @@ class TestHttpErrors(unittest.TestCase):
to raise an exception
"""
self._register_uri(httpretty.POST, status=500)
with self.assertRaises(openphoto.OpenPhotoError):
with self.assertRaises(trovebox.TroveboxError):
self.client.post(self.test_endpoint)
@httpretty.activate
@ -68,7 +68,7 @@ class TestHttpErrors(unittest.TestCase):
to raise a 404 exception
"""
self._register_uri(httpretty.GET, status=404)
with self.assertRaises(openphoto.OpenPhoto404Error):
with self.assertRaises(trovebox.Trovebox404Error):
self.client.get(self.test_endpoint)
@httpretty.activate
@ -78,7 +78,7 @@ class TestHttpErrors(unittest.TestCase):
to raise a 404 exception
"""
self._register_uri(httpretty.POST, status=404)
with self.assertRaises(openphoto.OpenPhoto404Error):
with self.assertRaises(trovebox.Trovebox404Error):
self.client.post(self.test_endpoint)
@httpretty.activate
@ -108,7 +108,7 @@ class TestHttpErrors(unittest.TestCase):
even with an error status is returned
"""
self._register_uri(httpretty.GET, body="Invalid JSON", status=500)
with self.assertRaises(openphoto.OpenPhotoError):
with self.assertRaises(trovebox.TroveboxError):
self.client.get(self.test_endpoint)
@httpretty.activate
@ -118,7 +118,7 @@ class TestHttpErrors(unittest.TestCase):
even with an error status is returned
"""
self._register_uri(httpretty.POST, body="Invalid JSON", status=500)
with self.assertRaises(openphoto.OpenPhotoError):
with self.assertRaises(trovebox.TroveboxError):
self.client.post(self.test_endpoint)
@httpretty.activate
@ -128,7 +128,7 @@ class TestHttpErrors(unittest.TestCase):
even with a 404 status is returned
"""
self._register_uri(httpretty.GET, body="Invalid JSON", status=404)
with self.assertRaises(openphoto.OpenPhoto404Error):
with self.assertRaises(trovebox.Trovebox404Error):
self.client.get(self.test_endpoint)
@httpretty.activate
@ -138,7 +138,7 @@ class TestHttpErrors(unittest.TestCase):
even with a 404 status is returned
"""
self._register_uri(httpretty.POST, body="Invalid JSON", status=404)
with self.assertRaises(openphoto.OpenPhoto404Error):
with self.assertRaises(trovebox.Trovebox404Error):
self.client.post(self.test_endpoint)
@httpretty.activate
@ -149,7 +149,7 @@ class TestHttpErrors(unittest.TestCase):
"""
data = {"message": "This photo already exists", "code": 409}
self._register_uri(httpretty.GET, data=data, status=409)
with self.assertRaises(openphoto.OpenPhotoDuplicateError):
with self.assertRaises(trovebox.TroveboxDuplicateError):
self.client.get(self.test_endpoint)
@httpretty.activate
@ -160,7 +160,7 @@ class TestHttpErrors(unittest.TestCase):
"""
data = {"message": "This photo already exists", "code": 409}
self._register_uri(httpretty.POST, data=data, status=409)
with self.assertRaises(openphoto.OpenPhotoDuplicateError):
with self.assertRaises(trovebox.TroveboxDuplicateError):
self.client.post(self.test_endpoint)
@httpretty.activate

View file

@ -7,7 +7,7 @@ try:
except ImportError:
import unittest
import openphoto
import trovebox
class TestPhotos(unittest.TestCase):
test_host = "test.example.com"
@ -17,8 +17,8 @@ class TestPhotos(unittest.TestCase):
{"id": "2b", "tags": ["tag3", "tag4"],
"totalPages": 1, "totalRows": 2}]
def setUp(self):
self.client = openphoto.OpenPhoto(host=self.test_host)
self.test_photos = [openphoto.objects.Photo(self.client, photo)
self.client = trovebox.Trovebox(host=self.test_host)
self.test_photos = [trovebox.objects.Photo(self.client, photo)
for photo in self.test_photos_dict]
@staticmethod
@ -26,7 +26,7 @@ class TestPhotos(unittest.TestCase):
return {"message": message, "code": code, "result": result}
class TestPhotosList(TestPhotos):
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_photos_list(self, mock_get):
"""Check that the photo list is returned correctly"""
mock_get.return_value = self._return_value(self.test_photos_dict)
@ -40,7 +40,7 @@ class TestPhotosList(TestPhotos):
self.assertEqual(result[1].tags, ["tag3", "tag4"])
class TestPhotosUpdate(TestPhotos):
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photos_update(self, mock_post):
"""Check that multiple photos can be updated"""
mock_post.return_value = self._return_value(True)
@ -49,7 +49,7 @@ class TestPhotosUpdate(TestPhotos):
ids=["1a", "2b"], title="Test")
self.assertEqual(result, True)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photos_update_ids(self, mock_post):
"""Check that multiple photos can be updated using their IDs"""
mock_post.return_value = self._return_value(True)
@ -58,18 +58,18 @@ class TestPhotosUpdate(TestPhotos):
ids=["1a", "2b"], title="Test")
self.assertEqual(result, True)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photos_update_failure(self, mock_post):
"""
Check that an exception is raised if multiple photos
cannot be updated
"""
mock_post.return_value = self._return_value(False)
with self.assertRaises(openphoto.OpenPhotoError):
with self.assertRaises(trovebox.TroveboxError):
self.client.photos.update(self.test_photos, title="Test")
class TestPhotosDelete(TestPhotos):
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photos_delete(self, mock_post):
"""Check that multiple photos can be deleted"""
mock_post.return_value = self._return_value(True)
@ -77,7 +77,7 @@ class TestPhotosDelete(TestPhotos):
mock_post.assert_called_with("/photos/delete.json", ids=["1a", "2b"])
self.assertEqual(result, True)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photos_delete_ids(self, mock_post):
"""Check that multiple photos can be deleted using their IDs"""
mock_post.return_value = self._return_value(True)
@ -85,18 +85,18 @@ class TestPhotosDelete(TestPhotos):
mock_post.assert_called_with("/photos/delete.json", ids=["1a", "2b"])
self.assertEqual(result, True)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photos_delete_failure(self, mock_post):
"""
Check that an exception is raised if multiple photos
cannot be deleted
"""
mock_post.return_value = self._return_value(False)
with self.assertRaises(openphoto.OpenPhotoError):
with self.assertRaises(trovebox.TroveboxError):
self.client.photos.delete(self.test_photos)
class TestPhotoDelete(TestPhotos):
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_delete(self, mock_post):
"""Check that a photo can be deleted"""
mock_post.return_value = self._return_value(True)
@ -104,7 +104,7 @@ class TestPhotoDelete(TestPhotos):
mock_post.assert_called_with("/photo/1a/delete.json")
self.assertEqual(result, True)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_delete_id(self, mock_post):
"""Check that a photo can be deleted using its ID"""
mock_post.return_value = self._return_value(True)
@ -112,14 +112,14 @@ class TestPhotoDelete(TestPhotos):
mock_post.assert_called_with("/photo/1a/delete.json")
self.assertEqual(result, True)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_delete_failure(self, mock_post):
"""Check that an exception is raised if a photo cannot be deleted"""
mock_post.return_value = self._return_value(False)
with self.assertRaises(openphoto.OpenPhotoError):
with self.assertRaises(trovebox.TroveboxError):
self.client.photo.delete(self.test_photos[0])
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_object_delete(self, mock_post):
"""
Check that a photo can be deleted when using
@ -133,18 +133,18 @@ class TestPhotoDelete(TestPhotos):
self.assertEqual(photo.get_fields(), {})
self.assertEqual(photo.id, None)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_object_delete_failure(self, mock_post):
"""
Check that an exception is raised if a photo cannot be deleted
when using the photo object directly
"""
mock_post.return_value = self._return_value(False)
with self.assertRaises(openphoto.OpenPhotoError):
with self.assertRaises(trovebox.TroveboxError):
self.test_photos[0].delete()
class TestPhotoEdit(TestPhotos):
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_photo_edit(self, mock_get):
"""Check that a the photo edit endpoint is working"""
mock_get.return_value = self._return_value({"markup": "<form/>"})
@ -152,7 +152,7 @@ class TestPhotoEdit(TestPhotos):
mock_get.assert_called_with("/photo/1a/edit.json")
self.assertEqual(result, "<form/>")
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_photo_edit_id(self, mock_get):
"""Check that a the photo edit endpoint is working when using an ID"""
mock_get.return_value = self._return_value({"markup": "<form/>"})
@ -160,7 +160,7 @@ class TestPhotoEdit(TestPhotos):
mock_get.assert_called_with("/photo/1a/edit.json")
self.assertEqual(result, "<form/>")
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_photo_object_edit(self, mock_get):
"""
Check that a the photo edit endpoint is working
@ -172,45 +172,45 @@ class TestPhotoEdit(TestPhotos):
self.assertEqual(result, "<form/>")
class TestPhotoReplace(TestPhotos):
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_replace(self, _):
""" If photo.replace gets implemented, write a test! """
with self.assertRaises(NotImplementedError):
self.client.photo.replace(self.test_photos[0], self.test_file)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_replace_id(self, _):
""" If photo.replace gets implemented, write a test! """
with self.assertRaises(NotImplementedError):
self.client.photo.replace("1a", self.test_file)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_object_replace(self, _):
""" If photo.replace gets implemented, write a test! """
with self.assertRaises(NotImplementedError):
self.test_photos[0].replace(self.test_file)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_replace_encoded(self, _):
""" If photo.replace_encoded gets implemented, write a test! """
with self.assertRaises(NotImplementedError):
self.client.photo.replace_encoded(self.test_photos[0],
self.test_file)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_replace_encoded_id(self, _):
""" If photo.replace_encoded gets implemented, write a test! """
with self.assertRaises(NotImplementedError):
self.client.photo.replace_encoded("1a", self.test_file)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_object_replace_encoded(self, _):
""" If photo.replace_encoded gets implemented, write a test! """
with self.assertRaises(NotImplementedError):
self.test_photos[0].replace_encoded(photo_file=self.test_file)
class TestPhotoUpdate(TestPhotos):
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_update(self, mock_post):
"""Check that a photo can be updated"""
mock_post.return_value = self._return_value(self.test_photos_dict[1])
@ -218,7 +218,7 @@ class TestPhotoUpdate(TestPhotos):
mock_post.assert_called_with("/photo/1a/update.json", title="Test")
self.assertEqual(result.get_fields(), self.test_photos_dict[1])
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_update_id(self, mock_post):
"""Check that a photo can be updated using its ID"""
mock_post.return_value = self._return_value(self.test_photos_dict[1])
@ -226,7 +226,7 @@ class TestPhotoUpdate(TestPhotos):
mock_post.assert_called_with("/photo/1a/update.json", title="Test")
self.assertEqual(result.get_fields(), self.test_photos_dict[1])
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_object_update(self, mock_post):
"""
Check that a photo can be updated
@ -239,7 +239,7 @@ class TestPhotoUpdate(TestPhotos):
self.assertEqual(photo.get_fields(), self.test_photos_dict[1])
class TestPhotoView(TestPhotos):
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_photo_view(self, mock_get):
"""Check that a photo can be viewed"""
mock_get.return_value = self._return_value(self.test_photos_dict[1])
@ -248,7 +248,7 @@ class TestPhotoView(TestPhotos):
mock_get.assert_called_with("/photo/1a/view.json", returnSizes="20x20")
self.assertEqual(result.get_fields(), self.test_photos_dict[1])
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_photo_view_id(self, mock_get):
"""Check that a photo can be viewed using its ID"""
mock_get.return_value = self._return_value(self.test_photos_dict[1])
@ -256,7 +256,7 @@ class TestPhotoView(TestPhotos):
mock_get.assert_called_with("/photo/1a/view.json", returnSizes="20x20")
self.assertEqual(result.get_fields(), self.test_photos_dict[1])
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_photo_object_view(self, mock_get):
"""
Check that a photo can be viewed
@ -269,7 +269,7 @@ class TestPhotoView(TestPhotos):
self.assertEqual(photo.get_fields(), self.test_photos_dict[1])
class TestPhotoUpload(TestPhotos):
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_upload(self, mock_post):
"""Check that a photo can be uploaded"""
mock_post.return_value = self._return_value(self.test_photos_dict[0])
@ -284,7 +284,7 @@ class TestPhotoUpload(TestPhotos):
self.assertIn("photo", files)
self.assertEqual(result.get_fields(), self.test_photos_dict[0])
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_upload_encoded(self, mock_post):
"""Check that a photo can be uploaded using Base64 encoding"""
mock_post.return_value = self._return_value(self.test_photos_dict[0])
@ -296,26 +296,26 @@ class TestPhotoUpload(TestPhotos):
self.assertEqual(result.get_fields(), self.test_photos_dict[0])
class TestPhotoDynamicUrl(TestPhotos):
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_photo_dynamic_url(self, _):
""" If photo.dynamic_url gets implemented, write a test! """
with self.assertRaises(NotImplementedError):
self.client.photo.dynamic_url(self.test_photos[0])
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_photo_dynamic_url_id(self, _):
""" If photo.dynamic_url gets implemented, write a test! """
with self.assertRaises(NotImplementedError):
self.client.photo.dynamic_url("1a")
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_photo_object_dynamic_url(self, _):
""" If photo.dynamic_url gets implemented, write a test! """
with self.assertRaises(NotImplementedError):
self.test_photos[0].dynamic_url()
class TestPhotoNextPrevious(TestPhotos):
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_photo_next_previous(self, mock_get):
"""Check that the next/previous photos are returned"""
mock_get.return_value = self._return_value(
@ -328,7 +328,7 @@ class TestPhotoNextPrevious(TestPhotos):
self.assertEqual(result["previous"][0].get_fields(),
self.test_photos_dict[1])
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_photo_next_previous_id(self, mock_get):
"""
Check that the next/previous photos are returned
@ -344,7 +344,7 @@ class TestPhotoNextPrevious(TestPhotos):
self.assertEqual(result["previous"][0].get_fields(),
self.test_photos_dict[1])
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_photo_object_next_previous(self, mock_get):
"""
Check that the next/previous photos are returned
@ -360,7 +360,7 @@ class TestPhotoNextPrevious(TestPhotos):
self.assertEqual(result["previous"][0].get_fields(),
self.test_photos_dict[1])
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_photo_next(self, mock_get):
"""Check that the next photos are returned"""
mock_get.return_value = self._return_value(
@ -371,7 +371,7 @@ class TestPhotoNextPrevious(TestPhotos):
self.test_photos_dict[0])
self.assertNotIn("previous", result)
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_photo_previous(self, mock_get):
"""Check that the previous photos are returned"""
mock_get.return_value = self._return_value(
@ -382,7 +382,7 @@ class TestPhotoNextPrevious(TestPhotos):
self.test_photos_dict[1])
self.assertNotIn("next", result)
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_photo_multiple_next_previous(self, mock_get):
"""Check that multiple next/previous photos are returned"""
mock_get.return_value = self._return_value(
@ -400,7 +400,7 @@ class TestPhotoNextPrevious(TestPhotos):
self.test_photos_dict[1])
class TestPhotoTransform(TestPhotos):
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_transform(self, mock_post):
"""Check that a photo can be transformed"""
mock_post.return_value = self._return_value(self.test_photos_dict[1])
@ -408,7 +408,7 @@ class TestPhotoTransform(TestPhotos):
mock_post.assert_called_with("/photo/1a/transform.json", rotate="90")
self.assertEqual(result.get_fields(), self.test_photos_dict[1])
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_transform_id(self, mock_post):
"""Check that a photo can be transformed using its ID"""
mock_post.return_value = self._return_value(self.test_photos_dict[1])
@ -416,7 +416,7 @@ class TestPhotoTransform(TestPhotos):
mock_post.assert_called_with("/photo/1a/transform.json", rotate="90")
self.assertEqual(result.get_fields(), self.test_photos_dict[1])
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_object_transform(self, mock_post):
"""
Check that a photo can be transformed

View file

@ -5,7 +5,7 @@ try:
except ImportError:
import unittest
import openphoto
import trovebox
class TestTags(unittest.TestCase):
test_host = "test.example.com"
@ -14,8 +14,8 @@ class TestTags(unittest.TestCase):
{"count": 5, "id":"tag2"}]
def setUp(self):
self.client = openphoto.OpenPhoto(host=self.test_host)
self.test_tags = [openphoto.objects.Tag(self.client, tag)
self.client = trovebox.Trovebox(host=self.test_host)
self.test_tags = [trovebox.objects.Tag(self.client, tag)
for tag in self.test_tags_dict]
@staticmethod
@ -23,7 +23,7 @@ class TestTags(unittest.TestCase):
return {"message": message, "code": code, "result": result}
class TestTagsList(TestTags):
@mock.patch.object(openphoto.OpenPhoto, 'get')
@mock.patch.object(trovebox.Trovebox, 'get')
def test_tags_list(self, mock_get):
"""Check that the the tag list is returned correctly"""
mock_get.return_value = self._return_value(self.test_tags_dict)
@ -36,7 +36,7 @@ class TestTagsList(TestTags):
self.assertEqual(result[1].count, 5)
class TestTagDelete(TestTags):
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_tag_delete(self, mock_post):
"""Check that a tag can be deleted"""
mock_post.return_value = self._return_value(True)
@ -44,7 +44,7 @@ class TestTagDelete(TestTags):
mock_post.assert_called_with("/tag/tag1/delete.json")
self.assertEqual(result, True)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_tag_delete_id(self, mock_post):
"""Check that a tag can be deleted using its ID"""
mock_post.return_value = self._return_value(True)
@ -52,14 +52,14 @@ class TestTagDelete(TestTags):
mock_post.assert_called_with("/tag/tag1/delete.json")
self.assertEqual(result, True)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_tag_delete_failure(self, mock_post):
"""Check that an exception is raised if a tag cannot be deleted"""
mock_post.return_value = self._return_value(False)
with self.assertRaises(openphoto.OpenPhotoError):
with self.assertRaises(trovebox.TroveboxError):
self.client.tag.delete(self.test_tags[0])
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_tag_object_delete(self, mock_post):
"""Check that a tag can be deleted when using the tag object directly"""
mock_post.return_value = self._return_value(True)
@ -70,18 +70,18 @@ class TestTagDelete(TestTags):
self.assertEqual(tag.get_fields(), {})
self.assertEqual(tag.id, None)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_tag_object_delete_failure(self, mock_post):
"""
Check that an exception is raised if a tag cannot be deleted
when using the tag object directly
"""
mock_post.return_value = self._return_value(False)
with self.assertRaises(openphoto.OpenPhotoError):
with self.assertRaises(trovebox.TroveboxError):
self.test_tags[0].delete()
class TestTagUpdate(TestTags):
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_tag_update(self, mock_post):
"""Check that a tag can be updated"""
mock_post.return_value = self._return_value(self.test_tags_dict[1])
@ -90,7 +90,7 @@ class TestTagUpdate(TestTags):
self.assertEqual(result.id, "tag2")
self.assertEqual(result.count, 5)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_tag_update_id(self, mock_post):
"""Check that a tag can be updated using its ID"""
mock_post.return_value = self._return_value(self.test_tags_dict[1])
@ -99,7 +99,7 @@ class TestTagUpdate(TestTags):
self.assertEqual(result.id, "tag2")
self.assertEqual(result.count, 5)
@mock.patch.object(openphoto.OpenPhoto, 'post')
@mock.patch.object(trovebox.Trovebox, 'post')
def test_tag_object_update(self, mock_post):
"""Check that a tag can be updated when using the tag object directly"""
mock_post.return_value = self._return_value(self.test_tags_dict[1])

35
trovebox/__init__.py Normal file
View file

@ -0,0 +1,35 @@
from .http import Http
from .errors import *
from ._version import __version__
from . import api_photo
from . import api_tag
from . import api_album
LATEST_API_VERSION = 2
class Trovebox(Http):
"""
Client library for Trovebox
If no parameters are specified, config is loaded from the default
location (~/.config/trovebox/default).
The config_file parameter is used to specify an alternate config file.
If the host parameter is specified, no config file is loaded and
OAuth tokens (consumer*, token*) can optionally be specified.
All requests will include the api_version path, if specified.
This should be used to ensure that your application will continue to work
even if the Trovebox API is updated to a new revision.
"""
def __init__(self, config_file=None, host=None,
consumer_key='', consumer_secret='',
token='', token_secret='',
api_version=None):
Http.__init__(self, config_file, host,
consumer_key, consumer_secret,
token, token_secret, api_version)
self.photos = api_photo.ApiPhotos(self)
self.photo = api_photo.ApiPhoto(self)
self.tags = api_tag.ApiTags(self)
self.tag = api_tag.ApiTag(self)
self.albums = api_album.ApiAlbums(self)
self.album = api_album.ApiAlbum(self)

View file

@ -1,4 +1,4 @@
from openphoto.objects import Album
from .objects import Album
class ApiAlbums:
def __init__(self, client):
@ -23,7 +23,7 @@ class ApiAlbum:
"""
Delete an album.
Returns True if successful.
Raises an OpenPhotoError if not.
Raises an TroveboxError if not.
"""
if not isinstance(album, Album):
album = Album(self._client, {"id": album})

View file

@ -1,8 +1,8 @@
import base64
from openphoto.errors import OpenPhotoError
import openphoto.openphoto_http
from openphoto.objects import Photo
from .errors import TroveboxError
from . import http
from .objects import Photo
def extract_ids(photos):
"""
@ -24,31 +24,31 @@ class ApiPhotos:
def list(self, **kwds):
""" Returns a list of Photo objects """
photos = self._client.get("/photos/list.json", **kwds)["result"]
photos = openphoto.openphoto_http.result_to_list(photos)
photos = http.result_to_list(photos)
return [Photo(self._client, photo) for photo in photos]
def update(self, photos, **kwds):
"""
Updates a list of photos.
Returns True if successful.
Raises OpenPhotoError if not.
Raises TroveboxError if not.
"""
ids = extract_ids(photos)
if not self._client.post("/photos/update.json", ids=ids,
**kwds)["result"]:
raise OpenPhotoError("Update response returned False")
raise TroveboxError("Update response returned False")
return True
def delete(self, photos, **kwds):
"""
Deletes a list of photos.
Returns True if successful.
Raises OpenPhotoError if not.
Raises TroveboxError if not.
"""
ids = extract_ids(photos)
if not self._client.post("/photos/delete.json", ids=ids,
**kwds)["result"]:
raise OpenPhotoError("Delete response returned False")
raise TroveboxError("Delete response returned False")
return True
class ApiPhoto:
@ -59,7 +59,7 @@ class ApiPhoto:
"""
Delete a photo.
Returns True if successful.
Raises an OpenPhotoError if not.
Raises an TroveboxError if not.
"""
if not isinstance(photo, Photo):
photo = Photo(self._client, {"id": photo})

View file

@ -1,4 +1,4 @@
from openphoto.objects import Tag
from .objects import Tag
class ApiTags:
def __init__(self, client):
@ -24,7 +24,7 @@ class ApiTag:
"""
Delete a tag.
Returns True if successful.
Raises an OpenPhotoError if not.
Raises an TroveboxError if not.
"""
if not isinstance(tag, Tag):
tag = Tag(self._client, {"id": tag})

View file

@ -41,7 +41,7 @@ def get_config_path(config_file):
config_path = os.path.join(os.getenv('HOME'), ".config")
if not config_file:
config_file = "default"
return os.path.join(config_path, "openphoto", config_file)
return os.path.join(config_path, "trovebox", config_file)
def read_config(config_path):
"""

View file

@ -1,12 +1,12 @@
class OpenPhotoError(Exception):
""" Indicates that an OpenPhoto operation failed """
class TroveboxError(Exception):
""" Indicates that an Trovebox operation failed """
pass
class OpenPhotoDuplicateError(OpenPhotoError):
class TroveboxDuplicateError(TroveboxError):
""" Indicates that an upload operation failed due to a duplicate photo """
pass
class OpenPhoto404Error(Exception):
class Trovebox404Error(Exception):
"""
Indicates that an Http 404 error code was received
(resource not found)

View file

@ -8,9 +8,9 @@ try:
except ImportError:
from urlparse import urlunparse # Python2
from openphoto.objects import OpenPhotoObject
from openphoto.errors import *
from openphoto.config import Config
from .objects import TroveboxObject
from .errors import *
from .config import Config
if sys.version < '3':
TEXT_TYPE = unicode
@ -20,24 +20,24 @@ else:
DUPLICATE_RESPONSE = {"code": 409,
"message": "This photo already exists"}
class OpenPhotoHttp:
class Http:
"""
Base class to handle HTTP requests to an OpenPhoto server.
Base class to handle HTTP requests to an Trovebox server.
If no parameters are specified, config is loaded from the default
location (~/.config/openphoto/default).
location (~/.config/trovebox/default).
The config_file parameter is used to specify an alternate config file.
If the host parameter is specified, no config file is loaded and
OAuth tokens (consumer*, token*) can optionally be specified.
All requests will include the api_version path, if specified.
This should be used to ensure that your application will continue to work
even if the OpenPhoto API is updated to a new revision.
even if the Trovebox API is updated to a new revision.
"""
def __init__(self, config_file=None, host=None,
consumer_key='', consumer_secret='',
token='', token_secret='', api_version=None):
self._api_version = api_version
self._logger = logging.getLogger("openphoto")
self._logger = logging.getLogger("trovebox")
self.config = Config(config_file, host,
consumer_key, consumer_secret,
@ -55,7 +55,7 @@ class OpenPhotoHttp:
Performs an HTTP GET from the specified endpoint (API path),
passing parameters if given.
The api_version is prepended to the endpoint,
if it was specified when the OpenPhoto object was created.
if it was specified when the Trovebox object was created.
Returns the decoded JSON dictionary, and raises exceptions if an
error code is received.
@ -98,7 +98,7 @@ class OpenPhotoHttp:
Performs an HTTP POST to the specified endpoint (API path),
passing parameters if given.
The api_version is prepended to the endpoint,
if it was specified when the OpenPhoto object was created.
if it was specified when the Trovebox object was created.
Returns the decoded JSON dictionary, and raises exceptions if an
error code is received.
@ -112,7 +112,7 @@ class OpenPhotoHttp:
url = urlunparse(('http', self.host, endpoint, '', '', ''))
if not self.config.consumer_key:
raise OpenPhotoError("Cannot issue POST without OAuth tokens")
raise TroveboxError("Cannot issue POST without OAuth tokens")
auth = requests_oauthlib.OAuth1(self.config.consumer_key,
self.config.consumer_secret,
@ -152,7 +152,7 @@ class OpenPhotoHttp:
processed_params = {}
for key, value in params.items():
# Extract IDs from objects
if isinstance(value, OpenPhotoObject):
if isinstance(value, TroveboxObject):
value = value.id
# Ensure value is UTF-8 encoded
@ -165,7 +165,7 @@ class OpenPhotoHttp:
new_list = list(value)
# Extract IDs from objects in the list
for i, item in enumerate(new_list):
if isinstance(item, OpenPhotoObject):
if isinstance(item, TroveboxObject):
new_list[i] = item.id
# Convert list to string
value = ','.join([str(item) for item in new_list])
@ -184,28 +184,28 @@ class OpenPhotoHttp:
Raises an exception if an invalid response code is received.
"""
if response.status_code == 404:
raise OpenPhoto404Error("HTTP Error %d: %s" %
raise Trovebox404Error("HTTP Error %d: %s" %
(response.status_code, response.reason))
try:
json_response = response.json()
code = json_response["code"]
message = json_response["message"]
except (ValueError, KeyError):
# Response wasn't OpenPhoto JSON - check the HTTP status code
# Response wasn't Trovebox JSON - check the HTTP status code
if 200 <= response.status_code < 300:
# Status code was valid, so just reraise the exception
raise
else:
raise OpenPhotoError("HTTP Error %d: %s" %
raise TroveboxError("HTTP Error %d: %s" %
(response.status_code, response.reason))
if 200 <= code < 300:
return json_response
elif (code == DUPLICATE_RESPONSE["code"] and
DUPLICATE_RESPONSE["message"] in message):
raise OpenPhotoDuplicateError("Code %d: %s" % (code, message))
raise TroveboxDuplicateError("Code %d: %s" % (code, message))
else:
raise OpenPhotoError("Code %d: %s" % (code, message))
raise TroveboxError("Code %d: %s" % (code, message))
def result_to_list(result):
""" Handle the case where the result contains no items """

View file

@ -4,7 +4,7 @@ import sys
import json
from optparse import OptionParser
import openphoto
import trovebox
CONFIG_ERROR = """
You must create a configuration file with the following contents:
@ -29,7 +29,7 @@ def main(args=sys.argv[1:]):
parser.add_option('-c', '--config', help="Configuration file to use",
action='store', type='string', dest='config_file')
parser.add_option('-h', '-H', '--host',
help=("Hostname of the OpenPhoto server "
help=("Hostname of the Trovebox server "
"(overrides config_file)"),
action='store', type='string', dest='host')
parser.add_option('-X', help="Method to use (GET or POST)",
@ -56,7 +56,7 @@ def main(args=sys.argv[1:]):
return
if options.version:
print(openphoto.__version__)
print(trovebox.__version__)
return
if args:
@ -70,10 +70,10 @@ def main(args=sys.argv[1:]):
# Host option overrides config file settings
if options.host:
client = openphoto.OpenPhoto(host=options.host)
client = trovebox.Trovebox(host=options.host)
else:
try:
client = openphoto.OpenPhoto(config_file=options.config_file)
client = trovebox.Trovebox(config_file=options.config_file)
except IOError as error:
print(error)
print(CONFIG_ERROR)
@ -108,7 +108,7 @@ def main(args=sys.argv[1:]):
def extract_files(params):
"""
Extract filenames from the "photo" parameter, so they can be uploaded, returning (updated_params, files).
Uses the same technique as openphoto-php:
Uses the same technique as the Trovebox PHP commandline tool:
* Filename can only be in the "photo" parameter
* Filename must be prefixed with "@"
* Filename must exist

View file

@ -3,14 +3,14 @@ try:
except ImportError:
from urllib import quote # Python2
from openphoto.errors import OpenPhotoError
from .errors import TroveboxError
class OpenPhotoObject:
class TroveboxObject:
""" Base object supporting the storage of custom fields as attributes """
def __init__(self, openphoto, json_dict):
def __init__(self, trovebox, json_dict):
self.id = None
self.name = None
self._openphoto = openphoto
self._trovebox = trovebox
self._json_dict = json_dict
self._set_fields(json_dict)
@ -54,23 +54,23 @@ class OpenPhotoObject:
return self._json_dict
class Photo(OpenPhotoObject):
class Photo(TroveboxObject):
def delete(self, **kwds):
"""
Delete this photo.
Returns True if successful.
Raises an OpenPhotoError if not.
Raises an TroveboxError if not.
"""
result = self._openphoto.post("/photo/%s/delete.json" %
result = self._trovebox.post("/photo/%s/delete.json" %
self.id, **kwds)["result"]
if not result:
raise OpenPhotoError("Delete response returned False")
raise TroveboxError("Delete response returned False")
self._delete_fields()
return result
def edit(self, **kwds):
""" Returns an HTML form to edit the photo """
result = self._openphoto.get("/photo/%s/edit.json" %
result = self._trovebox.get("/photo/%s/edit.json" %
self.id, **kwds)["result"]
return result["markup"]
@ -82,7 +82,7 @@ class Photo(OpenPhotoObject):
def update(self, **kwds):
""" Update this photo with the specified parameters """
new_dict = self._openphoto.post("/photo/%s/update.json" %
new_dict = self._trovebox.post("/photo/%s/update.json" %
self.id, **kwds)["result"]
self._replace_fields(new_dict)
@ -91,7 +91,7 @@ class Photo(OpenPhotoObject):
Used to view the photo at a particular size.
Updates the photo's fields with the response.
"""
new_dict = self._openphoto.get("/photo/%s/view.json" %
new_dict = self._trovebox.get("/photo/%s/view.json" %
self.id, **kwds)["result"]
self._replace_fields(new_dict)
@ -103,7 +103,7 @@ class Photo(OpenPhotoObject):
Returns a dict containing the next and previous photo lists
(there may be more than one next/previous photo returned).
"""
result = self._openphoto.get("/photo/%s/nextprevious.json" %
result = self._trovebox.get("/photo/%s/nextprevious.json" %
self.id, **kwds)["result"]
value = {}
if "next" in result:
@ -113,7 +113,7 @@ class Photo(OpenPhotoObject):
value["next"] = []
for photo in result["next"]:
value["next"].append(Photo(self._openphoto, photo))
value["next"].append(Photo(self._trovebox, photo))
if "previous" in result:
# Workaround for APIv1
@ -122,7 +122,7 @@ class Photo(OpenPhotoObject):
value["previous"] = []
for photo in result["previous"]:
value["previous"].append(Photo(self._openphoto, photo))
value["previous"].append(Photo(self._trovebox, photo))
return value
@ -131,65 +131,65 @@ class Photo(OpenPhotoObject):
Performs transformation specified in **kwds
Example: transform(rotate=90)
"""
new_dict = self._openphoto.post("/photo/%s/transform.json" %
new_dict = self._trovebox.post("/photo/%s/transform.json" %
self.id, **kwds)["result"]
# APIv1 doesn't return the transformed photo (frontend issue #955)
if isinstance(new_dict, bool):
new_dict = self._openphoto.get("/photo/%s/view.json" %
new_dict = self._trovebox.get("/photo/%s/view.json" %
self.id)["result"]
self._replace_fields(new_dict)
class Tag(OpenPhotoObject):
class Tag(TroveboxObject):
def delete(self, **kwds):
"""
Delete this tag.
Returns True if successful.
Raises an OpenPhotoError if not.
Raises an TroveboxError if not.
"""
result = self._openphoto.post("/tag/%s/delete.json" %
result = self._trovebox.post("/tag/%s/delete.json" %
quote(self.id), **kwds)["result"]
if not result:
raise OpenPhotoError("Delete response returned False")
raise TroveboxError("Delete response returned False")
self._delete_fields()
return result
def update(self, **kwds):
""" Update this tag with the specified parameters """
new_dict = self._openphoto.post("/tag/%s/update.json" % quote(self.id),
new_dict = self._trovebox.post("/tag/%s/update.json" % quote(self.id),
**kwds)["result"]
self._replace_fields(new_dict)
class Album(OpenPhotoObject):
def __init__(self, openphoto, json_dict):
class Album(TroveboxObject):
def __init__(self, trovebox, json_dict):
self.photos = None
self.cover = None
OpenPhotoObject.__init__(self, openphoto, json_dict)
TroveboxObject.__init__(self, trovebox, json_dict)
self._update_fields_with_objects()
def _update_fields_with_objects(self):
""" Convert dict fields into objects, where appropriate """
# Update the cover with a photo object
if isinstance(self.cover, dict):
self.cover = Photo(self._openphoto, self.cover)
self.cover = Photo(self._trovebox, self.cover)
# Update the photo list with photo objects
if isinstance(self.photos, list):
for i, photo in enumerate(self.photos):
if isinstance(photo, dict):
self.photos[i] = Photo(self._openphoto, photo)
self.photos[i] = Photo(self._trovebox, photo)
def delete(self, **kwds):
"""
Delete this album.
Returns True if successful.
Raises an OpenPhotoError if not.
Raises an TroveboxError if not.
"""
result = self._openphoto.post("/album/%s/delete.json" %
result = self._trovebox.post("/album/%s/delete.json" %
self.id, **kwds)["result"]
if not result:
raise OpenPhotoError("Delete response returned False")
raise TroveboxError("Delete response returned False")
self._delete_fields()
return result
@ -204,12 +204,12 @@ class Album(OpenPhotoObject):
def update(self, **kwds):
""" Update this album with the specified parameters """
new_dict = self._openphoto.post("/album/%s/update.json" %
new_dict = self._trovebox.post("/album/%s/update.json" %
self.id, **kwds)["result"]
# APIv1 doesn't return the updated album (frontend issue #937)
if isinstance(new_dict, bool):
new_dict = self._openphoto.get("/album/%s/view.json" %
new_dict = self._trovebox.get("/album/%s/view.json" %
self.id)["result"]
self._replace_fields(new_dict)
@ -220,7 +220,7 @@ class Album(OpenPhotoObject):
Requests the full contents of the album.
Updates the album's fields with the response.
"""
result = self._openphoto.get("/album/%s/view.json" %
result = self._trovebox.get("/album/%s/view.json" %
self.id, **kwds)["result"]
self._replace_fields(result)
self._update_fields_with_objects()