Skip to content

Commit

Permalink
assignment creation working
Browse files Browse the repository at this point in the history
  • Loading branch information
mattfreire committed Dec 12, 2018
1 parent 58823b3 commit 16d0635
Show file tree
Hide file tree
Showing 11 changed files with 340 additions and 8 deletions.
4 changes: 2 additions & 2 deletions api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ class Question(models.Model):
question = models.CharField(max_length=200)
choices = models.ManyToManyField(Choice)
answer = models.ForeignKey(
Choice, on_delete=models.CASCADE, related_name='answer')
Choice, on_delete=models.CASCADE, related_name='answer', blank=True, null=True)
assignment = models.ForeignKey(
Assignment, on_delete=models.CASCADE, related_name='questions')
Assignment, on_delete=models.CASCADE, related_name='questions', blank=True, null=True)
order = models.SmallIntegerField()

def __str__(self):
Expand Down
32 changes: 31 additions & 1 deletion api/serializers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from rest_framework import serializers

from .models import Assignment, Question
from users.models import User
from .models import Assignment, Question, Choice


class StringSerializer(serializers.StringRelatedField):
Expand All @@ -27,3 +28,32 @@ class Meta:
def get_questions(self, obj):
questions = QuestionSerializer(obj.questions.all(), many=True).data
return questions

def create(self, request):
data = request.data
print(data)

assignment = Assignment()
teacher = User.objects.get(username=data['teacher'])

This comment has been minimized.

Copy link
@preetgur

preetgur Mar 26, 2020

Explain this => data['teacher']

assignment.teacher = teacher
assignment.title = data['title']
assignment.save()

order = 1
for q in data['questions']:
newQ = Question()
newQ.question = q['title']
newQ.order = order

This comment has been minimized.

Copy link
@preetgur

preetgur Mar 26, 2020

Please Explain this point

newQ.save()

for c in q['choices']:
newC = Choice()
newC.title = c
newC.save()
newQ.choices.add(newC)

newQ.answer = Choice.objects.get(title=q['answer'])
newQ.assignment = assignment
newQ.save()
order += 1
return assignment
13 changes: 13 additions & 0 deletions api/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.status import (
HTTP_201_CREATED,
HTTP_400_BAD_REQUEST
)

from .models import Assignment
from .serializers import AssignmentSerializer
Expand All @@ -7,3 +12,11 @@
class AssignmentViewSet(viewsets.ModelViewSet):
serializer_class = AssignmentSerializer
queryset = Assignment.objects.all()

def create(self, request):
serializer = AssignmentSerializer(data=request.data)
if serializer.is_valid():
assignment = serializer.create(request)
if assignment:
return Response(status=HTTP_201_CREATED)
return Response(status=HTTP_400_BAD_REQUEST)
120 changes: 120 additions & 0 deletions src/containers/AssignmentCreate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import React from "react";
import { connect } from "react-redux";
import { Form, Input, Icon, Button, Divider } from "antd";
import QuestionForm from "./QuestionForm";
import Hoc from "../hoc/hoc";
import { createASNT } from "../store/actions/assignments";

const FormItem = Form.Item;

class AssignmentCreate extends React.Component {
state = {
formCount: 1
};

remove = () => {
const { formCount } = this.state;
this.setState({
formCount: formCount - 1
});
};

add = () => {
const { formCount } = this.state;
this.setState({
formCount: formCount + 1
});
};

handleSubmit = e => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
console.log("Received values of form: ", values);
const questions = [];
for (let i = 0; i < values.questions.length; i += 1) {
questions.push({
title: values.question[i],
choices: values.questions[i].choices.filter(el => el !== null),
answer: values.answers[i]
});
}
const asnt = {
teacher: this.props.username,
title: values.title,
questions
};
this.props.createASNT(this.props.token, asnt);
}
});
};

