├── .gitignore
├── .pylint.rc
├── .travis.yml
├── LICENSE
├── README.md
├── anydo
├── __init__.py
├── api.py
├── error.py
└── lib
│ ├── __init__.py
│ ├── auth.py
│ ├── bind.py
│ ├── error.py
│ ├── settings.py
│ ├── tests
│ ├── __init__.py
│ ├── test_api.py
│ ├── test_error.py
│ ├── test_lib.py
│ ├── test_pep8.py
│ └── test_utils.py
│ └── utils.py
├── bin
└── runtest.sh
├── setup.py
└── tox.ini
/.gitignore:
--------------------------------------------------------------------------------
1 | *.py[cod]
2 |
3 | # C extensions
4 | *.so
5 |
6 | # Packages
7 | *.egg
8 | *.egg-info
9 | dist
10 | build
11 | eggs
12 | parts
13 | var
14 | sdist
15 | develop-eggs
16 | .installed.cfg
17 | lib64
18 |
19 | # Installer logs
20 | pip-log.txt
21 |
22 | # Unit test / coverage reports
23 | .coverage
24 | .tox
25 | nosetests.xml
26 |
27 | # Translations
28 | *.mo
29 |
30 | # Mr Developer
31 | .mr.developer.cfg
32 | .project
33 | .pydevproject
34 |
35 | # Linux
36 | .*
37 | !.gitignore
38 | !.pylint.rc
39 | !.travis.yml
40 | *~
41 |
--------------------------------------------------------------------------------
/.pylint.rc:
--------------------------------------------------------------------------------
1 | [MASTER]
2 |
3 | # Specify a configuration file.
4 | #rcfile=
5 |
6 | # Python code to execute, usually for sys.path manipulation such as
7 | # pygtk.require().
8 | #init-hook=
9 |
10 | # Profiled execution.
11 | profile=no
12 |
13 | # Add files or directories to the blacklist. They should be base names, not
14 | # paths.
15 | ignore=CVS
16 |
17 | # Pickle collected data for later comparisons.
18 | persistent=yes
19 |
20 | # List of plugins (as comma separated values of python modules names) to load,
21 | # usually to register additional checkers.
22 | load-plugins=
23 |
24 |
25 | [MESSAGES CONTROL]
26 |
27 | # Enable the message, report, category or checker with the given id(s). You can
28 | # either give multiple identifier separated by comma (,) or put this option
29 | # multiple time. See also the "--disable" option for examples.
30 | #enable=
31 |
32 | # Disable the message, report, category or checker with the given id(s). You
33 | # can either give multiple identifiers separated by comma (,) or put this
34 | # option multiple times (only on the command line, not in the configuration
35 | # file where it should appear only once).You can also use "--disable=all" to
36 | # disable everything first and then reenable specific checks. For example, if
37 | # you want to run only the similarities checker, you can use "--disable=all
38 | # --enable=similarities". If you want to run only the classes checker, but have
39 | # no Warning level messages displayed, use"--disable=all --enable=classes
40 | # --disable=W"
41 | #disable=
42 |
43 |
44 | [REPORTS]
45 |
46 | # Set the output format. Available formats are text, parseable, colorized, msvs
47 | # (visual studio) and html. You can also give a reporter class, eg
48 | # mypackage.mymodule.MyReporterClass.
49 | output-format=text
50 |
51 | # Put messages in a separate file for each module / package specified on the
52 | # command line instead of printing them on stdout. Reports (if any) will be
53 | # written in a file name "pylint_global.[txt|html]".
54 | files-output=no
55 |
56 | # Tells whether to display a full report or only the messages
57 | reports=yes
58 |
59 | # Python expression which should return a note less than 10 (10 is the highest
60 | # note). You have access to the variables errors warning, statement which
61 | # respectively contain the number of errors / warnings messages and the total
62 | # number of statements analyzed. This is used by the global evaluation report
63 | # (RP0004).
64 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
65 |
66 | # Add a comment according to your evaluation note. This is used by the global
67 | # evaluation report (RP0004).
68 | comment=no
69 |
70 | # Template used to display messages. This is a python new-style format string
71 | # used to format the message information. See doc for all details
72 | #msg-template=
73 |
74 |
75 | [MISCELLANEOUS]
76 |
77 | # List of note tags to take in consideration, separated by a comma.
78 | notes=FIXME,XXX,TODO
79 |
80 |
81 | [BASIC]
82 |
83 | # Required attributes for module, separated by a comma
84 | required-attributes=
85 |
86 | # List of builtins function names that should not be used, separated by a comma
87 | bad-functions=map,filter,apply
88 |
89 | # Regular expression which should only match correct module names
90 | module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
91 |
92 | # Regular expression which should only match correct module level names
93 | const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
94 |
95 | # Regular expression which should only match correct class names
96 | class-rgx=[A-Z_][a-zA-Z0-9]+$
97 |
98 | # Regular expression which should only match correct function names
99 | function-rgx=[a-z_][a-z0-9_]{2,30}$
100 |
101 | # Regular expression which should only match correct method names
102 | method-rgx=[a-z_][a-z0-9_]{2,30}$
103 |
104 | # Regular expression which should only match correct instance attribute names
105 | attr-rgx=[a-z_][a-z0-9_]{2,30}$
106 |
107 | # Regular expression which should only match correct argument names
108 | argument-rgx=[a-z_][a-z0-9_]{2,30}$
109 |
110 | # Regular expression which should only match correct variable names
111 | variable-rgx=[a-z_][a-z0-9_]{2,30}$
112 |
113 | # Regular expression which should only match correct attribute names in class
114 | # bodies
115 | class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
116 |
117 | # Regular expression which should only match correct list comprehension /
118 | # generator expression variable names
119 | inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
120 |
121 | # Good variable names which should always be accepted, separated by a comma
122 | good-names=i,j,k,ex,Run,_
123 |
124 | # Bad variable names which should always be refused, separated by a comma
125 | bad-names=foo,bar,baz,toto,tutu,tata
126 |
127 | # Regular expression which should only match function or class names that do
128 | # not require a docstring.
129 | no-docstring-rgx=__.*__
130 |
131 | # Minimum line length for functions/classes that require docstrings, shorter
132 | # ones are exempt.
133 | docstring-min-length=-1
134 |
135 |
136 | [FORMAT]
137 |
138 | # Maximum number of characters on a single line.
139 | max-line-length=80
140 |
141 | # Regexp for a line that is allowed to be longer than the limit.
142 | ignore-long-lines=^\s*(# )??$
143 |
144 | # Allow the body of an if to be on the same line as the test if there is no
145 | # else.
146 | single-line-if-stmt=no
147 |
148 | # List of optional constructs for which whitespace checking is disabled
149 | no-space-check=trailing-comma,dict-separator
150 |
151 | # Maximum number of lines in a module
152 | max-module-lines=1000
153 |
154 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
155 | # tab).
156 | indent-string=' '
157 |
158 |
159 | [VARIABLES]
160 |
161 | # Tells whether we should check for unused import in __init__ files.
162 | init-import=no
163 |
164 | # A regular expression matching the beginning of the name of dummy variables
165 | # (i.e. not used).
166 | dummy-variables-rgx=_$|dummy
167 |
168 | # List of additional names supposed to be defined in builtins. Remember that
169 | # you should avoid to define new builtins when possible.
170 | additional-builtins=
171 |
172 |
173 | [SIMILARITIES]
174 |
175 | # Minimum lines number of a similarity.
176 | min-similarity-lines=4
177 |
178 | # Ignore comments when computing similarities.
179 | ignore-comments=yes
180 |
181 | # Ignore docstrings when computing similarities.
182 | ignore-docstrings=yes
183 |
184 | # Ignore imports when computing similarities.
185 | ignore-imports=no
186 |
187 |
188 | [TYPECHECK]
189 |
190 | # Tells whether missing members accessed in mixin class should be ignored. A
191 | # mixin class is detected if its name ends with "mixin" (case insensitive).
192 | ignore-mixin-members=yes
193 |
194 | # List of classes names for which member attributes should not be checked
195 | # (useful for classes with attributes dynamically set).
196 | ignored-classes=SQLObject
197 |
198 | # When zope mode is activated, add a predefined set of Zope acquired attributes
199 | # to generated-members.
200 | zope=no
201 |
202 | # List of members which are set dynamically and missed by pylint inference
203 | # system, and so shouldn't trigger E0201 when accessed. Python regular
204 | # expressions are accepted.
205 | generated-members=REQUEST,acl_users,aq_parent
206 |
207 |
208 | [IMPORTS]
209 |
210 | # Deprecated modules which should not be used, separated by a comma
211 | deprecated-modules=regsub,TERMIOS,Bastion,rexec
212 |
213 | # Create a graph of every (i.e. internal and external) dependencies in the
214 | # given file (report RP0402 must not be disabled)
215 | import-graph=
216 |
217 | # Create a graph of external dependencies in the given file (report RP0402 must
218 | # not be disabled)
219 | ext-import-graph=
220 |
221 | # Create a graph of internal dependencies in the given file (report RP0402 must
222 | # not be disabled)
223 | int-import-graph=
224 |
225 |
226 | [DESIGN]
227 |
228 | # Maximum number of arguments for function / method
229 | max-args=5
230 |
231 | # Argument names that match this expression will be ignored. Default to name
232 | # with leading underscore
233 | ignored-argument-names=_.*
234 |
235 | # Maximum number of locals for function / method body
236 | max-locals=15
237 |
238 | # Maximum number of return / yield for function / method body
239 | max-returns=6
240 |
241 | # Maximum number of branch for function / method body
242 | max-branches=12
243 |
244 | # Maximum number of statements in function / method body
245 | max-statements=50
246 |
247 | # Maximum number of parents for a class (see R0901).
248 | max-parents=7
249 |
250 | # Maximum number of attributes for a class (see R0902).
251 | max-attributes=7
252 |
253 | # Minimum number of public methods for a class (see R0903).
254 | min-public-methods=2
255 |
256 | # Maximum number of public methods for a class (see R0904).
257 | max-public-methods=20
258 |
259 |
260 | [CLASSES]
261 |
262 | # List of interface methods to ignore, separated by a comma. This is used for
263 | # instance to not check methods defines in Zope's Interface base class.
264 | ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
265 |
266 | # List of method names used to declare (i.e. assign) instance attributes.
267 | defining-attr-methods=__init__,__new__,setUp
268 |
269 | # List of valid names for the first argument in a class method.
270 | valid-classmethod-first-arg=cls
271 |
272 | # List of valid names for the first argument in a metaclass class method.
273 | valid-metaclass-classmethod-first-arg=mcs
274 |
275 |
276 | [EXCEPTIONS]
277 |
278 | # Exceptions that will emit a warning when being caught. Defaults to
279 | # "Exception"
280 | overgeneral-exceptions=Exception
281 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python: 2.7
3 | env:
4 | - TOX_ENV=py26
5 | - TOX_ENV=py27
6 | - TOX_ENV=py32
7 | - TOX_ENV=py33
8 | - TOX_ENV=py34
9 | - TOX_ENV=pypy
10 | install:
11 | - pip install tox coveralls
12 | script:
13 | - tox -e $TOX_ENV
14 | after_success:
15 | coveralls --verbose
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Gaurav Kalra
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | :warning: **This repository is not maintained anymore.
Refer [Network](https://github.com/gvkalra/python-anydo/network) for active forks**
2 |
3 | python-anydo
4 | ============
5 |
6 | Unofficial python bindings for Any.Do, an attractive todo list organizer.
7 |
8 | [](https://travis-ci.org/gvkalra/python-anydo)
9 | [](https://coveralls.io/r/gvkalra/python-anydo?branch=master)
10 |
11 | The bindings cooperate with the official applications available viz:
12 |
13 | [](https://play.google.com/store/apps/details?id=com.anydo)
14 | [](https://itunes.apple.com/us/app/any.do/id497328576?ls=1&mt=8)
15 | [](https://chrome.google.com/webstore/detail/anydo/kdadialhpiikehpdeejjeiikopddkjem)
16 |
17 | Usage Guide
18 | -----------------
19 | Authenticate to Any.Do and create AnyDoAPI object
20 | ```python
21 | from anydo.api import AnyDoAPI
22 | api = AnyDoAPI(username='username@example.org', password='password')
23 | ```
24 |
25 | **Get User Information**
26 | ```python
27 | api.get_user_info()
28 | ```
29 |
30 | **Get All Tasks (including Notes)**
31 | ```python
32 | api.get_all_tasks()
33 | ```
34 |
35 | **Get Task/Note by ID**
36 | ```python
37 | api.get_task_by_id()
38 | ```
39 |
40 | **Delete Task/Note by ID**
41 | ```python
42 | api.delete_task_by_id()
43 | ```
44 |
45 | **Get All Categories**
46 | ```python
47 | api.get_all_categories()
48 | ```
49 |
50 | **Delete Category by ID**
51 | ```python
52 | api.delete_category_by_id()
53 | ```
54 |
55 | **Create new category**
56 | ```python
57 | api.create_new_category()
58 | ```
59 |
60 | **Create new task**
61 | ```python
62 | api.create_new_task()
63 | ```
64 |
65 | Developing python-anydo
66 | --------------------------------------------
67 | You should add bin/runtest.sh as pre-commit git-hook.
68 | It will help you in verifying your changes locally.
69 | ```bash
70 | $ git clone https://github.com/gvkalra/python-anydo.git
71 | $ cd python-anydo
72 | $ cp bin/runtest.sh .git/hooks/pre-commit
73 | ```
74 |
75 | License
76 | -----------------
77 | ```text
78 | The MIT License (MIT)
79 |
80 | Copyright (c) 2014 Gaurav Kalra
81 |
82 | Permission is hereby granted, free of charge, to any person obtaining a copy
83 | of this software and associated documentation files (the "Software"), to deal
84 | in the Software without restriction, including without limitation the rights
85 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
86 | copies of the Software, and to permit persons to whom the Software is
87 | furnished to do so, subject to the following conditions:
88 |
89 | The above copyright notice and this permission notice shall be included in
90 | all copies or substantial portions of the Software.
91 |
92 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
93 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
94 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
95 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
96 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
97 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
98 | THE SOFTWARE.
99 | ```
100 |
101 | Authors
102 | -----------------
103 | - Gaurav Kalra ()
104 | - Kouhei Maeda ()
105 |
--------------------------------------------------------------------------------
/anydo/__init__.py:
--------------------------------------------------------------------------------
1 | """ modules of unofficial python bindings for Any.Do """
2 |
--------------------------------------------------------------------------------
/anydo/api.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """ anydo.api """
3 | from anydo.lib.bind import AnyDoAPIBinder
4 | from anydo.lib.utils import create_uuid
5 | from anydo.error import AnyDoAPIError
6 | import time
7 |
8 |
9 | class AnyDoAPI(object):
10 | """ Base class that all AnyDo API client"""
11 | def __init__(self, username=None, password=None):
12 | self.api = AnyDoAPIBinder(username, password)
13 |
14 | def __owner_id(self):
15 | """ Retrieve owner id.
16 | Returns: owner id of AnyDo task
17 | """
18 | return self.get_user_info().get('id')
19 |
20 | def __default_category_id(self):
21 | """ Retrieve default category id.
22 | Returns: default category id of AnyDo tasks
23 | """
24 | return [value for _, value in
25 | enumerate(self.get_all_categories())
26 | if value['isDefault'] is True][0].get('id')
27 |
28 | def get_user_info(self):
29 | """ Fetches user information
30 |
31 | Retrieves information of currently authenticated user
32 |
33 | Args:
34 | None
35 |
36 | Returns:
37 | A dictionary of following user information:
38 | {'anonymous': Boolean,
39 | 'creationDate': Number,
40 | 'email': String,
41 | 'emails': Array,
42 | 'facebookAccessToken': String,
43 | 'facebookId': String,
44 | 'fake': Boolean,
45 | 'id': String,
46 | 'name': String,
47 | 'phoneNumbers': Array}
48 |
49 | Raises:
50 | AnyDoAPIError:
51 | Code(420): JSON Decoding Error.
52 | """
53 | ret = self.api.user_info()
54 | try:
55 | return ret.json()
56 | except ValueError:
57 | raise AnyDoAPIError(420, "JSON Decoding Error")
58 |
59 | def get_all_tasks(self, response_type="flat",
60 | include_deleted=False,
61 | include_done=False):
62 | """ Retrieve all tasks
63 |
64 | Args:
65 | response_type: "flat" in default
66 | include_deleted: False in default
67 | include_done: False in default
68 |
69 | Returns:
70 | A list of all tasks
71 |
72 | Raises:
73 | AnyDoAPIError:
74 | Code(420): JSON Decoding Error.
75 | """
76 | ret = self.api.tasks(response_type,
77 | include_deleted,
78 | include_done)
79 | try:
80 | return ret.json()
81 | except ValueError:
82 | raise AnyDoAPIError(420, "JSON Decoding Error")
83 |
84 | def get_all_categories(self, response_type="flat",
85 | include_deleted=False,
86 | include_done=False):
87 | """ Retrieve all categories
88 |
89 | Args:
90 | response_type: "flat" in default
91 | include_deleted: False in default
92 | include_done: False in default
93 |
94 | Returns:
95 | A list of all categories
96 |
97 | Raises:
98 | AnyDoAPIError:
99 | Code(420): JSON Decoding Error.
100 | """
101 | ret = self.api.categories(response_type,
102 | include_deleted,
103 | include_done)
104 | try:
105 | return ret.json()
106 | except ValueError:
107 | raise AnyDoAPIError(420, "JSON Decoding Error")
108 |
109 | def get_task_by_id(self, task_id):
110 | """ Retrieve a task specified by id
111 |
112 | Args:
113 | task_id: task id formatted uuid
114 |
115 | Returns:
116 | A dictionary of task
117 |
118 | Raises:
119 | AnyDoAPIError:
120 | Code(420): JSON Decoding Error.
121 | """
122 | ret = self.api.task(uuid=task_id)
123 | try:
124 | return ret.json()
125 | except ValueError:
126 | raise AnyDoAPIError(420, "JSON Decoding Error")
127 |
128 | def delete_task_by_id(self, task_id):
129 | """ Delete a task specified by id
130 |
131 | Args:
132 | task_id: task id formatted uuid
133 |
134 | Returns:
135 | None
136 |
137 | Raises:
138 | AnyDoAPIError:
139 | Code(421): HTTP Error Code.
140 | """
141 | ret = self.api.delete_task(uuid=task_id)
142 | if ret.status_code != 204:
143 | raise AnyDoAPIError(421, "HTTP Error %d" % ret.status_code)
144 |
145 | def delete_category_by_id(self, category_id):
146 | """ Delete a category specified by id
147 |
148 | Args:
149 | category_id: category id formatted uuid
150 |
151 | Returns:
152 | None
153 |
154 | Raises:
155 | AnyDoAPIError:
156 | Code(421): HTTP Error Code.
157 | Code(422): Invalid Operation.
158 | """
159 | if category_id == self.__default_category_id():
160 | raise AnyDoAPIError(422, "Invalid Operation")
161 | ret = self.api.delete_category(uuid=category_id)
162 | if ret.status_code != 204:
163 | raise AnyDoAPIError(421, "HTTP Error %d" % ret.status_code)
164 |
165 | def create_new_category(self, category_name,
166 | default=False,
167 | list_position='null'):
168 | """ Create a new category
169 |
170 | Args:
171 | category_name: string of category name
172 | default: False in default
173 | list_position: 'null' in default
174 |
175 | Returns:
176 | A dictionary of category
177 |
178 | Raises:
179 | AnyDoAPIError:
180 | Code(420): JSON Decoding Error.
181 | """
182 | ret = self.api.create_category(category_name,
183 | default,
184 | isDefault="true" if default
185 | else "false",
186 | listPosition=list_position,
187 | id=create_uuid())
188 | try:
189 | return ret.json()
190 | except ValueError:
191 | raise AnyDoAPIError(420, "JSON Decoding Error")
192 |
193 | def create_new_task(self, task_title, due_day='someday'):
194 | """ Create a new task
195 |
196 | Args:
197 | task_title: string of task title
198 | due_day: 'someday' in default
199 |
200 | Returns:
201 | A dictionary of task
202 |
203 | Raises:
204 | AnyDoAPIError:
205 | Code(420): JSON Decoding Error.
206 | Code(422): Invalid Operation
207 | """
208 | try:
209 | ret = self.api.create_task(task_title,
210 | listPositionByCategory=0,
211 | listPositionByPriority=0,
212 | listPositionByDueDate=0,
213 | status="UNCHECKED",
214 | repeatingMethod="TASK_REPEAT_OFF",
215 | shared="false",
216 | priority="Normal",
217 | creationDate=int(time.time()),
218 | taskExpanded=False,
219 | categoryId=self.__default_category_id(),
220 | dueDate={'someday': None,
221 | 'today': 0}[due_day],
222 | id=create_uuid())
223 | return ret.json()
224 | except ValueError:
225 | raise AnyDoAPIError(420, "JSON Decoding Error")
226 | except KeyError:
227 | raise AnyDoAPIError(422, "Invalid Operation")
228 |
--------------------------------------------------------------------------------
/anydo/error.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """ anydo.error """
3 |
4 | class AnyDoAPIError(Exception):
5 | """An AnyDoAPI error occured."""
6 | def __init__(self, code, msg):
7 | super(AnyDoAPIError, self).__init__(code, msg)
8 | self.code = code
9 | self.msg = msg
10 |
11 | def __str__(self):
12 | return "(%s): %s" % (self.code, self.msg)
13 |
--------------------------------------------------------------------------------
/anydo/lib/__init__.py:
--------------------------------------------------------------------------------
1 | """ anydo.lib """
2 |
--------------------------------------------------------------------------------
/anydo/lib/auth.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """ anydo.lib.auth """
3 | import requests
4 | from anydo.lib import settings
5 |
6 |
7 | class AnyDoSession(object):
8 | """Authenticates with Any.Do"""
9 | def __init__(self, username=None, password=None):
10 | self.session = requests.session()
11 | AnyDoSession.post(self,
12 | url='https://sm-prod.any.do/j_spring_security_check',
13 | data={'j_username': username, 'j_password': password,
14 | '_spring_security_remember_me': 'on'},
15 | headers={'content-type':
16 | 'application/x-www-form-urlencoded'})
17 |
18 | def get(self, url, **kwargs):
19 | """ HTTP GET method for AnyDo.API
20 | Returns:
21 | :param url: URL for the AnyDo API resources
22 | :param **kwargs: Optional arguments that ``Request`` objects.
23 | """
24 | return self.session.get(url, proxies=settings.PROXIES, **kwargs)
25 |
26 | def post(self, url, data=None, **kwargs):
27 | """ HTTP POST method for AnyDo.API
28 | Returns:
29 | :param url: URL for the AnyDo API resources
30 | :param data: (optional) Dictionay,
31 | bytes to send in the body of the :class:`Request`
32 | :param **kwargs: Optional arguments that ``Request`` objects.
33 | """
34 | return self.session.post(url, data, proxies=settings.PROXIES, **kwargs)
35 |
36 | def delete(self, url, **kwargs):
37 | """ HTTP DELETE method for AnyDo.API
38 | Returns:
39 | :param url: URL for the AnyDo API resources
40 | :param **kwargs: Optional arguments that ``Request`` objects.
41 | """
42 | return self.session.delete(url, proxies=settings.PROXIES, **kwargs)
43 |
44 | def put(self, url, data=None, **kwargs):
45 | """ HTTP PUT method for AnyDo.API
46 | Returns:
47 | :param url: URL for the AnyDo API resources
48 | :param data: (optional) Dictionay,
49 | bytes to send in the body of the :class:`Request`
50 | :param **kwargs: Optional arguments that ``Request`` objects.
51 | """
52 | return self.session.put(url, data, proxies=settings.PROXIES, **kwargs)
53 |
--------------------------------------------------------------------------------
/anydo/lib/bind.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """ anydo.lib.bind """
3 | from anydo.lib.utils import encode_string
4 | from anydo.lib.error import AnyDoAPIBinderError
5 | from anydo.lib.auth import AnyDoSession
6 | import re
7 | import json
8 | PATH_TEMPLATE = re.compile(r"{\w+}") # #To support {variable} in paths
9 |
10 |
11 | def bind_method(**config):
12 | """
13 | Binds Any.Do REST API to AnyDoAPIBinder()
14 | """
15 | class AnyDoAPIBinderMethod(object):
16 | """ Method class for AnyDoAPIBinder """
17 | path = config['path']
18 | method = config['method']
19 | accepts_parameters = config.get("accepts_parameters", [])
20 |
21 | def __init__(self, api, *args, **kwargs):
22 | self.api = api
23 | self.parameters = {}
24 | self._build_parameters(*args, **kwargs)
25 | self._build_path()
26 |
27 | def _build_parameters(self, *args, **kwargs):
28 | """ building parameters sending to API.
29 | :param *args:
30 | :param **kwargs:
31 | """
32 | for index, value in enumerate(args):
33 | if value is None:
34 | continue
35 |
36 | try:
37 | self.parameters[self.accepts_parameters[index]] = \
38 | encode_string(value)
39 | except IndexError:
40 | raise AnyDoAPIBinderError("Index Overflow")
41 |
42 | for key, value in kwargs.items():
43 | if value is None:
44 | continue
45 |
46 | if key in self.parameters:
47 | raise AnyDoAPIBinderError(
48 | "Already have %s as %s" % (key, self.parameters[key])
49 | )
50 |
51 | self.parameters[key] = encode_string(value)
52 |
53 | def _build_path(self):
54 | """ building API path. """
55 | for var in PATH_TEMPLATE.findall(self.path):
56 | name = var.strip("{}")
57 |
58 | try:
59 | value = self.parameters[name]
60 | except KeyError:
61 | raise AnyDoAPIBinderError("Could not find %s" % name)
62 | del self.parameters[name] # #Python won my heart!
63 |
64 | self.path = self.path.replace(var, value)
65 |
66 | def execute(self):
67 | """ executing API calling. """
68 | if self.method == 'GET':
69 | return self.api.get(self.api.host + self.path,
70 | params=self.parameters)
71 | if self.method == 'DELETE':
72 | return self.api.delete(self.api.host + self.path)
73 | if self.method == 'POST':
74 | data = []
75 | data.append(self.parameters)
76 | return self.api.post(self.api.host + self.path,
77 | data=str(json.dumps([item
78 | for item in data])),
79 | headers={'Content-Type':
80 | 'application/json'})
81 | if self.method == 'PUT':
82 | return self.api.put(self.api.host + self.path,
83 | data=str(json.dumps(self.parameters)),
84 | headers={'Content-Type':
85 | 'application/json'})
86 |
87 | def _call(self, *args, **kwargs):
88 | """
89 | :param *args:
90 | :param **kwargs:
91 | """
92 | # self=AnyDoAPIBinder(); satisfy pychecker
93 | method = AnyDoAPIBinderMethod(self, *args, **kwargs)
94 | return method.execute()
95 |
96 | return _call
97 |
98 |
99 | class AnyDoAPIBinder(AnyDoSession):
100 | """ Binder of AnyDoSession class """
101 | host = "https://sm-prod.any.do"
102 |
103 | def __init__(self, username, password):
104 | super(AnyDoAPIBinder, self).__init__(username=username,
105 | password=password)
106 |
107 | # Fetches user information
108 | user_info = bind_method(path="/me",
109 | method="GET")
110 |
111 | # Fetches tasks (including notes)
112 | tasks = bind_method(path="/me/tasks",
113 | method="GET",
114 | accepts_parameters=["responseType",
115 | "includeDeleted",
116 | "includeDone"])
117 |
118 | # Fetches categories
119 | categories = bind_method(path="/me/categories",
120 | method="GET",
121 | accepts_parameters=["responseType",
122 | "includeDeleted",
123 | "includeDone"])
124 |
125 | # Fetches task/note by UUID
126 | task = bind_method(path="/me/tasks/{uuid}",
127 | method="GET",
128 | accepts_parameters=["uuid"])
129 |
130 | # Deletes a task/note by UUID
131 | delete_task = bind_method(path="/me/tasks/{uuid}",
132 | method="DELETE",
133 | accepts_parameters=["uuid"])
134 |
135 | # Deletes a category by UUID
136 | delete_category = bind_method(path="/me/categories/{uuid}",
137 | method="DELETE",
138 | accepts_parameters=["uuid"])
139 |
140 | # Creates a new category
141 | create_category = bind_method(path="/me/categories",
142 | method="POST",
143 | accepts_parameters=["name",
144 | "default",
145 | "isDefault",
146 | "listPosition",
147 | "id"])
148 |
149 | # Creates a new task/note
150 | create_task = bind_method(path="/me/tasks",
151 | method="POST",
152 | accepts_parameters=["title",
153 | "listPositionByCategory",
154 | "listPositionByPriority",
155 | "listPositionByDueDate",
156 | "status",
157 | "repeatingMethod",
158 | "shared",
159 | "priority",
160 | "creationDate",
161 | "taskExpanded",
162 | "parentGlobalTaskId",
163 | "categoryId",
164 | "dueDate",
165 | "id"])
166 |
--------------------------------------------------------------------------------
/anydo/lib/error.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """ anydo.lib.error """
3 |
4 |
5 | class AnyDoAPIBinderError(Exception):
6 | """An AnyDoAPIBinder error occured."""
7 | def __init__(self, msg):
8 | super(AnyDoAPIBinderError, self).__init__(msg)
9 | self.msg = msg
10 |
11 | def __str__(self):
12 | return self.msg
13 |
--------------------------------------------------------------------------------
/anydo/lib/settings.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """ anydo.lib.settings """
3 | PROXIES = None
4 | USERNAME = "username@example.org"
5 | PASSWORD = "password"
6 |
--------------------------------------------------------------------------------
/anydo/lib/tests/__init__.py:
--------------------------------------------------------------------------------
1 | """ anydo.lib.tests """
2 |
--------------------------------------------------------------------------------
/anydo/lib/tests/test_api.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """ anydo.lib.tests.test_api """
3 |
4 | import unittest
5 | from httpretty import HTTPretty, httprettified
6 | from anydo import api
7 | from anydo.error import AnyDoAPIError
8 | from anydo.lib.bind import AnyDoAPIBinder
9 | from anydo.lib import settings
10 | from anydo.lib import utils
11 |
12 |
13 | class AnyDoAPITests(unittest.TestCase):
14 | """ unit test of anydo.lib.api """
15 |
16 | @httprettified
17 | def setUp(self):
18 | HTTPretty.register_uri(
19 | HTTPretty.POST,
20 | '%s/j_spring_security_check' % AnyDoAPIBinder.host)
21 | self.conn = api.AnyDoAPI(username=settings.USERNAME,
22 | password=settings.PASSWORD)
23 |
24 | @httprettified
25 | def test_get_user_info(self):
26 | """ unit test of get_user_info """
27 | HTTPretty.register_uri(
28 | HTTPretty.GET,
29 | '%s/me' % AnyDoAPIBinder.host,
30 | body='{"dummy_key": "dummy_value"}')
31 | self.assertEqual({'dummy_key': 'dummy_value'},
32 | self.conn.get_user_info())
33 |
34 | @httprettified
35 | def test_get_user_info_fail(self):
36 | """ unit test of get_user_info error case """
37 | HTTPretty.register_uri(
38 | HTTPretty.GET,
39 | '%s/me' % AnyDoAPIBinder.host)
40 | self.assertRaises(AnyDoAPIError, self.conn.get_user_info)
41 |
42 | @httprettified
43 | def test_get_all_tasks(self):
44 | """ unit test of get_all_tasks """
45 | HTTPretty.register_uri(
46 | HTTPretty.GET,
47 | '%s/me/tasks' % AnyDoAPIBinder.host,
48 | body='{"dummy_key": "dummy_value"}')
49 | self.assertEqual({"dummy_key": "dummy_value"},
50 | self.conn.get_all_tasks())
51 |
52 | @httprettified
53 | def test_get_all_tasks_fail(self):
54 | """ unit test of get_all_tasks error case """
55 | HTTPretty.register_uri(
56 | HTTPretty.GET,
57 | '%s/me/tasks' % AnyDoAPIBinder.host)
58 | self.assertRaises(AnyDoAPIError, self.conn.get_all_tasks)
59 |
60 | @httprettified
61 | def test_get_all_categories(self):
62 | """ unit test of get_all_categories """
63 | HTTPretty.register_uri(
64 | HTTPretty.GET,
65 | '%s/me/categories' % AnyDoAPIBinder.host,
66 | body='{"dummy_key": "dummy_value"}')
67 | self.assertEqual({"dummy_key": "dummy_value"},
68 | self.conn.get_all_categories())
69 |
70 | @httprettified
71 | def test_get_all_categories_fail(self):
72 | """ unit test of get_all_categories error case """
73 | HTTPretty.register_uri(
74 | HTTPretty.GET,
75 | '%s/me/categories' % AnyDoAPIBinder.host)
76 | self.assertRaises(AnyDoAPIError, self.conn.get_all_categories)
77 |
78 | @httprettified
79 | def test_get_task_by_id(self):
80 | """ unit test of get_task_by_id """
81 | uuid = utils.create_uuid()
82 | HTTPretty.register_uri(
83 | HTTPretty.GET,
84 | '%s/me/tasks/%s' % (AnyDoAPIBinder.host, uuid),
85 | body='{"dummy_key": "dummy_value"}')
86 | self.assertEqual({"dummy_key": "dummy_value"},
87 | self.conn.get_task_by_id(uuid))
88 |
89 | @httprettified
90 | def test_get_task_by_id_fail(self):
91 | """ unit test of get_task_by_id error case """
92 | uuid = utils.create_uuid()
93 | HTTPretty.register_uri(
94 | HTTPretty.GET,
95 | '%s/me/tasks/%s' % (AnyDoAPIBinder.host, uuid))
96 | self.assertRaises(AnyDoAPIError, self.conn.get_task_by_id, uuid)
97 |
98 | @httprettified
99 | def test_delete_task_by_id(self):
100 | """ unit test of delete_task_by_id """
101 | uuid = utils.create_uuid()
102 | HTTPretty.register_uri(
103 | HTTPretty.DELETE,
104 | '%s/me/tasks/%s' % (AnyDoAPIBinder.host, uuid),
105 | status=204)
106 | self.assertEqual(None,
107 | self.conn.delete_task_by_id(uuid))
108 |
109 | @httprettified
110 | def test_delete_task_by_id_fail(self):
111 | """ unit test of delete_task_by_id error case """
112 | uuid = utils.create_uuid()
113 | HTTPretty.register_uri(
114 | HTTPretty.DELETE,
115 | '%s/me/tasks/%s' % (AnyDoAPIBinder.host, uuid),
116 | status=503)
117 | self.assertRaises(AnyDoAPIError, self.conn.delete_task_by_id, uuid)
118 |
119 | @httprettified
120 | def test_delete_category_by_id(self):
121 | """ unit test of delete_category_by_id """
122 | uuid = utils.create_uuid()
123 | HTTPretty.register_uri(
124 | HTTPretty.GET,
125 | '%s/me/categories' % AnyDoAPIBinder.host,
126 | body='[{"isDefault": true, "id": "dummy_category0"}]')
127 | HTTPretty.register_uri(
128 | HTTPretty.DELETE,
129 | '%s/me/categories/%s' % (AnyDoAPIBinder.host, uuid),
130 | status=204)
131 | self.assertEqual(None,
132 | self.conn.delete_category_by_id(uuid))
133 |
134 | @httprettified
135 | def test_delete_category_by_id_fail(self):
136 | """ unit test of delete_category_by_id error case """
137 | uuid = utils.create_uuid()
138 | HTTPretty.register_uri(
139 | HTTPretty.GET,
140 | '%s/me/categories' % AnyDoAPIBinder.host,
141 | body='[{"isDefault": true, "id": "dummy_category0"}]')
142 | HTTPretty.register_uri(
143 | HTTPretty.DELETE,
144 | '%s/me/categories/%s' % (AnyDoAPIBinder.host, uuid),
145 | status=503)
146 | self.assertRaises(AnyDoAPIError, self.conn.delete_category_by_id, uuid)
147 |
148 | @httprettified
149 | def test_create_new_category(self):
150 | """ unit test of create_new_category """
151 | uuid = utils.create_uuid()
152 | HTTPretty.register_uri(
153 | HTTPretty.POST,
154 | '%s/me/categories' % AnyDoAPIBinder.host,
155 | body='{"dummy_key": "dummy_value"}',
156 | status=201)
157 | self.assertEqual({"dummy_key": "dummy_value"},
158 | self.conn.create_new_category(uuid))
159 |
160 | @httprettified
161 | def test_create_new_category_fail(self):
162 | """ unit test of create_new_category error case """
163 | uuid = utils.create_uuid()
164 | HTTPretty.register_uri(
165 | HTTPretty.POST,
166 | '%s/me/categories' % AnyDoAPIBinder.host)
167 | self.assertRaises(AnyDoAPIError, self.conn.create_new_category, uuid)
168 |
169 | @httprettified
170 | def test_create_new_task(self):
171 | """ unit test of create_new_task """
172 | uuid = utils.create_uuid()
173 | HTTPretty.register_uri(
174 | HTTPretty.GET,
175 | '%s/me/categories' % AnyDoAPIBinder.host,
176 | body='[{"isDefault": true, "id": "dummy_category0"}]')
177 |
178 | HTTPretty.register_uri(
179 | HTTPretty.POST,
180 | '%s/me/tasks' % AnyDoAPIBinder.host,
181 | body='{"dummy_key": "dummy_value"}',
182 | status=201)
183 | self.assertEqual({"dummy_key": "dummy_value"},
184 | self.conn.create_new_task(uuid))
185 |
186 | @httprettified
187 | def test_create_new_task_fail(self):
188 | """ unit test of create_new_task error case """
189 | uuid = utils.create_uuid()
190 | HTTPretty.register_uri(
191 | HTTPretty.GET,
192 | '%s/me/categories' % AnyDoAPIBinder.host,
193 | body='[{"isDefault": true, "id": "dummy_category0"}]')
194 |
195 | HTTPretty.register_uri(
196 | HTTPretty.POST,
197 | '%s/me/tasks' % AnyDoAPIBinder.host)
198 | self.assertRaises(AnyDoAPIError, self.conn.create_new_task, uuid)
199 |
--------------------------------------------------------------------------------
/anydo/lib/tests/test_error.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """ anydo.lib.tests.test_error """
3 |
4 | import unittest
5 | from anydo import error
6 | from anydo.lib import error as lib_error
7 |
8 |
9 | class ErrorTests(unittest.TestCase):
10 | """ unit test of anydo.error and anydo.lib.error """
11 |
12 | def test_error_msg(self):
13 | """ unit test error_msg of anydo.error """
14 | self.assertEqual(error.AnyDoAPIError('dummy', 'test').__str__(),
15 | '(dummy): test')
16 |
17 | def test_lib_error_msg(self):
18 | """ unit test error_msg of anydo.lib.error """
19 | self.assertEqual(lib_error.AnyDoAPIBinderError('test').__str__(),
20 | 'test')
21 |
--------------------------------------------------------------------------------
/anydo/lib/tests/test_lib.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """ anydo.lib.tests.test_lib """
3 | import unittest
4 | from httpretty import HTTPretty, httprettified
5 | import time
6 | from anydo.lib.bind import AnyDoAPIBinder
7 | from anydo.lib import settings
8 | from anydo.lib import utils
9 |
10 |
11 | class TestAnyDoAPIBinder(AnyDoAPIBinder):
12 | """ unit test of AnyDoAPIBinder class """
13 | def __getattribute__(self, attr):
14 | val = super(TestAnyDoAPIBinder, self).__getattribute__(attr)
15 | return val
16 |
17 |
18 | class AnyDoAPIBinderTests(unittest.TestCase):
19 | """ unit test of AnyDoAPIBind """
20 | setup_done = False # #TODO: setUpClass ?
21 |
22 | @httprettified
23 | def setUp(self):
24 | if not self.setup_done:
25 | HTTPretty.register_uri(
26 | HTTPretty.POST,
27 | '%s/j_spring_security_check' % AnyDoAPIBinder.host)
28 | super(AnyDoAPIBinderTests, self).setUp()
29 | self.__class__.api = TestAnyDoAPIBinder(username=settings.USERNAME,
30 | password=settings.PASSWORD)
31 | self.__class__.setup_done = True
32 |
33 | @httprettified
34 | def test_user_info(self):
35 | """ unit test of user_info """
36 | HTTPretty.register_uri(HTTPretty.GET,
37 | '%s/me' % AnyDoAPIBinder.host)
38 | ret = self.api.user_info()
39 | self.assertEqual(ret.status_code, 200)
40 |
41 | @httprettified
42 | def test_tasks(self):
43 | """ unit test of tasks """
44 | HTTPretty.register_uri(HTTPretty.GET,
45 | '%s/me/tasks' % AnyDoAPIBinder.host)
46 | ret = self.api.tasks(responseType="flat",
47 | includeDeleted="false",
48 | includeDone="false")
49 | self.assertEqual(ret.status_code, 200)
50 |
51 | @httprettified
52 | def test_categories(self):
53 | """ unit test of categories """
54 | HTTPretty.register_uri(HTTPretty.GET,
55 | '%s/me/categories' % AnyDoAPIBinder.host)
56 | ret = self.api.categories(responseType="flat",
57 | includeDeleted="false",
58 | includeDone="false")
59 | self.assertEqual(ret.status_code, 200)
60 |
61 | @httprettified
62 | def test_task(self):
63 | """ unit test of task """
64 | task_id = utils.create_uuid()
65 | HTTPretty.register_uri(HTTPretty.GET,
66 | '%s/me/tasks/%s' % (AnyDoAPIBinder.host,
67 | task_id))
68 | ret = self.api.task(uuid=task_id)
69 | self.assertEqual(ret.status_code, 200)
70 |
71 | @httprettified
72 | def test_delete_task(self):
73 | """ unit test of delete task """
74 | task_id = utils.create_uuid()
75 | HTTPretty.register_uri(HTTPretty.DELETE,
76 | '%s/me/tasks/%s' % (AnyDoAPIBinder.host,
77 | task_id),
78 | status=204)
79 | ret = self.api.delete_task(uuid=task_id)
80 | self.assertEqual(ret.status_code, 204)
81 |
82 | @httprettified
83 | def test_delete_category(self):
84 | """ unit test of delete category """
85 | category_id = utils.create_uuid()
86 | HTTPretty.register_uri(HTTPretty.DELETE,
87 | '%s/me/categories/%s' % (AnyDoAPIBinder.host,
88 | category_id),
89 | status=204)
90 | ret = self.api.delete_category(uuid=category_id)
91 | self.assertEqual(ret.status_code, 204)
92 |
93 | @httprettified
94 | def test_create_category(self):
95 | """ unit test of create category """
96 | category_id = utils.create_uuid()
97 | HTTPretty.register_uri(HTTPretty.POST,
98 | '%s/me/categories' % AnyDoAPIBinder.host,
99 | status=201)
100 | ret = self.api.create_category(name="ANY.DO_TEST_CATEGORY",
101 | default="false",
102 | isDefault="false",
103 | listPosition="null",
104 | id=category_id)
105 | self.assertEqual(ret.status_code, 201)
106 |
107 | @httprettified
108 | def test_create_task(self):
109 | """ unit test of create task """
110 | category_id = utils.create_uuid()
111 | task_id = utils.create_uuid()
112 | HTTPretty.register_uri(HTTPretty.POST,
113 | '%s/me/tasks' % AnyDoAPIBinder.host,
114 | status=200)
115 | ret = self.api.create_task(title="ANY.DO_TEST_TASK",
116 | listPositionByCategory=0,
117 | listPositionByPriority=0,
118 | listPositionByDueDate=0,
119 | status="UNCHECKED",
120 | repeatingMethod="TASK_REPEAT_OFF",
121 | shared="false",
122 | priority="Normal",
123 | creationDate=str(int(time.time())),
124 | taskExpanded="false",
125 | categoryId=str(category_id),
126 | dueDate=None,
127 | id=task_id)
128 | self.assertEqual(ret.status_code, 200)
129 |
130 | @httprettified
131 | def test_create_note(self):
132 | """ unit test of create note """
133 | task_id = utils.create_uuid()
134 | category_id = utils.create_uuid()
135 | note_id = utils.create_uuid()
136 | HTTPretty.register_uri(HTTPretty.POST,
137 | '%s/me/tasks' % AnyDoAPIBinder.host)
138 | # Add note in task
139 | ret = self.api.create_task(title="ANY.DO_TEST_NOTE",
140 | listPositionByCategory=0,
141 | listPositionByPriority=0,
142 | listPositionByDueDate=0,
143 | status="UNCHECKED",
144 | repeatingMethod="TASK_REPEAT_OFF",
145 | shared="false",
146 | priority="Normal",
147 | creationDate=str(int(time.time())),
148 | taskExpanded="false",
149 | parentGlobalTaskId=task_id,
150 | categoryId=str(category_id),
151 | dueDate=None,
152 | id=note_id)
153 | self.assertEqual(ret.status_code, 200)
154 |
--------------------------------------------------------------------------------
/anydo/lib/tests/test_pep8.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Copyright 2011 Takeshi KOMIYA
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 |
17 | original source from: ('https://bitbucket.org/tk0miya/blockdiag'
18 | '/src/0789c102744c92767f0f0efb87b1b297741bb04c'
19 | '/src/blockdiag/tests/test_pep8.py')
20 | """
21 | from __future__ import print_function
22 | import unittest
23 | import os
24 | import sys
25 | import pep8
26 | CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
27 | BASE_DIR = os.path.dirname(CURRENT_DIR)
28 | sys.path.append(CURRENT_DIR)
29 |
30 |
31 | class Pep8Tests(unittest.TestCase):
32 | """ Unit test Pep8 """
33 |
34 | def test_pep8(self):
35 | """ runner """
36 | arglist = [['statistics', True],
37 | ['show-source', True],
38 | ['repeat', True],
39 | ['paths', [BASE_DIR]]]
40 |
41 | pep8style = pep8.StyleGuide(arglist,
42 | parse_argv=False,
43 | config_file=True)
44 | options = pep8style.options
45 | report = pep8style.check_files()
46 | if options.statistics:
47 | report.print_statistics()
48 |
49 | # reporting errors (additional summary)
50 | errors = report.get_count('E')
51 | warnings = report.get_count('W')
52 | message = 'pep8: %d errors / %d warnings' % (errors, warnings)
53 | print(message)
54 | assert report.total_errors == 0, message
55 |
--------------------------------------------------------------------------------
/anydo/lib/tests/test_utils.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """ anydo.lib.tests.test_utils """
3 | import unittest
4 | import re
5 | import sys
6 | from anydo.lib import utils
7 |
8 |
9 | class UtilsTests(unittest.TestCase):
10 | """ unit test of anydo.lib.utils """
11 |
12 | def setUp(self):
13 | self.pattern = re.compile(r'(^([\w-]+)==$)', flags=re.U)
14 |
15 | def test_create_uuid(self):
16 | """ unit test of create_uuid """
17 | self.assertTrue(self.pattern.match(utils.create_uuid()))
18 |
19 | def test_encode_string(self):
20 | """ unit test of encode_string """
21 | self.assertEqual(utils.encode_string('test'), 'test')
22 | self.assertEqual(utils.encode_string('1234'), '1234')
23 | self.assertEqual(utils.encode_string('test1234 Äë'), 'test1234 Äë')
24 |
25 | # "テスト" means "test" in Japansese.
26 | if sys.version_info < (3, 0):
27 | word = ('\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88 123eA'
28 | '\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88')
29 | else:
30 | word = 'テスト 123eAテスト'
31 | self.assertEqual(utils.encode_string('テスト 123eAテスト'), word)
32 |
--------------------------------------------------------------------------------
/anydo/lib/utils.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """ anydo.lib.utils """
3 | import sys
4 | import base64
5 | import uuid
6 |
7 |
8 | def encode_string(value):
9 | """ encoding string
10 | Returns: value | utf-8 encoded string
11 | :param value: utf-8 encoded string or unicode
12 | """
13 | if sys.version_info < (3, 0):
14 | return value.encode('utf-8') \
15 | if isinstance(value, unicode) else str(value)
16 | else:
17 | return value
18 |
19 |
20 | def create_uuid():
21 | """ creating uuid.
22 | Returns: utf-8 encoded and base64 encoded uuid that is as follows;
23 | e.g. u'08s-gOkKSWC26ntWUGkKIQ=='
24 | """
25 | unique_id = base64.b64encode(uuid.uuid4().bytes)
26 | safe_id = unique_id.replace(b"+", b"-").replace(b"/", b"_")
27 | return safe_id.decode('utf-8')
28 |
--------------------------------------------------------------------------------
/bin/runtest.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | clean () {
4 | #python
5 | find . -name "*.pyc" -delete
6 |
7 | #linux
8 | find . -name "*~" -delete
9 | }
10 |
11 | cd $(git rev-parse --show-toplevel)
12 | clean
13 |
14 | if which pip > /dev/null; then
15 | pip install --upgrade pylint
16 | pip install --upgrade tox
17 | pylint --rcfile=.pylint.rc anydo/*.py anydo/lib/*.py anydo/lib/tests/*.py
18 | tox
19 | fi
20 |
21 | clean
22 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from setuptools import setup, find_packages
4 | from setuptools.command.test import test as TestCommand
5 | import sys
6 |
7 |
8 | class Tox(TestCommand):
9 | def finalize_options(self):
10 | TestCommand.finalize_options(self)
11 | self.test_args = []
12 | self.test_suite = True
13 |
14 | def run_tests(self):
15 | import tox
16 | errno = tox.cmdline(self.test_args)
17 | sys.exit(errno)
18 |
19 |
20 | classifiers = [
21 | "License :: OSI Approved :: MIT License",
22 | "Programming Language :: Python",
23 | "Programming Language :: Python :: 2",
24 | "Programming Language :: Python :: 2.6",
25 | "Programming Language :: Python :: 2.7",
26 | "Programming Language :: Python :: 3",
27 | "Programming Language :: Python :: 3.2",
28 | "Programming Language :: Python :: 3.3",
29 | "Programming Language :: Python :: Implementation :: CPython",
30 | "Programming Language :: Python :: Implementation :: PyPy",
31 | "Topic :: Internet",
32 | "Topic :: Internet :: WWW/HTTP",
33 | "Topic :: Software Development :: Libraries :: Python Modules",
34 | ]
35 |
36 | setup(name="python-anydo",
37 | version="0.1.0",
38 | description="Unoffical Any.Do API client",
39 | license="MIT",
40 | install_requires=["requests"],
41 | author="Gaurav Kalra",
42 | author_email="gvkalra@gmail.com",
43 | url="https://github.com/gvkalra/python-anydo",
44 | classifiers=classifiers,
45 | packages=find_packages(),
46 | keywords="anydo",
47 | tests_require=['tox'],
48 | cmdclass={'test': Tox},
49 | zip_safe=True)
50 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = py26,py27,py32,py33,py34,pypy
3 |
4 | [testenv]
5 | commands =
6 | {envpython} --version
7 | coverage run --source=anydo -m py.test -v
8 |
9 | [py]
10 | deps =
11 | pytest
12 | pep8
13 | coverage
14 | httpretty
15 |
16 | [testenv:py26]
17 | deps=
18 | {[py]deps}
19 | basepython = python2.6
20 |
21 | [testenv:py27]
22 | deps=
23 | {[py]deps}
24 | basepython = python2.7
25 |
26 | [testenv:py32]
27 | deps=
28 | {[py]deps}
29 | basepython = python3.2
30 |
31 | [testenv:py33]
32 | deps=
33 | {[py]deps}
34 | basepython = python3.3
35 |
36 | [testenv:py34]
37 | deps=
38 | {[py]deps}
39 | basepython = python3.4
40 |
41 | [testenv:pypy]
42 | deps=
43 | {[py]deps}
44 | basepython = pypy
45 |
--------------------------------------------------------------------------------