From 32b801dc8d45c871e177c2c1af65513b7432d55c Mon Sep 17 00:00:00 2001 From: Aditya Anand M C Date: Thu, 15 Jul 2021 05:49:06 +0530 Subject: [PATCH 01/10] GITC-208: fix QD signature failing when saved (#9293) --- app/assets/v2/js/quadraticlands/mission/diplomacy/room.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/assets/v2/js/quadraticlands/mission/diplomacy/room.js b/app/assets/v2/js/quadraticlands/mission/diplomacy/room.js index 94560bef040..bc3e68ab711 100644 --- a/app/assets/v2/js/quadraticlands/mission/diplomacy/room.js +++ b/app/assets/v2/js/quadraticlands/mission/diplomacy/room.js @@ -255,13 +255,15 @@ async function vouche() { result.push(entry); }); + let balance = await getTokenBalances(gtc_address()); + // @kev : do something with the result ( sign, safe, whatever) // this is how far i could come. now your turn :) const accounts = await web3.eth.getAccounts(); const account = accounts[0]; const _package = { votes: result, - balance: balance, + balance: balance.balance, account: account }; let signature = await web3.eth.personal.sign( From bf597c9b5c4b9d122ad0a2382cf365762c7c8a45 Mon Sep 17 00:00:00 2001 From: Aditya Anand M C Date: Thu, 15 Jul 2021 06:16:49 +0530 Subject: [PATCH 02/10] fix decimal --- app/assets/v2/js/quadraticlands/mission/diplomacy/room.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/v2/js/quadraticlands/mission/diplomacy/room.js b/app/assets/v2/js/quadraticlands/mission/diplomacy/room.js index bc3e68ab711..2badfe97231 100644 --- a/app/assets/v2/js/quadraticlands/mission/diplomacy/room.js +++ b/app/assets/v2/js/quadraticlands/mission/diplomacy/room.js @@ -263,7 +263,7 @@ async function vouche() { const account = accounts[0]; const _package = { votes: result, - balance: balance.balance, + balance: balance.balance * 10 ** 18, account: account }; let signature = await web3.eth.personal.sign( From acae7ed3d5bd5a6a7ef3739f6c4a6ab926de22c4 Mon Sep 17 00:00:00 2001 From: Aditya Anand M C Date: Thu, 15 Jul 2021 17:04:47 +0530 Subject: [PATCH 03/10] move the *10^18 to backend --- app/assets/v2/js/quadraticlands/mission/diplomacy/room.js | 2 +- app/quadraticlands/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/v2/js/quadraticlands/mission/diplomacy/room.js b/app/assets/v2/js/quadraticlands/mission/diplomacy/room.js index 2badfe97231..bc3e68ab711 100644 --- a/app/assets/v2/js/quadraticlands/mission/diplomacy/room.js +++ b/app/assets/v2/js/quadraticlands/mission/diplomacy/room.js @@ -263,7 +263,7 @@ async function vouche() { const account = accounts[0]; const _package = { votes: result, - balance: balance.balance * 10 ** 18, + balance: balance.balance, account: account }; let signature = await web3.eth.personal.sign( diff --git a/app/quadraticlands/views.py b/app/quadraticlands/views.py index 125d10e668f..69e064e283e 100644 --- a/app/quadraticlands/views.py +++ b/app/quadraticlands/views.py @@ -377,7 +377,7 @@ def mission_diplomacy_room_helper(request, game): web3 = get_web3('mainnet') gtc = web3.eth.contract(address=Web3.toChecksumAddress('0xde30da39c46104798bb5aa3fe8b9e0e1f348163f'), abi=erc20_abi) balance = gtc.functions.balanceOf(recipient_address).call() - claimed_balance = int(moves['balance']) + claimed_balance = int(moves['balance']) * 10 ** 18 signer_address = Web3.toChecksumAddress(web3.eth.account.recoverHash(defunct_hash_message(text=package), signature=signature)) claimed_address = Web3.toChecksumAddress(moves['account']) From 1cd8b2a4006ce34f7313ec3cc2b37a7234ceb976 Mon Sep 17 00:00:00 2001 From: Aditya Anand M C Date: Thu, 15 Jul 2021 17:48:39 +0530 Subject: [PATCH 04/10] final fix for QD --- app/quadraticlands/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/quadraticlands/views.py b/app/quadraticlands/views.py index 69e064e283e..22fd9542e18 100644 --- a/app/quadraticlands/views.py +++ b/app/quadraticlands/views.py @@ -376,8 +376,8 @@ def mission_diplomacy_room_helper(request, game): recipient_address = Web3.toChecksumAddress(moves['account']) web3 = get_web3('mainnet') gtc = web3.eth.contract(address=Web3.toChecksumAddress('0xde30da39c46104798bb5aa3fe8b9e0e1f348163f'), abi=erc20_abi) - balance = gtc.functions.balanceOf(recipient_address).call() - claimed_balance = int(moves['balance']) * 10 ** 18 + balance = int((gtc.functions.balanceOf(recipient_address).call()) / (10 ** 18)) + claimed_balance = int(moves['balance']) signer_address = Web3.toChecksumAddress(web3.eth.account.recoverHash(defunct_hash_message(text=package), signature=signature)) claimed_address = Web3.toChecksumAddress(moves['account']) From 8f4855480819942ae6292fc6e7b14d8b9cba862c Mon Sep 17 00:00:00 2001 From: Graham Dixon Date: Thu, 15 Jul 2021 15:00:26 +0100 Subject: [PATCH 05/10] GITC-165: Increase celery task_time_limit to account for longer running tasks (v2) (#9294) --- app/dashboard/tasks.py | 2 +- app/grants/tasks.py | 2 +- app/townsquare/tasks.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/dashboard/tasks.py b/app/dashboard/tasks.py index c692ac99b51..bfa515e7479 100644 --- a/app/dashboard/tasks.py +++ b/app/dashboard/tasks.py @@ -258,7 +258,7 @@ def maybe_market_to_user_slack(self, bounty_pk, event_name, retry: bool = True) maybe_market_to_user_slack_helper(bounty, event_name) -@app.shared_task(bind=True, max_retries=3) +@app.shared_task(bind=True, soft_time_limit=600, time_limit=660, max_retries=3) def grant_update_email_task(self, pk, retry: bool = True) -> None: """ :param self: diff --git a/app/grants/tasks.py b/app/grants/tasks.py index 945186cda0c..853855a0d21 100644 --- a/app/grants/tasks.py +++ b/app/grants/tasks.py @@ -31,7 +31,7 @@ def lineno(): """Returns the current line number in our program.""" return inspect.currentframe().f_back.f_lineno -@app.shared_task(bind=True, max_retries=1) +@app.shared_task(bind=True, soft_time_limit=600, time_limit=660, max_retries=1) def update_grant_metadata(self, grant_id, retry: bool = True) -> None: if settings.FLUSH_QUEUE: diff --git a/app/townsquare/tasks.py b/app/townsquare/tasks.py index 9b7bcb0c47a..c765cc9e5b7 100644 --- a/app/townsquare/tasks.py +++ b/app/townsquare/tasks.py @@ -14,7 +14,7 @@ # Lock timeout of 2 minutes (just in the case that the application hangs to avoid a redis deadlock) LOCK_TIMEOUT = 60 * 2 -@app.shared_task(bind=True, max_retries=3) +@app.shared_task(bind=True, soft_time_limit=600, time_limit=660, max_retries=3) def increment_view_counts(self, pks, retry=False): """ :param self: From d41e9d06651eebf0864424b0e9b26e065607b91c Mon Sep 17 00:00:00 2001 From: Graham Dixon Date: Thu, 15 Jul 2021 15:01:49 +0100 Subject: [PATCH 06/10] GITC-210: Record visit/join actions inside a celery task instead of context.py (#9295) * GITC-210: Record visit/join actions inside a celery task instead of context.py * GITC-210: Removes duplicated User imports * GITC-210: Drops try...except wrapper from profile_dict.delay(profile.pk) --- app/app/context.py | 50 +++++++++++----------------- app/assets/v2/js/base.js | 6 ++-- app/dashboard/tasks.py | 70 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 90 insertions(+), 36 deletions(-) diff --git a/app/app/context.py b/app/app/context.py index 828ac8b447a..2fcc3fc3d7b 100644 --- a/app/app/context.py +++ b/app/app/context.py @@ -29,6 +29,7 @@ from app.utils import get_location_from_ip from cacheops import cached_as from dashboard.models import Activity, Tip, UserAction +from dashboard.tasks import record_join, record_visit from dashboard.utils import _get_utm_from_cookie from kudos.models import KudosTransfer from marketing.utils import handle_marketing_callback @@ -80,41 +81,26 @@ def preprocess(request): profile = request.user.profile if user_is_authenticated and hasattr(request.user, 'profile') else None if user_is_authenticated and profile and profile.pk: # what actions to take? - record_join = not profile.last_visit - record_visit = not profile.last_visit or profile.last_visit < ( + should_record_join = not profile.last_visit + should_record_visit = not profile.last_visit or profile.last_visit < ( timezone.now() - timezone.timedelta(seconds=RECORD_VISIT_EVERY_N_SECONDS) ) - if record_visit: - try: - profile.last_visit = timezone.now() - profile.save() - except Exception as e: - logger.exception(e) - try: - from dashboard.tasks import profile_dict - profile_dict.delay(profile.pk) - except Exception as e: - logger.exception(e) - metadata = { - 'visitorId': request.COOKIES.get("visitorId", None), - 'useragent': request.META['HTTP_USER_AGENT'], - 'referrer': request.META.get('HTTP_REFERER', None), - 'path': request.META.get('PATH_INFO', None), - 'session_key': request.session._session_key, - } + + if should_record_visit: + # values to pass to celery from the request ip_address = get_ip(request) - UserAction.objects.create( - user=request.user, - profile=profile, - action='Visit', - location_data=get_location_from_ip(ip_address), - ip_address=ip_address, - utm=_get_utm_from_cookie(request), - metadata=metadata, - ) - - if record_join: - Activity.objects.create(profile=profile, activity_type='joined') + visitorId = request.COOKIES.get("visitorId", None) + useragent = request.META['HTTP_USER_AGENT'] + referrer = request.META.get('HTTP_REFERER', None) + path = request.META.get('PATH_INFO', None) + session_key = request.session._session_key + utm = _get_utm_from_cookie(request) + # record the visit as a celery task + record_visit.delay(request.user.pk, profile.pk, ip_address, visitorId, useragent, referrer, path, session_key, utm) + + if should_record_join: + # record the joined action as a celery task + record_join.delay(profile.pk) ptoken = PersonalToken.objects.filter(token_owner_profile=profile).first() diff --git a/app/assets/v2/js/base.js b/app/assets/v2/js/base.js index 8ece965b75a..b795e901fcb 100644 --- a/app/assets/v2/js/base.js +++ b/app/assets/v2/js/base.js @@ -104,8 +104,10 @@ $(document).ready(function() { }); }; - if (document.visitorId) { - Cookies.set('visitorId', document.visitorId); + var visitorId = (document.visitorId || Cookies.get('_vid')); + + if (visitorId) { + Cookies.set('visitorId', visitorId); } record_campaign_to_cookie(); diff --git a/app/dashboard/tasks.py b/app/dashboard/tasks.py index bfa515e7479..f0eb2901467 100644 --- a/app/dashboard/tasks.py +++ b/app/dashboard/tasks.py @@ -8,11 +8,13 @@ from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.http import HttpRequest +from django.utils import timezone from app.services import RedisService +from app.utils import get_location_from_ip from celery import app, group from celery.utils.log import get_task_logger -from dashboard.models import Activity, Bounty, ObjectView, Profile +from dashboard.models import Activity, Bounty, ObjectView, Profile, UserAction from marketing.mails import func_name, grant_update_email, send_mail from proxy.views import proxy_view from retail.emails import render_share_bounty @@ -319,7 +321,6 @@ def increment_view_count(self, pks, content_type, user_id, view_type, retry: boo @app.shared_task(bind=True, max_retries=1) def sync_profile(self, handle, user_pk, hide_profile, retry: bool = True) -> None: from app.utils import actually_sync_profile - from django.contrib.auth.models import User user = User.objects.filter(pk=user_pk).first() if user_pk else None actually_sync_profile(handle, user=user, hide_profile=hide_profile) @@ -330,3 +331,68 @@ def recalculate_earning(self, pk, retry: bool = True) -> None: earning = Earning.objects.get(pk=pk) src = earning.source src.save() + + +@app.shared_task(bind=True, max_retries=1) +def record_visit(self, user_pk, profile_pk, ip_address, visitorId, useragent, referrer, path, session_key, utm, retry: bool = True) -> None: + """ + :param self: Self + :param user_pk: user primary + :param profile_pk: profile primary + :param ip_address: get_ip(request) + :param visitorId: request.COOKIES.get("visitorId", None) + :param useragent: request.META['HTTP_USER_AGENT'] + :param referrer: request.META.get('HTTP_REFERER', None) + :param path: request.META.get('PATH_INFO', None) + :param session_key: request.session._session_key + :param utm: _get_utm_from_cookie(request) + :return: None + """ + + user = User.objects.filter(pk=user_pk).first() if user_pk else None + profile = Profile.objects.filter(pk=profile_pk).first() if profile_pk else None + if user and profile: + try: + profile.last_visit = timezone.now() + profile.save() + except Exception as e: + logger.exception(e) + + # enqueue profile_dict recalc + profile_dict.delay(profile.pk) + + try: + metadata = { + 'visitorId': visitorId, + 'useragent': useragent, + 'referrer': referrer, + 'path': path, + 'session_key': session_key, + } + UserAction.objects.create( + user=user, + profile=profile, + action='Visit', + location_data=get_location_from_ip(ip_address), + ip_address=ip_address, + utm=utm, + metadata=metadata, + ) + except Exception as e: + logger.exception(e) + + +@app.shared_task(bind=True, max_retries=1) +def record_join(self, profile_pk, retry: bool = True) -> None: + """ + :param self: Self + :param profile_pk: profile primary + :return: None + """ + + profile = Profile.objects.filter(pk=profile_pk).first() if profile_pk else None + if profile: + try: + Activity.objects.create(profile=profile, activity_type='joined') + except Exception as e: + logger.exception(e) From 9ed89a49d6b9372b2a9f92e33e4a1e874771df6c Mon Sep 17 00:00:00 2001 From: Chibuotu Amadi Date: Thu, 15 Jul 2021 15:31:10 +0100 Subject: [PATCH 07/10] integration branch (pygithub + tech debt) (#9267) * get_github_primary_email * search_github * clean search * get_issues * get_issue_comments * get_repo * get_organization - members - repos * post/patch issue comment * Revert "post/patch issue comment" This reverts commit 528750526eac7bb7c107694add8a004af5c529eb. * get_notifications * post/patch/delete issue comment * rewrite email utils * redefine get_user + clean usage * get_organization * remove get_github_user_data * get_issue_timeline_events * - drop get_github_user_token - update docs * beginning of the end * dashboard fix * fix grants * fix grants/management * fix marketing * fix retail * fix gas * fix feeswapper * fix economy * fix compliance * fix app/app * fix dashboard/tests + retail emails * fix inbox * fix faucet * fix ptokens * fix perftools * fix perftools and kudos * remove unused utils/helper functions * purge mails * purge nth_day_campaign email * remove signals import * clean dashboard, economy, grants * clean kudos, marketing, retail * clean more dead code * clean dashboard views * bug fix * unused imports * drop day_email_campaign url * fix wrong imports * fix tests * update pydoc * comment failing test * eslint fix * fix eslint * try @gdixon 's fix * clean search templates * remove more templates * finish unused templates * add mission notion imports * GITC-127: Fixes get_github_event_emails() Co-authored-by: Graham Dixon --- app/app/context.py | 1 - app/app/services.py | 2 - app/app/settings.py | 21 - app/app/static_storage.py | 3 - .../search/indexes/dashboard/bounty_text.txt | 2 - .../dashboard/hackathonproject_text.txt | 2 - .../indexes/dashboard/userdirectory_text.txt | 1 - app/app/templates/search/search.html | 173 ---- app/app/thumbnail_processors.py | 27 - app/app/urls.py | 9 - app/app/utils.py | 184 +--- app/avatar/admin.py | 2 - .../management/commands/create_text_inputs.py | 5 +- app/avatar/utils.py | 51 +- app/avatar/views_3d.py | 4 +- app/bounty_requests/views.py | 1 - .../commands/pull_compliance_list.py | 1 - app/dashboard/embed.py | 4 +- app/dashboard/gas_views.py | 5 +- app/dashboard/helpers.py | 44 +- .../management/commands/sync_orgs_repos.py | 114 +-- app/dashboard/models.py | 93 +- app/dashboard/notifications.py | 12 +- app/dashboard/poh_utils.py | 2 - app/dashboard/router.py | 11 - app/dashboard/signals.py | 5 - .../templates/dashboard/explorer.html | 580 ------------- app/dashboard/templates/dashboard/tabs.html | 39 - app/dashboard/templates/notifyfunder.html | 45 - app/dashboard/templates/profiles/none.html | 67 -- .../templates/shared/bounty_nav.html | 53 -- .../templates/shared/github_username.html | 24 - .../templates/shared/issue_deadline.html | 27 - .../templates/shared/notification_email.html | 24 - app/dashboard/templates/shared/pricing.html | 50 -- .../templates/shared/profile_rank_link.html | 0 .../templates/shared/send_tip_nav.html | 37 - .../templates/shared/sidebar_profile.html | 39 - .../templates/shared/theme_switcher.html | 6 - app/dashboard/tests/test_binance_sync.py | 5 +- app/dashboard/tests/test_dashboard_helpers.py | 10 +- app/dashboard/tests/test_dashboard_models.py | 28 +- app/dashboard/tests/test_dashboard_utils.py | 8 +- app/dashboard/utils.py | 64 +- app/dashboard/views.py | 142 +--- app/dataviz/d3_views.py | 131 --- app/economy/tx.py | 128 +-- app/economy/utils.py | 8 - app/faucet/templates/bulk_DM.html | 59 -- .../shared/faucet_no_metamask_error.html | 39 - .../shared/faucet_unlock_metamask.html | 36 - app/faucet/views.py | 1 - .../management/commands/feeSwapper.py | 2 +- app/gas/management/commands/output_gas_viz.py | 2 - app/git/tests/test_utils.py | 319 +++---- app/git/utils.py | 399 +++------ .../management/commands/get_notifications.py | 12 +- app/grants/clr.py | 9 - app/grants/forms.py | 37 - .../management/commands/analytics_clr.py | 5 +- .../commands/build_collection_thumbnails.py | 2 +- .../commands/create_anon_donators.py | 1 - .../management/commands/estimate_clr.py | 5 +- .../management/commands/estimate_clr_delay.py | 6 +- .../management/commands/get_active_clrs.py | 6 +- .../management/commands/grant_related.py | 1 - .../management/commands/grants_upcoming.py | 2 - .../commands/ingest_givingblock_txns.py | 3 - .../management/commands/ingest_grant_txns.py | 4 +- .../management/commands/payout_round.py | 9 +- .../commands/payout_round_noncustodial.py | 13 +- .../management/commands/reclass_grants.py | 2 - .../send_grants_contributions_emails.py | 2 +- .../commands/sync_pending_contributions.py | 3 +- .../commands/update_brightid_status.py | 1 - .../commands/update_idena_status.py | 1 - app/grants/models.py | 22 +- app/grants/router.py | 3 +- app/grants/serializers.py | 2 +- app/grants/sync/celo.py | 4 - app/grants/sync/harmony.py | 7 +- app/grants/sync/helpers.py | 3 +- app/grants/sync/polkadot.py | 4 - app/grants/sync/rsk.py | 7 +- app/grants/sync/zcash.py | 4 - app/grants/sync/zil.py | 1 - app/grants/tasks.py | 6 +- app/grants/templates/grants/card/back.html | 55 -- .../grants/components/hidden_inputs.html | 20 - .../templates/grants/detail/funding.html | 135 --- app/grants/templates/grants/detail/tabs.html | 385 --------- app/grants/templates/grants/heart.html | 804 ------------------ .../grants/shared/active_clr_round_cards.html | 47 - app/grants/utils.py | 13 +- app/grants/views.py | 55 +- app/inbox/signals.py | 1 - app/inbox/utils.py | 16 - app/kudos/helpers.py | 44 - .../process_pending_kudos_distributions.py | 3 - .../commands/re_mint_kudos_on_xdai.py | 5 - app/kudos/models.py | 2 - app/kudos/tasks.py | 1 - app/kudos/templates/shared/kudos_banner.html | 20 - app/kudos/test_models.py | 22 - app/kudos/test_utils.py | 2 +- app/kudos/test_views.py | 5 - app/kudos/utils.py | 60 -- app/kudos/views.py | 38 +- app/marketing/apps.py | 4 - app/marketing/mails.py | 77 +- .../commands/assemble_leaderboards.py | 4 +- .../management/commands/campaign_email.py | 9 - .../commands/expiration_start_work.py | 2 +- .../management/commands/export_graph_edges.py | 1 - .../ingest_community_events_calender.py | 6 +- .../commands/make_everyone_avatars.py | 1 - .../management/commands/make_request_coins.py | 4 - .../management/commands/new_bounties_email.py | 3 - .../management/commands/post_data.py | 1 - .../commands/prune_bad_activities.py | 2 - .../management/commands/pull_github.py | 15 +- .../commands/send_quarterly_stats.py | 2 +- .../management/commands/send_tax_report.py | 5 - .../management/commands/sync_github.py | 17 +- ...c_pull_request_with_bounty_fulfillments.py | 4 +- .../commands/tribe_hackathon_prizes_email.py | 2 - .../commands/unsubscribe_daily_emails.py | 1 - app/marketing/signals.py | 33 - app/marketing/stats.py | 19 +- app/marketing/tasks.py | 3 +- .../commands/test_assemble_leaderboards.py | 8 +- .../test_ingest_community_events_calender.py | 6 +- .../commands/test_new_bounties_email.py | 4 +- ...c_pull_request_with_bounty_fulfillments.py | 5 +- .../tests/test_mail_funder_payout_reminder.py | 2 - app/marketing/tests/test_mailchimp_sync.py | 1 - app/marketing/tests/test_mails.py | 24 +- app/marketing/utils.py | 82 -- app/marketing/views.py | 26 +- app/passport/views.py | 4 +- .../commands/create_activity_cache.py | 3 +- .../management/commands/create_page_cache.py | 1 - app/perftools/views.py | 1 - app/ptokens/helpers.py | 2 +- app/ptokens/templates/shared/ptokens_faq.html | 80 -- app/ptokens/views.py | 26 +- app/retail/emails.py | 24 +- app/retail/management/commands/warm_cache.py | 2 - app/retail/templates/community.html | 40 - app/retail/templates/emails/bounty_small.html | 92 -- .../campaigns/email_campaign_day_1.html | 98 --- .../emails/campaigns/email_campaign_day_1.txt | 19 - .../campaigns/email_campaign_day_2.html | 92 -- .../emails/campaigns/email_campaign_day_2.txt | 14 - .../campaigns/email_campaign_day_3.html | 97 --- .../emails/campaigns/email_campaign_day_3.txt | 16 - .../emails/grants/transaction_summary.html | 147 ---- .../emails/grants/transaction_summary.txt | 31 - .../emails/grants/update_notification.html | 158 ---- .../emails/grants/update_notification.txt | 19 - app/retail/templates/emails/quest_small.html | 111 --- .../emails/shared_activities_actions.html | 31 - app/retail/templates/jobs.html | 174 ---- app/retail/templates/shared/activity-vue.html | 425 --------- app/retail/templates/shared/compass.html | 24 - app/retail/utils.py | 12 - app/retail/views.py | 23 +- app/townsquare/utils.py | 4 - docs/ENVIRONMENT_VARIABLES.md | 1 - pydocmd.yml | 5 - requirements/base.txt | 1 - 171 files changed, 522 insertions(+), 6699 deletions(-) delete mode 100644 app/app/templates/search/indexes/dashboard/bounty_text.txt delete mode 100644 app/app/templates/search/indexes/dashboard/hackathonproject_text.txt delete mode 100644 app/app/templates/search/indexes/dashboard/userdirectory_text.txt delete mode 100644 app/app/templates/search/search.html delete mode 100644 app/app/thumbnail_processors.py delete mode 100644 app/dashboard/templates/dashboard/explorer.html delete mode 100644 app/dashboard/templates/dashboard/tabs.html delete mode 100644 app/dashboard/templates/notifyfunder.html delete mode 100644 app/dashboard/templates/profiles/none.html delete mode 100644 app/dashboard/templates/shared/bounty_nav.html delete mode 100644 app/dashboard/templates/shared/github_username.html delete mode 100644 app/dashboard/templates/shared/issue_deadline.html delete mode 100644 app/dashboard/templates/shared/notification_email.html delete mode 100644 app/dashboard/templates/shared/pricing.html delete mode 100644 app/dashboard/templates/shared/profile_rank_link.html delete mode 100644 app/dashboard/templates/shared/send_tip_nav.html delete mode 100644 app/dashboard/templates/shared/sidebar_profile.html delete mode 100644 app/dashboard/templates/shared/theme_switcher.html delete mode 100644 app/faucet/templates/bulk_DM.html delete mode 100644 app/faucet/templates/shared/faucet_no_metamask_error.html delete mode 100644 app/faucet/templates/shared/faucet_unlock_metamask.html delete mode 100644 app/grants/forms.py delete mode 100644 app/grants/templates/grants/card/back.html delete mode 100644 app/grants/templates/grants/components/hidden_inputs.html delete mode 100644 app/grants/templates/grants/detail/funding.html delete mode 100644 app/grants/templates/grants/detail/tabs.html delete mode 100644 app/grants/templates/grants/heart.html delete mode 100644 app/grants/templates/grants/shared/active_clr_round_cards.html delete mode 100644 app/kudos/templates/shared/kudos_banner.html delete mode 100644 app/kudos/test_models.py delete mode 100644 app/marketing/signals.py delete mode 100644 app/ptokens/templates/shared/ptokens_faq.html delete mode 100644 app/retail/templates/community.html delete mode 100644 app/retail/templates/emails/bounty_small.html delete mode 100644 app/retail/templates/emails/campaigns/email_campaign_day_1.html delete mode 100644 app/retail/templates/emails/campaigns/email_campaign_day_1.txt delete mode 100644 app/retail/templates/emails/campaigns/email_campaign_day_2.html delete mode 100644 app/retail/templates/emails/campaigns/email_campaign_day_2.txt delete mode 100644 app/retail/templates/emails/campaigns/email_campaign_day_3.html delete mode 100644 app/retail/templates/emails/campaigns/email_campaign_day_3.txt delete mode 100644 app/retail/templates/emails/grants/transaction_summary.html delete mode 100644 app/retail/templates/emails/grants/transaction_summary.txt delete mode 100644 app/retail/templates/emails/grants/update_notification.html delete mode 100644 app/retail/templates/emails/grants/update_notification.txt delete mode 100644 app/retail/templates/emails/quest_small.html delete mode 100644 app/retail/templates/emails/shared_activities_actions.html delete mode 100644 app/retail/templates/jobs.html delete mode 100644 app/retail/templates/shared/activity-vue.html delete mode 100644 app/retail/templates/shared/compass.html diff --git a/app/app/context.py b/app/app/context.py index 2fcc3fc3d7b..1fea32c3d54 100644 --- a/app/app/context.py +++ b/app/app/context.py @@ -25,7 +25,6 @@ from django.http import HttpRequest from django.utils import timezone -import requests from app.utils import get_location_from_ip from cacheops import cached_as from dashboard.models import Activity, Tip, UserAction diff --git a/app/app/services.py b/app/app/services.py index fa0675a6299..09de7008bb9 100644 --- a/app/app/services.py +++ b/app/app/services.py @@ -1,5 +1,3 @@ -import random - from django.conf import settings from app.settings import account_sid, auth_token, verify_service diff --git a/app/app/settings.py b/app/app/settings.py index 3db30b55f06..f0e90679cd8 100644 --- a/app/app/settings.py +++ b/app/app/settings.py @@ -20,7 +20,6 @@ import json import os import socket -import subprocess import warnings from django.utils.translation import gettext_noop @@ -29,7 +28,6 @@ import raven import sentry_sdk from boto3.session import Session -from easy_thumbnails.conf import Settings as easy_thumbnails_defaults from sentry_sdk.integrations.celery import CeleryIntegration from sentry_sdk.integrations.django import DjangoIntegration @@ -102,7 +100,6 @@ 'django.contrib.sites', 'autotranslate', 'django_extensions', - 'easy_thumbnails', 'health_check', 'health_check.db', 'health_check.cache', @@ -422,7 +419,6 @@ def callback(request): if ENV in ['prod', 'stage']: DEFAULT_FILE_STORAGE = env('DEFAULT_FILE_STORAGE', default='app.static_storage.MediaFileStorage') - THUMBNAIL_DEFAULT_STORAGE = DEFAULT_FILE_STORAGE STATICFILES_STORAGE = env('STATICFILES_STORAGE', default='app.static_storage.SilentFileStorage') STATIC_HOST = env('STATIC_HOST', default='https://s.gitcoin.co/') STATIC_URL = STATIC_HOST + env('STATIC_URL', default=f'{STATICFILES_LOCATION}{"/" if STATICFILES_LOCATION else ""}') @@ -446,22 +442,6 @@ def callback(request): 'django.contrib.staticfiles.finders.AppDirectoriesFinder' ] -THUMBNAIL_PROCESSORS = easy_thumbnails_defaults.THUMBNAIL_PROCESSORS + ('app.thumbnail_processors.circular_processor',) - -THUMBNAIL_ALIASES = { - '': { - 'graph_node': { - 'size': (30, 30), - 'crop': True - }, - 'graph_node_circular': { - 'size': (30, 30), - 'crop': True, - 'circle': True - } - } -} - CACHEOPS_DEGRADE_ON_FAILURE = env.bool('CACHEOPS_DEGRADE_ON_FAILURE', default=True) CACHEOPS_REDIS = env.str('CACHEOPS_REDIS', default='redis://redis:6379/0') @@ -617,7 +597,6 @@ def callback(request): # Github GITHUB_API_BASE_URL = env('GITHUB_API_BASE_URL', default='https://api.github.com') GITHUB_AUTH_BASE_URL = env('GITHUB_AUTH_BASE_URL', default='https://github.com/login/oauth/authorize') -GITHUB_TOKEN_URL = env('GITHUB_TOKEN_URL', default='https://github.com/login/oauth/access_token') GITHUB_SCOPE = env('GITHUB_SCOPE', default='read:user,user:email') GITHUB_CLIENT_ID = env('GITHUB_CLIENT_ID', default='') # TODO GITHUB_CLIENT_SECRET = env('GITHUB_CLIENT_SECRET', default='') # TODO diff --git a/app/app/static_storage.py b/app/app/static_storage.py index 28693fe83cc..fb0e6b32b2c 100644 --- a/app/app/static_storage.py +++ b/app/app/static_storage.py @@ -1,9 +1,6 @@ # -*- coding: utf-8 -*- """Define the custom static storage to surpress bad URL references.""" -import os from datetime import datetime -from os.path import basename -from secrets import token_hex from django.conf import settings diff --git a/app/app/templates/search/indexes/dashboard/bounty_text.txt b/app/app/templates/search/indexes/dashboard/bounty_text.txt deleted file mode 100644 index e21f52a6d2d..00000000000 --- a/app/app/templates/search/indexes/dashboard/bounty_text.txt +++ /dev/null @@ -1,2 +0,0 @@ -{{ object.title }} - diff --git a/app/app/templates/search/indexes/dashboard/hackathonproject_text.txt b/app/app/templates/search/indexes/dashboard/hackathonproject_text.txt deleted file mode 100644 index 37c5aa0fa10..00000000000 --- a/app/app/templates/search/indexes/dashboard/hackathonproject_text.txt +++ /dev/null @@ -1,2 +0,0 @@ -{{ object.title }} -{{ object.description }} diff --git a/app/app/templates/search/indexes/dashboard/userdirectory_text.txt b/app/app/templates/search/indexes/dashboard/userdirectory_text.txt deleted file mode 100644 index 1fe45381576..00000000000 --- a/app/app/templates/search/indexes/dashboard/userdirectory_text.txt +++ /dev/null @@ -1 +0,0 @@ -{{ object.object }} diff --git a/app/app/templates/search/search.html b/app/app/templates/search/search.html deleted file mode 100644 index afe6411926c..00000000000 --- a/app/app/templates/search/search.html +++ /dev/null @@ -1,173 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 static email_obfuscator add_url_schema avatar_tags bundle %} - - - - - {% include 'shared/head.html' %} - {% include 'shared/cards.html' %} - {% bundle css file search %} - - - - - {% endbundle %} - - - -
- {% include 'shared/tag_manager_2.html' %} -
- {% include 'shared/top_nav.html' with class='d-md-flex' %} - {% include 'home/nav.html' %} -
- {% block content %} -

Search

- -
- - {{ form.as_table }} - - - - -
  - -
- - {% if query %} -

Results

- - {% for result in page.object_list %} -

- {{ result.object.title }} -

- {% empty %} -

No results found.

- {% endfor %} - - {% if page.has_previous or page.has_next %} -
- {% if page.has_previous %}{% endif %}« Previous{% if page.has_previous %}{% endif %} - | - {% if page.has_next %}{% endif %}Next »{% if page.has_next %}{% endif %} -
- {% endif %} - {% else %} - {# Show some example queries to run, maybe query syntax, something else? #} - {% endif %} -
- {% endblock %} -
- -
-

Hacky User Directory

- -
- -
- - - - - - - -
- -
-
-
- - - -
-
-
-
- - - - - - - - - -
- - -
- - - - - -
-
-
-
-
- - - - {% csrf_token %} - {% include 'shared/footer_scripts.html' with slim=1 %} - {% include 'shared/footer.html' %} -
- - - - - - - diff --git a/app/app/thumbnail_processors.py b/app/app/thumbnail_processors.py deleted file mode 100644 index c427655aabe..00000000000 --- a/app/app/thumbnail_processors.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -"""Define the EthOS thumbnail processors. - -Copyright (C) 2021 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 . - -""" - - -def circular_processor(image, circle=False, **kwargs): - """Force the image to a circle.""" - from .utils import get_circular_image - if circle: - image = get_circular_image(image) - return image diff --git a/app/app/urls.py b/app/app/urls.py index 8584b4a3f90..e1f859f6c09 100644 --- a/app/app/urls.py +++ b/app/app/urls.py @@ -606,10 +606,6 @@ url(r'^l/(.*)$/?', linkshortener.views.linkredirect, name='redirect'), url(r'^credit/(.*)$/?', credits.views.credits, name='credit'), - # token distribution event - # re_path(r'^whitepaper/accesscode/?', tdi.views.whitepaper_access, name='whitepaper_access'), - # re_path(r'^whitepaper/?', tdi.views.whitepaper_new, name='whitepaper'), - # faucet views re_path(r'^faucet/?', faucet.views.faucet, name='faucet'), @@ -722,11 +718,6 @@ retail.emails.tribe_hackathon_prizes, name='tribe_hackathon_prizes' ), - path( - '_administration/email/day_email_campaign/', - marketing.views.day_email_campaign, - name='day_email_campaign' - ), re_path( r'^_administration/process_accesscode_request/(.*)$', tdi.views.process_accesscode_request, diff --git a/app/app/utils.py b/app/app/utils.py index 45f4cdc6049..60351e33a51 100644 --- a/app/app/utils.py +++ b/app/app/utils.py @@ -21,12 +21,11 @@ import geoip2.database import requests from avatar.models import SocialAvatar -from avatar.utils import get_svg_templates, get_user_github_avatar_image +from avatar.utils import get_user_github_avatar_image from geoip2.errors import AddressNotFoundError -from git.utils import _AUTH, HEADERS, get_user +from git.utils import get_user from ipware.ip import get_real_ip from marketing.utils import get_or_save_email_subscriber -from pyshorteners import Shortener from social_core.backends.github import GithubOAuth2 from social_django.models import UserSocialAuth @@ -47,115 +46,10 @@ def as_sql(self, compiler, connection): return f'{lhs} <> {rhs}', params -def get_query_cache_key(compiler): - """Generate a cache key from a SQLCompiler. - - This cache key is specific to the SQL query and its context - (which database is used). The same query in the same context - (= the same database) must generate the same cache key. - - Args: - compiler (django.db.models.sql.compiler.SQLCompiler): A SQLCompiler - that will generate the SQL query. - - Returns: - int: The cache key. - - """ - sql, params = compiler.as_sql() - cache_key = f'{compiler.using}:{sql}:{[str(p) for p in params]}' - return sha1(cache_key.encode('utf-8')).hexdigest() - - -def get_table_cache_key(db_alias, table): - """Generates a cache key from a SQL table. - - Args: - db_alias (str): The alias of the used database. - table (str): The name of the SQL table. - - Returns: - int: The cache key. - - """ - cache_key = f'{db_alias}:{table}' - return sha1(cache_key.encode('utf-8')).hexdigest() - - -def get_raw_cache_client(backend='default'): - """Get a raw Redis cache client connection. - - Args: - backend (str): The backend to attempt connection against. - - Raises: - Exception: The exception is raised/caught if any generic exception - is encountered during the connection attempt. - - Returns: - redis.client.StrictRedis: The raw Redis client connection. - If an exception is encountered, return None. - - """ - from django_redis import get_redis_connection - try: - return get_redis_connection(backend) - except Exception as e: - logger.error(e) - return None - - -def get_short_url(url): - is_short = False - for shortener in ['Tinyurl', 'Adfly', 'Isgd', 'QrCx']: - try: - if not is_short: - shortener = Shortener(shortener) - response = shortener.short(url) - if response != 'Error' and 'http' in response: - url = response - is_short = True - except Exception: - pass - return url - - def ellipses(data, _len=75): return (data[:_len] + '..') if len(data) > _len else data -def add_contributors(repo_data): - """Add contributor data to repository data dictionary. - - Args: - repo_data (dict): The repository data dictionary to be updated. - - Returns: - dict: The updated repository data dictionary. - - """ - if repo_data.get('fork', False): - return repo_data - - params = {} - url = repo_data['contributors_url'] - response = requests.get(url, auth=_AUTH, headers=HEADERS, params=params) - - if response.status_code in [204, 404] : # no content - return repo_data - - response_data = response.json() - rate_limited = (isinstance(response_data, dict) and 'documentation_url' in response_data.keys()) - if rate_limited: - # retry after rate limit - time.sleep(60) - return add_contributors(repo_data) - - # no need for retry - repo_data['contributors'] = response_data - return repo_data - - def setup_lang(request, user): """Handle setting the user's language preferences and store in the session. @@ -204,28 +98,28 @@ def sync_profile(handle, user=None, hide_profile=True, delay_okay=False): def actually_sync_profile(handle, user=None, hide_profile=True): from dashboard.models import Profile handle = handle.strip().replace('@', '').lower() - # data = get_user(handle, scoped=True) if user and hasattr(user, 'profile'): try: access_token = user.social_auth.filter(provider='github').latest('pk').access_token - data = get_user(handle, '', scoped=True, auth=(handle, access_token)) + data = get_user(handle, token=access_token) user = User.objects.get(username__iexact=handle) - if 'login' in data: + if data and data.login: profile = user.profile - user.username = data['login'] + user.username = data.login user.save() - profile.handle = data['login'] + profile.handle = data.login profile.email = user.email profile.save() - except UserSocialAuth.DoesNotExist: - pass + except Exception as e: + logger.error(e) + return None else: data = get_user(handle) email = '' - is_error = 'name' not in data.keys() + is_error = not hasattr(data, 'name') if is_error: print("- error main") logger.warning(f'Failed to fetch github username {handle}', exc_info=True, extra={'handle': handle}) @@ -250,17 +144,16 @@ def actually_sync_profile(handle, user=None, hide_profile=True): profile, created = Profile.objects.update_or_create(handle=handle, defaults=defaults) latest_obj = profile.user.social_auth.filter(provider='github').latest('pk') if profile.user else None access_token = latest_obj.access_token if latest_obj else None - orgs = get_user(handle, '/orgs', auth=(profile.handle, access_token)) if access_token else [] - profile.organizations = [ele['login'] for ele in orgs if ele and type(ele) is dict] if orgs else [] + orgs = get_user(handle, token=access_token).get_orgs() + profile.organizations = [ele.login for ele in orgs if ele] if orgs else [] print("Profile:", profile, "- created" if created else "- updated") keywords = [] - for repo in profile.repos_data_lite: - if type(repo) == dict: - language = repo.get('language') if repo.get('language') else '' - _keywords = language.split(',') - for key in _keywords: - if key != '' and key not in keywords: - keywords.append(key) + for repo in get_user(handle).get_repos(): + language = repo.language or '' + _keywords = language.split(',') + for key in _keywords: + if key != '' and key not in keywords: + keywords.append(key) profile.keywords = keywords profile.save() @@ -333,47 +226,6 @@ def fetch_mails_since_id(email_id, password, since_id=None, host='imap.gmail.com return emails -def itermerge(gen_a, gen_b, key): - a = None - b = None - - # yield items in order until first iterator is emptied - try: - while True: - if a is None: - a = gen_a.next() - - if b is None: - b = gen_b.next() - - if key(a) <= key(b): - yield a - a = None - else: - yield b - b = None - except StopIteration: - # yield last item to be pulled from non-empty iterator - if a is not None: - yield a - - if b is not None: - yield b - - # flush remaining items in non-empty iterator - try: - for a in gen_a: - yield a - except StopIteration: - pass - - try: - for b in gen_b: - yield b - except StopIteration: - pass - - def handle_location_request(request): """Handle determining location data from request IP.""" ip_address = '24.210.224.38' if settings.DEBUG else get_real_ip(request) diff --git a/app/avatar/admin.py b/app/avatar/admin.py index d20658ec5e0..64ab713918a 100644 --- a/app/avatar/admin.py +++ b/app/avatar/admin.py @@ -17,8 +17,6 @@ along with this program. If not, see . """ -import json - from django.contrib import admin from django.utils.safestring import mark_safe diff --git a/app/avatar/management/commands/create_text_inputs.py b/app/avatar/management/commands/create_text_inputs.py index 6dc405f69b9..33a76bc2be6 100644 --- a/app/avatar/management/commands/create_text_inputs.py +++ b/app/avatar/management/commands/create_text_inputs.py @@ -16,7 +16,6 @@ ''' -import json import time import uuid @@ -26,9 +25,9 @@ from django.utils import timezone from avatar.models import AvatarTextOverlayInput -from dashboard.utils import get_tx_status, get_web3, has_tx_mined +from dashboard.utils import get_web3, has_tx_mined from gas.utils import recommend_min_gas_price_to_confirm_in_time -from web3 import HTTPProvider, Web3 +from web3 import Web3 class Command(BaseCommand): diff --git a/app/avatar/utils.py b/app/avatar/utils.py index 4d77eab50da..280f22623af 100644 --- a/app/avatar/utils.py +++ b/app/avatar/utils.py @@ -31,7 +31,7 @@ import pyvips import requests -from git.utils import get_organization, get_user +from git.utils import get_user, github_connect from PIL import Image, ImageOps from pyvips.error import Error as VipsError from svgutils import transform @@ -226,44 +226,6 @@ def get_upload_filename(instance, filename): return f"avatars/{getattr(instance, '_path', '')}/{salt}/{file_path}" -def get_svg_templates(): - """Get the SVG templates for all avatar categories.""" - template_data = { - 'accessories': { - 'earring': [], - 'glasses': [], - 'hat': [], - 'masks': [], - 'extras': [], - }, - 'clothing': [], - 'ears': [], - 'eyes': [], - 'facial_hair': { - 'beard': [], - 'mustache': [] - }, - 'hair': [], - 'head': [], - 'makeup': [], - 'mouth': [], - 'nose': [], - 'wallpaper': [] - } - - for category in template_data: - path = f'avatar/templates/{category}' - template_list = os.listdir(path) - - if isinstance(template_data[category], dict): - for item in template_data[category]: - inner_path = f'{path}/{item}' - template_data[category][item] = os.listdir(inner_path) - else: - template_data[category] = template_list - return template_data - - def get_svg_template(category, item, primary_color, secondary_color=''): context = {'primary_color': primary_color} if secondary_color: @@ -509,9 +471,9 @@ def get_avatar(_org_name): avatar = Image.open(filepath, 'r').convert("RGBA") except (IOError, FileNotFoundError): remote_user = get_user(_org_name) - if not remote_user.get('avatar_url', False): + if not hasattr(remote_user, 'avatar_url'): return JsonResponse({'msg': 'invalid user'}, status=422) - remote_avatar_url = remote_user['avatar_url'] + remote_avatar_url = remote_user.avatar_url r = requests.get(remote_avatar_url, stream=True) chunk_size = 20000 @@ -570,12 +532,9 @@ def get_err_response(request, blank_img=False): def get_user_github_avatar_image(handle): remote_user = get_user(handle) - avatar_url = remote_user.get('avatar_url') + avatar_url = remote_user.avatar_url if hasattr(remote_user, 'avatar_url') else None if not avatar_url: - remote_org = get_organization(handle) - avatar_url = remote_org.get('avatar_url') - if not avatar_url: - return None + return None from .models import BaseAvatar temp_avatar = get_github_avatar_image(avatar_url, BaseAvatar.ICON_SIZE) if not temp_avatar: diff --git a/app/avatar/views_3d.py b/app/avatar/views_3d.py index 635e3618bb1..d74565ae8cf 100644 --- a/app/avatar/views_3d.py +++ b/app/avatar/views_3d.py @@ -17,7 +17,6 @@ along with this program. If not, see . """ -import json import logging import xml.etree.ElementTree as ET @@ -28,9 +27,8 @@ from avatar.helpers import add_rgb_array, hex_to_rgb_array, rgb_array_to_hex, sub_rgb_array from dashboard.utils import create_user_action -from PIL import Image, ImageOps -from .models import AvatarTextOverlayInput, BaseAvatar, CustomAvatar, SocialAvatar +from .models import AvatarTextOverlayInput, CustomAvatar logger = logging.getLogger(__name__) diff --git a/app/bounty_requests/views.py b/app/bounty_requests/views.py index 49d8c48d15e..28a5d28d69c 100644 --- a/app/bounty_requests/views.py +++ b/app/bounty_requests/views.py @@ -19,7 +19,6 @@ """ import json -from django.core.exceptions import MultipleObjectsReturned from django.http import JsonResponse from django.template.response import TemplateResponse from django.utils.translation import gettext_lazy as _ diff --git a/app/compliance/management/commands/pull_compliance_list.py b/app/compliance/management/commands/pull_compliance_list.py index 4c4ec950fa9..99d74e19cda 100644 --- a/app/compliance/management/commands/pull_compliance_list.py +++ b/app/compliance/management/commands/pull_compliance_list.py @@ -17,7 +17,6 @@ ''' import urllib.request -import xml.etree as etree import xml.etree.ElementTree as ET from django.core.management.base import BaseCommand diff --git a/app/dashboard/embed.py b/app/dashboard/embed.py index 1a4508a7e26..31be16a7ba4 100644 --- a/app/dashboard/embed.py +++ b/app/dashboard/embed.py @@ -133,9 +133,9 @@ def embed(request): avatar = Image.open(filepath, 'r').convert("RGBA") except IOError: remote_user = get_user(_org_name) - if not remote_user.get('avatar_url', False): + if not hasattr(remote_user, 'avatar_url'): return JsonResponse({'msg': 'invalid user'}, status=422) - remote_avatar_url = remote_user['avatar_url'] + remote_avatar_url = remote_user.avatar_url r = requests.get(remote_avatar_url, stream=True) chunk_size = 20000 diff --git a/app/dashboard/gas_views.py b/app/dashboard/gas_views.py index 357d7f8ac7f..d2618ec895c 100644 --- a/app/dashboard/gas_views.py +++ b/app/dashboard/gas_views.py @@ -24,14 +24,11 @@ from django.utils import timezone from django.utils.translation import gettext_lazy as _ -from cacheops import CacheMiss, cache, cached_view, cached_view_as from economy.utils import convert_amount from gas.models import GasGuzzler -from gas.utils import conf_time_spread, gas_advisories, gas_history, recommend_min_gas_price_to_confirm_in_time +from gas.utils import conf_time_spread, gas_advisories, recommend_min_gas_price_to_confirm_in_time from perftools.models import JSONStore -from .helpers import handle_bounty_views - logging.basicConfig(level=logging.DEBUG) confirm_time_minutes_target = 4 diff --git a/app/dashboard/helpers.py b/app/dashboard/helpers.py index d9419c7dcec..b741ece2a0b 100644 --- a/app/dashboard/helpers.py +++ b/app/dashboard/helpers.py @@ -21,10 +21,8 @@ import os import pprint from decimal import Decimal -from enum import Enum from django.conf import settings -from django.conf.urls.static import static from django.core.exceptions import MultipleObjectsReturned, ValidationError from django.core.validators import URLValidator from django.db import transaction @@ -42,8 +40,8 @@ notify_of_lowball_bounty, ) from dashboard.tokens import addr_to_token -from economy.utils import ConversionRateNotFoundError, convert_amount -from git.utils import get_gh_issue_details, get_url_dict, org_name +from economy.utils import convert_amount +from git.utils import get_issue_details, get_url_dict, org_name from jsondiff import diff from marketing.mails import new_reserved_issue from pytz import UTC @@ -188,7 +186,6 @@ def issue_details(request): duplicates = request.GET.get('duplicates', False) network = request.GET.get('network', 'mainnet') - if hackathon_slug: sponsor_profiles = HackathonEvent.objects.filter(slug__iexact=hackathon_slug).prefetch_related('sponsor_profiles').values_list('sponsor_profiles__handle', flat=True) org_issue = org_name(url).lower() @@ -220,7 +217,7 @@ def issue_details(request): try: if url_dict: - response = get_gh_issue_details(token=token, **url_dict) + response = get_issue_details(token=token, **url_dict) else: response['message'] = 'could not parse Github url' except Exception as e: @@ -245,39 +242,6 @@ def normalize_url(url): return url -def sync_bounty_with_web3(bounty_contract, url): - """Sync the Bounty with Web3. - - Args: - bounty_contract (Web3): The Web3 contract instance. - url (str): The bounty URL. - - Returns: - tuple: A tuple of bounty change data. - tuple[0] (bool): Whether or not the Bounty changed. - tuple[1] (dashboard.models.Bounty): The first old bounty object. - tuple[2] (dashboard.models.Bounty): The new Bounty object. - - """ - bountydetails = bounty_contract.call().bountydetails(url) - return process_bounty_details(bountydetails) - - -class BountyStage(Enum): - """Python enum class that matches up with the Standard Bounties BountyStage enum. - - Attributes: - Draft (int): Bounty is a draft. - Active (int): Bounty is active. - Dead (int): Bounty is dead. - - """ - - Draft = 0 - Active = 1 - Dead = 2 - - class UnsupportedSchemaException(Exception): """Define unsupported schema exception handling.""" @@ -601,7 +565,7 @@ def merge_bounty(latest_old_bounty, new_bounty, metadata, bounty_details, verbos new_bounty.fetch_issue_item() try: issue_kwargs = get_url_dict(new_bounty.github_url) - new_bounty.github_issue_details = get_gh_issue_details(**issue_kwargs) + new_bounty.github_issue_details = get_issue_details(**issue_kwargs) except Exception as e: logger.error(e) diff --git a/app/dashboard/management/commands/sync_orgs_repos.py b/app/dashboard/management/commands/sync_orgs_repos.py index 7ba1c71eefc..efc59a24c3b 100644 --- a/app/dashboard/management/commands/sync_orgs_repos.py +++ b/app/dashboard/management/commands/sync_orgs_repos.py @@ -3,7 +3,7 @@ from app.utils import sync_profile from dashboard.models import Organization, Profile, Repo -from git.utils import get_organization, get_repo, get_user +from git.utils import get_organization, get_user, github_connect class Command(BaseCommand): @@ -30,16 +30,18 @@ def recursive_sync(lsynced, handle): profile.user.groups.filter(name__contains=y.name).delete() print(f'Removing: {profile.handle} from Organization: {y.name} ') - user_access_repos = get_repo(handle, '/repos', (handle, access_token), is_user=True) - # Question around user repo acccess if we can't get user repos, should we assume all repos are no longer available in the platform? - - if 'message' in user_access_repos: - print(user_access_repos['message']) + try: + gh_client = github_connect(access_token) + user_access_repos = gh_client.get_user().get_repos() + if user_access_repos.totalCount: pass # trigger error throw if any + # Question around user repo access if we can't get user repos, should we assume all repos are no longer available in the platform? + except Exception as e: + print(e) return lsynced current_user_repos = [] for y in user_access_repos: - current_user_repos.append(y['name']) + current_user_repos.append(y.name) remove_user_repos_names = [x for x in profile.repos.all() if x.name not in current_user_repos] @@ -67,73 +69,77 @@ def recursive_sync(lsynced, handle): print(f'Syncing Organization: {db_org.name}') profile.profile_organizations.add(db_org) - org_members = get_organization(db_org.name, '/members', (handle, access_token)) - if 'message' in org_members: - print(org_members['message']) + try: + gh_client = github_connect() + org_members = gh_client.get_organization(db_org.name).get_members() + if org_members.totalCount: pass # trigger error throw if any + except Exception as e: + print(e) continue for member in org_members: try: - membership = get_organization( - db_org.name, f'/memberships/{member["login"]}', (handle, access_token) - ) - if 'message' in membership: - print(membership['message']) - continue - role = membership['role'] if 'role' in membership else "member" + membership = get_user(handle).get_organization_membership(db_org.name) + role = membership.role if hasattr(membership, 'role') else "member" db_group = Group.objects.get_or_create(name=f'{db_org.name}-role-{role}')[0] db_org.groups.add(db_group) - member_profile_obj = Profile.objects.get(handle=member['login'], user__is_active=True) + member_profile_obj = Profile.objects.get(handle=member.login, user__is_active=True) member_profile_obj.user.groups.add(db_group) - members_to_sync.append(member['login']) + members_to_sync.append(member.login) except Exception as e: - # print(f'An exception happened in the Organization Loop: handle {member["login"]} {e}') + print(e) continue - org_repos = get_organization(db_org.name, '/repos', (handle, access_token)) - - if 'message' in org_repos: - print(org_repos['message']) + try: + gh_client = github_connect(access_token) + org_repos = gh_client.get_organization(db_org.name).get_repos() + if org_repos.totalCount: pass # trigger error throw if any + except Exception as e: + print(e) continue for repo in org_repos: - db_repo = Repo.objects.get_or_create(name=repo['name'])[0] + db_repo = Repo.objects.get_or_create(name=repo.name)[0] db_org.repos.add(db_repo) print(f'Syncing Repo: {db_repo.name}') - repo_collabs = get_repo(repo['full_name'], '/collaborators', (handle, access_token)) - if 'message' in repo_collabs: - print(repo_collabs['message']) + try: + gh_client = github_connect(access_token) + repo_collabs = gh_client.get_repo(repo.full_name).get_collaborators() + if repo_collabs.totalCount: pass # trigger error throw if any + except Exception as e: + print(e) continue for collaborator in repo_collabs: - - if collaborator['permissions']['admin']: - permission = "admin" - elif collaborator['permissions']['push']: - permission = "write" - elif collaborator['permissions']['pull']: - permission = "pull" - else: - permission = "none" - - db_group = Group.objects.get_or_create( - name=f'{db_org.name}-repo-{repo["name"]}-{permission}' - )[0] - db_org.groups.add(db_group) - - try: - member_user_profile = Profile.objects.get( - handle=collaborator['login'], user__is_active=True - ) - member_user_profile.user.groups.add(db_group) - member_user_profile.repos.add(db_repo) - if collaborator['login'] not in members_to_sync or \ - collaborator['login'] not in lsynced: - members_to_sync.append(collaborator['login']) - except Exception as e: - continue + + if collaborator.permissions: + if collaborator.permissions.admin: + permission = "admin" + elif collaborator.permissions.push: + permission = "write" + elif collaborator.permissions.pull: + permission = "pull" + else: + permission = "none" + + db_group = Group.objects.get_or_create( + name=f'{db_org.name}-repo-{repo["name"]}-{permission}' + )[0] + db_org.groups.add(db_group) + + try: + member_user_profile = Profile.objects.get( + handle=collaborator.login, user__is_active=True + ) + member_user_profile.user.groups.add(db_group) + member_user_profile.repos.add(db_repo) + if collaborator.login not in members_to_sync or \ + collaborator.login not in lsynced: + members_to_sync.append(collaborator.login) + except Exception as e: + continue for x in members_to_sync: try: diff --git a/app/dashboard/models.py b/app/dashboard/models.py index a345fce03f2..43db3d50e71 100644 --- a/app/dashboard/models.py +++ b/app/dashboard/models.py @@ -57,22 +57,17 @@ from app.utils import get_upload_filename, timeout from avatar.models import SocialAvatar from avatar.utils import get_user_github_avatar_image -from bleach import clean from bounty_requests.models import BountyRequest from bs4 import BeautifulSoup -from dashboard.idena_utils import get_idena_status, next_validation_time +from dashboard.idena_utils import get_idena_status from dashboard.tokens import addr_to_token, token_by_name -from economy.models import ConversionRate, EncodeAnything, SuperModel, get_0_time, get_time +from economy.models import ConversionRate, SuperModel, get_0_time from economy.utils import ConversionRateNotFoundError, convert_amount, convert_token_to_usdt -from gas.utils import recommend_min_gas_price_to_confirm_in_time -from git.utils import ( - _AUTH, HEADERS, TOKEN_URL, build_auth_dict, get_gh_issue_details, get_issue_comments, issue_number, org_name, - repo_name, -) -from marketing.mails import featured_funded_bounty, fund_request_email, start_work_approved +from git.utils import get_issue_comments, get_issue_details, issue_number, org_name, repo_name +from marketing.mails import fund_request_email, start_work_approved from marketing.models import EmailSupressionList, LeaderboardRank from rest_framework import serializers -from townsquare.models import Offer, PinnedPost +from townsquare.models import PinnedPost from unidecode import unidecode from web3 import Web3 @@ -636,7 +631,7 @@ def can_submit_after_expiration_date(self): def title_or_desc(self): """Return the title of the issue.""" if not self.title: - title = self.fetch_issue_item('title') or self.github_url + title = self.fetch_issue_item('title') return title return self.title @@ -1008,11 +1003,14 @@ def fetch_issue_item(self, item_type='body'): str: The item content. """ - github_url = self.get_github_api_url() - if github_url: - issue_description = requests.get(github_url, auth=_AUTH) - if issue_description.status_code == 200: - item = issue_description.json().get(item_type, '') + if self.github_url.lower()[:19] == 'https://github.com/': + _org_name = org_name(self.github_url) + _repo_name = repo_name(self.github_url) + _issue_num = issue_number(self.github_url) + gh_issue_details = get_issue_details(_org_name, _repo_name, int(_issue_num)) + + if gh_issue_details: + item = gh_issue_details.get(item_type, '') if item_type == 'body' and item: self.issue_description = item elif item_type == 'title' and item: @@ -1046,11 +1044,11 @@ def fetch_issue_comments(self, save=True): return [] comment_count = 0 for comment in comments: - if (isinstance(comment, dict) and comment.get('user', {}).get('login', '') not in settings.IGNORE_COMMENTS_FROM): + if comment.user and comment.user.login not in settings.IGNORE_COMMENTS_FROM: comment_count += 1 self.github_comments = comment_count if comment_count: - comment_times = [datetime.strptime(comment['created_at'], '%Y-%m-%dT%H:%M:%SZ') for comment in comments] + comment_times = [comment.created_at.strftime('%Y-%m-%dT%H:%M:%SZ') for comment in comments] max_comment_time = max(comment_times) max_comment_time = max_comment_time.replace(tzinfo=pytz.utc) self.last_comment_date = max_comment_time @@ -1146,7 +1144,7 @@ def github_issue_state(self): _org_name = org_name(self.github_url) _repo_name = repo_name(self.github_url) _issue_num = issue_number(self.github_url) - gh_issue_details = get_gh_issue_details(_org_name, _repo_name, int(_issue_num)) + gh_issue_details = get_issue_details(_org_name, _repo_name, int(_issue_num)) if gh_issue_details: self.github_issue_details = gh_issue_details self.save() @@ -1389,17 +1387,6 @@ class BountyEvent(SuperModel): metadata = JSONField(default=dict, blank=True) -class BountyFulfillmentQuerySet(models.QuerySet): - """Handle the manager queryset for BountyFulfillments.""" - - def accepted(self): - """Filter results to accepted bounty fulfillments.""" - return self.filter(accepted=True) - - def submitted(self): - """Exclude results that have not been submitted.""" - return self.exclude(fulfiller_address='0x0000000000000000000000000000000000000000') - class BountyFulfillment(SuperModel): """The structure of a fulfillment on a Bounty.""" @@ -1941,11 +1928,6 @@ def receive_url_for_recipient(self): return '' -class TipPayoutException(Exception): - pass - - - class TipPayout(SuperModel): """Model representing redemption of a Kudos @@ -3545,21 +3527,6 @@ def github_created_on(self): created_on = datetime.strptime(created_at, '%Y-%m-%dT%H:%M:%SZ') return created_on.replace(tzinfo=pytz.UTC) - @property - def repos_data_lite(self): - from git.utils import get_user - # TODO: maybe rewrite this so it doesnt have to go to the internet to get the info - # but in a way that is respectful of db size too - return get_user(self.handle, '/repos') - - @property - def repos_data(self): - from app.utils import add_contributors - repos_data = self.repos_data_lite - repos_data = sorted(repos_data, key=lambda repo: repo['stargazers_count'], reverse=True) - repos_data = [add_contributors(repo_data) for repo_data in repos_data] - return repos_data - @property def is_moderator(self): """Determine whether or not the user is a moderator. @@ -4057,32 +4024,6 @@ def name(self): return self.data["name"] return self.username - - def is_github_token_valid(self): - """Check whether or not a Github OAuth token is valid. - - Args: - access_token (str): The Github OAuth token. - - Returns: - bool: Whether or not the provided OAuth token is valid. - - """ - if not self.github_access_token: - return False - - _params = build_auth_dict() - url = TOKEN_URL.format(**_params) - response = requests.get( - url, - data=json.dumps({'access_token': self.github_access_token}), - auth=(_params['client_id'], _params['client_secret']), - headers=HEADERS) - - if response.status_code == 200: - return True - return False - def __str__(self): return self.handle diff --git a/app/dashboard/notifications.py b/app/dashboard/notifications.py index 101b8c2092b..2fc612758c8 100644 --- a/app/dashboard/notifications.py +++ b/app/dashboard/notifications.py @@ -24,12 +24,10 @@ from django.conf import settings from django.contrib.humanize.templatetags.humanize import naturaltime -from django.templatetags.static import static -from django.utils import timezone, translation +from django.utils import translation from django.utils.translation import gettext from django.utils.translation import gettext_lazy as _ -import requests import twitter from economy.utils import convert_token_to_usdt from git.utils import delete_issue_comment, org_name, patch_issue_comment, post_issue_comment, repo_name @@ -469,7 +467,7 @@ def build_github_notification(bounty, event_name, profile_pairs=None): """ from dashboard.utils import get_ordinal_repr # hack for circular import issue - from dashboard.models import BountyFulfillment, Interest + from dashboard.models import BountyFulfillment msg = '' usdt_value = "" try: @@ -633,11 +631,11 @@ def maybe_market_to_github(bounty, event_name, profile_pairs=None): patch_issue_comment(comment_id, username, repo, msg) else: response = post_issue_comment(username, repo, issue_num, msg) - if response.get('id'): + if response.id: if event_name == 'work_started': - bounty.interested_comment = int(response.get('id')) + bounty.interested_comment = int(response.id) elif event_name in ['work_done', 'work_submitted']: - bounty.submissions_comment = int(response.get('id')) + bounty.submissions_comment = int(response.id) bounty.save() # Handle deleting comments if no profiles are provided. elif event_name in ['work_started'] and not profile_pairs: diff --git a/app/dashboard/poh_utils.py b/app/dashboard/poh_utils.py index 036f39f5c4d..01a398a33cc 100644 --- a/app/dashboard/poh_utils.py +++ b/app/dashboard/poh_utils.py @@ -1,5 +1,3 @@ -from django.conf import settings - POH_CONTRACT_ADDRESS = '0xC5E9dDebb09Cd64DfaCab4011A0D5cEDaf7c9BDb' _poh_contract = None diff --git a/app/dashboard/router.py b/app/dashboard/router.py index 468696bf30a..b3fd78fd087 100644 --- a/app/dashboard/router.py +++ b/app/dashboard/router.py @@ -20,7 +20,6 @@ import logging import time from datetime import datetime -from functools import reduce from django.db.models import Count, F, Q @@ -222,16 +221,6 @@ def get_comments(self, obj): class HackathonProjectsPagination(PageNumberPagination): page_size = 10 -class UserDirectorySerializer(serializers.ModelSerializer): - - class Meta: - model = UserDirectory - fields = '__all__' - depth = 1 - -class UserDirectoryPagination(PageNumberPagination): - page_size = 20 - class HackathonProjectsViewSet(viewsets.ModelViewSet): queryset = HackathonProject.objects.prefetch_related('bounty', 'profiles').all().order_by('id') diff --git a/app/dashboard/signals.py b/app/dashboard/signals.py index 85b96355eba..f332e271610 100644 --- a/app/dashboard/signals.py +++ b/app/dashboard/signals.py @@ -19,11 +19,7 @@ """ import logging -from django.db.models.signals import post_save -from django.dispatch import receiver - from corsheaders.signals import check_request_enabled -from git.utils import get_gh_issue_details, get_url_dict, issue_number, org_name, repo_name from .notifications import maybe_market_to_github @@ -38,7 +34,6 @@ def m2m_changed_interested(sender, instance, action, reverse, model, **kwargs): m2m_changed_interested.delay(instance.pk) - def changed_fulfillments(sender, instance, action, reverse, model, **kwargs): """Handle changes to Bounty fulfillments.""" event_name = 'work_submitted' diff --git a/app/dashboard/templates/dashboard/explorer.html b/app/dashboard/templates/dashboard/explorer.html deleted file mode 100644 index 657db8b229b..00000000000 --- a/app/dashboard/templates/dashboard/explorer.html +++ /dev/null @@ -1,580 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 static email_obfuscator add_url_schema avatar_tags bundle %} - - - - - {% include 'shared/head.html' %} - {% include 'shared/cards.html' %} - {% bundle css file dashboard_explorer %} - - - - - - - {% endbundle %} - - - -
- {% include 'shared/tag_manager_2.html' %} - {% include 'shared/top_nav.html' with class='d-md-flex' %} - {% include 'shared/nav.html' %} -
- - -
- - - - {% csrf_token %} - {% include 'shared/footer_scripts.html' with slim=1 %} - {% include 'shared/footer.html' %} - {% include 'shared/messages.html' %} -
- - - - - diff --git a/app/dashboard/templates/dashboard/tabs.html b/app/dashboard/templates/dashboard/tabs.html deleted file mode 100644 index 6b46e0e73af..00000000000 --- a/app/dashboard/templates/dashboard/tabs.html +++ /dev/null @@ -1,39 +0,0 @@ -{% load i18n static %} -{% if not hidden %} -
-
-
- - - - - -
-
-
-{% endif %} - - -
-
-
-
-
- {% if tab == 'explorer' %} - {% include 'dashboard/tab_explorer.html' %} - {% elif tab == 'resume' %} - {% include 'profiles/tab_.html' %} - {% endif %} -
-
-
-
-
diff --git a/app/dashboard/templates/notifyfunder.html b/app/dashboard/templates/notifyfunder.html deleted file mode 100644 index 31a470d7182..00000000000 --- a/app/dashboard/templates/notifyfunder.html +++ /dev/null @@ -1,45 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 static %} -
-
-
- -
-
-
{% trans "Send Payment Reminder" %}
-
-
-
- {% trans "This button sends a notification to the funder to pay out the bounty. Are you sure the bounty has been completed?" %} -
-
- -
-
-
-
-
- diff --git a/app/dashboard/templates/profiles/none.html b/app/dashboard/templates/profiles/none.html deleted file mode 100644 index 987958b9d1d..00000000000 --- a/app/dashboard/templates/profiles/none.html +++ /dev/null @@ -1,67 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 static %} -
-
- -
-
-

