<repo_name>Silvian/phishtray<file_sep>/participant/serializer.py
from rest_framework import serializers

from .models import (
    Participant,
    ParticipantAction,
    ParticipantProfileEntry,
    ActionLog)


class ParticipantActionSerializer(serializers.ModelSerializer):
    action_details = serializers.SerializerMethodField()

    class Meta:
        model = ParticipantAction
        fields = ('id', 'action_details',)

    def get_action_details(self, participant_action):
        log_entries_queryset = ActionLog.objects.filter(action=participant_action)
        data = {}

        for entry in log_entries_queryset:
            data[entry.name] = entry.value

        return data


class ParticipantProfileEntrySerializer(serializers.ModelSerializer):
    question = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = ParticipantProfileEntry
        fields = ('id', 'question', 'answer',)

    def get_question(self, profile_entry):
        return profile_entry.question


class ParticipantSerializer(serializers.ModelSerializer):
    actions = serializers.SerializerMethodField(read_only=True)
    profile = ParticipantProfileEntrySerializer(read_only=True, many=True)

    class Meta:
        model = Participant
        fields = ('id', 'exercise', 'profile', 'actions',)

    def get_actions(self, participant):
        participant_actions_queryset = ParticipantAction.objects.filter(participant=participant)
        return ParticipantActionSerializer(participant_actions_queryset, many=True).data
<file_sep>/exercise/serializer.py
from rest_framework import serializers

from .models import *


class DemographicsInfoSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = DemographicsInfo
        fields = ('id', 'question', 'question_type', 'required')


class ExerciseEmailReplySerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = ExerciseEmailReply
        fields = ('id', 'reply_type', 'message')


class ExerciseFileSerializer(serializers.HyperlinkedModelSerializer):
    date_created = serializers.DateTimeField(source='created_date')
    file_url = serializers.CharField(source='img_url')

    class Meta:
        model = ExerciseFile
        fields = ('id', 'file_name', 'description', 'date_created', 'file_url')


class ExerciseEmailSerializer(serializers.HyperlinkedModelSerializer):
    replies = ExerciseEmailReplySerializer(many=True)
    attachments = ExerciseFileSerializer(many=True)

    class Meta:
        model = ExerciseEmail
        fields = (
            'id',
            'subject',
            'from_address',
            'from_name',
            'to_address',
            'to_name',
            'phish_type',
            'content',
            'attachments',
            'replies',
        )


class EmailDetailsSerializer(serializers.HyperlinkedModelSerializer):
    replies = ExerciseEmailReplySerializer(many=True)
    attachments = ExerciseFileSerializer(many=True)
    from_account = serializers.SerializerMethodField()
    to_account = serializers.SerializerMethodField()
    body = serializers.CharField(source='content')

    class Meta:
        model = ExerciseEmail
        fields = (
            'id',
            'subject',
            'phish_type',
            'from_account',
            'to_account',
            'body',
            'attachments',
            'replies',
        )

    def get_from_account(self, email):
        return email.from_account

    def get_to_account(self, email):
        return email.to_account


class ThreadSerializer(serializers.ModelSerializer):
    body = serializers.CharField(source='content')
    from_account = serializers.SerializerMethodField()
    to_account = serializers.SerializerMethodField()
    replies = ExerciseEmailReplySerializer(many=True)
    attachments = ExerciseFileSerializer(many=True)
    emails = serializers.SerializerMethodField()
    reveal_time = serializers.SerializerMethodField()

    class Meta:
        model = ExerciseEmail
        fields = (
            'id',
            'subject',
            'reveal_time',
            'from_account',
            'to_account',
            'body',
            'attachments',
            'replies',
            'emails',
        )

    def get_emails(self, email):
        belonging_emails_queryset = ExerciseEmail.objects.filter(belongs_to=email.id)
        return EmailDetailsSerializer(belonging_emails_queryset, many=True).data

    def get_from_account(self, email):
        return email.from_account

    def get_to_account(self, email):
        return email.to_account

    def get_reveal_time(self, email):
        return email.reveal_time


class ExerciseSerializer(serializers.HyperlinkedModelSerializer):
    threads = ThreadSerializer(source='emails', many=True)
    profile_form = DemographicsInfoSerializer(source='demographics', many=True)
    files = ExerciseFileSerializer(many=True)

    class Meta:
        model = Exercise
        fields = (
            'id',
            'title',
            'description',
            'introduction',
            'afterword',
            'length_minutes',
            'profile_form',
            'threads',
            'files'
        )