render() {
const { getFieldDecorator } = this.props.form;
const questions = [];
for (let i = 0; i < this.state.formCount; i += 1) {
questions.push(
<Hoc key={i}>
{questions.length > 0 ? (
<Icon
className="dynamic-delete-button"
type="minus-circle-o"
disabled={questions.length === 0}
onClick={() => this.remove()}
/>
) : null}
<QuestionForm id={i} {...this.props} />
<Divider />
</Hoc>
);
}
return (
<Form onSubmit={this.handleSubmit}>
<h1>Create an assignment</h1>
<FormItem label={"Title: "}>
{getFieldDecorator(`title`, {
validateTrigger: ["onChange", "onBlur"],
rules: [
{
required: true,
message: "Please input a title"
}
]
})(<Input placeholder="Add a title" />)}
</FormItem>
{questions}
<FormItem>
<Button type="secondary" onClick={this.add}>
<Icon type="plus" /> Add question
</Button>
</FormItem>
<FormItem>
<Button type="primary" htmlType="submit">
Submit
</Button>
</FormItem>
</Form>
);
}
}

const WrappedAssignmentCreate = Form.create()(AssignmentCreate);

const mapStateToProps = state => {
return {
token: state.auth.token,
username: state.auth.username,
loading: state.assignments.loading
};
};

const mapDispatchToProps = dispatch => {
return {
createASNT: (token, asnt) => dispatch(createASNT(token, asnt))
};
};