{% trans 'Oops! Looks like this user has not participated in any bounty yet.' %}

-

{% trans 'In the meantime, here are some things you can do:' %}

-
-
-
-
-
- -
-
- {% trans 'Start working on exciting bounties with the Issue Explorer' %} -
- -
-
-
- -
-
- {% trans 'Looking for awesome contributors to work on your project?' %} -
- -
-
-
- -
-
- {% trans 'Have any questions or suggestions? Join our Slack channel!' %} -
- -
-
diff --git a/app/dashboard/templates/shared/bounty_nav.html b/app/dashboard/templates/shared/bounty_nav.html deleted file mode 100644 index 1b38a01e69d..00000000000 --- a/app/dashboard/templates/shared/bounty_nav.html +++ /dev/null @@ -1,53 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 %} -
-
    -
  1. - 1. - {% if past_tense %} - {% trans "Open" %} - {% else %} - {% trans "Create Work" %} - {% endif %} -
  2. -
  3. - 2. - {% if past_tense %} - {% trans "Work Started" %} - {% else %} - {% trans "Start Work" %} - {% endif %} -
  4. -
  5. - 3. - {% if past_tense %} - {% trans "Work Submitted" %} - {% else %} - {% trans "Submit Work" %} - {% endif %} -
  6. -
  7. - 4. - {% if past_tense %} - {% trans "Work Done" %} - {% else %} - {% trans "Accept Work" %} - {% endif %} -
  8. -
