Skip to content

Commit

Permalink
Merge pull request #27 from metaodi/develop
Browse files Browse the repository at this point in the history
Release 0.4.0
  • Loading branch information
metaodi committed Oct 7, 2014
2 parents 53127c8 + 6267be5 commit 2307782
Show file tree
Hide file tree
Showing 17 changed files with 1,230 additions and 32 deletions.
46 changes: 29 additions & 17 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -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=
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
7 changes: 7 additions & 0 deletions divshot.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "osmapi",
"root": ".",
"routes": {
"**": "OsmApi.m.html"
}
}
193 changes: 188 additions & 5 deletions osmapi/OsmApi.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -509,10 +513,8 @@ def ChangesetUpload(self, ChangesData):
).decode("utf-8")
data += "</"+change["action"]+">\n"
data += "</osmChange>"
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)
Expand Down Expand Up @@ -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 #
##################################################
Expand Down Expand Up @@ -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 #
##################################################
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 #
##################################################
Expand Down Expand Up @@ -993,3 +1169,10 @@ def _XmlEncode(self, text):
.replace("<", "&lt;")
.replace(">", "&gt;")
)

def _GetXmlValue(self, DomElement, tag):
try:
elem = DomElement.getElementsByTagName(tag)[0]
return elem.firstChild.nodeValue
except:
return None
2 changes: 1 addition & 1 deletion osmapi/__init__.py
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading

0 comments on commit 2307782

Please sign in to comment.