Exemple #1
0
def get_invitations_count(request):
    """
    API endpoint that returns the potential participant count based on the selected experiment metadata and Institution
    """
    form = SessionInviteForm(request.POST or None)
    if form.is_valid():
        session_pk_list = request.POST.get('session_pk_list').split(",")
        experiment_metadata_ids = ExperimentSession.objects.filter(
            pk__in=session_pk_list).values_list('experiment_metadata__pk',
                                                flat=True)
        if len(set(experiment_metadata_ids)) == 1:
            # only allow invitations for sessions of a single ExperimentMetadata type
            # get the experiment metadata pk of any session (This is ensured as it is a constraint)
            potential_participants = Participant.objects.invitation_eligible(
                experiment_metadata_ids[0],
                gender=form.cleaned_data.get('gender'),
                institution=form.cleaned_data.get('affiliated_institution'),
                port_of_mars=form.cleaned_data.get('port_of_mars'),
                only_undergrad=form.cleaned_data.get('only_undergrad'))
            return JsonResponse({
                'success': True,
                'invitesCount': len(potential_participants)
            })
    return JsonResponse({
        'success': False,
        'invitesCount': 0,
        'errors': form.errors
    })
Exemple #2
0
def submit_decision(request, experiment_id=None):
    form = SingleIntegerDecisionForm(request.POST or None)
    experiment = get_object_or_404(Experiment, pk=experiment_id)
    if form.is_valid():
        participant_group_id = form.cleaned_data['participant_group_id']
        pgr = get_object_or_404(ParticipantGroupRelationship,
                                pk=participant_group_id)
        harvest_decision = form.cleaned_data['integer_decision']
        submitted = form.cleaned_data['submitted']
        logger.debug("pgr %s harvested %s - final submission? %s", pgr,
                     harvest_decision, submitted)
        with transaction.atomic():
            round_data = experiment.current_round_data
            set_harvest_decision(pgr,
                                 harvest_decision,
                                 round_data,
                                 submitted=submitted)
            message = "%s harvested %s trees"
            experiment.log(message % (pgr.participant, harvest_decision))
            response_dict = {
                'success': True,
                'message':
                message % (pgr.participant_handle, harvest_decision),
            }
            return JsonResponse(response_dict)
    else:
        logger.debug("invalid form: %s", form)
    for field in form:
        if field.errors:
            logger.debug("field %s errors %s", field, field.errors)
    return JsonResponse({'success': False})
Exemple #3
0
def like(request):
    form = LikeForm(request.POST or None)
    if form.is_valid():
        participant_group_id = form.cleaned_data['participant_group_id']
        target_id = form.cleaned_data['target_id']
        participant_group_relationship = get_object_or_404(
            ParticipantGroupRelationship.objects.select_related(
                'participant__user', 'group__experiment'),
            pk=participant_group_id,
            participant=request.user.participant)
        target = get_object_or_404(ParticipantRoundDataValue, pk=target_id)
        # FIXME: either needs a uniqueness constraint to ensure that duplicates don't get created or add guards when we
        # retrieve them to only send back the latest one (feels hacky).  See
        # https://bitbucket.org/virtualcommons/vcweb/issue/59/get_or_create-issues-for-likes
        round_data = participant_group_relationship.current_round_data
        Like.objects.create(
            round_data=round_data,
            participant_group_relationship=participant_group_relationship,
            target_data_value=target)
        logger.debug("Participant %s liked %s", participant_group_relationship,
                     target)
        return JsonResponse({'success': True})
    else:
        logger.debug("invalid form: %s from request: %s", form, request)
        return JsonResponse({'success': False, 'message': 'Invalid like post'})
Exemple #4
0
def post_comment(request):
    form = CommentForm(request.POST or None)
    if form.is_valid():
        participant_group_id = form.cleaned_data['participant_group_id']
        target_id = form.cleaned_data['target_id']
        message = form.cleaned_data['message']
        participant_group_relationship = get_object_or_404(
            ParticipantGroupRelationship.objects.select_related(
                'participant__user', 'group__experiment'),
            pk=participant_group_id,
            participant=request.user.participant,
        )
        target = get_object_or_404(ParticipantRoundDataValue, pk=target_id)
        Comment.objects.create(
            string_value=message,
            round_data=participant_group_relationship.current_round_data,
            participant_group_relationship=participant_group_relationship,
            target_data_value=target)
        logger.debug("Participant %s commented '%s' on %s",
                     participant_group_relationship.participant, message,
                     target)
        return JsonResponse({
            'success':
            True,
            'viewModel':
            LighterprintsViewModel.create(
                participant_group_relationship).to_dict()
        })
    else:
        logger.debug("invalid form: %s from request: %s", form.errors, request)
        return JsonResponse({
            'success': False,
            'message': 'Invalid post comment'
        })