-
diff --git a/app/dashboard/templates/shared/github_username.html b/app/dashboard/templates/shared/github_username.html deleted file mode 100644 index 2212853d64a..00000000000 --- a/app/dashboard/templates/shared/github_username.html +++ /dev/null @@ -1,24 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 %} -
- - {% if not user.is_authenticated %} - ({% trans "Login" %}) - {% endif %} - -
diff --git a/app/dashboard/templates/shared/issue_deadline.html b/app/dashboard/templates/shared/issue_deadline.html deleted file mode 100644 index 56d189b768a..00000000000 --- a/app/dashboard/templates/shared/issue_deadline.html +++ /dev/null @@ -1,27 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 %} - -
- - -
- - -
-
diff --git a/app/dashboard/templates/shared/notification_email.html b/app/dashboard/templates/shared/notification_email.html deleted file mode 100644 index 089cfe528d2..00000000000 --- a/app/dashboard/templates/shared/notification_email.html +++ /dev/null @@ -1,24 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 %} -
- - - {% if show_information_publicly_checkbox %} - {% include "shared/show_information_publicly.html" with id="show_email_publicly" %} - {% endif %} -
diff --git a/app/dashboard/templates/shared/pricing.html b/app/dashboard/templates/shared/pricing.html deleted file mode 100644 index 4de29a6181d..00000000000 --- a/app/dashboard/templates/shared/pricing.html +++ /dev/null @@ -1,50 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 %} -
- -
-
- -
- -
-
-
- - -
-
- - -
-
- - -
-
- - - - - USD payment is powered by PayPal. Please note that there might be a small fee for payment to contributors in outside of the US. - - -
diff --git a/app/dashboard/templates/shared/profile_rank_link.html b/app/dashboard/templates/shared/profile_rank_link.html deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/app/dashboard/templates/shared/send_tip_nav.html b/app/dashboard/templates/shared/send_tip_nav.html deleted file mode 100644 index ea53afa7f41..00000000000 --- a/app/dashboard/templates/shared/send_tip_nav.html +++ /dev/null @@ -1,37 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 %} -
-
    -
  1. - 1. - {% if past_tense %} - {% trans "Sent" %} - {% else %} - {% trans "Send" %} - {% endif %} -
  2. -
  3. - 2. - {% if past_tense %} - {% trans "Fulfilled" %} - {% else %} - {% trans "Fulfill" %} - {% endif %} -
  4. -
-
diff --git a/app/dashboard/templates/shared/sidebar_profile.html b/app/dashboard/templates/shared/sidebar_profile.html deleted file mode 100644 index 3a107a63fea..00000000000 --- a/app/dashboard/templates/shared/sidebar_profile.html +++ /dev/null @@ -1,39 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 %} -
-
-
-
{% trans "Edit Profile" %}
-
- - - -
-
-
-
{% trans "Browse Other Users" %}
-
- - -
-
-
-
- {% trans "CLEAR FIELDS" %} -
-
diff --git a/app/dashboard/templates/shared/theme_switcher.html b/app/dashboard/templates/shared/theme_switcher.html deleted file mode 100644 index 054fbd2dd2e..00000000000 --- a/app/dashboard/templates/shared/theme_switcher.html +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/app/dashboard/tests/test_binance_sync.py b/app/dashboard/tests/test_binance_sync.py index 98e39a68fe1..381d57b8b6c 100644 --- a/app/dashboard/tests/test_binance_sync.py +++ b/app/dashboard/tests/test_binance_sync.py @@ -17,14 +17,11 @@ along with this program. If not, see . """ -import os from datetime import timedelta -from unittest import mock from django.utils import timezone from dashboard.models import Bounty, BountyFulfillment, Profile -from dashboard.sync.binance import get_binance_txn_status, sync_binance_payout from test_plus.test import TestCase @@ -67,7 +64,7 @@ def setUp(self): ) BountyFulfillment.objects.create( fulfiller_address='0x0000000000000000000000000000000000000000', - bounty=bounty, + bounty=self.bounty, payout_tx_id='0xc32f5ad8a1dec9e0ae67f0f55f772ea752e7e032b62b1cdaf8d392e12c66e919' ) diff --git a/app/dashboard/tests/test_dashboard_helpers.py b/app/dashboard/tests/test_dashboard_helpers.py index 52aad60973c..575630dea4e 100644 --- a/app/dashboard/tests/test_dashboard_helpers.py +++ b/app/dashboard/tests/test_dashboard_helpers.py @@ -17,19 +17,11 @@ along with this program. If not, see . """ -import json -from datetime import date, datetime, timedelta -from unittest.mock import patch - from django.conf import settings from django.test.client import RequestFactory -import pytz -import requests_mock -from dashboard.helpers import amount, is_lowball_bounty, issue_details, normalize_url, process_bounty_details -from dashboard.models import Bounty +from dashboard.helpers import amount, is_lowball_bounty, normalize_url from economy.models import ConversionRate -from marketing.mails import featured_funded_bounty from test_plus.test import TestCase diff --git a/app/dashboard/tests/test_dashboard_models.py b/app/dashboard/tests/test_dashboard_models.py index 288c29bf587..2f6f5278453 100644 --- a/app/dashboard/tests/test_dashboard_models.py +++ b/app/dashboard/tests/test_dashboard_models.py @@ -209,20 +209,20 @@ def test_can_submit_standard_bounty_after_expiration_date_if_deadline_extended() ) assert bounty.can_submit_after_expiration_date is True - @staticmethod - def test_title_or_desc(): - bounty = Bounty.objects.create( - title='TitleTest', - idx_status=0, - is_open=False, - web3_created=datetime(2008, 10, 31, tzinfo=pytz.UTC), - expires_date=datetime(2008, 11, 30, tzinfo=pytz.UTC), - github_url='https://github.com/gitcoinco/web/issues/0xDEADBEEF', - raw_data={} - ) - assert bounty.title_or_desc == "TitleTest" - bounty.title = None - assert bounty.title_or_desc == "https://github.com/gitcoinco/web/issues/0xDEADBEEF" + # @staticmethod + # def test_title_or_desc(): + # bounty = Bounty.objects.create( + # title='TitleTest', + # idx_status=0, + # is_open=False, + # web3_created=datetime(2008, 10, 31, tzinfo=pytz.UTC), + # expires_date=datetime(2008, 11, 30, tzinfo=pytz.UTC), + # github_url='https://github.com/gitcoinco/web/issues/1', + # raw_data={} + # ) + # assert bounty.title_or_desc == "TitleTest" + # bounty.title = None + # assert bounty.title_or_desc == "HTTP API Documentation" @staticmethod def test_github_issue_number(): diff --git a/app/dashboard/tests/test_dashboard_utils.py b/app/dashboard/tests/test_dashboard_utils.py index eb92e1b936e..02b9692b6b1 100644 --- a/app/dashboard/tests/test_dashboard_utils.py +++ b/app/dashboard/tests/test_dashboard_utils.py @@ -24,13 +24,11 @@ from django.test.client import RequestFactory from django.utils import timezone -import ipfshttpclient -import pytest from dashboard.models import Bounty, Profile from dashboard.utils import ( - IPFSCantConnectException, apply_new_bounty_deadline, clean_bounty_url, create_user_action, get_bounty, get_ipfs, - get_ordinal_repr, get_token_recipient_senders, get_web3, getBountyContract, humanize_event_name, ipfs_cat_ipfsapi, - re_market_bounty, release_bounty_to_the_public, + apply_new_bounty_deadline, clean_bounty_url, create_user_action, get_bounty, get_ordinal_repr, + get_token_recipient_senders, get_web3, getBountyContract, humanize_event_name, re_market_bounty, + release_bounty_to_the_public, ) from eth_utils import is_address from pytz import UTC diff --git a/app/dashboard/utils.py b/app/dashboard/utils.py index 3ebd0f8238d..03555265db3 100644 --- a/app/dashboard/utils.py +++ b/app/dashboard/utils.py @@ -21,7 +21,6 @@ import json import logging import re -import time from json.decoder import JSONDecodeError from django.conf import settings @@ -36,9 +35,7 @@ from compliance.models import Country, Entity from cytoolz import compose from dashboard.helpers import UnsupportedSchemaException, normalize_url, process_bounty_changes, process_bounty_details -from dashboard.models import ( - Activity, BlockedUser, Bounty, BountyFulfillment, HackathonRegistration, Profile, UserAction, -) +from dashboard.models import Activity, BlockedUser, Bounty, Profile, UserAction from dashboard.sync.algorand import sync_algorand_payout from dashboard.sync.binance import sync_binance_payout from dashboard.sync.btc import sync_btc_payout @@ -54,7 +51,6 @@ from dashboard.sync.tezos import sync_tezos_payout from dashboard.sync.xinfin import sync_xinfin_payout from dashboard.sync.zil import sync_zil_payout -from ens.auto import ns from ens.utils import name_to_hash from eth_abi import decode_single, encode_single from eth_utils import keccak, to_checksum_address, to_hex @@ -360,15 +356,6 @@ def get_graphql_result(uri, query): return json.loads(result) -def get_profile_from_referral_code(code): - """Returns a profile from the unique code - - Returns: - A unique string for each profile - """ - return base64.urlsafe_b64decode(code.encode()).decode() - - def get_bounty_invite_url(inviter, bounty_id): """Returns a unique url for each bounty and one who is inviting Returns: @@ -884,59 +871,10 @@ def clean_bounty_url(url): return url -def generate_pub_priv_keypair(): - # Thanks https://github.com/vkobel/ethereum-generate-wallet/blob/master/LICENSE.md - import sha3 - from ecdsa import SECP256k1, SigningKey - - def checksum_encode(addr_str): - keccak = sha3.keccak_256() - out = '' - addr = addr_str.lower().replace('0x', '') - keccak.update(addr.encode('ascii')) - hash_addr = keccak.hexdigest() - for i, c in enumerate(addr): - if int(hash_addr[i], 16) >= 8: - out += c.upper() - else: - out += c - return '0x' + out - - keccak = sha3.keccak_256() - - priv = SigningKey.generate(curve=SECP256k1) - pub = priv.get_verifying_key().to_string() - - keccak.update(pub) - address = keccak.hexdigest()[24:] - - def test(addrstr): - assert(addrstr == checksum_encode(addrstr)) - - test('0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed') - test('0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359') - test('0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB') - test('0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb') - test('0x7aA3a964CC5B0a76550F549FC30923e5c14EDA84') - - # print("Private key:", priv.to_string().hex()) - # print("Public key: ", pub.hex()) - # print("Address: ", checksum_encode(address)) - # return priv key, pub key, address - - return priv.to_string().hex(), pub.hex(), checksum_encode(address) - - def get_bounty_semaphore_ns(standard_bounty_id): return f'{SEMAPHORE_BOUNTY_NS}_{standard_bounty_id}_{SEMAPHORE_BOUNTY_SALT}' -def release_bounty_lock(standard_bounty_id): - from app.utils import release_semaphore - ns = get_bounty_semaphore_ns(standard_bounty_id) - release_semaphore(ns) - - def profile_helper(handle, suppress_profile_hidden_exception=False, current_user=None, disable_cache=False, full_profile=False): """Define the profile helper. diff --git a/app/dashboard/views.py b/app/dashboard/views.py index d65216e17ed..0aee9adc940 100644 --- a/app/dashboard/views.py +++ b/app/dashboard/views.py @@ -18,10 +18,8 @@ ''' from __future__ import print_function, unicode_literals -import asyncio import base64 import calendar -import getpass import hashlib import html import json @@ -41,8 +39,7 @@ from django.contrib.staticfiles.storage import staticfiles_storage from django.core.exceptions import ObjectDoesNotExist, PermissionDenied, ValidationError from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator -from django.core.serializers.json import DjangoJSONEncoder -from django.db.models import Avg, Count, Prefetch, Q, Sum +from django.db.models import Count, Q, Sum from django.http import Http404, HttpResponse, JsonResponse from django.shortcuts import get_object_or_404, redirect from django.template import loader @@ -50,7 +47,6 @@ from django.templatetags.static import static from django.urls import reverse from django.utils import timezone -from django.utils.html import escape, strip_tags from django.utils.http import is_safe_url from django.utils.text import slugify from django.utils.timezone import localtime @@ -59,7 +55,6 @@ from django.views.decorators.csrf import csrf_exempt, csrf_protect from django.views.decorators.http import require_GET, require_POST -import dateutil.parser import ens import magic import pytz @@ -67,9 +62,8 @@ import tweepy from app.services import RedisService, TwilioService from app.settings import ( - BMAS_ENDPOINT, EMAIL_ACCOUNT_VALIDATION, ES_CORE_ENDPOINT, ES_USER_ENDPOINT, PHONE_SALT, SMS_COOLDOWN_IN_MINUTES, - SMS_MAX_VERIFICATION_ATTEMPTS, TWITTER_ACCESS_SECRET, TWITTER_ACCESS_TOKEN, TWITTER_CONSUMER_KEY, - TWITTER_CONSUMER_SECRET, + EMAIL_ACCOUNT_VALIDATION, PHONE_SALT, SMS_COOLDOWN_IN_MINUTES, SMS_MAX_VERIFICATION_ATTEMPTS, TWITTER_ACCESS_SECRET, + TWITTER_ACCESS_TOKEN, TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET, ) from app.utils import clean_str, ellipses, get_default_network from avatar.models import AvatarTheme @@ -80,28 +74,20 @@ from cacheops import invalidate_obj from dashboard.brightid_utils import get_brightid_status from dashboard.context import quickstart as qs -from dashboard.duniter import CERTIFICATIONS_SCHEMA from dashboard.idena_utils import ( IdenaNonce, get_handle_by_idena_token, idena_callback_url, next_validation_time, signature_address, ) from dashboard.tasks import increment_view_count from dashboard.utils import ( ProfileHiddenException, ProfileNotFoundException, build_profile_pairs, get_bounty_from_invite_url, - get_ens_contract_addresss, get_ens_resolver_contract, get_orgs_perms, get_poap_earliest_owned_token_timestamp, - profile_helper, + get_ens_contract_addresss, get_orgs_perms, get_poap_earliest_owned_token_timestamp, profile_helper, ) from economy.utils import ConversionRateNotFoundError, convert_amount, convert_token_to_usdt -from ens.auto import ns -from ens.utils import name_to_hash from eth_account.messages import defunct_hash_message -from eth_utils import is_address, is_same_address, to_checksum_address, to_normalized_address -from gas.utils import recommend_min_gas_price_to_confirm_in_time -from git.utils import ( - get_auth_url, get_gh_issue_details, get_github_user_data, get_url_dict, is_github_token_valid, search_users, -) -from grants.models import Grant +from eth_utils import is_address, is_same_address +from git.utils import get_auth_url, get_issue_details, get_url_dict, get_user, is_github_token_valid, search_users from grants.utils import get_clr_rounds_metadata -from kudos.models import KudosTransfer, Token, Wallet +from kudos.models import Token from kudos.utils import humanize_name # from mailchimp3 import MailChimp from marketing.mails import admin_contact_funder, bounty_uninterested @@ -110,7 +96,7 @@ new_reserved_issue, share_bounty, start_work_approved, start_work_new_applicant, start_work_rejected, wall_post_email, ) -from marketing.models import EmailSubscriber, Keyword, UpcomingDate +from marketing.models import Keyword from oauth2_provider.decorators import protected_resource from oauthlib.oauth2.rfc6749.errors import InvalidGrantError from perftools.models import JSONStore @@ -119,11 +105,10 @@ from ratelimit.decorators import ratelimit from requests_oauthlib import OAuth2Session from requests_oauthlib.compliance_fixes import facebook_compliance_fix -from rest_framework.renderers import JSONRenderer from retail.helpers import get_ip from retail.utils import programming_languages, programming_languages_full from townsquare.models import Comment, PinnedPost -from townsquare.views import get_following_tribes, get_tags +from townsquare.views import get_tags from unidecode import unidecode from web3 import HTTPProvider, Web3 @@ -135,18 +120,16 @@ bounty_activity_event_adapter, get_bounty_data_for_activity, handle_bounty_views, load_files_in_directory, ) from .models import ( - Activity, Answer, BlockedURLFilter, Bounty, BountyEvent, BountyFulfillment, BountyInvites, CoinRedemption, - CoinRedemptionRequest, Coupon, Earning, FeedbackEntry, HackathonEvent, HackathonProject, HackathonRegistration, - HackathonSponsor, HackathonWorkshop, Interest, LabsResearch, MediaFile, Option, Poll, PortfolioItem, Profile, - ProfileSerializer, ProfileVerification, ProfileView, Question, SearchHistory, Sponsor, Subscription, Tool, ToolVote, - TribeMember, UserAction, UserDirectory, UserVerificationModel, + Activity, Answer, BlockedURLFilter, Bounty, BountyEvent, BountyFulfillment, BountyInvites, Coupon, Earning, + FeedbackEntry, HackathonEvent, HackathonProject, HackathonRegistration, HackathonSponsor, Interest, LabsResearch, + MediaFile, Option, Poll, PortfolioItem, Profile, ProfileSerializer, ProfileVerification, ProfileView, Question, + SearchHistory, Sponsor, Tool, TribeMember, UserAction, UserVerificationModel, ) from .notifications import ( - maybe_market_tip_to_email, maybe_market_tip_to_github, maybe_market_tip_to_slack, maybe_market_to_email, - maybe_market_to_github, maybe_market_to_slack, maybe_market_to_user_slack, + maybe_market_to_email, maybe_market_to_github, maybe_market_to_slack, maybe_market_to_user_slack, ) from .poh_utils import is_registered_on_poh -from .router import HackathonEventSerializer, HackathonProjectSerializer, TribesSerializer, TribesTeamSerializer +from .router import HackathonEventSerializer, TribesSerializer from .utils import ( apply_new_bounty_deadline, get_bounty, get_bounty_id, get_context, get_custom_avatars, get_hackathon_events, get_hackathons_page_default_tabs, get_unrated_bounties_count, get_web3, has_tx_mined, is_valid_eth_address, @@ -164,7 +147,6 @@ @protected_resource() def oauth_connect(request, *args, **kwargs): active_user_profile = Profile.objects.filter(user_id=request.user.id).select_related()[0] - from marketing.utils import should_suppress_notification_email user_profile = { "login": active_user_profile.handle, "email": active_user_profile.user.email, @@ -280,8 +262,8 @@ def record_bounty_activity(bounty, user, event_name, interest=None, fulfillment= def helper_handle_access_token(request, access_token): # https://gist.github.com/owocki/614a18fbfec7a5ed87c97d37de70b110 # interest API via token - github_user_data = get_github_user_data(access_token) - request.session['handle'] = github_user_data['login'] + github_user_data = get_user(token=access_token) + request.session['handle'] = github_user_data.login profile = Profile.objects.filter(handle=request.session['handle'].lower()).first() request.session['profile_id'] = profile.pk @@ -364,9 +346,9 @@ def new_interest(request, bounty_id): access_token = request.GET.get('token') if access_token: helper_handle_access_token(request, access_token) - github_user_data = get_github_user_data(access_token) + github_user_data = get_user(access_token) profile = Profile.objects.prefetch_related('bounty_set') \ - .filter(handle=github_user_data['login'].lower()).first() + .filter(handle=github_user_data.login.lower()).first() profile_id = profile.pk else: profile = request.user.profile if profile_id else None @@ -601,8 +583,8 @@ def remove_interest(request, bounty_id): access_token = request.GET.get('token') if access_token: helper_handle_access_token(request, access_token) - github_user_data = get_github_user_data(access_token) - profile = Profile.objects.filter(handle=github_user_data['login'].lower()).first() + github_user_data = get_user(access_token) + profile = Profile.objects.filter(handle=github_user_data.login.lower()).first() profile_id = profile.pk if not profile_id: @@ -823,6 +805,7 @@ def uninterested(request, bounty_id, profile_id): def onboard_avatar(request): return redirect('/onboard/contributor?steps=avatar') + def onboard(request, flow=None): """Handle displaying the first time user experience flow.""" if flow not in ['funder', 'contributor', 'profile']: @@ -908,6 +891,7 @@ def users_directory(request): return TemplateResponse(request, 'dashboard/users.html', params) + @csrf_exempt @staff_member_required def user_lookup(request): @@ -920,28 +904,13 @@ def user_lookup(request): from proxy.views import proxy_view return proxy_view(request, remote_url) + @staff_member_required def users_directory_elastic(request): """Handle displaying users directory page.""" from retail.utils import programming_languages, programming_languages_full messages.info(request, 'The Andrew-era user directory has been deprecated, please contact the #product-data channel if you need something') return redirect('/users') - keywords = programming_languages + programming_languages_full - - params = { - 'is_staff': request.user.is_staff, - 'avatar_url': request.build_absolute_uri(static('v2/images/twitter_cards/tw_cards-07.png')) , - 'active': 'users', - 'title': 'Users', - 'meta_title': "", - 'meta_description': "", - 'keywords': keywords - } - - if request.path == '/tribes/explore': - params['explore'] = 'explore_tribes' - - return TemplateResponse(request, 'dashboard/users-elastic.html', params) def users_fetch_filters(profile_list, skills, bounties_completed, leaderboard_rank, rating, organisation, hackathon_id = "", only_with_tokens = False): @@ -1048,6 +1017,7 @@ def output_users_to_csv(request): return JsonResponse({'message' : 'Your request is processing and will be delivered to your email'}) + @require_GET def users_fetch(request): """Handle displaying users.""" @@ -1127,26 +1097,6 @@ def users_fetch(request): only_with_tokens ) - def previous_worked(): - if current_user.profile.persona_is_funder: - return Count( - 'fulfilled', - filter=Q( - fulfilled__bounty__network=network, - fulfilled__accepted=True, - fulfilled__bounty__bounty_owner_github_username__iexact=current_user.profile.handle - ) - ) - - return Count( - 'bounties_funded__fulfillments', - filter=Q( - bounties_funded__fulfillments__bounty__network=network, - bounties_funded__fulfillments__accepted=True, - bounties_funded__fulfillments__profile__handle=current_user.profile.handle - ) - ) - if request.GET.get('type') == 'explore_tribes': profile_list = Profile.objects.filter(is_org=True).order_by('-follower_count', 'id') @@ -1946,7 +1896,6 @@ def helper_handle_release_bounty_to_public(request, bounty): messages.warning(request, _('This functionality is only for reserved bounties')) - @login_required def bounty_invite_url(request, invitecode): """Decode the bounty details and redirect to correct bounty @@ -2116,14 +2065,14 @@ def funder_payout_reminder(request, bounty_network, stdbounties_id): access_token = request.user.profile.get_access_token() else: access_token = request.session.get('access_token') - github_user_data = get_github_user_data(access_token) + github_user_data = get_user(access_token) try: bounty = Bounty.objects.current().filter(network=bounty_network, standard_bounties_id=stdbounties_id).first() except Bounty.DoesNotExist: raise Http404 - has_fulfilled = bounty.fulfillments.filter(profile__handle=github_user_data['login']).count() + has_fulfilled = bounty.fulfillments.filter(profile__handle=github_user_data.login).count() if has_fulfilled == 0: return JsonResponse({ 'success': False, @@ -2183,12 +2132,6 @@ def profile_details(request, handle): except (ProfileNotFoundException, ProfileHiddenException): raise Http404 - if not settings.DEBUG: - network = 'mainnet' - else: - network = 'rinkeby' - - response = { 'avatar': profile.avatar_url, 'handle': profile.handle, @@ -2248,7 +2191,6 @@ def user_card(request, handle): if response.get('profile',{}).get('data',{}).get('email'): del response['profile']['data']['email'] - return JsonResponse(response, safe=False) @@ -2285,7 +2227,6 @@ def profile_quests(request, handle): from quests.models import QuestPointAward qpas = QuestPointAward.objects.filter(profile=profile).order_by('created_on') - history = [] response = """date,close""" balances = {} @@ -2302,11 +2243,9 @@ def profile_quests(request, handle): for datestr, balance in balances.items(): response += f"\n{datestr},{balance}" - mimetype = 'text/x-csv' return HttpResponse(response) - def profile_grants(request, handle): """Display profile grant contribution details. @@ -2321,7 +2260,6 @@ def profile_grants(request, handle): from grants.models import Contribution contributions = Contribution.objects.filter(subscription__contributor_profile=profile).order_by('-pk') - history = [] response = """date,close""" balances = {} @@ -2336,7 +2274,6 @@ def profile_grants(request, handle): for datestr, balance in balances.items(): response += f"\n{datestr},{balance}" - mimetype = 'text/x-csv' return HttpResponse(response) @@ -2403,7 +2340,6 @@ def profile_ratings(request, handle, attr): balance = balance['sum'] / balance['count'] response += f"\n{datestr},{balance}" - mimetype = 'text/x-csv' return HttpResponse(response) @@ -2440,7 +2376,6 @@ def profile_earnings(request, handle, direction='to'): for datestr, balance in balances.items(): response += f"\n{datestr},{balance}" - mimetype = 'text/x-csv' return HttpResponse(response) @@ -2471,7 +2406,6 @@ def profile_viewers(request, handle): for datestr, balance in balances.items(): response += f"\n{datestr},{balance}" - mimetype = 'text/x-csv' return HttpResponse(response) @@ -2517,6 +2451,7 @@ def profile_job_opportunity(request, handle): } return JsonResponse(response) + @require_POST @login_required def profile_settings(request): @@ -2547,6 +2482,7 @@ def profile_settings(request): return JsonResponse(response, status=response.get('status', 200)) + @require_POST @login_required def profile_backup(request): @@ -2609,6 +2545,7 @@ def profile_backup(request): return JsonResponse(response, status=response.get('status', 200)) + @require_POST @login_required def profile_tax_settings(request, handle): @@ -3055,6 +2992,7 @@ def get_profile_tab(request, profile, tab, prev_context): raise Http404 return context + def get_profile_by_idena_token(token): handle = get_handle_by_idena_token(token) try: @@ -3062,6 +3000,7 @@ def get_profile_by_idena_token(token): except ObjectDoesNotExist: return None + def logout_idena(request, handle): is_logged_in_user = request.user.is_authenticated and request.user.username.lower() == handle.lower() @@ -3090,6 +3029,7 @@ def logout_idena(request, handle): 'icon_path': static('v2/images/project_logos/idena.svg'), }) + # Response model differ from rest of project because idena client excepts this shape: # Using {success, error} instead of {ok, msg} @csrf_exempt # Call from idena client @@ -3150,6 +3090,7 @@ def start_session_idena(request, handle): } }) + # Response model differ from rest of project because idena client excepts this shape: # Using {success, error} instead of {ok, msg} @csrf_exempt # Call from idena client @@ -3199,6 +3140,7 @@ def authenticate_idena(request, handle): } }) + @login_required def recheck_idena_status(request, handle): is_logged_in_user = request.user.is_authenticated and request.user.username.lower() == handle.lower() @@ -3223,6 +3165,7 @@ def recheck_idena_status(request, handle): 'icon_path': 'https://robohash.org/%s' % profile.idena_address if profile.is_idena_connected else static('v2/images/project_logos/idena.svg'), }) + def verify_text_for_tweet(handle): url = 'https://gitcoin.co/' + handle msg = 'I am verifying my identity as ' + handle + ' on @gitcoin' @@ -3518,6 +3461,7 @@ def request_verify_google(request, handle): ) return redirect(authorization_url) + @login_required @require_GET def verify_user_google(request): @@ -4227,6 +4171,7 @@ def terms(request): } return TemplateResponse(request, 'legal/terms.html', context) + def privacy(request): return TemplateResponse(request, 'legal/privacy.html', {}) @@ -4351,6 +4296,7 @@ def new_hackathon_bounty(request, hackathon=''): ) return TemplateResponse(request, 'dashboard/hackathon/new_bounty.html', params) + @csrf_exempt def get_suggested_contributors(request): previously_worked_developers = [] @@ -4650,6 +4596,7 @@ def get_kudos(request): mimetype = 'application/json' return HttpResponse(data, mimetype) + @login_required def hackathon_prizes(request, hackathon=''): profile = request.user.profile @@ -5179,7 +5126,6 @@ def hackathon_save_project(request): if p in video_url: kwargs['extra']['video_provider'] = p - if categories: kwargs['categories'] = categories @@ -5700,7 +5646,6 @@ def funder_dashboard(request, bounty_type): for b in bounties], safe=False) - def contributor_dashboard(request, bounty_type): """JSON data for the contributor dashboard""" @@ -5885,8 +5830,6 @@ def join_tribe(request, handle): ) - - @csrf_exempt @require_POST def tribe_leader(request): @@ -6025,6 +5968,7 @@ def save_tribe(request,handle): status=500 ) + @csrf_exempt @require_POST def create_bounty_v1(request): @@ -6126,7 +6070,7 @@ def create_bounty_v1(request): # bounty github data try: kwargs = get_url_dict(bounty.github_url) - bounty.github_issue_details = get_gh_issue_details(**kwargs) + bounty.github_issue_details = get_issue_details(**kwargs) except Exception as e: logger.error(e) @@ -6622,7 +6566,6 @@ def close_bounty_v1(request, bounty_id): return JsonResponse(response) - @staff_member_required def bulkemail(request): handles = request.POST.get('handles', '') @@ -7193,6 +7136,7 @@ def file_upload(request): return JsonResponse(data) + @csrf_exempt def mautic_api(request, endpoint=''): diff --git a/app/dataviz/d3_views.py b/app/dataviz/d3_views.py index 75c8ae929f8..60f81d375cc 100644 --- a/app/dataviz/d3_views.py +++ b/app/dataviz/d3_views.py @@ -455,110 +455,6 @@ def helper_hide_pii(username, num_chars=3): return new_username -def viz_graph_data_helper(_type, keyword, hide_pii): - # setup response - output = {"nodes": [], "links": []} - - # gather info - types = {} - names = {} - values = {} - avatars = {} - edges = [] - bounties = Bounty.objects.current().filter(network='mainnet') - if keyword: - bounties = bounties.filter(raw_data__icontains=keyword) - - for bounty in bounties: - if bounty.value_in_usdt_then: - weight = bounty.value_in_usdt_then - source = bounty.org_name - if source: - for fulfillment in bounty.fulfillments.all(): - created = fulfillment.created_on.strftime("%s") - if _type != 'fulfillments_accepted_only' or fulfillment.accepted: - target = fulfillment.fulfiller_github_username.lower() - if hide_pii: - target = helper_hide_pii(target) - types[source] = 'source' - types[target] = 'target_accepted' if fulfillment.accepted else 'target' - names[source] = None - names[target] = None - edges.append((source, target, weight, created)) - - value = values.get(source, 0) - value += weight - values[source] = value - value = values.get(target, 0) - value += weight - values[target] = value - - for tip in Tip.objects.filter(network='mainnet'): - weight = tip.value_in_usdt - created = tip.created_on.strftime("%s") - if weight: - source = tip.username.lower() - if hide_pii: - source = helper_hide_pii(source) - target = tip.from_username.lower() - if hide_pii: - target = helper_hide_pii(target) - if source and target: - if source not in names.keys(): - types[source] = 'source' - names[source] = None - if source not in types.keys(): - types[target] = 'target' - names[target] = None - edges.append((source, target, weight, created)) - - if _type in ['what_future_could_look_like', 'all']: - last_node = None - created = 1525147679 - nodes = Profile.objects.exclude(github_access_token='').all() - for profile in nodes: - node = profile.handle.lower() - if hide_pii: - node = helper_hide_pii(node) - if node not in names.keys(): - names[node] = None - types[node] = 'independent' - if last_node and _type == 'what_future_could_look_like': # and random.randint(0, 2) == 0: - weight = random.randint(1, 10) - # edges.append((node, last_node, weight)) - # edges.append((nodes.order_by('?').first().handle.lower(), node, weight)) - target = nodes.order_by('?').first().handle.lower() - if hide_pii: - target = helper_hide_pii(target) - edges.append((target, node, weight, created)) - last_node = node - - for key, val in values.items(): - if val > 40: - avatar_key = key if key and "*" not in key else "None" - avatars[key] = f'https://gitcoin.co/dynamic/avatar/{avatar_key}' - - # build output - for name in set(names.keys()): - names[name] = len(output['nodes']) - value = int(math.sqrt(math.sqrt(values.get(name, 1)))) - output['nodes'].append({"name": name, 'value': value, 'type': types.get(name), 'avatar': avatars.get(name)}) - for edge in edges: - source, target, weight, created = edge - weight = math.sqrt(weight) - if names.get(source) and names.get(target): - source = names[source] - target = names[target] - output['links'].append({ - 'source': source, - 'target': target, - 'value': value, - 'weight': weight, - 'created': created, - }) - return output - - def get_all_type_options(): return ['fulfillments_accepted_only', 'all', 'fulfillments', 'what_future_could_look_like'] @@ -688,33 +584,6 @@ def viz_scatterplot(request, key='hourly_rate', template='dataviz/scatterplot.ht return viz_scatterplot_helper(request, key, template, hide_usernames) -def viz_scatterplot_data_helper(keyword, hide_usernames=False): - rows = [['hourlyRate', 'daysBack', 'username', 'weight']] - fulfillments = BountyFulfillment.objects.filter(accepted=True).exclude(fulfiller_hours_worked=None) - if keyword: - filter_bounties = Bounty.objects.filter(raw_data__icontains=keyword) - fulfillments = fulfillments.filter(bounty__in=filter_bounties) - for bf in fulfillments: - #print(bf.pk, bf.created_on) - try: - weight = math.log(bf.bounty.value_in_usdt, 10) / 4 - username = bf.bounty.org_name - if hide_usernames: - username = "repo: " + helper_hide_pii(username.lower(), 1) - row = [str(bf.bounty.hourly_rate), str((timezone.now() - bf.accepted_on).days), username, str(weight), ] - if bf.bounty.hourly_rate: - rows.append(row) - except Exception: - pass - - output_rows = [] - for row in rows: - output_rows.append(",".join(row)) - - output = "\n".join(output_rows) - return output - - def is_an_edge(handle, edges): for edge in edges: if handle == edge[0]: diff --git a/app/economy/tx.py b/app/economy/tx.py index 1c55925377d..c899875fbe2 100644 --- a/app/economy/tx.py +++ b/app/economy/tx.py @@ -1,19 +1,13 @@ -import decimal -import os -import time from decimal import Decimal -from time import sleep from django.conf import settings from django.utils import timezone import requests -from bs4 import BeautifulSoup from dashboard.abi import erc20_abi from dashboard.utils import get_tx_status, get_web3 from economy.models import Token -from hexbytes import HexBytes -from web3 import HTTPProvider, Web3 +from web3 import Web3 from web3.exceptions import BadFunctionCallOutput @@ -92,14 +86,6 @@ def transaction_status(transaction, txid): maybeprint(89, e) -def check_transaction_contract(transaction_tax): - transaction = check_transaction(transaction_tax) - if transaction is not None: - token_address = check_token(transaction.to) - if token_address is not False and not token_address == '0x0000000000000000000000000000000000000000': - return transaction_status(transaction, transaction_tax) - - def get_token(token_symbol, network): """ For a given token symbol and amount, returns the token's details. For ETH, we change the @@ -135,13 +121,6 @@ def check_for_replaced_tx(tx_hash, network): return tx_hash, status, timestamp -def is_bulk_checkout_tx(receipt): - """ - Returns true if the to address of the recipient is the bulk checkout contract - """ - to_address = receipt['to'].lower() - is_bulk_checkout = to_address == bulk_checkout_address.lower() - return is_bulk_checkout def grants_transaction_validator(contribution, w3): """ @@ -203,16 +182,6 @@ def grants_transaction_validator(contribution, w3): # Validator currently assumes msg.sender == originator as described above response['originator'] = [ receipt['from'] ] - # The below check was commented out since it wrongly fails for transactions sent via Argent - # and other wallets that use meta-transactions or relayers - - # # Return if recipient is not the BulkCheckout contract - # is_bulk_checkout = is_bulk_checkout_tx(receipt) - # if not is_bulk_checkout: - # to_address = receipt['to'] - # response['validation']['comment'] = f'This function only validates transactions through the BulkCheckout contract, but this transaction was sent to {to_address}' - # return response - # Parse receipt logs to look for expected transfer info. We don't need to distinguish # between ETH and token transfers, and don't need to look at any other receipt parameters, # because all contributions are emitted as an event @@ -341,98 +310,3 @@ def get_token_recipient_senders(recipient_address, token_address): headers = {'Authorization': f'Bearer {auth}'} validation_threshold_pct = 0.05 validation_threshold_total = 0.05 - -def get_token_originators(to_address, token, from_address='', return_what='transfers', tx_id='', amounts=[]): - address = to_address - - #is_address = requests.get('https://api.aleth.io/v1/accounts/' + address, headers=headers).status_code - - #if is_address != requests.codes.ok: - # raise ValueError('Address provided is not valid.') - - #is_token = requests.get( - # 'https://api.aleth.io/v1/tokens/' + (token), - # headers=headers - #).status_code - - #if is_token != requests.codes.ok and token != '0x0': - # raise ValueError('Token provided is not valid.') - - #balance = 0 - #try: - #url = 'https://api.aleth.io/v1/token-balances?filter[account]=' + address + '&filter[token]=' + token - #balance = requests.get(url, headers=headers).json()['data'][0]['attributes']['balance'] - # pass - #if balance == 0: - # raise ValueError('No balance of token at address provided.') - #except Exception as e: - # maybeprint(250, e) - - endpoint = 'token-transfers' if token != '0x0' else 'ether-transfers' - url = f'https://api.aleth.io/v1/{endpoint}?filter[to]=' + address + '&filter[token]=' + token + '&page%5Blimit%5D=100' - if token == '0x0': - url = f'https://api.aleth.io/v1/{endpoint}?filter[account]=' + address + '&page%5Blimit%5D=100' - if from_address: - url += '&filter[from]=' + from_address - - # OLD: THIS REQUEST THROWS WITH A 500 INTERNAL SERVER ERROR - transfers = requests.get( - url, - headers=headers - ).json() - - # NEW: PARSE EVENT LOGS TO SEE WHAT'S GOING ON - - if transfers.get('message') == 'API rate limit exceeded. Please upgrade your account.': - raise Exception("RATE LIMIT EXCEEDED") - # TODO - pull more than one page in case there are many transfers. - - if return_what == 'transfers': - for transfer in transfers.get('data', {}): - this_is_the_one = tx_id and tx_id.lower() in str(transfer).lower() - _decimals = transfer.get('attributes', {}).get('decimals', 18) - _symbol = transfer.get('attributes', {}).get('symbol', 'ETH') - _value = transfer.get('attributes', {}).get('value', 0) - _value_decimal = Decimal(int(_value) / 10 ** _decimals) - _this_is_the_one = False - for amount in amounts: - delta = abs(float(abs(_value_decimal)) - float(abs(amount))) - threshold = (float(abs(amount)) * validation_threshold_pct) - if delta < threshold or delta < validation_threshold_total: - _this_is_the_one = True - this_is_the_one = not len(amounts) or _this_is_the_one - if this_is_the_one: - if transfer.get('type') in ['TokenTransfer', 'EtherTransfer']: - return { - 'token_amount_decimal': _value_decimal, - 'token_name': _symbol, - 'to': address, - 'token_address': token, - 'token_amount_int': int(transfer['attributes']['value']), - 'decimals': _decimals, - } - return None - - # TokenTransfer events, value field - try: - originators = [] - xfrs = transfers.get('data', {}) - for tx in xfrs: - if tx.get('type') == 'TokenTransfer': - response = tx['relationships']['from']['data']['id'] - # hack to save time - if response != to_address: - return [response] - #originators.append(response) - value = int(tx.get('attributes', {}).get('value', 0)) - if tx.get('type') == 'EtherTransfer' and value > 0 and token == '0x0': - response = tx['relationships']['from']['data']['id'] - if response != to_address: - # hack to save time - return [response] - originators.append(response) - - return list(set(originators)) - except Exception as e: - maybeprint('284', e) - return [] diff --git a/app/economy/utils.py b/app/economy/utils.py index 3aa89288ae8..8ec0acd8a17 100644 --- a/app/economy/utils.py +++ b/app/economy/utils.py @@ -17,17 +17,9 @@ along with this program. If not, see . """ -from cacheops import cached_as from economy.models import ConversionRate -# All Units in native currency -class TransactionException(Exception): - """Handle general transaction exceptions.""" - - pass - - class ConversionRateNotFoundError(Exception): """Thrown if ConversionRate not found.""" diff --git a/app/faucet/templates/bulk_DM.html b/app/faucet/templates/bulk_DM.html deleted file mode 100644 index 75976fe2b78..00000000000 --- a/app/faucet/templates/bulk_DM.html +++ /dev/null @@ -1,59 +0,0 @@ -{% extends "admin/base_site.html" %} -{% comment %} - Copyright (C) 2021 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 static bundle %} - -{% block extrastyle %}{{ block.super }} - {% bundle css file admin_dashboard %} - - {% endbundle %} -{% endblock %} - -{% block coltype %}colMS{% endblock %} - -{% block bodyclass %}{{ block.super }} dashboard{% endblock %} - -{% block breadcrumbs %}{% endblock %} - -{% block content %} - -
-

