Skip to content

Commit

Permalink
Shuffling questions in assessments as well, added version to certific…
Browse files Browse the repository at this point in the history
…ation for checking assessments are still on latest update of certification, first usable addition of posts
  • Loading branch information
DigitalFlow committed Dec 10, 2017
1 parent 7bc10df commit 304e727
Show file tree
Hide file tree
Showing 15 changed files with 254 additions and 127 deletions.
5 changes: 2 additions & 3 deletions dataBaseSchema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,7 @@ ALTER TABLE certification OWNER TO postgres;
CREATE TABLE post (
id uuid DEFAULT public.gen_random_uuid() NOT NULL,
content text,
created_on timestamp without time zone DEFAULT timezone('utc'::text, now()),
title character varying(255)
created_on timestamp without time zone DEFAULT timezone('utc'::text, now())
);


Expand Down Expand Up @@ -130,6 +129,7 @@ CREATE TABLE "user" (

ALTER TABLE "user" OWNER TO postgres;


--
-- Data for Name: user; Type: TABLE DATA; Schema: open_certification_trainer; Owner: postgres
--
Expand Down Expand Up @@ -284,4 +284,3 @@ GRANT ALL ON TABLE "user" TO dev;

--
-- PostgreSQL database dump complete
--
11 changes: 9 additions & 2 deletions src/components/Assessment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ export default class Assessment extends React.Component<IBaseProps, AssessmentSt
shuffle<T>(array: Array<T>): void {
let j, x, i;

if (!array) {
return;
}

for (i = array.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
x = array[i];
Expand All @@ -153,7 +157,9 @@ export default class Assessment extends React.Component<IBaseProps, AssessmentSt
}
}

shuffleAnswers(certification: Certification) {
shuffleCertification(certification: Certification) {
this.shuffle<Question>(certification.questions);

for (let i = 0; certification.questions && i < certification.questions.length; i++) {
let question = certification.questions[i];

Expand All @@ -175,7 +181,7 @@ export default class Assessment extends React.Component<IBaseProps, AssessmentSt
return results.json();
})
.then(data => {
this.shuffleAnswers(data);
this.shuffleCertification(data);

this.setState({certification: data as Certification, session: this.getDefaultState().session, activeQuestion: 0});
});
Expand Down Expand Up @@ -333,6 +339,7 @@ export default class Assessment extends React.Component<IBaseProps, AssessmentSt

content = (
<div>
<p style={{"text-align": "right"}}>Version {this.state.certification.version}</p>
<h1>{this.state.certification.name}</h1>
<ProgressBar striped now={progress} />
<QuestionView checkedAnswers={this.state.checkedAnswers} onAnswerChange={this.answerChangedHandler} question={activeQuestion} key={activeQuestion.id} highlightCorrectAnswers={this.state.checkingAnswers} highlightIncorrectAnswers={this.state.checkingAnswers} answersDisabled={this.state.checkingAnswers} />
Expand Down
16 changes: 16 additions & 0 deletions src/components/CertificationManagement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export default class CertificationManagement extends React.Component<IBaseProps,
this.reset = this.reset.bind(this);
this.save = this.save.bind(this);
this.onNameChange = this.onNameChange.bind(this);
this.onVersionChange = this.onVersionChange.bind(this);
this.onQuestionChange = this.onQuestionChange.bind(this);
this.addQuestion = this.addQuestion.bind(this);
this.addMultipleQuestions = this.addMultipleQuestions.bind(this);
Expand Down Expand Up @@ -258,6 +259,16 @@ export default class CertificationManagement extends React.Component<IBaseProps,
});
}

onVersionChange(e: any){
let cert = this.state.certification;
let newVersion = e.target.value;
let update = {...cert, version: newVersion};

this.setState({
certification: update
});
}

