Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: coupon code for bounty creation fee #4237

Merged
merged 6 commits into from
Jun 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/assets/v2/css/submit_bounty.css
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ input:read-only {

#no-issue-banner img {
max-width: 100%;
border: 1px solid #d1d1d1;
border-top: 1px solid #d1d1d1;
border-bottom: 1px solid #d1d1d1;
}

#no-issue-banner {
Expand All @@ -98,7 +99,6 @@ input:read-only {
}

.issue-details-public {
background-color: #F9F9F9;
padding: 1em;
}

Expand Down
3 changes: 2 additions & 1 deletion app/assets/v2/js/pages/new_bounty.js
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,8 @@ $('#submitBounty').validate({
metadata: metadata,
tokenName: tokenName,
tokenAddress: tokenAddress,
expire_date: expire_date
expire_date: expire_date,
coupon_code: $('#coupon_code').val()
},
meta: {
platform: 'gitcoin',
Expand Down
4 changes: 2 additions & 2 deletions app/assets/v2/js/pages/wallet_estimate.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
window.addEventListener('load', function() {
try {
if (web3.currentProvider.isTrust) {
if (web3 && web3.currentProvider.isTrust) {
$('#trust_label').show();
} else {
$('#metamask_label').show();
}
} catch (ignore) {
console.log('%c error: web3 not defined', 'color: red');
console.log('%c error: web3 not defined. ensure metamask is installed & unlocked', 'color: red');
}
setTimeout(prefill_recommended_prices, 1000);

Expand Down
4 changes: 2 additions & 2 deletions app/assets/v2/js/truncate-hash.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ new truncateHash();
new getaddress(elem[i], currentWallet);
}
} catch (ignore) {
console.log('%c error: web3 not defined', 'color: red');
console.log('%c error: web3 not defined. ensure metamask is installed & unlocked', 'color: red');
}
};
}());
Expand All @@ -78,5 +78,5 @@ try {
new metamaskAddress();
});
} catch (ignore) {
console.log('%c error: web3 not defined', 'color: red');
console.log('%c error: web3 not defined. ensure metamask is installed & unlocked', 'color: red');
}
24 changes: 22 additions & 2 deletions app/dashboard/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

from .models import (
Activity, BlockedUser, Bounty, BountyFulfillment, BountyInvites, BountySyncRequest, CoinRedemption,
CoinRedemptionRequest, FeedbackEntry, HackathonEvent, Interest, LabsResearch, Profile, RefundFeeRequest,
CoinRedemptionRequest, Coupon, FeedbackEntry, HackathonEvent, Interest, LabsResearch, Profile, RefundFeeRequest,
SearchHistory, Tip, TokenApproval, Tool, ToolVote, UserAction, UserVerificationModel,
)

Expand Down Expand Up @@ -142,7 +142,10 @@ class BountyAdmin(admin.ModelAdmin):

search_fields = ['raw_data', 'title', 'bounty_owner_github_username', 'token_name']
list_display = ['pk', 'img', 'idx_status', 'network_link', 'standard_bounties_id_link', 'bounty_link', 'what']
readonly_fields = ['what', 'img', 'fulfillments_link', 'standard_bounties_id_link', 'bounty_link', 'network_link', '_action_urls']
readonly_fields = [
'what', 'img', 'fulfillments_link', 'standard_bounties_id_link', 'bounty_link', 'network_link',
'_action_urls', 'coupon_link'
]

def img(self, instance):
if not instance.avatar_url:
Expand Down Expand Up @@ -179,6 +182,11 @@ def network_link(self, instance):
url = f'/_administrationdashboard/bounty/?network={instance.network}'
return mark_safe(f"<a href={url}>{copy}</a>")

def coupon_link(self, instance):
copy = f'{instance.coupon_code.code}'
url = f'/_administrationdashboard/coupon/{instance.coupon_code.pk}'
return mark_safe(f"<a href={url}>{copy}</a>")


class RefundFeeRequestAdmin(admin.ModelAdmin):
"""Setup the RefundFeeRequest admin results display."""
Expand Down Expand Up @@ -253,6 +261,17 @@ def explorer_link(self, instance):
return mark_safe(f'<a href="{url}">Explorer Link</a>')