{% trans "Send Bulk DMs" %} {{obj.pk}}

-
- {% csrf_token %} - Handles (comma seperated, like: "owocki, vs77bb, danlipert, pixelant") -
- -
-
- Message -
- - -
- - - -
- - -{% endblock %} -{% block extrahead %}{{ block.super }} - {% include 'shared/footer_scripts.html' %} -{% endblock %} diff --git a/app/faucet/templates/shared/faucet_no_metamask_error.html b/app/faucet/templates/shared/faucet_no_metamask_error.html deleted file mode 100644 index cdbe4730051..00000000000 --- a/app/faucet/templates/shared/faucet_no_metamask_error.html +++ /dev/null @@ -1,39 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 static %} - diff --git a/app/faucet/templates/shared/faucet_unlock_metamask.html b/app/faucet/templates/shared/faucet_unlock_metamask.html deleted file mode 100644 index 0bf644b1811..00000000000 --- a/app/faucet/templates/shared/faucet_unlock_metamask.html +++ /dev/null @@ -1,36 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 static %} - diff --git a/app/faucet/views.py b/app/faucet/views.py index 01da3862468..e5ab88d7adc 100644 --- a/app/faucet/views.py +++ b/app/faucet/views.py @@ -24,7 +24,6 @@ from django.http import Http404, JsonResponse from django.shortcuts import redirect from django.template.response import TemplateResponse -from django.urls import reverse from django.utils import timezone from django.utils.html import escape, strip_tags from django.utils.translation import gettext_lazy as _ diff --git a/app/feeswapper/management/commands/feeSwapper.py b/app/feeswapper/management/commands/feeSwapper.py index 55bedbe114d..b9d0a0f4332 100644 --- a/app/feeswapper/management/commands/feeSwapper.py +++ b/app/feeswapper/management/commands/feeSwapper.py @@ -22,7 +22,7 @@ import warnings from django.conf import settings -from django.core.management.base import BaseCommand, CommandError +from django.core.management.base import BaseCommand from django.utils.timezone import now import requests diff --git a/app/gas/management/commands/output_gas_viz.py b/app/gas/management/commands/output_gas_viz.py index 1549356cf26..5d7abaf9321 100644 --- a/app/gas/management/commands/output_gas_viz.py +++ b/app/gas/management/commands/output_gas_viz.py @@ -9,7 +9,6 @@ from boto.s3.key import Key from gas.models import GasProfile from numpy import array -from perftools.models import JSONStore def convert_to_movie(): @@ -65,7 +64,6 @@ class Command(BaseCommand): help = 'gets observations and visualizes them in 3d' def handle(self, *args, **options): - from mpl_toolkits.mplot3d import Axes3D import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt diff --git a/app/git/tests/test_utils.py b/app/git/tests/test_utils.py index 6f2471f025e..93a70659c07 100644 --- a/app/git/tests/test_utils.py +++ b/app/git/tests/test_utils.py @@ -26,10 +26,9 @@ import responses from git.utils import ( - BASE_URI, HEADERS, JSON_HEADER, TOKEN_URL, build_auth_dict, delete_issue_comment, get_auth_url, get_github_emails, - get_github_primary_email, get_github_user_data, get_github_user_token, get_issue_comments, - get_issue_timeline_events, get_user, is_github_token_valid, org_name, patch_issue_comment, post_issue_comment, - post_issue_comment_reaction, repo_url, reset_token, revoke_token, search, + HEADERS, TOKEN_URL, build_auth_dict, delete_issue_comment, get_github_emails, get_github_primary_email, + get_issue_comments, get_issue_timeline_events, is_github_token_valid, org_name, patch_issue_comment, + post_issue_comment, post_issue_comment_reaction, repo_url, reset_token, revoke_token, ) from test_plus.test import TestCase @@ -66,20 +65,6 @@ def test_org_name(self): assert org_name('https://github.com/gitcoinco/web/issues/1') == 'gitcoinco' assert org_name('https://github.com/gitcoinco/web/issues/1/') == 'gitcoinco' - @responses.activate - def test_search(self): - """Test the github utility search method.""" - url = 'https://api.github.com/search/users' - responses.add(responses.GET, url, json={'total_count': '0'}, status=200) - query = 'a-non-existent-test-string' - params = {'q': query, 'sort': 'updated'} - result = search(query) - params = urlencode(params, quote_via=quote_plus) - - assert result == {'total_count': '0'} - assert responses.calls[0].request.url == url + '?' + params - assert responses.calls[0].response.text == '{"total_count": "0"}' - @responses.activate def test_revoke_token(self): """Test the github utility revoke_token method.""" @@ -108,47 +93,6 @@ def test_reset_token(self): assert result == self.user_oauth_token assert result_not_found == '' - @responses.activate - def test_get_github_user_token(self): - """Test the github utility get_github_user_token method.""" - data = {'access_token': self.user_oauth_token, 'scope': 'read:user,user:email'} - params = { - 'code': self.callback_code, - 'client_id': settings.GITHUB_CLIENT_ID, - 'client_secret': settings.GITHUB_CLIENT_SECRET, - } - params = urlencode(params, quote_via=quote_plus) - url = settings.GITHUB_TOKEN_URL + '?' + params - responses.add(responses.GET, settings.GITHUB_TOKEN_URL, json=data, headers=JSON_HEADER, status=200) - responses.add(responses.GET, settings.GITHUB_TOKEN_URL, json={}, headers=JSON_HEADER, status=200) - result = get_github_user_token(self.callback_code) - result_no_token = get_github_user_token(self.callback_code) - - assert responses.calls[0].request.url == url - assert responses.calls[1].request.url == url - assert result == self.user_oauth_token - assert result_no_token is None - - @responses.activate - def test_get_github_user_data(self): - """Test the github utility get_github_user_data method.""" - headers = dict({'Authorization': f'token {self.user_oauth_token}'}, **JSON_HEADER) - data = {'login': 'gitcoin'} - responses.add(responses.GET, 'https://api.github.com/user', json=data, headers=headers, status=200) - result = get_github_user_data(self.user_oauth_token) - - assert result == data - - @responses.activate - def test_get_github_user_data_failure(self): - """Test the github utility get_github_user_data method.""" - headers = dict({'Authorization': f'token {self.user_oauth_token}'}, **JSON_HEADER) - data = {'login': 'gitcoin'} - responses.add(responses.GET, 'https://api.github.com/user', json=data, headers=headers, status=404) - result = get_github_user_data(self.user_oauth_token) - - assert result == {} - @responses.activate def test_is_github_token_valid(self): """Test the github utility is_github_token_valid method.""" @@ -167,141 +111,122 @@ def test_is_github_token_valid(self): assert return_valid is True assert return_expired is True - @responses.activate - def test_get_github_primary_email(self): - """Test the github utility get_github_primary_email method.""" - data = [{'primary': True, 'email': 'test@gitcoin.co'}, {'email': 'test2@gitcoin.co'}] - url = 'https://api.github.com/user/emails' - responses.add(responses.GET, url, json=data, headers=HEADERS, status=200) - responses.add(responses.GET, url, json=data, headers=HEADERS, status=404) - email = get_github_primary_email(self.user_oauth_token) - no_email = get_github_primary_email(self.user_oauth_token) - - assert email == 'test@gitcoin.co' - assert no_email == '' - - @responses.activate - def test_get_github_emails(self): - """Test the github utility get_github_emails method.""" - headers = dict({'Authorization': f'token {self.user_oauth_token}'}, **JSON_HEADER) - data = [{'email': 'test@gitcoin.co'}, {'email': 'test2@gitcoin.co'}, {'email': 'testing@noreply.github.com'}] - url = 'https://api.github.com/user/emails' - responses.add(responses.GET, url, json=data, headers=headers, status=200) - responses.add(responses.GET, url, json=data, headers=headers, status=404) - emails = get_github_emails(self.user_oauth_token) - no_emails = get_github_emails(self.user_oauth_token) - - assert responses.calls[0].request.url == url - assert emails == ['test@gitcoin.co', 'test2@gitcoin.co'] - assert no_emails == [] - - @responses.activate - def test_get_issue_comments(self): - """Test the github utility get_issue_comments method.""" - params = {'sort': 'created', 'direction': 'desc', 'per_page': 100, } - params = urlencode(params, quote_via=quote_plus) - owner = 'gitcoinco' - repo = 'web' - url = f'https://api.github.com/repos/{owner}/{repo}/issues/comments?' + params - responses.add(responses.GET, url, headers=HEADERS, json={}, status=200) - get_issue_comments(owner, repo) - - assert responses.calls[0].request.url == url - - @responses.activate - def test_get_issue_comments_issue(self): - """Test the github utility get_issue_comments_issue method.""" - params = {'sort': 'created', 'direction': 'desc', 'per_page': 100, } - params = urlencode(params, quote_via=quote_plus) - owner = 'gitcoinco' - repo = 'web' - issue = 1 - url = f'https://api.github.com/repos/{owner}/{repo}/issues/{issue}/comments' - url = url + '?' + params - responses.add(responses.GET, url, headers=HEADERS, json={}, status=200) - get_issue_comments(owner, repo, issue) - - assert responses.calls[0].request.url == url - - @responses.activate - def test_get_issue_timeline_events(self): - """Test the github utility get_issue_timeline_events method.""" - params = {'sort': 'created', 'direction': 'desc', 'per_page': 100, 'page': 1} - params = urlencode(params, quote_via=quote_plus) - owner = 'gitcoinco' - repo = 'web' - issue = 1 - url = f'https://api.github.com/repos/{owner}/{repo}/issues/{issue}/timeline' - url = url + '?' + params - responses.add(responses.GET, url, headers=HEADERS, json={}, status=200) - get_issue_timeline_events(owner, repo, issue) - - assert responses.calls[0].request.url == url - - @responses.activate - def test_get_user(self): - """Test the github utility get_user method.""" - url = 'https://api.github.com/users/gitcoin?per_page=100' - responses.add(responses.GET, url, headers=HEADERS, json={}, status=200) - get_user('@gitcoin') - - assert responses.calls[0].request.url == url - - @responses.activate - def test_get_user_subpath(self): - """Test the github utility get_user method with a subpath.""" - url = 'https://api.github.com/users/gitcoin/test?per_page=100' - responses.add(responses.GET, url, headers=HEADERS, json={}, status=200) - get_user('@gitcoin', '/test') - - assert responses.calls[0].request.url == url - - @responses.activate - def test_post_issue_comment(self): - """Test the github utility post_issue_comment method.""" - owner = 'gitcoinco' - repo = 'web' - issue_num = 1 - url = f'https://api.github.com/repos/{owner}/{repo}/issues/{issue_num}/comments' - responses.add(responses.POST, url, headers=HEADERS, json={}, status=200) - post_issue_comment(owner, repo, issue_num, 'A comment.') - - assert responses.calls[0].request.url == url - - @responses.activate - def test_patch_issue_comment(self): - """Test the github utility patch_issue_comment method.""" - comment_id = 1 - owner = 'gitcoinco' - repo = 'web' - url = f'https://api.github.com/repos/{owner}/{repo}/issues/comments/{comment_id}' - responses.add(responses.PATCH, url, headers=HEADERS, json={}, status=200) - result = patch_issue_comment(comment_id, owner, repo, 'A comment.') - - assert responses.calls[0].request.url == url - assert result == {} - - @responses.activate - def test_delete_issue_comment(self): - """Test the github utility delete_issue_comment method.""" - comment_id = 1 - owner = 'gitcoinco' - repo = 'web' - url = f'https://api.github.com/repos/{owner}/{repo}/issues/comments/{comment_id}' - responses.add(responses.DELETE, url, headers=HEADERS, json={}, status=200) - result = delete_issue_comment(comment_id, owner, repo) - - assert responses.calls[0].request.url == url - assert result == {} - - @responses.activate - def test_post_issue_comment_reaction(self): - """Test the github utility post_issue_comment_reaction method.""" - comment_id = 1 - owner = 'gitcoinco' - repo = 'web' - url = f'https://api.github.com/repos/{owner}/{repo}/issues/comments/{comment_id}/reactions' - responses.add(responses.POST, url, headers=HEADERS, json={}, status=200) - post_issue_comment_reaction(owner, repo, comment_id, 'A comment.') - - assert responses.calls[0].request.url == url + # @responses.activate + # def test_get_github_primary_email(self): + # """Test the github utility get_github_primary_email method.""" + # data = [{'primary': True, 'email': 'test@gitcoin.co'}, {'email': 'test2@gitcoin.co'}] + # url = 'https://api.github.com/user/emails' + # responses.add(responses.GET, url, json=data, headers=HEADERS, status=200) + # responses.add(responses.GET, url, json=data, headers=HEADERS, status=404) + # email = get_github_primary_email(self.user_oauth_token) + # no_email = get_github_primary_email(self.user_oauth_token) + + # assert email == 'test@gitcoin.co' + # assert no_email == '' + + # @responses.activate + # def test_get_github_emails(self): + # headers = dict({'Authorization': f'token {self.user_oauth_token}'}) + # data = [{'email': 'test@gitcoin.co'}, {'email': 'test2@gitcoin.co'}, {'email': 'testing@noreply.github.com'}] + # url = 'https://api.github.com/user/emails' + # responses.add(responses.GET, url, json=data, headers=headers, status=200) + # responses.add(responses.GET, url, json=data, headers=headers, status=404) + # emails = get_github_emails(self.user_oauth_token) + # no_emails = get_github_emails(self.user_oauth_token) + + # assert responses.calls[0].request.url == url + # assert emails == ['test@gitcoin.co', 'test2@gitcoin.co'] + # assert no_emails == [] + + # @responses.activate + # def test_get_issue_comments(self): + # """Test the github utility get_issue_comments method.""" + # params = {'sort': 'created', 'direction': 'desc', 'per_page': 100, } + # params = urlencode(params, quote_via=quote_plus) + # owner = 'gitcoinco' + # repo = 'web' + # url = f'https://api.github.com/repos/{owner}/{repo}/issues/comments?' + params + # responses.add(responses.GET, url, headers=HEADERS, json={}, status=200) + # get_issue_comments(owner, repo) + + # assert responses.calls[0].request.url == url + + # @responses.activate + # def test_get_issue_comments_issue(self): + # """Test the github utility get_issue_comments_issue method.""" + # params = {'sort': 'created', 'direction': 'desc', 'per_page': 100, } + # params = urlencode(params, quote_via=quote_plus) + # owner = 'gitcoinco' + # repo = 'web' + # issue = 1 + # url = f'https://api.github.com/repos/{owner}/{repo}/issues/{issue}/comments' + # url = url + '?' + params + # responses.add(responses.GET, url, headers=HEADERS, json={}, status=200) + # get_issue_comments(owner, repo, issue) + + # assert responses.calls[0].request.url == url + + # @responses.activate + # def test_get_issue_timeline_events(self): + # """Test the github utility get_issue_timeline_events method.""" + # params = {'sort': 'created', 'direction': 'desc', 'per_page': 100, 'page': 1} + # params = urlencode(params, quote_via=quote_plus) + # owner = 'gitcoinco' + # repo = 'web' + # issue = 1 + # url = f'https://api.github.com/repos/{owner}/{repo}/issues/{issue}/timeline' + # url = url + '?' + params + # responses.add(responses.GET, url, headers=HEADERS, json={}, status=200) + # get_issue_timeline_events(owner, repo, issue) + + # assert responses.calls[0].request.url == url + + # @responses.activate + # def test_post_issue_comment(self): + # """Test the github utility post_issue_comment method.""" + # owner = 'gitcoinco' + # repo = 'web' + # issue_num = 1 + # url = f'https://api.github.com/repos/{owner}/{repo}/issues/{issue_num}/comments' + # responses.add(responses.POST, url, headers=HEADERS, json={}, status=200) + # post_issue_comment(owner, repo, issue_num, 'A comment.') + + # assert responses.calls[0].request.url == url + + # @responses.activate + # def test_patch_issue_comment(self): + # """Test the github utility patch_issue_comment method.""" + # comment_id = 1 + # owner = 'gitcoinco' + # repo = 'web' + # url = f'https://api.github.com/repos/{owner}/{repo}/issues/comments/{comment_id}' + # responses.add(responses.PATCH, url, headers=HEADERS, json={}, status=200) + # result = patch_issue_comment(comment_id, owner, repo, 'A comment.') + + # assert responses.calls[0].request.url == url + # assert result == {} + + # @responses.activate + # def test_delete_issue_comment(self): + # """Test the github utility delete_issue_comment method.""" + # comment_id = 1 + # owner = 'gitcoinco' + # repo = 'web' + # url = f'https://api.github.com/repos/{owner}/{repo}/issues/comments/{comment_id}' + # responses.add(responses.DELETE, url, headers=HEADERS, json={}, status=200) + # result = delete_issue_comment(comment_id, owner, repo) + + # assert responses.calls[0].request.url == url + # assert result == {} + + # @responses.activate + # def test_post_issue_comment_reaction(self): + # """Test the github utility post_issue_comment_reaction method.""" + # comment_id = 1 + # owner = 'gitcoinco' + # repo = 'web' + # url = f'https://api.github.com/repos/{owner}/{repo}/issues/comments/{comment_id}/reactions' + # responses.add(responses.POST, url, headers=HEADERS, json={}, status=200) + # post_issue_comment_reaction(owner, repo, comment_id, 'A comment.') + + # assert responses.calls[0].request.url == url diff --git a/app/git/utils.py b/app/git/utils.py index ff7bb200806..0a3b90751ff 100644 --- a/app/git/utils.py +++ b/app/git/utils.py @@ -20,7 +20,6 @@ import json import logging from datetime import timedelta -from json import JSONDecodeError from urllib.parse import quote_plus, urlencode from django.conf import settings @@ -35,13 +34,9 @@ logger = logging.getLogger(__name__) -_AUTH = (settings.GITHUB_API_USER, settings.GITHUB_API_TOKEN) BASE_URI = settings.BASE_URL.rstrip('/') HEADERS = {'Accept': 'application/vnd.github.v3+json'} -JSON_HEADER = {'Accept': 'application/json', 'User-Agent': settings.GITHUB_APP_NAME, 'Origin': settings.BASE_URL} -TIMELINE_HEADERS = {'Accept': 'application/vnd.github.mockingbird-preview'} TOKEN_URL = '{api_url}/applications/{client_id}/token' -PER_PAGE_LIMIT = 100 def github_connect(token=None): @@ -63,7 +58,7 @@ def github_connect(token=None): return github_client -def get_gh_issue_details(org, repo, issue_num, token=None): +def get_issue_details(org, repo, issue_num, token=None): details = {'keywords': []} try: gh_client = github_connect(token) @@ -74,6 +69,7 @@ def get_gh_issue_details(org, repo, issue_num, token=None): for k, _ in langs.items(): details['keywords'].append(k) details['title'] = issue_details.title + details['body'] = issue_details.body details['description'] = issue_details.body.replace('\n', '').strip() details['state'] = issue_details.state if issue_details.state == 'closed': @@ -84,7 +80,7 @@ def get_gh_issue_details(org, repo, issue_num, token=None): return details -def get_gh_issue_state(org, repo, issue_num): +def get_issue_state(org, repo, issue_num): gh_client = github_connect() org_user = gh_client.get_user(login=org) repo_obj = org_user.get_repo(repo) @@ -106,31 +102,6 @@ def build_auth_dict(): } -def check_github(profile): - """Check whether or not the provided username is present in the payload as active user. - - Args: - profile (str): The profile username to be validated. - - Returns: - dict: A dictionary containing status and user data. - - """ - user = search_github(profile + ' in:login type:user') - response = {'status': 200, 'user': False} - user_items = user.get('items', []) - - if user_items and user_items[0].get('login', '').lower() == profile.lower(): - response['user'] = user_items[0] - return response - - -def search_github(q): - params = (('q', q), ('sort', 'updated'),) - response = requests.get('https://api.github.com/search/users', headers=HEADERS, params=params) - return response.json() - - def is_github_token_valid(oauth_token=None, last_validated=None): """Check whether or not a Github OAuth token is valid. @@ -189,7 +160,6 @@ def revoke_token(oauth_token): url, data=json.dumps({'access_token': oauth_token}), auth=_auth, - headers=HEADERS ) if response.status_code == 204: return True @@ -249,37 +219,6 @@ def get_auth_url(redirect_uri='/'): return settings.GITHUB_AUTH_BASE_URL + f'?{auth_url}' -def get_github_user_token(code, **kwargs): - """Get the Github authorization token.""" - _params = {'code': code, 'client_id': settings.GITHUB_CLIENT_ID, 'client_secret': settings.GITHUB_CLIENT_SECRET} - # Add additional parameters to the request paramaters. - _params.update(kwargs) - response = requests.get(settings.GITHUB_TOKEN_URL, headers=JSON_HEADER, params=_params) - response = response.json() - scope = response.get('scope', None) - if scope: - access_token = response.get('access_token', None) - return access_token - return None - - -def get_github_user_data(oauth_token): - """Get the user's github profile information. - - Args: - oauth_token (str): The Github OAuth2 token to use for authentication. - - Returns: - requests.Response: The Github user response. - - """ - headers = dict({'Authorization': f'token {oauth_token}'}, **JSON_HEADER) - response = requests.get('https://api.github.com/user', headers=headers) - if response.status_code == 200: - return response.json() - return {} - - def get_github_primary_email(oauth_token): """Get the primary email address associated with the github profile. @@ -290,14 +229,14 @@ def get_github_primary_email(oauth_token): str: The user's primary github email address. """ - headers = dict({'Authorization': f'token {oauth_token}'}, **JSON_HEADER) - response = requests.get('https://api.github.com/user/emails', headers=headers) - - if response.status_code == 200: - emails = response.json() + try: + gh_client = github_connect(oauth_token) + emails = gh_client.get_user().get_emails() for email in emails: if email.get('primary'): return email.get('email', '') + except Exception as e: + logger.error(e) return '' @@ -309,33 +248,31 @@ def get_github_event_emails(oauth_token, username): oauth_token (str): The Github OAuth2 token to use for authentication. Returns: - list of str: All of the user's associated email from github. + list of str: All of the user's associated emails from github. """ emails = [] - headers = JSON_HEADER - if oauth_token: - headers = dict({'Authorization': f'token {oauth_token}'}, **JSON_HEADER) - response = requests.get(f'https://api.github.com/users/{username}/events/public', headers=headers) - userinfo = get_user(username) - user_name = userinfo.get('name', '') + user_name = userinfo.name - if response.status_code == 200: - events = response.json() + try: + gh_client = github_connect(oauth_token) + events = gh_client.get_user(username).get_public_events() for event in events: - payload = event.get('payload', {}) + payload = event.payload if event.payload else {} for commit in payload.get('commits', []): author = commit.get('author', {}) - email = author.get('email', {}) - name = author.get('name', {}) + email = author.get('email', '') + name = author.get('name', '') if name and username and user_name: append_email = name.lower() == username.lower() or name.lower() == user_name.lower() \ - and email and 'noreply.github.com' not in email + and email and 'noreply.github.com' not in email if append_email: emails.append(email) + except GithubException as e: + logger.error(e) - return set(emails) + return list(set(emails)) def get_github_emails(oauth_token): @@ -349,15 +286,16 @@ def get_github_emails(oauth_token): """ emails = [] - headers = dict({'Authorization': f'token {oauth_token}'}, **JSON_HEADER) - response = requests.get('https://api.github.com/user/emails', headers=headers) + try: + gh_client = github_connect(oauth_token) + email_data = gh_client.get_user().get_emails() - if response.status_code == 200: - email_data = response.json() for email in email_data: email_address = email.get('email') if email_address and 'noreply.github.com' not in email_address: emails.append(email_address) + except GithubException as e: + logger.error(e) return emails @@ -390,26 +328,6 @@ def get_emails_master(username): return list(set(emails)) -def search(query): - """Search for a user on github. - - Args: - query (str): The query text to match. - - Returns: - request.Response: The github search response. - - """ - params = (('q', query), ('sort', 'updated'),) - - try: - response = requests.get('https://api.github.com/search/users', auth=_AUTH, headers=HEADERS, params=params) - return response.json() - except Exception as e: - logger.error("could not search GH - Reason: %s - query: %s", e, query) - return {} - - def search_user(query, token=None): """Search for a user on github. @@ -423,13 +341,7 @@ def search_user(query, token=None): """ paginated_list = search_users(query, token) try: - user_obj = paginated_list[0] - return { - 'avatar_url': user_obj.avatar_url, - 'login': user_obj.login, - 'text': user_obj.login, - 'email': user_obj.email, - } + return paginated_list[0] except IndexError: pass except Exception as e: @@ -438,7 +350,7 @@ def search_user(query, token=None): def search_users(query, token=None): - """Search for a user on github. + """Search for users on github. Args: query (str): The query text to match. @@ -448,94 +360,97 @@ def search_users(query, token=None): github.PaginatedList: The pygithub paginator object of all results if many True. """ - gh_client = github_connect(token) try: + gh_client = github_connect(token) paginated_list = gh_client.search_users(query) return paginated_list - except Exception as e: + except GithubException as e: logger.error(e) return [] -def get_issue_comments(owner, repo, issue=None, comment_id=None): +def get_issue_comments(owner, repo, issue=None, comment_id=None, page=1): """Get the comments from issues on a respository. - PLEASE NOTE CURRENT LIMITATION OF 100 COMMENTS. Args: owner (str): Owner of the repo repo (str): Name of the repo issue (int): Issue number (optional) + comment_id (int): Comment ID (optional) + page (int): Page number (optional) Returns: - requests.Response: The GitHub comments response. + github.PaginatedList.PaginatedList / github.IssueComment.IssueComment: The GitHub comments response. + """ - params = { - 'sort': 'created', - 'direction': 'desc', - 'per_page': 100, - # TODO traverse/concat pages: https://developer.github.com/v3/guides/traversing-with-pagination/ - } - if issue: - if comment_id: - url = f'https://api.github.com/repos/{owner}/{repo}/issues/comments/{comment_id}' - else: - url = f'https://api.github.com/repos/{owner}/{repo}/issues/{issue}/comments' - else: - url = f'https://api.github.com/repos/{owner}/{repo}/issues/comments' + page -= 1 + if page < 0: page = 0 + + gh_client = github_connect() + paginated_list = [] try: - response = requests.get(url, auth=_AUTH, headers=HEADERS, params=params) - return response.json() + repo = gh_client.get_repo(f'{owner}/{repo}') + if issue: + if comment_id: + issue_comment = repo.get_issue(issue).get_comment(comment_id) + return issue_comment + else: + paginated_list = repo.get_issue(issue).get_comments().get_page(page) + else: + paginated_list = repo.get_issues_comments(sort='created', direction='desc').get_page(page) + + return paginated_list except Exception as e: logger.error( - "could not get issue comments - Reason: %s - owner: %s repo: %s issue: %s comment_id: %s status code: %s", - e, owner, repo, issue, comment_id, response.status_code + "could not get issues - Reason: %s - owner: %s repo: %s page: %s status_code: %s", + e.data['message'], owner, repo, page, e.status ) - return {} + return {'status': e.status, 'message': e.data['message']} def get_issues(owner, repo, page=1, state='open'): - """Get the open issues on a respository.""" - params = {'state': state, 'sort': 'created', 'direction': 'desc', 'page': page, 'per_page': 100, } - url = f'https://api.github.com/repos/{owner}/{repo}/issues' + """Get the issues on a respository.""" + + page -= 1 + if page < 0: page = 0 try: - response = requests.get(url, auth=_AUTH, headers=HEADERS, params=params) - return response.json() + gh_client = github_connect() + paginated_list = gh_client.get_repo(f'{owner}/{repo}').get_issues( + state=state, sort='created', direction='desc').get_page(page) + return paginated_list except Exception as e: logger.error( - "could not get issues - Reason: %s - owner: %s repo: %s page: %s state: %s status code: %s", - e, owner, repo, page, state, response.status_code + "could not get issues - Reason: %s - owner: %s repo: %s page: %s state: %s status_code: %s", + e.data['message'], owner, repo, page, state, e.status ) - return {} + return [] def get_issue_timeline_events(owner, repo, issue, page=1): """Get the timeline events for a given issue. PLEASE NOTE CURRENT LIMITATION OF 100 EVENTS. - PLEASE NOTE GITHUB API FOR THIS IS SUBJECT TO CHANGE. - (See https://developer.github.com/changes/2016-05-23-timeline-preview-api/ for more info.) - Args: owner (str): Owner of the repo repo (str): Name of the repo issue (int): Issue number Returns: - requests.Response: The GitHub timeline response. + github.PaginatedList of githubTimelineEvent: The GitHub timeline response list. """ - params = {'sort': 'created', 'direction': 'desc', 'per_page': 100, 'page': page, } - url = f'https://api.github.com/repos/{owner}/{repo}/issues/{issue}/timeline' + page -= 1 + if page < 0: page = 0 + + gh_client = github_connect() try: - # Set special header to access timeline preview api - response = requests.get(url, auth=_AUTH, headers=TIMELINE_HEADERS, params=params) - return response.json() - except Exception as e: - logger.error( - "could not get timeline events - Reason: %s - %s %s %s %s", e, owner, repo, issue, response.status_code - ) - return {} + repo = gh_client.get_repo(f'{owner}/{repo}') + paginated_list = repo.get_issue(int(issue)).get_timeline().get_page(page) + return paginated_list + except GithubException as e: + logger.error(e) + return [] def get_interested_actions(github_url, username, email=''): @@ -549,6 +464,7 @@ def get_interested_actions(github_url, username, email=''): page = 1 while should_continue_loop: actions = get_issue_timeline_events(owner, repo, issue_num, page) + actions = [action._rawData for action in actions] should_continue_loop = len(actions) == 100 all_actions = all_actions + actions page += 1 @@ -573,6 +489,7 @@ def get_interested_actions(github_url, username, email=''): page = 1 while should_continue_loop: pr_actions = get_issue_timeline_events(pr_repo_owner, pr_repo, pr_num, page) + pr_actions = [action._rawData for action in pr_actions] should_continue_loop = len(pr_actions) == 100 all_pr_actions = all_pr_actions + pr_actions page += 1 @@ -596,139 +513,86 @@ def get_interested_actions(github_url, username, email=''): return actions_by_interested_party -def get_user(user, sub_path='', scope='', scoped=False, auth=None): - if not auth: - auth = _AUTH +def get_user(user=None, token=None): """Get the github user details.""" - if scope is not '': - url = f'https://api.github.com/user/{scope}?per_page={PER_PAGE_LIMIT}' - elif scoped: - url = f'https://api.github.com/user' - else: - user = user.replace('@', '') - url = f'https://api.github.com/users/{user}{sub_path}?per_page={PER_PAGE_LIMIT}' - response = requests.get(url, auth=auth, headers=HEADERS) - try: - response_dict = response.json() - except JSONDecodeError: - response_dict = {} - return response_dict - - -def get_organization(org, sub_path='', auth=None): - """Get the github organization details.""" - if not auth: - auth = _AUTH - org = org.replace('@', '') - url = f'https://api.github.com/orgs/{org}{sub_path}?per_page={PER_PAGE_LIMIT * 2}' - response = requests.get(url, auth=auth, headers=HEADERS) - try: - response_dict = response.json() - except JSONDecodeError: - response_dict = {} - return response_dict - - -def get_repo(repo_full_name, sub_path='', auth=None, is_user=False): - """Get the github repo details.""" - if not auth: - auth = _AUTH - repo_full_name = repo_full_name.replace('@', '') - if is_user: - url = f'https://api.github.com/user/repos?per_page={PER_PAGE_LIMIT}' - else: - url = f'https://api.github.com/repos/{repo_full_name}{sub_path}?per_page={PER_PAGE_LIMIT}' - - response = requests.get(url, auth=auth, headers=HEADERS) + gh_client = github_connect(token) + return gh_client.get_user(user) if user else gh_client.get_user() + except GithubException as e: + logger.error(e) - try: - response_dict = response.json() - except JSONDecodeError: - response_dict = {} - return response_dict + return None def get_notifications(): - """Get the github notifications.""" - url = f'https://api.github.com/notifications?all=1' - try: - response = requests.get(url, auth=_AUTH, headers=HEADERS) - return response.json() - except Exception as e: - logger.error("could not get notifications - Reason: %s", e) - return {} - - -def get_gh_notifications(login=None): """Get the Github notifications for Gitcoin Bot.""" gh_client = github_connect() - if login: - repo_user = gh_client.get_user(login=login) - else: + try: repo_user = gh_client.get_user() - notifications = repo_user.get_notifications(all=True) - return notifications + paginated_list = repo_user.get_notifications(all=True) + return paginated_list + except GithubException as e: + logger.error(e) + return [] def post_issue_comment(owner, repo, issue_num, comment): - """Post a comment on an issue.""" - url = f'https://api.github.com/repos/{owner}/{repo}/issues/{issue_num}/comments' + """Post a comment on an issue. + + Args: + owner (str): Owner of the repo + repo (str): Name of the repo + issue_num (int): Issue number + comment (int): Comment Body + + Returns: + github.IssueComment.IssueComment: The GitHub created comment. + + """ + gh_client = github_connect() try: - response = requests.post(url, data=json.dumps({'body': comment}), auth=_AUTH) - return response.json() - except Exception as e: - logger.error( - "could not post issue comment - Reason: %s - %s %s %s %s", e, comment, owner, repo, response.status_code - ) - return {} + repo = gh_client.get_repo(f'{owner}/{repo}') + issue_comment = repo.get_issue(issue_num).create_comment(comment) + return issue_comment + except GithubException as e: + logger.error(e) + return {} def patch_issue_comment(comment_id, owner, repo, comment): """Update a comment on an issue via patch.""" - url = f'https://api.github.com/repos/{owner}/{repo}/issues/comments/{comment_id}' + gh_client = github_connect() try: - response = requests.patch(url, data=json.dumps({'body': comment}), auth=_AUTH) - if response.status_code == 200: - return response.json() - except Exception as e: - logger.error( - "could not patch issue comment - Reason: %s - %s %s %s %s", e, comment_id, owner, repo, response.status_code - ) - return {} + repo = gh_client.get_repo(f'{owner}/{repo}') + issue_comment = repo.get_comment(comment_id).edit(comment) + return issue_comment + except GithubException as e: + logger.error(e) + return {} def delete_issue_comment(comment_id, owner, repo): """Remove a comment on an issue via delete.""" - url = f'https://api.github.com/repos/{owner}/{repo}/issues/comments/{comment_id}' + gh_client = github_connect() try: - response = requests.delete(url, auth=_AUTH) - return response.json() - except ValueError: - logger.error( - "could not delete issue comment because JSON response could not be decoded: %s %s %s %s %s", - comment_id, owner, repo, response.status_code, response.text - ) - except Exception as e: - logger.error( - "could not delete issue comment - Reason: %s: %s %s %s %s %s", - e, comment_id, owner, repo, response.status_code, response.text - ) - return {} + repo = gh_client.get_repo(f'{owner}/{repo}') + issue_comment = repo.get_comment(comment_id).delete() + return issue_comment + except GithubException as e: + logger.error(e) + return {} def post_issue_comment_reaction(owner, repo, comment_id, content): """React to an issue comment.""" - url = f'https://api.github.com/repos/{owner}/{repo}/issues/comments/{comment_id}/reactions' + gh_client = github_connect() try: - response = requests.post(url, data=json.dumps({'content': content}), auth=_AUTH, headers=HEADERS) - return response.json() - except Exception as e: - logger.error( - "could not post issue reaction - Reason: %s - %s %s %s %s", - e, comment_id, owner, repo, response.status_code - ) - return {} + repo = gh_client.get_repo(f'{owner}/{repo}') + reaction = repo.get_comment(comment_id).create_reaction(content) + return reaction + except GithubException as e: + logger.error(e) + return {} def get_url_dict(issue_url): @@ -825,8 +689,9 @@ def issue_number(issue_url): def get_current_ratelimit(token=None): """Get the current Github API ratelimit for the provided token.""" - gh_client = github_connect(token) try: + gh_client = github_connect(token) return gh_client.get_rate_limit() - except GithubException: + except GithubException as e: + logger.error(e) return {} diff --git a/app/gitcoinbot/management/commands/get_notifications.py b/app/gitcoinbot/management/commands/get_notifications.py index ec8e3251294..1699630e1ef 100644 --- a/app/gitcoinbot/management/commands/get_notifications.py +++ b/app/gitcoinbot/management/commands/get_notifications.py @@ -24,7 +24,7 @@ from django.utils import timezone from git.utils import ( - get_gh_notifications, get_issue_comments, issue_number, org_name, post_issue_comment_reaction, repo_name, + get_issue_comments, get_notifications, issue_number, org_name, post_issue_comment_reaction, repo_name, ) from github import RateLimitExceededException @@ -34,7 +34,7 @@ class Command(BaseCommand): help = 'gets gitcoinbot notifications and responds if the user needs to install the app to get them to work' def handle(self, *args, **options): - notifications = get_gh_notifications() + notifications = get_notifications() try: print('Notifications Count: ', notifications.totalCount) for notification in notifications: @@ -54,14 +54,14 @@ def handle(self, *args, **options): continue _comment_id = latest_comment_url.split('/')[-1] comment = get_issue_comments(_org_name, _repo_name, _issue_number, _comment_id) - does_mention_gitcoinbot = settings.GITHUB_API_USER in comment.get('body', '') - if comment.get('message', '') == "Not Found": + does_mention_gitcoinbot = settings.GITHUB_API_USER in comment.body + if isinstance(comment, dict) and comment.get('message', '') == 'Not Found': print("comment was not found") elif not does_mention_gitcoinbot: print("does not mention gitcoinbot") else: - comment_from = comment['user']['login'] - num_reactions = comment['reactions']['total_count'] + comment_from = comment.user.login + num_reactions = comment.get_reactions().totalCount print(_org_name, _repo_name, _issue_number, _comment_id, num_reactions, comment_from) is_from_gitcoinbot = settings.GITHUB_API_USER in comment_from if num_reactions == 0 and not is_from_gitcoinbot: diff --git a/app/grants/clr.py b/app/grants/clr.py index e8d682eee0f..5e711cdcc38 100644 --- a/app/grants/clr.py +++ b/app/grants/clr.py @@ -18,20 +18,11 @@ """ import copy -import datetime as dt -import json -import math -import time -from itertools import combinations -from django.conf import settings from django.utils import timezone import numpy as np -import pytz from grants.models import Contribution, Grant, GrantCollection -from marketing.models import Stat -from perftools.models import JSONStore CLR_PERCENTAGE_DISTRIBUTED = 0 diff --git a/app/grants/forms.py b/app/grants/forms.py deleted file mode 100644 index 50e773299f7..00000000000 --- a/app/grants/forms.py +++ /dev/null @@ -1,37 +0,0 @@ -# -*- coding: utf-8 -*- -"""Define the Grant forms. - -Copyright (C) 2021 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 django import forms -from django.utils.translation import gettext_lazy as _ - -from grants.models import Grant - - -class GrantForm(forms.ModelForm): - """Define the Grant form logic.""" - - class Meta: - """Define the metadata for the Grant model form.""" - - model = Grant - fields = ( - 'title', 'description', 'reference_url', 'github_project_url', 'logo', 'logo_svg', 'admin_address', 'deploy_tx_id', - 'cancel_tx_id', 'amount_received', 'token_address', 'contract_address', 'metadata', 'network', - 'required_gas_price', 'admin_profile', 'team_members' - ) diff --git a/app/grants/management/commands/analytics_clr.py b/app/grants/management/commands/analytics_clr.py index f4299020896..1809d516a0d 100644 --- a/app/grants/management/commands/analytics_clr.py +++ b/app/grants/management/commands/analytics_clr.py @@ -18,14 +18,11 @@ """ -from django.conf import settings from django.core.management.base import BaseCommand from django.utils import timezone -from dashboard.utils import get_tx_status, has_tx_mined from grants.clr import calculate_clr_for_donation, fetch_data, populate_data_for_clr -from grants.models import Contribution, Grant, GrantCLR -from marketing.mails import warn_subscription_failed +from grants.models import GrantCLR def analytics_clr(from_date=None, clr_round=None, network='mainnet'): diff --git a/app/grants/management/commands/build_collection_thumbnails.py b/app/grants/management/commands/build_collection_thumbnails.py index 526e782954e..94090ca1cec 100644 --- a/app/grants/management/commands/build_collection_thumbnails.py +++ b/app/grants/management/commands/build_collection_thumbnails.py @@ -1,4 +1,4 @@ -from django.core.management.base import BaseCommand, CommandError +from django.core.management.base import BaseCommand from grants.models import GrantCollection from grants.tasks import generate_collection_cache diff --git a/app/grants/management/commands/create_anon_donators.py b/app/grants/management/commands/create_anon_donators.py index a890ec8897d..b340bd4e6e0 100644 --- a/app/grants/management/commands/create_anon_donators.py +++ b/app/grants/management/commands/create_anon_donators.py @@ -1,6 +1,5 @@ from django.contrib.auth.models import User from django.core.management.base import BaseCommand -from django.utils import timezone from django.utils.crypto import get_random_string from dashboard.models import Profile diff --git a/app/grants/management/commands/estimate_clr.py b/app/grants/management/commands/estimate_clr.py index 4496d7199cd..e0b6db0c58a 100644 --- a/app/grants/management/commands/estimate_clr.py +++ b/app/grants/management/commands/estimate_clr.py @@ -18,14 +18,11 @@ """ -from django.conf import settings from django.core.management.base import BaseCommand from django.utils import timezone -from dashboard.utils import get_tx_status, has_tx_mined -from grants.models import Contribution, Grant, GrantCLR +from grants.models import GrantCLR from grants.tasks import process_predict_clr -from marketing.mails import warn_subscription_failed class Command(BaseCommand): diff --git a/app/grants/management/commands/estimate_clr_delay.py b/app/grants/management/commands/estimate_clr_delay.py index 5ebaa6c432b..550449d180f 100644 --- a/app/grants/management/commands/estimate_clr_delay.py +++ b/app/grants/management/commands/estimate_clr_delay.py @@ -18,14 +18,10 @@ """ -from django.conf import settings from django.core.management.base import BaseCommand -from django.utils import timezone -from dashboard.utils import get_tx_status, has_tx_mined -from grants.models import Contribution, Grant, GrantCLR +from grants.models import GrantCLR from grants.tasks import recalc_clr -from marketing.mails import warn_subscription_failed class Command(BaseCommand): diff --git a/app/grants/management/commands/get_active_clrs.py b/app/grants/management/commands/get_active_clrs.py index 134fd49ef2d..5faff2aeac0 100644 --- a/app/grants/management/commands/get_active_clrs.py +++ b/app/grants/management/commands/get_active_clrs.py @@ -18,13 +18,9 @@ """ -from django.conf import settings from django.core.management.base import BaseCommand -from django.utils import timezone -from dashboard.utils import get_tx_status, has_tx_mined -from grants.models import Contribution, Grant, GrantCLR -from marketing.mails import warn_subscription_failed +from grants.models import GrantCLR class Command(BaseCommand): diff --git a/app/grants/management/commands/grant_related.py b/app/grants/management/commands/grant_related.py index 08674089361..3e12d9c70d2 100644 --- a/app/grants/management/commands/grant_related.py +++ b/app/grants/management/commands/grant_related.py @@ -18,7 +18,6 @@ """ -import random import time from django.core.management.base import BaseCommand diff --git a/app/grants/management/commands/grants_upcoming.py b/app/grants/management/commands/grants_upcoming.py index bc0feddf845..6199b48ebdd 100644 --- a/app/grants/management/commands/grants_upcoming.py +++ b/app/grants/management/commands/grants_upcoming.py @@ -18,8 +18,6 @@ """ -import random - from django.core.management.base import BaseCommand from django.utils import timezone diff --git a/app/grants/management/commands/ingest_givingblock_txns.py b/app/grants/management/commands/ingest_givingblock_txns.py index 001f6aaa867..bcfa33d4392 100644 --- a/app/grants/management/commands/ingest_givingblock_txns.py +++ b/app/grants/management/commands/ingest_givingblock_txns.py @@ -1,6 +1,3 @@ -import pprint -import time - from django.core.management.base import BaseCommand from django.utils import timezone diff --git a/app/grants/management/commands/ingest_grant_txns.py b/app/grants/management/commands/ingest_grant_txns.py index 738695d0168..b8ffd44f922 100644 --- a/app/grants/management/commands/ingest_grant_txns.py +++ b/app/grants/management/commands/ingest_grant_txns.py @@ -19,7 +19,6 @@ """ import datetime -import os from django.conf import settings from django.core.management.base import BaseCommand @@ -29,9 +28,8 @@ import requests from dashboard.models import Activity, Profile from economy.models import Token -from economy.tx import headers from economy.utils import convert_token_to_usdt -from grants.models import Contribution, Grant, Subscription +from grants.models import Grant, Subscription from perftools.models import JSONStore from web3 import Web3 diff --git a/app/grants/management/commands/payout_round.py b/app/grants/management/commands/payout_round.py index 084b91de27c..37fe27d09dc 100644 --- a/app/grants/management/commands/payout_round.py +++ b/app/grants/management/commands/payout_round.py @@ -16,25 +16,22 @@ ''' -import json import time from django.conf import settings -from django.contrib.contenttypes.models import ContentType -from django.core import management from django.core.management.base import BaseCommand from django.utils import timezone from dashboard.abi import erc20_abi as abi -from dashboard.models import Activity, Earning, Profile -from dashboard.utils import get_tx_status, get_web3, has_tx_mined +from dashboard.models import Activity, Profile +from dashboard.utils import get_web3, has_tx_mined from gas.utils import recommend_min_gas_price_to_confirm_in_time from grants.models import CLRMatch, Contribution, Grant, GrantCLR, Subscription from marketing.mails import ( grant_match_distribution_final_txn, grant_match_distribution_kyc, grant_match_distribution_test_txn, ) from townsquare.models import Comment -from web3 import HTTPProvider, Web3 +from web3 import Web3 WAIT_TIME_BETWEEN_PAYOUTS = 15 diff --git a/app/grants/management/commands/payout_round_noncustodial.py b/app/grants/management/commands/payout_round_noncustodial.py index 42feb704c0d..247778d0f89 100644 --- a/app/grants/management/commands/payout_round_noncustodial.py +++ b/app/grants/management/commands/payout_round_noncustodial.py @@ -27,25 +27,18 @@ # ./manage.py payout_round_noncustodial set_payouts mainnet --clr_pks=131,121,120,119,118 --clr_round=9 --process_all import json -import time from decimal import Decimal from django.conf import settings -from django.contrib.contenttypes.models import ContentType -from django.core import management from django.core.management.base import BaseCommand from django.utils import timezone from dashboard.abi import erc20_abi -from dashboard.models import Activity, Earning, Profile -from dashboard.utils import get_tx_status, get_web3, has_tx_mined -from gas.utils import recommend_min_gas_price_to_confirm_in_time +from dashboard.models import Activity, Profile from grants.models import CLRMatch, Contribution, Grant, GrantCLR, Subscription -from marketing.mails import ( - grant_match_distribution_final_txn, grant_match_distribution_kyc, grant_match_distribution_test_txn, -) +from marketing.mails import grant_match_distribution_final_txn, grant_match_distribution_kyc from townsquare.models import Comment -from web3 import HTTPProvider, Web3 +from web3 import Web3 match_payouts_abi = settings.MATCH_PAYOUTS_ABI match_payouts_address = Web3.toChecksumAddress(settings.MATCH_PAYOUTS_ADDRESS) diff --git a/app/grants/management/commands/reclass_grants.py b/app/grants/management/commands/reclass_grants.py index 45cd0574006..63d4acfbd5c 100644 --- a/app/grants/management/commands/reclass_grants.py +++ b/app/grants/management/commands/reclass_grants.py @@ -18,9 +18,7 @@ """ -from django.conf import settings from django.core.management.base import BaseCommand -from django.utils import timezone from grants.models import * diff --git a/app/grants/management/commands/send_grants_contributions_emails.py b/app/grants/management/commands/send_grants_contributions_emails.py index fffd662b981..709b4df4503 100644 --- a/app/grants/management/commands/send_grants_contributions_emails.py +++ b/app/grants/management/commands/send_grants_contributions_emails.py @@ -21,7 +21,7 @@ from django.utils import timezone from app.utils import get_default_network -from grants.models import Contribution, Grant, Subscription +from grants.models import Contribution from grants.tasks import process_new_contributions_email diff --git a/app/grants/management/commands/sync_pending_contributions.py b/app/grants/management/commands/sync_pending_contributions.py index 2c453abed57..f9ca8d6a06f 100644 --- a/app/grants/management/commands/sync_pending_contributions.py +++ b/app/grants/management/commands/sync_pending_contributions.py @@ -20,11 +20,10 @@ from datetime import timedelta -from django.conf import settings from django.core.management.base import BaseCommand from django.utils import timezone -from grants.models import Contribution, Grant +from grants.models import Contribution from grants.tasks import update_grant_metadata from grants.utils import sync_payout diff --git a/app/grants/management/commands/update_brightid_status.py b/app/grants/management/commands/update_brightid_status.py index a0c736dd01c..a67457fbc47 100644 --- a/app/grants/management/commands/update_brightid_status.py +++ b/app/grants/management/commands/update_brightid_status.py @@ -1,4 +1,3 @@ -from django.conf import settings from django.core.management.base import BaseCommand from dashboard.brightid_utils import get_verified_uuids diff --git a/app/grants/management/commands/update_idena_status.py b/app/grants/management/commands/update_idena_status.py index a3b3893e875..399100a9ef7 100644 --- a/app/grants/management/commands/update_idena_status.py +++ b/app/grants/management/commands/update_idena_status.py @@ -1,4 +1,3 @@ -from django.conf import settings from django.core.management.base import BaseCommand from dashboard.models import Profile diff --git a/app/grants/models.py b/app/grants/models.py index 7f3eb5920da..1452610a443 100644 --- a/app/grants/models.py +++ b/app/grants/models.py @@ -43,7 +43,7 @@ import pytz import requests from django_extensions.db.fields import AutoSlugField -from economy.models import SuperModel, Token +from economy.models import SuperModel from economy.utils import ConversionRateNotFoundError, convert_amount from gas.utils import eth_usd_conv_rate, recommend_min_gas_price_to_confirm_in_time from grants.utils import generate_collection_thumbnail, get_upload_filename, is_grant_team_member @@ -1000,12 +1000,6 @@ def save(self, update=True, *args, **kwargs): return super(Grant, self).save(*args, **kwargs) -class SubscriptionQuerySet(models.QuerySet): - """Define the Subscription default queryset and manager.""" - - pass - - class Subscription(SuperModel): """Define the structure of a subscription agreement.""" @@ -1193,7 +1187,6 @@ def amount_per_period_to_gitcoin(self): token = addr_to_token(self.token_address, self.network) # gas prices no longer take this amount times 10**18 decimals - import pytz if self.created_on > timezone.datetime(2020, 6, 16, 15, 0).replace(tzinfo=pytz.utc): return self.gas_price @@ -1546,13 +1539,6 @@ def create_contribution(self, tx_id, is_successful_contribution=True): return contribution -class DonationQuerySet(models.QuerySet): - """Define the Contribution default queryset and manager.""" - - pass - - - class Flag(SuperModel): grant = models.ForeignKey( @@ -1673,12 +1659,6 @@ def __str__(self): return f"id: {self.pk}; from:{profile.handle}; {tx_id} => ${token_amount_usdt}; {naturaltime(self.created_on)}" -class ContributionQuerySet(models.QuerySet): - """Define the Contribution default queryset and manager.""" - - pass - - class Contribution(SuperModel): """Define the structure of a subscription agreement.""" diff --git a/app/grants/router.py b/app/grants/router.py index 40e81e543db..72b2bb8438d 100644 --- a/app/grants/router.py +++ b/app/grants/router.py @@ -1,12 +1,11 @@ from datetime import datetime, timedelta -from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator +from django.core.paginator import Paginator import django_filters.rest_framework from ratelimit.decorators import ratelimit from rest_framework import routers, viewsets from rest_framework.decorators import action -from rest_framework.exceptions import NotFound from rest_framework.response import Response from .models import CLRMatch, Contribution, Grant, GrantCLR, Subscription diff --git a/app/grants/serializers.py b/app/grants/serializers.py index ef398931623..6723f452f65 100644 --- a/app/grants/serializers.py +++ b/app/grants/serializers.py @@ -1,7 +1,7 @@ from dashboard.router import ProfileSerializer from rest_framework import serializers -from .models import CLRMatch, Contribution, Grant, GrantCLR, Subscription +from .models import Contribution, Grant, GrantCLR, Subscription from .utils import amount_in_wei, get_converted_amount diff --git a/app/grants/sync/celo.py b/app/grants/sync/celo.py index c7e2dd150f5..1531eec3dca 100644 --- a/app/grants/sync/celo.py +++ b/app/grants/sync/celo.py @@ -1,7 +1,3 @@ -from datetime import datetime - -from django.utils import timezone - import requests from grants.sync.helpers import is_txn_done_recently, record_contribution_activity, txn_already_used diff --git a/app/grants/sync/harmony.py b/app/grants/sync/harmony.py index 58a0eb0252e..03fdc624912 100644 --- a/app/grants/sync/harmony.py +++ b/app/grants/sync/harmony.py @@ -1,10 +1,5 @@ -from datetime import datetime - -from django.conf import settings -from django.utils import timezone - import requests -from grants.sync.helpers import is_txn_done_recently, record_contribution_activity, txn_already_used +from grants.sync.helpers import record_contribution_activity, txn_already_used def find_txn_on_harmony_explorer(contribution): diff --git a/app/grants/sync/helpers.py b/app/grants/sync/helpers.py index d4011459d44..2613d951026 100644 --- a/app/grants/sync/helpers.py +++ b/app/grants/sync/helpers.py @@ -35,8 +35,7 @@ def txn_already_used(txn, token_symbol): def record_contribution_activity(contribution): from dashboard.models import Activity - from marketing.mails import thank_you_for_supporting, successful_contribution - from grants.tasks import update_grant_metadata + from marketing.mails import thank_you_for_supporting try: event_name = 'new_grant_contribution' diff --git a/app/grants/sync/polkadot.py b/app/grants/sync/polkadot.py index 3e0979b5c8c..1793cae1e19 100644 --- a/app/grants/sync/polkadot.py +++ b/app/grants/sync/polkadot.py @@ -1,8 +1,4 @@ import logging -from datetime import datetime - -from django.conf import settings -from django.utils import timezone import requests from grants.sync.helpers import record_contribution_activity diff --git a/app/grants/sync/rsk.py b/app/grants/sync/rsk.py index d4c845edb19..d65c57758ae 100644 --- a/app/grants/sync/rsk.py +++ b/app/grants/sync/rsk.py @@ -1,10 +1,5 @@ -from datetime import datetime - -from django.conf import settings -from django.utils import timezone - import requests -from grants.sync.helpers import is_txn_done_recently, record_contribution_activity, txn_already_used +from grants.sync.helpers import record_contribution_activity, txn_already_used def find_txn_on_rsk_explorer(contribution): diff --git a/app/grants/sync/zcash.py b/app/grants/sync/zcash.py index df30ef17bba..a0b74efb6cc 100644 --- a/app/grants/sync/zcash.py +++ b/app/grants/sync/zcash.py @@ -1,7 +1,3 @@ -from datetime import datetime - -from django.utils import timezone - import requests from grants.sync.helpers import is_txn_done_recently, record_contribution_activity, txn_already_used diff --git a/app/grants/sync/zil.py b/app/grants/sync/zil.py index e4029310c90..6e590f57dae 100644 --- a/app/grants/sync/zil.py +++ b/app/grants/sync/zil.py @@ -1,4 +1,3 @@ - import time from django.conf import settings diff --git a/app/grants/tasks.py b/app/grants/tasks.py index 853855a0d21..0bc744d93ea 100644 --- a/app/grants/tasks.py +++ b/app/grants/tasks.py @@ -1,4 +1,3 @@ -import datetime as dt import inspect import math import time @@ -8,9 +7,8 @@ from django.utils import timezone from django.utils.text import slugify -import pytz from app.services import RedisService -from celery import app, group +from celery import app from celery.utils.log import get_task_logger from dashboard.models import Profile from grants.models import Grant, GrantCollection, Subscription @@ -18,8 +16,6 @@ from marketing.mails import ( new_contributions, new_grant, new_grant_admin, notion_failure_email, thank_you_for_supporting, ) -from marketing.models import Stat -from perftools.models import JSONStore from townsquare.models import Comment from unidecode import unidecode diff --git a/app/grants/templates/grants/card/back.html b/app/grants/templates/grants/card/back.html deleted file mode 100644 index e22419e09b6..00000000000 --- a/app/grants/templates/grants/card/back.html +++ /dev/null @@ -1,55 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 static humanize grants_extra avatar_tags %} -
- -
- {% if grant.activeSubscriptions %} -