onPublishedChange(e: any){
let cert = this.state.certification;
let isPublished = e.target.checked;
Expand Down Expand Up @@ -362,6 +373,11 @@ export default class CertificationManagement extends React.Component<IBaseProps,
control={{type: "text", disabled: this.props.match.params.courseName !== "new", value: this.state.certification.uniqueName, onChange: this.onUniqueNameChange}}
label="Certification Unique Name"
/>
<FieldGroup
id={this.state.certification.id + "version"}
control={{type: "text", value: this.state.certification.version, onChange: this.onVersionChange}}
label="Version"
/>
<Checkbox key={this.state.certification.id + "_isPublished"} checked={this.state.certification.isPublished} onChange={this.onPublishedChange}>Is Published</Checkbox>
{this.state.certification.questions ? (this.state.certification.questions.map((q, index) =>
(<QuestionEditView onQuestionChange={(q: Question) => this.onQuestionChange(index, q)} requestDeletion={() => this.deleteQuestion(index)} question={q} key={q.id} />)
Expand Down
1 change: 1 addition & 0 deletions src/components/CertificationOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export default class CertificationOverview extends React.Component<IBaseProps, C
if (this.state.certification){
content = (
<div>
<p style={{"text-align": "right"}}>Version {this.state.certification.version}</p>
<h1>{this.state.certification.name}</h1>
{this.state.certification.questions ? (this.state.certification.questions.map(q =>
(<QuestionView question={q} key={q.id} highlightCorrectAnswers={true} highlightIncorrectAnswers={false} answersDisabled={true} />)
Expand Down
70 changes: 2 additions & 68 deletions src/components/PortalManagement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,49 +21,6 @@ export default class PortalManagement extends React.Component<IBaseProps, Portal
posts: [],
postInput: "Demo"
};

this.setInput = this.setInput.bind(this);
this.fetchUsers = this.fetchUsers.bind(this);
this.fetchPosts = this.fetchPosts.bind(this);
}

fetchUsers(){
return fetch("/userList",
{
credentials: 'include'
})
.then(results => {
return results.json();
})
.then((users: Array<DbUser>) => {
this.setState({
users: users
});
});
}

fetchPosts(){
return fetch("/posts",
{
credentials: 'include'
})
.then(results => {
return results.json();
})
.then((posts: Array<DbPost>) => {
this.setState({
posts: posts
});
});
}

componentDidMount(){
this.fetchUsers();
this.fetchPosts();
}

setInput(e: any) {
this.setState({postInput: e.target.value});
}

render(){
Expand All @@ -87,36 +44,13 @@ export default class PortalManagement extends React.Component<IBaseProps, Portal
<Well>
<Tab.Content animation>
<Tab.Pane eventKey="users">
<Table striped bordered condensed hover>
<thead>
<tr>
<th>Username</th>
<th>First Name</th>
<th>Last Name</th>
<th>E-Mail</th>
<th>Is Admin</th>
</tr>
</thead>
<tbody>
{this.state.users.map(u => { return (<UserListView key={u.id} user={u} />)})}
</tbody>
</Table>
<UserListView />
</Tab.Pane>
<Tab.Pane eventKey="groups">
<Jumbotron><h2>Groups - In construction</h2></Jumbotron>
</Tab.Pane>
<Tab.Pane eventKey="posts">
<Table striped bordered condensed hover>
<thead>
<tr>
<th>Title</th>
<th>Created On</th>
</tr>
</thead>
<tbody>
{this.state.posts.map(p => { return (<PostListView key={p.id} post={p} />)})}
</tbody>
</Table>
<PostListView />
</Tab.Pane>
</Tab.Content>
</Well>
Expand Down
49 changes: 40 additions & 9 deletions src/components/PostEditView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import DbPost from "../model/DbPost";
import IBaseProps from "../domain/IBaseProps";
import * as ReactMarkdown from "react-markdown";
import MessageBar from "./MessageBar";
import * as CodeMirror from "react-codemirror";
import * as uuid from "uuid/v4";

interface PostEditViewState {
post: DbPost;
Expand All @@ -24,6 +26,7 @@ export default class PostEditView extends React.PureComponent<IBaseProps, PostEd
this.retrievePost = this.retrievePost.bind(this);
this.markdownChanged = this.markdownChanged.bind(this);
this.save = this.save.bind(this);
this.delete = this.delete.bind(this);
}

componentDidMount(){
Expand All @@ -33,6 +36,12 @@ export default class PostEditView extends React.PureComponent<IBaseProps, PostEd
retrievePost(){
let postId = this.props.match.params.postId;

if (postId === "new") {
return this.setState({
post: {...this.state.post, id: uuid()}
});
}

fetch(`/posts/${postId}`,
{
credentials: 'include'
Expand All @@ -53,19 +62,40 @@ export default class PostEditView extends React.PureComponent<IBaseProps, PostEd
});
}

delete(){
let postId = this.state.post.id;

fetch(`/posts/${postId}`,
{
method: "DELETE",
credentials: 'include'
})
.then(() => {
this.setState({
errors: [],
message: "Successfully deleted post"
});
})
.catch(err => {
this.setState({
errors: [err]
});
});
}

save(){
let postId = this.props.match.params.postId;
let postId = this.state.post.id;
let headers = new Headers();
headers.set("Content-Type", "application/json");

fetch(`/posts/${postId}`,
{
method: "POST",
credentials: 'include',
body: JSON.stringify(this.state.post)
body: JSON.stringify(this.state.post),
headers: headers
})
.then(results => {
return results.json();
})
.then((posts: Array<DbPost>) => {
.then(() => {
this.setState({
errors: [],
message: "Successfully saved post"
Expand All @@ -90,16 +120,17 @@ export default class PostEditView extends React.PureComponent<IBaseProps, PostEd
<ButtonGroup>
<Button bsStyle="default" onClick={this.save}>Save</Button>
</ButtonGroup>
<ButtonGroup>
<Button bsStyle="danger" onClick={this.delete}>Delete</Button>
</ButtonGroup>
</ButtonToolbar>
<Well>
<textarea className="col-xs-6" value={this.state.post.content} onChange={this.markdownChanged} />
<textarea className="col-xs-6" style={{"height": "100vh"}} value={this.state.post.content} onChange={this.markdownChanged} />
<ReactMarkdown
className="result col-xs-6"
source={this.state.post.content}
skipHtml={true}
escapeHtml={true}
/>
</Well>
</div>
);
}
Expand Down
91 changes: 80 additions & 11 deletions src/components/PostListView.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,87 @@
import * as React from "react";
import DbPost from "../model/DbPost";
import { Tab, Row, Col, NavItem, Nav, Table, Jumbotron, Well, ButtonToolbar, ButtonGroup, Button } from "react-bootstrap";
import { LinkContainer } from "react-router-bootstrap";
import IBaseProps from "../domain/IBaseProps";

export interface PostListViewProps{
post: DbPost
export interface PostListViewState {
posts: Array<DbPost>;
}

const PostListView = (props: PostListViewProps) => (
<LinkContainer key={`${props.post.title}_link`} to={`/post/${props.post.id}`}>
<tr>
<td>{props.post.title}</td>
<td>{props.post.created_on}</td>
</tr>
</LinkContainer>
);
export default class PostListView extends React.PureComponent<IBaseProps, PostListViewState> {
constructor (props: IBaseProps) {
super(props);

export default PostListView;
this.state = {
posts: []
};

this.fetchPosts = this.fetchPosts.bind(this);
}

fetchPosts () {
return fetch("/posts",
{
credentials: 'include'
})
.then(results => {
return results.json();
})
.then((posts: Array<DbPost>) => {
this.setState({
posts: posts
});
});
}

componentDidMount() {
this.fetchPosts();
}

render () {
return (
<div>
<ButtonToolbar>
<ButtonGroup>
<LinkContainer key={"newLink"} to={"/post/new"}>
<Button bsStyle="default">New Post</Button>
</LinkContainer>
</ButtonGroup>
</ButtonToolbar>
<Table striped bordered condensed hover>
<thead>
<tr>
<th>Content</th>
<th>Created On</th>
</tr>
</thead>
<tbody>
{
this.state.posts.map(p => {
let content = p.content || "";
content = content.trim();

let firstLineBreak = content.indexOf("\n");

if (firstLineBreak !== -1) {
content = content.substr(0, firstLineBreak);
}

content = content.replace(/[#]*/g, "");

return (
<LinkContainer key={`${p.id}_link`} to={`/post/${p.id}`}>
<tr>
<td>{content}</td>
<td>{new Date(p.created_on).toTimeString()}</td>
</tr>
</LinkContainer>
);
})
}
</tbody>
</Table>
</div>
);
}
}
Loading

0 comments on commit 304e727

Please sign in to comment.