def put(self, id): """ update one item """ # 1. parsing reqest try: orgArgs, self.args = GetTwoLayerRequestArgs( field_inputs_wrap_head, field_inputs_post) except Exception as error: logger.debug('traceback.format_exc(%s)', traceback.format_exc()) return omitError(ErrorMsg=repr(error)), 400 # 2. get orm from db r = obj.query.filter(obj.id == id, obj.isdel == False).scalar() if r is None: return omitError('CE_NOT_EXIST', 'id {} not found'.format(id)), 400 # 3. assign request data to orm try: t = datetime.utcnow() r = PrepareObjORM(r, self.args.items()) r.mtime = t except Exception as error: return omitError(ErrorMsg=repr(error)), 400 # 4. commit to save try: db.session.merge(r) db.session.flush() db.session.commit() except Exception as error: db.session.rollback() logger.warning('session commit error(%s)', error) if exc.IntegrityError == type(error): return omitError('CE_NAME_CONFLICT', repr(error)), 400 return omitError(ErrorMsg=repr(error)), 400 # 5. return all data to user out = SerialObjOutput(r, objname=field_inputs_wrap_head, resource_fields=resource_fields_post), 200 next(iter(out))[field_inputs_wrap_head].update({'id': id}) next(iter(out))['type'] = 'business' next(iter(out))['subtype'] = 'rate' return out
def put(self, id): """ update one item """ # 1. parsing reqest try: orgArgs, self.args = GetTwoLayerRequestArgs( field_inputs_wrap_head, field_inputs_post) except Exception as error: logger.debug('traceback.format_exc(%s)', traceback.format_exc()) return omitError(ErrorMsg=repr(error)), 400 # 2. get orm from db r = obj.query.filter(obj.id == id, obj.isdel == False).scalar() if r is None: return omitError('CE_NOT_EXIST', 'id {} not found'.format(id)), 400 # 3. assign request data to orm try: t = datetime.utcnow() if not self.args['passHash']: self.args['passHash'] = r.passHash # not allow others change expect itself # TODO: raise custome error check_permission(self.args['login'], r.login) # not allow change login name self.args['login'] = r.login r = PrepareObjORM(r, self.args.items()) r.mtime = t except Exception as error: if RuntimeError == type(error): return omitError('CE_UNAUTHORIZED', repr(error)), 400 return omitError(ErrorMsg=repr(error)), 400 # 4. commit to save try: db.session.merge(r) db.session.flush() db.session.commit() except Exception as error: db.session.rollback() logger.warning('session commit error(%s)', error) if exc.IntegrityError == type(error): return omitError('CE_NAME_CONFLICT', repr(error)), 400 return omitError(ErrorMsg=repr(error)), 400 # 5. return all data to user _resource_fields_post = resource_fields_post.copy() _resource_fields_post.pop('access_token', None) out = SerialObjOutput(r, objname=field_inputs_wrap_head, resource_fields=_resource_fields_post), 200 next(iter(out))['type'] = 'system' next(iter(out))['subtype'] = 'regist' return out
def post(self): """create data """ # 1. parsing reqest # 1.1 parsing 1st layer reqest try: orgArgs, self.args = GetTwoLayerRequestArgs( field_inputs_wrap_head, field_inputs_post) except Exception as error: logger.debug('traceback.format_exc(%s)', traceback.format_exc()) return omitError(ErrorMsg=repr(error)), 400 # 1.3 check name unique r = obj.query.filter(obj.user_id == self.args['user_id'], obj.business_id == self.args['business_id'], obj.isdel == False).scalar() if r is not None: return omitError( 'CE_DATA_DUPLICATE', 'user_id {}, business_id {} are duplicate'.format( self.args['user_id'], self.args['business_id'])), 400 # 2. validate follows spec if db.session.query(obj).filter(obj.isdel == False).count() > max: return omitError('CE_EXCEED_LIMIT', 'limit is {}'.format(max)), 400 r = obj() try: r = PrepareObjORM(r, self.args.items()) except Exception as error: return omitError(ErrorMsg=repr(error)), 400 # 4. commit to save try: db.session.add(r) db.session.flush() db.session.refresh(r) db.session.commit() except Exception as error: db.session.rollback() logger.warning('session commit error(%s)', error) if exc.IntegrityError == type(error): return omitError('CE_NAME_CONFLICT', repr(error)), 400 return omitError(ErrorMsg=repr(error)), 400 # 5. return all data to user out = SerialObjOutput(r, objname=field_inputs_wrap_head, resource_fields=resource_fields_post), 200 next(iter(out))[field_inputs_wrap_head].update({'id': r.id}) next(iter(out))['type'] = 'business' next(iter(out))['subtype'] = 'rate' return out
def put(self, id): """ update one item @api {put} /rest/customer/business/:id Update a item @apiVersion 0.0.5 @apiName UpdateCustomerBusiness @apiGroup business @apiPermission registered user @apiDescription todo certificate with cookies / oauth 2.0<br /> todo long/lat validation<br /> todo error / success return code in api <br /> @apiParam {Object} data object of business. @apiParam {Number} data.id item's uniq id. @apiParam {String} data.name item's name. @apiParam {Number} data.cat item's business industry category. @apiParam {float} data.lat item's entered latitude. @apiParam {float} data.long item's entered longitude. @apiParam {String} data.address item's address @apiParam {String} data.description item's description. @apiParam {String} data.image_url items's image url. @apiParam {Float} data.rate item's rate, average from each comments @apiParam {Number} data.deal one of item's deal for display in list @apiParam {Object[]} data.deals item's deals @apiParam {String} data.deals.title item's deal title @apiParam {String} data.deals.description item's deal description @apiParam {String} data.open item open time with 24h format @apiParam {String} data.open item close time with 24h format @apiParam {Number} data.dist item's distance farward with your current location, the unit is meter @apiParam {Object} data.images_url item images' path @apiParam {String} data.images_url.bg item backgound images' path @apiParam {String} data.images_url.icon item icon images' path @apiExample {curl} Example usage: curl -X PUT -H "mTag: xx" -H "Content-Type:application/json" -d " { "data":{ "dist":12245, "close":"2200", "lat":120.678, "features":"this is features", "address":"this is address", "deals":[ { "title":"10% Off Any Order", "description":"Use this promo code and save on coffee, tea, and..." } ], "cat":1, "long":23.5383, "meals":"this is meals", "deal":200, "open":"0600", "description":"early Bird Special: Get off.", "name":"Starbucks Coffee 31", "images_url":{ "icon":"/img/business/1/icon", "bg":"/img/business/1/bg" }, "id":1 }, "type":"business", "subtype":"overview" } " http://localhost/rest/customer/business/1 @apiError CE_INVALID_PARAM invalid parameter HTTP/1.0 400 BAD REQUEST { "errorId": 2001, "message": "ValueError('[cat]: (cat Valid Error) Missing required parameter in dataDict, error=400: Bad Request',)" } @apiError CE_NAME_CONFLICT name conflict HTTP/1.0 400 BAD REQUEST { "errorId": 2004, "message": "IntegrityError('(sqlite3.IntegrityError) UNIQUE constraint failed: customer_businesses.name',)" } @apiError CE_NOT_EXIST item not found HTTP/1.0 400 BAD REQUEST { "errorId": 2003, "message": "id 17 not found" } """ # 1. parsing reqest try: orgArgs, self.args = GetTwoLayerRequestArgs( field_inputs_wrap_head, field_inputs_post) RemoveRequestArgs(field_inputs_post) j = request.get_json() orgdetailImgsUrlArgs, self.detailimgsurlargs = GetTwoLayerRequestArgs( None, field_inputs_detail_images_url, j[field_inputs_wrap_head][__head_detail_images_url__]) except Exception as error: logger.debug('traceback.format_exc(%s)', traceback.format_exc()) return omitError(ErrorMsg=repr(error)), 400 # 2. get orm from db r = obj.query.filter(obj.id == id, obj.isdel == False).scalar() if r is None: return omitError('CE_NOT_EXIST', 'id {} not found'.format(id)), 400 # 3. assign request data to orm try: t = datetime.utcnow() _item = [] for k, v in self.args.items(): if k != "deals": _item.append((k, v)) r = PrepareObjORM(r, _item) r.mtime = t d = detail.query.filter(detail.business_id == id, detail.isdel == False).scalar() __deals = deals.query.filter(deals.business_id == id, deals.isdel == False).all() # TODO: mark isdel = True? #for v in __deals: # v.isdel = True; _deals = [] for k, v in self.args.items(): if v != None: if k == 'deals': deal = deals() for v1 in v: # each entry for k2, v2 in v1.items(): setattr(deal, k2, v2) _deals.append(deal) else: setattr(d, k, v) d.mtime = t _pics = [] _ps = pics.query.filter(pics.business_id == id, pics.isdel == False).all() # FIXME: hard code mapping for k, v in self.detailimgsurlargs.items(): # (1, 'icon'), (2, 'bg'), (3, 'gallery') p = pics() if k == 'icon': p.type = 1 elif k == 'bg': p.type = 2 if p.type: p.path = v _pics.append(p) except Exception as error: return omitError(ErrorMsg=repr(error)), 400 # 4. commit to save try: #db.session.merge(r) for v in __deals: db.session.delete(v) for v in _ps: db.session.delete(v) for v in _pics: db.session.add(v) for v in _deals: db.session.add(v) db.session.flush() db.session.commit() except Exception as error: db.session.rollback() logger.warning('session commit error(%s)', error) if exc.IntegrityError == type(error): return omitError('CE_NAME_CONFLICT', repr(error)), 400 return omitError(ErrorMsg=repr(error)), 400 # 5. return all data to user out = SerialObjOutput(d, objname=field_inputs_wrap_head, resource_fields=resource_fields_post), 200 for k, v in field_inputs.items(): if k not in ['id', 'image_url']: next(iter(out))[field_inputs_wrap_head].update({k: orgArgs[k]}) next(iter(out))[field_inputs_wrap_head].update( {'deals': orgArgs['deals']}) next(iter(out))[field_inputs_wrap_head].update( {'images_url': orgArgs['images_url']}) next(iter(out))[field_inputs_wrap_head].update({'id': id}) next(iter(out))['type'] = 'business' next(iter(out))['subtype'] = 'overview' return out
def post(self): """regist local user @api {post} /rest/admins regist local account @apiVersion 0.0.5 @apiName RegistOneUser @apiGroup account @apiPermission registered user @apiDescription todo certificate with cookies / oauth 2.0<br /> todo validation<br /> todo error / success return code in api @apiParam {Object[]} user user object @apiParam {String{..254}} user.name user name for display @apiParam {String{..254}} user.login user email for login @apiParam {String{..254}} user.passHash user password encode with sha1 @apiExample {curl} Example usage: curl -X POST -H "mTag: xx" -H "Content-Type:application/json" -d " { 'data':{ 'id':2, 'login':'******', 'passHash':'c4f9375f9834b4e7f0a528cc65c055702bf5f24a', 'name':'test' }, 'type':'system', 'subtype':'regist' } " http://localhost/rest/admins """ # 1. check id exist try: orgArgs, self.args = GetTwoLayerRequestArgs( field_inputs_wrap_head, field_inputs_post) except Exception as error: logger.debug('traceback.format_exc(%s)', traceback.format_exc()) return omitError('CE_INVALID_PARAM', 'not found'), 400 # 2. validate follows spec if db.session.query(obj).filter(obj.isdel == False).count() > max: return omitError('CE_EXCEED_LIMIT', 'limit is {}'.format(max)), 400 # 3 check name unique r = obj.query.filter(obj.login == self.args['login'], obj.isdel == False).scalar() if r is not None: return omitError( 'CE_DATA_DUPLICATE', 'login {} is duplicate'.format(self.args['login'])), 400 r = obj() try: r = PrepareObjORM(r, self.args.items()) except Exception as error: return omitError(ErrorMsg=repr(error)), 400 # 4. commit to save try: db.session.add(r) db.session.flush() db.session.refresh(r) db.session.commit() except Exception as error: db.session.rollback() logger.warning('session commit error(%s)', error) if exc.IntegrityError == type(error): return omitError('CE_NAME_CONFLICT', repr(error)), 400 return omitError(ErrorMsg=repr(error)), 400 # 5. return all data to user _resource_fields_post = resource_fields_post.copy() _resource_fields_post.pop('access_token', None) out = SerialObjOutput(r, objname=field_inputs_wrap_head, resource_fields=_resource_fields_post), 200 next(iter(out))[field_inputs_wrap_head].update({'id': r.id}) next(iter(out))['type'] = 'system' next(iter(out))['subtype'] = 'regist' return out
def post(self): """create data @api {post} /rest/customer/businesses Create a item @apiVersion 0.0.3 @apiName CreateCustomerBusiness @apiGroup business @apiPermission registered user @apiDescription todo validation <br/> todo certificate with cookies / oauth 2.0 <br/> todo muti-create <br /> todo error / success return code in api @apiParam {Object} data object of business. @apiParam {Number} data.id item's uniq id. @apiParam {String} data.name item's name. @apiParam {Number} data.cat item's business industry category. @apiParam {float} data.lat item's entered latitude. @apiParam {float} data.long item's entered longitude. @apiParam {String} data.address item's address @apiParam {String} data.description item's description. @apiParam {String} data.image_url items's image url. @apiParam {Float} data.rate item's rate, average from each comments @apiParam {Number} data.deal one of item's deal for display in list @apiParam {Object[]} data.deals item's deals @apiParam {String} data.deals.title item's deal title @apiParam {String} data.deals.description item's deal description @apiParam {String} data.open item open time with 24h format @apiParam {String} data.open item close time with 24h format @apiParam {Number} data.dist item's distance farward with your current location, the unit is meter @apiParam {Object} data.images_url item images' path @apiParam {String} data.images_url.bg item backgound images' path @apiParam {String} data.images_url.icon item icon images' path @apiExample {curl} Example usage: curl -X POST -H "mTag: xx" -H "Content-Type:application/json" -d ' { "data":{ "dist":12245, "close":"2200", "lat":120.678, "features":"this is features", "address":"this is address", "deals":[ { "title":"10% Off Any Order", "description":"Use this promo code and save on coffee, tea, and..." } ], "cat":1, "long":23.5383, "meals":"this is meals", "deal":200, "open":"0600", "description":"early Bird Special: Get off.", "name":"Starbucks Coffee 31", "images_url":{ "icon":"/img/business/1/icon", "bg":"/img/business/1/bg" }, "id":1 }, "type":"business", "subtype":"overview" }' http://localhost/rest/customer/businesses @apiError CE_INVALID_PARAM invalid parameter HTTP/1.0 400 BAD REQUEST { "errorId": 2001, "message": "ValueError('[cat]: (cat Valid Error) Missing required parameter in dataDict, error=400: Bad Request',)" } @apiError CE_NAME_CONFLICT name conflict HTTP/1.0 400 BAD REQUEST { "errorId": 2004, "message": "IntegrityError('(sqlite3.IntegrityError) UNIQUE constraint failed: customer_businesses.name',)" } @apiError CE_EXCEED_LIMIT exceed max limit HTTP/1.0 400 BAD REQUEST { "errorId": 2005, "message": "limit is 5" } """ # 1. parsing reqest # 1.1 parsing 1st layer reqest try: #orgArgs = {'type': 'business', 'subtype': 'overview', 'data': {'name': 'Starbucks Coffee 1', 'description': 'early Bird Special: Get off.', 'address': 'this is address', 'close': '2200', 'meals': 'this is meals', 'long': 23.5383, 'open': '0600', 'lat': 120.678, 'dist': 12245, 'cat': 1, 'images_url': {'icon': '/img/business/1/icon', 'bg': '/img/business/1/bg'}, 'features': 'this is features', 'deal': 200, 'deals': [{'description': 'Use this promo code and save on coffee, tea, and...', 'title': '10% Off Any Order'}]}}' orgArgs, self.args = GetTwoLayerRequestArgs( field_inputs_wrap_head, field_inputs_post) RemoveRequestArgs(field_inputs_post) j = request.get_json() orgdetailImgsUrlArgs, self.detailimgsurlargs = GetTwoLayerRequestArgs( None, field_inputs_detail_images_url, j[field_inputs_wrap_head][__head_detail_images_url__]) #self.args= {'name': 'Starbucks Coffee 1', 'description': 'early Bird Special: Get off.', 'address': 'this is address', 'close': '2200', 'meals': 'this is meals', 'long': '23.5383', 'open': '0600', 'lat': '120.678', 'dist': 12245, 'cat': 1, 'features': 'this is features', 'deal': 200, 'deals': [{'description': 'Use this promo code and save on coffee, tea, and...', 'title': '10% Off Any Order'}]} self.detailimgsurlargs= {'icon': '/img/business/1/icon', 'bg': '/img/business/1/bg'}} #print("self.args=", self.args, "self.detailimgsurlargs=", self.detailimgsurlargs) except Exception as error: logger.debug('traceback.format_exc(%s)', traceback.format_exc()) return omitError(ErrorMsg=repr(error)), 400 # 1.3 check name unique r = obj.query.filter(obj.name == self.args['name'], obj.isdel == False).scalar() if r is not None: return omitError('CE_NAME_CONFLICT', 'name {} conflict'.format(self.args['name'])), 400 # 2. validate follows spec if db.session.query(obj).filter(obj.isdel == False).count() > max: return omitError('CE_EXCEED_LIMIT', 'limit is {}'.format(max)), 400 r = obj() d = detail() _pics = [] _deals = [] try: _item = [] for k, v in self.args.items(): if k != "deals": _item.append((k, v)) r = PrepareObjORM(r, _item) # FIXME: hard code mapping for k, v in self.detailimgsurlargs.items(): # (1, 'icon'), (2, 'bg'), (3, 'gallery') p = pics() if k == 'icon': p.type = 1 elif k == 'bg': p.type = 2 if p.type: p.path = v _pics.append(p) for k, v in self.args.items(): if v != None: if k == 'deals': deal = deals() for v1 in v: # each entry for k2, v2 in v1.items(): setattr(deal, k2, v2) _deals.append(deal) else: setattr(d, k, v) #print("d.__dict__ = ", d.__dict__) except Exception as error: return omitError(ErrorMsg=repr(error)), 400 # 4. commit to save try: db.session.add(r) # At this point, the object f has been pushed to the DB, # and has been automatically assigned a unique primary key id db.session.flush() # refresh updates given object in the session with its state in the DB # (and can also only refresh certain attributes - search for documentation) db.session.refresh(r) d.business_id = r.id #print("d.__dict__ = ", d.__dict__) db.session.add(d) for v in _deals: v.business_id = r.id db.session.add(v) for v in _pics: v.business_id = r.id db.session.add(v) db.session.commit() except Exception as error: db.session.rollback() logger.warning('session commit error(%s)', error) if exc.IntegrityError == type(error): return omitError('CE_NAME_CONFLICT', repr(error)), 400 return omitError(ErrorMsg=repr(error)), 400 # 5. return all data to user _d = db.session.query(detail).filter(detail.business_id == r.id).one() out = SerialObjOutput(_d, objname=field_inputs_wrap_head, resource_fields=resource_fields_post), 200 for k, v in field_inputs.items(): if k not in ['id', 'image_url']: next(iter(out))[field_inputs_wrap_head].update({k: orgArgs[k]}) next(iter(out))[field_inputs_wrap_head].update( {'deals': orgArgs['deals']}) next(iter(out))[field_inputs_wrap_head].update( {'images_url': orgArgs['images_url']}) next(iter(out))[field_inputs_wrap_head].update({'id': r.id}) next(iter(out))['type'] = 'business' next(iter(out))['subtype'] = 'overview' return out
def put(self, id): """ update one item """ """update comment @api {put} /rest/customer/comment/:id create comment @apiVersion 0.0.5 @apiName UpdateComment @apiGroup campaign @apiPermission registered user @apiParam {Number} id comment id @apiDescription todo certificate with cookies / oauth 2.0<br /> todo long/lat validation<br /> todo metadata for counter of registerd devices<br /> todo error / success return code in api @apiParam {Object} data object @apiParam {String{..254}} data.content message's title @apiParam {Number} data.business_id comment belong to the business_id @apiParam {Number} data.user_id user who create it @apiParam {Number={0..5}} data.rate comment's rate @apiParam {String} type request's type @apiParam {String} subtype request's subtype @apiExample {curl} Example usage: curl -X PUT -H "mTag: xx" -H "Content-Type:application/json" -d " { "subtype":"comment", "type":"business", "data":{ "content":"Starbucks were all over Singapore and the quality ", "business_id":1, "user_id":2, "rate":1 } }" http://localhost/rest/customer/comment/1 HTTP/1.0 200 OK { "data":{ "content":"Starbucks were all over Singapore and the quality ", "user_id":1, "business_id":1, "rate":1, "mtime":"2016-08-31 04:16:42.091706", "id":2 }, "type":"business", "subtype":"comment" } @apiSuccess {Object} data object @apiSuccess {String{..254}} data.content message's title @apiSuccess {Number} data.business_id comment belong to the business_id @apiSuccess {Number} data.user_id user who create it @apiSuccess {Number} data.id comment's id @apiSuccess {String{..254}} data.mtime comment's modify time @apiSuccess {Number={0..5}} data.rate comment's rate @apiSuccess {String} type request's type @apiSuccess {String} subtype request's subtype """ # 1. parsing reqest try: orgArgs, self.args = GetTwoLayerRequestArgs( field_inputs_wrap_head, field_inputs_post) except Exception as error: logger.debug('traceback.format_exc(%s)', traceback.format_exc()) return omitError(ErrorMsg=repr(error)), 400 # 2. get orm from db r = obj.query.filter(obj.id == id, obj.isdel == False).scalar() if r is None: return omitError('CE_NOT_EXIST', 'id {} not found'.format(id)), 400 # 3. assign request data to orm try: t = datetime.utcnow() r = PrepareObjORM(r, self.args.items()) r.mtime = t except Exception as error: return omitError(ErrorMsg=repr(error)), 400 # TODO: make sure tranaction _r = rates.query.filter(rates.user_id == self.args['user_id'], rates.business_id == self.args['business_id'], rates.isdel == False).scalar() if _r is None: return omitError( 'CE_NOT_EXIST', 'user_id {}, business_id {} '.format( self.args['user_id'], self.args['business_id'])), 400 _r.rate = self.args['rate'] # 4. commit to save try: db.session.merge(r) db.session.merge(_r) db.session.flush() db.session.commit() except Exception as error: db.session.rollback() logger.warning('session commit error(%s)', error) if exc.IntegrityError == type(error): return omitError('CE_NAME_CONFLICT', repr(error)), 400 return omitError(ErrorMsg=repr(error)), 400 # 5. return all data to user out = SerialObjOutput(r, objname=field_inputs_wrap_head, resource_fields=resource_fields_post), 200 next(iter(out))[field_inputs_wrap_head].update({'id': id}) next(iter(out))[field_inputs_wrap_head].update( {'rate': self.args['rate']}) next(iter(out))['type'] = 'business' next(iter(out))['subtype'] = 'comment' return out
def post(self): """create data """ """create comment @api {post} /rest/customer/comments create comment @apiVersion 0.0.5 @apiName CreateComment @apiGroup campaign @apiPermission registered user @apiDescription todo certificate with cookies / oauth 2.0<br /> todo long/lat validation<br /> todo metadata for counter of registerd devices<br /> todo error / success return code in api @apiParam {Object} data object @apiParam {String{..254}} data.content message's title @apiParam {Number} data.business_id comment belong to the business_id @apiParam {Number} data.user_id user who create it @apiParam {Number={0..5}} data.rate comment's rate @apiParam {String} type request's type @apiParam {String} subtype request's subtype @apiExample {curl} Example usage: curl -X POST -H "mTag: xx" -H "Content-Type:application/json" -d " { "subtype":"comment", "type":"business", "data":{ "content":"Starbucks were all over Singapore and the quality ", "business_id":1, "user_id":2, "rate":1 } }" http://localhost/rest/customer/comments HTTP/1.0 200 OK { "data":{ "content":"Starbucks were all over Singapore and the quality ", "user_id":1, "business_id":1, "rate":1, "mtime":"2016-08-31 04:16:42.091706", "id":2 }, "type":"business", "subtype":"comment" } @apiSuccess {Object} data object @apiSuccess {String{..254}} data.content message's title @apiSuccess {Number} data.business_id comment belong to the business_id @apiSuccess {Number} data.user_id user who create it @apiSuccess {Number} data.id comment's id @apiSuccess {String{..254}} data.mtime comment's modify time @apiSuccess {Number={0..5}} data.rate comment's rate @apiSuccess {String} type request's type @apiSuccess {String} subtype request's subtype """ # 1. parsing reqest # 1.1 parsing 1st layer reqest try: #orgArgs = {'type': 'business', 'subtype': 'comment', 'data': {"content": "Starbucks were all over Singapore and the quality" ,"business_id": 1, "user_id": 1}}' orgArgs, self.args = GetTwoLayerRequestArgs( field_inputs_wrap_head, field_inputs_post) #print("self.args=", self.args) #return {}, 400 except Exception as error: logger.debug('traceback.format_exc(%s)', traceback.format_exc()) return omitError(ErrorMsg=repr(error)), 400 # 1.3 check name unique r = obj.query.filter(obj.user_id == self.args['user_id'], obj.business_id == self.args['business_id'], obj.isdel == False).scalar() if r is not None: return omitError( 'CE_DATA_DUPLICATE', 'user_id {}, business_id {} are duplicate'.format( self.args['user_id'], self.args['business_id'])), 400 # 2. validate follows spec if db.session.query(obj).filter(obj.isdel == False).count() > max: return omitError('CE_EXCEED_LIMIT', 'limit is {}'.format(max)), 400 r = obj() try: r = PrepareObjORM(r, self.args.items()) except Exception as error: return omitError(ErrorMsg=repr(error)), 400 # TODO: make sure tranaction # 1.3 check name unique _r = rates.query.filter(rates.user_id == self.args['user_id'], rates.business_id == self.args['business_id'], rates.isdel == False).scalar() if _r is not None: return omitError( 'CE_DATA_DUPLICATE', 'user_id {}, business_id {} are duplicate'.format( self.args['user_id'], self.args['business_id'])), 400 _r = rates() _r.business_id = self.args['business_id'] _r.user_id = self.args['user_id'] _r.rate = self.args['rate'] # 4. commit to save try: db.session.add(r) db.session.add(_r) db.session.flush() db.session.refresh(r) db.session.commit() except Exception as error: db.session.rollback() logger.warning('session commit error(%s)', error) if exc.IntegrityError == type(error): return omitError('CE_NAME_CONFLICT', repr(error)), 400 return omitError(ErrorMsg=repr(error)), 400 # 5. return all data to user out = SerialObjOutput(r, objname=field_inputs_wrap_head, resource_fields=resource_fields_post), 200 next(iter(out))[field_inputs_wrap_head].update({'id': r.id}) next(iter(out))[field_inputs_wrap_head].update( {'rate': self.args['rate']}) next(iter(out))['type'] = 'business' next(iter(out))['subtype'] = 'comment' return out
def post(self, business_id): """push notification to all device under business id @api {post} /rest/pushno/send/:id send message to registed devices under business id @apiVersion 0.0.4 @apiName SendNoticeToDevicesByBusinessId @apiGroup pushno @apiPermission registered user @apiParam {Number} id business id @apiDescription todo certificate with cookies / oauth 2.0<br /> todo long/lat validation<br /> todo metadata for counter of registerd devices<br /> todo error / success return code in api @apiParam {Object} data push notification's body @apiParam {String{..254}} data.title message's title @apiParam {String{..254}} data.message message @apiParam {String{..254}} data.url message's url @apiExample {curl} Example usage: curl -X POST -H "mTag: xx" -H "Content-Type:application/json" -d " { "data":{ "title":"XXXX", "message":"scscs", "uri":"xxxxx" } }" http://localhost/rest/pushno/reg/1 """ # 1. parsing reqest # 1.1 parsing 1st layer reqest try: orgArgs, self.args = GetTwoLayerRequestArgs( field_inputs_wrap_head, field_inputs_send) except Exception as error: logger.debug('traceback.format_exc(%s)', traceback.format_exc()) return omitError(ErrorMsg=repr(error)), 400 # 2. get orm from db r = business.query.filter(business.id == business_id, business.isdel == False).scalar() if r is None: return omitError('CE_NOT_EXIST', 'id {} not found'.format(business_id)), 400 # 2. validate follows spec r = obj.query.filter(obj.business_id == business_id, obj.isdel == False).all() alert = {} alert['title'] = self.args['title'] alert['message'] = self.args['message'] alert['uri'] = self.args['uri'] gcms = [] apns = [] for _r in r: # (1, 'andriod'), (2, 'ios') if _r.type == 1: gcms.append(_r.dev_id) elif _r.type == 2: apns.append(_r.dev_id) # ref: http://pushjack.readthedocs.io/en/latest/ #logger.debug('gcm len %d, all items: %s', len(gcms), gcms) #>>> l = [1, 2, 3, 4] #>>> [l[i:i + 2] for i in range(0, len(l), 2)] #[[1, 2], [3, 4]]] _l = gcms itemsPerPage = settings.GCM_MAX_BATCH_SEND for chunk in [ _l[i:i + itemsPerPage] for i in range(0, len(_l), itemsPerPage) ]: # FIXME: config it response = gcm_client.send(chunk, alert, collapse_key='mi', delay_while_idle=True, time_to_live=604800) #logger.debug("gcm r:", response, dir(response)) #gcm errors: [GCMInvalidRegistrationError (code=InvalidRegistration): Invalid registration ID for identifier XXXXX, GCMInvalidRegistrationError (code=InvalidRegistration): Invalid registration ID for identifier XXXXX, GCMInvalidRegistrationError (code=InvalidRegistration): Invalid registration ID for identifier XXXXX] #gcm failures: ['XXXXX', 'XXXXX', 'XXXXX'] #gcm data: [{'multicast_id': 5353943955785430694, 'results': [{'error': 'InvalidRegistration'}, {'error': 'InvalidRegistration'}, {'error': 'InvalidRegistration'}], 'success': 0, 'failure': 3, 'canonical_ids': 0}] #logger.debug('gcm errors: %s', response.errors) #logger.debug('gcm failures: %s', response.failures) #logger.debug('gcm data: %s', response.data) # Successfully handled registration_ids for reg_id in response.successes: logger.debug('Successfully sent notification for reg_id %d', (reg_id)) # Handling errors # FIXME: add error code if len(response.errors) > 0: logger.debug('gcm data: %s', response.data) return omitError('CE_NOT_EXIST', '{} not found'.format(response.errors)), 400 _l = apns itemsPerPage = settings.APNS_MAX_BATCH_SEND for chunk in [ _l[i:i + itemsPerPage] for i in range(0, len(_l), itemsPerPage) ]: response = apns_client.send(apns, alert, expiration=int(time.time() + 604800), error_timeout=5, batch_size=1000) print("apns r:", dir(response)) if len(response.errors) or len(response.token_errors) > 0: return omitError('CE_NOT_EXIST', '{} not '.format(response)), 400 apns_client.close() # 5. return all data to user out = SerialObjOutput(r, objname=field_inputs_wrap_head, resource_fields=resource_fields_send), 200 next(iter(out))[field_inputs_wrap_head].update({'gcm_nr': len(gcms)}) next(iter(out))[field_inputs_wrap_head].update({'apns_nr': len(apns)}) next(iter(out))['type'] = 'pushno' next(iter(out))['subtype'] = 'send' return out
def post(self): """upload pics to business @api {post} /rest/pic/gallery upload multiple pictures to business @apiVersion 0.0.5 @apiName UploadPicsToBusiness @apiGroup business @apiPermission registered user @apiDescription todo certificate with cookies / oauth 2.0<br /> todo long/lat validation<br /> todo metadata for counter of registerd devices<br /> todo error / success return code in api @apiHeader {String} file[] upload file path @apiHeader {Number} business_id upload to business @apiHeader {String={customer}} type upload type @apiExample {curl} Example usage: curl -v -i -X POST -H "Content-Type: multipart/form-data" -F "file[]=@20160618_6615.jpg" -F "business_id=1" -F type="customer" -F "file[][email protected]" http://localhost/rest/pic/gallery { "type":"pic", "data":[ { "id":9, "path":"/img/customer/1/G30SLL_20160618_6615.jpg", "height":1365, "width":1024 }, { "id":10, "path":"/img/customer/1/3AZGA4_10473.jpg", "height":1365, "width":1024 } ], "page":1, "subtype":"gallery", "gallery_nr":2, "total":6 } @apiSuccess {Number} total total items in one business @apiSuccess {String} orderBy the items order by column you specified @apiSuccess {Number} page page you want to request from, start with 1 @apiSuccess {Number} gallery_nr current upload files number @apiSuccess {String} type request's type @apiSuccess {String} subtype request's subtype @apiSuccess {Object[]} data object @apiSuccess {Number} data.id image's id @apiSuccess {Number} data.path image's url that you could directly access @apiSuccess {Number} data.height image's height(px) @apiSuccess {Number} data.width image's width(px) """ try: orgArgs, self.args = GetTwoLayerRequestArgs( None, field_inputs, request.form) except Exception as error: logger.debug('traceback.format_exc(%s)', traceback.format_exc()) return omitError('CE_INVALID_PARAM', 'not found'), 400 id = self.args['business_id'] type = self.args['type'] #print(self.args, 'self.args', id, type) #self.args = {} #id = request.form.get('business_id') #type = request.form.get('type') #if type not in PIC_ALLOWED_TYPE: # logger.debug('%s not in PIC_ALLOWED_TYPE', type) # type = None #if not id or not type: # return omitError('CE_INVALID_PARAM', 'id {} not found'.format(id)), 400 print("galleryed_files", request.files, "form data=", request.form) # 1. check id exist r = business.query.filter(business.id == id, business.isdel == False).scalar() if r is None: return omitError('CE_NOT_EXIST', 'id {} not found'.format(id)), 400 # if dir not exist, create it # /img/business/1/ pic_dir = os.path.join(STATIC_ROOT, PIC_PREFIX, type, str(id)) rest_pic_prefix = os.path.join(REST_PIC_PREFIX, type, str(id)) if not os.path.exists(pic_dir): os.makedirs(pic_dir) # TODO: review trigger time # 2. check last status that records are same with local _pics = [] count = 0 filenames = next(os.walk(pic_dir))[2] _filenames = filenames.copy() print("filenames=", _filenames) ps = obj.query.filter(obj.business_id == id).all() for v in ps: #print("filenames=", filenames, "v.path=", v.path) if v.path in filenames: count += 1 _filenames.remove(v.path) else: # prepare remove _pics.append(v) # TODO: need check duplicate record? # 2.1. commit to delete not relationship records try: for v in _pics: db.session.delete(v) db.session.flush() db.session.commit() except Exception as error: db.session.rollback() logger.warning('session commit error(%s)', error) return omitError(ErrorMsg=repr(error)), 400 # remove not exist db's file for v in _filenames: os.remove(os.path.join(pic_dir, v)) # 3. validate follows spec if count > max: return omitError('CE_EXCEED_LIMIT', 'limit is {}'.format(max)), 400 # 4. check pic name _pics = [] filenames = [] galleryed_files = request.files.getlist("file[]") print("galleryed_files file[]= ", galleryed_files) if len(galleryed_files) == 0: galleryed_files = request.files.getlist("customer") # for ios print("galleryed_files customer= ", galleryed_files) for file in galleryed_files: # Check if the file is one of the allowed types/extensions if file and self.allowed_file(file.filename): # Make the filename safe, remove unsupported chars filename = secure_filename(file.filename) print("file.filename=", file.filename, "filename=", filename) # Move the file form the temporal folder to the gallery # folder we setup p = obj() rs = ''.join( random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(6)) # 5FATMM_35e4183b.jpg.png -> 5FATMM_35e4183b.png extend = os.path.splitext(filename)[1] #print("self.remove_extend(filename)=", self.remove_extend(filename)) p.path = '_'.join([rs, self.remove_extend(filename)]) + extend p.business_id = id # (1, 'icon'), (2, 'bg'), (3, 'gallery') p.type = 3 _pics.append(p) filenames.append(filename) # 4. move to pic dir # FIXME: check file exist if abnormal error that db record save but pic not exist self.args[field_inputs_wrap_head] = [] path = [] _filenames = [] for k, v in enumerate(_pics): filename = os.path.join(pic_dir, filenames[k]) n_filename = os.path.join(pic_dir, v.path) try: # FIXME: handle <FileStorage: '35e4183b.jpg.png' ('application/octet-stream')>) galleryed_files[k].save(filename) #import shutil #shutil.copy(filename, n_filename) os.rename(filename, n_filename) _filenames.append(n_filename) print("n_filename = ", n_filename, "filename=", filename) img = Image.open(n_filename) s = img.size ratio = 0 v.width = s[0] v.height = s[1] if s[0] > MAXWIDTH: ratio = MAXWIDTH / s[0] v.width = MAXWIDTH v.height = int(round(s[1] * ratio)) img.thumbnail((v.width, v.height), Image.ANTIALIAS) print("img = ", img, "n_filenam=", n_filename, "format:", img.format, "width:", MAXWIDTH, ", height: ", int(round(s[1] * ratio)), "orgsize:", s) #img.save(n_filename, img.format) # FIXME: open 35e4183b.jpg.png error img.save(n_filename) except IOError: #for v in _filenames: # os.remove(v) return omitError('CE_IMAGE_RESIZE', 'image resize {} error'.format( filenames[k])), 400 # 5. commit to save try: for v in _pics: db.session.add(v) db.session.flush() db.session.refresh(r) for v in _pics: _pic = dict((col, getattr(v, col)) for col in v.__table__.columns.keys()) print("_pic:", _pic) _pic['path'] = os.path.join(rest_pic_prefix, v.path) self.args[field_inputs_wrap_head].append(_pic) db.session.commit() except Exception as error: db.session.rollback() logger.warning('session commit error(%s)', error) return omitError(ErrorMsg=repr(error)), 400 #output = {"type": "pic", "subtype": "gallery", # "imgs": {"total": count, "gallery_nr": len(_pics), "path": (path)}} for v in ['itemsPerPage', 'desc', 'orderBy']: resource_fields_wrap.pop(v, None) _resource_fields = {} resource_fields_wrap[field_inputs_wrap_head] = fields.List( fields.Nested(resource_fields_post)) self.args['total'] = len( obj.query.filter(obj.business_id == id, obj.isdel == False).all()) self.args['gallery_nr'] = len(_pics) self.args['type'] = "pic" self.args['subtype'] = "gallery" return marshal(self.args, resource_fields_wrap), 200