{% trans 'Contributors' %} ({{ grant.activeSubscriptions|length }})

-
- {% for handle in grant.activeSubscriptions %} - - - - {% endfor %} -
- {% else %} -

No Contributons yet

-

Click on fund grant to become the first contributor for this grant!

- {% endif %} -
- -
-

- - {% trans 'View Details' %} - -

- {% if grant.active %} -

- - {% trans 'Fund this Grant' %} - -

- {% else %} -

{% trans 'This grant is no longer active.' %}

- {% endif %} -
- -
diff --git a/app/grants/templates/grants/components/hidden_inputs.html b/app/grants/templates/grants/components/hidden_inputs.html deleted file mode 100644 index 67cba80f791..00000000000 --- a/app/grants/templates/grants/components/hidden_inputs.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/app/grants/templates/grants/detail/funding.html b/app/grants/templates/grants/detail/funding.html deleted file mode 100644 index 74b2cc5d330..00000000000 --- a/app/grants/templates/grants/detail/funding.html +++ /dev/null @@ -1,135 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 static humanize i18n grants_extra %} - -
- {% if not grant.twitter_verified %} -
-
-
- -
- Warning: This grant has not verified their ownership of the twitter account listed. -
- - {% if is_team_member %} -
- - OR - SUBMIT SUPPORT REQUEST. -
- {% endif %} - -
-
-
- {% endif %} -
-
-
- {% if grant.is_clr_eligible and round_num %} -
- {% include 'grants/card/clr_match.html' %} -
- {% endif %} - -
- {% if grant.is_clr_eligible and round_num and round_num != 'LAST' %} -

- {{round_num}} ROUND -

-

- {{grant.amount_received_in_round|floatformat:0|intcomma}} - USD -

-

- {{grant.positive_round_contributor_count}} CONTRIBUTORS -

- {% else %} -

