def _create_investment_agreement(amount, currency, token, token_count_float, username, version, app_user, **kwargs): tff_profile = get_tff_profile(username) if tff_profile.kyc.status != KYCStatus.VERIFIED: raise HttpBadRequestException('cannot_invest_not_kyc_verified') applicant = get_applicant(tff_profile.kyc.applicant_id) name = '%s %s ' % (applicant.first_name, applicant.last_name) address = '%s %s' % (applicant.addresses[0].street, applicant.addresses[0].building_number) address += '\n%s %s' % (applicant.addresses[0].postcode, applicant.addresses[0].town) country = filter(lambda c: c['value'] == applicant.addresses[0].country, country_choices)[0]['label'] address += '\n%s' % country precision = 2 reference = user_code(username) agreement = InvestmentAgreement(creation_time=now(), app_user=app_user, token=token, amount=amount, token_count=long(token_count_float * pow(10, precision)), token_precision=precision, currency=currency, name=name, address=address, version=version, reference=reference, **kwargs) return agreement
def put_investment_agreement(agreement_id, agreement, admin_user): admin_app_user = create_app_user_by_email( admin_user.email(), get_config(NAMESPACE).rogerthat.app_id) # type: (long, InvestmentAgreement, users.User) -> InvestmentAgreement agreement_model = InvestmentAgreement.get_by_id( agreement_id) # type: InvestmentAgreement if not agreement_model: raise HttpNotFoundException('investment_agreement_not_found') if agreement_model.status == InvestmentAgreement.STATUS_CANCELED: raise HttpBadRequestException('order_canceled') if agreement.status not in (InvestmentAgreement.STATUS_SIGNED, InvestmentAgreement.STATUS_CANCELED): raise HttpBadRequestException('invalid_status') # Only support updating the status for now agreement_model.status = agreement.status if agreement_model.status == InvestmentAgreement.STATUS_CANCELED: agreement_model.cancel_time = now() elif agreement_model.status == InvestmentAgreement.STATUS_SIGNED: agreement_model.paid_time = now() if agreement.currency == 'BTC': _set_token_count(agreement_model, agreement.token_count_float) deferred.defer(_send_ito_agreement_to_admin, agreement_model.key, admin_app_user) agreement_model.put() return agreement_model
def invest_complete(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): email = user_details[0].email app_id = user_details[0].app_id if 'confirm' in end_id: agreement_key = InvestmentAgreement.create_key(json.loads(tag)['investment_id']) deferred.defer(_invest, agreement_key, email, app_id, 0)
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 trans(): agreement = InvestmentAgreement.create_key( tag_dict['agreement_id']).get() # type: InvestmentAgreement if answer_id != FormTO.POSITIVE: logging.info('Investment agreement sign aborted') return if agreement.status == InvestmentAgreement.STATUS_PAID: logging.warn( 'Ignoring request to set InvestmentAgreement %s as paid because it is already paid', agreement.id) return agreement.status = InvestmentAgreement.STATUS_PAID agreement.paid_time = now() agreement.put() user_email, app_id, = get_app_user_tuple(agreement.app_user) deferred.defer(transfer_genesis_coins_to_user, agreement.app_user, TokenType.I, long(agreement.token_count_float * 100), _transactional=True) deferred.defer(update_investor_progress, user_email.email(), app_id, INVESTMENT_TODO_MAPPING[agreement.status], _transactional=True) deferred.defer(_send_tokens_assigned_message, agreement.app_user, _transactional=True)
def _move_investment_agreement_pdf_to_gcs(investment_agreement_key): investment_agreement = get_investment_agreement_details( investment_agreement_key.id()) if not investment_agreement.see_document: return original_url = investment_agreement.see_document.versions[0].link _move_pdf(InvestmentAgreement.filename(investment_agreement.id), original_url)
def invest_complete(status, answer_id, received_timestamp, member, message_key, tag, acked_timestamp, parent_message_key, service_identity, user_details): email = user_details[0].email app_id = user_details[0].app_id if answer_id == u'confirm': agreement_key = InvestmentAgreement.create_key( json.loads(tag)['investment_id']) deferred.defer(_invest, agreement_key, email, app_id, 0)
def get_total_investment_value(app_user): statuses = (InvestmentAgreement.STATUS_PAID, InvestmentAgreement.STATUS_SIGNED) total_token_count = get_total_token_count(app_user, InvestmentAgreement.list_by_status_and_user(app_user, statuses)) tokens = total_token_count.keys() stats = dict(zip(tokens, ndb.get_multi([GlobalStats.create_key(token) for token in tokens]))) total_usd = 0 for token, token_count in total_token_count.iteritems(): total_usd += token_count * stats[token].value logging.debug('The tokens of %s are worth $%s', app_user, total_usd) return total_usd
def migrate(dry_run=False): investments = InvestmentAgreement.query().fetch(1000) # type: list[InvestmentAgreement] updates = {} for investment in investments: new_status = INVESTMENT_TODO_MAPPING[investment.status] if investment.app_user not in updates or updates[investment.app_user] < new_status: updates[investment.app_user] = INVESTMENT_TODO_MAPPING[investment.status] if dry_run: return updates for app_user, step in updates.iteritems(): email, app_id = get_app_user_tuple(app_user) deferred.defer(update_investor_progress, email.email(), app_id, step)
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 fix_investments(): investments = InvestmentAgreement.query().filter( InvestmentAgreement.status > InvestmentAgreement.STATUS_CANCELED) to_put = [] to_fix = [] for agreement in investments: # type: InvestmentAgreement if not agreement.token_count: if not agreement.iyo_see_id: _set_token_count(agreement) to_put.append(agreement) elif agreement.currency != 'BTC': to_fix.append(agreement.id) ndb.put_multi(to_put) logging.warn('These investment agreements need manual migration: %s', to_fix)
def manual_fix(): to_fix = [5667908084563968] mapping = {5667908084563968: 0.85} # Incomplete to_put = [] agreements = ndb.get_multi( [InvestmentAgreement.create_key(id) for id in to_fix]) for agreement in agreements: # type: InvestmentAgreement if agreement.currency == 'USD': conversion = 1 else: conversion = mapping[agreement.id] token_count_float = agreement.amount / (conversion * 5) precision = 8 if agreement.currency == 'BTC' else 2 agreement.token_count = long(token_count_float * pow(10, precision)) agreement.token_precision = precision to_put.append(agreement) ndb.put_multi(to_put)
def search_investment_agreements(query=None, page_size=20, cursor=None): # type: (unicode, int, unicode) -> tuple[list[InvestmentAgreement], search.Cursor, bool] options = search.QueryOptions( limit=page_size, cursor=search.Cursor(cursor), ids_only=True, sort_options=search.SortOptions(expressions=[ SortExpression(expression='creation_time', direction=SortExpression.DESCENDING) ])) search_results = INVESTMENT_INDEX.search( search.Query(query, options=options)) # type: search.SearchResults results = search_results.results # type: list[search.ScoredDocument] investment_agreements = ndb.get_multi([ InvestmentAgreement.create_key(long(result.doc_id)) for result in results ]) return investment_agreements, search_results.cursor, search_results.cursor is not None
def token_value_addendum_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): parsed_tag = json.loads(tag) document_key = Document.create_key(parsed_tag['document_id']) last_step = steps[-1] assert isinstance(last_step, FormFlowStepTO) if last_step.answer_id != FormTO.POSITIVE: logging.error('User pressed cancel in the %s flow', FLOW_SIGN_TOKEN_VALUE_ADDENDUM) return None logging.info('Received signature for Document') sign_result = last_step.form_result.result.get_value() assert isinstance(sign_result, SignWidgetResultTO) user_detail = user_details[0] app_user = create_app_user_by_email(user_detail.email, user_detail.app_id) investment_keys = InvestmentAgreement.list_by_user(app_user).fetch(keys_only=True) multiply_agreements_tokens(document_key, sign_result, user_details[0], investment_keys)
def _invest(agreement_key, email, app_id, retry_count): # type: (ndb.Key, unicode, unicode, long, list[int]) -> None from plugins.tff_backend.bizz.agreements import create_token_agreement_pdf app_user = create_app_user_by_email(email, app_id) logging.debug('Creating Token agreement') agreement = get_investment_agreement(agreement_key.id()) _set_token_count(agreement) agreement.put() currency_full = _get_currency_name(agreement.currency) pdf_name = InvestmentAgreement.filename(agreement_key.id()) username = get_iyo_username(app_user) has_verified_utility_bill = get_tff_profile(username).kyc.utility_bill_verified pdf_contents = create_token_agreement_pdf(agreement.name, agreement.address, agreement.amount, currency_full, agreement.currency, agreement.token, agreement.payment_info, has_verified_utility_bill) pdf_url = upload_to_gcs(pdf_name, pdf_contents, 'application/pdf') logging.debug('Storing Investment Agreement in the datastore') pdf_size = len(pdf_contents) deferred.defer(_create_investment_agreement_iyo_see_doc, agreement_key, app_user, pdf_url, pdf_size=pdf_size) deferred.defer(update_investor_progress, email, app_id, INVESTMENT_TODO_MAPPING[agreement.status])
def create_itft_amendment_1_pdf(app_user): from plugins.tff_backend.bizz.investor import get_total_token_count agreements = InvestmentAgreement.list_by_status_and_user(app_user, (InvestmentAgreement.STATUS_PAID, InvestmentAgreement.STATUS_SIGNED)) azzert(agreements) agreements.sort(key=lambda a: a.sign_time) purchase_amounts = '' sign_dates = '' for i, agreement in enumerate(agreements): if i: purchase_amounts += '<br>' sign_dates += '<br>' purchase_amounts += '%s %s' % (agreement.amount, agreement.currency) sign_dates += _get_effective_date(agreement.sign_time) old_count = get_total_token_count(app_user, agreements)[TOKEN_ITFT] new_count = old_count * 100.0 purchase_amount_in_usd = old_count * 5.0 fmt = lambda x: '{:.2f}'.format(x) template_variables = { 'logo_path': 'assets/logo.jpg', 'agreement': _get_effective_date, 'full_name': agreements[0].name, 'purchase_amounts': purchase_amounts, 'sign_dates': sign_dates, 'old_count': fmt(old_count), 'new_count': fmt(new_count), 'purchase_amount_in_usd': fmt(purchase_amount_in_usd), 'title': 'iTFT Purchase Agreement - Amendment I<br>iTFT Token Price & Volume Adjustment' } md = JINJA_ENVIRONMENT.get_template('itft_amendment_1.md').render(template_variables) markdown_to_html = markdown.markdown(md, extensions=['markdown.extensions.tables']) template_variables['markdown_to_html'] = markdown_to_html.replace('<th', '<td') return _render_pdf_from_html('token_itft.html', template_variables)
def create_investment_agreement(agreement): # type: (CreateInvestmentAgreementTO) -> InvestmentAgreement app_user = users.User(agreement.app_user) username = get_iyo_username(app_user) tff_profile = get_tff_profile(username) if tff_profile.kyc.status != KYCStatus.VERIFIED: raise HttpBadRequestException('cannot_invest_not_kyc_verified') 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, pdf_size=len(doc_content)) return agreement_model
def _create_token_value_agreement_if_needed(profile_key): profile = profile_key.get() # type: TffProfile investments = [ i for i in InvestmentAgreement.list_by_user(profile.app_user) if PaymentInfo.HAS_MULTIPLIED_TOKENS not in i.payment_info and i.creation_time <= FF_ENDED_TIMESTAMP ] statuses = [ InvestmentAgreement.STATUS_PAID, InvestmentAgreement.STATUS_SIGNED ] canceled_or_started_investments = [ i for i in investments if i.status not in statuses ] to_put, token_count = multiply_tokens_for_agreements( canceled_or_started_investments) azzert(token_count == 0, 'Expected token_count to be 0') logging.info('Updated %s agreements for user %s', len(to_put), profile.username) if to_put: ndb.put_multi(to_put) has_document = any(d.type == DocumentType.TOKEN_VALUE_ADDENDUM.value for d in Document.list_by_username(profile.username)) if any(i.status in statuses for i in investments) and not has_document: create_token_value_agreement(profile.username)
def _get_investment_agreements(): return InvestmentAgreement.query()
def send_signed_investments_messages(app_user): agreements = InvestmentAgreement.list_by_status_and_user(app_user, InvestmentAgreement.STATUS_SIGNED) for agreement in agreements: deferred.defer(send_payment_instructions, app_user, agreement.id, '')
def _order_node(order_key, user_email, app_id, steps): logging.info('Receiving order of Zero-Node') app_user = create_app_user_by_email(user_email, app_id) overview_step = get_step(steps, 'message_overview') if overview_step and overview_step.answer_id == u"button_use": api_key = get_rogerthat_api_key() user_data_keys = [ 'name', 'email', 'phone', 'billing_address', 'address', 'shipping_name', 'shipping_email', 'shipping_phone', 'shipping_address' ] user_data = system.get_user_data(api_key, user_email, app_id, user_data_keys) billing_info = ContactInfo(name=user_data['name'], email=user_data['email'], phone=user_data['phone'], address=user_data['billing_address'] or user_data['address']) if user_data['shipping_name']: shipping_info = ContactInfo(name=user_data['shipping_name'], email=user_data['shipping_email'], phone=user_data['shipping_phone'], address=user_data['shipping_address']) else: shipping_info = billing_info updated_user_data = None else: name = get_step_value(steps, 'message_name') email = get_step_value(steps, 'message_email') phone = get_step_value(steps, 'message_phone') billing_address = get_step_value(steps, 'message_billing_address') updated_user_data = { 'name': name, 'email': email, 'phone': phone, 'billing_address': billing_address, } billing_info = ContactInfo(name=name, email=email, phone=phone, address=billing_address) same_shipping_info_step = get_step(steps, 'message_choose_shipping_info') if same_shipping_info_step and same_shipping_info_step.answer_id == u"button_yes": shipping_info = billing_info else: shipping_name = get_step_value(steps, 'message_shipping_name') shipping_email = get_step_value(steps, 'message_shipping_email') shipping_phone = get_step_value(steps, 'message_shipping_phone') shipping_address = get_step_value(steps, 'message_shipping_address') updated_user_data.update({ 'shipping_name': shipping_name, 'shipping_email': shipping_email, 'shipping_phone': shipping_phone, 'shipping_address': shipping_address, }) shipping_info = ContactInfo(name=shipping_name, email=shipping_email, phone=shipping_phone, address=shipping_address) socket_step = get_step(steps, 'message_socket') socket = socket_step and socket_step.answer_id.replace('button_', '') # Only one node is allowed per user, and one per location if NodeOrder.has_order_for_user_or_location( app_user, billing_info.address) and not DEBUG: logging.info('User already has a node order, sending abort message') msg = u'Dear ThreeFold Member, we sadly cannot grant your request to host an additional ThreeFold Node:' \ u' We are currently only allowing one Node to be hosted per ThreeFold Member and location.' \ u' This will allow us to build a bigger base and a more diverse Grid.' subject = u'Your ThreeFold Node request' send_message_and_email(app_user, msg, subject) return # Check if user has invested >= 120 tokens paid_orders = InvestmentAgreement.list_by_status_and_user( app_user, InvestmentAgreement.STATUS_PAID) total_tokens = sum([o.token_count_float for o in paid_orders]) can_host = total_tokens >= REQUIRED_TOKEN_COUNT_TO_HOST def trans(): logging.debug('Storing order in the database') order = NodeOrder(key=order_key, app_user=app_user, tos_iyo_see_id=None, billing_info=billing_info, shipping_info=shipping_info, order_time=now(), status=NodeOrderStatus.APPROVED if can_host else NodeOrderStatus.WAITING_APPROVAL, socket=socket) order.put() if can_host: logging.info( 'User has invested more than %s tokens, immediately creating node order PDF.', REQUIRED_TOKEN_COUNT_TO_HOST) deferred.defer(_create_node_order_pdf, order_key.id(), _transactional=True) else: logging.info( 'User has not invested more than %s tokens, an admin needs to approve this order manually.', REQUIRED_TOKEN_COUNT_TO_HOST) deferred.defer(_inform_support_of_new_node_order, order_key.id(), _transactional=True) deferred.defer(set_hoster_status_in_user_data, order.app_user, False, _transactional=True) if updated_user_data: deferred.defer(put_user_data, app_user, updated_user_data, _transactional=True) ndb.transaction(trans)
def investment_agreement_signed(status, form_result, answer_id, member, message_key, tag, received_timestamp, acked_timestamp, parent_message_key, result_key, service_identity, user_details): """ Args: status (int) form_result (FormResultTO) answer_id (unicode) member (unicode) message_key (unicode) tag (unicode) received_timestamp (int) acked_timestamp (int) parent_message_key (unicode) result_key (unicode) service_identity (unicode) user_details(list[UserDetailsTO]) Returns: FormAcknowledgedCallbackResultTO """ try: user_detail = user_details[0] tag_dict = json.loads(tag) agreement = InvestmentAgreement.create_key( tag_dict['agreement_id']).get() # type: InvestmentAgreement if 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 = form_result.result.get_value() assert isinstance(sign_result, SignWidgetResultTO) payload_signature = sign_result.payload_signature iyo_organization_id = get_iyo_organization_id() iyo_username = get_iyo_username(user_detail) logging.debug('Getting IYO SEE document %s', agreement.iyo_see_id) doc = get_see_document(iyo_organization_id, iyo_username, agreement.iyo_see_id) doc_view = IYOSeeDocumentView(username=doc.username, globalid=doc.globalid, uniqueid=doc.uniqueid, **serialize_complex_value( doc.versions[-1], IYOSeeDocumenVersion, False)) doc_view.signature = payload_signature keystore_label = get_publickey_label(sign_result.public_key.public_key, user_detail) if not keystore_label: return create_error_message(FormAcknowledgedCallbackResultTO()) doc_view.keystore_label = keystore_label logging.debug('Signing IYO SEE document') sign_see_document(iyo_organization_id, iyo_username, doc_view) logging.debug('Storing signature in DB') agreement.populate(status=InvestmentAgreement.STATUS_SIGNED, signature=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) logging.debug('Sending confirmation message') prefix_message = u'Thank you. We successfully received your digital signature.' \ u' We have stored a copy of this agreement in your ThreeFold Documents.' \ u' We will contact you again when we have received your payment.' \ u' Thanks again for your purchase and your support of the ThreeFold Foundation!' \ u'\n\nWe would like to take this opportunity to remind you once again to keep a back-up of' \ u' your wallet in a safe place, by writing down the 29 words that can be used to restore it' \ u' to a different device.' \ u' As usual, if you have any questions, don\'t hesitate to contact us.\n\n' msg = u'%sReference: %s' % (prefix_message, agreement.reference) deferred.defer(send_payment_instructions, agreement.app_user, agreement.id, prefix_message) message = MessageCallbackResultTypeTO() message.alert_flags = Message.ALERT_FLAG_VIBRATE message.answers = [] message.branding = get_main_branding_hash() message.dismiss_button_ui_flags = 0 message.flags = Message.FLAG_ALLOW_DISMISS | Message.FLAG_AUTO_LOCK message.message = msg message.step_id = u'investment_agreement_accepted' message.tag = None result = FormAcknowledgedCallbackResultTO() result.type = TYPE_MESSAGE result.value = message return result except: logging.exception('An unexpected error occurred') return create_error_message(FormAcknowledgedCallbackResultTO())
def get_investment_agreement(agreement_id): # type: (long) -> InvestmentAgreement agreement = InvestmentAgreement.get_by_id(agreement_id) if not agreement: raise HttpNotFoundException('investment_agreement_not_found') return agreement