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

Add hooks and add new question type #6778

Merged
merged 21 commits into from
Jun 22, 2020
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
3 changes: 3 additions & 0 deletions app/assets/v2/css/colors.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ html {
--gc-purple-hover: #0b0d9d;
--gc-green: #0FCE7C;
--gc-green-hover: #25E899;
--gc-light-green: rgb(211, 250, 235);
--gc-pink: #f9006c;
--gc-pink-hover: #f167a3;
--gc-yellow: #F5A623;
Expand Down Expand Up @@ -52,6 +53,8 @@ html.dark-mode {
--gc-purple-hover: #0b0d9d;
--gc-green: #0FCE7C;
--gc-green-hover: #25E899;
--gc-light-green: transparent;

--gc-pink: #f9006c;
--gc-pink-hover: #f167a3;
--gc-yellow: #F5A623;
Expand Down
66 changes: 66 additions & 0 deletions app/assets/v2/css/town_square.css
Original file line number Diff line number Diff line change
Expand Up @@ -1216,10 +1216,65 @@ body.green.offer_view .announce {
border-top: 6px solid rgba(62, 0, 255, 0.4);
}

.activity.hackathon_new_hacker {
position: relative;
box-sizing: border-box;
z-index: 2;
}

.activity.hackathon_new_hacker:before {
content: '';
position: absolute;
top: -6px;
left: 0;
height: 6px;
width: 100%;
display: block;
background: linear-gradient(to right, #B572D4 0%, #F9DD5B 37%, #75EDB5 100%);
}

.activity.hackathon_new_hacker:after {
content: '';
position: absolute;
top: 58px;
left: 3%;
height: 143px;
width: 94%;
display: block;
background: --var(--gc-light-green);
z-index: -2;
}

.activity.hackathon_new_hacker .activity-avatar::after {
height: 6.1rem;
content: '';
background-image: url(/static/v2/images/townsquare-nh.svg);
width: 6.1rem;
position: absolute;
top: -24px;
left: 8px;
z-index: -1;
}

.activity.hackathon_new_hacker .activity_detail::after {
height: 100%;
content: '';
background: url(/static/v2/images/townsqure-party.png) 0 0 no-repeat;
width: 100%;
position: absolute;
top: 55px;
right: -309px;
z-index: -1;
}

.new_hackathon_project {
border-top: 6px solid rgba(15, 206, 124, 0.25);
}

.activity_main .bg_hackathon_new_hacker {
background-color: #D3FAEB;
}

.bg-gc-blue {
background-color: var(--gc-blue);
}
Expand Down Expand Up @@ -1299,3 +1354,14 @@ body.green.offer_view .announce {
background: var(--gc-dark-violet);
color: white;
}

.tag-list .tag-list__item {
background: #E8F0FA;
padding: 4px;
border-radius: 2px;
font-size: 11px;
color: #6487AE;
display: inline-block;
margin-bottom: 2px;
margin-top: 2px;
}
28 changes: 28 additions & 0 deletions app/assets/v2/images/dots.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions app/assets/v2/images/townsquare-nh.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/v2/images/townsqure-party.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 25 additions & 5 deletions app/dashboard/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
Activity, Answer, BlockedURLFilter, BlockedUser, Bounty, BountyEvent, BountyFulfillment, BountyInvites,
BountySyncRequest, CoinRedemption, CoinRedemptionRequest, Coupon, Earning, FeedbackEntry, FundRequest,
HackathonEvent, HackathonProject, HackathonRegistration, HackathonSponsor, Interest, Investigation, LabsResearch,
ObjectView, Option, Poll, PortfolioItem, Profile, ProfileVerification, ProfileView, Question, SearchHistory,
Sponsor, Tip, TipPayout, TokenApproval, TribeMember, UserAction, UserVerificationModel,
ObjectView, Option, Poll, PollMedia, PortfolioItem, Profile, ProfileVerification, ProfileView, Question, SearchHistory,
Sponsor, Tip, TipPayout, TokenApproval, TribeMember, UserAction, UserVerificationModel,
)


Expand Down Expand Up @@ -445,7 +445,7 @@ class FundRequestAdmin(admin.ModelAdmin):


class QuestionInline(SortableInlineAdminMixin, admin.TabularInline):
fields = ['id', 'poll', 'question_type', 'text']
fields = ['id', 'poll', 'question_type', 'text', 'hook']
readonly_fields = ['id']
raw_id_fields = ['poll']
show_change_link = True
Expand All @@ -470,11 +470,19 @@ class PollsAdmin(admin.ModelAdmin):


class QuestionsAdmin(admin.ModelAdmin):
list_display = ['id', 'poll', 'question_type', 'text']
raw_id_fields = ['poll']
list_display = ['id', 'poll', 'question_type', 'text', 'img']
raw_id_fields = ['poll', 'header']
search_fields = ['question_type', 'text']
inlines = [OptionsInline]

def img(self, instance):
header = instance.header
if not header or not header.image:
return 'n/a'
Copy link
Contributor

Choose a reason for hiding this comment

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

why return n/a here instead of just None?

Copy link
Member

Choose a reason for hiding this comment

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

@zoek1 could you fix this up in a follow up PR please ?

img_html = format_html('<img src={} style="max-width:30px; max-height: 30px">', mark_safe(header.image.url))
Copy link
Contributor

Choose a reason for hiding this comment

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

Note to other reviewers - generally we probably wouldn't allow this with the mark_save tag but in this case its probably ok since only Admins should have the ability to set and add an image

return img_html



class OptionsAdmin(admin.ModelAdmin):
list_display = ['id', 'question', 'text']
Expand All @@ -488,6 +496,17 @@ class AnswersAdmin(admin.ModelAdmin):
unique_together = ('user', 'question', 'choice')


class PollMediaAdmin(admin.ModelAdmin):
list_display = ['id', 'name', 'img']

def img(self, instance):
image = instance.image
if not image:
return 'n/a'
img_html = format_html('<img src={} style="max-width:30px; max-height: 30px">', mark_safe(image.url))
return img_html


class ProfileVerificationAdmin(admin.ModelAdmin):
list_display = ['id', 'profile', 'success', 'validation_passed', 'caller_type', 'mobile_network_code', 'country_code', 'carrier_name', 'carrier_type',
'phone_number', 'carrier_error_code']
Expand Down Expand Up @@ -531,4 +550,5 @@ class ProfileVerificationAdmin(admin.ModelAdmin):
admin.site.register(ObjectView, ObjectViewAdmin)
admin.site.register(Option, OptionsAdmin)
admin.site.register(Answer, AnswersAdmin)
admin.site.register(PollMedia, PollMediaAdmin)
admin.site.register(ProfileVerification, ProfileVerificationAdmin)
59 changes: 59 additions & 0 deletions app/dashboard/migrations/0123_auto_20200617_1549.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Generated by Django 2.2.4 on 2020-06-17 15:49

import app.utils
from django.db import migrations, models
import django.db.models.deletion
import economy.models


class Migration(migrations.Migration):

dependencies = [
('dashboard', '0122_auto_20200615_1510'),
]

operations = [
migrations.CreateModel(
name='PollMedia',
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)),
('name', models.CharField(max_length=350)),
('image', models.ImageField(blank=True, help_text='Poll media asset', null=True, upload_to=app.utils.get_upload_filename)),
],
options={
'abstract': False,
},
),
migrations.AddField(
model_name='hackathonregistration',
name='looking_project',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='hackathonregistration',
name='looking_team_members',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='question',
name='hook',
field=models.CharField(choices=[('NO_ACTION', 'No trigger any action'), ('TOWNSQUARE_INTRO', 'Create intro on Townsquare'), ('LOOKING_TEAM_PROJECT', 'Looking for team or project')], default='NO_ACTION', max_length=50),
),
migrations.AlterField(
model_name='activity',
name='activity_type',
field=models.CharField(blank=True, choices=[('wall_post', 'Wall Post'), ('status_update', 'Update status'), ('new_bounty', 'New Bounty'), ('start_work', 'Work Started'), ('stop_work', 'Work Stopped'), ('work_submitted', 'Work Submitted'), ('work_done', 'Work Done'), ('worker_approved', 'Worker Approved'), ('worker_rejected', 'Worker Rejected'), ('worker_applied', 'Worker Applied'), ('increased_bounty', 'Increased Funding'), ('killed_bounty', 'Canceled Bounty'), ('new_tip', 'New Tip'), ('receive_tip', 'Tip Received'), ('bounty_abandonment_escalation_to_mods', 'Escalated checkin from @gitcoinbot about bounty status'), ('bounty_abandonment_warning', 'Checkin from @gitcoinbot about bounty status'), ('bounty_removed_slashed_by_staff', 'Dinged and Removed from Bounty by Staff'), ('bounty_removed_by_staff', 'Removed from Bounty by Staff'), ('bounty_removed_by_funder', 'Removed from Bounty by Funder'), ('new_crowdfund', 'New Crowdfund Contribution'), ('new_grant', 'New Grant'), ('update_grant', 'Updated Grant'), ('killed_grant', 'Cancelled Grant'), ('negative_contribution', 'Negative Grant Contribution'), ('new_grant_contribution', 'Contributed to Grant'), ('new_grant_subscription', 'Subscribed to Grant'), ('killed_grant_contribution', 'Cancelled Grant Contribution'), ('new_kudos', 'New Kudos'), ('created_kudos', 'Created Kudos'), ('receive_kudos', 'Receive Kudos'), ('joined', 'Joined Gitcoin'), ('played_quest', 'Played Quest'), ('beat_quest', 'Beat Quest'), ('created_quest', 'Created Quest'), ('updated_avatar', 'Updated Avatar'), ('mini_clr_payout', 'Mini CLR Payout'), ('leaderboard_rank', 'Leaderboard Rank'), ('consolidated_leaderboard_rank', 'Consolidated Leaderboard Rank'), ('consolidated_mini_clr_payout', 'Consolidated CLR Payout'), ('hackathon_registration', 'Hackathon Registration'), ('hackathon_new_hacker', 'Hackathon Registration'), ('new_hackathon_project', 'New Hackathon Project'), ('flagged_grant', 'Flagged Grant')], db_index=True, max_length=50),
),
migrations.AlterField(
model_name='question',
name='question_type',
field=models.CharField(choices=[('SINGLE_OPTION', 'Single option'), ('SINGLE_CHOICE', 'Single Choice'), ('MULTIPLE_CHOICE', 'Multiple Choices'), ('OPEN', 'Open')], max_length=50),
),
migrations.AddField(
model_name='question',
name='header',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='dashboard.PollMedia'),
),
]
68 changes: 68 additions & 0 deletions app/dashboard/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2164,6 +2164,7 @@ class Activity(SuperModel):
('consolidated_leaderboard_rank', 'Consolidated Leaderboard Rank'),
('consolidated_mini_clr_payout', 'Consolidated CLR Payout'),
('hackathon_registration', 'Hackathon Registration'),
('hackathon_new_hacker', 'Hackathon Registration'),
('new_hackathon_project', 'New Hackathon Project'),
('flagged_grant', 'Flagged Grant'),
]
Expand Down Expand Up @@ -2583,6 +2584,8 @@ class HackathonRegistration(SuperModel):
blank=True
)
referer = models.URLField(null=True, blank=True, help_text='Url comes from')
looking_team_members = models.BooleanField(default=False)
looking_project = models.BooleanField(default=False)
registrant = models.ForeignKey(
'dashboard.Profile',
related_name='hackathon_registration',
Expand Down Expand Up @@ -5026,17 +5029,39 @@ class Poll(SuperModel):
hackathon = models.ManyToManyField(HackathonEvent)


class PollMedia(SuperModel):
name = models.CharField(max_length=350)
image = models.ImageField(
Copy link
Contributor

Choose a reason for hiding this comment

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

I have a feeling this won't work in prod because of the way static files are handled - @thelostone-mc @octavioamu what do y'all think?

Copy link
Member

Choose a reason for hiding this comment

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

Hmm it's done like that for a few other image fields in our model 🤔

upload_to=get_upload_filename,
null=True,
blank=True,
help_text=_('Poll media asset')
)

def __str__(self):
return f'{self.id} - {self.name}'


class Question(SuperModel):
TYPE_QUESTIONS = (
('SINGLE_OPTION', 'Single option'),
('SINGLE_CHOICE', 'Single Choice'),
('MULTIPLE_CHOICE', 'Multiple Choices'),
('OPEN', 'Open'),
)

TYPE_HOOKS = (
('NO_ACTION', 'No trigger any action'),
('TOWNSQUARE_INTRO', 'Create intro on Townsquare'),
('LOOKING_TEAM_PROJECT', 'Looking for team or project')
)

hook = models.CharField(default='NO_ACTION', choices=TYPE_HOOKS, max_length=50)
poll = models.ForeignKey(Poll, on_delete=models.CASCADE, null=True, blank=True)
question_type = models.CharField(choices=TYPE_QUESTIONS, max_length=50, blank=False, null=False)
text = models.CharField(max_length=350, blank=True, null=True)
order = models.PositiveIntegerField(default=0, blank=False, null=False)
header = models.ForeignKey(PollMedia, null=True, on_delete=models.SET_NULL)

class Meta(object):
ordering = ['order']
Expand All @@ -5062,6 +5087,49 @@ class Answer(SuperModel):
hackathon = models.ForeignKey(HackathonEvent, null=True, on_delete=models.CASCADE)


@receiver(post_save, sender=Answer, dispatch_uid='hooks_on_question_response')
def psave_answer(sender, instance, created, **kwargs):
if created:
if instance.question.hook == 'TOWNSQUARE_INTRO':
registration = HackathonRegistration.objects.filter(hackathon=instance.hackathon,
registrant=instance.user.profile).first()

Activity.objects.create(
profile=instance.user.profile,
hackathonevent=instance.hackathon,
activity_type='hackathon_new_hacker',
metadata={
'answer': instance.id,
'intro_text': f'{instance.open_response or ""} #intro',
'looking_members': registration.looking_team_members if registration else False,
'looking_project': registration.looking_project if registration else False,
'hackathon_registration': registration.id if registration else 0
}
)
elif instance.question.hook == 'LOOKING_TEAM_PROJECT':
registration = HackathonRegistration.objects.filter(hackathon=instance.hackathon,
registrant=instance.user.profile).first()
print(instance)
if registration:
if instance.choice.text.lower().find('team') != -1:
registration.looking_team_members = True

if instance.choice.text.lower().find('project') != -1:
registration.looking_project = True

registration.save()

activity = Activity.objects.filter(
profile=instance.user.profile,
hackathonevent=instance.hackathon,
activity_type='hackathon_new_hacker').last()

if activity:
activity.metadata['looking_team_members'] = registration.looking_team_members
activity.metadata['looking_project'] = registration.looking_project
activity.save()


class Investigation(SuperModel):
profile = models.ForeignKey(
'dashboard.Profile', on_delete=models.CASCADE, related_name='investigations', blank=True
Expand Down
Loading