<file_sep>/frontend/src/components/WebBrowser/websites/Bluestar/index.js
import React, { Component } from 'react';
import { Switch, Route } from 'react-router-dom';
import styled, { css } from 'react-emotion';
import { TextInput, Button, Checkbox, Icon } from 'carbon-components-react';
import { iconStarOutline } from 'carbon-icons';

import Bg from './assets/bg.png';

const LoginContainer = styled('div')({
  margin: '120px auto 0',
  padding: '42px 42px 36px',
  overflow: 'hidden',
  width: '80%',
  // maxWidth: '1500px',
  flexDirection: 'row',
  flex: 1,
  justifyContent: 'space-around',
  display: 'flex',
});

const LoginContainerImage = styled('div')({
  flex: 1,
});

const LoginContainerForm = styled('div')({
  maxWidth: '440px',
  width: '100%',
});

const FieldWrapper = styled('div')({
  padding: '12px 12px 23px',
  overflow: 'hidden',
  display: 'flex',
});

const LoginBanner = styled('div')({
  flex: 1,
  borderBottom: '1px solid #eaeced',
  display: 'flex',
  position: 'absolute',
  top: '70px',
  width: '100%',
  padding: '15px 0px ',
});

const LoginBannerText = styled('div')({
  textAlign: 'center',
  color: '#4285F4',
  flexGrow: 1,
  fontSize: '36px',
  fontWeight: '800',
  fontFamily: 'Open Sans',
  letterSpacing: '3.6px',
  lineHeight: '40px',
});

const ForgotPassLink = styled('a')({
  color: '#4285F4',
  fontSize: '15px',
  textDecoration: 'none',
});

const LoginHeaderText = styled('h2')({
  color: '#4285F4',
  lineHeight: '40px',
  fontFamily: 'Open Sans',
  fontSize: '36px',
});

const TextInputProps = () => ({
  className: css({ display: 'flex' }),
  disabled: false,
  labelText: '',
});

const ButtonProps = () => ({
  className: css({ display: 'flex', flexGrow: 1 }),
  id: 'button',
  kind: 'primary',
});

export default class MyPayment extends Component {
  render() {
    return (
      <Switch>
        <Route
          path="/congrats"
          render={() => (
            <LoginContainer>
              <h1>Thanks for visiting Bluestar Technologies.</h1>
            </LoginContainer>
          )}
        />
        <Route
          path="/"
          render={({ history }) => (
            <div>
              <LoginBanner>
                <Icon
                  icon={iconStarOutline}
                  name="iconStart"
                  fill="#4285F4"
                  height="40px"
                  width="150px"
                  className={css({
                    position: 'absolute',
                  })}
                />
                <LoginBannerText>BlueStar Technologies</LoginBannerText>
              </LoginBanner>
              <LoginContainer>
                <LoginContainerImage>
                  <img src={Bg} alt="background" />
                </LoginContainerImage>
                <LoginContainerForm>
                  <FieldWrapper>
                    <LoginHeaderText>Login</LoginHeaderText>
                  </FieldWrapper>
                  <FieldWrapper>
                    <TextInput
                      {...TextInputProps()}
                      id="email"
                      placeholder="Email"
                      type="email"
                      onClick={() => {
                        this.props.logBrowserActions({
                          event: `clicked_input_email`,
                        });
                      }}
                    />
                  </FieldWrapper>
                  <FieldWrapper>
                    <TextInput
                      {...TextInputProps()}
                      id="password"
                      placeholder="<PASSWORD>"
                      type="<PASSWORD>"
                      onClick={() => {
                        this.props.logBrowserActions({
                          event: `clicked_input_password`,
                        });
                      }}
                    />
                  </FieldWrapper>
                  <FieldWrapper>
                    <Checkbox
                      labelText="Remember Me"
                      onChange={value =>
                        this.props.logBrowserActions({
                          event: `set_remember_${value}`,
                        })
                      }
                      id="checkbox-label-1"
                    />
                    <Button
                      {...ButtonProps()}
                      onClick={() => {
                        this.props.logBrowserActions({
                          event: `clicked_${ButtonProps().kind}_button_next`,
                        });
                        history.push('/congrats');
                      }}
                    >
                      Login
                    </Button>
                  </FieldWrapper>
                  <FieldWrapper>
                    <ForgotPassLink
                      onClick={() =>
                        this.props.logBrowserActions({
                          event: `clicked_forgotten_password`,
                        })
                      }
                    >
                      Forgotten your password?
                    </ForgotPassLink>
                  </FieldWrapper>
                </LoginContainerForm>
              </LoginContainer>
            </div>
          )}
        />
      </Switch>
    );
  }
}
<file_sep>/exercise/views.py
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.response import Response

