From 4077ea1689b721c57a6d4e374ced0a26b044bd2d Mon Sep 17 00:00:00 2001 From: owocki Date: Thu, 2 Jul 2020 10:03:10 -0600 Subject: [PATCH 1/6] more coming soon --- app/grants/templates/grants/detail/tabs.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/grants/templates/grants/detail/tabs.html b/app/grants/templates/grants/detail/tabs.html index ad946cf7c8e..5f6183bf7a3 100644 --- a/app/grants/templates/grants/detail/tabs.html +++ b/app/grants/templates/grants/detail/tabs.html @@ -114,6 +114,9 @@

Grant Sybil Profile

- Suspicious Ethereum Interactions - Suspicious SMS Address - Suspicious Account Activity + - BrightID Trust Score (coming soon) + - Idena Network Trust Score (coming soon) + - KYC (maybe coming soon) a grant's RiskScore ™️ is equal to its SybilScore ™️ * its matching funds for this round. From 14c3981a1313075c930761b3d6e00627cce08ca0 Mon Sep 17 00:00:00 2001 From: owocki Date: Thu, 2 Jul 2020 11:00:50 -0600 Subject: [PATCH 2/6] cherry-pick of https://github.com/gitcoinco/web/pull/7022/filesgs --- .../management/commands/sent_cart_reminder.py | 103 ++++++++++++++++++ app/marketing/mails.py | 46 +++++--- app/retail/emails.py | 15 +++ app/retail/templates/emails/cart.html | 79 ++++++++++++++ app/retail/templates/emails/cart.txt | 10 ++ 5 files changed, 238 insertions(+), 15 deletions(-) create mode 100644 app/grants/management/commands/sent_cart_reminder.py create mode 100644 app/retail/templates/emails/cart.html create mode 100644 app/retail/templates/emails/cart.txt diff --git a/app/grants/management/commands/sent_cart_reminder.py b/app/grants/management/commands/sent_cart_reminder.py new file mode 100644 index 00000000000..ffdef57fa7e --- /dev/null +++ b/app/grants/management/commands/sent_cart_reminder.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +"""Define the Grant subminer management command. + +Copyright (C) 2020 Gitcoin Core + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +""" +from datetime import datetime + +from django.conf import settings +from django.core.management.base import BaseCommand +from django.db.models import Max, F +from django.utils import timezone + +from dashboard.utils import get_tx_status, has_tx_mined +from grants.clr import predict_clr +from grants.models import Contribution, Grant, CartActivity, Subscription +from grants.views import clr_active, round_end, next_round_start +from marketing.mails import warn_subscription_failed, remember_your_cart +from townsquare.models import MatchRound + + +class Command(BaseCommand): + help = 'Sent reminder to user who forgot its cart ' + + def add_arguments(self, parser): + parser.add_argument( + '--test', + default=False, + type=bool, + help="Only process and display the carts to being delivered" + ) + parser.add_argument( + '--full-cart', + default=False, + type=bool, + help="Should the cart being delivered partially" + ) + parser.add_argument( + '--hours', + type=int, + help="Should the cart being delivered partially" + ) + + + def handle(self, *args, **options): + last_activity_by_user = CartActivity.objects.filter(latest=True, created_on__gt=next_round_start).exclude(metadata=[]) + count = 0 + if options.get('hours'): + hours = options.get('hours') + else: + hours = int((round_end - datetime.now()).total_seconds() / 3600) + + for activity in last_activity_by_user: + print(activity) + + # Check if this cart is still valid + no_checkout_grants = [] + for grant_entry in activity.metadata: + subscription = Subscription.objects.filter(grant_id=grant_entry['grant_id'], + contributor_profile=activity.profile, + created_on__gt=activity.created_on, + created_on__lte=round_end).first() + + if not subscription: + no_checkout_grants.append(grant_entry) + + if options['full_cart']: + if len(no_checkout_grants) != len(activity.metadata): + print(f' * Activity {activity.id}: The grants were partially contributed but no notification will be delivered') + continue + + cart_query = [f'{grant["grant_id"]};{grant.get("grant_donation_amount", 5)};{grant.get("token_local_id", 283)}' + for grant in no_checkout_grants] + + cart_query = ','.join(cart_query) + + if not cart_query: + print(f'** No items left in the {activity.profile}\'s cart') + continue + + if not options['test']: + try: + remember_your_cart(activity.profile, cart_query, no_checkout_grants, hours) + count += 1 + except Exception as e: + print(f'!! Failed to sent cart reminder email to {activity.profile}') + print(e) + + print(f'\n\nSent {count} emails of {last_activity_by_user.count()} carts') + diff --git a/app/marketing/mails.py b/app/marketing/mails.py index 7db847e73cf..1280e579e8e 100644 --- a/app/marketing/mails.py +++ b/app/marketing/mails.py @@ -45,7 +45,7 @@ render_start_work_approved, render_start_work_new_applicant, render_start_work_rejected, render_subscription_terminated_email, render_successful_contribution_email, render_support_cancellation_email, render_tax_report, render_thank_you_for_supporting_email, render_tip_email, - render_unread_notification_email_weekly_roundup, render_wallpost, render_weekly_recap, + render_unread_notification_email_weekly_roundup, render_wallpost, render_weekly_recap, render_remember_your_cart, ) from sendgrid.helpers.mail import Attachment, Content, Email, Mail, Personalization from sendgrid.helpers.stats import Category @@ -118,7 +118,7 @@ def send_mail(from_email, _to_email, subject, body, html=False, mail.add_attachment(attachment) # debug logs - logger.info(f"-- Sending Mail '{subject}' to {to_email.email}") + logger.info(f"-- Sending Mail '{subject}' to {to_email}") try: response = sg.client.mail.send.post(request_body=mail.get()) except UnauthorizedError as e: @@ -1031,7 +1031,7 @@ def grant_match_distribution_test_txn(match): coupon = f"Pick up ONE item of Gitcoin Schwag at http://store.gitcoin.co/ at 50% off with coupon code {settings.GRANTS_COUPON_50_OFF}" if match.amount > 1000: coupon = f"Pick up ONE item of Gitcoin Schwag at http://store.gitcoin.co/ at 100% off with coupon code {settings.GRANTS_COUPON_100_OFF}" - # NOTE: IF YOURE A CLEVER BISCUT AND FOUND THIS BY READING OUR CODEBASE, + # NOTE: IF YOURE A CLEVER BISCUT AND FOUND THIS BY READING OUR CODEBASE, # THEN GOOD FOR YOU! HERE IS A 100% OFF COUPON CODE U CAN USE (LIMIT OF 1 FOR THE FIRST PERSON # TO FIND THIS EASTER EGG) : GRANTS-ROUND-5-HAXXOR try: @@ -1090,9 +1090,9 @@ def grant_match_distribution_final_txn(match): We have sent your {rounded_amount} DAI to the address on file at {match.grant.admin_address}. The txid of this transaction is {match.payout_tx}. -Congratulations on a successful Gitcoin Grants Round {match.round_number}. +Congratulations on a successful Gitcoin Grants Round {match.round_number}. -What now? +What now? 1. Send a tweet letting us know how these grant funds are being used to support your project (our twitter username is @gitcoin). 2. Remember to update your grantees on what you use the funds for by clicking through to your grant ( https://gitcoin.co{match.grant.get_absolute_url()} ) and posting to your activity feed. 3. Celebrate 🎉 and then get back to BUIDLing something great. 🛠 @@ -1216,7 +1216,7 @@ def new_bounty_daily(bounties, old_bounties, to_emails=None): bounties = bounties[0:max_bounties] if to_emails is None: to_emails = [] - + from marketing.views import quest_of_the_day, upcoming_grant, upcoming_hackathon, latest_activities, upcoming_dates, upcoming_dates, email_announcements quest = quest_of_the_day() grant = upcoming_grant() @@ -1258,7 +1258,7 @@ def new_bounty_daily(bounties, old_bounties, to_emails=None): elif old_bounties: plural_old_bounties = "Bounties" if len(old_bounties)>1 else "Bounty" new_bounties = f"💰{len(old_bounties)} {plural_old_bounties}" - + new_quests = "" if quest: new_quests = f"🎯1 Quest" @@ -1551,7 +1551,7 @@ def quarterly_stats(to_emails=None, platform_wide_stats=None): ) finally: translation.activate(cur_language) - + def tax_report(to_emails=None, zip_paths=None, tax_year=None): if to_emails is None: @@ -1570,18 +1570,18 @@ def tax_report(to_emails=None, zip_paths=None, tax_year=None): html, text = render_tax_report(to_email, tax_year) from_email = settings.CONTACT_EMAIL send_mail( - from_email, - to_email, - subject, - text, - html, + from_email, + to_email, + subject, + text, + html, from_name="Kevin Owocki (Gitcoin.co)", categories=['marketing', func_name()], zip_path=zip_paths[idx] ) - finally: + finally: translation.activate(cur_language) - + def bounty_expire_warning(bounty, to_emails=None): if not bounty or not bounty.value_in_usdt_now: @@ -1903,3 +1903,19 @@ def fund_request_email(request, to_emails, is_new=False): send_mail(from_email, to_email, subject, text, html, categories=['transactional', func_name()]) finally: translation.activate(cur_language) + + +def remember_your_cart(profile, cart_query, grants, hours): + to_email = profile.email + from_email = settings.CONTACT_EMAIL + + cur_language = translation.get_language() + try: + setup_lang(to_email) + subject = f"⏱{hours} hours left 🛒 Your grant cart is waiting for you 🛒" + html, text = render_remember_your_cart(cart_query, grants, hours) + + if not should_suppress_notification_email(to_email, 'grant_updates'): + send_mail(from_email, to_email, subject, text, html, categories=['marketing', func_name()]) + finally: + translation.activate(cur_language) diff --git a/app/retail/emails.py b/app/retail/emails.py index 53d31ad46f1..53122568268 100644 --- a/app/retail/emails.py +++ b/app/retail/emails.py @@ -1621,3 +1621,18 @@ def start_work_applicant_expired(request): bounty = Bounty.objects.last() response_html, _, _ = render_start_work_applicant_expired(interest, bounty) return HttpResponse(response_html) + + + +def render_remember_your_cart(grants_query, grants, hours): + params = { + 'base_url': settings.BASE_URL, + 'desc': f'Only left {hours} hours until the end of the match round and seems you have some grants on your cart', + 'cart_query': grants_query, + 'grants': grants + } + + response_html = premailer_transform(render_to_string("emails/cart.html", params)) + response_txt = render_to_string("emails/cart.txt", params) + + return response_html, response_txt diff --git a/app/retail/templates/emails/cart.html b/app/retail/templates/emails/cart.html new file mode 100644 index 00000000000..82de27b075c --- /dev/null +++ b/app/retail/templates/emails/cart.html @@ -0,0 +1,79 @@ +{% extends 'emails/template.html' %} +{% comment %} + Copyright (C) 2020 Gitcoin Core + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +{% endcomment %} +{% load i18n humanize %} + +{% block content %} + + + +
+

{% trans "Your cart is waiting for you" %}

+
+ + + + +
 
+
+

+ {{desc|safe}} +

+
+ +
+ +
+

+ You have the following {{grants|length}} grants in your cart: +

+
    + {% for grant in grants %} +
  1. {{ grant.grant_title }} ({{ grant.grant_donation_amount|default:5 }} {{ grant.grant_donation_currency|default:"DAI" }})
  2. + {% endfor %} +
+
+ +
+ + +
+ {% trans "Go to cart" %} +
+ + + +{% endblock %} diff --git a/app/retail/templates/emails/cart.txt b/app/retail/templates/emails/cart.txt new file mode 100644 index 00000000000..862bda17e64 --- /dev/null +++ b/app/retail/templates/emails/cart.txt @@ -0,0 +1,10 @@ +Your cart is waiting for you + +{{ desc }}. + +Your cart grants: +{% for grant in grants %} + - {{ grant.grant_title }} ({{ grant.grant_donation_amount|default:5 }} {{ grant.grant_donation_currency|default:"DAI" }}) +{% endfor %} + +Checkout your cart: {{base_url}}{% url 'grants:grants_bulk_add' cart_query %} From cca6a56019cc496ee5f9f75189b9f8a08a44e8a9 Mon Sep 17 00:00:00 2001 From: octavioamu Date: Thu, 2 Jul 2020 17:57:50 -0300 Subject: [PATCH 3/6] fix usd bounty detail --- app/dashboard/templates/bounty/details2.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/dashboard/templates/bounty/details2.html b/app/dashboard/templates/bounty/details2.html index 42ada3f0fbb..d40f1a42e5b 100644 --- a/app/dashboard/templates/bounty/details2.html +++ b/app/dashboard/templates/bounty/details2.html @@ -66,7 +66,7 @@

[[bounty.title]]< [[ bounty.value_true ]] [[ bounty.token_name ]]

-
+

[[bounty.value_in_usdt_now]] USD From 39dd1cd9c5012e39295a9fbeeef01eee8bbb0687 Mon Sep 17 00:00:00 2001 From: octavioamu Date: Thu, 2 Jul 2020 18:06:05 -0300 Subject: [PATCH 4/6] fix hack onboard explorer link --- app/dashboard/templates/dashboard/hackathon/onboard.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/dashboard/templates/dashboard/hackathon/onboard.html b/app/dashboard/templates/dashboard/hackathon/onboard.html index d387fa3d140..244defec039 100644 --- a/app/dashboard/templates/dashboard/hackathon/onboard.html +++ b/app/dashboard/templates/dashboard/hackathon/onboard.html @@ -253,7 +253,7 @@

How does the Hackathon w {% trans "Check out the Prizes" %} {% blocktrans %}

- Visit the Prize Explorer to check out the prizes posted by our hackathon sponsors. Click each prize to show important details, including the submission requirements, submission deadline, etc. + Visit the Prize Explorer to check out the prizes posted by our hackathon sponsors. Click each prize to show important details, including the submission requirements, submission deadline, etc.

{% endblocktrans %}

From e741f2e34392a9a0f2667ae7f527d7adf1e740ed Mon Sep 17 00:00:00 2001 From: octavioamu Date: Thu, 2 Jul 2020 18:53:12 -0300 Subject: [PATCH 5/6] fix onboard --- app/dashboard/templates/dashboard/hackathon/onboard.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/dashboard/templates/dashboard/hackathon/onboard.html b/app/dashboard/templates/dashboard/hackathon/onboard.html index 244defec039..447f6d653ce 100644 --- a/app/dashboard/templates/dashboard/hackathon/onboard.html +++ b/app/dashboard/templates/dashboard/hackathon/onboard.html @@ -251,11 +251,11 @@

How does the Hackathon w

{% trans "Check out the Prizes" %} - {% blocktrans %} +

Visit the Prize Explorer to check out the prizes posted by our hackathon sponsors. Click each prize to show important details, including the submission requirements, submission deadline, etc.

- {% endblocktrans %} +
From 20491a02a0aaaf74dba7e5f8ea364d836b0cec1c Mon Sep 17 00:00:00 2001 From: Aditya Anand M C Date: Fri, 3 Jul 2020 16:18:33 +0530 Subject: [PATCH 6/6] chore: fix sync_amount API + prettify conversion table (#7049) Co-authored-by: Dan Lipert --- app/dashboard/helpers.py | 59 ++++++++++++++++++++++------------------ app/economy/admin.py | 1 + 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/app/dashboard/helpers.py b/app/dashboard/helpers.py index 1b8bd1dafa0..7bb298842bb 100644 --- a/app/dashboard/helpers.py +++ b/app/dashboard/helpers.py @@ -126,40 +126,45 @@ def amount(request): """ response = {} - try: - amount = str(request.GET.get('amount', '1')) + amount = str(request.GET.get('amount', '1')) - if not amount.replace('.','').isnumeric(): - return HttpResponseBadRequest('not number') + if not amount.replace('.','').isnumeric(): + return HttpResponseBadRequest('not number') - denomination = request.GET.get('denomination', 'ETH') - tokens = denomination.split(',') + denomination = request.GET.get('denomination', 'ETH') + tokens = denomination.split(',') - response = [] + response = [] - for token in tokens: - if token in settings.STABLE_COINS: - token = 'USDT' - if token == 'ETH': - amount_in_eth = float(amount) - else: + for token in tokens: + if token in settings.STABLE_COINS: + token = 'USDT' + if token == 'ETH': + amount_in_eth = float(amount) + else: + try: amount_in_eth = convert_amount(amount, token, 'ETH') - amount_in_usdt = convert_amount(amount_in_eth, 'ETH', 'USDT') + except Exception as e: + logger.debug(e) + amount_in_eth = None + try: + if amount_in_eth: + amount_in_usdt = convert_amount(amount_in_eth, 'ETH', 'USDT') + else: + amount_in_usdt = convert_amount(amount, token, 'USDT') - response.append({ - 'token': token, - 'amount': float(amount), - 'eth': amount_in_eth, - 'usdt': amount_in_usdt - }) + except Exception as e: + logger.debug(e) + amount_in_usdt = None - return JsonResponse(response, safe=False) - except ConversionRateNotFoundError as e: - logger.debug(e) - raise Http404 - except Exception as e: - logger.error(e) - raise Http404 + response.append({ + 'token': token, + 'amount': float(amount), + 'eth': amount_in_eth, + 'usdt': amount_in_usdt + }) + + return JsonResponse(response, safe=False) @ratelimit(key='ip', rate='50/m', method=ratelimit.UNSAFE, block=True) diff --git a/app/economy/admin.py b/app/economy/admin.py index 89bd134a2c2..f830e3a91ec 100644 --- a/app/economy/admin.py +++ b/app/economy/admin.py @@ -54,6 +54,7 @@ class ConvRateAdmin(admin.ModelAdmin): ordering = ['-id'] search_fields = ['from_currency', 'to_currency'] + list_display =['id', 'from_currency', 'from_amount','to_currency', 'to_amount', 'source'] admin.site.register(ConversionRate, ConvRateAdmin)