class CouponAdmin(admin.ModelAdmin):
"""The admin object to maintain discount coupons for bounty"""

list_display = ['pk', 'code', 'fee_percentage', 'expiry_date', 'link']
search_fields = ['created_on', 'code', 'fee_percentage']

def link(self, instance):
url = f'/funding/new?coupon={instance.code}'
return mark_safe(f'<a target="_blank" href="{url}">http://gitcoin.co{url}</a>')


admin.site.register(SearchHistory, SearchHistoryAdmin)
admin.site.register(Activity, ActivityAdmin)
admin.site.register(BlockedUser, GeneralAdmin)
Expand All @@ -274,3 +293,4 @@ def explorer_link(self, instance):
admin.site.register(LabsResearch)
admin.site.register(UserVerificationModel, VerificationAdmin)
admin.site.register(RefundFeeRequest, RefundFeeRequestAdmin)
admin.site.register(Coupon, CouponAdmin)
16 changes: 13 additions & 3 deletions app/dashboard/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@

from app.utils import get_semaphore, sync_profile
from dashboard.models import (
Activity, Bounty, BountyDocuments, BountyFulfillment, BountyInvites, BountySyncRequest, HackathonEvent, UserAction,
Activity, Bounty, BountyDocuments, BountyFulfillment, BountyInvites, BountySyncRequest, Coupon, HackathonEvent,
UserAction,
)
from dashboard.notifications import (
maybe_market_to_email, maybe_market_to_github, maybe_market_to_slack, maybe_market_to_twitter,
Expand Down Expand Up @@ -418,6 +419,15 @@ def create_new_bounty(old_bounties, bounty_payload, bounty_details, bounty_id):
'fee_tx_id': bounty_payload.get('fee_tx_id', '0x0'),
'fee_amount': bounty_payload.get('fee_amount', 0)
}

coupon_code = bounty_payload.get('coupon_code', None)
if coupon_code:
coupon = Coupon.objects.get(code=coupon_code)
if coupon:
bounty_kwargs.update({
'coupon_code': coupon
})

if not latest_old_bounty:
print("no latest old bounty")
schemes = bounty_payload.get('schemes', {})
Expand Down Expand Up @@ -476,8 +486,8 @@ def create_new_bounty(old_bounties, bounty_payload, bounty_details, bounty_id):
'bounty_owner_github_username', 'bounty_owner_address', 'bounty_owner_email', 'bounty_owner_name',
'github_comments', 'override_status', 'last_comment_date', 'snooze_warnings_for_days',
'admin_override_and_hide', 'admin_override_suspend_auto_approval', 'admin_mark_as_remarket_ready',
'funding_organisation', 'bounty_reserved_for_user', 'is_featured', 'featuring_date', 'fee_tx_id',
'fee_amount', 'repo_type', 'unsigned_nda'
'funding_organisation', 'bounty_reserved_for_user', 'is_featured', 'featuring_date', 'coupon_code',
'fee_tx_id', 'fee_amount', 'repo_type', 'unsigned_nda'
],
)
if latest_old_bounty_dict['bounty_reserved_for_user']:
Expand Down
35 changes: 35 additions & 0 deletions app/dashboard/migrations/0032_auto_20190601_1103.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Generated by Django 2.1.7 on 2019-06-01 11:03

import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import economy.models


class Migration(migrations.Migration):

dependencies = [
('dashboard', '0031_auto_20190424_1611'),
]