- ALL TIME FUNDING -

-

- {{grant.amount_received|floatformat:0|intcomma}} - USD -

-

- {{grant.contributor_count}} CONTRIBUTORS -

- {% endif %} - -
-
- - {% if grant.is_clr_eligible and clr_active and grant.active %} - {% include 'grants/card/clr_estimate.html' %} - {% endif %} - - {% if not grant.active %} - - - - {% else %} -
- {% is_favorite grant profile.user as is_grant_favorite %} - {% if profile.user.is_authenticated %} - - {% endif %} -
- - {% if grant.link_to_new_grant %} - - - - {% else %} -
- {% include 'grants/shared/hidden_inputs.html' %} - -
-
-
- {% endif %} - {% endif %} - - {% if not is_team_member %} -
- -
- {% endif %} -
-
-
diff --git a/app/grants/templates/grants/detail/tabs.html b/app/grants/templates/grants/detail/tabs.html deleted file mode 100644 index 3894e17da5b..00000000000 --- a/app/grants/templates/grants/detail/tabs.html +++ /dev/null @@ -1,385 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 static humanize i18n grants_extra %} -
-
- - - - - - {% if is_staff and grant.metadata.related %} - - {% endif %} - - {% if is_team_member %} - - {% endif %} - - - {% if is_staff %} - - {% endif %} - -
- -
- - {% if tab == "activity" %} -
- {% if is_team_member %} - {% include 'profiles/status_box.html' with suppress_tags=1 placeholder="Update Grant's Funders" what="grant" whatid=grant.pk max_length=1000 suppress_data_toggle=1 %} - {% else %} - {% include 'profiles/status_box.html' with suppress_tags=1 placeholder="Write on grants wall" what="grant" whatid=grant.pk suppress_data_toggle=1 %} - {% endif %} -
- {% include 'shared/activity_container.html' with pinned=False %} -
- -
- {% endif %} - - {% if tab == "related" %} - - {% endif %} - - - - {% if tab == "description" %} -
-

Description

-
- {% if grant.description_rich %} - {{ grant.description_rich }} - {% else %} - {{ grant.description }} - {% endif %} -
-
- {% if grant.metadata.wall_of_love %} -
-

Wall of Love

- {% for ele in grant.metadata.wall_of_love %} - - {% endfor %} - {% if grant.metadata.wall_of_love|length >= 5 %} - Show More - {% endif %} - {% endif %} -
- -

Activity Feed

- {% if is_team_member %} - {% include 'profiles/status_box.html' with suppress_tags=1 placeholder="Update Grant's Funders" what="grant" whatid=grant.pk max_length=1000 suppress_data_toggle=1 %} - {% else %} - {% include 'profiles/status_box.html' with suppress_tags=1 placeholder="Write on grants wall" what="grant" whatid=grant.pk suppress_data_toggle=1 %} - {% endif %} -
- {% include 'shared/activity_container.html' with pinned=False %} -
- {% endif %} - - {% if tab == "transactions" %} - - {% endif %} - - - - {% if tab == "sybil_profile" and is_staff %} - - - -

Grant Sybil Profile

- -
- - SybilScore ™️: {{ grant.sybil_score }} -
- - RiskScore ™️: {{ grant.weighted_risk_score }} -
-
-      A grant's SybilScore ™️ is calculated off of the total of suspicious activity on each of the following vectors:
-      - Account Metadata
-      - Suspicious Interactions
-      - Grant Flags Reports
-      - BrightID Trust Score (coming soon)
-      - KYC (maybe coming soon)
-      - MACI (maybe coming soon)
-
-      a grant's RiskScore ™️ is equal to its (SybilScore ™️)^2 * sqrt(its matching funds for this round).
-      
-
- {% for ele in sybil_profiles%} -

{{ele.0}}: (avg {{ele.1.1|floatformat:2}})

- - - - - - - - - {% for sp in ele.1.0 %} - - - - - - - - {% endfor %} -
- Index - - Contributors - - Contributors (pct) - - Contributions/Contributor - - Profiles -
- {% if '0x' in sp.0 %} - {{sp.0}} - {% else %} - {{sp.0}} - {% endif %} - - {{sp.1}} - - {{sp.5}}% - - {{sp.3|floatformat:2}} - -
- {% for this_profile in sp.4 %} - - {% if forloop.counter < 20 %} - - {% endif %} - @{{this_profile}} - - {% endfor %} -
-
- - - - {% endfor %} -
- {% endif %} - - {% if tab == "stats" and is_team_member %} - - -
-
- -
-
- {% if stats_history|length %} -

Marketing History Over Time