from participant.models import Participant
from .models import (
    Exercise,
    ExerciseEmail,
)
from .serializer import (
    ExerciseSerializer,
    ExerciseEmailSerializer,
    ThreadSerializer)


class ExerciseViewSet(viewsets.ModelViewSet):
    queryset = Exercise.objects.all()
    serializer_class = ExerciseSerializer

    def get_permissions(self):
        """
        Instantiates and returns the list of permissions that this view requires.
        """
        if self.action == 'list':
            permission_classes = [IsAuthenticated, IsAdminUser]
        else:
            permission_classes = []
        return [permission() for permission in permission_classes]

    http_method_names = ['get', 'head', 'options']

    @action(methods=['get'], detail=True, permission_classes=[])
    def init(self, request, *args, **kwargs):
        exercise = self.get_object()
        participant = Participant(exercise=exercise)
        participant.save()
        resp = {
            'participant': str(participant.id),
            'exercise': ExerciseSerializer(exercise).data
        }
        return Response(data=resp)


class ExerciseEmailViewSet(viewsets.ModelViewSet):
    queryset = ExerciseEmail.objects.all()
    serializer_class = ExerciseEmailSerializer

    def get_permissions(self):
        """
        Instantiates and returns the list of permissions that this view requires.
        """
        if self.action == 'list':
            permission_classes = [IsAuthenticated, IsAdminUser]
        else:
            permission_classes = []
        return [permission() for permission in permission_classes]

    http_method_names = ['get', 'head', 'options']


class ExerciseEmailThreadViewSet(viewsets.ModelViewSet):
    """
    This view retrieves emails in thread style
    """
    queryset = ExerciseEmail.objects.all()
    serializer_class = ThreadSerializer

    def get_permissions(self):
        """
        Instantiates and returns the list of permissions that this view requires.
        """
        if self.action == 'list':
            permission_classes = [IsAuthenticated, IsAdminUser]
        else:
            permission_classes = []
        return [permission() for permission in permission_classes]

    http_method_names = ['get', 'head', 'options']
<file_sep>/frontend/src/components/WebBrowser/websites/MyPayment/index.js
import React, { Component } from 'react';
import { Switch, Route } from 'react-router-dom';
import styled, { css } from 'react-emotion';
import { TextInput, Button } from 'carbon-components-react';

import Logo from './assets/logo.png';

const LoginContainer = styled('div')({
  margin: '120px auto 0',
  padding: '42px 42px 36px',
  overflow: 'hidden',
  width: '500px',
  border: '1px solid #eaeced',
  textAlign: 'center',
});

const FieldWrapper = styled('div')({
  padding: '12px 12px 23px',
  overflow: 'hidden',
  display: 'flex',
});

const TextInputProps = () => ({
  className: css({ display: 'flex' }),
  id: 'test2',
  type: 'email',
  placeholder: 'Email Address',
  disabled: false,
  labelText: '',
});

const ButtonProps = () => ({
  className: css({ display: 'flex', flexGrow: 1 }),
  id: 'test2',
  kind: 'primary',
});

const ButtonAlternateProps = () => ({
  className: css({ display: 'flex', flexGrow: 1 }),
  id: 'test2',
  kind: 'secondary',
});

export default class MyPayment extends Component {
  render() {
    return (
      <Switch>
        <Route
          path="/congrats"
          render={() => (
            <LoginContainer>
              <h1>
                Thank you for checking. Your recent transactions have been
                verified.
              </h1>
            </LoginContainer>
          )}
        />
        <Route
          path="/"
          render={({ history }) => (
            <LoginContainer>
              <img src={Logo} width={323} height={58} alt="logo" />
              <FieldWrapper>
                <TextInput
                  {...TextInputProps()}
                  onClick={() => {
                    this.props.logBrowserActions({
                      event: `clicked_input_email`,
                    });
                  }}
                />
              </FieldWrapper>
              <FieldWrapper>
                <Button
                  {...ButtonProps()}
                  onClick={() => {
                    this.props.logBrowserActions({
                      event: `clicked_${ButtonProps().kind}_button_next`,
                    });
                    history.push('/congrats');
                  }}
                >
                  Next
                </Button>
              </FieldWrapper>
              <FieldWrapper>Having trouble logging in?</FieldWrapper>
              <FieldWrapper>
                <Button
                  {...ButtonAlternateProps()}
                  onClick={() =>
                    this.props.logBrowserActions({
                      event: `clicked_${ButtonProps().kind}_button_signup`,
                    })
                  }
                >
                  Sign Up
                </Button>
              </FieldWrapper>
            </LoginContainer>
          )}
        />
      </Switch>
    );
  }
}
<file_sep>/exercise/tests/test_models.py
from django.test import TestCase