operations = [
migrations.CreateModel(
name='Coupon',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_on', models.DateTimeField(db_index=True, default=economy.models.get_time)),
('modified_on', models.DateTimeField(default=economy.models.get_time)),
('code', models.CharField(max_length=10, unique=True)),
('fee_percentage', models.IntegerField(validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(100)])),
('expiry_date', models.DateField()),
],
options={
'abstract': False,
},
),
migrations.AddField(
model_name='bounty',
name='coupon_code',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='coupon', to='dashboard.Coupon'),
),
]
13 changes: 13 additions & 0 deletions app/dashboard/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from django.contrib.auth.signals import user_logged_in, user_logged_out
from django.contrib.humanize.templatetags.humanize import naturalday, naturaltime
from django.contrib.postgres.fields import ArrayField, JSONField
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.db.models import Q, Sum
from django.db.models.signals import m2m_changed, post_delete, post_save, pre_save
Expand Down Expand Up @@ -282,6 +283,7 @@ class Bounty(SuperModel):
featuring_date = models.DateTimeField(blank=True, null=True)
fee_amount = models.DecimalField(default=0, decimal_places=18, max_digits=50)
fee_tx_id = models.CharField(default="0x0", max_length=255, blank=True)
coupon_code = models.ForeignKey('dashboard.Coupon', blank=True, null=True, related_name='coupon', on_delete=models.SET_NULL)
unsigned_nda = models.ForeignKey('dashboard.BountyDocuments', blank=True, null=True, related_name='bounty', on_delete=models.SET_NULL)

token_value_time_peg = models.DateTimeField(blank=True, null=True)
Expand Down Expand Up @@ -3090,6 +3092,7 @@ def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super().save(*args, **kwargs)


class FeedbackEntry(SuperModel):
bounty = models.ForeignKey(
'dashboard.Bounty',
Expand Down Expand Up @@ -3124,3 +3127,13 @@ class FeedbackEntry(SuperModel):
def __str__(self):
"""Return the string representation of a Bounty."""
return f'<Feedback Bounty #{self.bounty} - from: {self.sender_profile} to: {self.receiver_profile}>'


class Coupon(SuperModel):
code = models.CharField(unique=True, max_length=10)
fee_percentage = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(100)])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!

expiry_date = models.DateField()

def __str__(self):
"""Return the string representation of Coupon."""
return f'code: {self.code} | fee: {self.fee_percentage} %'
4 changes: 4 additions & 0 deletions app/dashboard/templates/bounty/fund.html
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ <h5 class="font-smaller-3 font-weight-bold mb-2">Important Notes on Private Repo
</div>

<div class="bg-white my-3 px-5 rounded">
<input type="hidden" id="coupon_code" name="coupon_code" value="{{ coupon_code }}">
{% if FEE_PERCENTAGE != 0 %}
<div id="fee-section" class="py-4">
<h5 class="font-body letter-spacing text-black-60 text-uppercase">
Expand Down Expand Up @@ -388,6 +389,9 @@ <h5 class="text-uppercase h3 font-weight-bold mb-0">{% trans "Total"%}</h5>

<script>
document.FEE_PERCENTAGE = {{ FEE_PERCENTAGE }};
{% if expired_coupon %}
_alert({ message: 'This coupon has expired.' }, 'error');
{% endif %}
</script>

<script src="{% static "v2/js/pages/wallet_estimate.js" %}"></script>
Expand Down
13 changes: 12 additions & 1 deletion app/dashboard/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@

from .helpers import get_bounty_data_for_activity, handle_bounty_views
from .models import (
Activity, Bounty, BountyDocuments, BountyFulfillment, BountyInvites, CoinRedemption, CoinRedemptionRequest,
Activity, Bounty, BountyDocuments, BountyFulfillment, BountyInvites, CoinRedemption, CoinRedemptionRequest, Coupon,
FeedbackEntry, HackathonEvent, Interest, LabsResearch, Profile, ProfileSerializer, RefundFeeRequest, Subscription,
Tool, ToolVote, UserAction, UserVerificationModel,
)
Expand Down Expand Up @@ -2464,7 +2464,18 @@ def new_bounty(request):
title=_('Create Funded Issue'),
update=bounty_params,
)

params['FEE_PERCENTAGE'] = request.user.profile.fee_percentage if request.user.is_authenticated else 10

coupon_code = request.GET.get('coupon', False)
if coupon_code:
coupon = Coupon.objects.get(code=coupon_code)
if coupon.expiry_date > datetime.now().date():
params['FEE_PERCENTAGE'] = coupon.fee_percentage
params['coupon_code'] = coupon.code
else:
params['expired_coupon'] = True

return TemplateResponse(request, 'bounty/fund.html', params)


Expand Down