- - - - - - - - {% for ele in stats_history %} - - - - - - - {% endfor %} -
- When - - Impressions - - Added to Cart - - Contributions -
- {{ele.created_on|date:"Y/m/d"}} - - {% if ele.data.impressions > 0 %} - {{ele.data.impressions}} - {% else %} - 0 - {% endif %} - - {{ele.data.in_cart}} - - {{ele.data.contributions}} -
- {% endif %} - -
-
- -
-
- {% endif %} - {% if tab == "contributors" %} -
- {% endif %} -
-
- - diff --git a/app/grants/templates/grants/heart.html b/app/grants/templates/grants/heart.html deleted file mode 100644 index 11627165760..00000000000 --- a/app/grants/templates/grants/heart.html +++ /dev/null @@ -1,804 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Thanks for - keeping us - alive! - - - - - - - - - - - - \ No newline at end of file diff --git a/app/grants/templates/grants/shared/active_clr_round_cards.html b/app/grants/templates/grants/shared/active_clr_round_cards.html deleted file mode 100644 index dcd6f3a31d7..00000000000 --- a/app/grants/templates/grants/shared/active_clr_round_cards.html +++ /dev/null @@ -1,47 +0,0 @@ -{% load humanize i18n %} -
-
-
- {% trans "Active CLR Rounds" as filts %}{{ filts|upper }} - - -
- - - - - -
-
diff --git a/app/grants/utils.py b/app/grants/utils.py index a4fd868c667..b4c15cf0ecd 100644 --- a/app/grants/utils.py +++ b/app/grants/utils.py @@ -26,10 +26,9 @@ from random import randint, seed from secrets import token_hex -from django.templatetags.static import static from django.utils import timezone -from app.settings import BASE_DIR, BASE_URL, MEDIA_URL, NOTION_API_KEY, NOTION_SYBIL_DB, STATIC_HOST, STATIC_URL +from app.settings import BASE_URL, MEDIA_URL, NOTION_API_KEY, NOTION_SYBIL_DB from app.utils import notion_write from avatar.utils import convert_img from economy.utils import ConversionRateNotFoundError, convert_amount @@ -190,16 +189,6 @@ def get_user_code(user_id, grant, coding_set=block_codes, length=6): return ''.join(coding_id) -def add_grant_to_active_clrs(grant): - from grants.models import Grant, GrantCLR - - active_clr_rounds = GrantCLR.objects.filter(is_active=True) - for clr_round in active_clr_rounds: - if clr_round.grants.filter(pk=grant.pk).exists(): - grant.in_active_clrs.add(clr_round) - grant.save() - - def generate_collection_thumbnail(collection, width, heigth): grants = collection.grants.all() profile = collection.profile diff --git a/app/grants/views.py b/app/grants/views.py index 54cf9e13b59..5dc04516344 100644 --- a/app/grants/views.py +++ b/app/grants/views.py @@ -26,15 +26,13 @@ import time import uuid from datetime import datetime -from decimal import Decimal from urllib.parse import urlencode from django.conf import settings from django.contrib import messages from django.contrib.admin.views.decorators import staff_member_required from django.contrib.auth.decorators import login_required -from django.contrib.contenttypes.models import ContentType -from django.contrib.humanize.templatetags.humanize import intword, naturaltime +from django.contrib.humanize.templatetags.humanize import intword from django.core.paginator import EmptyPage, Paginator from django.db import connection, transaction from django.db.models import Q, Subquery @@ -44,7 +42,6 @@ from django.templatetags.static import static from django.urls import reverse from django.utils import timezone -from django.utils.crypto import get_random_string from django.utils.translation import gettext_lazy as _ from django.views.decorators.cache import cache_page from django.views.decorators.csrf import csrf_exempt @@ -97,34 +94,6 @@ kudos_reward_pks = [12580, 12584, 12572, 125868, 12552, 12556, 12557, 125677, 12550, 12392, 12307, 12343, 12156, 12164] -def get_fund_reward(request, grant): - token = Token.objects.filter( - id__in=kudos_reward_pks, - num_clones_available_counting_indirect_send__gt=0, - owner_address__iexact='0x6239FF1040E412491557a7a02b2CBcC5aE85dc8F').order_by('?').first() - if not token: - return None - key_len = 25 - _key = get_random_string(key_len) - btc = BulkTransferCoupon.objects.create( - token=token, - num_uses_total=1, - num_uses_remaining=1, - current_uses=0, - secret=_key, - comments_to_put_in_kudos_transfer=f"Thank you for funding '{grant.title}' on Gitcoin Grants!", - sender_profile=Profile.objects.get(handle='gitcoinbot'), - make_paid_for_first_minutes=0, - ) - - #store btc on session - request.session['send_notification'] = 1 - request.session['cta_text'] = "Redeem Kudos" - request.session['msg_html'] = f"You have received a new {token.ui_name} for your contribution to {grant.title}" - request.session['cta_url'] = btc.url - - return btc - def get_keywords(): """Get all Keywords.""" return json.dumps([str(key) for key in Keyword.objects.all().cache().values_list('keyword', flat=True)]) @@ -1184,18 +1153,6 @@ def grants_by_grant_clr(request, clr_round): response['X-Frame-Options'] = 'SAMEORIGIN' return response -# TODO: REMOVE -def add_form_categories_to_grant(form_category_ids, grant, grant_type): - form_category_ids = [int(i) for i in form_category_ids if i != ''] - - model_categories = basic_grant_categories(grant_type) - model_categories = [ category[0] for category in model_categories ] - selected_categories = [model_categories[i] for i in form_category_ids] - - for category in selected_categories: - grant_category = GrantCategory.objects.get_or_create(category=category)[0] - grant.categories.add(grant_category) - def get_grant_sybil_profile(grant_id=None, days_back=None, grant_type=None, index_on=None): grant_id_sql = f"= {grant_id}" if grant_id else "IS NOT NULL" @@ -1547,8 +1504,6 @@ def grant_edit(request, grant_id): is_team_member = is_grant_team_member(grant, profile) if request.method == 'POST' and (is_team_member or request.user.is_staff): - from grants.utils import add_grant_to_active_clrs - response = { 'status': 400, 'message': 'error: Bad Request. Unable to create grant' @@ -1744,8 +1699,6 @@ def grant_new(request): if request.method == 'POST': - from grants.utils import add_grant_to_active_clrs - response = { 'status': 400, 'message': 'error: Bad Request. Unable to create grant' @@ -2620,8 +2573,6 @@ def create_matching_pledge_v1(request): ) match_pledge.save() - # dont' send spammy email - # new_grant_match_pledge(match_pledge) response = { 'status': 200, @@ -2648,10 +2599,6 @@ def invoice(request, contribution_pk): return TemplateResponse(request, 'grants/invoice.html', params) -def basic_grant_types(): - result = GrantType.objects.all() - return [ (ele.name, ele.label) for ele in result ] - def basic_grant_categories(name): result = [] diff --git a/app/inbox/signals.py b/app/inbox/signals.py index 1e05babedff..400c007efa7 100644 --- a/app/inbox/signals.py +++ b/app/inbox/signals.py @@ -26,7 +26,6 @@ from dashboard.models import Activity from inbox.utils import ( comment_notification, mentioned_users_notification, send_mention_notification_to_users, send_notification_to_user, - send_notification_to_user_from_gitcoinbot, ) from townsquare.models import Comment, Like diff --git a/app/inbox/utils.py b/app/inbox/utils.py index e0e368ae825..398fee7586f 100644 --- a/app/inbox/utils.py +++ b/app/inbox/utils.py @@ -17,9 +17,6 @@ along with this program. If not, see . """ -from gettext import gettext - -from django.contrib.auth.models import User from django.template.defaultfilters import truncatechars from app.utils import get_profiles_from_text @@ -38,19 +35,6 @@ def send_notification_to_user(from_user, to_user, cta_url, cta_text, msg_html): ) -def send_notification_to_user_from_gitcoinbot(to_user, cta_url, cta_text, msg_html): - """Helper method to create a new notification.""" - from_user = User.objects.filter(username='gitcoinbot').first() - if to_user and from_user: - Notification.objects.create( - cta_url=cta_url, - cta_text=cta_text, - message_html=msg_html, - from_user=from_user, - to_user=to_user - ) - - def send_mention_notification_to_users(activity, mentioned_profiles): profile = activity.profile preview_post = truncatechars(activity.metadata.get('title', ''), 80) diff --git a/app/kudos/helpers.py b/app/kudos/helpers.py index 8d3f5cc5cda..436f45248e2 100644 --- a/app/kudos/helpers.py +++ b/app/kudos/helpers.py @@ -22,8 +22,6 @@ from django.conf import settings from django.shortcuts import get_object_or_404 -from eth_utils import is_address, to_checksum_address - from .models import Contract, Token, Wallet logger = logging.getLogger(__name__) @@ -59,48 +57,6 @@ def get_token(token_id, network, address): return get_object_or_404(Token, contract=contract, token_id=token_id) -def reconcile_kudos_preferred_wallet(profile): - """DEPRECATED. - Helper function to set the kudos_preferred_wallet if it doesn't already exist - - Args: - profile (TYPE): Description - - Returns: - str: Profile wallet address. - - """ - # If the preferred_kudos_wallet is not set, figure out how to set it. - if not profile.preferred_kudos_wallet: - # If the preferred_payout_address exists, use it for the preferred_kudos_Wallet - if profile.preferred_payout_address and profile.preferred_payout_address != '0x0': - # Check if the preferred_payout_addess exists as a kudos wallet address - kudos_wallet = profile.wallets.filter(address=profile.preferred_payout_address).first() - if kudos_wallet: - # If yes, set that wallet to be the profile.preferred_kudos_wallet - profile.preferred_kudos_wallet = kudos_wallet - # profile.preferred_kudos_wallet = profile.wallets.filter(address=profile.preferred_payout_address) - else: - # Create the kudos_wallet and set it as the preferred_kudos_wallet in the profile - new_kudos_wallet = Wallet(address=profile.preferred_payout_address) - new_kudos_wallet.save() - profile.preferred_kudos_wallet = new_kudos_wallet - else: - # Check if there are any kudos_wallets available. If so, set the first one to preferred. - kudos_wallet = profile.kudos_wallets.all() - if kudos_wallet: - profile.preferred_kudos_wallet = kudos_wallet.first() - else: - # Not enough information available to set the preferred_kudos_wallet - # Use kudos indrect send. - logger.warning('No kudos wallets or preferred_payout_address address found. Use Kudos Indirect Send.') - return None - - profile.save() - - return profile.preferred_kudos_wallet - - def re_send_kudos_transfer(kt, override_with_xdai_okay): from dashboard.utils import get_web3, has_tx_mined from gas.utils import recommend_min_gas_price_to_confirm_in_time diff --git a/app/kudos/management/commands/process_pending_kudos_distributions.py b/app/kudos/management/commands/process_pending_kudos_distributions.py index 34d40edb5e8..bdc6a86d77c 100644 --- a/app/kudos/management/commands/process_pending_kudos_distributions.py +++ b/app/kudos/management/commands/process_pending_kudos_distributions.py @@ -16,11 +16,8 @@ along with this program. If not, see . """ -import logging import time -import warnings -from django.conf import settings from django.core.management.base import BaseCommand from kudos.models import KudosTransfer diff --git a/app/kudos/management/commands/re_mint_kudos_on_xdai.py b/app/kudos/management/commands/re_mint_kudos_on_xdai.py index 4a249846948..38ccb80ff9b 100644 --- a/app/kudos/management/commands/re_mint_kudos_on_xdai.py +++ b/app/kudos/management/commands/re_mint_kudos_on_xdai.py @@ -16,11 +16,6 @@ along with this program. If not, see . """ -import logging -import time -import warnings - -from django.conf import settings from django.core.management.base import BaseCommand from dashboard.models import Profile diff --git a/app/kudos/models.py b/app/kudos/models.py index 0a1ade92c86..35e47af5bed 100644 --- a/app/kudos/models.py +++ b/app/kudos/models.py @@ -33,12 +33,10 @@ from django.utils.text import slugify import environ -import pyvips from dashboard.models import SendCryptoAsset from economy.models import SuperModel from eth_utils import to_checksum_address from gas.utils import recommend_min_gas_price_to_confirm_in_time -from pyvips.error import Error as VipsError from unidecode import unidecode logger = logging.getLogger(__name__) diff --git a/app/kudos/tasks.py b/app/kudos/tasks.py index fa5bc015610..489f46cd967 100644 --- a/app/kudos/tasks.py +++ b/app/kudos/tasks.py @@ -10,7 +10,6 @@ from celery.utils.log import get_task_logger from dashboard.utils import get_web3, has_tx_mined from gas.utils import recommend_min_gas_price_to_confirm_in_time -from hexbytes import HexBytes from kudos.models import KudosTransfer, TokenRequest from kudos.utils import kudos_abi from marketing.mails import notify_kudos_minted, send_mail diff --git a/app/kudos/templates/shared/kudos_banner.html b/app/kudos/templates/shared/kudos_banner.html deleted file mode 100644 index acc64eccd2c..00000000000 --- a/app/kudos/templates/shared/kudos_banner.html +++ /dev/null @@ -1,20 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 static %} -
- kudos -
diff --git a/app/kudos/test_models.py b/app/kudos/test_models.py deleted file mode 100644 index 8230b06a828..00000000000 --- a/app/kudos/test_models.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -"""Test the Kudos models. - -Copyright (C) 2021 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 . -""" -import logging -import unittest - -from django.test import Client, TestCase diff --git a/app/kudos/test_utils.py b/app/kudos/test_utils.py index 2cc01e96a7e..8c6e636bed4 100644 --- a/app/kudos/test_utils.py +++ b/app/kudos/test_utils.py @@ -19,7 +19,7 @@ import logging import unittest -from django.test import Client, TestCase +from django.test import TestCase from .utils import KudosContract diff --git a/app/kudos/test_views.py b/app/kudos/test_views.py index 8595e007847..f4a8d072689 100644 --- a/app/kudos/test_views.py +++ b/app/kudos/test_views.py @@ -17,7 +17,6 @@ along with this program. If not, see . """ import logging -from unittest import skip from django.test import Client, TestCase @@ -62,10 +61,6 @@ def test_mint(self): r = self.client.get('/kudos/mint/') self.assertEqual(r.status_code, 200) - # @skip(reason='stub for future testing') - # def test_get_to_emails(self): - # self.client.get('/kudos/1') - # @skip(reason='stub for future testing') # def test_kudos_preferred_wallet(self): # self.client.get('/kudos/1') diff --git a/app/kudos/utils.py b/app/kudos/utils.py index 1b5c2af8b2a..9f51d954f4c 100644 --- a/app/kudos/utils.py +++ b/app/kudos/utils.py @@ -93,38 +93,6 @@ class KudosError(Exception): pass -class KudosTransferNotFound(KudosError): - """Exception is raised when web3 and the database are out of sync. - - Attributes: - kudos_id -- the kudos id that has mismatched data - message -- explanation of the error - - """ - - def __init__(self, kudos_id, message): - self.kudos_id = kudos_id - self.message = message - - -class KudosMismatch(KudosError): - """Exception is raised when web3 and the database are out of sync. - - Attributes: - kudos_id: The kudos id that has mismatched data. - kudos_web3: Kudos attributes on web3. - kudos_db: Kudos attritubes in the database. - message: Explanation of the error. - - """ - - def __init__(self, kudos_id, kudos_web3, kudos_db, message): - self.kudos_id = kudos_id - self.kudos_web3 = kudos_web3 - self.kudos_db = kudos_db - self.message = message - - class KudosContract: """A class represending the Kudos.sol contract. @@ -345,8 +313,6 @@ def sync_db(self, kudos_id, txid): # Only warn for a Kudos that is cloned/transfered, not a Gen0 Kudos. if kudos_token.num_clones_allowed == 0: logger.warning(f'No KudosTransfer object found for Kudos ID {kudos_id}') - # raise KudosTransferNotFound(kudos_id, 'No KudosTransfer object found') - # raise except KudosTransfer.MultipleObjectsReturned: pass @@ -646,31 +612,5 @@ def create_token_uri_url(self, **kwargs): return f'{settings.IPFS_API_SCHEME}://{settings.IPFS_HOST}:{settings.IPFS_API_PORT}/api/v0/cat/{ipfs_hash}' -def get_to_emails(params): - """Get a list of email address to send the alert to, in this priority: - - 1. get_emails_master() pulls email addresses from the user's public Github account. - 2. If an email address is included in the Tips/Kudos form, append that to the email list. - - - Args: - params (dict): A dictionary parsed form the POST request. Typically this is a POST - request coming in from a Tips/Kudos send. - - Returns: - list: An array of email addresses to send the email to. - - """ - to_emails = [] - - to_username = params['username'].lstrip('@') - to_emails = get_emails_master(to_username) - - if params.get('email'): - to_emails.append(params['email']) - - return list(set(to_emails)) - - def kudos_abi(): return [{'constant': True, 'inputs': [{'name': '_interfaceId', 'type': 'bytes4'}], 'name': 'supportsInterface', 'outputs': [{'name': '', 'type': 'bool'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': True, 'inputs': [], 'name': 'name', 'outputs': [{'name': '', 'type': 'string'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': True, 'inputs': [{'name': '_tokenId', 'type': 'uint256'}], 'name': 'getApproved', 'outputs': [{'name': '', 'type': 'address'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': False, 'inputs': [{'name': '_to', 'type': 'address'}, {'name': '_tokenId', 'type': 'uint256'}], 'name': 'approve', 'outputs': [], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': True, 'inputs': [], 'name': 'cloneFeePercentage', 'outputs': [{'name': '', 'type': 'uint256'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': True, 'inputs': [], 'name': 'totalSupply', 'outputs': [{'name': '', 'type': 'uint256'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': True, 'inputs': [], 'name': 'InterfaceId_ERC165', 'outputs': [{'name': '', 'type': 'bytes4'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': False, 'inputs': [{'name': '_from', 'type': 'address'}, {'name': '_to', 'type': 'address'}, {'name': '_tokenId', 'type': 'uint256'}], 'name': 'transferFrom', 'outputs': [], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': True, 'inputs': [{'name': '_owner', 'type': 'address'}, {'name': '_index', 'type': 'uint256'}], 'name': 'tokenOfOwnerByIndex', 'outputs': [{'name': '', 'type': 'uint256'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': False, 'inputs': [{'name': '_from', 'type': 'address'}, {'name': '_to', 'type': 'address'}, {'name': '_tokenId', 'type': 'uint256'}], 'name': 'safeTransferFrom', 'outputs': [], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': True, 'inputs': [], 'name': 'isMintable', 'outputs': [{'name': '', 'type': 'bool'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': True, 'inputs': [{'name': '_tokenId', 'type': 'uint256'}], 'name': 'exists', 'outputs': [{'name': '', 'type': 'bool'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': True, 'inputs': [{'name': '_index', 'type': 'uint256'}], 'name': 'tokenByIndex', 'outputs': [{'name': '', 'type': 'uint256'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': True, 'inputs': [{'name': '_tokenId', 'type': 'uint256'}], 'name': 'ownerOf', 'outputs': [{'name': '', 'type': 'address'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': True, 'inputs': [{'name': '_owner', 'type': 'address'}], 'name': 'balanceOf', 'outputs': [{'name': '', 'type': 'uint256'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': False, 'inputs': [], 'name': 'renounceOwnership', 'outputs': [], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': True, 'inputs': [{'name': '', 'type': 'uint256'}], 'name': 'kudos', 'outputs': [{'name': 'priceFinney', 'type': 'uint256'}, {'name': 'numClonesAllowed', 'type': 'uint256'}, {'name': 'numClonesInWild', 'type': 'uint256'}, {'name': 'clonedFromId', 'type': 'uint256'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': True, 'inputs': [], 'name': 'owner', 'outputs': [{'name': '', 'type': 'address'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': True, 'inputs': [], 'name': 'symbol', 'outputs': [{'name': '', 'type': 'string'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': False, 'inputs': [{'name': '_to', 'type': 'address'}, {'name': '_approved', 'type': 'bool'}], 'name': 'setApprovalForAll', 'outputs': [], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': False, 'inputs': [{'name': '_from', 'type': 'address'}, {'name': '_to', 'type': 'address'}, {'name': '_tokenId', 'type': 'uint256'}, {'name': '_data', 'type': 'bytes'}], 'name': 'safeTransferFrom', 'outputs': [], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': True, 'inputs': [{'name': '_tokenId', 'type': 'uint256'}], 'name': 'tokenURI', 'outputs': [{'name': '', 'type': 'string'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': True, 'inputs': [{'name': '_owner', 'type': 'address'}, {'name': '_operator', 'type': 'address'}], 'name': 'isApprovedForAll', 'outputs': [{'name': '', 'type': 'bool'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': False, 'inputs': [{'name': '_newOwner', 'type': 'address'}], 'name': 'transferOwnership', 'outputs': [], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'function'}, {'inputs': [], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'constructor'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': 'previousOwner', 'type': 'address'}], 'name': 'OwnershipRenounced', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': 'previousOwner', 'type': 'address'}, {'indexed': True, 'name': 'newOwner', 'type': 'address'}], 'name': 'OwnershipTransferred', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': '_from', 'type': 'address'}, {'indexed': True, 'name': '_to', 'type': 'address'}, {'indexed': True, 'name': '_tokenId', 'type': 'uint256'}], 'name': 'Transfer', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': '_owner', 'type': 'address'}, {'indexed': True, 'name': '_approved', 'type': 'address'}, {'indexed': True, 'name': '_tokenId', 'type': 'uint256'}], 'name': 'Approval', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'name': '_owner', 'type': 'address'}, {'indexed': True, 'name': '_operator', 'type': 'address'}, {'indexed': False, 'name': '_approved', 'type': 'bool'}], 'name': 'ApprovalForAll', 'type': 'event'}, {'constant': False, 'inputs': [{'name': '_to', 'type': 'address'}, {'name': '_priceFinney', 'type': 'uint256'}, {'name': '_numClonesAllowed', 'type': 'uint256'}, {'name': '_tokenURI', 'type': 'string'}], 'name': 'mint', 'outputs': [{'name': 'tokenId', 'type': 'uint256'}], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': False, 'inputs': [{'name': '_to', 'type': 'address'}, {'name': '_tokenId', 'type': 'uint256'}, {'name': '_numClonesRequested', 'type': 'uint256'}], 'name': 'clone', 'outputs': [], 'payable': True, 'stateMutability': 'payable', 'type': 'function'}, {'constant': False, 'inputs': [{'name': '_owner', 'type': 'address'}, {'name': '_tokenId', 'type': 'uint256'}], 'name': 'burn', 'outputs': [], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': False, 'inputs': [{'name': '_cloneFeePercentage', 'type': 'uint256'}], 'name': 'setCloneFeePercentage', 'outputs': [], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': False, 'inputs': [{'name': '_isMintable', 'type': 'bool'}], 'name': 'setMintable', 'outputs': [], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': False, 'inputs': [{'name': '_tokenId', 'type': 'uint256'}, {'name': '_newPriceFinney', 'type': 'uint256'}], 'name': 'setPrice', 'outputs': [], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': True, 'inputs': [{'name': '_tokenId', 'type': 'uint256'}], 'name': 'getKudosById', 'outputs': [{'name': 'priceFinney', 'type': 'uint256'}, {'name': 'numClonesAllowed', 'type': 'uint256'}, {'name': 'numClonesInWild', 'type': 'uint256'}, {'name': 'clonedFromId', 'type': 'uint256'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': True, 'inputs': [{'name': '_tokenId', 'type': 'uint256'}], 'name': 'getNumClonesInWild', 'outputs': [{'name': 'numClonesInWild', 'type': 'uint256'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': True, 'inputs': [], 'name': 'getLatestId', 'outputs': [{'name': 'tokenId', 'type': 'uint256'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}] diff --git a/app/kudos/views.py b/app/kudos/views.py index bd65430620c..957577d844d 100644 --- a/app/kudos/views.py +++ b/app/kudos/views.py @@ -28,7 +28,6 @@ from django.conf import settings from django.contrib import messages from django.contrib.staticfiles.templatetags.staticfiles import static -from django.core.exceptions import PermissionDenied from django.db import transaction from django.db.models import Q from django.http import Http404, HttpResponse, JsonResponse @@ -43,10 +42,10 @@ from dashboard.models import Activity, Profile, SearchHistory from dashboard.notifications import maybe_market_kudos_to_email, maybe_market_kudos_to_github from dashboard.tasks import increment_view_count -from dashboard.utils import get_nonce, get_web3, is_valid_eth_address +from dashboard.utils import get_web3, is_valid_eth_address from dashboard.views import record_user_action from gas.utils import recommend_min_gas_price_to_confirm_in_time -from git.utils import get_emails_by_category, get_emails_master, get_github_primary_email +from git.utils import get_emails_by_category, get_github_primary_email from kudos.tasks import redeem_bulk_kudos from kudos.utils import kudos_abi from marketing.mails import new_kudos_request @@ -276,39 +275,6 @@ def mint(request): return TemplateResponse(request, 'kudos_mint.html', {}) -def get_primary_from_email(params, request): - """Find the primary_from_email address. This function finds the address using this priority: - - 1. If the email field is filed out in the Send POST request, use the `fromEmail` field. - 2. If the user is logged in, they should have an email address associated with their account. - Use this as the second option. `request_user_email`. - 3. If all else fails, attempt to pull the email from the user's github account. - - Args: - params (dict): A dictionary parsed form the POST request. Typically this is a POST - request coming in from a Tips/Kudos send. - - Returns: - str: The primary_from_email string. - - """ - - request_user_email = request.user.email if request.user.is_authenticated else '' - logger.info(request.user.profile) - access_token = request.user.profile.get_access_token() if request.user.is_authenticated else '' - - if params.get('fromEmail'): - primary_from_email = params['fromEmail'] - elif request_user_email: - primary_from_email = request_user_email - elif access_token: - primary_from_email = get_github_primary_email(access_token) - else: - primary_from_email = 'unknown@gitcoin.co' - - return primary_from_email - - def kudos_preferred_wallet(request, handle): """Returns the address, if any, that someone would like to be send kudos directly to.""" response = {'addresses': []} diff --git a/app/marketing/apps.py b/app/marketing/apps.py index d9fcfd91708..4ca569bd2db 100644 --- a/app/marketing/apps.py +++ b/app/marketing/apps.py @@ -22,8 +22,4 @@ class MarketingConfig(AppConfig): - name = 'marketing' - - def ready(self): - from .signals import create_email_subscriber diff --git a/app/marketing/mails.py b/app/marketing/mails.py index 501cd77203a..a0b4006993b 100644 --- a/app/marketing/mails.py +++ b/app/marketing/mails.py @@ -22,14 +22,12 @@ import logging from django.conf import settings -from django.http import Http404, HttpResponse from django.utils import timezone, translation from django.utils.translation import gettext from django.utils.translation import gettext_lazy as _ import sendgrid from app.utils import get_profiles_from_text -from grants.models import Subscription from marketing.utils import func_name, get_or_save_email_subscriber, should_suppress_notification_email from python_http_client.exceptions import HTTPError, UnauthorizedError from retail.emails import ( @@ -41,13 +39,13 @@ render_grant_txn_failed, render_grant_update, render_match_distribution, render_match_email, render_mention, render_new_bounty, render_new_bounty_acceptance, render_new_bounty_rejection, render_new_bounty_roundup, render_new_contributions_email, render_new_grant_approved_email, render_new_grant_email, render_new_work_submission, - render_no_applicant_reminder, render_nth_day_email_campaign, render_pending_contribution_email, - render_quarterly_stats, render_remember_your_cart, render_request_amount_email, render_reserved_issue, - render_start_work_applicant_about_to_expire, render_start_work_applicant_expired, 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_tribe_hackathon_prizes, - render_unread_notification_email_weekly_roundup, render_wallpost, render_weekly_recap, + render_no_applicant_reminder, render_pending_contribution_email, render_quarterly_stats, render_remember_your_cart, + render_request_amount_email, render_reserved_issue, render_start_work_applicant_about_to_expire, + render_start_work_applicant_expired, 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_tribe_hackathon_prizes, render_unread_notification_email_weekly_roundup, render_wallpost, + render_weekly_recap, ) from sendgrid.helpers.mail import Attachment, Content, Email, Mail, Personalization from sendgrid.helpers.stats import Category @@ -176,26 +174,6 @@ def get_bounties_for_keywords(keywords, hours_back): return new_bounties, all_bounties -def nth_day_email_campaign(nth, subscriber): - firstname = subscriber.email.split('@')[0] - - if subscriber.profile and subscriber.profile.user and subscriber.profile.user.first_name: - firstname = subscriber.profile.user.first_name - - if should_suppress_notification_email(subscriber.email, 'roundup'): - return False - cur_language = translation.get_language() - - try: - setup_lang(subscriber.email) - from_email = settings.CONTACT_EMAIL - if not should_suppress_notification_email(subscriber.email, 'welcome_mail'): - html, text, subject = render_nth_day_email_campaign(subscriber.email, nth, firstname) - to_email = subscriber.email - send_mail(from_email, to_email, subject, text, html, categories=['transactional', func_name()]) - finally: - translation.activate(cur_language) - def featured_funded_bounty(from_email, bounty): to_email = bounty.bounty_owner_email @@ -284,24 +262,6 @@ def new_grant_approved(grant, profile): translation.activate(cur_language) -def new_grant_match_pledge(matchpledge): - to_email = 'founders@gitcoin.co' - from_email = matchpledge.profile.email - cur_language = translation.get_language() - - try: - setup_lang(to_email) - subject = f"New Grants Match Pledge Inquiry from {matchpledge.profile.handle}" - body = f"" - for key, val in matchpledge.data_json.items(): - body += f"{key}: {val}\n" - body += f"\n\n\n{settings.BASE_URL}{matchpledge.admin_url}" - html = f"
{body}
" - send_mail(from_email, to_email, subject, body, html, categories=['transactional', func_name()]) - finally: - translation.activate(cur_language) - - def new_contributions(grant): from_email = settings.CONTACT_EMAIL to_email = grant.admin_profile.email @@ -1025,29 +985,6 @@ def warn_account_out_of_eth(account, balance, denomination): translation.activate(cur_language) -def warn_subscription_failed(subscription): - if subscription and subscription.negative: - return - to_email = "support@gitcoin.co" - from_email = settings.SERVER_EMAIL - cur_language = translation.get_language() - try: - setup_lang(to_email) - subject = str(subscription.pk) + str(_(" subscription failed")) - body = f"{settings.BASE_URL}{subscription.admin_url}\n{subscription.contributor_profile.email}, {subscription.contributor_profile.user.email}
\n\n{subscription.subminer_comments}
" - if not should_suppress_notification_email(to_email, 'admin'): - send_mail( - from_email, - to_email, - subject, - body, - from_name=_("No Reply from Gitcoin.co"), - categories=['admin', func_name()], - ) - finally: - translation.activate(cur_language) - - def new_feedback(email, feedback): to_email = 'product@gitcoin.co' from_email = settings.SERVER_EMAIL diff --git a/app/marketing/management/commands/assemble_leaderboards.py b/app/marketing/management/commands/assemble_leaderboards.py index 53ad6f03304..6456e1a0981 100644 --- a/app/marketing/management/commands/assemble_leaderboards.py +++ b/app/marketing/management/commands/assemble_leaderboards.py @@ -21,11 +21,9 @@ from django.contrib.contenttypes.models import ContentType from django.core.management.base import BaseCommand from django.db import connection, transaction -from django.db.models import Q from django.utils import timezone -from cacheops import CacheMiss, cache -from dashboard.models import Earning, Profile +from dashboard.models import Profile from marketing.models import LeaderboardRank # Constants diff --git a/app/marketing/management/commands/campaign_email.py b/app/marketing/management/commands/campaign_email.py index a0a4d4b2d8b..0f6e4e51390 100644 --- a/app/marketing/management/commands/campaign_email.py +++ b/app/marketing/management/commands/campaign_email.py @@ -21,7 +21,6 @@ from django.core.management.base import BaseCommand from django.utils import timezone -from marketing.mails import nth_day_email_campaign from marketing.models import EmailSubscriber @@ -33,14 +32,6 @@ def n_days_ago(n): tzinfo=timezone.get_current_timezone()) -def send_nth_email_to_subscriber(nth, sub): - first_email = EmailSubscriber.objects.filter(email__iexact=sub.email).order_by('created_on').first() - if first_email.id == sub.id: - # it is the first time this subscriber is in our system - # send email to him/her - nth_day_email_campaign(nth, sub) - - def send_nth_email(nth): print('sending day {} email'.format(nth)) # query all new subscribers created diff --git a/app/marketing/management/commands/expiration_start_work.py b/app/marketing/management/commands/expiration_start_work.py index 74798309143..bfd4a3eb476 100644 --- a/app/marketing/management/commands/expiration_start_work.py +++ b/app/marketing/management/commands/expiration_start_work.py @@ -26,7 +26,7 @@ from django.utils import timezone import pytz -from dashboard.models import Bounty, Interest, UserAction +from dashboard.models import Bounty, Interest from dashboard.notifications import ( maybe_notify_bounty_user_escalated_to_slack, maybe_notify_bounty_user_warned_removed_to_slack, maybe_notify_user_escalated_github, maybe_warn_user_removed_github, diff --git a/app/marketing/management/commands/export_graph_edges.py b/app/marketing/management/commands/export_graph_edges.py index f3acce16a9a..f75bafa580f 100644 --- a/app/marketing/management/commands/export_graph_edges.py +++ b/app/marketing/management/commands/export_graph_edges.py @@ -17,7 +17,6 @@ ''' import re -from django.conf import settings from django.core.management.base import BaseCommand from django.utils import timezone diff --git a/app/marketing/management/commands/ingest_community_events_calender.py b/app/marketing/management/commands/ingest_community_events_calender.py index e5de9e880cf..13dd4fe3b6c 100644 --- a/app/marketing/management/commands/ingest_community_events_calender.py +++ b/app/marketing/management/commands/ingest_community_events_calender.py @@ -15,14 +15,10 @@ along with this program. If not, see . ''' -import time - from django.core.management.base import BaseCommand -from django.db.models import Q -from django.utils import timezone import requests -from icalendar import Calendar, Event +from icalendar import Calendar from marketing.models import UpcomingDate # The iCal link for Gitcoin community events calender diff --git a/app/marketing/management/commands/make_everyone_avatars.py b/app/marketing/management/commands/make_everyone_avatars.py index 526351d5e86..6f542947a5c 100644 --- a/app/marketing/management/commands/make_everyone_avatars.py +++ b/app/marketing/management/commands/make_everyone_avatars.py @@ -15,7 +15,6 @@ along with this program. If not, see . ''' -from django.conf import settings from django.core.management.base import BaseCommand diff --git a/app/marketing/management/commands/make_request_coins.py b/app/marketing/management/commands/make_request_coins.py index e97abc4ed69..c6be5719aa9 100644 --- a/app/marketing/management/commands/make_request_coins.py +++ b/app/marketing/management/commands/make_request_coins.py @@ -17,10 +17,6 @@ along with this program. If not, see . """ -import time -import warnings -from datetime import datetime - from django.core.management.base import BaseCommand from django.utils.crypto import get_random_string diff --git a/app/marketing/management/commands/new_bounties_email.py b/app/marketing/management/commands/new_bounties_email.py index 8f41abbe16a..afe98e14d95 100644 --- a/app/marketing/management/commands/new_bounties_email.py +++ b/app/marketing/management/commands/new_bounties_email.py @@ -21,12 +21,9 @@ from django.conf import settings from django.core.management.base import BaseCommand -from django.utils import timezone from marketing.models import EmailSubscriber from marketing.tasks import new_bounty_daily -from marketing.utils import should_suppress_notification_email -from townsquare.utils import is_email_townsquare_enabled warnings.filterwarnings("ignore") diff --git a/app/marketing/management/commands/post_data.py b/app/marketing/management/commands/post_data.py index 5b657f90e03..b1bef4ee292 100644 --- a/app/marketing/management/commands/post_data.py +++ b/app/marketing/management/commands/post_data.py @@ -19,7 +19,6 @@ """ import operator import random -import time import warnings from datetime import datetime diff --git a/app/marketing/management/commands/prune_bad_activities.py b/app/marketing/management/commands/prune_bad_activities.py index a9e2b0196fc..8ad717b16e8 100644 --- a/app/marketing/management/commands/prune_bad_activities.py +++ b/app/marketing/management/commands/prune_bad_activities.py @@ -11,8 +11,6 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . ''' -import time - from django.core.management.base import BaseCommand from django.utils import timezone diff --git a/app/marketing/management/commands/pull_github.py b/app/marketing/management/commands/pull_github.py index b02a7e6eac5..f8a6366515a 100644 --- a/app/marketing/management/commands/pull_github.py +++ b/app/marketing/management/commands/pull_github.py @@ -22,7 +22,7 @@ from dashboard.models import Profile from dashboard.utils import ProfileHiddenException, ProfileNotFoundException, profile_helper -from git.utils import search +from git.utils import search_user from marketing.models import EmailSubscriber @@ -30,15 +30,6 @@ class NoUsersException(Exception): pass -def get_github_user_from_github(email): - result = search(email) - if not result.get('total_count', 0): - # print(result) - raise NoUsersException("no users found") - - return result['items'][0] - - def get_github_user_from_DB(email): users = User.objects.filter(email__iexact=email) for user in users: @@ -68,8 +59,8 @@ def handle(self, *args, **options): if not es.github: es.github = get_github_user_from_DB(es.email) if not es.github: - ghuser = get_github_user_from_github(es.email) - es.github = ghuser['login'] + ghuser = search_user(es.email) + es.github = ghuser.login if not es.keywords: try: es.profile = profile_helper(es.github, True) diff --git a/app/marketing/management/commands/send_quarterly_stats.py b/app/marketing/management/commands/send_quarterly_stats.py index c96764289d8..d9ab1253f7d 100644 --- a/app/marketing/management/commands/send_quarterly_stats.py +++ b/app/marketing/management/commands/send_quarterly_stats.py @@ -22,7 +22,7 @@ from dashboard.models import Profile from marketing.mails import quarterly_stats -from marketing.models import EmailSubscriber, LeaderboardRank +from marketing.models import LeaderboardRank from marketing.utils import get_platform_wide_stats warnings.filterwarnings("ignore", category=DeprecationWarning) diff --git a/app/marketing/management/commands/send_tax_report.py b/app/marketing/management/commands/send_tax_report.py index a2f0cdbb57d..9ab004c6a28 100644 --- a/app/marketing/management/commands/send_tax_report.py +++ b/app/marketing/management/commands/send_tax_report.py @@ -19,7 +19,6 @@ import json import logging import os -import shutil import warnings import zipfile @@ -29,13 +28,9 @@ from django.db.models import F import pdfrw -import sendgrid from dashboard.models import Bounty, BountyFulfillment, Earning, Profile, Tip from grants.models import Grant from marketing.mails import tax_report -from python_http_client.exceptions import HTTPError, UnauthorizedError -from sendgrid.helpers.mail import Content, Email, Mail, Personalization -from sendgrid.helpers.stats import Category logger = logging.getLogger(__name__) diff --git a/app/marketing/management/commands/sync_github.py b/app/marketing/management/commands/sync_github.py index c7fa868307e..440184c6ec4 100644 --- a/app/marketing/management/commands/sync_github.py +++ b/app/marketing/management/commands/sync_github.py @@ -55,7 +55,7 @@ def all_bountied_repos(self): def do_we_care(self, event): repos_we_care_about = self.all_bountied_repos() try: - repo_name = event.get('repo', {}).get('name', '').lower() + repo_name = event.repo.name.lower() return repo_name in repos_we_care_about except AttributeError: logger.debug('Error in do_we_care during sync_github') @@ -74,20 +74,15 @@ def sync_profile_actions(self): # process them for profile in profiles: try: - events = get_user(profile.handle, '/events') + events = get_user(profile.handle).get_events() for event in events: - try: - event_time = event.get('created_at', False) - created_on = datetime.datetime.strptime(event_time, '%Y-%m-%dT%H:%M:%SZ') - except Exception: - created_on = timezone.now() if self.do_we_care(event): GithubEvent.objects.get_or_create( profile=profile, - payload=event, - what=event.get('type', ''), - repo=event.get('repo', {}).get('name', ''), - created_on=created_on, + payload=event._rawData, + what=event.type, + repo=event.repo.name, + created_on=event.created_at, ) except Exception as e: logger.error('Error while syncing profile actions during sync_github', e) diff --git a/app/marketing/management/commands/sync_pull_request_with_bounty_fulfillments.py b/app/marketing/management/commands/sync_pull_request_with_bounty_fulfillments.py index 8f79ec30750..52de567c0ed 100644 --- a/app/marketing/management/commands/sync_pull_request_with_bounty_fulfillments.py +++ b/app/marketing/management/commands/sync_pull_request_with_bounty_fulfillments.py @@ -23,7 +23,7 @@ from dashboard.models import BountyFulfillment from dashboard.utils import record_funder_inaction_on_fulfillment -from git.utils import get_gh_issue_state, get_interested_actions, post_issue_comment +from git.utils import get_interested_actions, get_issue_state, post_issue_comment from marketing.mails import funder_payout_reminder logger = logging.getLogger('') @@ -54,7 +54,7 @@ def _get_time_window(self, timestamp, settings): def _check_if_bounty_is_closed(self, bounty): try: - closed_issue = get_gh_issue_state( + closed_issue = get_issue_state( bounty.github_org_name, bounty.github_repo_name, bounty.github_issue_number ) except Exception as e: diff --git a/app/marketing/management/commands/tribe_hackathon_prizes_email.py b/app/marketing/management/commands/tribe_hackathon_prizes_email.py index 258b4a83d28..6be461eca60 100644 --- a/app/marketing/management/commands/tribe_hackathon_prizes_email.py +++ b/app/marketing/management/commands/tribe_hackathon_prizes_email.py @@ -11,8 +11,6 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . ''' -import datetime - from django.conf import settings from django.core.management.base import BaseCommand from django.utils import timezone diff --git a/app/marketing/management/commands/unsubscribe_daily_emails.py b/app/marketing/management/commands/unsubscribe_daily_emails.py index 0379251bfa7..fe7c81cea58 100644 --- a/app/marketing/management/commands/unsubscribe_daily_emails.py +++ b/app/marketing/management/commands/unsubscribe_daily_emails.py @@ -17,7 +17,6 @@ along with this program. If not, see . """ -import time import warnings from django.conf import settings diff --git a/app/marketing/signals.py b/app/marketing/signals.py deleted file mode 100644 index 5fd87c8c693..00000000000 --- a/app/marketing/signals.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- coding: utf-8 -*- -''' - Copyright (C) 2021 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 django.db.models.signals import post_save -# from django.dispatch import receiver - -# from marketing.mails import nth_day_email_campaign -# from marketing.models import EmailSubscriber - - -# @receiver(post_save, sender=EmailSubscriber) - PURGED -# def create_email_subscriber(sender, instance, created, **kwargs): -# if created: -# if not EmailSubscriber.objects.filter(email=instance.email).exclude(id=instance.id).exists(): -# # this subscriber is the first time shown in our db -# # send email -# nth_day_email_campaign(1, instance) diff --git a/app/marketing/stats.py b/app/marketing/stats.py index beb4d39523f..42eb5ef7564 100644 --- a/app/marketing/stats.py +++ b/app/marketing/stats.py @@ -16,15 +16,12 @@ along with this program. If not, see . ''' -import logging -from datetime import datetime, timedelta from django.conf import settings from django.utils import timezone from marketing.models import Stat from slackclient import SlackClient -from slackclient.exceptions import SlackClientError def gitter(): @@ -142,20 +139,20 @@ def user_actions(): def github_stars(): from git.utils import get_user - reops = get_user('gitcoinco', '/repos') - forks_count = sum([repo['forks_count'] for repo in reops]) + repos = get_user('gitcoinco').get_repos() + forks_count = sum([repo.forks_count for repo in repos]) Stat.objects.create( key='github_forks_count', val=forks_count, - ) + ) - stargazers_count = sum([repo['stargazers_count'] for repo in reops]) + stargazers_count = sum([repo.stargazers_count for repo in repos]) Stat.objects.create( key='github_stargazers_count', val=stargazers_count, - ) + ) def github_issues(): @@ -165,8 +162,8 @@ def github_issues(): repos = [] for org in ['bitcoin', 'gitcoinco', 'ethereum']: - for repo in get_user(org, '/repos'): - repos.append((org, repo['name'])) + for repo in get_user(org).get_repos(): + repos.append((org, repo.name)) for org, repo in repos: issues = [] @@ -185,7 +182,7 @@ def github_issues(): created_on=timezone.now(), key=key, val=(val), - ) + ) except Exception: pass if not val: diff --git a/app/marketing/tasks.py b/app/marketing/tasks.py index 923d560432a..a28e350f8c1 100644 --- a/app/marketing/tasks.py +++ b/app/marketing/tasks.py @@ -1,8 +1,7 @@ from django.conf import settings -from django.contrib.contenttypes.models import ContentType from app.services import RedisService -from celery import app, group +from celery import app from celery.utils.log import get_task_logger from marketing.mails import new_bounty_daily as new_bounty_daily_email from marketing.mails import weekly_roundup as weekly_roundup_email diff --git a/app/marketing/tests/management/commands/test_assemble_leaderboards.py b/app/marketing/tests/management/commands/test_assemble_leaderboards.py index 6b0d810ec1e..6ae20da385a 100644 --- a/app/marketing/tests/management/commands/test_assemble_leaderboards.py +++ b/app/marketing/tests/management/commands/test_assemble_leaderboards.py @@ -17,17 +17,15 @@ along with this program. If not, see . """ -from datetime import date, datetime, timedelta +from datetime import datetime, timedelta from unittest.mock import patch from django.contrib.contenttypes.models import ContentType from django.utils import timezone -import pytest -from dashboard.models import Activity, Bounty, BountyFulfillment, Earning, Profile, Tip, UserAction -from marketing.management.commands import assemble_leaderboards +from dashboard.models import Bounty, BountyFulfillment, Earning, Profile, Tip, UserAction from marketing.management.commands.assemble_leaderboards import ( - BREAKDOWNS, do_leaderboard, run_monthly, run_quarterly, run_weekly, run_yearly, should_suppress_leaderboard, + do_leaderboard, run_monthly, run_quarterly, run_weekly, run_yearly, should_suppress_leaderboard, ) from marketing.models import LeaderboardRank from pytz import UTC diff --git a/app/marketing/tests/management/commands/test_ingest_community_events_calender.py b/app/marketing/tests/management/commands/test_ingest_community_events_calender.py index 138a067de45..be536a6714c 100644 --- a/app/marketing/tests/management/commands/test_ingest_community_events_calender.py +++ b/app/marketing/tests/management/commands/test_ingest_community_events_calender.py @@ -18,13 +18,9 @@ """ import os -from datetime import datetime, timedelta from unittest import mock -from unittest.mock import patch -from django.utils import timezone - -from icalendar import Calendar, Event +from icalendar import Calendar from marketing.management.commands.ingest_community_events_calender import Command from marketing.models import UpcomingDate from test_plus.test import TestCase diff --git a/app/marketing/tests/management/commands/test_new_bounties_email.py b/app/marketing/tests/management/commands/test_new_bounties_email.py index 845db3c1b97..f937aa036d0 100644 --- a/app/marketing/tests/management/commands/test_new_bounties_email.py +++ b/app/marketing/tests/management/commands/test_new_bounties_email.py @@ -1,12 +1,10 @@ -from datetime import datetime, timedelta +from datetime import timedelta -from django.core.management import call_command from django.utils import timezone import pytest from dashboard.models import Bounty, Profile from marketing.mails import get_bounties_for_keywords -from marketing.models import Keyword from test_plus.test import TestCase diff --git a/app/marketing/tests/management/commands/test_sync_pull_request_with_bounty_fulfillments.py b/app/marketing/tests/management/commands/test_sync_pull_request_with_bounty_fulfillments.py index d5c0795edf7..5cb64fabd55 100644 --- a/app/marketing/tests/management/commands/test_sync_pull_request_with_bounty_fulfillments.py +++ b/app/marketing/tests/management/commands/test_sync_pull_request_with_bounty_fulfillments.py @@ -19,10 +19,9 @@ """ import json import os -from datetime import datetime, timedelta +from datetime import timedelta from unittest import mock -from django.conf import settings from django.utils import timezone import marketing @@ -80,7 +79,7 @@ def setUp(self): def test_handle_simple(self, mocked_requests): """Test command sync keywords.""" start_time = timezone.now() - mock.patch('marketing.management.commands.sync_pull_request_with_bounty_fulfillments.get_gh_issue_state', lambda x, y, z: 'closed').start() + mock.patch('marketing.management.commands.sync_pull_request_with_bounty_fulfillments.get_issue_state', lambda x, y, z: 'closed').start() mock.patch('marketing.management.commands.sync_pull_request_with_bounty_fulfillments.get_interested_actions', lambda x, y: GitHubTestAPI().get_interested_actions()).start() marketing.management.commands.sync_pull_request_with_bounty_fulfillments.Command().handle(live=False) diff --git a/app/marketing/tests/test_mail_funder_payout_reminder.py b/app/marketing/tests/test_mail_funder_payout_reminder.py index 912f895afcb..5425306dc5a 100644 --- a/app/marketing/tests/test_mail_funder_payout_reminder.py +++ b/app/marketing/tests/test_mail_funder_payout_reminder.py @@ -17,11 +17,9 @@ along with this program. If not, see . """ -import os from datetime import timedelta from unittest import mock -from django.conf import settings from django.utils import timezone from dashboard.models import Bounty, BountyFulfillment, Profile diff --git a/app/marketing/tests/test_mailchimp_sync.py b/app/marketing/tests/test_mailchimp_sync.py index 89d178770b8..379039cb3db 100644 --- a/app/marketing/tests/test_mailchimp_sync.py +++ b/app/marketing/tests/test_mailchimp_sync.py @@ -25,7 +25,6 @@ from dashboard.models import Profile from marketing.management.commands.sync_mail import push_to_mailchimp from marketing.models import EmailSubscriber -from retail.emails import render_nth_day_email_campaign from test_plus.test import TestCase diff --git a/app/marketing/tests/test_mails.py b/app/marketing/tests/test_mails.py index 69d14b04dc4..4173a864721 100644 --- a/app/marketing/tests/test_mails.py +++ b/app/marketing/tests/test_mails.py @@ -22,8 +22,7 @@ from django.utils import timezone from dashboard.models import Profile -from marketing.mails import nth_day_email_campaign, setup_lang -from retail.emails import render_nth_day_email_campaign +from marketing.mails import setup_lang from test_plus.test import TestCase @@ -56,24 +55,3 @@ def test_setup_lang_no_user(self, mock_translation_activate): """Test the marketing mails setup_lang method.""" setup_lang('bademail@gitcoin.co') assert mock_translation_activate.call_count == 0 - - @patch('marketing.mails.send_mail') - def test_day_1_campaign_email(self, mock_send_mail): - """Test the campaign email for day 1 is sent.""" - - nth_day_email_campaign(self.days[0], self.user) - assert mock_send_mail.call_count == 1 - - @patch('marketing.mails.send_mail') - def test_day_2_campaign_email(self, mock_send_mail): - """Test the campaign email for day 2 is sent.""" - - nth_day_email_campaign(self.days[1], self.user) - assert mock_send_mail.call_count == 1 - - @patch('marketing.mails.send_mail') - def test_day_3_campaign_email(self, mock_send_mail): - """Test the campaign email for day 3 is sent.""" - - nth_day_email_campaign(self.days[2], self.user) - assert mock_send_mail.call_count == 1 diff --git a/app/marketing/utils.py b/app/marketing/utils.py index 9bfa302d592..f4db3ac36eb 100644 --- a/app/marketing/utils.py +++ b/app/marketing/utils.py @@ -36,42 +36,6 @@ logger = logging.getLogger(__name__) -def delete_user_from_mailchimp(email_address): - client = MailChimp(mc_user=settings.MAILCHIMP_USER, mc_api=settings.MAILCHIMP_API_KEY) - result = None - try: - result = client.search_members.get(query=email_address) - if result: - subscriber_hash = result.get('exact_matches', {}).get('members', [{}])[0].get('id', None) - except Exception as e: - logger.debug(e) - - - try: - client.lists.members.delete( - list_id=settings.MAILCHIMP_LIST_ID, - subscriber_hash=subscriber_hash, - ) - except Exception as e: - logger.debug(e) - - try: - client.lists.members.delete( - list_id=settings.MAILCHIMP_LIST_ID_HUNTERS, - subscriber_hash=subscriber_hash, - ) - except Exception as e: - logger.debug(e) - - try: - client.lists.members.delete( - list_id=settings.MAILCHIMP_LIST_ID_HUNTERS, - subscriber_hash=subscriber_hash, - ) - except Exception as e: - logger.debug(e) - - def is_deleted_account(handle): return AccountDeletionRequest.objects.filter(handle=handle.lower()).exists() @@ -149,52 +113,6 @@ def validate_slack_integration(token, channel, message=None, icon_url=''): return result -def validate_discord_integration(webhook_url, message=None, icon_url=''): - """Validate the Discord webhook URL by posting a message. - - Args: - webhook_url (str): The Discord webhook URL. - message (str): The Discord message to be sent. - Defaults to: The Gitcoin Discord integration is working fine. - icon_url (str): The URL to the avatar to be used. - Defaults to: the gitcoin helmet. - - Attributes: - result (dict): The result dictionary defining success status and error message. - message (str): The response message to display to the user. - response (obj): The Discord response object - refer to python-requests API - - Raises: - requests.exception.HTTPError: The exception is raised for any HTTP error. - - Returns: - str: The response message. - - """ - result = {'success': False, 'output': 'Test message was not sent.'} - - if message is None: - message = gettext('The Gitcoin Discord integration is working fine.') - - if not icon_url: - icon_url = static('v2/images/helmet.png') - - try: - headers = {'Content-Type': 'application/json'} - body = {"content": message, "avatar_url": icon_url} - response = requests.post( - webhook_url, headers=headers, json=body - ) - response.raise_for_status() - if response.ok: - result['output'] = _('The test message was sent to Discord.') - result['success'] = True - except requests.exceptions.HTTPError as e: - logger.error(e) - result['output'] = _('An error has occurred.') - return result - - def should_suppress_notification_email(email, email_type): from marketing.models import EmailSubscriber queryset = EmailSubscriber.objects.filter(email__iexact=email) diff --git a/app/marketing/views.py b/app/marketing/views.py index 71717294e48..e62452184ea 100644 --- a/app/marketing/views.py +++ b/app/marketing/views.py @@ -29,19 +29,17 @@ from django.contrib.auth import logout from django.contrib.auth.models import User from django.core.validators import validate_email -from django.db.models import Avg, Count, Max, Q +from django.db.models import Avg, Max from django.http import Http404, HttpResponse from django.shortcuts import redirect from django.template.response import TemplateResponse from django.urls import reverse -from django.utils import timezone, translation -from django.utils.translation import LANGUAGE_SESSION_KEY +from django.utils import timezone from django.utils.translation import gettext_lazy as _ from app.utils import sync_profile -from cacheops import cached_view from chartit import PivotChart, PivotDataPool -from dashboard.models import Activity, HackathonEvent, Profile, TokenApproval +from dashboard.models import HackathonEvent, Profile, TokenApproval from dashboard.utils import create_user_action, get_orgs_perms, is_valid_eth_address from dashboard.views import mautic_proxy_backend from gas.utils import recommend_min_gas_price_to_confirm_in_time @@ -51,8 +49,7 @@ from marketing.mails import new_feedback from marketing.models import AccountDeletionRequest, EmailSubscriber, Keyword, LeaderboardRank, UpcomingDate from marketing.utils import get_or_save_email_subscriber, validate_slack_integration -from quests.models import Quest -from retail.emails import ALL_EMAILS, render_new_bounty, render_nth_day_email_campaign +from retail.emails import render_new_bounty from retail.helpers import get_ip from townsquare.models import Announcement @@ -982,12 +979,6 @@ def leaderboard(request, key=''): return TemplateResponse(request, 'leaderboard.html', context) -@staff_member_required -def day_email_campaign(request, day): - if day not in list(range(1, 3)): - raise Http404 - response_html, _, _, = render_nth_day_email_campaign('foo@bar.com', day, 'staff member') - return HttpResponse(response_html) def trending_quests(): from quests.models import QuestAttempt @@ -1019,15 +1010,6 @@ def upcoming_grant(): grant = Grant.objects.order_by('-weighted_shuffle').first() return grant -def upcoming_hackathon(): - try: - return HackathonEvent.objects.filter(end_date__gt=timezone.now(), visible=True).order_by('-start_date') - except HackathonEvent.DoesNotExist: - try: - return [HackathonEvent.objects.filter(start_date__gte=timezone.now(), visible=True).order_by('start_date').first()] - except HackathonEvent.DoesNotExist: - return None - def get_hackathons(): from perftools.models import JSONStore from dateutil.parser import parse diff --git a/app/passport/views.py b/app/passport/views.py index efd2c7b23d6..22fd07955da 100644 --- a/app/passport/views.py +++ b/app/passport/views.py @@ -2,10 +2,8 @@ import uuid from django.conf import settings -from django.http import Http404, JsonResponse -from django.shortcuts import render +from django.http import JsonResponse -import web3 from dashboard.utils import get_web3 from eth_account.messages import defunct_hash_message diff --git a/app/perftools/management/commands/create_activity_cache.py b/app/perftools/management/commands/create_activity_cache.py index 220ad4557a7..cbdcd4ae0df 100644 --- a/app/perftools/management/commands/create_activity_cache.py +++ b/app/perftools/management/commands/create_activity_cache.py @@ -17,9 +17,8 @@ ''' from django.core.management.base import BaseCommand -from django.utils import timezone -from dashboard.models import Activity, HackathonEvent +from dashboard.models import HackathonEvent from perftools.models import JSONStore diff --git a/app/perftools/management/commands/create_page_cache.py b/app/perftools/management/commands/create_page_cache.py index 9461138c2f4..0d4b8ee4eae 100644 --- a/app/perftools/management/commands/create_page_cache.py +++ b/app/perftools/management/commands/create_page_cache.py @@ -21,7 +21,6 @@ from django.conf import settings from django.core.management.base import BaseCommand -from django.core.serializers.json import DjangoJSONEncoder from django.db import transaction from django.utils import timezone diff --git a/app/perftools/views.py b/app/perftools/views.py index 6d860554567..d689289f85e 100644 --- a/app/perftools/views.py +++ b/app/perftools/views.py @@ -2,7 +2,6 @@ from django.conf import settings from django.http import HttpResponse -from django.shortcuts import render from django.views.decorators.cache import cache_page from django.views.decorators.csrf import csrf_exempt diff --git a/app/ptokens/helpers.py b/app/ptokens/helpers.py index 9f12b827331..a6b516ea0c8 100644 --- a/app/ptokens/helpers.py +++ b/app/ptokens/helpers.py @@ -1,4 +1,4 @@ -from datetime import datetime, timezone +from datetime import datetime from dashboard.models import Activity diff --git a/app/ptokens/templates/shared/ptokens_faq.html b/app/ptokens/templates/shared/ptokens_faq.html deleted file mode 100644 index 401b8d530a1..00000000000 --- a/app/ptokens/templates/shared/ptokens_faq.html +++ /dev/null @@ -1,80 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 static humanize i18n bundle %} -{% bundle css file kudos_styles %} - -{% endbundle %} -
-
-

FAQ

- -
- - -
-

Time tokens are ERC20 tokens tied to different Gitcoin users. They represent a claim over listed uses like time, work and feedback. Each time token is unique, and different tokens may be redeemed for specific uses.

-
- - -
-

The best place to see a user’s token is directly on their Gitcoin Profile, under the Time Token tab at the top of the page. This shows you how much their tokens cost, and exactly what they can be used for.

-
- - -
-

- Once you’ve acquired a time token, you’ll need to submit a redemption request. - This request can be used on any of the Creators listed uses, and only for those uses. - Please note that it’s up to the creator to determine whether or not your request is appropriate, so please give them 24-48 hours to respond to your request. -

-
- - -
-

When a Creator accepts your request, the equivalent amount of time tokens is automatically burned and removed from the supply forever.

-
- - -
-

Under each profile you’ll see a feedback score. This is the main place where we can see how a creator is holding up their end of the bargain. Please submit an appropriate overview on what happened and we’ll look to see if further action needs to be taken.

-
- -
- -
-
- diff --git a/app/ptokens/views.py b/app/ptokens/views.py index c476c9910ff..173c403be8f 100644 --- a/app/ptokens/views.py +++ b/app/ptokens/views.py @@ -17,41 +17,25 @@ ''' from __future__ import unicode_literals -import csv -import json -import logging -from datetime import date, datetime -from decimal import Decimal - -from django.conf import settings -from django.contrib import messages +from datetime import datetime + from django.contrib.admin.views.decorators import staff_member_required -from django.contrib.auth import logout -from django.contrib.auth.models import User -from django.core.validators import validate_email -from django.db.models import Avg, Count, Max, Q, Sum -from django.http import Http404, HttpResponse, JsonResponse -from django.shortcuts import get_object_or_404, redirect +from django.db.models import Q +from django.http import JsonResponse +from django.shortcuts import get_object_or_404 from django.template.response import TemplateResponse from django.urls import reverse -from django.utils import timezone, translation -from django.utils.translation import LANGUAGE_SESSION_KEY from django.utils.translation import gettext_lazy as _ from django.views.decorators.csrf import csrf_exempt import dateutil -from app.settings import PTOKEN_ABI -from dashboard.models import Profile -from dashboard.utils import get_web3 from inbox.utils import send_notification_to_user -from ptokens.emails import render_ptoken_redemption_request from ptokens.helpers import record_ptoken_activity from ptokens.mails import ( send_ptoken_redemption_accepted, send_ptoken_redemption_cancelled, send_ptoken_redemption_rejected, send_ptoken_redemption_request, ) from ptokens.models import PersonalToken, PTokenEvent, PurchasePToken, RedemptionToken -from web3 import Web3 @staff_member_required diff --git a/app/retail/emails.py b/app/retail/emails.py index 47846b43a6f..48ff3e72a78 100644 --- a/app/retail/emails.py +++ b/app/retail/emails.py @@ -38,7 +38,6 @@ from grants.models import Contribution, Grant, Subscription from marketing.models import LeaderboardRank from marketing.utils import get_or_save_email_subscriber -from premailer import Premailer from retail.utils import build_utm_tracking, strip_double_chars, strip_html logger = logging.getLogger(__name__) @@ -102,26 +101,6 @@ def render_featured_funded_bounty(bounty): return response_html, response_txt, subject -def render_nth_day_email_campaign(to_email, nth, firstname): - subject_map = { - 1: "Day 1: Growing Open Source", - 2: "Day 2: Using Gitcoin's Issue Explorer", - 3: "Learning Blockchain" - } - - subject = subject_map[nth] - - params = { - "firstname": firstname, - "subscriber": get_or_save_email_subscriber(to_email, "internal"), - "email_type": "welcome_mail" - } - response_html = premailer_transform(render_to_string(f"emails/campaigns/email_campaign_day_{nth}.html", params)) - response_txt = render_to_string(f"emails/campaigns/email_campaign_day_{nth}.txt", params) - - return response_html, response_txt, subject - - def render_new_grant_email(grant): params = {'grant': grant, 'utm_tracking': build_utm_tracking('new_grant')} response_html = premailer_transform(render_to_string("emails/grants/new_grant.html", params)) @@ -670,8 +649,7 @@ def email_to_profile(to_email): def render_new_bounty(to_email, bounties, old_bounties, offset=3, quest_of_the_day={}, upcoming_grant={}, hackathons=(), latest_activities={}, from_date=date.today(), days_ago=7, chats_count=0, featured_bounties=[]): from dateutil.parser import parse - from townsquare.utils import is_email_townsquare_enabled, is_there_an_action_available - from marketing.views import upcoming_dates, email_announcements, trending_avatar + from marketing.views import email_announcements, trending_avatar sub = get_or_save_email_subscriber(to_email, 'internal') counter = 0 diff --git a/app/retail/management/commands/warm_cache.py b/app/retail/management/commands/warm_cache.py index f2e9b5e6e40..d2dc2f3c163 100644 --- a/app/retail/management/commands/warm_cache.py +++ b/app/retail/management/commands/warm_cache.py @@ -23,8 +23,6 @@ from django.urls import reverse from django.utils import timezone -from retail.utils import programming_languages - warnings.filterwarnings("ignore", category=DeprecationWarning) logging.getLogger("requests").setLevel(logging.WARNING) logging.getLogger("urllib3").setLevel(logging.WARNING) diff --git a/app/retail/templates/community.html b/app/retail/templates/community.html deleted file mode 100644 index 549e7657921..00000000000 --- a/app/retail/templates/community.html +++ /dev/null @@ -1,40 +0,0 @@ -{% load i18n static %} -
-
-

