# encoding: utf-8
from __future__ import absolute_import
import sys
from uuid import UUID
try:
from typing import Any, Dict
except:
pass
from enum import Enum
from schema import Schema, And, Or, Use, Optional
import arrow
if (3, 0) <= sys.version_info:
unicode = str
[docs]class UserType(Enum):
"""ユーザ種別"""
human = 'human'
bocco = 'bocco'
sensor_door = 'sensor_door'
sensor_lock = 'sensor_lock'
unknown = 'unknown'
[docs]class MessageType(Enum):
"""メッセージの種別"""
normal = 'normal'
system_sensor_joined = 'system.sensor_joined'
system_human_joined = 'system.human_joined'
unknown = 'unknown'
URLSchema = And(unicode, lambda v: v.startswith('http://') or v.startswith('https://') or v == '')
UUIDSchema = Or(UUID, Use(UUID))
DateTimeSchema = Or(arrow.Arrow, Use(arrow.get))
class _Model(object):
schema = Schema(None)
@classmethod
def validate(cls, data):
# type: (dict) -> dict
return cls.schema.validate(data)
@classmethod
def is_list(cls, l):
# type: (Any) -> bool
if l is None:
return False
if len(l) == 0:
return True
for i in l:
if type(i) != cls:
return False
return True
def __init__(self, data):
# type: (dict) -> None
cls = type(self)
self._data = cls.validate(data) # type: Dict[str, Any]
def __getitem__(self, key):
return self._data[key]
def __repr__(self):
return '<{0} {1}>'.format(type(self).__name__, self._data)
[docs]class User(_Model):
"""BOCCO ユーザ
BOCCO 本体またはアプリユーザの情報。
>>> u = User({
... 'uuid': u'7b44ddd8-d1b0-4666-a11d-4dac68068ebd',
... 'user_type': u'bocco',
... 'nickname': u'ニックネーム',
... 'seller': u'',
... 'address': u'00:11:22:33:44:55',
... 'icon': u'http://example.com/image.png'
... })
>>> u['uuid']
UUID('7b44ddd8-d1b0-4666-a11d-4dac68068ebd')
>>> u['user_type'] == UserType.bocco
True
>>> u['nickname'] == u'ニックネーム'
True
>>> u['seller'] == u''
True
>>> u['icon'] == u'http://example.com/image.png'
True
"""
schema = Schema({
'uuid': UUIDSchema,
'user_type': Or(UserType, Use(UserType), Use(lambda v: UserType.unknown)),
'nickname': And(unicode, lambda v: 0 <= len(v) < 120),
'seller': unicode,
Optional('address'): unicode,
Optional('icon'): URLSchema,
}, ignore_extra_keys=True)
[docs]class RoomUser(_Model):
"""部屋と紐付いたユーザ情報
>>> u = RoomUser({
... 'read_id': 123,
... 'joined_at': u'2015-01-02',
... 'user': {
... 'uuid': u'7b44ddd8-d1b0-4666-a11d-4dac68068ebd',
... 'user_type': u'bocco',
... 'nickname': u'TEST USER',
... 'seller': u'',
... 'address': u'00:11:22:33:44:55',
... 'icon': u'http://example.com/image.png'
... }
... })
>>> u['read_id']
123
>>> u['joined_at']
<Arrow [2015-01-02T00:00:00+00:00]>
>>> u['user']['uuid']
UUID('7b44ddd8-d1b0-4666-a11d-4dac68068ebd')
"""
schema = Schema({
'read_id': int,
'joined_at': DateTimeSchema,
'user': Or(User, Use(User)),
}, ignore_extra_keys=True)
[docs]class Room(_Model):
"""部屋情報
>>> r = Room({
... 'uuid': u'3e6aceea-4db1-44a3-b2a9-4ccfccd843e1',
... 'name': u'テストルーム',
... 'updated_at': u'2011-02-03',
... 'members': [
... {
... 'read_id': 123,
... 'joined_at': u'2010-01-02',
... 'user': {
... 'uuid': 'u7b44ddd8-d1b0-4666-a11d-4dac68068ebd',
... 'user_type': u'human',
... 'nickname': u'TEST USER',
... 'seller': u'',
... 'address': u'00:11:22:33:44:55',
... 'icon': u'http://example.com/image.png'
... }
... }
... ],
... 'messages': [
... {
... 'id': 24686,
... 'unique_id': u'1DB34B93-0DFA-4150-AEF5-ffffffffffff',
... 'date': u'2015-07-31T21:47:46+09:00',
... 'media': u'text',
... 'message_type': u'normal',
... 'user': {
... 'uuid': u'cffbf787-dd20-4157-8279-ffffffffffff',
... 'user_type': u'human',
... 'nickname': u'mash',
... 'icon': u'http://example.com/1/users/cffbf787-dd20-4157-8279-ffffffffffff/d4187679-bd07-49f8-94c9-000000000000.png',
... 'seller': u''
... },
... 'dictated': False,
... 'text': u'hoge',
... 'audio': u'http://example.com/1/messages/24686.ogg',
... 'image': u'',
... 'sender': u'cffbf787-dd20-4157-8279-ffffffffffff',
... 'detail': None
... }
... ],
... 'sensors': [
... {
... 'uuid': u'0af1c101-3b7d-40a8-9e63-bf03f2dda6c4',
... 'user_type': u'sensor_door',
... 'nickname': u'DOOR SENSOR',
... 'seller': u'',
... 'address': u'00:11:22:33:44:55',
... 'icon': u'http://example.com/image.png'
... }
... ],
... })
>>> r['uuid']
UUID('3e6aceea-4db1-44a3-b2a9-4ccfccd843e1')
>>> r['name'] == u'テストルーム'
True
>>> r['updated_at']
<Arrow [2011-02-03T00:00:00+00:00]>
>>> r['members'][0]['user']['nickname'] == 'TEST USER'
True
>>> r['sensors'][0]['user_type'] == UserType.sensor_door
True
>>> r['messages'][0]['id'] == 24686
True
"""
schema = Schema({
'uuid': UUIDSchema,
'name': And(unicode, lambda v: 0 < len(v) < 120),
'updated_at': DateTimeSchema,
'members': Or(
Use(lambda l: l or []),
lambda l: RoomUser.is_list(l),
Use(lambda l: [RoomUser(i) for i in l])),
'sensors': Or(
lambda l: User.is_list(l),
Use(lambda l: [User(i) for i in l])),
'messages': Or(
lambda l: Message.is_list(l),
Use(lambda l: [Message(i) for i in l])),
}, ignore_extra_keys=True)
[docs]class Session(_Model):
"""API クライアントのセッション情報
>>> s = Session({
... 'access_token': u'Dummy Token',
... 'uuid': u'17023f65-065f-41e4-b648-cdd38076a7c9',
... })
>>> s['access_token'] == u'Dummy Token'
True
>>> s['uuid']
UUID('17023f65-065f-41e4-b648-cdd38076a7c9')
"""
schema = Schema({
'access_token': unicode,
'uuid': UUIDSchema,
}, ignore_extra_keys=True)
[docs]class Message(_Model):
"""部屋へ送信されたメッセージ
>>> m = Message({
... 'id': 123,
... 'dictated': True,
... 'unique_id': u'a8852948-fc6e-40d4-a384-e4c6a63b705e',
... 'media': u'text',
... 'audio': u'',
... 'message_type': u'normal',
... 'text': u'メッセージです',
... 'image': u'',
... 'sender': u'0a0f6b39-ac63-4731-9c94-756ae80dd0b9',
... 'date': u'2016-03-02 11:00:59',
... 'user': {
... 'uuid': u'0a0f6b39-ac63-4731-9c94-756ae80dd0b9',
... 'user_type': u'bocco',
... 'nickname': u'ニックネーム',
... 'seller': u'',
... 'address': u'00:11:22:33:44:55',
... 'icon': u'http://example.com/image.png'
... }
... })
>>> m['id']
123
>>> m['text'] == u'メッセージです'
True
>>> m['dictated']
True
>>> m['user']['uuid']
UUID('0a0f6b39-ac63-4731-9c94-756ae80dd0b9')
"""
schema = Schema({
'id': int,
'dictated': bool,
'unique_id': UUIDSchema,
'media': Or(MessageMedia, Use(MessageMedia), Use(lambda v: MessageMedia.unknown)),
'audio': URLSchema,
'message_type': Or(MessageType, Use(MessageType), Use(lambda v: MessageType.unknown)),
'text': unicode,
'image': URLSchema,
'sender': UUIDSchema,
'date': DateTimeSchema,
'user': Or(User, Use(User)),
}, ignore_extra_keys=True)
[docs]class ApiErrorBody(_Model):
"""エラーレスポンス
エラーコードの詳細は以下を参照
http://api-docs.bocco.me/reference.html#section-31
>>> e = ApiErrorBody({
... 'code': 401,
... 'message': u'ERROR MESSAGE'
... })
>>> e['code']
401
>>> e['message'] == u'ERROR MESSAGE'
True
"""
schema = Schema({
'code': int,
'message': unicode,
}, ignore_extra_keys=True)
if __name__ == '__main__':
import doctest
doctest.testmod()