fixes #29: port to Python 3 - welcome to the future
This commit is contained in:
parent
26296974cb
commit
83b29fd2d4
6 changed files with 56 additions and 32 deletions
|
@ -22,10 +22,9 @@ See [https://ibt2.ismito.it:3002/](https://ibt2.ismito.it:3002/)
|
|||
To install it:
|
||||
``` bash
|
||||
wget https://bootstrap.pypa.io/get-pip.py
|
||||
sudo python get-pip.py
|
||||
sudo pip install tornado
|
||||
sudo pip install pymongo
|
||||
sudo pip install python-dateutil
|
||||
sudo python3 get-pip.py
|
||||
sudo pip3 install tornado
|
||||
sudo pip3 install pymongo
|
||||
git clone https://github.com/raspibo/ibt2
|
||||
cd ibt2
|
||||
```
|
||||
|
@ -62,7 +61,7 @@ npm run devserver &
|
|||
npm run dev
|
||||
|
||||
# only when the devserver is running, you can also run the testsuite
|
||||
python ./tests/ibt2_tests.py
|
||||
python3 ./tests/ibt2_tests.py
|
||||
```
|
||||
|
||||
Your browser will automatically open [http://localhost:8080/](http://localhost:8080/) (that's the server for development)
|
||||
|
|
20
ibt2.py
20
ibt2.py
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
"""I'll Be There, 2 (ibt2) - an oversimplified attendees registration system.
|
||||
|
||||
Copyright 2016-2017 Davide Alberani <da@erlug.linux.it>
|
||||
|
@ -63,8 +63,8 @@ class BaseHandler(tornado.web.RequestHandler):
|
|||
collection = None
|
||||
|
||||
# A property to access the first value of each argument.
|
||||
arguments = property(lambda self: dict([(k, v[0])
|
||||
for k, v in self.request.arguments.iteritems()]))
|
||||
arguments = property(lambda self: dict([(k, v[0].decode('utf-8'))
|
||||
for k, v in self.request.arguments.items()]))
|
||||
|
||||
# Arguments suitable for a query on MongoDB.
|
||||
clean_arguments = property(lambda self: self._clean_dict(self.arguments))
|
||||
|
@ -86,8 +86,8 @@ class BaseHandler(tornado.web.RequestHandler):
|
|||
:param data: dictionary to clean
|
||||
:type data: dict"""
|
||||
if isinstance(data, dict):
|
||||
for key in data.keys():
|
||||
if (isinstance(key, (str, unicode)) and key.startswith('$')) or key in ('_id', 'created_by',
|
||||
for key in list(data.keys()):
|
||||
if (isinstance(key, str) and key.startswith('$')) or key in ('_id', 'created_by',
|
||||
'created_at', 'updated_by',
|
||||
'updated_at'):
|
||||
del data[key]
|
||||
|
@ -109,13 +109,16 @@ class BaseHandler(tornado.web.RequestHandler):
|
|||
|
||||
def initialize(self, **kwargs):
|
||||
"""Add every passed (key, value) as attributes of the instance."""
|
||||
for key, value in kwargs.iteritems():
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
@property
|
||||
def current_user(self):
|
||||
"""Retrieve current user ID from the secure cookie."""
|
||||
return self.get_secure_cookie("user")
|
||||
current_user = self.get_secure_cookie("user")
|
||||
if isinstance(current_user, bytes):
|
||||
current_user = current_user.decode('utf-8')
|
||||
return current_user
|
||||
|
||||
@property
|
||||
def current_user_info(self):
|
||||
|
@ -381,8 +384,7 @@ class GroupsHandler(BaseHandler):
|
|||
if not (day and group):
|
||||
return self.build_error(status=404, message='unable to access the resource')
|
||||
if not self.current_user_info.get('isAdmin'):
|
||||
self.build_error(status=401, message='insufficient permissions: must be admin')
|
||||
return False
|
||||
return self.build_error(status=401, message='insufficient permissions: must be admin')
|
||||
query = {'day': day, 'group': group}
|
||||
howMany = self.db.delete('attendees', query)
|
||||
self.db.delete('groups', query)
|
||||
|
|
4
monco.py
4
monco.py
|
@ -56,7 +56,7 @@ def convert(seq):
|
|||
"""
|
||||
if isinstance(seq, dict):
|
||||
d = {}
|
||||
for key, item in seq.iteritems():
|
||||
for key, item in seq.items():
|
||||
if key in _force_conversion:
|
||||
try:
|
||||
d[key] = _force_conversion[key](item)
|
||||
|
@ -254,7 +254,7 @@ class Monco(object):
|
|||
operator = self._operations.get(operation)
|
||||
if updateList:
|
||||
newData = {}
|
||||
for key, value in data.iteritems():
|
||||
for key, value in data.items():
|
||||
newData['%s.$.%s' % (updateList, key)] = value
|
||||
data = newData
|
||||
res = db[collection].find_and_modify(query=_id_or_query,
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
},
|
||||
"scripts": {
|
||||
"dev": "node build/dev-server.js",
|
||||
"devserver": "python ibt2.py --debug --db_name=ibt2_test",
|
||||
"server": "python ibt2.py --debug",
|
||||
"devserver": "python3 ibt2.py --debug --db_name=ibt2_test",
|
||||
"server": "python3 ibt2.py --debug",
|
||||
"build": "node build/build.js"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
"""I'll Be There, 2 (ibt2) - tests
|
||||
|
||||
Copyright 2016-2017 Davide Alberani <da@erlug.linux.it>
|
||||
|
@ -23,7 +23,7 @@ BASE_URL = 'http://localhost:3000/v1.1/'
|
|||
DB_NAME = 'ibt2_test'
|
||||
|
||||
def dictInDict(d, dContainer):
|
||||
for k, v in d.viewitems():
|
||||
for k, v in d.items():
|
||||
if k not in dContainer:
|
||||
return False
|
||||
if v != dContainer[k]:
|
||||
|
@ -39,15 +39,15 @@ class Ibt2Tests(unittest.TestCase):
|
|||
self.db['attendees'].drop()
|
||||
self.db['days'].drop()
|
||||
self.db['groups'].drop()
|
||||
self.db['users'].remove({'username': 'newuser'})
|
||||
self.db['users'].remove({'username': 'newuser2'})
|
||||
self.db['users'].delete_one({'username': 'newuser'})
|
||||
self.db['users'].delete_one({'username': 'newuser2'})
|
||||
|
||||
def tearDown(self):
|
||||
self.db['attendees'].drop()
|
||||
self.db['days'].drop()
|
||||
self.db['groups'].drop()
|
||||
self.db['users'].remove({'username': 'newuser'})
|
||||
self.db['users'].remove({'username': 'newuser2'})
|
||||
self.db['users'].delete_one({'username': 'newuser'})
|
||||
self.db['users'].delete_one({'username': 'newuser2'})
|
||||
|
||||
def add_attendee(self, attendee):
|
||||
r = requests.post('%sattendees' % BASE_URL, json=attendee)
|
||||
|
@ -85,9 +85,11 @@ class Ibt2Tests(unittest.TestCase):
|
|||
attendee = {'name': 'A Name', 'day': '2017-01-15', 'group': 'A group'}
|
||||
r = self.add_attendee(attendee)
|
||||
id_ = r.json().get('_id')
|
||||
r.connection.close()
|
||||
r = requests.delete(BASE_URL + 'attendees/' + id_)
|
||||
r.raise_for_status()
|
||||
self.assertTrue(r.json().get('success'))
|
||||
r.connection.close()
|
||||
|
||||
def test_get_days(self):
|
||||
self.add_attendee({'day': '2017-01-15', 'name': 'A name', 'group': 'group A'})
|
||||
|
@ -118,9 +120,11 @@ class Ibt2Tests(unittest.TestCase):
|
|||
def test_create_user(self):
|
||||
r = requests.post(BASE_URL + 'users', json={'username': 'newuser', 'password': 'ibt2'})
|
||||
r.raise_for_status()
|
||||
r.connection.close()
|
||||
s = self.login('newuser', 'ibt2')
|
||||
r = s.get(BASE_URL + 'users/current')
|
||||
r.raise_for_status()
|
||||
r.connection.close()
|
||||
|
||||
def test_update_user(self):
|
||||
r = requests.post(BASE_URL + 'users', json={'username': 'newuser', 'password': 'ibt2'})
|
||||
|
@ -130,17 +134,20 @@ class Ibt2Tests(unittest.TestCase):
|
|||
r.raise_for_status()
|
||||
id2_ = r.json()['_id']
|
||||
r = requests.put(BASE_URL + 'users/' + id_, json={'email': 't@example.com'})
|
||||
self.assertRaises(r.raise_for_status)
|
||||
self.assertRaises(requests.exceptions.HTTPError, r.raise_for_status)
|
||||
s = self.login('newuser', 'ibt2')
|
||||
r = s.put(BASE_URL + 'users/' + id_, json={'email': 'test@example.com'})
|
||||
r.raise_for_status()
|
||||
self.assertEqual(r.json().get('email'), 'test@example.com')
|
||||
r.connection.close()
|
||||
r = s.put(BASE_URL + 'users/' + id2_, json={'email': 'test@example.com'})
|
||||
self.assertRaises(r.raise_for_status)
|
||||
self.assertRaises(requests.exceptions.HTTPError, r.raise_for_status)
|
||||
r.connection.close()
|
||||
s = self.login('admin', 'ibt2')
|
||||
r = s.put(BASE_URL + 'users/' + id_, json={'email': 'test2@example.com'})
|
||||
r.raise_for_status()
|
||||
self.assertEqual(r.json().get('email'), 'test2@example.com')
|
||||
r.connection.close()
|
||||
|
||||
def test_delete_user(self):
|
||||
r = requests.post(BASE_URL + 'users', json={'username': 'newuser', 'password': 'ibt2'})
|
||||
|
@ -150,26 +157,31 @@ class Ibt2Tests(unittest.TestCase):
|
|||
r.raise_for_status()
|
||||
id2_ = r.json()['_id']
|
||||
r = requests.delete(BASE_URL + 'users/' + id_)
|
||||
self.assertRaises(r.raise_for_status)
|
||||
self.assertRaises(requests.exceptions.HTTPError, r.raise_for_status)
|
||||
r.connection.close()
|
||||
s = self.login('newuser', 'ibt2')
|
||||
r = s.delete(BASE_URL + 'users/' + id_)
|
||||
r.raise_for_status()
|
||||
r.connection.close()
|
||||
r = s.delete(BASE_URL + 'users/' + id2_)
|
||||
self.assertRaises(r.raise_for_status)
|
||||
self.assertRaises(requests.exceptions.HTTPError, r.raise_for_status)
|
||||
r.connection.close()
|
||||
s = self.login('admin', 'ibt2')
|
||||
r = s.delete(BASE_URL + 'users/' + id2_)
|
||||
r.raise_for_status()
|
||||
r.connection.close()
|
||||
|
||||
def test_duplicate_user(self):
|
||||
r = requests.post(BASE_URL + 'users', json={'username': 'newuser', 'password': 'ibt2'})
|
||||
r.raise_for_status()
|
||||
r = requests.post(BASE_URL + 'users', json={'username': 'newuser', 'password': 'ibt3'})
|
||||
self.assertRaises(r.raise_for_status)
|
||||
self.assertRaises(requests.exceptions.HTTPError, r.raise_for_status)
|
||||
|
||||
def login(self, username, password):
|
||||
s = requests.Session()
|
||||
r = s.post(BASE_URL + 'login', json={'username': username, 'password': password})
|
||||
r.raise_for_status()
|
||||
r.connection.close()
|
||||
return s
|
||||
|
||||
def test_created_by(self):
|
||||
|
@ -177,12 +189,14 @@ class Ibt2Tests(unittest.TestCase):
|
|||
r = s.get(BASE_URL + 'users/current')
|
||||
r.raise_for_status()
|
||||
user_id = r.json()['_id']
|
||||
r.connection.close()
|
||||
attendee = {'day': '2017-01-15', 'name': 'A name', 'group': 'group A'}
|
||||
r = s.post('%sattendees' % BASE_URL, json=attendee)
|
||||
r.raise_for_status()
|
||||
rj = r.json()
|
||||
self.assertEqual(user_id, rj['created_by'])
|
||||
self.assertEqual(user_id, rj['updated_by'])
|
||||
r.connection.close()
|
||||
|
||||
def test_put_day(self):
|
||||
day = {'day': '2017-01-16', 'notes': 'A day note'}
|
||||
|
@ -214,10 +228,12 @@ class Ibt2Tests(unittest.TestCase):
|
|||
r = s.delete(BASE_URL + 'days/2017-01-16/groups/A group', params={'day': '2017-01-16', 'group': 'A group'})
|
||||
r.raise_for_status()
|
||||
rj = r.json()
|
||||
r.connection.close()
|
||||
r = requests.get(BASE_URL + 'days/2017-01-16')
|
||||
r.raise_for_status()
|
||||
rj = r.json()
|
||||
self.assertTrue(rj == {})
|
||||
r.connection.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
|
|
13
utils.py
13
utils.py
|
@ -36,15 +36,22 @@ def hash_password(password, salt=None):
|
|||
:rtype: str"""
|
||||
if salt is None:
|
||||
salt_pool = string.ascii_letters + string.digits
|
||||
salt = ''.join(random.choice(salt_pool) for x in xrange(32))
|
||||
hash_ = hashlib.sha512('%s%s' % (salt, password))
|
||||
salt = ''.join(random.choice(salt_pool) for x in range(32))
|
||||
pass_and_salt = '%s%s' % (salt, password)
|
||||
pass_and_salt = pass_and_salt.encode('utf-8', 'ignore')
|
||||
hash_ = hashlib.sha512(pass_and_salt)
|
||||
return '$%s$%s' % (salt, hash_.hexdigest())
|
||||
|
||||
|
||||
class ImprovedEncoder(json.JSONEncoder):
|
||||
"""Enhance the default JSON encoder to serialize datetime and ObjectId instances."""
|
||||
def default(self, o):
|
||||
if isinstance(o, (datetime.datetime, datetime.date,
|
||||
if isinstance(o, bytes):
|
||||
try:
|
||||
return o.decode('utf-8')
|
||||
except:
|
||||
pass
|
||||
elif isinstance(o, (datetime.datetime, datetime.date,
|
||||
datetime.time, datetime.timedelta, ObjectId)):
|
||||
try:
|
||||
return str(o)
|
||||
|
|
Loading…
Reference in a new issue