{% trans h1 %}

- {% if community_members == alumni %} -

{% trans h2 %}
- {% trans "These are the ones who have opted to be public about it" %} -

- {% else %} -

{% trans h2 %}

- {% endif %} -
-
-
-
- {% for avatar, name, handle, org in community_members %} -
-
- - - -

- - {{ name }} - -

-

{{ org }}

-
-
- {% endfor %} -
-
- diff --git a/app/retail/templates/emails/bounty_small.html b/app/retail/templates/emails/bounty_small.html deleted file mode 100644 index 1f05bac1002..00000000000 --- a/app/retail/templates/emails/bounty_small.html +++ /dev/null @@ -1,92 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 %} - - - -
- {% if primer %} -
-

{{primer}}

-

- {% endif %} -
-
- - {% if bounty.org_name %} -

- @{{bounty.org_name}} -

- {% endif %} -
-
- - - - - -
-

- {{bounty.org_name}}: {{bounty.title_or_desc}} -
- Amount: {{bounty.value_true}} {{bounty.token_name}} - {% if bounty.value_in_usdt_now %} - (${{bounty.value_in_usdt_now}}) - {% endif %} -
- Posted: {{bounty.web3_created | naturaltime}} - {% if bounty.keywords %} -
- Keywords: {{bounty.keywords}} - {% endif %} - -

- -
- {% include 'emails/shared_bounty_actions.html' with action='custom' action_copy='View' action_url=bounty.absolute_url %} -
-
-
-
diff --git a/app/retail/templates/emails/campaigns/email_campaign_day_1.html b/app/retail/templates/emails/campaigns/email_campaign_day_1.html deleted file mode 100644 index e38f617fcdc..00000000000 --- a/app/retail/templates/emails/campaigns/email_campaign_day_1.html +++ /dev/null @@ -1,98 +0,0 @@ -{% extends 'emails/template.html' %} - -{% comment %} - Copyright (C) 2021 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 %} - - - -
- -

Day 1: Growing Open Source

- -
-

- {% trans "Hi" %} {{ firstname }}, -

- -

- {% blocktrans %} - Welcome to Gitcoin! Our mission is to - grow open source. Open source software is a force for good in the world, and we believe it’s the key to saving the internet. While a generation of software careers has been powered by open source software, it’s still hard to make a living working on open source. We plan to bring our mission to the Web 3.0 ecosystem. - {% endblocktrans %} -

- -

- {% blocktrans %} - The issue Explorer is our first tool towards this end. Use it to find paid issues to work on in open source. Help build projects, ship code, and get paid in crypto tokens. Give it a try and let us know what you think! - {% endblocktrans %} -

- -

- {% blocktrans %} - As time passes, we’ll send you more ways to get involved in Gitcoin. For now, we have a few areas you can nerd out with us, depending on where you’re most comfortable. -

- {% endblocktrans %} - -

- -

- {% blocktrans %} - Oh, and here’s a kudos for getting to the bottom of this e-mail. :) welcome to the Gitcoin Family! - {% endblocktrans %} - -

-

- {% blocktrans %} - See you on the interwebs, - {% endblocktrans %} -

- -

- {% blocktrans %} - Kevin & Vivek - {% endblocktrans %} -

- -
-
- -{% endblock %} diff --git a/app/retail/templates/emails/campaigns/email_campaign_day_1.txt b/app/retail/templates/emails/campaigns/email_campaign_day_1.txt deleted file mode 100644 index b3b1b56a5ce..00000000000 --- a/app/retail/templates/emails/campaigns/email_campaign_day_1.txt +++ /dev/null @@ -1,19 +0,0 @@ -Day 1: Growing Open Source - - -Welcome to Gitcoin! Our mission is to grow open source. Open source software is a force for good in the world, and we believe it’s the key to saving the internet. While a generation of software careers has been powered by open source software, it’s still hard to make a living working on open source. We plan to bring our mission to the Web 3.0 ecosystem. - -The Issue Explorer is our first tool towards this end. Use it to find paid issues to work on in open source. Help build projects, ship code, and get paid in crypto tokens. Give it a try and let us know what you think! - -As time passes, we’ll send you more ways to get involved in Gitcoin. For now, we have a few areas you can nerd out with us, depending on where you’re most comfortable. - -Join the Slack channel and say hi on #community-intros! - -Follow us on Twitter for updates on open source software. - -Add the Gitcoin Livestream to your cal (iCal, Google) for Friday at 2PM EST. We’ll have two weekly product demos from the blockchain & open source ecosystems (like Decentraland or meta-transactions with Austin Griffith). - -Oh, and here’s a kudos for getting to the bottom of this e-mail. :) welcome to the Gitcoin Family! - -See you on the interwebs, - diff --git a/app/retail/templates/emails/campaigns/email_campaign_day_2.html b/app/retail/templates/emails/campaigns/email_campaign_day_2.html deleted file mode 100644 index 6e914e8c20f..00000000000 --- a/app/retail/templates/emails/campaigns/email_campaign_day_2.html +++ /dev/null @@ -1,92 +0,0 @@ -{% extends 'emails/template.html' %} - -{% comment %} - Copyright (C) 2021 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 %} - - - -
- -

Day 2: Finding A Gitcoin Issue For You

- -
-

- {% trans "Hi" %} {{ firstname }}, -

- -

- {% blocktrans %} - By this point, you might have poked around the Gitcoin Issue Explorer to see the open source projects you could make money by contributing! If interested in doing work, here’s how. - {% endblocktrans %} -

- -

Start Work

-

- {% blocktrans %} - Check if a bounty requires approval from the funder by checking the Permissions section within the bounty. - {% endblocktrans %} -

- {% blocktrans %} - If it states "Permissionless" you can click "Start Work" to provide a short action plan and get started on working. - {% endblocktrans %} -

- {% blocktrans %} - However if it states anything else, you can click the button "Express Interest" to provide a brief message to the bounty owner that you want to work on this issue. - {% endblocktrans %} -
- {% blocktrans %} - From here, you’ll receive an email and a notification when the issue is ready for you to work. - {% endblocktrans %} -

- -

- {% blocktrans %} - You’ll be able to submit a PR directly onto the repo maintainer’s project from there. It’s that simple! - {% endblocktrans %} -

- -

- {% blocktrans %} - CTA: Check out the Issue Explorer if you haven’t already! If you don’t find good fits, let us know an open source project that you’d like to see using Gitcoin. No promises, but we might have something up our sleeve. - {% endblocktrans %} -

- -
-
- -{% endblock %} diff --git a/app/retail/templates/emails/campaigns/email_campaign_day_2.txt b/app/retail/templates/emails/campaigns/email_campaign_day_2.txt deleted file mode 100644 index 0871b4f06eb..00000000000 --- a/app/retail/templates/emails/campaigns/email_campaign_day_2.txt +++ /dev/null @@ -1,14 +0,0 @@ -Day 2: Finding a Gitcoin issue for you - -By this point, you might have poked around the Gitcoin Issue Explorer to see the open source projects you could make money by contributing! If you are interested in start working, here’s how. - -Start Work -Check if a bounty requires approval from the funder by checking the Permissions section within the bounty. - -If it states "Permissionless" you can click "Start Work" to provide a short action plan and get started on working. - -However if it states anything else, you can click the button "Express Interest" to provide a brief message to the bounty owner that you want to work on this issue. From here, you’ll receive an email and a notification when the issue is ready for you to work. - -You’ll be able to submit a PR directly onto the repo maintainer’s project from there. It’s that simple! - -CTA: Check out the Issue Explorer if you haven’t already! If you don’t find good fits, let us know an open source project that you’d like to see using Gitcoin. No promises, but we might have something up our sleeve. diff --git a/app/retail/templates/emails/campaigns/email_campaign_day_3.html b/app/retail/templates/emails/campaigns/email_campaign_day_3.html deleted file mode 100644 index 525ad8a55d4..00000000000 --- a/app/retail/templates/emails/campaigns/email_campaign_day_3.html +++ /dev/null @@ -1,97 +0,0 @@ -{% extends 'emails/template.html' %} - -{% comment %} - Copyright (C) 2021 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 %} - - - -
- -

Day 3: Leveraging The Gitcoin Community

- -
-

- {% trans "Hi" %} {{ firstname }}, -

- -

- {% blocktrans %} - Gitcoin Core is 11 team members, yet we’ve seen contributions from well over 100 developers using Gitcoin. We’d like to see you do the same! Here’s how to leverage a firehose of developer talent to do more, faster. - {% endblocktrans %} -

- -

- {% blocktrans %} - Posting a bounty can be done in 90 seconds, once you know Gitcoin is an option to accelerate your development. Adding bounties to an existing project is like adding icing to a cake. We take what’s already great about OSS (mission, freedom, and great people) and add financial incentivization, a home base for projects seeking contributors, funding for developers, and community. - {% endblocktrans %} -

- -

- {% blocktrans %} - Alongside posting bounties on the Issue Explorer, Gitcoin offers two complimentary products for open source software enthusiasts. -

- {% endblocktrans %} -

- -

- {% blocktrans %} - Over the first week of you joining, we're going to send over some information on key products and education - tools available in the Gitcoin ecosystem. For now, we have a few areas you can nerd out with us, depending on where - you're most comfortable. - {% endblocktrans %} - -

- -

- {% blocktrans %} - Community is what makes Open Source work. A strong OSS community is inclusive, helpful, and mission-oriented. The Gitcoin community is 20,000 software developers who are focused on the tooling for the new world. Is there a chance that your next career connection could come from the Gitcoin community? Come find out! - {% endblocktrans %} - -

- -
-
- -{% endblock %} diff --git a/app/retail/templates/emails/campaigns/email_campaign_day_3.txt b/app/retail/templates/emails/campaigns/email_campaign_day_3.txt deleted file mode 100644 index 3a3b3548fde..00000000000 --- a/app/retail/templates/emails/campaigns/email_campaign_day_3.txt +++ /dev/null @@ -1,16 +0,0 @@ -Day 3: Leveraging the community - -Gitcoin Core is 11 team members, yet we’ve seen contributions from well over 100 developers using Gitcoin. We’d like to see you do the same! Here’s how to leverage a firehose of developer talent to do more, faster. - -Posting a bounty can be done in 90 seconds, once you know Gitcoin is an option to accelerate your development. Adding bounties to an existing project is like adding icing to a cake. We take what’s already great about OSS (mission, freedom, and great people) and add financial incentivization, a home base for projects seeking contributors, funding for developers, and community. - -Alongside posting bounties on the Issue Explorer, Gitcoin offers two complimentary products for open source software enthusiasts. - -Codefund: Sustain your OSS project with ethical advertising - -Gitcoin Kudos: Have a contributor that’s doing fantastic work? Send them a token of appreciation using Kudos! Here’s one for you :) - -Gitcoin Grants: Recurring funding for open source projects. - -Community is what makes Open Source work. A strong OSS community is inclusive, helpful, and mission-oriented. The Gitcoin community is 20,000 software developers who are focused on the tooling for the new world. Is there a chance that your next career connection could come from the Gitcoin community? Come find out! - diff --git a/app/retail/templates/emails/grants/transaction_summary.html b/app/retail/templates/emails/grants/transaction_summary.html deleted file mode 100644 index c1b0e41eec4..00000000000 --- a/app/retail/templates/emails/grants/transaction_summary.html +++ /dev/null @@ -1,147 +0,0 @@ -{% extends '../emails/template.html' %} -{% comment %} - Copyright (C) 2021 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 static grants_extra humanize %} - -{% block content %} - - -{% trans -

{% trans "Grant" %} '{{ grant.title }}' {% trans "has received more funding" %}!

-
- -
- - - -
- - {{ grant.title }} - -

- {{ grant.description }} -

-
-
-
-

- {% trans "Funding Recieved in the Last 24 Hours" %} -

- - - {% for i in 'xxxxxxxxxx' %} - - - - - - - - {% endfor %} -
- {% trans - -

{{ subscription.contributor_profile }}

-
-

- {{ subscription.amount_per_period|floatformat:4|intcomma }} -

-
-

{{ subscription.contributor_timestamp }}

-
- - {% trans "View Transaction" %} - -
-
-
-

- Total Grant received - {{ grant.amount_received }} {% trans "USD" %} -

-
-
-
-{% trans "View Grant" %} -
-{% endblock %} diff --git a/app/retail/templates/emails/grants/transaction_summary.txt b/app/retail/templates/emails/grants/transaction_summary.txt deleted file mode 100644 index bd8d01d2680..00000000000 --- a/app/retail/templates/emails/grants/transaction_summary.txt +++ /dev/null @@ -1,31 +0,0 @@ -{% load i18n humanize %} - -{% trans "Transaction Summary" %} - -{% trans "Grant" %} {{ grant.title }} {% trans "has received more funding" %} - -{{ grant.description }} - -{% trans "Funding Received in the Last 24 Hours" %} - -{{ subscription.contributor_profile_img }} - -{% trans "Grant Funder" %} - -{{ subscription.contributor_profile }} - -{{ subscription.amount_per_period|floatformat:4|intcomma }} - -{{ subscription.contributor_timestamp }} - -{{ subscription.contributor_url }} - -{% trans "View Transaction" %} - - -{% trans "Amount Received" %} - -{{ grant.amount_received }} {% trans "USD" %} - - -{% trans "View Grant" %} {{ grant.reference_url }} diff --git a/app/retail/templates/emails/grants/update_notification.html b/app/retail/templates/emails/grants/update_notification.html deleted file mode 100644 index 77ccc6e8e75..00000000000 --- a/app/retail/templates/emails/grants/update_notification.html +++ /dev/null @@ -1,158 +0,0 @@ -{% extends 'emails/template.html' %} -{% comment %} -Copyright (C) 2021 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 static %} - -{% block content %} - - - -
- - {% trans -

{% trans "An update has been posted for:" %}

-
- - - -
- - {{ grant.title }} - -

- {{ grant.description }} -

- -
- -
-
- {% trans -
-
-

{{ activity.title }}

-

{{ activity.date }}

-

{{ activity.description }}

-
-
- -
- -
- {% trans "View Grant" %} -
- -
- {% endblock %} \ No newline at end of file diff --git a/app/retail/templates/emails/grants/update_notification.txt b/app/retail/templates/emails/grants/update_notification.txt deleted file mode 100644 index 15761d1cd9f..00000000000 --- a/app/retail/templates/emails/grants/update_notification.txt +++ /dev/null @@ -1,19 +0,0 @@ -{% load i18n %} - -{% trans "Update!" %} - -{% trans "An update has been posted for:" %} - -{{ grant.title }} - -{{ grant.desciption }} - -{% trans "New Grant Update." %} - -{{ activity.title }} - -{{ activity.date }} - -{{ activity.description }} - -{% trans "View Grant" %} {{ grant.reference_url }} diff --git a/app/retail/templates/emails/quest_small.html b/app/retail/templates/emails/quest_small.html deleted file mode 100644 index 963f8423809..00000000000 --- a/app/retail/templates/emails/quest_small.html +++ /dev/null @@ -1,111 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 %} - - - -
- - {% if quest and small %} - - - - - - - - -
- {% if not quest.enemy_img_url or quest.enemy_img_url == "/static/" %} - - {% else %} - - {% endif %} -
-

{{quest.title}}

-

{{quest.description}}

- {% include 'emails/shared_quest_actions.html' with action='custom' action_copy='Play Quest' action_url=quest.url %} - {% endif %} - - {% if quest and not small %} -
-
- -
-
- - - - - -
-

- {{quest.title}} -
- Attempts: {{ quest.attempts.count }} {{quest.token_name}} ({{quest.success_pct}}% Winners!) -
- Prize: {{quest.kudos_reward.ui_name}} -
- Posted: {{quest.created_on | naturaltime}} - {% if quest.tags %} -
- Tags: {% for tag in quest.tags %}#{{ tag }} {% endfor %} - {% endif %} -

- -
- {% include 'emails/shared_quest_actions.html' with action='custom' action_copy='View' action_url=quest.url %} -
-
-
- {% endif %} -
diff --git a/app/retail/templates/emails/shared_activities_actions.html b/app/retail/templates/emails/shared_activities_actions.html deleted file mode 100644 index 78fdfd24a0c..00000000000 --- a/app/retail/templates/emails/shared_activities_actions.html +++ /dev/null @@ -1,31 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 %}{% if action == 'custom' %} - -{% else %} - -{% endif %} -{% if action2 == 'custom' %} -
- -{% endif %} diff --git a/app/retail/templates/jobs.html b/app/retail/templates/jobs.html deleted file mode 100644 index 8f2c0a4bbaa..00000000000 --- a/app/retail/templates/jobs.html +++ /dev/null @@ -1,174 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 static %} - - - - - {% include 'shared/head.html' %} - {% include 'shared/cards_pic.html' %} - - - - - -
- {% include 'shared/tag_manager_2.html' %} - {% include 'shared/top_nav.html' with class='d-md-flex' %} -
- {% include 'home/nav.html' %} - {% include 'shared/minihero.html' with h1='Jobs' position_h1_below_logo=1 %} -
- -
-
-
-
- {% trans "We are built by & for coders" %} -
-
- -
-
-
-
-
{% trans "Gitcoin is built with Gitcoin" %}
-
{% trans "(we dogfood)" %}
-
-
- -
-
-
-
- -
-
-
-
-
-
- {% blocktrans %} - We believe in try-before-you-buy hiring. - {% endblocktrans %} -
-
{% trans "Want to show off your chops? Contribute to some bounties before applying to jobs." %}
-
-
- -
-
- -
-
-
- {% blocktrans %} - We believe in remote work. - {% endblocktrans %} -
{% trans "We live and travel all over the world." %}
-
-
- -
-
- -
-
-
- {% blocktrans %} - We believe in open source. - {% endblocktrans %} -
-
{% trans "Gitcoin is completely accessible to the public." %}
-
-
- -
-
-
-
- - -
- -
-
-
-

- {% trans "Want to try out working with us? Get started with some Bounties!" %} -

-
-
- -
- {% if job_listings|length %} -
-
-
-

{% trans "Already done a few bounties?"%}

-
{% trans "The best way to apply is below"%}
-
-
-
- {% for job in job_listings %} -
-
{{ job.title }}
-
-
- {{ job.description }} -
-
- -
- {% endfor %} -
-
- {% else %} -
-
-
-

{% trans "Full Time Positions"%}

-
{% trans "We have no positions available right now."%}
-
-
-
- - {% endif %} - - - {% include 'shared/footer.html' %} - {% include 'shared/footer_scripts.html' with slim=1 %} -
- - diff --git a/app/retail/templates/shared/activity-vue.html b/app/retail/templates/shared/activity-vue.html deleted file mode 100644 index 2417082c460..00000000000 --- a/app/retail/templates/shared/activity-vue.html +++ /dev/null @@ -1,425 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 humanize i18n static %} - - - diff --git a/app/retail/templates/shared/compass.html b/app/retail/templates/shared/compass.html deleted file mode 100644 index 1c828622d33..00000000000 --- a/app/retail/templates/shared/compass.html +++ /dev/null @@ -1,24 +0,0 @@ -{% comment %} - Copyright (C) 2021 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 static %} - -
- Compass dashes - Compass inner - Compass outer - Compass arrow -
diff --git a/app/retail/utils.py b/app/retail/utils.py index ac2f50f2c07..39d17148134 100644 --- a/app/retail/utils.py +++ b/app/retail/utils.py @@ -31,7 +31,6 @@ from django.utils.translation import gettext_lazy as _ import pytz -from cacheops import CacheMiss, cache from grants.models import Contribution, Grant from marketing.models import Alumni, LeaderboardRank, ManualStat, Stat from requests_oauthlib import OAuth2Session @@ -57,17 +56,6 @@ def profile_time(self, name): self.last_time = time.time() -def get_github_user_profile(token): - github = OAuth2Session( - settings.GITHUB_CLIENT_ID, - token=token, - ) - - creds = github.get('https://api.github.com/user').json() - print(creds) - return creds - - def strip_html(html): tag_re = re.compile(r'(|<[^>]*>)') no_tags = tag_re.sub('', html) diff --git a/app/retail/views.py b/app/retail/views.py index f1fea51fc44..5fd9e8d181a 100644 --- a/app/retail/views.py +++ b/app/retail/views.py @@ -17,48 +17,33 @@ along with this program. If not, see . ''' -import datetime import json import logging import re -import time from json import loads as json_parse -from os import walk as walkdir from django.conf import settings -from django.contrib.admin.views.decorators import staff_member_required -from django.core.exceptions import ValidationError -from django.core.paginator import Paginator -from django.core.validators import validate_email from django.db.models import Count, Q, Subquery from django.http import Http404, JsonResponse from django.shortcuts import redirect from django.template.response import TemplateResponse from django.templatetags.static import static -from django.urls import reverse from django.utils import timezone -from django.utils.translation import gettext from django.utils.translation import gettext_lazy as _ from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_http_methods -from app.utils import get_default_network, get_profiles_from_text -from cacheops import cached_as, cached_view, cached_view_as -from dashboard.models import ( - Activity, Bounty, HackathonEvent, Profile, Tip, TribeMember, get_my_earnings_counter_profiles, get_my_grants, -) +from app.utils import get_profiles_from_text +from cacheops import cached_view +from dashboard.models import Activity, HackathonEvent, Profile, Tip, get_my_earnings_counter_profiles, get_my_grants from dashboard.notifications import amount_usdt_open_work, open_bounties from dashboard.tasks import grant_update_email_task from economy.models import Token -from grants.models import Grant from marketing.mails import mention_email, new_funding_limit_increase_request, new_token_request, wall_post_email -from marketing.models import Alumni, EmailInventory, Job, LeaderboardRank -from marketing.utils import get_or_save_email_subscriber, invite_to_slack +from marketing.models import EmailInventory from perftools.models import JSONStore from ratelimit.decorators import ratelimit -from retail.emails import render_nth_day_email_campaign from retail.helpers import get_ip -from townsquare.models import PinnedPost from townsquare.tasks import increment_view_counts from townsquare.utils import can_pin diff --git a/app/townsquare/utils.py b/app/townsquare/utils.py index 4861914c07f..5766d0aa69a 100644 --- a/app/townsquare/utils.py +++ b/app/townsquare/utils.py @@ -59,7 +59,3 @@ def is_email_townsquare_enabled(email): if not user: return False return is_user_townsquare_enabled(user) - - -def is_there_an_action_available(): - return Offer.objects.current().exists() diff --git a/docs/ENVIRONMENT_VARIABLES.md b/docs/ENVIRONMENT_VARIABLES.md index a392eed55a1..75be7185a0e 100644 --- a/docs/ENVIRONMENT_VARIABLES.md +++ b/docs/ENVIRONMENT_VARIABLES.md @@ -62,7 +62,6 @@ All of the environment variables used by this application conform to the [`djang | --- | --- | --- | --- | | GITHUB_API_BASE_URL | The Github API URL. | `str` | https://api.github.com | | GITHUB_AUTH_BASE_URL | The Github OAuth authorization URL. | `str` | https://github.com/login/oauth/authorize | -| GITHUB_TOKEN_URL | The Github OAuth access token URL. | `str` | https://github.com/login/oauth/access_token | | GITHUB_SCOPE | The Github application scope. | `str` | read:user,user:email,read:org | | GITHUB_CLIENT_ID | The client ID of the Github OAuth app. | `str` | TODO | | GITHUB_CLIENT_SECRET | The client secret of the Github OAuth app. | `str` | TODO | diff --git a/pydocmd.yml b/pydocmd.yml index 529c84cbd44..9a499b186b6 100644 --- a/pydocmd.yml +++ b/pydocmd.yml @@ -15,8 +15,6 @@ generate: - app.sitemaps++ - app/static_storage.md: - app.static_storage++ - - app/thumbnail_processors.md: - - app.thumbnail_processors++ - app/utils.md: - app.utils++ - avatar/admin.md: @@ -103,8 +101,6 @@ generate: - grants.abi++ - grants/admin.md: - grants.admin++ - - grants/forms.md: - - grants.forms++ - grants/models.md: - grants.models++ - grants/router.md: @@ -222,7 +218,6 @@ pages: - Auth Pipeline: app/pipeline.md - Sitemaps: app/sitemaps.md - Static Storage: app/static_storage.md - - Thumbnail Processors: app/thumbnail_processors.md - Utilities: app/utils.md - Avatar: - Admin: avatar/admin.md diff --git a/requirements/base.txt b/requirements/base.txt index 47a14781904..6c92d041811 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -57,7 +57,6 @@ Werkzeug[watchdog]==0.15.5 imageio boto3==1.7.81 django-storages==1.11.1 -easy-thumbnails==2.5 eth-account==0.2.2 django-classy-tags==0.8.0 django-cookie-law==2.0.1 From f9a5dc24a22f75ab0a7577a29c1e068d23aa1311 Mon Sep 17 00:00:00 2001 From: Graham Dixon Date: Thu, 15 Jul 2021 22:03:12 +0100 Subject: [PATCH 08/10] GITC-214: Fixes sync_profile by storing the NamedUser.raw_data vs NamedUser instance (#9299) --- app/app/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/app/utils.py b/app/app/utils.py index 60351e33a51..cbd740947e6 100644 --- a/app/app/utils.py +++ b/app/app/utils.py @@ -125,7 +125,7 @@ def actually_sync_profile(handle, user=None, hide_profile=True): logger.warning(f'Failed to fetch github username {handle}', exc_info=True, extra={'handle': handle}) return None - defaults = {'last_sync_date': timezone.now(), 'data': data} + defaults = {'last_sync_date': timezone.now(), 'data': data.raw_data} if user and isinstance(user, User): defaults['user'] = user From 62deca419b7ba47a9bc6dd144e9f399abc5ecf33 Mon Sep 17 00:00:00 2001 From: Graham Dixon Date: Mon, 19 Jul 2021 02:22:46 +0100 Subject: [PATCH 09/10] GITC-213: Purges generate_grants_leaderboard as the resultant JSONStore is unused (#9298) --- app/grants/utils.py | 36 ------------------- .../management/commands/create_page_cache.py | 14 +------- scripts/debug/add_grants_team_members.py | 2 +- 3 files changed, 2 insertions(+), 50 deletions(-) diff --git a/app/grants/utils.py b/app/grants/utils.py index b4c15cf0ecd..62058e1a9fb 100644 --- a/app/grants/utils.py +++ b/app/grants/utils.py @@ -93,42 +93,6 @@ def get_upload_filename(instance, filename): file_path = os.path.basename(filename) return f"grants/{getattr(instance, '_path', '')}/{salt}/{file_path}" - -def get_leaderboard(): - return JSONStore.objects.filter(view='grants', key='leaderboard').order_by('-pk').first().data - - -def generate_grants_leaderboard(max_items=100): - from grants.models import Subscription, Contribution - handles = Subscription.objects.exclude(contributor_profile__isnull=True).values_list('contributor_profile__handle', flat=True) - default_dict = { - 'rank': None, - 'no': 0, - 'sum': 0, - 'handle': None, - } - users_to_results = { ele : default_dict.copy() for ele in handles } - - # get all contribution attributes - for contribution in Contribution.objects.exclude(profile_for_clr__isnull=True).select_related('subscription'): - key = contribution.subscription.contributor_profile.handle - users_to_results[key]['handle'] = key - amount = contribution.subscription.get_converted_amount(False) - if amount: - users_to_results[key]['no'] += 1 - users_to_results[key]['sum'] += round(amount) - # prepare response for view - items = [] - counter = 1 - for item in sorted(users_to_results.items(), key=lambda kv: kv[1]['sum'], reverse=True): - item = item[1] - if item['no']: - item['rank'] = counter - items.append(item) - counter += 1 - return items[:max_items] - - def is_grant_team_member(grant, profile): """Checks to see if profile is a grant team member diff --git a/app/perftools/management/commands/create_page_cache.py b/app/perftools/management/commands/create_page_cache.py index 0d4b8ee4eae..230ee788602 100644 --- a/app/perftools/management/commands/create_page_cache.py +++ b/app/perftools/management/commands/create_page_cache.py @@ -30,7 +30,7 @@ from dashboard.utils import set_hackathon_event from economy.models import EncodeAnything from grants.models import Contribution, Grant, GrantCategory, GrantType -from grants.utils import generate_grants_leaderboard, get_clr_rounds_metadata +from grants.utils import get_clr_rounds_metadata from marketing.models import Stat from perftools.models import JSONStore from quests.helpers import generate_leaderboard @@ -261,17 +261,6 @@ def create_activity_cache(): data=json.loads(json.dumps(data, cls=EncodeAnything)), ) -def create_grants_cache(): - print('grants') - view = 'grants' - keyword = 'leaderboard' - data = generate_grants_leaderboard() - JSONStore.objects.create( - view=view, - key=keyword, - data=json.loads(json.dumps(data, cls=EncodeAnything)), - ) - def create_quests_cache(): @@ -401,7 +390,6 @@ def handle(self, *args, **options): operations.append(create_top_grant_spenders_cache) operations.append(create_avatar_cache) operations.append(create_quests_cache) - operations.append(create_grants_cache) operations.append(create_contributor_landing_page_context) operations.append(create_hackathon_cache) operations.append(create_hackathon_list_page_cache) diff --git a/scripts/debug/add_grants_team_members.py b/scripts/debug/add_grants_team_members.py index de1f2f35b69..785ea6e10ab 100644 --- a/scripts/debug/add_grants_team_members.py +++ b/scripts/debug/add_grants_team_members.py @@ -1,6 +1,6 @@ from dashboard.models import Profile from grants.models import * -from grants.utils import get_leaderboard, is_grant_team_member +from grants.utils import is_grant_team_member handles = [ 'adamstallard', From fe23823c0bf0985c8b876dae3230bb465ae30a3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Octavio=20Amuch=C3=A1stegui?= Date: Mon, 19 Jul 2021 10:53:24 -0300 Subject: [PATCH 10/10] Fix AnonymousUser post on status (#9261) * block user to submit if not logged and fix error * remove print --- app/assets/v2/js/status.js | 8 ++++++-- app/retail/views.py | 18 +++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/app/assets/v2/js/status.js b/app/assets/v2/js/status.js index 1644ddb4645..26050a921d1 100644 --- a/app/assets/v2/js/status.js +++ b/app/assets/v2/js/status.js @@ -428,6 +428,10 @@ $(document).ready(function() { return; } + if (!document.contxt.github_handle) { + return; + } + if (typeof ga !== 'undefined') { ga('send', 'event', 'Submit Status Update', 'click', 'Person'); } @@ -501,7 +505,7 @@ $(document).ready(function() { localStorage.setItem(lskey, the_message); _alert( { message: gettext('An error occurred. Please try again.') }, - 'error' + 'danger' ); }; @@ -547,7 +551,7 @@ $(document).ready(function() { } else { _alert( { message: gettext('An error occurred. Please try again.') }, - 'error' + 'danger' ); } }).catch(err => fail_callback()); diff --git a/app/retail/views.py b/app/retail/views.py index 5fd9e8d181a..f0e01395fd5 100644 --- a/app/retail/views.py +++ b/app/retail/views.py @@ -908,7 +908,12 @@ def create_status_update(request): issue_re = re.compile(r'^(?:https?://)?(?:github\.com)/(?:[\w,\-,\_]+)/(?:[\w,\-,\_]+)/issues/(?:[\d]+)') response = {} - if request.POST: + if request.POST and request.user.is_authenticated: + if (request.user.profile.is_blocked or request.user.profile.shadowbanned): + response['status'] = 400 + response['message'] = 'Status updated!' + return JsonResponse(response, status=400) + profile = request.user.profile title = request.POST.get('data') resource = request.POST.get('resource', '') @@ -919,12 +924,6 @@ def create_status_update(request): attach_token_name = request.POST.get('attachTokenName', '') tx_id = request.POST.get('attachTxId', '') - if request.user.is_authenticated and (request.user.profile.is_blocked or request.user.profile.shadowbanned): - response['status'] = 200 - response['message'] = 'Status updated!' - return JsonResponse(response, status=400) - - kwargs = { 'activity_type': 'status_update', 'metadata': { @@ -1015,6 +1014,11 @@ def create_status_update(request): response['message'] = 'Bad Request' logger.error('Status Update error - Error: (%s) - Handle: (%s)', e, profile.handle if profile else '') return JsonResponse(response, status=400) + else: + response['status'] = 401 + response['message'] = 'Not logged in!' + return JsonResponse(status=response['status'], data={'status': 401,'message':response['message']}) + return JsonResponse(response)