From 39c5bc3f161f80a5f994788f876dbaa6c860dddc Mon Sep 17 00:00:00 2001 From: Owocki Date: Mon, 7 May 2018 17:36:55 -0600 Subject: [PATCH 01/39] work schemes --- app/assets/v2/js/pages/dashboard.js | 8 ++- app/assets/v2/js/pages/new_bounty.js | 4 ++ app/assets/v2/js/shared.js | 6 ++ app/dashboard/helpers.py | 2 + app/dashboard/models.py | 11 ++++ app/dashboard/router.py | 4 +- .../templates/shared/sidebar_search.html | 63 +++++++++++++++++++ app/dashboard/templates/submit_bounty.html | 19 ++++++ 8 files changed, 113 insertions(+), 4 deletions(-) diff --git a/app/assets/v2/js/pages/dashboard.js b/app/assets/v2/js/pages/dashboard.js index 3e39170c20c..e603db15c4e 100644 --- a/app/assets/v2/js/pages/dashboard.js +++ b/app/assets/v2/js/pages/dashboard.js @@ -15,7 +15,9 @@ var sidebar_keys = [ 'bounty_filter', 'network', 'idx_status', - 'tech_stack' + 'tech_stack', + 'work_scheme', + 'application_scheme', ]; var localStorage; @@ -452,8 +454,8 @@ var refreshBounties = function(event) { result.action = result['url']; result['title'] = result['title'] ? result['title'] : result['github_url']; - - result['p'] = ((result['experience_level'] ? result['experience_level'] : 'Unknown Experience Level') + ' • '); + var work_scheme = ucwords(result['work_scheme']) + ' • '; + result['p'] = work_scheme + ((result['experience_level'] ? result['experience_level'] : 'Unknown Experience Level') + ' • '); if (result['status'] === 'done') result['p'] += 'Done'; diff --git a/app/assets/v2/js/pages/new_bounty.js b/app/assets/v2/js/pages/new_bounty.js index a77713bd1c8..2b34e1ef04d 100644 --- a/app/assets/v2/js/pages/new_bounty.js +++ b/app/assets/v2/js/pages/new_bounty.js @@ -137,6 +137,10 @@ $(document).ready(function() { githubUsername: metadata.githubUsername, address: '' // Fill this in later }, + schemes: { + work_scheme: data.work_scheme, + application_scheme: data.application_scheme, + }, funders: [], categories: metadata.issueKeywords.split(','), created: (new Date().getTime() / 1000) | 0, diff --git a/app/assets/v2/js/shared.js b/app/assets/v2/js/shared.js index 3067fac412c..80e2b349ba7 100644 --- a/app/assets/v2/js/shared.js +++ b/app/assets/v2/js/shared.js @@ -95,6 +95,12 @@ var sanitizeAPIResults = function(results) { return results; }; +function ucwords (str) { + return (str + '').replace(/^([a-z])|\s+([a-z])/g, function ($1) { + return $1.toUpperCase(); + }); +} + var sanitize = function(str) { if (typeof str != 'string') { return str; diff --git a/app/dashboard/helpers.py b/app/dashboard/helpers.py index 5737dd7c799..3aa48bb25ae 100644 --- a/app/dashboard/helpers.py +++ b/app/dashboard/helpers.py @@ -401,6 +401,8 @@ def create_new_bounty(old_bounties, bounty_payload, bounty_details, bounty_id): accepted=accepted, interested_comment=interested_comment_id, submissions_comment=submissions_comment_id, + work_scheme=bounty_payload.get('schemes', {}).get('work_scheme', 'traditional'), + application_scheme=bounty_payload.get('schemes', {}).get('application_scheme', 'permissionless'), # These fields are after initial bounty creation, in bounty_details.js expires_date=timezone.make_aware( timezone.datetime.fromtimestamp(bounty_details.get('deadline')), diff --git a/app/dashboard/models.py b/app/dashboard/models.py index 2da0e2a5d1d..db1ea030f64 100644 --- a/app/dashboard/models.py +++ b/app/dashboard/models.py @@ -84,6 +84,15 @@ class Bounty(SuperModel): """ + APPLICATION_SCHEMES = [ + ('permissionless', 'permissionless'), + ('approval', 'approval'), + ] + WORK_SCHEMES = [ + ('traditional', 'traditional'), + ('contest', 'contest'), + ('cooperative', 'cooperative'), + ] BOUNTY_TYPES = [ ('Bug', 'Bug'), ('Security', 'Security'), @@ -157,6 +166,8 @@ class Bounty(SuperModel): fulfillment_submitted_on = models.DateTimeField(null=True, blank=True) fulfillment_started_on = models.DateTimeField(null=True, blank=True) canceled_on = models.DateTimeField(null=True, blank=True) + work_scheme = models.CharField(max_length=50, choices=WORK_SCHEMES, default='traditional') + application_scheme = models.CharField(max_length=50, choices=APPLICATION_SCHEMES, default='permissionless') token_value_time_peg = models.DateTimeField(blank=True, null=True) token_value_in_usdt = models.DecimalField(default=0, decimal_places=2, max_digits=50, blank=True, null=True) diff --git a/app/dashboard/router.py b/app/dashboard/router.py index 51b2ea24e9b..c5108730fa8 100644 --- a/app/dashboard/router.py +++ b/app/dashboard/router.py @@ -76,6 +76,7 @@ class Meta: 'github_issue_number', 'github_org_name', 'github_repo_name', 'idx_status', 'token_value_time_peg', 'fulfillment_accepted_on', 'fulfillment_submitted_on', 'fulfillment_started_on', 'canceled_on', 'action_urls', + 'work_scheme', 'application_scheme', ) def create(self, validated_data): @@ -118,7 +119,8 @@ def get_queryset(self): # filtering for key in ['raw_data', 'experience_level', 'project_length', 'bounty_type', 'bounty_owner_address', - 'idx_status', 'network', 'bounty_owner_github_username', 'standard_bounties_id']: + 'idx_status', 'network', 'bounty_owner_github_username', 'standard_bounties_id', + 'application_scheme', 'work_scheme' ]: if key in param_keys: # special hack just for looking up bounties posted by a certain person request_key = key if key != 'bounty_owner_address' else 'coinbase' diff --git a/app/dashboard/templates/shared/sidebar_search.html b/app/dashboard/templates/shared/sidebar_search.html index 7a1260a9164..7ab0793fef2 100644 --- a/app/dashboard/templates/shared/sidebar_search.html +++ b/app/dashboard/templates/shared/sidebar_search.html @@ -118,6 +118,69 @@ +
+
{% trans "Application Scheme" %}
+
+ - What is this?
+
+ + +
+ +
+
+
+ + +
+ +
+
+
+ + +
+ +
+
+
+
+ +
+
{% trans "Work Scheme" %}
+
+ - What is this?
+
+ + +
+ +
+
+
+ + +
+ +
+
+
+ + +
+ +
+
+
+ + +
+ +
+
+
+
+
{% trans "Tech Stack" %}
diff --git a/app/dashboard/templates/submit_bounty.html b/app/dashboard/templates/submit_bounty.html index 39055bb7b33..f653ce6ef6b 100644 --- a/app/dashboard/templates/submit_bounty.html +++ b/app/dashboard/templates/submit_bounty.html @@ -130,6 +130,25 @@

{% trans "Fund an Issue" %}

+
+ +
+ +
+
+
+ +
+ +
+
From 958af750eb02cd06ff329e11216e6d3c17127104 Mon Sep 17 00:00:00 2001 From: Owocki Date: Mon, 7 May 2018 17:37:01 -0600 Subject: [PATCH 02/39] work schemes --- .../migrations/0070_auto_20180507_2327.py | 23 +++++++++++ .../templates/shared/app_scheme_tooltip.html | 34 ++++++++++++++++ .../templates/shared/work_scheme_tooltip.html | 40 +++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 app/dashboard/migrations/0070_auto_20180507_2327.py create mode 100644 app/dashboard/templates/shared/app_scheme_tooltip.html create mode 100644 app/dashboard/templates/shared/work_scheme_tooltip.html diff --git a/app/dashboard/migrations/0070_auto_20180507_2327.py b/app/dashboard/migrations/0070_auto_20180507_2327.py new file mode 100644 index 00000000000..6ac10a01308 --- /dev/null +++ b/app/dashboard/migrations/0070_auto_20180507_2327.py @@ -0,0 +1,23 @@ +# Generated by Django 2.0.4 on 2018-05-07 23:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dashboard', '0069_auto_20180507_1841'), + ] + + operations = [ + migrations.AddField( + model_name='bounty', + name='application_scheme', + field=models.CharField(choices=[('permissionless', 'permissionless'), ('approval', 'approval')], default='permissionless', max_length=50), + ), + migrations.AddField( + model_name='bounty', + name='work_scheme', + field=models.CharField(choices=[('traditional', 'traditional'), ('contest', 'contest'), ('cooperative', 'cooperative')], default='traditional', max_length=50), + ), + ] diff --git a/app/dashboard/templates/shared/app_scheme_tooltip.html b/app/dashboard/templates/shared/app_scheme_tooltip.html new file mode 100644 index 00000000000..cea257fe622 --- /dev/null +++ b/app/dashboard/templates/shared/app_scheme_tooltip.html @@ -0,0 +1,34 @@ +{% comment %} + Copyright (C) 2018 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 "Application Schemes" %}

+
+
+
1
+
+ {% trans "Permissionless" %}: {% trans "Anyone can start work" %} +
+
+
+
2
+
+ {% trans "Approval Required" %}: {% trans "Submitter will approve bounty hunters.." %} +
+
+
+
diff --git a/app/dashboard/templates/shared/work_scheme_tooltip.html b/app/dashboard/templates/shared/work_scheme_tooltip.html new file mode 100644 index 00000000000..6fe95b9203e --- /dev/null +++ b/app/dashboard/templates/shared/work_scheme_tooltip.html @@ -0,0 +1,40 @@ +{% comment %} + Copyright (C) 2018 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 "Work Schemes" %}

+
+
+
1
+
+ {% trans "Traditional" %}: {% trans "1 worker at a time, 1 is paid out" %} +
+
+
+
2
+
+ {% trans "Contest" %}: {% trans "many workers at a time, 1 is paid out" %} +
+
+
+
2
+
+ {% trans "Cooperative" %}: {% trans "many workers at a time, many are paid out" %} +
+
+
+
From d17cbd082e98ea910ee97918b309fc5f6643fbd5 Mon Sep 17 00:00:00 2001 From: Owocki Date: Mon, 7 May 2018 19:14:04 -0600 Subject: [PATCH 03/39] enforce limits on # ppl starting work --- app/assets/v2/js/pages/bounty_details.js | 3 ++- app/assets/v2/js/pages/dashboard.js | 3 ++- app/assets/v2/js/pages/new_bounty.js | 2 +- app/assets/v2/js/shared.js | 8 ++++---- app/dashboard/views.py | 7 +++++++ 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app/assets/v2/js/pages/bounty_details.js b/app/assets/v2/js/pages/bounty_details.js index b3f518c3669..1873e5dfec6 100644 --- a/app/assets/v2/js/pages/bounty_details.js +++ b/app/assets/v2/js/pages/bounty_details.js @@ -514,7 +514,8 @@ var do_actions = function(result) { pull_interest_list(result['pk'], function(is_interested) { // which actions should we show? - var show_start_stop_work = is_still_on_happy_path; + var should_block_from_starting_work = !is_interested && result['work_scheme'] == 'traditional' && (result['status'] == 'started' || result['status'] == 'submitted') + var show_start_stop_work = is_still_on_happy_path && !should_block_from_starting_work; var show_github_link = result['github_url'].substring(0, 4) == 'http'; var show_submit_work = true; var show_kill_bounty = !is_status_done && !is_status_expired && !is_status_cancelled; diff --git a/app/assets/v2/js/pages/dashboard.js b/app/assets/v2/js/pages/dashboard.js index e603db15c4e..8b671a45ffc 100644 --- a/app/assets/v2/js/pages/dashboard.js +++ b/app/assets/v2/js/pages/dashboard.js @@ -17,7 +17,7 @@ var sidebar_keys = [ 'idx_status', 'tech_stack', 'work_scheme', - 'application_scheme', + 'application_scheme' ]; var localStorage; @@ -455,6 +455,7 @@ var refreshBounties = function(event) { result['title'] = result['title'] ? result['title'] : result['github_url']; var work_scheme = ucwords(result['work_scheme']) + ' • '; + result['p'] = work_scheme + ((result['experience_level'] ? result['experience_level'] : 'Unknown Experience Level') + ' • '); if (result['status'] === 'done') diff --git a/app/assets/v2/js/pages/new_bounty.js b/app/assets/v2/js/pages/new_bounty.js index 2b34e1ef04d..d97953a8edd 100644 --- a/app/assets/v2/js/pages/new_bounty.js +++ b/app/assets/v2/js/pages/new_bounty.js @@ -139,7 +139,7 @@ $(document).ready(function() { }, schemes: { work_scheme: data.work_scheme, - application_scheme: data.application_scheme, + application_scheme: data.application_scheme }, funders: [], categories: metadata.issueKeywords.split(','), diff --git a/app/assets/v2/js/shared.js b/app/assets/v2/js/shared.js index 80e2b349ba7..6acbbbb2880 100644 --- a/app/assets/v2/js/shared.js +++ b/app/assets/v2/js/shared.js @@ -95,10 +95,10 @@ var sanitizeAPIResults = function(results) { return results; }; -function ucwords (str) { - return (str + '').replace(/^([a-z])|\s+([a-z])/g, function ($1) { - return $1.toUpperCase(); - }); +function ucwords(str) { + return (str + '').replace(/^([a-z])|\s+([a-z])/g, function($1) { + return $1.toUpperCase(); + }); } var sanitize = function(str) { diff --git a/app/dashboard/views.py b/app/dashboard/views.py index 19956647399..a277197aa10 100644 --- a/app/dashboard/views.py +++ b/app/dashboard/views.py @@ -170,6 +170,13 @@ def new_interest(request, bounty_id): except Bounty.DoesNotExist: raise Http404 + is_too_many_already_started = bounty.work_scheme == 'traditional' and bounty.interested.count() > 0 + if is_too_many_already_started: + return JsonResponse({ + 'error': _(f'There is already someone working on this bounty.'), + 'success': False}, + status=401) + num_issues = 3 active_bounties = Bounty.objects.current().filter(idx_status__in=['open', 'started']) num_active = Interest.objects.filter(profile_id=profile_id, bounty__in=active_bounties).count() From 0f2bf25c66f1b0c49a7e1cbe747e767887d97246 Mon Sep 17 00:00:00 2001 From: Owocki Date: Tue, 8 May 2018 07:51:34 -0600 Subject: [PATCH 04/39] work scheme on profile --- app/assets/v2/js/pages/bounty_details.js | 10 +++++++++- app/dashboard/templates/bounty_details.html | 8 ++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/assets/v2/js/pages/bounty_details.js b/app/assets/v2/js/pages/bounty_details.js index 1873e5dfec6..53a255211fe 100644 --- a/app/assets/v2/js/pages/bounty_details.js +++ b/app/assets/v2/js/pages/bounty_details.js @@ -58,6 +58,8 @@ var rows = [ 'token_value_time_peg', 'web3_created', 'status', + 'work_scheme', + 'application_scheme', 'bounty_owner_address', 'bounty_owner_email', 'issue_description', @@ -133,7 +135,13 @@ var callbacks = { 'bounty_type': unknown_if_empty, 'bounty_owner_github_username': gitcoin_ize, 'bounty_owner_name': function(key, val, result) { - return [ 'bounty_owner_name', result.metadata.fullName ]; + return [ 'bounty_owner_name', ucwords(result.metadata.fullName) ]; + }, + 'application_scheme': function(key, val, result) { + return [ 'application_scheme', ucwords(result.application_scheme) ]; + }, + 'work_scheme': function(key, val, result) { + return [ 'work_scheme', result.work_scheme ]; }, 'issue_keywords': function(key, val, result) { if (!result.metadata.issueKeywords || result.metadata.issueKeywords.length == 0) diff --git a/app/dashboard/templates/bounty_details.html b/app/dashboard/templates/bounty_details.html index ada9e69b229..f97c1524256 100644 --- a/app/dashboard/templates/bounty_details.html +++ b/app/dashboard/templates/bounty_details.html @@ -99,6 +99,14 @@

{% trans "Issue Type" %}
+
+ {% trans "Work Scheme" %} + +
+
+ {% trans "Application Scheme" %} + +
From d47279cf4d7dc899737b421e0435aec5338efdd3 Mon Sep 17 00:00:00 2001 From: Owocki Date: Sun, 20 May 2018 11:46:42 -0600 Subject: [PATCH 05/39] small copy change --- app/dashboard/templates/submit_bounty.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/dashboard/templates/submit_bounty.html b/app/dashboard/templates/submit_bounty.html index f653ce6ef6b..34d4ed409bb 100644 --- a/app/dashboard/templates/submit_bounty.html +++ b/app/dashboard/templates/submit_bounty.html @@ -131,7 +131,7 @@

{% trans "Fund an Issue" %}

- +
@@ -42,7 +45,13 @@
{% trans "Great to see that you're interested
diff --git a/app/dashboard/templates/bounty_details.html b/app/dashboard/templates/bounty_details.html index b7dcb5c12f7..132c9203d74 100644 --- a/app/dashboard/templates/bounty_details.html +++ b/app/dashboard/templates/bounty_details.html @@ -100,12 +100,14 @@

- {% trans "Work Scheme" %} - + {% trans "Work Scheme" %} + > +
- {% trans "Application Scheme" %} - + {% trans "Worker Application Scheme" %} + > +
diff --git a/app/dashboard/views.py b/app/dashboard/views.py index 75933f095e5..4cfe2603b9b 100644 --- a/app/dashboard/views.py +++ b/app/dashboard/views.py @@ -54,7 +54,7 @@ ) from .notifications import ( maybe_market_tip_to_email, maybe_market_tip_to_github, maybe_market_tip_to_slack, maybe_market_to_slack, - maybe_market_to_twitter, maybe_market_to_user_slack, + maybe_market_to_twitter, maybe_market_to_user_slack, maybe_market_to_github, ) from .utils import ( get_bounty, get_bounty_id, get_context, has_tx_mined, record_user_action_on_interest, web3_process_bounty, @@ -115,8 +115,13 @@ def helper_handle_access_token(request, access_token): def create_new_interest_helper(bounty, user, issue_message): + approval_required = bounty.application_scheme == 'approval' profile_id = user.profile.pk - interest = Interest.objects.create(profile_id=profile_id, issue_message=issue_message) + interest = Interest.objects.create( + profile_id=profile_id, + issue_message=issue_message, + pending=approval_required, + ) bounty.interested.add(interest) record_user_action(user, 'start_work', interest) maybe_market_to_slack(bounty, 'start_work') @@ -133,7 +138,10 @@ def gh_login(request): def get_interest_modal(request): + bounty = Bounty.objects.get(pk=request.GET.get("pk")) + context = { + 'bounty': bounty, 'active': 'get_interest_modal', 'title': _('Add Interest'), 'user_logged_in': request.user.is_authenticated, @@ -197,14 +205,14 @@ def new_interest(request, bounty_id): if profile.has_been_removed_by_staff(): return JsonResponse({ - 'error': _('Because a staff member has had to remove you from a bounty in the past, you are unable to start more work at this time. Please contact support.'), + 'error': _('Because a staff member has had to remove you from a bounty in the past, you are unable to start more work at this time. Please leave a message on slack if you feel this message is in error.'), 'success': False}, status=401) try: Interest.objects.get(profile_id=profile_id, bounty=bounty) return JsonResponse({ - 'error': _('You have already expressed interest in this bounty!'), + 'error': _('You have already started work on this bounty!'), 'success': False}, status=401) except Interest.DoesNotExist: @@ -220,11 +228,20 @@ def new_interest(request, bounty_id): Interest.objects.filter(pk__in=list(bounty_ids)).delete() return JsonResponse({ - 'error': _('You have already expressed interest in this bounty!'), + 'error': _('You have already started work on this bounty!'), 'success': False}, status=401) - return JsonResponse({'success': True, 'profile': ProfileSerializer(interest.profile).data}) + msg = _("You have started work.") + approval_required = bounty.application_scheme == 'approval' + if approval_required: + msg = _("You have applied to start work. If approved, you will be notified via email.") + + return JsonResponse({ + 'success': True, + 'profile': ProfileSerializer(interest.profile).data, + 'msg': msg, + }) @csrf_exempt @@ -286,7 +303,10 @@ def remove_interest(request, bounty_id): bounty.interested.remove(*interest_ids) Interest.objects.filter(pk__in=list(interest_ids)).delete() - return JsonResponse({'success': True}) + return JsonResponse({ + 'success': True, + 'msg': _("You've stopped working on this, thanks for letting us know."), + }) @require_POST @@ -347,7 +367,11 @@ def uninterested(request, bounty_id, profile_id): bounty_uninterested(profile.user.email, bounty, interest) else: print("no email sent -- user was not found") - return JsonResponse({'success': True}) + + return JsonResponse({ + 'success': True, + 'msg': _("You've stopped working on this, thanks for letting us know."), + }) @csrf_exempt @@ -635,6 +659,58 @@ def cancel_bounty(request): return TemplateResponse(request, 'kill_bounty.html', params) +def helper_handle_snooze(request, bounty): + snooze_days = int(request.GET.get('snooze', 0)) + if snooze_days: + is_funder = bounty.is_funder(request.user.username.lower()) + is_staff = request.user.is_staff + if is_funder or is_staff: + bounty.snooze_warnings_for_days = snooze_days + bounty.save() + messages.success(request, _(f'Warning messages have been snoozed for {snooze_days} days')) + else: + messages.warning(request, _('Only the funder of this bounty may snooze warnings.')) + + +def helper_handle_approvals(request, bounty): + mutate_worker_action = request.GET.get('mutate_worker_action', None) + mutate_worker_action_past_tense = 'approved' if mutate_worker_action == 'approve' else "rejected" + worker = request.GET.get('worker', None) + if mutate_worker_action: + is_funder = bounty.is_funder(request.user.username.lower()) + is_staff = request.user.is_staff + if is_funder or is_staff: + interests = bounty.interested.filter(pending=True, profile__handle=worker) + if not interests.exists(): + messages.warning(request, _('This worker does not exist or is not in a pending state. Please check your link and try again.')) + return + interest = interests.first() + + if mutate_worker_action == 'approve': + interest.pending = False + interest.save() + + # TODO: send an email when + + maybe_market_to_github(bounty, 'work_started', profile_pairs=bounty.profile_pairs) + maybe_market_to_slack(bounty, 'worker_approved') + maybe_market_to_user_slack(bounty, 'worker_approved') + maybe_market_to_twitter(bounty, 'worker_approved') + + + else: + bounty.interested.remove(interest) + interest.delete() + + maybe_market_to_slack(bounty, 'worker_rejected') + maybe_market_to_user_slack(bounty, 'worker_rejected') + maybe_market_to_twitter(bounty, 'worker_rejected') + + messages.success(request, _(f'{worker} has been {mutate_worker_action_past_tense}')) + else: + messages.warning(request, _('Only the funder of this bounty may perform this action.')) + + def bounty_details(request, ghuser='', ghrepo='', ghissue=0, stdbounties_id=None): """Display the bounty details. @@ -697,16 +773,8 @@ def bounty_details(request, ghuser='', ghrepo='', ghissue=0, stdbounties_id=None params['interested_profiles'] = bounty.interested.select_related('profile').all() params['avatar_url'] = bounty.get_avatar_url(True) - snooze_days = int(request.GET.get('snooze', 0)) - if snooze_days: - is_funder = bounty.is_funder(request.user.username.lower()) - is_staff = request.user.is_staff - if is_funder or is_staff: - bounty.snooze_warnings_for_days = snooze_days - bounty.save() - messages.success(request, _(f'Warning messages have been snoozed for {snooze_days} days')) - else: - messages.warning(request, _('Only the funder of this bounty may snooze warnings.')) + helper_handle_snooze(request, bounty) + helper_handle_approvals(request, bounty) except Bounty.DoesNotExist: pass From 9935daf9ded49fcbe1585a9beef0d616bd6e13c4 Mon Sep 17 00:00:00 2001 From: Owocki Date: Sun, 20 May 2018 14:32:22 -0600 Subject: [PATCH 07/39] make fix --- app/assets/v2/js/pages/bounty_details.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/assets/v2/js/pages/bounty_details.js b/app/assets/v2/js/pages/bounty_details.js index d256e9bb551..84a26773582 100644 --- a/app/assets/v2/js/pages/bounty_details.js +++ b/app/assets/v2/js/pages/bounty_details.js @@ -429,7 +429,8 @@ var show_interest_modal = function() { var self = this; setTimeout(function() { - var url = '/interest/modal?redirect=' + window.location.pathname + "&pk=" + document.result['pk'] + var url = '/interest/modal?redirect=' + window.location.pathname + '&pk=' + document.result['pk']; + $.get(url, function(newHTML) { var modal = $(newHTML).appendTo('body').modal({ modalClass: 'modal add-interest-modal' @@ -539,7 +540,7 @@ var do_actions = function(result) { pull_interest_list(result['pk'], function(is_interested) { // which actions should we show? - var should_block_from_starting_work = !is_interested && result['work_scheme'] == 'traditional' && (result['status'] == 'started' || result['status'] == 'submitted') + var should_block_from_starting_work = !is_interested && result['work_scheme'] == 'traditional' && (result['status'] == 'started' || result['status'] == 'submitted'); var show_start_stop_work = is_still_on_happy_path && !should_block_from_starting_work; var show_github_link = result['github_url'].substring(0, 4) == 'http'; var show_submit_work = true; From 47d23d44af313edd98409b063e49a3bdd3a8c2a2 Mon Sep 17 00:00:00 2001 From: Owocki Date: Sun, 20 May 2018 14:33:44 -0600 Subject: [PATCH 08/39] syntax fixes vai make fix --- app/assets/v2/css/bounty.css | 6 ++++-- app/dashboard/views.py | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/assets/v2/css/bounty.css b/app/assets/v2/css/bounty.css index 53a5f41a195..165b12af6db 100644 --- a/app/assets/v2/css/bounty.css +++ b/app/assets/v2/css/bounty.css @@ -191,7 +191,8 @@ body { padding: 5px; } -#network, .red_warning { +.red_warning, +#network { background-color: #fbe0d6; color: #fb9470; padding: 5px 10px; @@ -200,7 +201,8 @@ body { display: block; } -#network a, .red_warning a{ +.red_warning a, +#network a{ color: #fb9470; font-weight: bold; } diff --git a/app/dashboard/views.py b/app/dashboard/views.py index 4cfe2603b9b..92dffc5846e 100644 --- a/app/dashboard/views.py +++ b/app/dashboard/views.py @@ -53,8 +53,8 @@ ToolVote, UserAction, ) from .notifications import ( - maybe_market_tip_to_email, maybe_market_tip_to_github, maybe_market_tip_to_slack, maybe_market_to_slack, - maybe_market_to_twitter, maybe_market_to_user_slack, maybe_market_to_github, + maybe_market_tip_to_email, maybe_market_tip_to_github, maybe_market_tip_to_slack, maybe_market_to_github, + maybe_market_to_slack, maybe_market_to_twitter, maybe_market_to_user_slack, ) from .utils import ( get_bounty, get_bounty_id, get_context, has_tx_mined, record_user_action_on_interest, web3_process_bounty, From 6d7f95700384416fd67842f5b91a088a3084c3f3 Mon Sep 17 00:00:00 2001 From: Owocki Date: Mon, 21 May 2018 10:18:01 -0600 Subject: [PATCH 09/39] todos --- app/dashboard/views.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/dashboard/views.py b/app/dashboard/views.py index 92dffc5846e..c8aee06fde6 100644 --- a/app/dashboard/views.py +++ b/app/dashboard/views.py @@ -690,7 +690,7 @@ def helper_handle_approvals(request, bounty): interest.pending = False interest.save() - # TODO: send an email when + # TODO: send an email when this happen maybe_market_to_github(bounty, 'work_started', profile_pairs=bounty.profile_pairs) maybe_market_to_slack(bounty, 'worker_approved') @@ -702,6 +702,8 @@ def helper_handle_approvals(request, bounty): bounty.interested.remove(interest) interest.delete() + # TODO: send an email when this happen + maybe_market_to_slack(bounty, 'worker_rejected') maybe_market_to_user_slack(bounty, 'worker_rejected') maybe_market_to_twitter(bounty, 'worker_rejected') From 07548391ef9be5508ce20f9f4f7c0a37778ba3b5 Mon Sep 17 00:00:00 2001 From: Owocki Date: Tue, 22 May 2018 11:51:53 -0600 Subject: [PATCH 10/39] quick update --- app/dashboard/helpers.py | 2 +- app/dashboard/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/dashboard/helpers.py b/app/dashboard/helpers.py index d32b82d9673..7cb9a3cb844 100644 --- a/app/dashboard/helpers.py +++ b/app/dashboard/helpers.py @@ -414,7 +414,7 @@ def create_new_bounty(old_bounties, bounty_payload, bounty_details, bounty_id): # Currently we are only considering the latest fulfillment. Std bounties supports multiple. # If any of the fulfillments have been accepted, the bounty is now accepted and complete. accepted = any([fulfillment.get('accepted') for fulfillment in fulfillments]) - + with transaction.atomic(): old_bounties = old_bounties.distinct().order_by('created_on') latest_old_bounty = None diff --git a/app/dashboard/views.py b/app/dashboard/views.py index c8aee06fde6..318615d538d 100644 --- a/app/dashboard/views.py +++ b/app/dashboard/views.py @@ -186,7 +186,7 @@ def new_interest(request, bounty_id): except Bounty.DoesNotExist: raise Http404 - is_too_many_already_started = bounty.work_scheme == 'traditional' and bounty.interested.count() > 0 + is_too_many_already_started = bounty.work_scheme == 'traditional' and bounty.interested.filter(pending=False).count() > 0 if is_too_many_already_started: return JsonResponse({ 'error': _(f'There is already someone working on this bounty.'), From 27b8c2207c1442e0b1ed57dc7b71bbf9da909cfa Mon Sep 17 00:00:00 2001 From: Mark Beacom Date: Wed, 23 May 2018 03:55:53 -0400 Subject: [PATCH 11/39] Stickler fixes, move work scheme fulfillment to method, rebuild migrations --- app/dashboard/helpers.py | 2 +- ...0507_2327.py => 0075_auto_20180523_0751.py} | 9 +++++++-- .../migrations/0075_merge_20180520_1753.py | 14 -------------- .../migrations/0076_interest_pending.py | 18 ------------------ app/dashboard/models.py | 16 ++++++++++++++++ app/dashboard/notifications.py | 7 ++++--- app/dashboard/router.py | 2 +- app/dashboard/views.py | 10 +++++----- 8 files changed, 34 insertions(+), 44 deletions(-) rename app/dashboard/migrations/{0070_auto_20180507_2327.py => 0075_auto_20180523_0751.py} (66%) delete mode 100644 app/dashboard/migrations/0075_merge_20180520_1753.py delete mode 100644 app/dashboard/migrations/0076_interest_pending.py diff --git a/app/dashboard/helpers.py b/app/dashboard/helpers.py index 7cb9a3cb844..d32b82d9673 100644 --- a/app/dashboard/helpers.py +++ b/app/dashboard/helpers.py @@ -414,7 +414,7 @@ def create_new_bounty(old_bounties, bounty_payload, bounty_details, bounty_id): # Currently we are only considering the latest fulfillment. Std bounties supports multiple. # If any of the fulfillments have been accepted, the bounty is now accepted and complete. accepted = any([fulfillment.get('accepted') for fulfillment in fulfillments]) - + with transaction.atomic(): old_bounties = old_bounties.distinct().order_by('created_on') latest_old_bounty = None diff --git a/app/dashboard/migrations/0070_auto_20180507_2327.py b/app/dashboard/migrations/0075_auto_20180523_0751.py similarity index 66% rename from app/dashboard/migrations/0070_auto_20180507_2327.py rename to app/dashboard/migrations/0075_auto_20180523_0751.py index 6ac10a01308..dafa1662755 100644 --- a/app/dashboard/migrations/0070_auto_20180507_2327.py +++ b/app/dashboard/migrations/0075_auto_20180523_0751.py @@ -1,4 +1,4 @@ -# Generated by Django 2.0.4 on 2018-05-07 23:27 +# Generated by Django 2.0.5 on 2018-05-23 07:51 from django.db import migrations, models @@ -6,7 +6,7 @@ class Migration(migrations.Migration): dependencies = [ - ('dashboard', '0069_auto_20180507_1841'), + ('dashboard', '0074_auto_20180515_1510'), ] operations = [ @@ -20,4 +20,9 @@ class Migration(migrations.Migration): name='work_scheme', field=models.CharField(choices=[('traditional', 'traditional'), ('contest', 'contest'), ('cooperative', 'cooperative')], default='traditional', max_length=50), ), + migrations.AddField( + model_name='interest', + name='pending', + field=models.BooleanField(default=False, help_text='If this option is chosen, this interest is pending and not yet active'), + ), ] diff --git a/app/dashboard/migrations/0075_merge_20180520_1753.py b/app/dashboard/migrations/0075_merge_20180520_1753.py deleted file mode 100644 index 4406e925632..00000000000 --- a/app/dashboard/migrations/0075_merge_20180520_1753.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 2.0.5 on 2018-05-20 17:53 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('dashboard', '0074_auto_20180515_1510'), - ('dashboard', '0070_auto_20180507_2327'), - ] - - operations = [ - ] diff --git a/app/dashboard/migrations/0076_interest_pending.py b/app/dashboard/migrations/0076_interest_pending.py deleted file mode 100644 index 52429803ff7..00000000000 --- a/app/dashboard/migrations/0076_interest_pending.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.0.5 on 2018-05-20 18:40 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('dashboard', '0075_merge_20180520_1753'), - ] - - operations = [ - migrations.AddField( - model_name='interest', - name='pending', - field=models.BooleanField(default=False, help_text='If this option is chosen, this interest is pending and not yet active'), - ), - ] diff --git a/app/dashboard/models.py b/app/dashboard/models.py index 5518bf5c634..43a5b7d7c94 100644 --- a/app/dashboard/models.py +++ b/app/dashboard/models.py @@ -706,6 +706,22 @@ def action_urls(self): urls.update({item: f'/issue/{item}?{params}'}) return urls + @property + def is_work_scheme_fulfilled(self): + """Determine whether or not the work scheme is currently fulfilled. + + Todo: + * Add remaining work scheme fulfillment handling. + + Returns: + bool: Whether or not the Bounty work scheme is fully staffed. + + """ + fulfilled = False + if self.work_scheme == 'traditional': + fulfilled = self.interested.filter(pending=False).exists() + return fulfilled + class BountyFulfillmentQuerySet(models.QuerySet): """Handle the manager queryset for BountyFulfillments.""" diff --git a/app/dashboard/notifications.py b/app/dashboard/notifications.py index b0da8a6c2b8..40fb5ef4783 100644 --- a/app/dashboard/notifications.py +++ b/app/dashboard/notifications.py @@ -408,10 +408,10 @@ def build_github_notification(bounty, event_name, profile_pairs=None): f"${amount_open_work} more funded OSS Work available on the [Gitcoin Issue Explorer](https://gitcoin.co/explorer)\n" elif event_name == 'work_started': interested = bounty.interested.all() - interestd_plural = "s" if interested.count() != 0 else "" + # interested_plural = "s" if interested.count() != 0 else "" from_now = naturaltime(bounty.expires_date) started_work = bounty.interested.filter(pending=False).all() - pending_approval = bounty.interested.filter(pending=True).all() + # pending_approval = bounty.interested.filter(pending=True).all() bounty_owner_clear = f"@{bounty.bounty_owner_github_username}" if bounty.bounty_owner_github_username else "" approval_required = bounty.application_scheme == 'approval' @@ -437,7 +437,8 @@ def build_github_notification(bounty, event_name, profile_pairs=None): issue_message = interest.issue_message.strip() if issue_message: - msg += f"{bounty_owner_clear} they have the following comments/questions for you:\n\n```{issue_message}```" + msg += f"{bounty_owner_clear} they have the following comments/questions for you:\n\n" \ + f"```{issue_message}```" msg += "\n\n" elif event_name == 'work_submitted': diff --git a/app/dashboard/router.py b/app/dashboard/router.py index 6657d41c145..27b4df6f04a 100644 --- a/app/dashboard/router.py +++ b/app/dashboard/router.py @@ -132,7 +132,7 @@ def get_queryset(self): # filtering for key in ['raw_data', 'experience_level', 'project_length', 'bounty_type', 'bounty_owner_address', 'idx_status', 'network', 'bounty_owner_github_username', 'standard_bounties_id', - 'application_scheme', 'work_scheme' ]: + 'application_scheme', 'work_scheme']: if key in param_keys: # special hack just for looking up bounties posted by a certain person request_key = key if key != 'bounty_owner_address' else 'coinbase' diff --git a/app/dashboard/views.py b/app/dashboard/views.py index 3ade398f631..b3e8b39c4f5 100644 --- a/app/dashboard/views.py +++ b/app/dashboard/views.py @@ -186,8 +186,7 @@ def new_interest(request, bounty_id): except Bounty.DoesNotExist: raise Http404 - is_too_many_already_started = bounty.work_scheme == 'traditional' and bounty.interested.filter(pending=False).count() > 0 - if is_too_many_already_started: + if bounty.is_work_scheme_fulfilled: return JsonResponse({ 'error': _(f'There is already someone working on this bounty.'), 'success': False}, @@ -205,7 +204,8 @@ def new_interest(request, bounty_id): if profile.has_been_removed_by_staff(): return JsonResponse({ - 'error': _('Because a staff member has had to remove you from a bounty in the past, you are unable to start more work at this time. Please leave a message on slack if you feel this message is in error.'), + 'error': _('Because a staff member has had to remove you from a bounty in the past, you are unable to start' + 'more work at this time. Please leave a message on slack if you feel this message is in error.'), 'success': False}, status=401) @@ -682,7 +682,8 @@ def helper_handle_approvals(request, bounty): if is_funder or is_staff: interests = bounty.interested.filter(pending=True, profile__handle=worker) if not interests.exists(): - messages.warning(request, _('This worker does not exist or is not in a pending state. Please check your link and try again.')) + messages.warning(request, + _('This worker does not exist or is not in a pending state. Please check your link and try again.')) return interest = interests.first() @@ -696,7 +697,6 @@ def helper_handle_approvals(request, bounty): maybe_market_to_slack(bounty, 'worker_approved') maybe_market_to_user_slack(bounty, 'worker_approved') maybe_market_to_twitter(bounty, 'worker_approved') - else: bounty.interested.remove(interest) From 405b4e75c6e7e519eb17860be8ce31ae8748f84e Mon Sep 17 00:00:00 2001 From: Owocki Date: Wed, 30 May 2018 11:07:02 -0600 Subject: [PATCH 12/39] Merge mgiration, also migrates work type / schemes to project type --- CHANGELOG.md | 2 +- app/assets/v2/js/pages/bounty_details.js | 14 +++++------ app/assets/v2/js/pages/dashboard.js | 8 +++---- app/assets/v2/js/pages/new_bounty.js | 24 +++++++++---------- app/dashboard/helpers.py | 4 ++-- .../migrations/0075_auto_20180523_0751.py | 4 ++-- .../migrations/0082_merge_20180530_1704.py | 14 +++++++++++ app/dashboard/models.py | 18 +++++++------- app/dashboard/notifications.py | 2 +- app/dashboard/router.py | 4 ++-- app/dashboard/templates/addinterest.html | 6 ++--- app/dashboard/templates/bounty_details.html | 12 +++++----- ...tip.html => permissions_type_tooltip.html} | 0 ...tooltip.html => project_type_tooltip.html} | 2 +- .../templates/shared/sidebar_search.html | 24 +++++++++---------- app/dashboard/templates/submit_bounty.html | 8 +++---- app/dashboard/views.py | 6 ++--- 17 files changed, 83 insertions(+), 69 deletions(-) create mode 100644 app/dashboard/migrations/0082_merge_20180530_1704.py rename app/dashboard/templates/shared/{app_scheme_tooltip.html => permissions_type_tooltip.html} (100%) rename app/dashboard/templates/shared/{work_scheme_tooltip.html => project_type_tooltip.html} (95%) diff --git a/CHANGELOG.md b/CHANGELOG.md index e91bad48ada..06758ad3b38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,7 +72,7 @@ - Epic 1 [\#1125](https://github.com/gitcoinco/web/issues/1125) - nginx Doesn't Seem to Recognize \(or Direct Properly\) www.gitcoin.co [\#1122](https://github.com/gitcoinco/web/issues/1122) - As a user, I want to be prompted to authenticate before seeing a start work modal [\#1107](https://github.com/gitcoinco/web/issues/1107) -- How to surface work schemes to the community [\#1106](https://github.com/gitcoinco/web/issues/1106) +- How to surface Project Types to the community [\#1106](https://github.com/gitcoinco/web/issues/1106) - Make Image a Thumbnail for Long Descriptions [\#1105](https://github.com/gitcoinco/web/issues/1105) - BUILD - As a funder \(Project recruiter\), I want a place where I can track all my stuff, so i can have a flow of web3.0 talent [\#1092](https://github.com/gitcoinco/web/issues/1092) - As a member of the community, I want these Profile Privacy Features, so I can be more private [\#1091](https://github.com/gitcoinco/web/issues/1091) diff --git a/app/assets/v2/js/pages/bounty_details.js b/app/assets/v2/js/pages/bounty_details.js index 562ea8da531..6a14ab43bd2 100644 --- a/app/assets/v2/js/pages/bounty_details.js +++ b/app/assets/v2/js/pages/bounty_details.js @@ -59,8 +59,8 @@ var rows = [ 'token_value_time_peg', 'web3_created', 'status', - 'work_scheme', - 'application_scheme', + 'project_type', + 'permission_type', 'bounty_owner_address', 'bounty_owner_email', 'issue_description', @@ -138,11 +138,11 @@ var callbacks = { 'bounty_owner_name': function(key, val, result) { return [ 'bounty_owner_name', result.bounty_owner_name ]; }, - 'application_scheme': function(key, val, result) { - return [ 'application_scheme', ucwords(result.application_scheme) ]; + 'permission_type': function(key, val, result) { + return [ 'permission_type', ucwords(result.permission_type) ]; }, - 'work_scheme': function(key, val, result) { - return [ 'work_scheme', ucwords(result.work_scheme) ]; + 'project_type': function(key, val, result) { + return [ 'project_type', ucwords(result.project_type) ]; }, 'issue_keywords': function(key, val, result) { if (!result.keywords || result.keywords.length == 0) @@ -548,7 +548,7 @@ var do_actions = function(result) { pull_interest_list(result['pk'], function(is_interested) { // which actions should we show? - var should_block_from_starting_work = !is_interested && result['work_scheme'] == 'traditional' && (result['status'] == 'started' || result['status'] == 'submitted'); + var should_block_from_starting_work = !is_interested && result['project_type'] == 'traditional' && (result['status'] == 'started' || result['status'] == 'submitted'); var show_start_stop_work = is_still_on_happy_path && !should_block_from_starting_work; var show_github_link = result['github_url'].substring(0, 4) == 'http'; var show_submit_work = true; diff --git a/app/assets/v2/js/pages/dashboard.js b/app/assets/v2/js/pages/dashboard.js index e83e08dd4f5..cb8a8f3a6bc 100644 --- a/app/assets/v2/js/pages/dashboard.js +++ b/app/assets/v2/js/pages/dashboard.js @@ -16,8 +16,8 @@ var sidebar_keys = [ 'network', 'idx_status', 'tech_stack', - 'work_scheme', - 'application_scheme' + 'project_type', + 'permission_type' ]; var localStorage; @@ -459,9 +459,9 @@ var refreshBounties = function(event) { result.action = result['url']; result['title'] = result['title'] ? result['title'] : result['github_url']; - var work_scheme = ucwords(result['work_scheme']) + ' • '; + var project_type = ucwords(result['project_type']) + ' • '; - result['p'] = work_scheme + ((result['experience_level'] ? result['experience_level'] : 'Unknown Experience Level') + ' • '); + result['p'] = project_type + ((result['experience_level'] ? result['experience_level'] : 'Unknown Experience Level') + ' • '); if (result['status'] === 'done') result['p'] += 'Done'; diff --git a/app/assets/v2/js/pages/new_bounty.js b/app/assets/v2/js/pages/new_bounty.js index e25848382ee..66acb367286 100644 --- a/app/assets/v2/js/pages/new_bounty.js +++ b/app/assets/v2/js/pages/new_bounty.js @@ -12,19 +12,19 @@ $(document).ready(function() { } else if (localStorage['issueURL']) { $('input[name=issueURL]').val(localStorage['issueURL']); } - if (localStorage['work_scheme']) { - $('select[name=work_scheme] option').prop('selected', false); + if (localStorage['project_type']) { + $('select[name=project_type] option').prop('selected', false); $( - "select[name=work_scheme] option[value='" + - localStorage['work_scheme'] + + "select[name=project_type] option[value='" + + localStorage['project_type'] + "']" ).prop('selected', true); } - if (localStorage['application_scheme']) { - $('select[name=application_scheme] option').prop('selected', false); + if (localStorage['permission_type']) { + $('select[name=permission_type] option').prop('selected', false); $( - "select[name=application_scheme] option[value='" + - localStorage['application_scheme'] + + "select[name=permission_type] option[value='" + + localStorage['permission_type'] + "']" ).prop('selected', true); } @@ -159,8 +159,8 @@ $(document).ready(function() { address: '' // Fill this in later }, schemes: { - work_scheme: data.work_scheme, - application_scheme: data.application_scheme + project_type: data.project_type, + permission_type: data.permission_type }, privacy_preferences: privacy_preferences, funders: [], @@ -186,8 +186,8 @@ $(document).ready(function() { $(this).attr('disabled', 'disabled'); // save off local state for later - localStorage['work_scheme'] = data.work_scheme; - localStorage['application_scheme'] = data.application_scheme; + localStorage['project_type'] = data.project_type; + localStorage['permission_type'] = data.permission_type; localStorage['issueURL'] = issueURL; localStorage['amount'] = amount; localStorage['notificationEmail'] = notificationEmail; diff --git a/app/dashboard/helpers.py b/app/dashboard/helpers.py index d32b82d9673..f5e00d64839 100644 --- a/app/dashboard/helpers.py +++ b/app/dashboard/helpers.py @@ -459,8 +459,8 @@ def create_new_bounty(old_bounties, bounty_payload, bounty_details, bounty_id): accepted=accepted, interested_comment=interested_comment_id, submissions_comment=submissions_comment_id, - work_scheme=bounty_payload.get('schemes', {}).get('work_scheme', 'traditional'), - application_scheme=bounty_payload.get('schemes', {}).get('application_scheme', 'permissionless'), + project_type=bounty_payload.get('schemes', {}).get('project_type', 'traditional'), + permission_type=bounty_payload.get('schemes', {}).get('permission_type', 'permissionless'), privacy_preferences=bounty_payload.get('privacy_preferences', {}), # These fields are after initial bounty creation, in bounty_details.js expires_date=timezone.make_aware( diff --git a/app/dashboard/migrations/0075_auto_20180523_0751.py b/app/dashboard/migrations/0075_auto_20180523_0751.py index dafa1662755..af62062a6e8 100644 --- a/app/dashboard/migrations/0075_auto_20180523_0751.py +++ b/app/dashboard/migrations/0075_auto_20180523_0751.py @@ -12,12 +12,12 @@ class Migration(migrations.Migration): operations = [ migrations.AddField( model_name='bounty', - name='application_scheme', + name='permission_type', field=models.CharField(choices=[('permissionless', 'permissionless'), ('approval', 'approval')], default='permissionless', max_length=50), ), migrations.AddField( model_name='bounty', - name='work_scheme', + name='project_type', field=models.CharField(choices=[('traditional', 'traditional'), ('contest', 'contest'), ('cooperative', 'cooperative')], default='traditional', max_length=50), ), migrations.AddField( diff --git a/app/dashboard/migrations/0082_merge_20180530_1704.py b/app/dashboard/migrations/0082_merge_20180530_1704.py new file mode 100644 index 00000000000..0e1444c9b20 --- /dev/null +++ b/app/dashboard/migrations/0082_merge_20180530_1704.py @@ -0,0 +1,14 @@ +# Generated by Django 2.0.5 on 2018-05-30 17:04 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('dashboard', '0081_merge_20180529_2228'), + ('dashboard', '0075_auto_20180523_0751'), + ] + + operations = [ + ] diff --git a/app/dashboard/models.py b/app/dashboard/models.py index dd76f49ae06..d3662aa03ef 100644 --- a/app/dashboard/models.py +++ b/app/dashboard/models.py @@ -87,11 +87,11 @@ class Bounty(SuperModel): """ - APPLICATION_SCHEMES = [ + PERMISSION_TYPES = [ ('permissionless', 'permissionless'), ('approval', 'approval'), ] - WORK_SCHEMES = [ + PROJECT_TYPES = [ ('traditional', 'traditional'), ('contest', 'contest'), ('cooperative', 'cooperative'), @@ -172,8 +172,8 @@ class Bounty(SuperModel): fulfillment_submitted_on = models.DateTimeField(null=True, blank=True) fulfillment_started_on = models.DateTimeField(null=True, blank=True) canceled_on = models.DateTimeField(null=True, blank=True) - work_scheme = models.CharField(max_length=50, choices=WORK_SCHEMES, default='traditional') - application_scheme = models.CharField(max_length=50, choices=APPLICATION_SCHEMES, default='permissionless') + project_type = models.CharField(max_length=50, choices=PROJECT_TYPES, default='traditional') + permission_type = models.CharField(max_length=50, choices=PERMISSION_TYPES, default='permissionless') snooze_warnings_for_days = models.IntegerField(default=0) token_value_time_peg = models.DateTimeField(blank=True, null=True) @@ -728,18 +728,18 @@ def is_notification_eligible(self, var_to_check=True): @property - def is_work_scheme_fulfilled(self): - """Determine whether or not the work scheme is currently fulfilled. + def is_project_type_fulfilled(self): + """Determine whether or not the Project Type is currently fulfilled. Todo: - * Add remaining work scheme fulfillment handling. + * Add remaining Project Type fulfillment handling. Returns: - bool: Whether or not the Bounty work scheme is fully staffed. + bool: Whether or not the Bounty Project Type is fully staffed. """ fulfilled = False - if self.work_scheme == 'traditional': + if self.project_type == 'traditional': fulfilled = self.interested.filter(pending=False).exists() return fulfilled diff --git a/app/dashboard/notifications.py b/app/dashboard/notifications.py index dcab62a1ede..ae122073784 100644 --- a/app/dashboard/notifications.py +++ b/app/dashboard/notifications.py @@ -405,7 +405,7 @@ def build_github_notification(bounty, event_name, profile_pairs=None): started_work = bounty.interested.filter(pending=False).all() # pending_approval = bounty.interested.filter(pending=True).all() bounty_owner_clear = f"@{bounty.bounty_owner_github_username}" if bounty.bounty_owner_github_username else "" - approval_required = bounty.application_scheme == 'approval' + approval_required = bounty.permission_type == 'approval' if started_work.exists(): msg = f"{status_header}__Work has been started__.\n\n" diff --git a/app/dashboard/router.py b/app/dashboard/router.py index 58c2fc40172..bb826c39365 100644 --- a/app/dashboard/router.py +++ b/app/dashboard/router.py @@ -88,7 +88,7 @@ class Meta: 'github_issue_number', 'github_org_name', 'github_repo_name', 'idx_status', 'token_value_time_peg', 'fulfillment_accepted_on', 'fulfillment_submitted_on', 'fulfillment_started_on', 'canceled_on', 'action_urls', - 'work_scheme', 'application_scheme', + 'project_type', 'permission_type', ) def create(self, validated_data): @@ -132,7 +132,7 @@ def get_queryset(self): # filtering for key in ['raw_data', 'experience_level', 'project_length', 'bounty_type', 'bounty_owner_address', 'idx_status', 'network', 'bounty_owner_github_username', 'standard_bounties_id', - 'application_scheme', 'work_scheme']: + 'permission_type', 'project_type']: if key in param_keys: # special hack just for looking up bounties posted by a certain person request_key = key if key != 'bounty_owner_address' else 'coinbase' diff --git a/app/dashboard/templates/addinterest.html b/app/dashboard/templates/addinterest.html index 3d521969204..6b7637da772 100644 --- a/app/dashboard/templates/addinterest.html +++ b/app/dashboard/templates/addinterest.html @@ -25,7 +25,7 @@
{% trans "Great to see that you're interested in this project!" %}
- {% if bounty and bounty.application_scheme == 'approval' %} + {% if bounty and bounty.permission_type == 'approval' %}

{% trans "The funder is approving workers for this issue. Please submit the form below, and if you are selected to work on this bouty, you will be notified via email." %}

@@ -45,9 +45,9 @@
{% trans "Great to see that you're interested
- {% trans "Work Scheme" %} - > - + {% trans "Project Type" %} + > +
- {% trans "Worker Application Scheme" %} - > - + {% trans "Worker Application Scheme" %} + > +
diff --git a/app/dashboard/templates/shared/app_scheme_tooltip.html b/app/dashboard/templates/shared/permissions_type_tooltip.html similarity index 100% rename from app/dashboard/templates/shared/app_scheme_tooltip.html rename to app/dashboard/templates/shared/permissions_type_tooltip.html diff --git a/app/dashboard/templates/shared/work_scheme_tooltip.html b/app/dashboard/templates/shared/project_type_tooltip.html similarity index 95% rename from app/dashboard/templates/shared/work_scheme_tooltip.html rename to app/dashboard/templates/shared/project_type_tooltip.html index 6fe95b9203e..e576e803e1e 100644 --- a/app/dashboard/templates/shared/work_scheme_tooltip.html +++ b/app/dashboard/templates/shared/project_type_tooltip.html @@ -16,7 +16,7 @@ {% endcomment %} {% load i18n %}
-

{% trans "Work Schemes" %}

+

{% trans "Project Types" %}

1
diff --git a/app/dashboard/templates/shared/sidebar_search.html b/app/dashboard/templates/shared/sidebar_search.html index 7ab0793fef2..a66233b2b69 100644 --- a/app/dashboard/templates/shared/sidebar_search.html +++ b/app/dashboard/templates/shared/sidebar_search.html @@ -121,61 +121,61 @@
{% trans "Application Scheme" %}
- - What is this?
+ - What is this?
- +
- +
- +
- +
-
{% trans "Work Scheme" %}
+
{% trans "Project Type" %}
- - What is this?
+ - What is this?
- +
- +
- +
- +
- +
diff --git a/app/dashboard/templates/submit_bounty.html b/app/dashboard/templates/submit_bounty.html index 82644eb16a1..c7b8db7b353 100644 --- a/app/dashboard/templates/submit_bounty.html +++ b/app/dashboard/templates/submit_bounty.html @@ -131,18 +131,18 @@

{% trans "Fund an Issue" %}

- +
-
- +
- diff --git a/app/dashboard/views.py b/app/dashboard/views.py index 453b9fc4924..b83d1271642 100644 --- a/app/dashboard/views.py +++ b/app/dashboard/views.py @@ -115,7 +115,7 @@ def helper_handle_access_token(request, access_token): def create_new_interest_helper(bounty, user, issue_message): - approval_required = bounty.application_scheme == 'approval' + approval_required = bounty.permission_type == 'approval' profile_id = user.profile.pk interest = Interest.objects.create( profile_id=profile_id, @@ -186,7 +186,7 @@ def new_interest(request, bounty_id): except Bounty.DoesNotExist: raise Http404 - if bounty.is_work_scheme_fulfilled: + if bounty.is_project_type_fulfilled: return JsonResponse({ 'error': _(f'There is already someone working on this bounty.'), 'success': False}, @@ -233,7 +233,7 @@ def new_interest(request, bounty_id): status=401) msg = _("You have started work.") - approval_required = bounty.application_scheme == 'approval' + approval_required = bounty.permission_type == 'approval' if approval_required: msg = _("You have applied to start work. If approved, you will be notified via email.") From 5cef2379c74b7de0a20f23584cf38676ace2acb4 Mon Sep 17 00:00:00 2001 From: Owocki Date: Wed, 30 May 2018 11:20:10 -0600 Subject: [PATCH 13/39] docker-compose was wrong --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index a81a1860f24..63d058cf958 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,7 +30,7 @@ services: - PYTHONUNBUFFERED=1 - PYTHONPATH=/code/app env_file: - - app/app/local.env + - app/app/.env build: . volumes: - .:/code From bdee8b51db87394944c8470e79d4a007269d1add Mon Sep 17 00:00:00 2001 From: Owocki Date: Wed, 30 May 2018 11:28:29 -0600 Subject: [PATCH 14/39] approval required as a default" --- app/dashboard/templates/submit_bounty.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/dashboard/templates/submit_bounty.html b/app/dashboard/templates/submit_bounty.html index c7b8db7b353..9b5db7a64a0 100644 --- a/app/dashboard/templates/submit_bounty.html +++ b/app/dashboard/templates/submit_bounty.html @@ -135,7 +135,7 @@

{% trans "Fund an Issue" %}

From 5bd8d39bf48b8f58cf38f1ed7b3acd15426b116f Mon Sep 17 00:00:00 2001 From: Owocki Date: Wed, 30 May 2018 11:32:50 -0600 Subject: [PATCH 15/39] for contests, make it so it's open even if it's started --- app/dashboard/models.py | 55 +++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/app/dashboard/models.py b/app/dashboard/models.py index d3662aa03ef..8d18c8a5054 100644 --- a/app/dashboard/models.py +++ b/app/dashboard/models.py @@ -426,40 +426,29 @@ def status(self): if self.override_status: return self.override_status if self.is_legacy: - # TODO: Remove following full deprecation of legacy bounties - try: - fulfillments = self.fulfillments \ - .exclude(fulfiller_address='0x0000000000000000000000000000000000000000') \ - .exists() - if not self.is_open: - if timezone.now() > self.expires_date and fulfillments: - return 'expired' + return self.idx_status + + # standard bounties + try: + if not self.is_open: + if self.accepted: return 'done' - elif not fulfillments: - if self.pk and self.interested.filter(pending=False).exists(): - return 'started' - return 'open' - return 'submitted' - except Exception as e: - logger.warning(e) - return 'unknown' - else: - try: - if not self.is_open: - if self.accepted: - return 'done' - elif self.past_hard_expiration_date: - return 'expired' - # If its not expired or done, it must be cancelled. - return 'cancelled' - if self.num_fulfillments == 0: - if self.pk and self.interested.filter(pending=False).exists(): - return 'started' - return 'open' - return 'submitted' - except Exception as e: - logger.warning(e) - return 'unknown' + elif self.past_hard_expiration_date: + return 'expired' + # If its not expired or done, it must be cancelled. + return 'cancelled' + # per https://github.com/gitcoinco/web/pull/1098 , + # contests are open no matter how much started/submitted work they have + if self.pk and self.project_type == 'contest': + return 'open' + if self.num_fulfillments == 0: + if self.pk and self.interested.filter(pending=False).exists(): + return 'started' + return 'open' + return 'submitted' + except Exception as e: + logger.warning(e) + return 'unknown' @property def get_value_true(self): From 39ea65b9cec77a9c8b34db25a2ebbecf032c58ef Mon Sep 17 00:00:00 2001 From: Owocki Date: Wed, 30 May 2018 13:06:15 -0600 Subject: [PATCH 16/39] all the app schema emails --- app/app/urls.py | 5 + app/dashboard/models.py | 4 + app/dashboard/views.py | 10 +- app/marketing/mails.py | 75 ++++++++++- .../commands/pending_start_work_actions.py | 58 ++++++++ app/retail/emails.py | 125 ++++++++++++++++++ app/retail/templates/emails/bounty.html | 7 +- app/retail/templates/emails/bounty.txt | 3 + .../start_work_applicant_about_to_expire.html | 43 ++++++ .../start_work_applicant_about_to_expire.txt | 9 ++ .../emails/start_work_applicant_expired.html | 40 ++++++ .../emails/start_work_applicant_expired.txt | 7 + .../templates/emails/start_work_approved.html | 43 ++++++ .../templates/emails/start_work_approved.txt | 9 ++ .../emails/start_work_new_applicant.html | 43 ++++++ .../emails/start_work_new_applicant.txt | 9 ++ .../templates/emails/start_work_rejected.html | 41 ++++++ .../templates/emails/start_work_rejected.txt | 8 ++ scripts/crontab | 1 + 19 files changed, 534 insertions(+), 6 deletions(-) create mode 100644 app/marketing/management/commands/pending_start_work_actions.py create mode 100644 app/retail/templates/emails/start_work_applicant_about_to_expire.html create mode 100644 app/retail/templates/emails/start_work_applicant_about_to_expire.txt create mode 100644 app/retail/templates/emails/start_work_applicant_expired.html create mode 100644 app/retail/templates/emails/start_work_applicant_expired.txt create mode 100644 app/retail/templates/emails/start_work_approved.html create mode 100644 app/retail/templates/emails/start_work_approved.txt create mode 100644 app/retail/templates/emails/start_work_new_applicant.html create mode 100644 app/retail/templates/emails/start_work_new_applicant.txt create mode 100644 app/retail/templates/emails/start_work_rejected.html create mode 100644 app/retail/templates/emails/start_work_rejected.txt diff --git a/app/app/urls.py b/app/app/urls.py index 2f0b81391a9..b75c9ddb1b8 100644 --- a/app/app/urls.py +++ b/app/app/urls.py @@ -206,6 +206,11 @@ url(r'^_administration/email/new_tip/resend$', retail.emails.resend_new_tip, name='resend_new_tip'), url(r'^_administration/process_accesscode_request/(.*)$', tdi.views.process_accesscode_request, name='process_accesscode_request'), url(r'^_administration/process_faucet_request/(.*)$', faucet.views.process_faucet_request, name='process_faucet_request'), + re_path(r'^_administration/email/start_work_approved$', retail.emails.start_work_approved, name='start_work_approved'), + re_path(r'^_administration/email/start_work_rejected$', retail.emails.start_work_rejected, name='start_work_rejected'), + re_path(r'^_administration/email/start_work_new_applicant$', retail.emails.start_work_new_applicant, name='start_work_new_applicant'), + re_path(r'^_administration/email/start_work_applicant_about_to_expire$', retail.emails.start_work_applicant_about_to_expire, name='start_work_applicant_about_to_expire'), + re_path(r'^_administration/email/start_work_applicant_expired$', retail.emails.start_work_applicant_expired, name='start_work_applicant_expired'), # settings re_path(r'^settings/email/(.*)', marketing.views.email_settings, name='email_settings'), diff --git a/app/dashboard/models.py b/app/dashboard/models.py index 8d18c8a5054..0891a11ab58 100644 --- a/app/dashboard/models.py +++ b/app/dashboard/models.py @@ -983,6 +983,10 @@ def __str__(self): """Define the string representation of an interested profile.""" return f"{self.profile.handle} / pending: {self.pending}" + @property + def bounties(self): + return Bounty.objects.filter(interested=self) + @receiver(post_save, sender=Interest, dispatch_uid="psave_interest") @receiver(post_delete, sender=Interest, dispatch_uid="pdel_interest") diff --git a/app/dashboard/views.py b/app/dashboard/views.py index b83d1271642..5b50326517a 100644 --- a/app/dashboard/views.py +++ b/app/dashboard/views.py @@ -41,7 +41,7 @@ from github.utils import ( get_auth_url, get_github_emails, get_github_primary_email, get_github_user_data, is_github_token_valid, ) -from marketing.mails import bounty_uninterested +from marketing.mails import bounty_uninterested, start_work_rejected, start_work_approved, start_work_new_applicant from marketing.models import Keyword from ratelimit.decorators import ratelimit from retail.helpers import get_ip @@ -218,6 +218,8 @@ def new_interest(request, bounty_id): except Interest.DoesNotExist: issue_message = request.POST.get("issue_message") interest = create_new_interest_helper(bounty, request.user, issue_message) + if interest.pending: + start_work_new_applicant(interest, bounty) except Interest.MultipleObjectsReturned: bounty_ids = bounty.interested \ @@ -695,7 +697,7 @@ def helper_handle_approvals(request, bounty): interest.pending = False interest.save() - # TODO: send an email when this happen + start_work_approved(interest, bounty) maybe_market_to_github(bounty, 'work_started', profile_pairs=bounty.profile_pairs) maybe_market_to_slack(bounty, 'worker_approved') @@ -703,11 +705,11 @@ def helper_handle_approvals(request, bounty): maybe_market_to_twitter(bounty, 'worker_approved') else: + start_work_rejected(interest, bounty) + bounty.interested.remove(interest) interest.delete() - # TODO: send an email when this happen - maybe_market_to_slack(bounty, 'worker_rejected') maybe_market_to_user_slack(bounty, 'worker_rejected') maybe_market_to_twitter(bounty, 'worker_rejected') diff --git a/app/marketing/mails.py b/app/marketing/mails.py index cd81c4fd6a1..e2b91838721 100644 --- a/app/marketing/mails.py +++ b/app/marketing/mails.py @@ -29,7 +29,9 @@ render_bounty_expire_warning, render_bounty_feedback, render_bounty_startwork_expire_warning, render_bounty_unintersted, render_faucet_rejected, render_faucet_request, render_gdpr_reconsent, render_gdpr_update, render_match_email, render_new_bounty, render_new_bounty_acceptance, render_new_bounty_rejection, - render_new_bounty_roundup, render_new_work_submission, render_tip_email, + render_new_bounty_roundup, render_new_work_submission, render_tip_email, render_start_work_approved, + render_start_work_rejected, render_start_work_new_applicant, render_start_work_applicant_about_to_expire, + render_start_work_applicant_expired, ) from sendgrid.helpers.mail import Content, Email, Mail, Personalization @@ -437,6 +439,77 @@ def bounty_uninterested(to_email, bounty, interest): translation.activate(cur_language) +def start_work_approved(bounty, interest): + from_email = settings.CONTACT_EMAIL + to_email = interest.profile.email + cur_language = translation.get_language() + try: + setup_lang(to_email) + html, text, subject = render_start_work_approved(to_email, bounty, interest) + + if not should_suppress_notification_email(to_email, 'bounty'): + send_mail(from_email, to_email, subject, text, html) + finally: + translation.activate(cur_language) + + +def start_work_rejected(bounty, interest): + from_email = settings.CONTACT_EMAIL + to_email = interest.profile.email + cur_language = translation.get_language() + try: + setup_lang(to_email) + html, text, subject = render_start_work_rejected(to_email, bounty, interest) + + if not should_suppress_notification_email(to_email, 'bounty'): + send_mail(from_email, to_email, subject, text, html) + finally: + translation.activate(cur_language) + + +def start_work_new_applicant(bounty, interest): + from_email = settings.CONTACT_EMAIL + to_email = bounty.bounty_owner_email + cur_language = translation.get_language() + try: + setup_lang(to_email) + html, text, subject = render_start_work_new_applicant(to_email, bounty, interest) + + if not should_suppress_notification_email(to_email, 'bounty'): + send_mail(from_email, to_email, subject, text, html) + finally: + translation.activate(cur_language) + + + +def start_work_applicant_about_to_expire(bounty, interest): + from_email = settings.CONTACT_EMAIL + to_email = bounty.bounty_owner_email + cur_language = translation.get_language() + try: + setup_lang(to_email) + html, text, subject = render_start_work_applicant_about_to_expire(to_email, bounty, interest) + + if not should_suppress_notification_email(to_email, 'bounty'): + send_mail(from_email, to_email, subject, text, html) + finally: + translation.activate(cur_language) + + +def start_work_applicant_expired(bounty, interest): + from_email = settings.CONTACT_EMAIL + to_email = bounty.bounty_owner_email + cur_language = translation.get_language() + try: + setup_lang(to_email) + html, text, subject = render_start_work_applicant_expired(to_email, bounty, interest) + + if not should_suppress_notification_email(to_email, 'bounty'): + send_mail(from_email, to_email, subject, text, html) + finally: + translation.activate(cur_language) + + def setup_lang(to_email): """Activate the User's language preferences based on their email address. diff --git a/app/marketing/management/commands/pending_start_work_actions.py b/app/marketing/management/commands/pending_start_work_actions.py new file mode 100644 index 00000000000..9c62a249639 --- /dev/null +++ b/app/marketing/management/commands/pending_start_work_actions.py @@ -0,0 +1,58 @@ +''' + Copyright (C) 2017 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.conf import settings +from django.core.management.base import BaseCommand +from django.utils import timezone +from dashboard.models import Bounty, Interest +from marketing.mails import start_work_applicant_about_to_expire, start_work_applicant_expired, start_work_approved + +THRESHOLD_HOURS_AUTO_APPROVE = 3 * 24 +THRESHOLD_HOURS_AUTO_APPROVE_WARNING = 2 * 24 + + +def start_work_applicant_expired_executer(interest, bounty): + + start_work_approved(interest, bounty) + start_work_applicant_expired(interest, bounty) + interest.pending = False + interest.save() + + +def helper_execute_emails(threshold, func_to_execute, action_str): + start_time = timezone.now() - timezone.timedelta(hours=(threshold+1)) + end_time = timezone.now() - timezone.timedelta(hours=(threshold)) + interests = Interest.objects.filter(pending=True, created__gt=start_time, created__lt=end_time) + print(f"{interests.count()} {action_str}") + for interest in interests: + bounty = interest.bounties.first() #TODO is this right? is this how i get the bounty? + print(f"- {interest.pk} {action_str}") + func_to_execute(interest, bounty) + + +class Command(BaseCommand): + + help = 'handles pending start work actions for https://github.com/gitcoinco/web/pull/1098' + + def handle(self, *args, **options): + + # warninings + helper_execute_emails(THRESHOLD_HOURS_AUTO_APPROVE_WARNING, start_work_applicant_about_to_expire, 'warning') + + # auto approval + helper_execute_emails(THRESHOLD_HOURS_AUTO_APPROVE, start_work_applicant_expired_executer, 'auto approval') + diff --git a/app/retail/emails.py b/app/retail/emails.py index 1b797f1f19c..154ae0c45c8 100644 --- a/app/retail/emails.py +++ b/app/retail/emails.py @@ -347,6 +347,86 @@ def render_gdpr_update(to_email): return response_html, response_txt, subject +def render_start_work_approved(interest, bounty): + to_email = interest.profile.email + params = { + 'subscriber': get_or_save_email_subscriber(to_email, 'internal'), + 'interest': interest, + 'bounty': bounty, + 'approve_worker_url': bounty.approve_worker_url(interest.profile.handle), + } + + subject = "Request Accepted " + response_html = premailer_transform(render_to_string("emails/start_work_approved.html", params)) + response_txt = render_to_string("emails/start_work_approved.txt", params) + + return response_html, response_txt, subject + + +def render_start_work_rejected(interest, bounty): + to_email = interest.profile.email + params = { + 'subscriber': get_or_save_email_subscriber(to_email, 'internal'), + 'interest': interest, + 'bounty': bounty, + 'approve_worker_url': bounty.approve_worker_url(interest.profile.handle), + } + + subject = "Work Request Denied" + response_html = premailer_transform(render_to_string("emails/start_work_rejected.html", params)) + response_txt = render_to_string("emails/start_work_rejected.txt", params) + + return response_html, response_txt, subject + + +def render_start_work_new_applicant(interest, bounty): + to_email = bounty.bounty_owner_email + params = { + 'subscriber': get_or_save_email_subscriber(to_email, 'internal'), + 'interest': interest, + 'bounty': bounty, + 'approve_worker_url': bounty.approve_worker_url(interest.profile.handle), + } + + subject = "A new request to work on your bounty" + response_html = premailer_transform(render_to_string("emails/start_work_new_applicant.html", params)) + response_txt = render_to_string("emails/start_work_new_applicant.txt", params) + + return response_html, response_txt, subject + + +def render_start_work_applicant_about_to_expire(interest, bounty): + to_email = bounty.bounty_owner_email + params = { + 'subscriber': get_or_save_email_subscriber(to_email, 'internal'), + 'interest': interest, + 'bounty': bounty, + 'approve_worker_url': bounty.approve_worker_url(interest.profile.handle), + } + + subject = "24 Hrs to Approve" + response_html = premailer_transform(render_to_string("emails/start_work_applicant_about_to_expire.html", params)) + response_txt = render_to_string("emails/start_work_applicant_about_to_expire.txt", params) + + return response_html, response_txt, subject + + +def render_start_work_applicant_expired(interest, bounty): + to_email = bounty.bounty_owner_email + params = { + 'subscriber': get_or_save_email_subscriber(to_email, 'internal'), + 'interest': interest, + 'bounty': bounty, + 'approve_worker_url': bounty.approve_worker_url(interest.profile.handle), + } + + subject = "A Worker was Auto Approved" + response_html = premailer_transform(render_to_string("emails/start_work_applicant_expired.html", params)) + response_txt = render_to_string("emails/start_work_applicant_expired.txt", params) + + return response_html, response_txt, subject + + # ROUNDUP_EMAIL def render_new_bounty_roundup(to_email): from dashboard.models import Bounty @@ -575,3 +655,48 @@ def roundup(request): def gdpr_reconsent(request): response_html, _ = render_gdpr_reconsent(settings.CONTACT_EMAIL) return HttpResponse(response_html) + + +@staff_member_required +def start_work_approved(request): + from dashboard.models import Interest, Bounty + interest = Interest.objects.last() + bounty = Bounty.objects.last() + response_html, _, _ = render_start_work_approved(interest, bounty) + return HttpResponse(response_html) + + +@staff_member_required +def start_work_rejected(request): + from dashboard.models import Interest, Bounty + interest = Interest.objects.last() + bounty = Bounty.objects.last() + response_html, _, _ = render_start_work_rejected(interest, bounty) + return HttpResponse(response_html) + + +@staff_member_required +def start_work_new_applicant(request): + from dashboard.models import Interest, Bounty + interest = Interest.objects.last() + bounty = Bounty.objects.last() + response_html, _, _ = render_start_work_new_applicant(interest, bounty) + return HttpResponse(response_html) + + +@staff_member_required +def start_work_applicant_about_to_expire(request): + from dashboard.models import Interest, Bounty + interest = Interest.objects.last() + bounty = Bounty.objects.last() + response_html, _, _ = render_start_work_applicant_about_to_expire(interest, bounty) + return HttpResponse(response_html) + + +@staff_member_required +def start_work_applicant_expired(request): + from dashboard.models import Interest, Bounty + interest = Interest.objects.last() + bounty = Bounty.objects.last() + response_html, _, _ = render_start_work_applicant_expired(interest, bounty) + return HttpResponse(response_html) diff --git a/app/retail/templates/emails/bounty.html b/app/retail/templates/emails/bounty.html index e0202344eba..b22d5396a7c 100644 --- a/app/retail/templates/emails/bounty.html +++ b/app/retail/templates/emails/bounty.html @@ -127,8 +127,13 @@ {% endif %}
- + {% if action == 'custom' %} + + {% else %} + {% endif %}
diff --git a/app/retail/templates/emails/bounty.txt b/app/retail/templates/emails/bounty.txt index 3eea705403b..f55f86fef1c 100644 --- a/app/retail/templates/emails/bounty.txt +++ b/app/retail/templates/emails/bounty.txt @@ -5,3 +5,6 @@ {% endif %} * Status: {{bounty.status}} * View More: {{bounty.absolute_url}} +{% if action == 'custom' %} +* {{action_copy}}: {{action_url}} +{% endif %} diff --git a/app/retail/templates/emails/start_work_applicant_about_to_expire.html b/app/retail/templates/emails/start_work_applicant_about_to_expire.html new file mode 100644 index 00000000000..c4eec50694b --- /dev/null +++ b/app/retail/templates/emails/start_work_applicant_about_to_expire.html @@ -0,0 +1,43 @@ +{% extends 'emails/template.html' %} +{% comment %} + Copyright (C) 2017 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 %} +{% load static %} + +{% block content %} + +

{% trans "24 Hrs to Approve" %}

+
+

+ {% trans "Hello" %}, +

+

+ {% trans "Good news!" %} @{{interest.profile.handle}} {% trans "would like to work on "%}{% trans "this bounty" %}. {% trans "Please review and approve" %} @{{interest.profile.handle}} {% trans "by clicking approve below." %} +

+
+ {% include 'emails/bounty.html' with bounty=bounty action='custom' action_copy='Approve Worker' action_url=approve_worker_url %} +

+ {% trans "As a reminder, after 24 hours any contributor will be auto-approved and can automatically submit a PR against your issue." %} +

+

+ {% blocktrans %} + - Gitcoin Team + {% endblocktrans %} +

+
+{% endblock %} diff --git a/app/retail/templates/emails/start_work_applicant_about_to_expire.txt b/app/retail/templates/emails/start_work_applicant_about_to_expire.txt new file mode 100644 index 00000000000..b6730b17f07 --- /dev/null +++ b/app/retail/templates/emails/start_work_applicant_about_to_expire.txt @@ -0,0 +1,9 @@ +{% load i18n %} + +{% trans "Good news!" %} @{{interest.profile.handle}} {% trans "would like to work on this bounty. Please review and approve" %} @{{interest.profile.handle}} {% trans "by clicking approve below." %} + + + +{% include 'emails/bounty.txt' with bounty=bounty action='custom' action_copy='Approve Worker' action_url=approve_worker_url %} + +{% trans "As a reminder, after 24 hours any contributor will be auto-approved and can automatically submit a PR against your issue." %} diff --git a/app/retail/templates/emails/start_work_applicant_expired.html b/app/retail/templates/emails/start_work_applicant_expired.html new file mode 100644 index 00000000000..8296c712601 --- /dev/null +++ b/app/retail/templates/emails/start_work_applicant_expired.html @@ -0,0 +1,40 @@ +{% extends 'emails/template.html' %} +{% comment %} + Copyright (C) 2017 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 %} +{% load static %} + +{% block content %} + +

{% trans "A Worker was Auto Approved" %}

+
+

+ {% trans "Hello" %}, +

+

+ @{{interest.profile.handle}} {% trans "was automatically approved to work on "%}{% trans "this bounty" %}. {% trans "Please work together to complete the project." %} +

+
+ {% include 'emails/bounty.html' with bounty=bounty %} +

+ {% blocktrans %} + - Gitcoin Team + {% endblocktrans %} +

+
+{% endblock %} diff --git a/app/retail/templates/emails/start_work_applicant_expired.txt b/app/retail/templates/emails/start_work_applicant_expired.txt new file mode 100644 index 00000000000..0f6b401591a --- /dev/null +++ b/app/retail/templates/emails/start_work_applicant_expired.txt @@ -0,0 +1,7 @@ +{% load i18n %} + +@{{interest.profile.handle}} {% trans "was automatically approved to work on this bounty! Please work together to complete the project." %} + + +{% include 'emails/bounty.txt' with bounty=bounty action='custom' action_copy='Approve Worker' action_url=approve_worker_url %} + diff --git a/app/retail/templates/emails/start_work_approved.html b/app/retail/templates/emails/start_work_approved.html new file mode 100644 index 00000000000..125918f6759 --- /dev/null +++ b/app/retail/templates/emails/start_work_approved.html @@ -0,0 +1,43 @@ +{% extends 'emails/template.html' %} +{% comment %} + Copyright (C) 2017 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 %} +{% load static %} + +{% block content %} + +

{% trans "Request to Work" %}

+
+

+ {% trans "Hello" %} @{{interest.profile.handle}}, +

+

+ {% trans "Good news! The funder has accepted your request to work on"%} {% trans "this bounty" %}. {% trans "You can go ahead and start working on the issue and as a courtesy provide an update to the maintainer every few days." %} +

+
+ {% include 'emails/bounty.html' with bounty=bounty %} +

+ {% trans "As always, if you have questions, please reach out to the project owner!" %} +

+

+ {% blocktrans %} + - Gitcoin Team + {% endblocktrans %} +

+
+{% endblock %} diff --git a/app/retail/templates/emails/start_work_approved.txt b/app/retail/templates/emails/start_work_approved.txt new file mode 100644 index 00000000000..a0dc9bda687 --- /dev/null +++ b/app/retail/templates/emails/start_work_approved.txt @@ -0,0 +1,9 @@ +{% load i18n %} + +{% trans "Good news! The funder has accepted your request to work on this bounty. You can go ahead and start working on the issue and as a courtesy provide an update to the maintainer every few days. " %} + + + +{% include 'emails/bounty.txt' with bounty=bounty action='custom' action_copy='Approve Worker' action_url=approve_worker_url %} + +{% trans "As always, if you have questions, please reach out to the project owner!" %} diff --git a/app/retail/templates/emails/start_work_new_applicant.html b/app/retail/templates/emails/start_work_new_applicant.html new file mode 100644 index 00000000000..9fb424b6cc1 --- /dev/null +++ b/app/retail/templates/emails/start_work_new_applicant.html @@ -0,0 +1,43 @@ +{% extends 'emails/template.html' %} +{% comment %} + Copyright (C) 2017 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 %} +{% load static %} + +{% block content %} + +

{% trans "Request to Work" %}

+
+

+ {% trans "Hello" %}, +

+

+ {% trans "Good news!" %} @{{interest.profile.handle}} {% trans "would like to work on "%}{% trans "this bounty" %}. {% trans "Please review and approve" %} @{{interest.profile.handle}} {% trans "by clicking approve below." %} +

+
+ {% include 'emails/bounty.html' with bounty=bounty action='custom' action_copy='Approve Worker' action_url=approve_worker_url %} +

+ {% trans "As a reminder, after 72 hours any contributor will be auto-approved and can automatically submit a PR against your issue." %} +

+

+ {% blocktrans %} + - Gitcoin Team + {% endblocktrans %} +

+
+{% endblock %} diff --git a/app/retail/templates/emails/start_work_new_applicant.txt b/app/retail/templates/emails/start_work_new_applicant.txt new file mode 100644 index 00000000000..8393eb919b7 --- /dev/null +++ b/app/retail/templates/emails/start_work_new_applicant.txt @@ -0,0 +1,9 @@ +{% load i18n %} + +{% trans "Good news!" %} @{{interest.profile.handle}} {% trans "would like to work on this bounty. Please review and approve" %} @{{interest.profile.handle}} {% trans "by clicking approve below." %} + + + +{% include 'emails/bounty.txt' with bounty=bounty action='custom' action_copy='Approve Worker' action_url=approve_worker_url %} + +{% trans "As a reminder, after 72 hours any contributor will be auto-approved and can automatically submit a PR against your issue." %} diff --git a/app/retail/templates/emails/start_work_rejected.html b/app/retail/templates/emails/start_work_rejected.html new file mode 100644 index 00000000000..3ecaa359376 --- /dev/null +++ b/app/retail/templates/emails/start_work_rejected.html @@ -0,0 +1,41 @@ +{% extends 'emails/template.html' %} +{% comment %} + Copyright (C) 2017 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 %} +{% load static %} + +{% block content %} + +

{% trans "Work Request Rejected" %}

+
+

+ {% trans "Hello" %} @{{interest.profile.handle}}, +

+

+ {% trans "The funder has denied your request to work on "%}{% trans "this bounty" %}. {% trans "Checkout the issue explorer for other projects on the platform!" %} +

+

+ Issue Explorer +

+

+ {% blocktrans %} + - Gitcoin Team + {% endblocktrans %} +

+
+{% endblock %} diff --git a/app/retail/templates/emails/start_work_rejected.txt b/app/retail/templates/emails/start_work_rejected.txt new file mode 100644 index 00000000000..ae487b69430 --- /dev/null +++ b/app/retail/templates/emails/start_work_rejected.txt @@ -0,0 +1,8 @@ +{% load i18n %} + +{% trans "The funder has denied your request to work on this issue. +Checkout the issue explorer for other projects on the platform! + " %} + + +https://gitcon.co/explorer \ No newline at end of file diff --git a/scripts/crontab b/scripts/crontab index 3319115a4c2..eb16ae36238 100644 --- a/scripts/crontab +++ b/scripts/crontab @@ -38,6 +38,7 @@ PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/us */15 * * * * cd gitcoin/coin; bash scripts/run_management_command_if_not_already_running.bash get_notifications >> /var/log/gitcoin/get_notifications.log 2>&1 15 10 * * * cd gitcoin/coin; bash scripts/run_management_command_if_not_already_running.bash bounty_feedback_email >> /var/log/gitcoin/bounty_feedback_email.log 2>&1 15 11 * * * cd gitcoin/coin; bash scripts/run_management_command_if_not_already_running.bash new_bounties_email >> /var/log/gitcoin/new_bounties_email.log 2>&1 +15 * * * * cd gitcoin/coin; bash scripts/run_management_command_if_not_already_running.bash pending_start_work_actions >> /var/log/gitcoin/pending_start_work_actions.log 2>&1 11 10 * * * cd gitcoin/coin; bash scripts/run_management_command.bash expiration_tip >> /var/log/gitcoin/expiration_tip.log 2>&1 From 2b08d6255017769a6c85094da4dd0542d4be7dd7 Mon Sep 17 00:00:00 2001 From: Owocki Date: Wed, 30 May 2018 13:06:57 -0600 Subject: [PATCH 17/39] make fix --- app/dashboard/views.py | 2 +- app/marketing/mails.py | 6 +++--- .../management/commands/pending_start_work_actions.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/dashboard/views.py b/app/dashboard/views.py index 5b50326517a..c503c5330ca 100644 --- a/app/dashboard/views.py +++ b/app/dashboard/views.py @@ -41,7 +41,7 @@ from github.utils import ( get_auth_url, get_github_emails, get_github_primary_email, get_github_user_data, is_github_token_valid, ) -from marketing.mails import bounty_uninterested, start_work_rejected, start_work_approved, start_work_new_applicant +from marketing.mails import bounty_uninterested, start_work_approved, start_work_new_applicant, start_work_rejected from marketing.models import Keyword from ratelimit.decorators import ratelimit from retail.helpers import get_ip diff --git a/app/marketing/mails.py b/app/marketing/mails.py index e2b91838721..61d7bf70151 100644 --- a/app/marketing/mails.py +++ b/app/marketing/mails.py @@ -29,9 +29,9 @@ render_bounty_expire_warning, render_bounty_feedback, render_bounty_startwork_expire_warning, render_bounty_unintersted, render_faucet_rejected, render_faucet_request, render_gdpr_reconsent, render_gdpr_update, render_match_email, render_new_bounty, render_new_bounty_acceptance, render_new_bounty_rejection, - render_new_bounty_roundup, render_new_work_submission, render_tip_email, render_start_work_approved, - render_start_work_rejected, render_start_work_new_applicant, render_start_work_applicant_about_to_expire, - render_start_work_applicant_expired, + render_new_bounty_roundup, render_new_work_submission, 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_tip_email, ) from sendgrid.helpers.mail import Content, Email, Mail, Personalization diff --git a/app/marketing/management/commands/pending_start_work_actions.py b/app/marketing/management/commands/pending_start_work_actions.py index 9c62a249639..f1358db5f93 100644 --- a/app/marketing/management/commands/pending_start_work_actions.py +++ b/app/marketing/management/commands/pending_start_work_actions.py @@ -18,6 +18,7 @@ from django.conf import settings from django.core.management.base import BaseCommand from django.utils import timezone + from dashboard.models import Bounty, Interest from marketing.mails import start_work_applicant_about_to_expire, start_work_applicant_expired, start_work_approved @@ -55,4 +56,3 @@ def handle(self, *args, **options): # auto approval helper_execute_emails(THRESHOLD_HOURS_AUTO_APPROVE, start_work_applicant_expired_executer, 'auto approval') - From 48eb347ea4e775f6c11662c6db17247f3f014f19 Mon Sep 17 00:00:00 2001 From: Owocki Date: Wed, 30 May 2018 13:17:53 -0600 Subject: [PATCH 18/39] qa and notifications --- app/dashboard/notifications.py | 6 +++--- app/marketing/mails.py | 21 ++++++++++----------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/app/dashboard/notifications.py b/app/dashboard/notifications.py index ae122073784..3a73e52ccaf 100644 --- a/app/dashboard/notifications.py +++ b/app/dashboard/notifications.py @@ -412,7 +412,7 @@ def build_github_notification(bounty, event_name, profile_pairs=None): else: msg = f"{status_header}__Workers have applied to start work__.\n\n" - msg += f"\nThese users each claimed they can complete the work by {from_now}:\n\n" + msg += f"\nThese users each claimed they can complete the work by {from_now}. Please review their questions below:\n\n" for i, interest in enumerate(interested, start=1): @@ -429,8 +429,8 @@ def build_github_notification(bounty, event_name, profile_pairs=None): issue_message = interest.issue_message.strip() if issue_message: - msg += f"{bounty_owner_clear} they have the following comments/questions for you:\n\n" \ - f"```{issue_message}```" + msg += f"\t\n * Q: " \ + f"{issue_message}" msg += "\n\n" elif event_name == 'work_submitted': diff --git a/app/marketing/mails.py b/app/marketing/mails.py index 61d7bf70151..1f479b7f14e 100644 --- a/app/marketing/mails.py +++ b/app/marketing/mails.py @@ -439,13 +439,13 @@ def bounty_uninterested(to_email, bounty, interest): translation.activate(cur_language) -def start_work_approved(bounty, interest): +def start_work_approved(interest, bounty): from_email = settings.CONTACT_EMAIL to_email = interest.profile.email cur_language = translation.get_language() try: setup_lang(to_email) - html, text, subject = render_start_work_approved(to_email, bounty, interest) + html, text, subject = render_start_work_approved(interest, bounty) if not should_suppress_notification_email(to_email, 'bounty'): send_mail(from_email, to_email, subject, text, html) @@ -453,13 +453,13 @@ def start_work_approved(bounty, interest): translation.activate(cur_language) -def start_work_rejected(bounty, interest): +def start_work_rejected(interest, bounty): from_email = settings.CONTACT_EMAIL to_email = interest.profile.email cur_language = translation.get_language() try: setup_lang(to_email) - html, text, subject = render_start_work_rejected(to_email, bounty, interest) + html, text, subject = render_start_work_rejected(interest, bounty) if not should_suppress_notification_email(to_email, 'bounty'): send_mail(from_email, to_email, subject, text, html) @@ -467,13 +467,13 @@ def start_work_rejected(bounty, interest): translation.activate(cur_language) -def start_work_new_applicant(bounty, interest): +def start_work_new_applicant(interest, bounty): from_email = settings.CONTACT_EMAIL to_email = bounty.bounty_owner_email cur_language = translation.get_language() try: setup_lang(to_email) - html, text, subject = render_start_work_new_applicant(to_email, bounty, interest) + html, text, subject = render_start_work_new_applicant(interest, bounty) if not should_suppress_notification_email(to_email, 'bounty'): send_mail(from_email, to_email, subject, text, html) @@ -481,14 +481,13 @@ def start_work_new_applicant(bounty, interest): translation.activate(cur_language) - -def start_work_applicant_about_to_expire(bounty, interest): +def start_work_applicant_about_to_expire(interest, bounty): from_email = settings.CONTACT_EMAIL to_email = bounty.bounty_owner_email cur_language = translation.get_language() try: setup_lang(to_email) - html, text, subject = render_start_work_applicant_about_to_expire(to_email, bounty, interest) + html, text, subject = render_start_work_applicant_about_to_expire(interest, bounty) if not should_suppress_notification_email(to_email, 'bounty'): send_mail(from_email, to_email, subject, text, html) @@ -496,13 +495,13 @@ def start_work_applicant_about_to_expire(bounty, interest): translation.activate(cur_language) -def start_work_applicant_expired(bounty, interest): +def start_work_applicant_expired(interest, bounty): from_email = settings.CONTACT_EMAIL to_email = bounty.bounty_owner_email cur_language = translation.get_language() try: setup_lang(to_email) - html, text, subject = render_start_work_applicant_expired(to_email, bounty, interest) + html, text, subject = render_start_work_applicant_expired(interest, bounty) if not should_suppress_notification_email(to_email, 'bounty'): send_mail(from_email, to_email, subject, text, html) From c07282a890d1c53057ca40bcf8764d4411c13aee Mon Sep 17 00:00:00 2001 From: Mark Beacom Date: Thu, 31 May 2018 11:22:37 -0400 Subject: [PATCH 19/39] Update migrations --- ...20180523_0751.py => 0082_auto_20180531_1521.py} | 4 ++-- .../migrations/0082_merge_20180530_1704.py | 14 -------------- 2 files changed, 2 insertions(+), 16 deletions(-) rename app/dashboard/migrations/{0075_auto_20180523_0751.py => 0082_auto_20180531_1521.py} (90%) delete mode 100644 app/dashboard/migrations/0082_merge_20180530_1704.py diff --git a/app/dashboard/migrations/0075_auto_20180523_0751.py b/app/dashboard/migrations/0082_auto_20180531_1521.py similarity index 90% rename from app/dashboard/migrations/0075_auto_20180523_0751.py rename to app/dashboard/migrations/0082_auto_20180531_1521.py index af62062a6e8..1b73591d8f4 100644 --- a/app/dashboard/migrations/0075_auto_20180523_0751.py +++ b/app/dashboard/migrations/0082_auto_20180531_1521.py @@ -1,4 +1,4 @@ -# Generated by Django 2.0.5 on 2018-05-23 07:51 +# Generated by Django 2.0.5 on 2018-05-31 15:21 from django.db import migrations, models @@ -6,7 +6,7 @@ class Migration(migrations.Migration): dependencies = [ - ('dashboard', '0074_auto_20180515_1510'), + ('dashboard', '0081_merge_20180529_2228'), ] operations = [ diff --git a/app/dashboard/migrations/0082_merge_20180530_1704.py b/app/dashboard/migrations/0082_merge_20180530_1704.py deleted file mode 100644 index 0e1444c9b20..00000000000 --- a/app/dashboard/migrations/0082_merge_20180530_1704.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 2.0.5 on 2018-05-30 17:04 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('dashboard', '0081_merge_20180529_2228'), - ('dashboard', '0075_auto_20180523_0751'), - ] - - operations = [ - ] From 1594b3160cbeaa4bcf1d2d578d27bb4252f28584 Mon Sep 17 00:00:00 2001 From: Owocki Date: Mon, 4 Jun 2018 09:57:10 -0600 Subject: [PATCH 20/39] merge migrations --- .../migrations/0084_merge_20180604_1601.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 app/dashboard/migrations/0084_merge_20180604_1601.py diff --git a/app/dashboard/migrations/0084_merge_20180604_1601.py b/app/dashboard/migrations/0084_merge_20180604_1601.py new file mode 100644 index 00000000000..da2310154bd --- /dev/null +++ b/app/dashboard/migrations/0084_merge_20180604_1601.py @@ -0,0 +1,14 @@ +# Generated by Django 2.0.5 on 2018-06-04 16:01 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('dashboard', '0083_merge_20180601_1945'), + ('dashboard', '0082_auto_20180531_1521'), + ] + + operations = [ + ] From 108111bf391ff058c8fed528377118b0a78bb448 Mon Sep 17 00:00:00 2001 From: Owocki Date: Mon, 4 Jun 2018 10:09:31 -0600 Subject: [PATCH 21/39] copy --- app/dashboard/templates/bounty_details.html | 2 +- app/dashboard/templates/shared/permissions_type_tooltip.html | 2 +- app/dashboard/templates/shared/sidebar_search.html | 2 +- app/dashboard/templates/submit_bounty.html | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/dashboard/templates/bounty_details.html b/app/dashboard/templates/bounty_details.html index 2ee6183ea18..10391c335a7 100644 --- a/app/dashboard/templates/bounty_details.html +++ b/app/dashboard/templates/bounty_details.html @@ -105,7 +105,7 @@

- {% trans "Worker Application Scheme" %} + {% trans "Permissions" %} >
diff --git a/app/dashboard/templates/shared/permissions_type_tooltip.html b/app/dashboard/templates/shared/permissions_type_tooltip.html index cea257fe622..fb24d1df295 100644 --- a/app/dashboard/templates/shared/permissions_type_tooltip.html +++ b/app/dashboard/templates/shared/permissions_type_tooltip.html @@ -16,7 +16,7 @@ {% endcomment %} {% load i18n %}
-

{% trans "Application Schemes" %}

+

{% trans "Permissions" %}

1
diff --git a/app/dashboard/templates/shared/sidebar_search.html b/app/dashboard/templates/shared/sidebar_search.html index a66233b2b69..28d65749f0c 100644 --- a/app/dashboard/templates/shared/sidebar_search.html +++ b/app/dashboard/templates/shared/sidebar_search.html @@ -119,7 +119,7 @@
-
{% trans "Application Scheme" %}
+
{% trans "Permissions" %}
- What is this?
diff --git a/app/dashboard/templates/submit_bounty.html b/app/dashboard/templates/submit_bounty.html index eaf2ede4eba..ceddc4167a0 100644 --- a/app/dashboard/templates/submit_bounty.html +++ b/app/dashboard/templates/submit_bounty.html @@ -108,7 +108,7 @@

{% trans "Fund an Issue" %}

- +