diff --git a/.travis.yml b/.travis.yml index 51af248..f4beaa1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,30 +1,42 @@ language: python python: - - '2.6' - - '2.7' - - '3.2' - - '3.3' +- '2.6' +- '2.7' +- '3.2' +- '3.3' before_install: - - sudo apt-get update -qq - - sudo apt-get install -qq pandoc +- sudo apt-get update -qq +- sudo apt-get install -qq pandoc install: - - if [[ $TRAVIS_PYTHON_VERSION == 2.6 ]]; then pip install --use-mirrors unittest2; fi - - pip install -r requirements.txt - - pip install -r test-requirements.txt +- if [[ $TRAVIS_PYTHON_VERSION == 2.6 ]]; then + pip install --use-mirrors unittest2; + fi +- pip install . +- pip install -r requirements.txt +- pip install -r test-requirements.txt script: ./build.sh after_success: coveralls deploy: - provider: pypi - user: odi - password: - secure: MU3ZQ4rcpsXo0xIYSWXBfaKTAPn1IrL7AEcH231sseFV1RVmdC96Sfmtc2llvD9Eoc0KJpdW0Vy50azNqAMJwXCt/q3gagfao1PTnAEbklU+g1s2PTqW401E95Qm6w192WzWk/q0dy3SJwxEQt023QR78K+nEcYaCdLWDHjR2hY= - on: - branch: master - python: 2.7 - repo: metaodi/osmapi + - provider: pypi + user: odi + password: + secure: MU3ZQ4rcpsXo0xIYSWXBfaKTAPn1IrL7AEcH231sseFV1RVmdC96Sfmtc2llvD9Eoc0KJpdW0Vy50azNqAMJwXCt/q3gagfao1PTnAEbklU+g1s2PTqW401E95Qm6w192WzWk/q0dy3SJwxEQt023QR78K+nEcYaCdLWDHjR2hY= + on: + branch: master + python: 2.7 + repo: metaodi/osmapi + - provider: divshot + skip_cleanup: true + environment: + develop: development + master: production + on: + python: 2.7 + api_key: + secure: WTpFZfmA9QOPGLp9c+yljSVLk8GijvlMT4FxPX1BlI0cdU0VHHBkKfRk+UW3n8vHBAcaH+1SlbPc+9BWw9oHirtJ5Wc57SNfWS1NHKVB/rUxVCn+GYM6RBN9wJ26St0KNG81VtvajkA41ZP5jkpWVFtbylbctcTmVNagJke7cUE= diff --git a/README.md b/README.md index ac49d03..ef2fa08 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,10 @@ Scripted imports and automated edits should only be carried out by those with ex See the [Import/Guidelines](http://wiki.openstreetmap.org/wiki/Import/Guidelines) and [Automated Edits/Code of Conduct](http://wiki.openstreetmap.org/wiki/Automated_Edits/Code_of_Conduct) for more information. +## Documentation + +The documentation is generated using `pdoc` and can be [viewed online](http://osmapi.divshot.io/). + ## Examples ### Read from OpenStreetMap diff --git a/build.sh b/build.sh index 9c03807..64a4f81 100755 --- a/build.sh +++ b/build.sh @@ -13,3 +13,8 @@ flake8 --show-pep8 --show-source . # run tests nosetests --verbose --with-coverage + +# generate docs (currently it's not possible to generate docs in Python 2.6) +if [[ $TRAVIS_PYTHON_VERSION != 2.6 ]]; then + pdoc --html --overwrite osmapi/OsmApi.py +fi diff --git a/divshot.json b/divshot.json new file mode 100644 index 0000000..06e7861 --- /dev/null +++ b/divshot.json @@ -0,0 +1,7 @@ +{ + "name": "osmapi", + "root": ".", + "routes": { + "**": "OsmApi.m.html" + } +} diff --git a/osmapi/OsmApi.py b/osmapi/OsmApi.py index 7d21fed..5518d24 100644 --- a/osmapi/OsmApi.py +++ b/osmapi/OsmApi.py @@ -15,7 +15,11 @@ if getattr(urllib, 'urlencode', None) is None: urllib.urlencode = urllib.parse.urlencode -from . import __version__ +from osmapi import __version__ + + +class UsernamePasswordMissingError(Exception): + pass class ApiError(Exception): @@ -509,10 +513,8 @@ def ChangesetUpload(self, ChangesData): ).decode("utf-8") data += "\n" data += "" - data = self._http( - "POST", + data = self._post( "/api/0.6/changeset/"+str(self._CurrentChangesetId)+"/upload", - True, data.encode("utf-8") ) data = xml.dom.minidom.parseString(data) @@ -597,6 +599,127 @@ def ChangesetsGet( # noqa result[tmpCS["id"]] = tmpCS return result + ################################################## + # Notes # + ################################################## + + def NotesGet(self, min_lon, min_lat, max_lon, max_lat, + limit=100, closed=7): + """ + Returns a list of dicts of notes in the specified bounding box. + + The limit parameter defines how many results should be returned. + + closed specifies the number of days a bug needs to be closed + to no longer be returned. + The value 0 means only open bugs are returned, + -1 means all bugs are returned. + """ + uri = ( + "/api/0.6/notes?bbox=%f,%f,%f,%f&limit=%d&closed=%d" + % (min_lon, min_lat, max_lon, max_lat, limit, closed) + ) + data = self._get(uri) + return self.ParseNotes(data) + + def NoteGet(self, id): + """ + Returns a note as dict: + { + id: integer, + action: opened|commented|closed, + status: open|closed + date_created: creation date + date_closed: closing data|None + uid: User ID|None + user: User name|None + comments: {} + }. + """ + uri = "/api/0.6/notes/%s" % (id) + data = self._get(uri) + data = xml.dom.minidom.parseString(data) + osm_data = data.getElementsByTagName("osm")[0] + + noteElement = osm_data.getElementsByTagName("note")[0] + note = self._DomParseNote(noteElement) + return note + + def NoteCreate(self, NoteData): + """ + Creates a note. + Returns updated NoteData (without timestamp). + """ + uri = "/api/0.6/notes" + uri += "?" + urllib.urlencode(NoteData) + return self._NoteAction(uri) + + def NoteComment(self, NoteId, comment): + """ + Adds a new comment to a note. + Returns the updated note. + """ + path = "/api/0.6/notes/%s/comment" % NoteId + return self._NoteAction(path, comment) + + def NoteClose(self, NoteId, comment): + """ + Closes a note. + Returns the updated note. + """ + path = "/api/0.6/notes/%s/close" % NoteId + return self._NoteAction(path, comment, optionalAuth=False) + + def NoteReopen(self, NoteId, comment): + """ + Reopens a note. + Returns the updated note. + """ + path = "/api/0.6/notes/%s/reopen" % NoteId + return self._NoteAction(path, comment, optionalAuth=False) + + def NotesSearch(self, query, limit=100, closed=7): + """ + Returns a list of dicts of notes that match the given search query. + + The limit parameter defines how many results should be returned. + + closed specifies the number of days a bug needs to be closed + to no longer be returned. + The value 0 means only open bugs are returned, + -1 means all bugs are returned. + """ + uri = "/api/0.6/notes/search" + params = {} + params['q'] = query + params['limit'] = limit + params['closed'] = closed + uri += "?" + urllib.urlencode(params) + data = self._get(uri) + + return self.ParseNotes(data) + + def _NoteAction(self, path, comment=None, optionalAuth=True): + """ + Performs an action on a Note with a comment + Return the updated note + """ + uri = path + if comment is not None: + params = {} + params['text'] = comment + uri += "?" + urllib.urlencode(params) + result = self._post(uri, None, optionalAuth=optionalAuth) + + # parse the result + data = xml.dom.minidom.parseString(result) + osm_data = data.getElementsByTagName("osm")[0] + + noteElement = osm_data.getElementsByTagName("note")[0] + note = self._DomParseNote(noteElement) + + return note + ################################################## # Other # ################################################## @@ -688,6 +811,16 @@ def ParseOsc(self, data): }) return result + def ParseNotes(self, data): + data = xml.dom.minidom.parseString(data) + result = [] + osm_data = data.getElementsByTagName("osm")[0] + + for noteElement in osm_data.getElementsByTagName("note"): + note = self._DomParseNote(noteElement) + result.append(note) + return result + ################################################## # Internal http function # ################################################## @@ -739,6 +872,9 @@ def _do_manu(self, action, OsmType, OsmData): return OsmData def flush(self): + """ + Force the changes to be uploaded to OSM and the changeset to be closed + """ return self._changesetautoflush(True) def _changesetautoflush(self, force=False): @@ -773,7 +909,10 @@ def _http_request(self, cmd, path, auth, send): # noqa self._conn.putrequest(cmd, path) self._conn.putheader('User-Agent', self._created_by) if auth: - user_pass = self._username + ':' + self._password + try: + user_pass = self._username + ':' + self._password + except AttributeError: + raise UsernamePasswordMissingError("Username/Password missing") try: # Python 2 @@ -836,6 +975,13 @@ def _get(self, path): def _put(self, path, data): return self._http('PUT', path, True, data) + def _post(self, path, data, optionalAuth=False): + auth = True + # the Notes API allows certain POSTs by non-authenticated users + if optionalAuth: + auth = hasattr(self, '_username') + return self._http('POST', path, auth, data) + def _delete(self, path, data): return self._http('DELETE', path, True, data) @@ -890,6 +1036,22 @@ def _DomGetNd(self, DomElement): result.append(int(int(t.attributes["ref"].value))) return result + def _DomGetComments(self, DomElement): + """ + Returns the list of comments of a DomElement. + """ + result = [] + for t in DomElement.getElementsByTagName("comment"): + comment = {} + comment['date'] = self._GetXmlValue(t, "date") + comment['action'] = self._GetXmlValue(t, "action") + comment['text'] = self._GetXmlValue(t, "text") + comment['html'] = self._GetXmlValue(t, "html") + comment['uid'] = self._GetXmlValue(t, "uid") + comment['user'] = self._GetXmlValue(t, "user") + result.append(comment) + return result + def _DomGetMember(self, DomElement): """ Returns a list of relation members. @@ -933,6 +1095,20 @@ def _DomParseChangeset(self, DomElement): result["tag"] = self._DomGetTag(DomElement) return result + def _DomParseNote(self, DomElement): + """ + Returns NoteData for the note. + """ + result = self._DomGetAttributes(DomElement) + result["id"] = self._GetXmlValue(DomElement, "id") + result["status"] = self._GetXmlValue(DomElement, "status") + + result["date_created"] = self._GetXmlValue(DomElement, "date_created") + result["date_closed"] = self._GetXmlValue(DomElement, "date_closed") + result["comments"] = self._DomGetComments(DomElement) + + return result + ################################################## # Internal xml builder # ################################################## @@ -993,3 +1169,10 @@ def _XmlEncode(self, text): .replace("<", "<") .replace(">", ">") ) + + def _GetXmlValue(self, DomElement, tag): + try: + elem = DomElement.getElementsByTagName(tag)[0] + return elem.firstChild.nodeValue + except: + return None diff --git a/osmapi/__init__.py b/osmapi/__init__.py index ea202bb..6e50aba 100644 --- a/osmapi/__init__.py +++ b/osmapi/__init__.py @@ -1,5 +1,5 @@ from __future__ import (absolute_import, print_function, unicode_literals) -__version__ = '0.3.1' +__version__ = '0.4.0' from .OsmApi import * # noqa diff --git a/requirements.txt b/requirements.txt index 756b46f..80475dc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ # Install with a command like: pip install -r pip-requirements.txt pypandoc==0.7.0 Unidecode==0.04.14 +pdoc==0.2.4 diff --git a/tests/changeset_tests.py b/tests/changeset_tests.py index a4fa487..fdaa30e 100644 --- a/tests/changeset_tests.py +++ b/tests/changeset_tests.py @@ -98,10 +98,10 @@ def test_ChangesetUpdate(self): xmltosorteddict(args[3]), xmltosorteddict( b'\n' - b'\n' + b'\n' b' \n' b' \n' - b' \n' + b' \n' b' \n' b'\n' ) @@ -132,7 +132,7 @@ def test_ChangesetUpdate_with_created_by(self): xmltosorteddict(args[3]), xmltosorteddict( b'\n' - b'\n' + b'\n' b' \n' b' \n' b' \n' @@ -171,10 +171,10 @@ def test_ChangesetCreate(self): xmltosorteddict(args[3]), xmltosorteddict( b'\n' - b'\n' + b'\n' b' \n' b' \n' - b' \n' + b' \n' b' \n' b'\n' ) @@ -199,7 +199,7 @@ def test_ChangesetCreate_with_created_by(self): xmltosorteddict(args[3]), xmltosorteddict( b'\n' - b'\n' + b'\n' b' \n' b' \n' b' \n' @@ -285,7 +285,7 @@ def test_ChangesetUpload_create_node(self): xmltosorteddict(args[3]), xmltosorteddict( b'\n' - b'\n' + b'\n' b'\n' b' \n' @@ -359,7 +359,7 @@ def test_ChangesetUpload_modify_way(self): xmltosorteddict(args[3]), xmltosorteddict( b'\n' - b'\n' + b'\n' b'\n' b' \n' @@ -443,7 +443,7 @@ def test_ChangesetUpload_delete_relation(self): xmltosorteddict(args[3]), xmltosorteddict( b'\n' - b'\n' + b'\n' b'\n' b' \n' diff --git a/tests/fixtures/test_NoteClose.xml b/tests/fixtures/test_NoteClose.xml new file mode 100644 index 0000000..63f75a3 --- /dev/null +++ b/tests/fixtures/test_NoteClose.xml @@ -0,0 +1,31 @@ + + + + 815 + http://api06.dev.openstreetmap.org/api/0.6/notes/815 + http://api06.dev.openstreetmap.org/api/0.6/notes/815/reopen + 2014-10-03 15:20:57 UTC + closed + 2014-10-05 16:35:13 UTC + + + 2014-10-03 15:20:57 UTC + 1841 + metaodi + http://master.apis.dev.openstreetmap.org/user/metaodi + opened + This is a test + <p>This is a test</p> + + + 2014-10-05 16:35:13 UTC + 1841 + metaodi + http://master.apis.dev.openstreetmap.org/user/metaodi + closed + Close this note! + <p>Close this note!</p> + + + + diff --git a/tests/fixtures/test_NoteComment.xml b/tests/fixtures/test_NoteComment.xml new file mode 100644 index 0000000..f7614dd --- /dev/null +++ b/tests/fixtures/test_NoteComment.xml @@ -0,0 +1,31 @@ + + + + 812 + http://api06.dev.openstreetmap.org/api/0.6/notes/812 + http://api06.dev.openstreetmap.org/api/0.6/notes/812/comment + http://api06.dev.openstreetmap.org/api/0.6/notes/812/close + 2014-10-03 15:11:05 UTC + open + + + 2014-10-03 15:11:05 UTC + 1841 + metaodi + http://master.apis.dev.openstreetmap.org/user/metaodi + opened + This is a test + <p>This is a test</p> + + + 2014-10-04 22:36:35 UTC + 1841 + metaodi + http://master.apis.dev.openstreetmap.org/user/metaodi + commented + This is a comment + <p>This is a comment</p> + + + + diff --git a/tests/fixtures/test_NoteCreate.xml b/tests/fixtures/test_NoteCreate.xml new file mode 100644 index 0000000..b0944a0 --- /dev/null +++ b/tests/fixtures/test_NoteCreate.xml @@ -0,0 +1,22 @@ + + + + 816 + http://api06.dev.openstreetmap.org/api/0.6/notes/816 + http://api06.dev.openstreetmap.org/api/0.6/notes/816/comment + http://api06.dev.openstreetmap.org/api/0.6/notes/816/close + 2014-10-03 15:21:21 UTC + open + + + 2014-10-03 15:21:22 UTC + 1841 + metaodi + http://master.apis.dev.openstreetmap.org/user/metaodi + opened + This is a test + <p>This is a test</p> + + + + diff --git a/tests/fixtures/test_NoteCreate_wo_auth.xml b/tests/fixtures/test_NoteCreate_wo_auth.xml new file mode 100644 index 0000000..b23f36c --- /dev/null +++ b/tests/fixtures/test_NoteCreate_wo_auth.xml @@ -0,0 +1,32 @@ + + + + 824 + http://api06.dev.openstreetmap.org/api/0.6/notes/824 + http://api06.dev.openstreetmap.org/api/0.6/notes/824/comment + http://api06.dev.openstreetmap.org/api/0.6/notes/824/close + 2014-10-03 16:09:11 UTC + open + + + 2014-10-03 16:09:12 UTC + opened + This is an unauthenticated test + <p>This is an unauthenticated test</p> + + + + + +{u'comments': [{u'action': u'opened', + u'date': u'2014-10-03 16:09:12 UTC', + u'html': u'

This is an unauthenticated test

', + u'text': u'This is an unauthenticated test', + u'uid': None, + u'user': None}], + u'date_closed': None, + u'date_created': u'2014-10-03 16:09:11 UTC', + u'id': u'824', + u'lat': 47.123, + u'lon': 8.432, + u'status': u'open'} diff --git a/tests/fixtures/test_NoteGet.xml b/tests/fixtures/test_NoteGet.xml new file mode 100644 index 0000000..a35b173 --- /dev/null +++ b/tests/fixtures/test_NoteGet.xml @@ -0,0 +1,31 @@ + + + + 1111 + http://api.openstreetmap.org/api/0.6/notes/1111 + http://api.openstreetmap.org/api/0.6/notes/1111/reopen + 2013-05-01 20:58:21 UTC + closed + 2013-08-21 16:43:26 UTC + + + 2013-05-01 20:58:21 UTC + 1363438 + giuseppemari + http://www.openstreetmap.org/user/giuseppemari + opened + It does not exist this path + <p>It does not exist this path</p> + + + 2013-08-21 16:43:26 UTC + 1714220 + luschi + http://www.openstreetmap.org/user/luschi + closed + there is no path signed + <p>there is no path signed</p> + + + + diff --git a/tests/fixtures/test_NoteReopen.xml b/tests/fixtures/test_NoteReopen.xml new file mode 100644 index 0000000..88a257b --- /dev/null +++ b/tests/fixtures/test_NoteReopen.xml @@ -0,0 +1,40 @@ + + + + 815 + http://api06.dev.openstreetmap.org/api/0.6/notes/815 + http://api06.dev.openstreetmap.org/api/0.6/notes/815/comment + http://api06.dev.openstreetmap.org/api/0.6/notes/815/close + 2014-10-03 15:20:57 UTC + open + + + 2014-10-03 15:20:57 UTC + 1841 + metaodi + http://master.apis.dev.openstreetmap.org/user/metaodi + opened + This is a test + <p>This is a test</p> + + + 2014-10-05 16:35:13 UTC + 1841 + metaodi + http://master.apis.dev.openstreetmap.org/user/metaodi + closed + Close this note! + <p>Close this note!</p> + + + 2014-10-05 16:44:56 UTC + 1841 + metaodi + http://master.apis.dev.openstreetmap.org/user/metaodi + reopened + Reopen this note! + <p>Reopen this note!</p> + + + + diff --git a/tests/fixtures/test_NotesGet.xml b/tests/fixtures/test_NotesGet.xml new file mode 100644 index 0000000..b8cb5a4 --- /dev/null +++ b/tests/fixtures/test_NotesGet.xml @@ -0,0 +1,441 @@ + + + + 231776 + http://www.openstreetmap.org/api/0.6/notes/231776 + http://www.openstreetmap.org/api/0.6/notes/231776/reopen + 2014-08-28 19:27:13 UTC + closed + 2014-09-27 09:27:48 UTC + + + 2014-08-28 19:27:13 UTC + 1486336 + Wyken Seagrave + http://www.openstreetmap.org/user/Wyken%20Seagrave + opened + Is it Pinners or Pinner's ? + <p>Is it Pinners or Pinner's ?</p> + + + 2014-09-26 13:10:36 UTC + commented + Royal Mail's postcode finder has PINNERS CROFT. + <p>Royal Mail's postcode finder has PINNERS CROFT.</p> + + + 2014-09-27 09:27:48 UTC + 1486336 + Wyken Seagrave + http://www.openstreetmap.org/user/Wyken%20Seagrave + closed + Pinners without apostrophe is correct, based on survey + <p>Pinners without apostrophe is correct, based on survey</p> + + + + + 231733 + http://www.openstreetmap.org/api/0.6/notes/231733 + http://www.openstreetmap.org/api/0.6/notes/231733/reopen + 2014-08-28 18:39:05 UTC + closed + 2014-09-27 09:24:55 UTC + + + 2014-08-28 18:39:05 UTC + 1486336 + Wyken Seagrave + http://www.openstreetmap.org/user/Wyken%20Seagrave + opened + Should be Finbarr? + <p>Should be Finbarr? </p> + + + 2014-09-26 13:09:14 UTC + commented + You're right. Royal Mail's postcode finder has FINBARR CLOSE. + <p>You're right. Royal Mail's postcode finder has FINBARR CLOSE.</p> + + + 2014-09-27 09:24:55 UTC + 1486336 + Wyken Seagrave + http://www.openstreetmap.org/user/Wyken%20Seagrave + closed + Finbar is name on street plate, verified by survey + <p>Finbar is name on street plate, verified by survey</p> + + + + + 231775 + http://www.openstreetmap.org/api/0.6/notes/231775 + http://www.openstreetmap.org/api/0.6/notes/231775/reopen + 2014-08-28 19:25:37 UTC + closed + 2014-09-27 09:21:41 UTC + + + 2014-08-28 19:25:37 UTC + 1486336 + Wyken Seagrave + http://www.openstreetmap.org/user/Wyken%20Seagrave + opened + Is it Paynes or Payne's + <p>Is it Paynes or Payne's</p> + + + 2014-09-26 13:05:33 UTC + commented + Royal Mail's postcode finder has PAYNES LANE + <p>Royal Mail's postcode finder has PAYNES LANE</p> + + + + + 231728 + http://www.openstreetmap.org/api/0.6/notes/231728 + http://www.openstreetmap.org/api/0.6/notes/231728/reopen + 2014-08-28 18:29:16 UTC + closed + 2014-09-27 09:15:46 UTC + + + 2014-08-28 18:29:16 UTC + 1486336 + Wyken Seagrave + http://www.openstreetmap.org/user/Wyken%20Seagrave + opened + Should it be Craner's Road? + <p>Should it be Craner's Road? </p> + + + 2014-09-26 13:04:15 UTC + commented + Royal Mail's Postcode finder has CRANERS ROAD + <p>Royal Mail's Postcode finder has CRANERS ROAD</p> + + + 2014-09-27 09:15:46 UTC + 1486336 + Wyken Seagrave + http://www.openstreetmap.org/user/Wyken%20Seagrave + closed + Craners without apostrophe based on survey + <p>Craners without apostrophe based on survey</p> + + + + + 231734 + http://www.openstreetmap.org/api/0.6/notes/231734 + http://www.openstreetmap.org/api/0.6/notes/231734/reopen + 2014-08-28 18:40:33 UTC + closed + 2014-09-27 09:03:23 UTC + + + 2014-08-28 18:40:33 UTC + 1486336 + Wyken Seagrave + http://www.openstreetmap.org/user/Wyken%20Seagrave + opened + Should be Forester's? + <p>Should be Forester's? </p> + + + 2014-09-26 13:21:59 UTC + commented + Royal Mail's postcode finder has FORESTERS ROAD + <p>Royal Mail's postcode finder has FORESTERS ROAD</p> + + + 2014-09-27 09:03:23 UTC + 1486336 + Wyken Seagrave + http://www.openstreetmap.org/user/Wyken%20Seagrave + closed + Foresters without apostrophe is correct. Verified by survey. + <p>Foresters without apostrophe is correct. Verified by survey.</p> + + + + + 226232 + http://www.openstreetmap.org/api/0.6/notes/226232 + http://www.openstreetmap.org/api/0.6/notes/226232/comment + http://www.openstreetmap.org/api/0.6/notes/226232/close + 2014-08-21 18:28:35 UTC + open + + + 2014-08-21 18:28:35 UTC + 1486336 + Wyken Seagrave + http://www.openstreetmap.org/user/Wyken%20Seagrave + opened + Is this really called Classic Drive? + <p>Is this really called Classic Drive? </p> + + + + + 141481 + http://www.openstreetmap.org/api/0.6/notes/141481 + http://www.openstreetmap.org/api/0.6/notes/141481/comment + http://www.openstreetmap.org/api/0.6/notes/141481/close + 2014-03-29 00:40:00 UTC + open + + + 2014-03-29 00:40:00 UTC + opened + Shared use footpath(s) + <p>Shared use footpath(s)</p> + + + + + 104971 + http://www.openstreetmap.org/api/0.6/notes/104971 + http://www.openstreetmap.org/api/0.6/notes/104971/comment + http://www.openstreetmap.org/api/0.6/notes/104971/close + 2014-01-20 11:14:35 UTC + open + + + 2014-01-20 11:14:35 UTC + 1894030 + david halliday + http://www.openstreetmap.org/user/david%20halliday + opened + Casino. DeVere Hotel. Primary Wine Bar + <p>Casino. DeVere Hotel. Primary Wine Bar</p> + + + + + 98185 + http://www.openstreetmap.org/api/0.6/notes/98185 + http://www.openstreetmap.org/api/0.6/notes/98185/comment + http://www.openstreetmap.org/api/0.6/notes/98185/close + 2014-01-08 20:51:59 UTC + open + + + 2014-01-08 20:51:59 UTC + 1874292 + CarlRose + http://www.openstreetmap.org/user/CarlRose + opened + tressler + + <p>tressler +</p> + + + 2014-01-08 20:52:05 UTC + 1874292 + CarlRose + http://www.openstreetmap.org/user/CarlRose + closed + + <p></p> + + + 2014-01-08 20:52:19 UTC + 1874292 + CarlRose + http://www.openstreetmap.org/user/CarlRose + reopened + + <p></p> + + + + + 98183 + http://www.openstreetmap.org/api/0.6/notes/98183 + http://www.openstreetmap.org/api/0.6/notes/98183/comment + http://www.openstreetmap.org/api/0.6/notes/98183/close + 2014-01-08 20:46:29 UTC + open + + + 2014-01-08 20:46:29 UTC + opened + 9 Humber Rd + <p>9 Humber Rd</p> + + + 2014-01-08 20:51:13 UTC + 1874292 + CarlRose + http://www.openstreetmap.org/user/CarlRose + closed + + <p></p> + + + 2014-01-08 20:51:21 UTC + 1874292 + CarlRose + http://www.openstreetmap.org/user/CarlRose + reopened + + <p></p> + + + + + 97502 + http://www.openstreetmap.org/api/0.6/notes/97502 + http://www.openstreetmap.org/api/0.6/notes/97502/comment + http://www.openstreetmap.org/api/0.6/notes/97502/close + 2014-01-07 14:02:05 UTC + open + + + 2014-01-07 14:02:05 UTC + opened + onosm.org submitted note from a business: +name: Britannick Engineering +phone: 01608 810332 +website: +twitter: +hours: +category: Factories +address: Market Street, Charlbury + <p>onosm.org submitted note from a business: +<br />name: Britannick Engineering +<br />phone: 01608 810332 +<br />website: +<br />twitter: +<br />hours: +<br />category: Factories +<br />address: Market Street, Charlbury</p> + + + + + 11430 + http://www.openstreetmap.org/api/0.6/notes/11430 + http://www.openstreetmap.org/api/0.6/notes/11430/comment + http://www.openstreetmap.org/api/0.6/notes/11430/close + 2013-07-04 12:08:26 UTC + open + + + 2013-07-04 12:08:26 UTC + 218616 + djakk + http://www.openstreetmap.org/user/djakk + opened + l'échangeur de Janzé-est sera ouvert en septembre 2013 : un rond-point sera construit au nord de l'échangeur -> à mapper + <p>l'échangeur de Janzé-est sera ouvert en septembre 2013 : un rond-point sera construit au nord de l'échangeur -&gt; à mapper</p> + + + 2013-11-20 09:48:18 UTC + 439947 + StephaneP + http://www.openstreetmap.org/user/StephaneP + commented + Qu'en est-il ? La note est toujours valide ou bien les modifs ont été entrées dans Osm ? + <p>Qu'en est-il ? La note est toujours valide ou bien les modifs ont été entrées dans Osm ?</p> + + + 2013-11-23 11:22:18 UTC + 218616 + djakk + http://www.openstreetmap.org/user/djakk + commented + Coucou, quelqu'un d'autre que moi a mappé et ça semble correct. +Reste une aire de covoiturage, qui est sans doute toujours en construction. (Son ouverture n'était pas synchronisée avec celle de l'échangeur). + <p>Coucou, quelqu'un d'autre que moi a mappé et ça semble correct. +<br />Reste une aire de covoiturage, qui est sans doute toujours en construction. (Son ouverture n'était pas synchronisée avec celle de l'échangeur). </p> + + + 2013-12-05 09:13:34 UTC + 704348 + JBacc1 + http://www.openstreetmap.org/user/JBacc1 + closed + Ok, alors on peut fermer. + <p>Ok, alors on peut fermer.</p> + + + 2013-12-06 19:28:20 UTC + 218616 + djakk + http://www.openstreetmap.org/user/djakk + reopened + + <p></p> + + + 2013-12-06 19:28:51 UTC + 218616 + djakk + http://www.openstreetmap.org/user/djakk + commented + Non, on va laisser parce qu'il reste l'aire de covoiturage à mapper ;) + <p>Non, on va laisser parce qu'il reste l'aire de covoiturage à mapper ;)</p> + + + + + 81429 + http://www.openstreetmap.org/api/0.6/notes/81429 + http://www.openstreetmap.org/api/0.6/notes/81429/comment + http://www.openstreetmap.org/api/0.6/notes/81429/close + 2013-12-01 13:02:25 UTC + open + + + 2013-12-01 13:02:25 UTC + opened + right of way around/through farm poorly marked and needs a re-survey + <p>right of way around/through farm poorly marked and needs a re-survey</p> + + + + + 4918 + http://www.openstreetmap.org/api/0.6/notes/4918 + http://www.openstreetmap.org/api/0.6/notes/4918/comment + http://www.openstreetmap.org/api/0.6/notes/4918/close + 2013-05-28 20:31:36 UTC + open + + + 2013-05-28 20:31:36 UTC + 2098 + Andy Street + http://www.openstreetmap.org/user/Andy%20Street + opened + Overlapping landuse + <p>Overlapping landuse</p> + + + 2013-06-05 09:24:52 UTC + 30587 + IknowJoseph + http://www.openstreetmap.org/user/IknowJoseph + commented + I've created a multipolygon that should take care of the overlapping clearings + <p>I've created a multipolygon that should take care of the overlapping clearings</p> + + + 2013-06-05 09:26:30 UTC + 30587 + IknowJoseph + http://www.openstreetmap.org/user/IknowJoseph + commented + (still more to do though) + <p>(still more to do though)</p> + + + + + diff --git a/tests/fixtures/test_NotesSearch.xml b/tests/fixtures/test_NotesSearch.xml new file mode 100644 index 0000000..fed62ca --- /dev/null +++ b/tests/fixtures/test_NotesSearch.xml @@ -0,0 +1,61 @@ + + + + 796 + http://api06.dev.openstreetmap.org/api/0.6/notes/796 + http://api06.dev.openstreetmap.org/api/0.6/notes/796/comment + http://api06.dev.openstreetmap.org/api/0.6/notes/796/close + 2014-07-16 16:42:46 UTC + open + + + 2014-07-16 16:42:46 UTC + 2132 + kalaset + http://master.apis.dev.openstreetmap.org/user/kalaset + opened + One way street: +jjhghkklll + <p>One way street: +<br />jjhghkklll</p> + + + + + 788 + http://api06.dev.openstreetmap.org/api/0.6/notes/788 + http://api06.dev.openstreetmap.org/api/0.6/notes/788/comment + http://api06.dev.openstreetmap.org/api/0.6/notes/788/close + 2014-07-16 16:12:41 UTC + open + + + 2014-07-16 16:12:41 UTC + opened + One way street: +comment + <p>One way street: +<br />comment</p> + + + + + 738 + http://api06.dev.openstreetmap.org/api/0.6/notes/738 + http://api06.dev.openstreetmap.org/api/0.6/notes/738/comment + http://api06.dev.openstreetmap.org/api/0.6/notes/738/close + 2014-07-03 12:09:05 UTC + open + + + 2014-07-03 12:09:05 UTC + 2132 + kalaset + http://master.apis.dev.openstreetmap.org/user/kalaset + opened + One way street tyuui + <p>One way street tyuui</p> + + + + diff --git a/tests/notes_tests.py b/tests/notes_tests.py new file mode 100644 index 0000000..768b3d3 --- /dev/null +++ b/tests/notes_tests.py @@ -0,0 +1,297 @@ +from __future__ import (unicode_literals, absolute_import) +from nose.tools import * # noqa +from . import osmapi_tests + +try: + import urlparse +except ImportError: + from urllib import parse as urlparse + + +class TestOsmApiNotes(osmapi_tests.TestOsmApi): + def test_NotesGet(self): + self._http_mock() + + result = self.api.NotesGet( + -1.4998534, + 45.9667901, + -1.4831815, + 52.4710193 + ) + + args, kwargs = self.api._http_request.call_args + self.assertEquals(args[0], 'GET') + + urlParts = urlparse.urlparse(args[1]) + params = urlparse.parse_qs(urlParts.query) + self.assertEquals( + params['bbox'][0], + '-1.499853,45.966790,-1.483181,52.471019' + ) + self.assertEquals(params['limit'][0], '100') + self.assertEquals(params['closed'][0], '7') + + self.assertEquals(len(result), 14) + self.assertEquals(result[2], { + 'id': '231775', + 'lon': -1.4929605, + 'lat': 52.4107312, + 'date_created': '2014-08-28 19:25:37 UTC', + 'date_closed': '2014-09-27 09:21:41 UTC', + 'status': 'closed', + 'comments': [ + { + 'date': '2014-08-28 19:25:37 UTC', + 'action': 'opened', + 'text': "Is it Paynes or Payne's", + 'html': "

Is it Paynes or Payne's

", + 'uid': '1486336', + 'user': 'Wyken Seagrave' + }, + { + 'date': '2014-09-26 13:05:33 UTC', + 'action': 'commented', + 'text': "Royal Mail's postcode finder has PAYNES LANE", + 'html': + ( + "

Royal Mail's postcode finder " + "has PAYNES LANE

" + ), + 'uid': None, + 'user': None + } + ] + }) + + def test_NoteGet(self): + self._http_mock() + + result = self.api.NoteGet(1111) + + args, kwargs = self.api._http_request.call_args + self.assertEquals(args[0], 'GET') + self.assertEquals(args[1], '/api/0.6/notes/1111') + + self.assertEquals(result, { + 'id': '1111', + 'lon': 12.3133135, + 'lat': 37.9305489, + 'date_created': '2013-05-01 20:58:21 UTC', + 'date_closed': '2013-08-21 16:43:26 UTC', + 'status': 'closed', + 'comments': [ + { + 'date': '2013-05-01 20:58:21 UTC', + 'action': 'opened', + 'text': "It does not exist this path", + 'html': "

It does not exist this path

", + 'uid': '1363438', + 'user': 'giuseppemari' + }, + { + 'date': '2013-08-21 16:43:26 UTC', + 'action': 'closed', + 'text': "there is no path signed", + 'html': "

there is no path signed

", + 'uid': '1714220', + 'user': 'luschi' + } + ] + }) + + def test_NoteCreate(self): + self._http_mock() + + note = { + 'lat': 47.123, + 'lon': 8.432, + 'text': 'This is a test' + } + result = self.api.NoteCreate(note) + + args, kwargs = self.api._http_request.call_args + self.assertEquals(args[0], 'POST') + + urlParts = urlparse.urlparse(args[1]) + params = urlparse.parse_qs(urlParts.query) + self.assertEquals(params['lat'][0], '47.123') + self.assertEquals(params['lon'][0], '8.432') + self.assertEquals(params['text'][0], 'This is a test') + + self.assertEquals(result, { + 'id': '816', + 'lat': 47.123, + 'lon': 8.432, + 'date_created': '2014-10-03 15:21:21 UTC', + 'date_closed': None, + 'status': 'open', + 'comments': [ + { + 'date': '2014-10-03 15:21:22 UTC', + 'action': 'opened', + 'text': "This is a test", + 'html': "

This is a test

", + 'uid': '1841', + 'user': 'metaodi' + } + ] + }) + + def test_NoteComment(self): + self._http_mock() + + result = self.api.NoteComment(812, 'This is a comment') + + args, kwargs = self.api._http_request.call_args + self.assertEquals(args[0], 'POST') + self.assertEquals( + args[1], + '/api/0.6/notes/812/comment?text=This+is+a+comment' + ) + + self.assertEquals(result, { + 'id': '812', + 'lat': 47.123, + 'lon': 8.432, + 'date_created': '2014-10-03 15:11:05 UTC', + 'date_closed': None, + 'status': 'open', + 'comments': [ + { + 'date': '2014-10-03 15:11:05 UTC', + 'action': 'opened', + 'text': "This is a test", + 'html': "

This is a test

", + 'uid': '1841', + 'user': 'metaodi' + }, + { + 'date': '2014-10-04 22:36:35 UTC', + 'action': 'commented', + 'text': "This is a comment", + 'html': "

This is a comment

", + 'uid': '1841', + 'user': 'metaodi' + } + ] + }) + + def test_NoteClose(self): + self._http_mock() + + result = self.api.NoteClose(814, 'Close this note!') + + args, kwargs = self.api._http_request.call_args + self.assertEquals(args[0], 'POST') + self.assertEquals( + args[1], + '/api/0.6/notes/814/close?text=Close+this+note%21' + ) + + self.assertEquals(result, { + 'id': '815', + 'lat': 47.123, + 'lon': 8.432, + 'date_created': '2014-10-03 15:20:57 UTC', + 'date_closed': '2014-10-05 16:35:13 UTC', + 'status': 'closed', + 'comments': [ + { + 'date': '2014-10-03 15:20:57 UTC', + 'action': 'opened', + 'text': "This is a test", + 'html': "

This is a test

", + 'uid': '1841', + 'user': 'metaodi' + }, + { + 'date': '2014-10-05 16:35:13 UTC', + 'action': 'closed', + 'text': "Close this note!", + 'html': "

Close this note!

", + 'uid': '1841', + 'user': 'metaodi' + } + ] + }) + + def test_NoteReopen(self): + self._http_mock() + + result = self.api.NoteReopen(815, 'Reopen this note!') + + args, kwargs = self.api._http_request.call_args + self.assertEquals(args[0], 'POST') + self.assertEquals( + args[1], + '/api/0.6/notes/815/reopen?text=Reopen+this+note%21' + ) + + self.assertEquals(result, { + 'id': '815', + 'lat': 47.123, + 'lon': 8.432, + 'date_created': '2014-10-03 15:20:57 UTC', + 'date_closed': None, + 'status': 'open', + 'comments': [ + { + 'date': '2014-10-03 15:20:57 UTC', + 'action': 'opened', + 'text': "This is a test", + 'html': "

This is a test

", + 'uid': '1841', + 'user': 'metaodi' + }, + { + 'date': '2014-10-05 16:35:13 UTC', + 'action': 'closed', + 'text': "Close this note!", + 'html': "

Close this note!

", + 'uid': '1841', + 'user': 'metaodi' + }, + { + 'date': '2014-10-05 16:44:56 UTC', + 'action': 'reopened', + 'text': "Reopen this note!", + 'html': "

Reopen this note!

", + 'uid': '1841', + 'user': 'metaodi' + } + ] + }) + + def test_NotesSearch(self): + self._http_mock() + + result = self.api.NotesSearch('street') + + args, kwargs = self.api._http_request.call_args + self.assertEquals(args[0], 'GET') + + urlParts = urlparse.urlparse(args[1]) + params = urlparse.parse_qs(urlParts.query) + self.assertEquals(params['q'][0], 'street') + self.assertEquals(params['limit'][0], '100') + self.assertEquals(params['closed'][0], '7') + + self.assertEquals(len(result), 3) + self.assertEquals(result[1], { + 'id': '788', + 'lon': 11.96395, + 'lat': 57.70301, + 'date_created': '2014-07-16 16:12:41 UTC', + 'date_closed': None, + 'status': 'open', + 'comments': [ + { + 'date': '2014-07-16 16:12:41 UTC', + 'action': 'opened', + 'text': "One way street:\ncomment", + 'html': "

One way street:\n
comment

", + 'uid': None, + 'user': None + } + ] + })