def uninterested(request, bounty_id, profile_id): """Remove party from given bounty Can only be called by the bounty funder :request method: GET Args: bounty_id (int): ID of the Bounty profile_id (int): ID of the interested profile Returns: dict: The success key with a boolean value and accompanying error. """ try: bounty = Bounty.objects.get(pk=bounty_id) except Bounty.DoesNotExist: return JsonResponse({'errors': ['Bounty doesn\'t exist!']}, status=401) is_funder = bounty.is_funder(request.user.username.lower()) is_staff = request.user.is_staff if not is_funder and not is_staff: return JsonResponse( {'error': 'Only bounty funders are allowed to remove users!'}, status=401) try: interest = Interest.objects.get(profile_id=profile_id, bounty=bounty) bounty.interested.remove(interest) maybe_market_to_slack(bounty, 'stop_work') maybe_market_to_user_slack(bounty, 'stop_work') event_name = "bounty_removed_by_staff" if is_staff else "bounty_removed_by_funder" record_user_action_on_interest(interest, event_name, None) interest.delete() except Interest.DoesNotExist: return JsonResponse( { 'errors': ['Party haven\'t expressed interest on this bounty.'], 'success': False }, status=401) except Interest.MultipleObjectsReturned: interest_ids = bounty.interested \ .filter( profile_id=profile_id, bounty=bounty ).values_list('id', flat=True) \ .order_by('-created') bounty.interested.remove(*interest_ids) Interest.objects.filter(pk__in=list(interest_ids)).delete() profile = Profile.objects.get(id=profile_id) if profile.user and profile.user.email: bounty_uninterested(profile.user.email, bounty, interest) else: print("no email sent -- user was not found") return JsonResponse({'success': True})
def handle(self, *args, **options): if settings.DEBUG: print('not running start work expiration because DEBUG is on') return # TODO: DRY with dashboard/notifications.py num_days_back_to_warn = 3 num_days_back_to_delete_interest = 6 num_days_back_to_ignore_bc_mods_got_it = 9 days = [i * 3 for i in range(1, 15)] days.reverse() if settings.DEBUG: days = range(1, 1000) for day in days: start_date = (timezone.now() - timezone.timedelta(days=(day + 1))) end_date = (timezone.now() - timezone.timedelta(days=day)) interests = Interest.objects.select_related("profile").filter( Q(created__gte=start_date, created__lt=end_date) | Q(acceptance_date__gte=start_date, acceptance_date__lt=end_date), pending=False).distinct('pk') print(f'day {day} got {interests.count()} interests') for interest in interests: if interest.acceptance_date: interest_day_0 = interest.acceptance_date bounties = Bounty.objects.current().filter( interested=interest, project_type='traditional', network='mainnet', idx_status__in=['open', 'started'], permission_type='approval', ) else: interest_day_0 = interest.created bounties = Bounty.objects.current().filter( interested=interest, project_type='traditional', network='mainnet', idx_status__in=['open', 'started'], permission_type='permissionless', ) for bounty in bounties: print("===========================================") print( f"{interest} is interested in {bounty.pk} / {bounty.github_url}" ) try: actions = get_interested_actions( bounty.github_url, interest.profile.handle, interest.profile.email) should_warn_user = False should_delete_interest = False should_ignore = False last_heard_from_user_days = None if not actions: should_warn_user = True should_delete_interest = False last_heard_from_user_days = (timezone.now() - interest_day_0).days print(" - no actions") else: # example format: 2018-01-26T17:56:31Z' action_times = [ datetime.strptime(action['created_at'], '%Y-%m-%dT%H:%M:%SZ') for action in actions if action.get('created_at') ] last_action_by_user = max(action_times).replace( tzinfo=pytz.UTC) # if user hasn't commented since they expressed interest, handled this condition # per https://github.com/gitcoinco/web/issues/462#issuecomment-368384384 if last_action_by_user.replace() < interest_day_0: last_action_by_user = interest_day_0 # some small calcs snooze_time = timezone.timedelta( days=bounty.snooze_warnings_for_days) delta_now_vs_last_action = timezone.now( ) - snooze_time - last_action_by_user last_heard_from_user_days = delta_now_vs_last_action.days # decide action params should_warn_user = last_heard_from_user_days >= num_days_back_to_warn should_delete_interest = last_heard_from_user_days >= num_days_back_to_delete_interest should_ignore = last_heard_from_user_days >= num_days_back_to_ignore_bc_mods_got_it print( f"- its been {last_heard_from_user_days} days since we heard from the user" ) if should_ignore: print( f'executing should_ignore for {interest.profile} / {bounty.github_url} ' ) elif should_delete_interest: print( f'executing should_delete_interest for {interest.profile} / {bounty.github_url} ' ) interest = interest.mark_for_review() record_user_action_on_interest( interest, 'bounty_abandonment_escalation_to_mods', last_heard_from_user_days) # commenting on the GH issue maybe_notify_user_escalated_github( bounty, interest.profile.handle, last_heard_from_user_days) # commenting in slack maybe_notify_bounty_user_escalated_to_slack( bounty, interest.profile.handle, last_heard_from_user_days) # send email bounty_startwork_expired( interest.profile.email, bounty, interest, last_heard_from_user_days) elif should_warn_user: record_user_action_on_interest( interest, 'bounty_abandonment_warning', last_heard_from_user_days) print( f'executing should_warn_user for {interest.profile} / {bounty.github_url} ' ) # commenting on the GH issue maybe_warn_user_removed_github( bounty, interest.profile.handle, last_heard_from_user_days) # commenting in slack maybe_notify_bounty_user_warned_removed_to_slack( bounty, interest.profile.handle, last_heard_from_user_days) # send email bounty_startwork_expire_warning( interest.profile.email, bounty, interest, last_heard_from_user_days) except Exception as e: print( f'Exception in expiration_start_work.handle(): {e}' )