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" %}
+
+
+
+
+
+
+
+
+
+
+
+
+ You have the following {{grants|length}} grants in your cart:
+
+
+ {% for grant in grants %}
+ - {{ grant.grant_title }} ({{ grant.grant_donation_amount|default:5 }} {{ grant.grant_donation_currency|default:"DAI" }})
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+{% 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)