from ..factories import (
    EmailFactory,
    ExerciseFactory
)

from ..models import ExerciseEmailProperties


class ExerciseModelTests(TestCase):

    def test_set_email_reveal_times_with_no_emails(self):
        exercise = ExerciseFactory()

        self.assertEqual(0, exercise.emails.all().count())
        exercise_reveal_times = ExerciseEmailProperties.objects.filter(exercise=exercise)
        self.assertEqual(0, exercise_reveal_times.count())

    def test_set_email_reveal_times_with_less_than_ten_emails(self):
        emails = EmailFactory.create_batch(4)
        exercise = ExerciseFactory.create(emails=emails)
        exercise_reveal_times = ExerciseEmailProperties.objects.filter(exercise=exercise)

        exercise.set_email_reveal_times()
        received_emails = [e for e in exercise_reveal_times if e.reveal_time is 0]

        self.assertEqual(4, exercise.emails.all().count())
        self.assertEqual(1, len(received_emails))

    def test_set_email_reveal_times_with_more_than_ten_emails(self):
        emails = EmailFactory.create_batch(27)
        exercise = ExerciseFactory.create(emails=emails)
        exercise_reveal_times = ExerciseEmailProperties.objects.filter(exercise=exercise)

        exercise.set_email_reveal_times()
        received_emails = [e for e in exercise_reveal_times if e.reveal_time is 0]

        self.assertEqual(27, exercise.emails.all().count())
        self.assertTrue(2 <= len(received_emails) <= 4)

    def test_sticky_received_emails(self):
        """
        Current logic dictates that received emails shouldn't change after saving the Exercise.
        """
        emails = EmailFactory.create_batch(35)
        exercise = ExerciseFactory.create(emails=emails)
        exercise_reveal_times = ExerciseEmailProperties.objects.filter(exercise=exercise)

        exercise.set_email_reveal_times()
        received_emails = [e for e in exercise_reveal_times if e.reveal_time is 0]
        received_email_ids = [re.id for re in exercise.emails.all()]

        self.assertEqual(35, exercise.emails.all().count())
        self.assertTrue(3 <= len(received_emails) <= 5)

        exercise.title = 'Updated Exercise'
        exercise.save()

        received_email_ids_after_update = [re.id for re in exercise.emails.all()]
        self.assertEqual(set(received_email_ids), set(received_email_ids_after_update))
<file_sep>/frontend/src/pages/Afterward/index.js
import React from 'react';
import { connect } from 'react-redux';
import ReactMarkdown from 'react-markdown';
import styled from 'react-emotion';
import { persistor } from '../../redux';
import { getRange } from '../../utils';

const Container = styled('div')({
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
  height: '100%',
  backgroundColor: '#fff',
  boxShadow: '0 1px 2px 0 rgba(0, 0, 0, 0.1)',
  padding: '1rem',
  flexDirection: 'column',
});

const Title = styled('h1')({
  display: 'block',
  fontSize: ' 2.25rem',
  lineHeight: 1.25,
  marginBottom: '35px',
  fontWeight: 300,
});

type Props = {
  afterwardMessage: String,
};

const clearSessionStorage = async () => await sessionStorage.clear();

class Afterward extends React.Component<Props> {
  state = {
    message: '',
  };

  componentDidMount() {
    this.setState(
      {
        message: this.props.afterwardMessage,
      },
      () => {
        getRange(0, 100).map(i => clearInterval(i)); //bad habits
        clearSessionStorage().then(() => {
          sessionStorage.clear();
          persistor.purge();
        });
      }
    );
  }

  render() {
    return (
      <Container>
        <Title>Thanks for taking the exercise.</Title>
        {this.props.message && <ReactMarkdown source={this.state.message} />}
      </Container>
    );
  }
}

export default connect(
  state => ({
    afterwardMessage: state.exercise.afterword,
  }),
  {}
)(Afterward);
