def process_accesscode_request(request, pk): obj = WhitepaperAccessRequest.objects.get(pk=pk) context = { 'obj': obj, } if obj.processed: raise if request.POST.get('submit', False): invitecode = token_hex(16)[:29] AccessCodes.objects.create( invitecode=invitecode, maxuses=1, ) obj.processed = True obj.save() from_email = settings.PERSONAL_CONTACT_EMAIL to_email = obj.email cur_language = translation.get_language() try: setup_lang(to_email) subject = request.POST.get('subject') body = request.POST.get('body').replace('[code]', invitecode) send_mail(from_email, to_email, subject, body, from_name=_("Kevin from Gitcoin.co")) messages.success(request, _('Invite sent')) finally: translation.activate(cur_language) return redirect('/_administration/tdi/whitepaperaccessrequest/?processed=False') return TemplateResponse(request, 'process_accesscode_request.html', context)
def notify_of_lowball_bounty(bounty): """Send an email to [email protected] with the lowball bounty info Args: bounty (dashboard.models.Bounty): The lowball bounty object """ to_email = '*****@*****.**' cc_emails = '*****@*****.**' from_email = settings.CONTACT_EMAIL cur_language = translation.get_language() try: setup_lang(to_email) subject = _("Low Bounty Notification") body = \ f"Bounty: {bounty}\n\n" \ f"url: {bounty.url}\n\n" \ f"Owner Name: {bounty.bounty_owner_name}\n\n" \ f"Owner Email: {bounty.bounty_owner_email}\n\n" \ f"Owner Address: {bounty.bounty_owner_address}\n\n" \ f"Owner Profile: {bounty.bounty_owner_profile}" send_mail(from_email, to_email, subject, body, from_name=_("No Reply from Gitcoin.co"), categories=['admin'], cc_emails=cc_emails) finally: translation.activate(cur_language)
def bounty_emails(self, emails, msg, profile_handle, invite_url=None, kudos_invite=False, retry: bool = True) -> None: """ :param self: :param emails: :param msg: :param profile_handle: :param invite_url: :param kudos_invite: :return: """ with redis.lock("tasks:bounty_email:%s" % invite_url, timeout=LOCK_TIMEOUT): # need to look at how to send bulk emails with SG profile = Profile.objects.get(handle=profile_handle.lower()) try: for email in emails: to_email = email from_email = settings.CONTACT_EMAIL subject = "You have been invited to work on a bounty." html, text = render_share_bounty(to_email, msg, profile, invite_url, kudos_invite) send_mail( from_email, to_email, subject, text, html, from_name=f"@{profile.handle}", categories=['transactional', func_name()], ) except ConnectionError as exc: logger.info(str(exc)) logger.info("Retrying connection") self.retry(countdown=30) except Exception as e: logger.error(str(e))
def send_ptoken_redemption_accepted(profile, ptoken, redemption): from_email = settings.CONTACT_EMAIL to_email = profile.email if not to_email: if profile and profile.user: to_email = profile.user.email if not to_email: return cur_language = translation.get_language() try: setup_lang(to_email) html, text, subject = render_ptoken_redemption_accepted( ptoken, redemption) if not should_suppress_notification_email(to_email, 'personal_token_redemption'): send_mail(from_email, to_email, subject, text, html, categories=['transactional', func_name()]) finally: translation.activate(cur_language)
def whitepaper_new(request, ratelimited=False): context = { 'active': 'whitepaper', 'title': _('Whitepaper'), 'minihero': _('Whitepaper'), 'suppress_logo': True, } if not request.POST.get('submit', False): return TemplateResponse(request, 'whitepaper_new.html', context) if ratelimited: context['msg'] = _( "You're ratelimited. Please contact [email protected]") return TemplateResponse(request, 'whitepaper_accesscode.html', context) context['role'] = request.POST.getlist('role') context['email'] = request.POST.get('email') context['comments'] = request.POST.get('comments') context['success'] = True ip = get_ip(request) body = gettext(""" Email: {} \n Role: {}\n Comments: {}\n IP: {}\n https://gitcoin.co/_administration/tdi/whitepaperaccessrequest/ """).format(context['email'], context['role'], context['comments'], ip) send_mail(settings.CONTACT_EMAIL, settings.CONTACT_EMAIL, _("New Whitepaper Request"), str(body)) WhitepaperAccessRequest.objects.create( email=context['email'], role=context['role'], comments=context['comments'], ip=ip, ) for code in AccessCodes.objects.all(): print(code) invite_to_slack(context['email']) valid_email = True try: validate_email(request.POST.get('email', False)) except ValidationError: valid_email = False if not request.POST.get('email', False) or not valid_email: context['msg'] = _("Invalid Email. Please contact [email protected]") context['success'] = False return TemplateResponse(request, 'whitepaper_new.html', context) context['msg'] = _( "Your request has been sent. <a href=/slack>Meantime, why don't you check out the slack channel?</a>" ) return TemplateResponse(request, 'whitepaper_new.html', context)
def handle(self, *args, **options): bounties = Bounty.objects.filter( network='mainnet', current_bounty=True, web3_created__gte=options['start_date'], web3_created__lte=options['end_date']).order_by( 'web3_created', 'id') tips = Tip.objects.filter( network='mainnet', created_on__gte=options['start_date'], created_on__lte=options['end_date']).order_by('created_on', 'id') formatted_bounties = imap(self.format_bounty, bounties) formatted_tips = imap(self.format_tip, tips) csvfile = StringIO.StringIO() csvwriter = csv.DictWriter( csvfile, fieldnames=[ 'type', 'created_on', 'last_activity', 'amount', 'denomination', 'amount_eth', 'amount_usdt', 'from_address', 'claimee_address', 'repo', 'from_username', 'fulfiller_github_username', 'status', 'comments' ]) csvwriter.writeheader() has_rows = False for item in itermerge(formatted_bounties, formatted_tips, lambda x: x['created_on']): has_rows = True csvwriter.writerow(item) start = options['start_date'].strftime(DATE_FORMAT_HYPHENATED) end = options['end_date'].strftime(DATE_FORMAT_HYPHENATED) now = str(datetime.datetime.now()) if has_rows: subject = 'Gitcoin Activity report from %s to %s' % (start, end) url = self.upload_to_s3( 'activity_report_%s_%s_generated_on_%s.csv' % (start, end, now), csvfile.getvalue()) body = '<a href="%s">%s</a>' % (url, url) print(url) send_mail(settings.CONTACT_EMAIL, settings.CONTACT_EMAIL, subject, body='', html=body, add_bcc=False) self.stdout.write( self.style.SUCCESS('Sent activity report from %s to %s to %s' % (start, end, settings.CONTACT_EMAIL))) else: self.stdout.write( self.style.WARNING('No activity from %s to %s to report' % (start, end)))
def maybe_market_kudos_to_email(kudos_transfer): """Send an email for the specified Kudos. The general flow of this function: - 1. Decide if we are sending it - 2. Generate subject - 3. Render email - 4. Send email - 5. Do translation Args: kudos_transfer (kudos.models.KudosTransfer): The Kudos Email object to be marketed. Returns: bool: Whether or not the email notification was sent successfully. """ # 1. Decide if we are sending it if kudos_transfer.network != settings.ENABLE_NOTIFICATIONS_ON_NETWORK: logger.warning('Notifications are disabled. Skipping email.') logger.debug(kudos_transfer.network) logger.debug(settings.ENABLE_NOTIFICATIONS_ON_NETWORK) return False if not kudos_transfer or not kudos_transfer.txid or not kudos_transfer.amount or not kudos_transfer.kudos_token_cloned_from: logger.warning('Some kudos information not found. Skipping email.') return False # 2. Generate subject from_name = 'Someone' if not kudos_transfer.from_username else f'{kudos_transfer.from_username}' on_network = '' if kudos_transfer.network == 'mainnet' else f'({kudos_transfer.network})' subject = gettext( f"⚡️ {from_name} Sent You a {kudos_transfer.kudos_token_cloned_from.humanized_name} Kudos {on_network}" ) logger.info(f'Emails to send to: {kudos_transfer.emails}') to_email = kudos_transfer.primary_email if to_email: cur_language = translation.get_language() try: setup_lang(to_email) # TODO: Does the from_email field in the database mean nothing? We just override it here. from_email = settings.CONTACT_EMAIL # 3. Render email html, text = render_new_kudos_email(to_email, kudos_transfer, True) # 4. Send email unless the email address has notifications disabled if not should_suppress_notification_email(to_email, 'kudos'): # TODO: Should we be doing something with the response from SendGrid? # Maybe we should store it somewhere. send_mail(from_email, to_email, subject, text, html) finally: # 5. Do translation translation.activate(cur_language) return True
def whitepaper_new(request, ratelimited=False): context = { 'active': 'whitepaper', 'title': 'Whitepaper', 'minihero': 'Whitepaper', 'suppress_logo': True, } if not request.POST.get('submit', False): return TemplateResponse(request, 'whitepaper_new.html', context) if ratelimited: context[ 'msg'] = "You're ratelimited. Please contact [email protected]" return TemplateResponse(request, 'whitepaper_accesscode.html', context) context['role'] = request.POST.getlist('role') context['email'] = request.POST.get('email') context['comments'] = request.POST.get('comments') ip = get_ip(request) body = """ Email: {} \n Role: {}\n Comments: {}\n IP: {}\n https://gitcoin.co/_administration/tdi/whitepaperaccessrequest/ """.format(context['email'], context['role'], context['comments'], ip) send_mail(settings.CONTACT_EMAIL, settings.CONTACT_EMAIL, "New Whitepaper Request", str(body)) war = WhitepaperAccessRequest.objects.create( email=context['email'], role=context['role'], comments=context['comments'], ip=ip, ) valid_email = True try: validate_email(request.POST.get('email', False)) except Exception as e: valid_email = False if not request.POST.get('email', False) or not valid_email: context['msg'] = "Invalid Email. Please contact [email protected]" return TemplateResponse(request, 'whitepaper_new.html', context) context['msg'] = "Your request has been sent." return TemplateResponse(request, 'whitepaper_new.html', context)
def send_personal_token_created(profile, ptoken): from_email = settings.CONTACT_EMAIL to_email = profile.email cur_language = translation.get_language() try: setup_lang(to_email) html, text, subject = render_ptoken_created(ptoken) if not should_suppress_notification_email(to_email, 'personal_token_created'): send_mail(from_email, to_email, subject, text, html, categories=['transactional', func_name()]) finally: translation.activate(cur_language)
def process_accesscode_request(request, pk): obj = WhitepaperAccessRequest.objects.get(pk=pk) context = { 'obj': obj, } if obj.processed: raise if request.POST.get('submit', False): h = hashlib.new('ripemd160') h.update(h.hexdigest() + str(timezone.now())) invitecode = h.hexdigest()[:29] code = AccessCodes.objects.create( invitecode=invitecode, maxuses=1, ) obj.processed = True obj.save() from_email = settings.PERSONAL_CONTACT_EMAIL to_email = obj.email subject = request.POST.get('subject') body = request.POST.get('body').replace('[code]', invitecode) send_mail(from_email, to_email, subject, body, from_name="Kevin from Gitcoin.co") messages.success(request, 'Invite sent') return redirect( '/_administration/tdi/whitepaperaccessrequest/?processed=False') return TemplateResponse(request, 'process_accesscode_request.html', context)
def whitepaper_access(request, ratelimited=False): context = { 'active': 'whitepaper', 'title': _('Whitepaper'), 'minihero': _('Whitepaper'), 'suppress_logo': True, } if not request.POST.get('submit', False): return TemplateResponse(request, 'whitepaper_accesscode.html', context) if ratelimited: context['msg'] = _( "You're ratelimited. Please contact [email protected]") return TemplateResponse(request, 'whitepaper_accesscode.html', context) context['accesskey'] = request.POST.get('accesskey') context['email'] = request.POST.get('email') access_codes = AccessCodes.objects.filter( invitecode=request.POST.get('accesskey')) valid_access_code = access_codes.exists() if not valid_access_code: context['msg'] = _( "Invalid Access Code. Please contact [email protected]") return TemplateResponse(request, 'whitepaper_accesscode.html', context) ac = access_codes.first() if ac.uses >= ac.maxuses: context['msg'] = _( "You have exceeded your maximum number of uses for this access code. Please contact [email protected]" ) return TemplateResponse(request, 'whitepaper_accesscode.html', context) valid_email = True try: validate_email(request.POST.get('email', False)) except Exception as e: valid_email = False if not request.POST.get('email', False) or not valid_email: context['msg'] = _("Invalid Email. Please contact [email protected]") return TemplateResponse(request, 'whitepaper_accesscode.html', context) ip = get_ip(request) wa = WhitepaperAccess.objects.create( invitecode=request.POST.get('accesskey', False), email=request.POST.get('email', False), ip=ip, ) send_mail( settings.CONTACT_EMAIL, settings.CONTACT_EMAIL, _("New Whitepaper Generated"), str(wa), categories=['admin', 'whitepaper_gen'], ) # bottom watermark packet1 = BytesIO() can = canvas.Canvas(packet1, pagesize=letter) grey = Color(22 / 255, 6 / 255, 62 / 255, alpha=0.3) can.setFillColor(grey) can.setFontSize(8) lim = 30 email__etc = wa.email if len(wa.email) < lim else wa.email[0:lim] + "..." msg = gettext( "Generated for access code {} by email {} at {} via ip: {}. https://gitcoin.co/whitepaper" ).format(wa.invitecode, email__etc, wa.created_on.strftime("%Y-%m-%d %H:%M"), wa.ip) charlength = 3.5 width = len(msg) * charlength left = (600 - width) / 2 can.drawString(left, 7, msg) can.save() # middle watermark packet2 = BytesIO() can = canvas.Canvas(packet2, pagesize=letter) grey = Color(22 / 255, 6 / 255, 62 / 255, alpha=0.02) can.setFillColor(grey) can.setFontSize(100) msg = "WP{}".format(str(wa.pk).zfill(5)) charlength = 55 width = len(msg) * charlength left = (600 - width) / 2 can.rotate(45) can.drawString(320, 50, msg) can.save() # move to the beginning of the StringIO buffer path_to_file = 'assets/other/wp.pdf' new_pdf1 = PdfFileReader(packet1) new_pdf2 = PdfFileReader(packet2) # read your existing PDF existing_pdf = PdfFileReader(open(path_to_file, "rb")) output = PdfFileWriter() # add the "watermark" (which is the new pdf) on the existing page try: for i in range(0, 50): page = existing_pdf.getPage(i) page.mergePage(new_pdf1.getPage(0)) if i != 0: page.mergePage(new_pdf2.getPage(0)) output.addPage(page) except Exception as e: print(e) # finally, write "output" to a real file outputfile = "output/whitepaper_{}.pdf".format(wa.pk) outputStream = open(outputfile, "wb") output.write(outputStream) outputStream.close() filename = outputfile wrapper = FileWrapper(open(filename, 'rb')) response = HttpResponse(wrapper, content_type='application/pdf') response[ 'Content-Disposition'] = 'attachment; filename="GitcoinWhitepaper.pdf"' response['Content-Length'] = os.path.getsize(filename) return response
def handle(self, *args, **options): bounties = Bounty.objects.prefetch_related('fulfillments').filter( network='mainnet', current_bounty=True, web3_created__gte=options['start_date'], web3_created__lte=options['end_date']).order_by( 'web3_created', 'id') formatted_bounties = imap(self.format_bounty, bounties) frs = FaucetRequest.objects.filter( created_on__gte=options['start_date'], created_on__lte=options['end_date'], fulfilled=True, ).order_by('created_on', 'id') formatted_frs = imap(self.format_faucet_distribution, frs) tips = Tip.objects.filter(network='mainnet', created_on__gte=options['start_date'], created_on__lte=options['end_date']).exclude( txid='', ).order_by('created_on', 'id') formatted_tips = imap(self.format_tip, tips) enssubregistrations = ENSSubdomainRegistration.objects.filter( created_on__gte=options['start_date'], created_on__lte=options['end_date']).order_by('created_on', 'id') formted_enssubreg = imap(self.format_ens_reg, enssubregistrations) # python3 list hack formatted_frs = [x for x in formatted_frs] formatted_bounties = [x for x in formatted_bounties] formatted_tips = [x for x in formatted_tips] formateted_enssubregistrations = [x for x in formted_enssubreg] all_items = formatted_bounties + formatted_tips + formatted_frs + formateted_enssubregistrations csvfile = StringIO() csvwriter = csv.DictWriter( csvfile, fieldnames=[ 'type', 'created_on', 'last_activity', 'amount', 'denomination', 'amount_eth', 'amount_usdt', 'from_address', 'claimee_address', 'repo', 'from_username', 'fulfiller_github_username', 'status', 'comments', 'payee_bio', 'payee_location' ]) csvwriter.writeheader() items = sorted(all_items, key=lambda x: x['created_on']) has_rows = False for item in items: has_rows = True csvwriter.writerow(item) start = options['start_date'].strftime(DATE_FORMAT_HYPHENATED) end = options['end_date'].strftime(DATE_FORMAT_HYPHENATED) now = str(datetime.datetime.now()) if has_rows: subject = 'Gitcoin Activity report from %s to %s' % (start, end) url = self.upload_to_s3( 'activity_report_%s_%s_generated_on_%s.csv' % (start, end, now), csvfile.getvalue()) body = '<a href="%s">%s</a>' % (url, url) print(url) send_mail(settings.CONTACT_EMAIL, settings.CONTACT_EMAIL, subject, body='', html=body) self.stdout.write( self.style.SUCCESS('Sent activity report from %s to %s to %s' % (start, end, settings.CONTACT_EMAIL))) else: self.stdout.write( self.style.WARNING('No activity from %s to %s to report' % (start, end)))
def redeem_bulk_kudos(self, kt_id, delay_if_gas_prices_gt_redeem=50, override_gas_price=None, send_notif_email=False, override_lock_timeout=LOCK_TIMEOUT, retry=False): """ :param self: :param kt_id: :return: """ try: if True: # override for allowing many xdai minting; we can change this back later there is a race condition # but for now the lock was providing more trouble than good - KO 10/20/2020 #with redis.lock("tasks:redeem_bulk_kudos:%s" % kt_id, timeout=override_lock_timeout): multiplier = 1 # high gas prices, 5 hour gas limit - DL gas_price = int( float(recommend_min_gas_price_to_confirm_in_time(300)) * multiplier) if override_gas_price: gas_price = override_gas_price if gas_price > delay_if_gas_prices_gt_redeem: # do not retry is gas prices are too high # TODO: revisit this when gas prices go down # self.retry(countdown=60*10) return if override_gas_price: gas_price = override_gas_price * 10**9 obj = KudosTransfer.objects.get(pk=kt_id) w3 = get_web3(obj.network) token = obj.kudos_token_cloned_from if token.owner_address.lower( ) != '0x6239FF1040E412491557a7a02b2CBcC5aE85dc8F'.lower(): raise Exception( "kudos isnt owned by Gitcoin; cowardly refusing to spend Gitcoin's ETH minting it" ) kudos_owner_address = settings.KUDOS_OWNER_ACCOUNT kudos_owner_address = Web3.toChecksumAddress(kudos_owner_address) contract_addr = settings.KUDOS_CONTRACT_MAINNET if obj.network == 'xdai': contract_addr = settings.KUDOS_CONTRACT_XDAI if obj.network == 'rinkeby': contract_addr = settings.KUDOS_CONTRACT_RINKEBY kudos_contract_address = Web3.toChecksumAddress(contract_addr) contract = w3.eth.contract( Web3.toChecksumAddress(kudos_contract_address), abi=kudos_abi()) nonce = w3.eth.getTransactionCount(kudos_owner_address) tx = contract.functions.clone( Web3.toChecksumAddress(obj.receive_address), token.token_id, 1).buildTransaction({ 'nonce': nonce, 'gas': 500000, 'gasPrice': gas_price, 'value': int(token.price_finney / 1000.0 * 10**18), }) private_key = settings.KUDOS_PRIVATE_KEY signed = w3.eth.account.signTransaction(tx, private_key) obj.txid = w3.eth.sendRawTransaction(signed.rawTransaction).hex() obj.receive_txid = obj.txid obj.save() while not has_tx_mined(obj.txid, obj.network): time.sleep(1) pass if send_notif_email: from_email = '*****@*****.**' from_name = 'Kevin @ Gitcoin' _to_email = obj.recipient_profile.email subject = f"Your '{obj.kudos_token_cloned_from.name}' Kudos has been minted 🌈" block_url = f'https://etherscan.io/tx/{obj.txid}' if obj.network == 'xdai': block_url = f'https://blockscout.com/poa/xdai/tx/{obj.txid}/internal-transactions' body = f''' Hello @{obj.recipient_profile.handle}, Back on {obj.created_on} you minted a '{obj.kudos_token_cloned_from.name}' Kudos, but the Ethereum network's gas fees were too high for us to mint it on-chain. We're writing with good news. The gas prices on Ethereum have come down, and we are have now minted your token. You can now see the Kudos in your gitcoin profile ( https://gitcoin.co/{obj.recipient_profile.handle} ) or any blockchain wallet that connects to the {obj.network} network ( {block_url} ). HOORAY! Party on, Kevin + the Gitcoin team ''' send_mail(from_email, _to_email, subject, body, from_name=from_name) except (SoftTimeLimitExceeded, TimeLimitExceeded): print('max timeout for bulk kudos redeem exceeded ... giving up!') except Exception as e: print(e) if self.request.retries < self.max_retries: self.retry(countdown=(30 * (self.request.retries + 1))) else: print("max retries for bulk kudos redeem exceeded ... giving up!")
def export_search_to_csv(self, body, user_handle, retry:bool = True) -> None: CSV_HEADER = [ 'profile_id', 'join_date', 'github_created_at', 'first_name', 'last_name', 'email', 'handle', 'sms_verification', 'persona', 'rank_coder', 'rank_funder', 'num_hacks_joined', 'which_hacks_joined', 'hack_work_starts', 'hack_work_submits', 'hack_work_start_orgs', 'hack_work_submit_orgs', 'bounty_work_starts', 'bounty_work_submits', 'hack_started_feature', 'hack_started_code_review', 'hack_started_security', 'hack_started_design', 'hack_started_documentation', 'hack_started_bug', 'hack_started_other', 'hack_started_improvement', 'started_feature', 'started_code_review', 'started_security', 'started_design', 'started_documentation', 'started_bug', 'started_other', 'started_improvement', 'submitted_feature', 'submitted_code_review', 'submitted_security', 'submitted_design', 'submitted_documentation', 'submitted_bug', 'submitted_other', 'submitted_improvement', 'bounty_earnings', 'bounty_work_start_orgs', 'bounty_work_submit_orgs', 'kudos_sends', 'kudos_receives', 'hack_winner_kudos_received', 'grants_opened', 'grant_contributed', 'grant_contributions', 'grant_contribution_amount', 'num_actions', 'action_points', 'avg_points_per_action', 'last_action_on', 'keywords', 'activity_level', 'reliability', 'average_rating', 'longest_streak', 'earnings_count', 'follower_count', 'following_count', 'num_repeated_relationships', 'verification_status' ] user_profile = Profile.objects.get(handle=user_handle) PAGE_SIZE = 1000 proxy_req = HttpRequest() proxy_req.method = 'GET' remote_url = f'{settings.HAYSTACK_ELASTIC_SEARCH_URL}/haystack/modelresult/_search' query_data = json.loads(body) proxy_request = proxy_view(proxy_req, remote_url, {'data': body}) proxy_json_str = proxy_request.content.decode('utf-8') proxy_body = json.loads(proxy_json_str) if not proxy_body['timed_out']: total_hits = proxy_body['hits']['total'] hits = proxy_body['hits']['hits'] finished = False output = [] results = [] if total_hits < PAGE_SIZE: finished = True results = hits if not finished: max_loops = math.ceil(total_hits / PAGE_SIZE) for x in range(0, max_loops): new_body = query_data new_body['from'] = 0 if x is 0 else (PAGE_SIZE * x) + 1 new_body['size'] = PAGE_SIZE new_body = json.dumps(new_body) proxy_request = proxy_view(proxy_req, remote_url, {'data': new_body}) proxy_json_str = proxy_request.content.decode('utf-8') proxy_body = json.loads(proxy_json_str) hits = proxy_body['hits']['hits'] results = results + hits for result in results: source = result['_source'] row_item = {} for k in source.copy(): new_column = k.replace('_exact', '') if new_column in CSV_HEADER: row_item[new_column] = source[k] output.append(row_item) now = datetime.now() csv_file_path = f'/tmp/user-directory-export-{user_profile.handle}-{now}.csv' try: with open(csv_file_path, 'w', encoding='utf-8') as csvfile: writer = csv.DictWriter(csvfile, fieldnames=CSV_HEADER) writer.writeheader() writer.writerows(output) except IOError: print("I/O error") if os.path.isfile(csv_file_path): to_email = user_profile.user.email from_email = settings.CONTACT_EMAIL subject = "Your exported user directory csv is attached" html = text = f'Your exported {csv_file_path.replace("/tmp/", "")} is attached.' send_mail( from_email, to_email, subject, text, html, from_name=f"@{user_profile.handle}", categories=['transactional'], csv=csv_file_path )
def handle(self, *args, **options): bounties = Bounty.objects.prefetch_related('fulfillments').current().filter( network='mainnet', web3_created__gte=options['start_date'], web3_created__lte=options['end_date'] ).order_by('web3_created', 'id') formatted_bounties = imap(self.format_bounty, bounties) frs = FaucetRequest.objects.filter( created_on__gte=options['start_date'], created_on__lte=options['end_date'], fulfilled=True, ).order_by('created_on', 'id') formatted_frs = imap(self.format_faucet_distribution, frs) all_scram = [] for _class in all_sendcryptoasset_models(): objs = _class.objects.filter( network='mainnet', created_on__gte=options['start_date'], created_on__lte=options['end_date'] ).send_success().order_by('created_on', 'id') objs = imap(self.format_cryptoasset, objs) objs = [x for x in objs] all_scram += objs enssubregistrations = ENSSubdomainRegistration.objects.filter( created_on__gte=options['start_date'], created_on__lte=options['end_date'] ).order_by('created_on', 'id') formted_enssubreg = imap(self.format_ens_reg, enssubregistrations) # python3 list hack formatted_frs = [x for x in formatted_frs] formatted_bounties = [x for x in formatted_bounties] formateted_enssubregistrations = [x for x in formted_enssubreg] all_items = formatted_bounties + all_scram + formatted_frs + formateted_enssubregistrations csvfile = StringIO() csvwriter = csv.DictWriter(csvfile, fieldnames=[ 'type', 'created_on', 'last_activity', 'amount', 'denomination', 'amount_eth', 'amount_usdt', 'from_address', 'claimee_address', 'repo', 'from_username', 'fulfiller_github_username', 'status', 'comments', 'payee_bio', 'payee_location']) csvwriter.writeheader() items = sorted(all_items, key=lambda x: x['created_on']) has_rows = False for item in items: has_rows = True csvwriter.writerow(item) start = options['start_date'].strftime(DATE_FORMAT_HYPHENATED) end = options['end_date'].strftime(DATE_FORMAT_HYPHENATED) now = str(datetime.datetime.now()) if has_rows: subject = f'Gitcoin Activity report from {start} to {end}' url = self.upload_to_s3(f'activity_report_{start}_{end}_generated_on_{now}.csv', csvfile.getvalue()) body = f'<a href="{url}">{url}</a>' print(url) send_mail( settings.CONTACT_EMAIL, settings.CONTACT_EMAIL, subject, body='', html=body, categories=['admin', 'activity_report'], ) self.stdout.write( self.style.SUCCESS('Sent activity report from %s to %s to %s' % (start, end, settings.CONTACT_EMAIL)) ) else: self.stdout.write(self.style.WARNING('No activity from %s to %s to report' % (start, end)))