def api_get_node_status(params, user_detail): # type: (dict, UserDetailsTO) -> list[dict] try: profile = TffProfile.create_key( get_iyo_username(user_detail)).get() # type: TffProfile if not DEBUG and not profile.nodes: # fallback, should only happen when user checks his node status before our cron job has ran. logging.warn( 'Fetching node serial number from odoo since it was not set on TffProfile for user %s', user_detail.email) app_user = create_app_user(users.User(user_detail.email), user_detail.app_id) nodes = get_nodes_for_user(app_user) if nodes: username = get_iyo_username(app_user) profile = add_nodes_to_profile(username, nodes) else: raise ApiCallException( u'It looks like you either do not have a node yet or it has never been online yet.' ) return get_nodes_stats_from_influx(profile.nodes) except ApiCallException: raise except Exception as e: logging.exception(e) raise ApiCallException( u'Could not get node status. Please try again later.')
def invest(message_flow_run_id, member, steps, end_id, end_message_flow_id, parent_message_key, tag, result_key, flush_id, flush_message_flow_id, service_identity, user_details, flow_params, token): if flush_id == 'flush_kyc' or flush_id == 'flush_corporation': # KYC flow started from within the invest flow return kyc_part_1(message_flow_run_id, member, steps, end_id, end_message_flow_id, parent_message_key, tag, result_key, flush_id, flush_message_flow_id, service_identity, user_details, flow_params) try: email = user_details[0].email app_id = user_details[0].app_id app_user = create_app_user_by_email(email, app_id) logging.info('User %s wants to invest', email) version = get_key_name_from_key_string(steps[0].message_flow_id) currency = get_step_value(steps, 'message_get_currency').replace('_cur', '') if version.startswith(BUY_TOKENS_FLOW_V3) or version.startswith(BUY_TOKENS_FLOW_V5): amount = float(get_step_value(steps, 'message_get_order_size_ITO').replace(',', '.')) token_count_float = get_token_count(currency, amount) else: token_count_float = float(get_step_value(steps, 'message_get_order_size_ITO')) amount = get_investment_amount(currency, token_count_float) username = get_iyo_username(app_user) agreement = _create_investment_agreement(amount, currency, token, token_count_float, username, version, app_user, status=InvestmentAgreement.STATUS_CREATED) payment_info = [] usd_within_uae_step = get_step(steps, 'message_usd_within_uae') if usd_within_uae_step and usd_within_uae_step.answer_id == 'button_yes': payment_info.append(PaymentInfo.UAE.value) agreement.payment_info.extend(payment_info) agreement.put() if version == BUY_TOKENS_FLOW_V3_PAUSED: return None utility_bill_step = get_step(steps, 'message_utility_bill') if utility_bill_step: azzert(utility_bill_step.answer_id == FormTO.POSITIVE) url = utility_bill_step.get_value() deferred.defer(save_utility_bill, url, TffProfile.create_key(get_iyo_username(user_details[0]))) tag = { '__rt__.tag': INVEST_FLOW_TAG, 'investment_id': agreement.id } flow_params = { 'token': agreement.token, 'amount': agreement.amount, 'currency': agreement.currency } result = FlowCallbackResultTypeTO(flow=FLOW_CONFIRM_INVESTMENT, tag=json.dumps(tag).decode('utf-8'), force_language=None, flow_params=json.dumps(flow_params)) return FlowMemberResultCallbackResultTO(type=TYPE_FLOW, value=result) except Exception as e: logging.exception(e) return create_error_message()
def _order_node_iyo_see(app_user, node_order_id, pdf_url): iyo_username = get_iyo_username(app_user) organization_id = get_iyo_organization_id() iyo_see_doc = IYOSeeDocumentView(username=iyo_username, globalid=organization_id, uniqueid=u'Zero-Node order %s' % NodeOrder.create_human_readable_id(node_order_id), version=1, category=u'Terms and conditions', link=pdf_url, content_type=u'application/pdf', markdown_short_description=u'Terms and conditions for ordering a Zero-Node', markdown_full_description=u'Terms and conditions for ordering a Zero-Node') logging.debug('Creating IYO SEE document: %s', iyo_see_doc) iyo_see_doc = create_see_document(iyo_username, iyo_see_doc) attachment_name = u' - '.join([iyo_see_doc.uniqueid, iyo_see_doc.category]) def trans(): order = get_node_order(node_order_id) order.tos_iyo_see_id = iyo_see_doc.uniqueid order.put() deferred.defer(_create_quotation, app_user, node_order_id, pdf_url, attachment_name, _transactional=True) ndb.transaction(trans)
def kyc_part_1(message_flow_run_id, member, steps, end_id, end_message_flow_id, parent_message_key, tag, result_key, flush_id, flush_message_flow_id, service_identity, user_details, flow_params): iyo_username = get_iyo_username(user_details[0]) if not iyo_username: logging.error('No username found for user %s', user_details[0]) return create_error_message(FlowMemberResultCallbackResultTO()) result = _validate_kyc_status(iyo_username) if isinstance(result, FlowMemberResultCallbackResultTO): return result step = get_step(steps, 'message_nationality') or get_step( steps, 'message_nationality_with_vibration') assert isinstance(step, FormFlowStepTO) assert isinstance(step.form_result, FormResultTO) assert isinstance(step.form_result.result, UnicodeWidgetResultTO) country_code = step.form_result.result.value ref_step = get_step(steps, 'message_referrer') if ref_step and not result.referrer_user: try: api_set_referral({'code': ref_step.get_value()}, user_details[0]) except ApiCallException as e: return create_error_message(FlowMemberResultCallbackResultTO(), e.message) xml, flow_params = generate_kyc_flow(country_code, iyo_username) result = FlowCallbackResultTypeTO(flow=xml, tag=KYC_FLOW_PART_2_TAG, force_language=None, flow_params=json.dumps(flow_params)) return FlowMemberResultCallbackResultTO(type=TYPE_FLOW, value=result)
def _order_node_iyo_see(app_user, node_order_id, pdf_url, pdf_size, create_quotation=True): iyo_username = get_iyo_username(app_user) doc_id = u'Zero-Node order %s' % NodeOrder.create_human_readable_id( node_order_id) category = u'Terms and conditions' content_type = u'application/pdf' description = u'Terms and conditions for ordering a Zero-Node' create_see_document(doc_id, category, description, iyo_username, pdf_url, content_type) attachment_name = u' - '.join([doc_id, category]) def trans(): order = get_node_order(node_order_id) order.tos_iyo_see_id = doc_id order.put() if create_quotation: deferred.defer(_create_quotation, app_user, node_order_id, pdf_url, attachment_name, pdf_size, _transactional=True) ndb.transaction(trans)
def create_investment_agreement(agreement): # type: (CreateInvestmentAgreementTO) -> InvestmentAgreement app_user = users.User(agreement.app_user) username = get_iyo_username(app_user) token_count_float = get_token_count(agreement.currency, agreement.amount) agreement_model = _create_investment_agreement( agreement.amount, agreement.currency, agreement.token, token_count_float, username, 'manually_created', app_user, status=agreement.status, paid_time=agreement.paid_time, sign_time=agreement.sign_time) prefix, doc_content_base64 = agreement.document.split(',') content_type = prefix.split(';')[0].replace('data:', '') doc_content = base64.b64decode(doc_content_base64) agreement_model.put() pdf_name = InvestmentAgreement.filename(agreement_model.id) pdf_url = upload_to_gcs(pdf_name, doc_content, content_type) deferred.defer(_create_investment_agreement_iyo_see_doc, agreement_model.key, app_user, pdf_url, content_type, send_sign_message=False) return agreement_model
def store_public_key(user_detail): # type: (UserDetailsTO) -> None logging.info('Storing %s key in IYO for user %s:%s', KEY_NAME, user_detail.email, user_detail.app_id) for rt_key in user_detail.public_keys: if rt_key.name == KEY_NAME and rt_key.algorithm == KEY_ALGORITHM: break else: raise PermanentTaskFailure('No key with name "%s" and algorithm "%s" in %s' % (KEY_NAME, KEY_ALGORITHM, repr(user_detail))) username = get_iyo_username(user_detail) keys = get_keystore(username) if any(True for iyo_key in keys if iyo_key.key == rt_key.public_key): logging.info('No new key to store starting with name "%s" and algorithm "%s" in %s', KEY_NAME, KEY_ALGORITHM, repr(user_detail)) return used_labels = [key.label for key in keys] label = KEY_NAME suffix = 2 while label in used_labels: label = u'%s %d' % (KEY_NAME, suffix) suffix += 1 organization_id = get_iyo_organization_id() key = IYOKeyStoreKey(key=rt_key.public_key, username=username, globalid=organization_id, label=label) key.keydata = IYOKeyStoreKeyData(comment=u'ThreeFold app', algorithm=rt_key.algorithm) key.keydata.timestamp = MISSING # Must be missing, else we get bad request since it can't be null result = create_keystore_key(username, key) # We cache the public key - label mapping here so we don't have to go to itsyou.online every time mapping_key = PublicKeyMapping.create_key(result.key, user_detail.email) mapping = PublicKeyMapping(key=mapping_key) mapping.label = result.label mapping.put()
def kyc_part_1(message_flow_run_id, member, steps, end_id, end_message_flow_id, parent_message_key, tag, result_key, flush_id, flush_message_flow_id, service_identity, user_details, flow_params): iyo_username = get_iyo_username(user_details[0]) if not iyo_username: logging.error('No username found for user %s', user_details[0]) return create_error_message() if flush_id == 'flush_corporation': url = get_base_url() + '/users/%s' % iyo_username msg = """"User %s (%s) wants to be KYC approved using his partnership/corporation or trust""" % ( iyo_username, url) send_emails_to_support('Corporation wants to sign up', msg) result = validate_kyc_status(get_tff_profile(iyo_username)) if isinstance(result, FlowMemberResultCallbackResultTO): return result if flush_id == 'flush_corporation': return step = get_step(steps, 'message_nationality') or get_step( steps, 'message_nationality_with_vibration') assert isinstance(step, FormFlowStepTO) assert isinstance(step.form_result, FormResultTO) assert isinstance(step.form_result.result, UnicodeWidgetResultTO) country_code = step.form_result.result.value xml, flow_params = generate_kyc_flow(country_code, iyo_username) result = FlowCallbackResultTypeTO(flow=xml, tag=KYC_FLOW_PART_2_TAG, force_language=None, flow_params=json.dumps(flow_params)) return FlowMemberResultCallbackResultTO(type=TYPE_FLOW, value=result)
def _set_node_id(order_key): order = order_key.get() # type: NodeOrder nodes = get_nodes_from_odoo(order.odoo_sale_order_id) if nodes: username = get_iyo_username(order.app_user) add_nodes_to_profile(username, nodes) logging.info('Saved node_id %s for user %s', nodes, username)
def _inform_support_of_new_node_order(node_order_id): node_order = get_node_order(node_order_id) cfg = get_config(NAMESPACE) iyo_username = get_iyo_username(node_order.app_user) subject = 'New Node Order by %s' % node_order.billing_info.name body = """Hello, We just received a new Node order from %(name)s (IYO username %(iyo_username)s) with id %(node_order_id)s. This order needs to be manually approved since this user has not invested more than %(tokens)s tokens yet via the app. Check the old purchase agreements to verify if this user can sign up as a hoster and if not, contact him. Please visit https://tff-backend.appspot.com/orders/%(node_order_id)s to approve or cancel this order. """ % { 'name': node_order.billing_info.name, 'iyo_username': iyo_username, 'node_order_id': node_order.id, 'tokens': REQUIRED_TOKEN_COUNT_TO_HOST } for email in cfg.investor.support_emails: mail.send_mail(sender='*****@*****.**', to=email, subject=subject, body=body)
def send_message_and_email(app_user, message, subject): human_user, app_id = get_app_user_tuple(app_user) member = MemberTO(member=human_user.email(), app_id=app_id, alert_flags=0) iyo_username = get_iyo_username(app_user) deferred.defer(send_rogerthat_message, member, message) if not DEBUG: deferred.defer(send_intercom_email, iyo_username, subject, message)
def add_user_to_public_role(user_detail): client = get_itsyouonline_client() username = get_iyo_username(user_detail) organization_id = Organization.get_by_role_name(Roles.MEMBERS) if has_access_to_organization(client, organization_id, username): logging.info('User is already in members role, not adding to public role') else: add_user_to_role(user_detail, RogerthatRoles.PUBLIC)
def update_presence(params, user_detail): presence = parse_complex_value(BasePresenceTO, params, False) iyo_username = get_iyo_username(user_detail) participant = EventParticipant.get_or_create_participant(presence.event_id, iyo_username) participant.status = presence.status participant.wants_recording = presence.wants_recording participant.put() return participant.to_dict()
def _create_flow_run(flow_run_key, tag, message_flow_name, user_details, timestamp): return FlowRun(key=flow_run_key, tag=tag, flow_name=message_flow_name, start_date=datetime.utcfromtimestamp(timestamp), user=get_iyo_username(user_details), status=FlowRunStatus.STARTED)
def api_iyo_see_detail(params, user_detail): try: iyo_organization_id = get_iyo_organization_id() iyo_username = get_iyo_username(user_detail) return get_see_document(iyo_organization_id, iyo_username, params['uniqueid'], u'all') except: logging.error('iyo.see.detail exception occurred', exc_info=True) raise ApiCallException(u'Could not load ThreeFold document details. Please try again later.')
def friend_invite_result(rt_settings, request_id, params, response): user_detail = log_and_parse_user_details(params['user_details'])[0] username = get_iyo_username(user_detail) if user_detail.public_keys: deferred.defer(store_public_key, user_detail) deferred.defer(store_info_in_userdata, username, user_detail) deferred.defer(add_grant, username, Grants.PUBLIC) deferred.defer(add_user_to_public_role, user_detail) deferred.defer(populate_intercom_user, Session.create_key(username), user_detail)
def get_investment_agreement_details(agreement_id): agreement = get_investment_agreement(agreement_id) if agreement.iyo_see_id: iyo_organization_id = get_iyo_organization_id() username = get_iyo_username(agreement.app_user) see_document = get_see_document(iyo_organization_id, username, agreement.iyo_see_id) else: see_document = None return InvestmentAgreementDetailsTO.from_model(agreement, see_document)
def needs_utility_bill(agreement): if agreement.currency in ('EUR', 'GBP') \ or (agreement.currency == 'USD' and PaymentInfo.UAE not in agreement.payment_info): tff_profile = get_tff_profile(get_iyo_username(agreement.app_user)) # not uploaded -> must be someone without a passport -> doesn't need utility bill if not tff_profile.kyc.utility_bill_url: return False return not tff_profile.kyc.utility_bill_verified return False
def add_user_to_public_role(user_detail): client = get_itsyouonline_client() username = get_iyo_username(user_detail) grant = Grants.get_by_role_name(Roles.MEMBERS) if has_grant(client, username, grant): logging.info( 'User is already in members role, not adding to public role') else: add_user_to_role(user_detail, RogerthatRoles.PUBLIC)
def _set_node_id(order_key): order = order_key.get() # type: NodeOrder node_id = get_node_id_from_odoo(order.odoo_sale_order_id) if node_id: username = get_iyo_username(order.app_user) profile = TffProfile.create_key(username).get() # type: TffProfile profile.node_id = node_id profile.put() logging.info('Saved node_id %s for user %s', node_id, username)
def get_node_order_details(order_id): # type: (long) -> NodeOrderDetailsTO node_order = get_node_order(order_id) if node_order.tos_iyo_see_id: iyo_organization_id = get_iyo_organization_id() username = get_iyo_username(node_order.app_user) see_document = get_see_document(iyo_organization_id, username, node_order.tos_iyo_see_id) else: see_document = None return NodeOrderDetailsTO.from_model(node_order, see_document)
def is_user_in_roles(user_detail, roles): client = get_itsyouonline_client() username = get_iyo_username(user_detail) result = [] for role in roles: organization_id = Organization.get_by_role_name(role.name) if not organization_id: continue if has_access_to_organization(client, organization_id, username): result.append(role.id) return result
def investment_agreement_signed(message_flow_run_id, member, steps, end_id, end_message_flow_id, parent_message_key, tag, result_key, flush_id, flush_message_flow_id, service_identity, user_details, flow_params): try: user_detail = user_details[0] tag_dict = json.loads(tag) agreement = InvestmentAgreement.create_key(tag_dict['agreement_id']).get() # type: InvestmentAgreement last_step = steps[-1] assert isinstance(last_step, FormFlowStepTO) if last_step.answer_id != FormTO.POSITIVE: logging.info('Investment agreement was canceled') agreement.status = InvestmentAgreement.STATUS_CANCELED agreement.cancel_time = now() agreement.put() return None logging.info('Received signature for Investment Agreement') sign_result = last_step.form_result.result.get_value() assert isinstance(sign_result, SignWidgetResultTO) iyo_username = get_iyo_username(user_detail) sign_see_document(iyo_username, agreement.iyo_see_id, sign_result, user_detail) logging.debug('Storing signature in DB') agreement.populate(status=InvestmentAgreement.STATUS_SIGNED, signature=sign_result.payload_signature, sign_time=now()) agreement.put_async() deferred.defer(add_user_to_role, user_detail, RogerthatRoles.INVESTOR) intercom_tags = get_intercom_tags_for_investment(agreement) if intercom_tags: for i_tag in intercom_tags: deferred.defer(intercom_helpers.tag_intercom_users, i_tag, [iyo_username]) deferred.defer(update_investor_progress, user_detail.email, user_detail.app_id, INVESTMENT_TODO_MAPPING[agreement.status]) deferred.defer(_inform_support_of_new_investment, iyo_username, agreement.id, agreement.token_count_float) if needs_utility_bill(agreement): logging.debug('Sending "utility bill received" message') deferred.defer(_send_utility_bill_received, agreement.app_user) else: logging.debug('Sending confirmation message') deferred.defer(send_payment_instructions, agreement.app_user, agreement.id, '') deferred.defer(send_hoster_reminder, agreement.app_user, _countdown=1) result = FlowCallbackResultTypeTO(flow=FLOW_INVESTMENT_CONFIRMED, tag=None, force_language=None, flow_params=json.dumps({'reference': agreement.reference})) return FlowMemberResultCallbackResultTO(type=TYPE_FLOW, value=result) except: logging.exception('An unexpected error occurred') return create_error_message()
def store_kyc_in_user_data(app_user): username = get_iyo_username(app_user) profile = get_tff_profile(username) user_data = { 'kyc': { 'status': profile.kyc.status, 'verified': profile.kyc.status == KYCStatus.VERIFIED } } email, app_id = get_app_user_tuple(app_user) api_key = get_rogerthat_api_key() return system.put_user_data(api_key, email.email(), app_id, user_data)
def is_user_in_roles(user_detail, roles): result = [] client = get_itsyouonline_client() username = get_iyo_username(user_detail) if not username: return result for role in roles: grant = Grants.get_by_role_name(role.name) if not grant: continue if has_grant(client, username, grant): result.append(role.id) return result
def get_publickey_label(public_key, user_details): # type: (unicode, UserDetailsTO) -> unicode mapping = PublicKeyMapping.create_key(public_key, user_details.email).get() if mapping: return mapping.label else: logging.error('No PublicKeyMapping found! falling back to doing a request to itsyou.online') iyo_keys = get_keystore(get_iyo_username(user_details)) results = filter(lambda k: public_key in k.key, iyo_keys) # some stuff is prepended to the key if len(results): return results[0].label else: logging.error('Could not find label for public key %s on itsyou.online', public_key) return None
def create_node_order(data): # type: (CreateNodeOrderTO) -> NodeOrder if data.status not in (NodeOrderStatus.SIGNED, NodeOrderStatus.SENT, NodeOrderStatus.ARRIVED, NodeOrderStatus.PAID): data.sign_time = MISSING if data.status not in (NodeOrderStatus.SENT, NodeOrderStatus.ARRIVED): data.send_time = MISSING app_user = users.User(data.app_user) order_count = NodeOrder.list_by_so(data.odoo_sale_order_id).count() if order_count > 0: raise OrderAlreadyExistsException(data.odoo_sale_order_id) try: nodes = get_nodes_from_odoo(data.odoo_sale_order_id) except (IndexError, TypeError): logging.warn('Could not get nodes from odoo for order id %s' % data.odoo_sale_order_id, exc_info=True) raise HttpBadRequestException('cannot_find_so_x', {'id': data.odoo_sale_order_id}) if not nodes: raise HttpBadRequestException('no_serial_number_configured_yet', {'sale_order': data.odoo_sale_order_id}) prefix, doc_content_base64 = data.document.split(',') content_type = prefix.split(';')[0].replace('data:', '') if content_type != 'application/pdf': raise InvalidContentTypeException(content_type, ['application/pdf']) doc_content = base64.b64decode(doc_content_base64) order_key = NodeOrder.create_key() pdf_name = NodeOrder.filename(order_key.id()) pdf_url = upload_to_gcs(pdf_name, doc_content, content_type) order = NodeOrder(key=order_key, app_user=app_user, **data.to_dict(exclude=['document', 'app_user'])) order.put() iyo_username = get_iyo_username(app_user) email, app_id = get_app_user_tuple(app_user) deferred.defer(add_nodes_to_profile, iyo_username, nodes) deferred.defer(set_hoster_status_in_user_data, order.app_user, False) deferred.defer(add_user_to_role, UserDetailsTO(email=email.email(), app_id=app_id), RogerthatRoles.HOSTERS) deferred.defer(tag_intercom_users, IntercomTags.HOSTER, [iyo_username]) deferred.defer(_order_node_iyo_see, order.app_user, order.id, pdf_url, len(doc_content), create_quotation=False) return order
def invest(message_flow_run_id, member, steps, end_id, end_message_flow_id, parent_message_key, tag, result_key, flush_id, flush_message_flow_id, service_identity, user_details, flow_params, token): if flush_id == 'flush_kyc': # KYC flow started from within the invest flow return kyc_part_1(message_flow_run_id, member, steps, end_id, end_message_flow_id, parent_message_key, tag, result_key, flush_id, flush_message_flow_id, service_identity, user_details, flow_params) try: email = user_details[0].email app_id = user_details[0].app_id app_user = create_app_user_by_email(email, app_id) logging.info('User %s wants to invest', email) version = db.Key(steps[0].message_flow_id).name() currency = get_step_value(steps, 'message_get_currency').replace('_cur', '') if version.startswith(BUY_TOKENS_FLOW_V3): amount = float( get_step_value(steps, 'message_get_order_size_ITO').replace(',', '.')) token_count_float = get_token_count(currency, amount) else: token_count_float = float( get_step_value(steps, 'message_get_order_size_ITO')) amount = get_investment_amount(currency, token_count_float) username = get_iyo_username(app_user) agreement = _create_investment_agreement( amount, currency, token, token_count_float, username, version, app_user, status=InvestmentAgreement.STATUS_CREATED) agreement.put() if version == BUY_TOKENS_FLOW_V3_PAUSED: return None result = FlowMemberResultCallbackResultTO(type=TYPE_MESSAGE) result.value = create_agreement_confirmation_message(agreement) return result except Exception as e: logging.exception(e) return create_error_message(FlowMemberResultCallbackResultTO())
def trans(): code = params.get("code") if not code: raise ApiCallException(u'Unknown invitation code received') pp = ProfilePointer.get_by_user_code(code) if not pp: raise ApiCallException(u'Unknown invitation code received') username = get_iyo_username(user_detail) if username == pp.username: raise ApiCallException(u'You can\'t use your own invitation code') my_profile = TffProfile.create_key(username).get() # type: TffProfile if not my_profile: raise ApiCallException(u'We were unable to find your profile') if my_profile.referrer_user: raise ApiCallException(u'You already set your referrer') referrer_profile = TffProfile.create_key(pp.username).get() if not referrer_profile: raise ApiCallException( u'We were unable to find your referrer\'s profile') my_profile.referrer_user = referrer_profile.app_user my_profile.referrer_username = pp.username my_profile.put() deferred.defer(remove_user_from_role, user_detail, RogerthatRoles.PUBLIC, _transactional=True) deferred.defer(add_user_to_role, user_detail, RogerthatRoles.MEMBERS, _transactional=True) deferred.defer(remove_user_from_organization, username, Organization.PUBLIC, _transactional=True) deferred.defer(invite_user_to_organization, username, Organization.MEMBERS, _transactional=True) deferred.defer(store_referral_in_user_data, my_profile.key, _transactional=True)
def _send_ito_agreement_to_admin(agreement_key, admin_app_user): logging.debug('Sending SIGN widget to payment admin %s', admin_app_user) agreement = agreement_key.get() # type: InvestmentAgreement widget = SignTO() widget.algorithm = KEY_ALGORITHM widget.caption = u'Sign to mark this investment as paid.' widget.key_name = KEY_NAME widget.payload = base64.b64encode(str(agreement_key.id())).decode('utf-8') form = SignFormTO() form.negative_button = u'Abort' form.negative_button_ui_flags = 0 form.positive_button = u'Accept' form.positive_button_ui_flags = Message.UI_FLAG_EXPECT_NEXT_WAIT_5 form.type = SignTO.TYPE form.widget = widget member_user, app_id = get_app_user_tuple(admin_app_user) message = u"""Enter your pin code to mark purchase agreement %(investment)s (reference %(reference)s as paid. - from: %(user)s\n - amount: %(amount)s %(currency)s - %(token_count_float)s %(token_type)s tokens """ % { 'investment': agreement.reference, 'user': get_iyo_username(agreement.app_user), 'amount': agreement.amount, 'currency': agreement.currency, 'token_count_float': agreement.token_count_float, 'token_type': agreement.token, 'reference': agreement.reference } messaging.send_form(api_key=get_rogerthat_api_key(), parent_message_key=None, member=member_user.email(), message=message, form=form, flags=0, alert_flags=Message.ALERT_FLAG_VIBRATE, branding=get_main_branding_hash(), tag=json.dumps({ u'__rt__.tag': u'sign_investment_agreement_admin', u'agreement_id': agreement_key.id() }).decode('utf-8'), attachments=[], app_id=app_id, step_id=u'sign_investment_agreement_admin')