Exemple #5
0
def post_chat_message(request):
    form = ChatForm(request.POST or None)
    if form.is_valid():
        participant_group_id = form.cleaned_data['participant_group_id']
        message = form.cleaned_data['message']
        pgr = get_object_or_404(
            ParticipantGroupRelationship.objects.select_related(
                'participant__user'),
            pk=participant_group_id,
            participant=request.user.participant,
        )
        chat_message = ChatMessage.objects.create(
            string_value=message, participant_group_relationship=pgr)
        logger.debug("%s: %s", pgr.participant, chat_message)
        # FIXME: optimize, only retrieving the latest group activity since the last checkin time
        group_activity = GroupActivity(
            pgr, limit=LighterprintsViewModel.activity_limit)
        return JsonResponse({
            'success': True,
            'viewModel': {
                'groupActivity': group_activity.all_activities
            }
        })
    return JsonResponse({
        'success': False,
        'message': "Invalid chat message post"
    })
Exemple #6
0
def perform_activity(request):
    form = ActivityForm(request.POST or None)
    if form.is_valid():
        activity_id = form.cleaned_data['activity_id']
        participant_group_id = form.cleaned_data['participant_group_id']
        logger.debug("%s request to perform activity %s", participant_group_id,
                     activity_id)
        participant_group_relationship = get_object_or_404(
            ParticipantGroupRelationship.objects.select_related(
                'participant__user', 'group__experiment'),
            pk=participant_group_id)
        if participant_group_relationship.participant == request.user.participant:
            activity = get_object_or_404(Activity, pk=activity_id)
            performed_activity = do_activity(
                activity=activity,
                participant_group_relationship=participant_group_relationship)
            experiment = participant_group_relationship.experiment
            if performed_activity is not None:
                participant_group_relationship.set_first_visit()
                return JsonResponse({
                    'success':
                    True,
                    'viewModel':
                    LighterprintsViewModel.create(
                        participant_group_relationship, experiment).to_dict()
                })
            else:
                message = "Activity was not available at this time"
        else:
            message = "Unauthorized access logged for %s" % participant_group_relationship
            logger.warning(
                "authenticated user %s tried to perform activity %s as %s",
                request.user, activity_id, participant_group_relationship)
    return JsonResponse({'success': False, 'response': message})
Exemple #7
0
def add_participant(request, pk=None):
    participant_email = request.POST.get('participantEmail')
    participant = get_object_or_404(Participant.objects.select_related('user'), user__email=participant_email)
    es = get_object_or_404(ExperimentSession, pk=pk)
    # First check that the experiment session has already been completed
    if es.scheduled_end_date > datetime.now():
        logger.debug("%s tried to add participant %s to pending experiment session %s - ignoring",
                     request.user, participant, pk)
        return JsonResponse({'success': False, 'error': "You can't manually add a participant to a pending experiment session."})

    # there should only be a single invitation for a given experiment session and a given participant
    try:
        invitation = es.invitation_set.get(participant=participant)
    except Invitation.DoesNotExist:
        logger.debug("%s tried to add participant %s without an invitation to experiment session %s. Creating a new one.",
                     request.user, participant, pk)
        # create an invitation for that user
        invitation = Invitation.objects.create(participant=participant, experiment_session=es, sender=request.user)
    except Invitation.MultipleObjectsReturned:
        logger.error("Experiment session %s had multiple invitations created for participant %s", es, participant)
        invitation = es.invitation_set.first()

    # third and final check, the participant must not have already signed up for the experiment session
    if invitation.signup_set.exists():
        logger.debug("%s tried to add participant %s to experiment session %s but they are already signed up",
                     request.user, participant, pk)
        return JsonResponse({'success': False, 'error': 'Participant is already signed up for this experiment session'})
    else:
        ParticipantSignup.objects.create(invitation=invitation, attendance=ParticipantSignup.ATTENDANCE.participated)
        return JsonResponse({'success': True})
Exemple #8
0
def manage_experiment_session(request, pk):
    form = ExperimentSessionForm(request.POST or None,
                                 pk=pk,
                                 user=request.user)
    if form.is_valid():
        es = form.save()
        return JsonResponse({'success': True, 'session': es})
    return JsonResponse({'success': False, 'errors': form.errors})
Exemple #9
0
def invite_email_preview(request):
    """
    Generates preview email for the provided invitation details
    """
    form = SessionInviteForm(request.POST or None)
    message = "Please fill in all the form fields to preview the invitation email."
    if form.is_valid():
        session_ids = request.POST.get('session_pk_list').split(",")
        invitation_text = form.cleaned_data.get('invitation_text')
        plaintext_content = InvitationEmail(request).get_plaintext_content(invitation_text, session_ids)
        html_content = markdown.markdown(plaintext_content)
        return JsonResponse({'success': True, 'content': html_content})
    return JsonResponse({'success': False, 'message': message})