export default connect(
mapStateToProps,
mapDispatchToProps
)(WrappedAssignmentCreate);
2 changes: 1 addition & 1 deletion src/containers/AssignmentList.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class AssignmentList extends React.PureComponent {

renderItem(item) {
return (
<Link to="/assignments/1">
<Link to={`/assignments/${item.id}`}>
<List.Item>{item.title}</List.Item>
</Link>
);
Expand Down
17 changes: 13 additions & 4 deletions src/containers/Layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,16 @@ class CustomLayout extends React.Component {
<Breadcrumb.Item>
<Link to="/">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
<Link to={`/profiles/${this.props.userId}`}>Profile</Link>
</Breadcrumb.Item>
{this.props.token !== null ? (
<Breadcrumb.Item>
<Link to={`/profiles/${this.props.userId}`}>Profile</Link>
</Breadcrumb.Item>
) : null}
{this.props.token !== null && this.props.is_teacher ? (
<Breadcrumb.Item>
<Link to="/create">Create</Link>
</Breadcrumb.Item>
) : null}
</Breadcrumb>
<div style={{ background: "#fff", padding: 24, minHeight: 280 }}>
{this.props.children}
Expand All @@ -52,7 +59,9 @@ class CustomLayout extends React.Component {

const mapStateToProps = state => {
return {
userId: state.auth.userId
userId: state.auth.userId,
token: state.auth.token,
is_teacher: state.auth.is_teacher
};
};

Expand Down
89 changes: 89 additions & 0 deletions src/containers/QuestionForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React from "react";
import { Form, Input, Icon, Button } from "antd";
import Hoc from "../hoc/hoc";

const FormItem = Form.Item;

let id = 0;

class QuestionForm extends React.Component {
remove = k => {
const { form } = this.props;
const keys = form.getFieldValue("keys");
if (keys.length === 1) return;
form.setFieldsValue({
keys: keys.filter(key => key !== k)
});
};

add = () => {
const { form } = this.props;
const keys = form.getFieldValue("keys");
const nextKeys = keys.concat(++id);
form.setFieldsValue({
keys: nextKeys
});
};

render() {
const { getFieldDecorator, getFieldValue } = this.props.form;
getFieldDecorator("keys", { initialValue: [] });
const keys = getFieldValue("keys");
const formItems = keys.map((k, index) => (
<FormItem label={index === 0 ? "Choices" : ""} key={k}>
{getFieldDecorator(`questions[${this.props.id}]choices[${k}]`, {
validateTrigger: ["onChange", "onBlur"],
rules: [
{
required: true,
whitespace: true,
message: "Please input a choice to the question"
}
]
})(<Input placeholder="Answer choice" />)}
{keys.length > 1 ? (
<Icon
className="dynamic-delete-button"
type="minus-circle-o"
disabled={keys.length === 1}
onClick={() => this.remove(k)}
/>
) : null}
</FormItem>
));
return (
<Hoc>
<FormItem label="Question: ">
{getFieldDecorator(`question[${this.props.id}]`, {
validateTrigger: ["onChange", "onBlur"],
rules: [
{
required: true,
message: "Please input a question"
}
]
})(<Input placeholder="Add a question" />)}
</FormItem>
<FormItem label="Answer: ">
{getFieldDecorator(`answers[${this.props.id}]`, {
validateTrigger: ["onChange", "onBlur"],
rules: [
{
required: true,
message: "Please input an answer to this question"
}
]
})(<Input placeholder="What is the answer?" />)}
</FormItem>
{formItems}
<FormItem>
<Button type="dashed" onClick={this.add} style={{ width: "60%" }}>
<Icon type="plus" /> Add an answer choice
</Button>
</FormItem>
</Hoc>
);
}
}

export default QuestionForm;
2 changes: 2 additions & 0 deletions src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import Signup from "./containers/Signup";
import Profile from "./containers/Profile";
import AssignmentList from "./containers/AssignmentList";
import AssignmentDetail from "./containers/AssignmentDetail";
import AssignmentCreate from "./containers/AssignmentCreate";

const BaseRouter = () => (
<Hoc>
<Route exact path="/" component={AssignmentList} />
<Route exact path="/assignments/:id" component={AssignmentDetail} />
<Route exact path="/create/" component={AssignmentCreate} />
<Route exact path="/login/" component={Login} />
<Route exact path="/signup/" component={Signup} />
<Route exact path="/profile/:id" component={Profile} />
Expand Down
4 changes: 4 additions & 0 deletions src/store/actions/actionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ export const GET_ASSIGNMENTS_LIST_SUCCESS = "GET_ASSIGNMENTS_LIST_SUCCESS";
export const GET_ASSIGNMENT_DETAIL_START = "GET_ASSIGNMENT_DETAIL_START";
export const GET_ASSIGNMENT_DETAIL_FAIL = "GET_ASSIGNMENT_DETAIL_FAIL";
export const GET_ASSIGNMENT_DETAIL_SUCCESS = "GET_ASSIGNMENT_DETAIL_SUCCESS";

export const CREATE_ASSIGNMENT_START = "CREATE_ASSIGNMENT_START";
export const CREATE_ASSIGNMENT_FAIL = "CREATE_ASSIGNMENT_FAIL";
export const CREATE_ASSIGNMENT_SUCCESS = "CREATE_ASSIGNMENT_SUCCESS";
38 changes: 38 additions & 0 deletions src/store/actions/assignments.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,41 @@ export const getASNTSDetail = (token, id) => {
});
};
};

const createASNTStart = () => {
return {
type: actionTypes.CREATE_ASSIGNMENT_START
};
};

const createASNTSuccess = assignment => {
return {
type: actionTypes.CREATE_ASSIGNMENT_SUCCESS,
assignment
};
};

const createASNTFail = error => {
return {
type: actionTypes.CREATE_ASSIGNMENT_FAIL,
error: error
};
};

export const createASNT = (token, asnt) => {
return dispatch => {
dispatch(createASNTStart());
axios.defaults.headers = {
"Content-Type": "application/json",
Authorization: `Token ${token}`
};
axios
.post(`http://127.0.0.1:8000/assignments/`, asnt)
.then(res => {
dispatch(createASNTSuccess());
})
.catch(err => {
dispatch(createASNTFail());
});
};
};
Loading

0 comments on commit 16d0635

Please sign in to comment.