-
-
Notifications
You must be signed in to change notification settings - Fork 775
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
Invite users to a bounty based on skills #5060
Changes from all commits
cae78b8
4ef41a7
14656e4
7c1cc5a
afefead
2eb2835
c140fdf
58147cb
619fa9e
be8b094
0178cfe
c676d6b
34d7d4b
640a913
4c1da1d
23c4c92
0e8d97e
defb188
623a6c0
2b69f46
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -62,7 +62,9 @@ | |
from kudos.utils import humanize_name | ||
from marketing.mails import admin_contact_funder, bounty_uninterested | ||
from marketing.mails import funder_payout_reminder as funder_payout_reminder_mail | ||
from marketing.mails import new_reserved_issue, start_work_approved, start_work_new_applicant, start_work_rejected | ||
from marketing.mails import ( | ||
new_reserved_issue, share_bounty, start_work_approved, start_work_new_applicant, start_work_rejected, | ||
) | ||
from marketing.models import Keyword | ||
from pytz import UTC | ||
from ratelimit.decorators import ratelimit | ||
|
@@ -742,6 +744,7 @@ def users_directory(request): | |
keywords = programming_languages + programming_languages_full | ||
|
||
params = { | ||
'is_staff': request.user.is_staff, | ||
'active': 'users', | ||
'title': 'Users', | ||
'meta_title': "", | ||
|
@@ -751,41 +754,11 @@ def users_directory(request): | |
return TemplateResponse(request, 'dashboard/users.html', params) | ||
|
||
|
||
@require_GET | ||
def users_fetch(request): | ||
"""Handle displaying users.""" | ||
q = request.GET.get('search', '') | ||
skills = request.GET.get('skills', '') | ||
limit = int(request.GET.get('limit', 10)) | ||
page = int(request.GET.get('page', 1)) | ||
order_by = request.GET.get('order_by', '-actions_count') | ||
bounties_completed = request.GET.get('bounties_completed', '').strip().split(',') | ||
leaderboard_rank = request.GET.get('leaderboard_rank', '').strip().split(',') | ||
rating = int(request.GET.get('rating', '0')) | ||
organisation = request.GET.get('organisation', '') | ||
|
||
user_id = request.GET.get('user', None) | ||
if user_id: | ||
current_user = User.objects.get(id=int(user_id)) | ||
else: | ||
current_user = request.user if hasattr(request, 'user') and request.user.is_authenticated else None | ||
|
||
context = {} | ||
def users_fetch_filters(profile_list, skills, bounties_completed, leaderboard_rank, rating, organisation ): | ||
if not settings.DEBUG: | ||
network = 'mainnet' | ||
else: | ||
network = 'rinkeby' | ||
if current_user: | ||
profile_list = Profile.objects.prefetch_related( | ||
'fulfilled', 'leaderboard_ranks', 'feedbacks_got' | ||
).exclude(hide_profile=True) | ||
else: | ||
profile_list = Profile.objects.prefetch_related( | ||
'fulfilled', 'leaderboard_ranks', 'feedbacks_got' | ||
).exclude(hide_profile=True) | ||
|
||
if q: | ||
profile_list = profile_list.filter(Q(handle__icontains=q) | Q(keywords__icontains=q)) | ||
|
||
if skills: | ||
profile_list = profile_list.filter(keywords__icontains=skills) | ||
|
@@ -821,6 +794,54 @@ def users_fetch(request): | |
fulfilled__bounty__github_url__icontains=organisation | ||
).distinct() | ||
|
||
return profile_list | ||
|
||
|
||
|
||
@require_GET | ||
def users_fetch(request): | ||
"""Handle displaying users.""" | ||
q = request.GET.get('search', '') | ||
skills = request.GET.get('skills', '') | ||
limit = int(request.GET.get('limit', 10)) | ||
page = int(request.GET.get('page', 1)) | ||
order_by = request.GET.get('order_by', '-actions_count') | ||
bounties_completed = request.GET.get('bounties_completed', '').strip().split(',') | ||
leaderboard_rank = request.GET.get('leaderboard_rank', '').strip().split(',') | ||
rating = int(request.GET.get('rating', '0')) | ||
organisation = request.GET.get('organisation', '') | ||
|
||
user_id = request.GET.get('user', None) | ||
if user_id: | ||
current_user = User.objects.get(id=int(user_id)) | ||
else: | ||
current_user = request.user if hasattr(request, 'user') and request.user.is_authenticated else None | ||
|
||
context = {} | ||
if not settings.DEBUG: | ||
network = 'mainnet' | ||
else: | ||
network = 'rinkeby' | ||
if current_user: | ||
profile_list = Profile.objects.prefetch_related( | ||
'fulfilled', 'leaderboard_ranks', 'feedbacks_got' | ||
).exclude(hide_profile=True) | ||
else: | ||
profile_list = Profile.objects.prefetch_related( | ||
'fulfilled', 'leaderboard_ranks', 'feedbacks_got' | ||
).exclude(hide_profile=True) | ||
|
||
if q: | ||
profile_list = profile_list.filter(Q(handle__icontains=q) | Q(keywords__icontains=q)) | ||
|
||
profile_list = users_fetch_filters( | ||
profile_list, | ||
skills, | ||
bounties_completed, | ||
leaderboard_rank, | ||
rating, | ||
organisation) | ||
|
||
def previous_worked(): | ||
if current_user.profile.persona_is_funder: | ||
return Count( | ||
|
@@ -861,8 +882,7 @@ def previous_worked(): | |
).order_by('-previous_worked_count') | ||
for user in this_page: | ||
previously_worked_with = 0 | ||
count_work_completed = Activity.objects.filter(profile=user, activity_type='work_done').count() | ||
count_work_in_progress = Activity.objects.filter(profile=user, activity_type='start_work').count() | ||
count_work_completed = user.get_fulfilled_bounties(network=network).count() | ||
profile_json = { | ||
k: getattr(user, k) for k in | ||
['id', 'actions_count', 'created_on', 'handle', 'hide_profile', | ||
|
@@ -875,7 +895,6 @@ def previous_worked(): | |
profile_json['position_contributor'] = user.get_contributor_leaderboard_index() | ||
profile_json['position_funder'] = user.get_funder_leaderboard_index() | ||
profile_json['work_done'] = count_work_completed | ||
profile_json['work_inprogress'] = count_work_in_progress | ||
profile_json['verification'] = user.get_my_verified_check | ||
profile_json['avg_rating'] = user.get_average_star_rating | ||
|
||
|
@@ -1145,6 +1164,76 @@ def social_contribution_modal(request): | |
return TemplateResponse(request, 'social_contribution_modal.html', params) | ||
|
||
|
||
@csrf_exempt | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we add the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. humm can be, Im restricting it using request.user.is_staff, seems to me there are plans to do this a feature for users so not sure if worst changing it. |
||
@require_POST | ||
def bulk_invite(request): | ||
"""Invite users with matching skills to a bounty. | ||
|
||
Args: | ||
bounty_id (int): The primary key of the bounty to be accepted. | ||
skills (string): Comma separated list of matching keywords. | ||
|
||
Raises: | ||
Http403: The exception is raised if the user is not authenticated or | ||
the args are missing. | ||
Http401: The exception is raised if the user is not a staff member. | ||
|
||
Returns: | ||
Http200: Json response with {'status': 200, 'msg': 'email_sent'}. | ||
|
||
""" | ||
from .utils import get_bounty_invite_url | ||
|
||
if not request.user.is_staff: | ||
return JsonResponse({'status': 401, | ||
'msg': 'Unauthorized'}) | ||
|
||
inviter = request.user if request.user.is_authenticated else None | ||
skills = ','.join(request.POST.getlist('params[skills][]', [])) | ||
bounties_completed = request.POST.get('params[bounties_completed]', '').strip().split(',') | ||
leaderboard_rank = request.POST.get('params[leaderboard_rank]', '').strip().split(',') | ||
rating = int(request.POST.get('params[rating]', '0')) | ||
organisation = request.POST.get('params[organisation]', '') | ||
bounty_id = request.POST.get('bountyId') | ||
octavioamu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if None in (skills, bounty_id, inviter): | ||
return JsonResponse({'success': False}, status=400) | ||
|
||
bounty = Bounty.objects.current().get(id=int(bounty_id)) | ||
|
||
profiles = Profile.objects.prefetch_related( | ||
'fulfilled', 'leaderboard_ranks', 'feedbacks_got' | ||
).exclude(hide_profile=True) | ||
|
||
profiles = users_fetch_filters( | ||
profiles, | ||
skills, | ||
bounties_completed, | ||
leaderboard_rank, | ||
rating, | ||
organisation) | ||
|
||
invite_url = f'{settings.BASE_URL}issue/{get_bounty_invite_url(request.user.username, bounty_id)}' | ||
|
||
if len(profiles): | ||
for profile in profiles: | ||
bounty_invite = BountyInvites.objects.create( | ||
status='pending' | ||
) | ||
bounty_invite.bounty.add(bounty) | ||
bounty_invite.inviter.add(inviter) | ||
bounty_invite.invitee.add(profile.user) | ||
try: | ||
msg = request.POST.get('msg', '') | ||
share_bounty([profile.email], msg, inviter.profile, invite_url, False) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder how many emails we can send within the request-response window before the request times out? I'm guessing this will be somewhat buggy, may have to refactor to use celery soon but for now we'll see since its internal only. |
||
except Exception as e: | ||
logging.exception(e) | ||
else: | ||
return JsonResponse({'success': False}, status=403) | ||
return JsonResponse({'status': 200, | ||
'msg': 'email_sent'}) | ||
|
||
|
||
@csrf_exempt | ||
@require_POST | ||
def social_contribution_email(request): | ||
|
@@ -1153,7 +1242,6 @@ def social_contribution_email(request): | |
Returns: | ||
JsonResponse: Success in sending email. | ||
""" | ||
from marketing.mails import share_bounty | ||
from .utils import get_bounty_invite_url | ||
|
||
emails = [] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh why did we move this from
def users_fetch_filters
? to keep that more generic ?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be DRY instead of copy paste the same filters, both users filters and bulk invite user
users_fetch_filters
to filter and have the same result