Exemple #10
0
def get_session_events(request):
    """
    Returns the list of Experiment sessions that fall within the given range,
    Used by the subject pool calendar to display experiment sessions falling within the date range shown
    """
    from_date = request.GET.get('from', None)
    to_date = request.GET.get('to', None)
    criteria = dict()
    if to_date:
        criteria.update(
            scheduled_end_date__gte=timestamp_to_datetime(from_date))

    queryset = ExperimentSession.objects.select_related(
        'experiment_metadata').filter(**criteria)
    objects_body = []
    for event in queryset:
        index = event.pk % 20  # for color selection
        field = {
            "id": event.pk,
            "title": event.experiment_metadata.title,
            "url": "session/detail/event/" + str(event.pk),
            "class": "event-color-" + str(index),
            "start": datetime_to_timestamp(event.scheduled_date),
            "end": datetime_to_timestamp(event.scheduled_end_date),
            "capacity": event.capacity
        }
        objects_body.append(field)

    objects_head = {"success": True, "result": objects_body}

    return JsonResponse(objects_head)
Exemple #11
0
def get_view_model(request, experiment_id=None):
    experiment = get_object_or_404(Experiment.objects.select_related(
        'experiment_metadata', 'experiment_configuration'),
                                   pk=experiment_id)
    pgr = experiment.get_participant_group_relationship(
        request.user.participant)
    return JsonResponse(ViewModel(pgr, experiment=experiment).to_dict())
Exemple #12
0
def get_view_model(request, participant_group_id=None):
    if participant_group_id is None:
        # check in the request query parameters as well
        participant_group_id = request.GET.get('participant_group_id')
    pgr = get_object_or_404(ParticipantGroupRelationship.objects.select_related('participant__user',
                                                                                'group__experiment'),
                            pk=participant_group_id)
    if pgr.participant != request.user.participant:
        # check that authenticated participant is the same as the participant whose data is being requested
        logger.warning("user %s tried to access view model for %s", request.user.participant, pgr)
        raise PermissionDenied("You don't appear to have permission to access this experiment.")
    return JsonResponse({'success': True, 'viewModel': LighterprintsViewModel.create(pgr).to_dict()})
Exemple #13
0
def send_invitations(request):
    """
    Sends out invitation emails to random participants which match the required invitation criteria,
    using the provided email subject and message. Also returns the total number of invites sent.
    """
    user = request.user
    form = SessionInviteForm(request.POST or None)

    if form.is_valid():
        invitation_subject = form.cleaned_data.get('invitation_subject')
        invitation_text = form.cleaned_data.get('invitation_text')
        # use currently logged in experimenter as the sender of the
        # invitations.
        from_email = user.email

        session_pk_list = request.POST.get('session_pk_list').split(",")
        invitation_count = form.cleaned_data.get('number_of_people')
        affiliated_institution = form.cleaned_data.get(
            'affiliated_institution')

        experiment_sessions = ExperimentSession.objects.filter(
            pk__in=session_pk_list)
        experiment_metadata_pk_list = experiment_sessions.values_list(
            'experiment_metadata__pk', flat=True)

        if len(set(experiment_metadata_pk_list)) == 1:
            # get the experiment metadata pk of any session, as all sessions selected by experimenter to send
            # invitations belong to same experiment metadata (This has to be ensured as it is a constraint)
            experiment_metadata_pk = experiment_metadata_pk_list[0]

            potential_participants = list(
                Participant.objects.invitation_eligible(
                    experiment_metadata_pk,
                    institution=affiliated_institution,
                    only_undergrad=form.cleaned_data.get('only_undergrad'),
                    gender=form.cleaned_data.get('gender')))
            potential_participants_count = len(potential_participants)

            final_participants = None

            if potential_participants_count == 0:
                final_participants = []
                message = "There are no more eligible participants that can be invited for this experiment."
            else:
                if potential_participants_count < invitation_count:
                    # less candidate participants than the number of requested
                    # participants, use them all
                    final_participants = potential_participants
                else:
                    final_participants = random.sample(potential_participants,
                                                       invitation_count)
                message = "Invitations were sent to %s / %s participants." % (
                    len(final_participants), invitation_count)

                today = timezone.now()
                invitations = []
                recipient_list = [settings.DEFAULT_FROM_EMAIL]
                for participant in final_participants:
                    recipient_list.append(participant.email)
                    invitations.extend([
                        Invitation(participant=participant,
                                   experiment_session=es,
                                   date_created=today,
                                   sender=user) for es in experiment_sessions
                    ])
                Invitation.objects.bulk_create(invitations)

                if settings.ENVIRONMENT.is_production:
                    ie = InvitationEmail(request)
                    plaintext_content = ie.get_plaintext_content(
                        invitation_text, session_pk_list)
                    html_content = markdown.markdown(plaintext_content)
                    msg = EmailMultiAlternatives(subject=invitation_subject,
                                                 body=plaintext_content,
                                                 from_email=from_email,
                                                 to=[from_email],
                                                 bcc=recipient_list)
                    msg.attach_alternative(html_content, "text/html")
                    msg.send()
                else:
                    logger.debug(
                        "Sending invitation emails in non-production environment is disabled: %s",
                        recipient_list)
            return JsonResponse({
                'success': True,
                'message': message,
                'invitesCount': len(final_participants)
            })
        else:
            return JsonResponse({
                'success':
                False,
                'message':
                "Please select experiment sessions from the same experiment to send invitations."
            })
    # Form is not valid
    return JsonResponse({'success': False, 'errors': form.errors})