Skip to content

Commit

Permalink
Merge pull request #34034 from code-dot-org/generate-single-student-pdf
Browse files Browse the repository at this point in the history
Parent Letter: generate custom letter for single student
  • Loading branch information
Erin007 committed Apr 4, 2020
2 parents 9e5f5e8 + ad9c4bc commit 72bd685
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 116 deletions.
2 changes: 1 addition & 1 deletion apps/i18n/common/en_us.json
Expand Up @@ -516,7 +516,6 @@
"downloadCSV": "Download CSV",
"downloadAssessmentCSV": "Download CSV of student responses",
"downloadFeedbackCSV": "Download CSV of Feedback",
"downloadParentLetter": "Download parent letter",
"downloadReplayVideoButtonDownload": "Animation",
"downloadReplayVideoButtonError": "Sorry, we were unable to download your animation. Please try re-running your project and trying again.",
"dragBlocksToMatch": "Drag the blocks to match",
Expand Down Expand Up @@ -1686,6 +1685,7 @@
"viewMyProjects": "View my projects",
"viewMore": "View more",
"viewPageAs": "View page as:",
"viewParentLetter": "View parent letter",
"viewSection": "View section",
"viewUnitOverview": "View Unit Overview",
"visible": "Visible",
Expand Down
269 changes: 168 additions & 101 deletions apps/src/lib/ui/ParentLetter.jsx
@@ -1,5 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import _ from 'lodash';
import {studio, pegasus} from '../util/urlHelpers';
import {SectionLoginType} from '../../util/sharedConstants';
import color from '../../util/color';
Expand All @@ -13,7 +15,7 @@ const ENGAGEMENT_URL =
const LOGIN_TYPE_NAMES = {
[SectionLoginType.clever]: 'Clever accounts',
[SectionLoginType.google_classroom]: 'Google Classroom accounts',
[SectionLoginType.picture]: 'picture passwosrds',
[SectionLoginType.picture]: 'picture passwords',
[SectionLoginType.word]: 'secret words',
[SectionLoginType.email]: 'personal logins'
};
Expand All @@ -32,106 +34,168 @@ const LOGIN_TYPE_NAMES = {
* secretPicturePath
* secretWords
*/
export default function ParentLetter({
loginType,
secretPicturePath,
secretWords,
sectionCode,
studentName,
teacherName
}) {
return (
<div>
<Header />
<article>
<p>Hello!</p>
<p>
In my class, your child {studentName} is learning computer science on{' '}
<a href={pegasus('/')}>Code.org</a>, a fun, creative platform for
learning computer science and basic coding to create interactive
animations, games, or apps. Your interest in what your child is
learning is critical, and Code.org makes it easy to stay involved.
</p>
<h1>
Step 1 - Encourage your child, show interest in computer science
</h1>
<p>
One of the best ways is to ask your child to explain what they’re
learning and show you a project they are proud of (
<a href={ENGAGEMENT_URL}>see details</a>
). Or watch one of{' '}
<a href={pegasus(`/educate/resources/inspire`)}>these videos</a>{' '}
together.
</p>
<h1>Step 2 - Get your child set up to use Code.org at home</h1>
<SignInInstructions
loginType={loginType}
secretPicturePath={secretPicturePath}
secretWords={secretWords}
sectionCode={sectionCode}
studentName={studentName}
/>
<p>
At the top of their homepage, {studentName || 'your student'} can
continue the course they are doing with their classroom at school.
They can also create their own{' '}
<a href={studio('/projects/public')}>
games or artwork in the Project Gallery
</a>{' '}
or check out <a href={pegasus('/athome')}>code.org/athome</a> for
ideas for things to work on at home.
</p>
<h1>Step 3 - Connect your email to your student’s account</h1>
<p>
Keep up to date with what your student is working on and receive
updates from Code.org. Have your child sign in to Code.org and then
enter your email in Account Settings or{' '}
<a href={studio('/users/edit')}>click here</a>.
</p>
<h1>Join the weekly “Code Break” every Wednesday</h1>
<p>
The team at Code.org hosts a live interactive classroom for students
of all ages. Anybody can participate, and learn computer science in a
fun format starring very special guests.{' '}
<a href={pegasus('/break')}>More info</a>.
</p>
<h1>Why computer science</h1>
<p>
Computer science teaches students critical thinking, problem solving,
and digital citizenship, and benefits all students, no matter what
opportunities they pursue in the future. And learning to make
interactive animations, code-art, games, and apps on Code.org
encourages creativity and makes learning fun.
</p>
<h1>Code.org’s commitment to student privacy</h1>
<p>
Code.org assigns utmost importance to student safety and security.
Code.org has signed the{' '}
<a href={PRIVACY_PLEDGE_URL}>Student Privacy Pledge</a> and their{' '}
<a href={pegasus('/privacy')}>privacy practices</a> have received{' '}
<a href={COMMON_SENSE_ARTICLE_URL}>
one of the highest overall scores from Common Sense Media
</a>
. You can find further details by viewing Code.org’s{' '}
<a href={pegasus('/privacy/student-privacy')}>Privacy Policy</a>.
</p>
<p>
Please let me know if you have any questions and thank you for your
continued support of your child and of our classroom!
</p>
<p>{teacherName}</p>
</article>
</div>
);
class ParentLetter extends React.Component {
static propTypes = {
studentId: PropTypes.string,
autoPrint: PropTypes.bool,
// Provided by Redux
section: PropTypes.shape({
id: PropTypes.number.isRequired,
loginType: PropTypes.oneOf(Object.values(SectionLoginType)).isRequired,
code: PropTypes.string.isRequired
}).isRequired,
students: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
secret_picture_path: PropTypes.string,
secret_words: PropTypes.string
})
),
teacherName: PropTypes.string.isRequired
};

static defaultProps = {
students: []
};

componentDidMount() {
if (this.props.autoPrint) {
this.printParentLetter();
}
}

printParentLetter = () => {
const printArea = document.getElementById('printArea').outerHTML;
// Adding a unique ID to the window name allows for multiple instances of this window
// to be open at once without affecting each other.
const windowName = `printWindow-${_.uniqueId()}`;
let printWindow = window.open('', windowName, '');

printWindow.document.open();
printWindow.addEventListener('load', event => {
printWindow.print();
});

printWindow.document.write(
`<html><head><link rel="stylesheet" type="text/css" href="/shared/css/standards-report-print.css"></head>`
);
printWindow.document.write('<body onafterprint="self.close()">');
printWindow.document.write(printArea);
printWindow.document.write('</body></html>');
printWindow.document.close();
};

render() {
const {students, teacherName, section, studentId} = this.props;
const sectionCode = section.code;
const loginType = section.loginType;
const student =
students.length !== 0 && studentId
? students
.filter(student => student.id.toString() === studentId)
.shift()
: null;
const studentName = student ? student.name : null;
const secretPicturePath = student ? student.secret_picture_path : null;
const secretWords = student ? student.secret_words : null;

return (
<div id="printArea">
<Header />
<article>
<p>Hello!</p>
<p>
In my class, your child {studentName} is learning computer science
on <a href={pegasus('/')}>Code.org</a>, a fun, creative platform for
learning computer science and basic coding to create interactive
animations, games, or apps. Your interest in what your child is
learning is critical, and Code.org makes it easy to stay involved.
</p>
<h1>
Step 1 - Encourage your child, show interest in computer science
</h1>
<p>
One of the best ways is to ask your child to explain what they’re
learning and show you a project they are proud of (
<a href={ENGAGEMENT_URL}>see details</a>
). Or watch one of{' '}
<a href={pegasus(`/educate/resources/inspire`)}>
these videos
</a>{' '}
together.
</p>
<h1>Step 2 - Get your child set up to use Code.org at home</h1>
<SignInInstructions
loginType={loginType}
secretPicturePath={secretPicturePath}
secretWords={secretWords}
sectionCode={sectionCode}
studentName={studentName}
/>
<p>
At the top of their homepage, {studentName || 'your student'} can
continue the course they are doing with their classroom at school.
They can also create their own{' '}
<a href={studio('/projects/public')}>
games or artwork in the Project Gallery
</a>{' '}
or check out <a href={pegasus('/athome')}>code.org/athome</a> for
ideas for things to work on at home.
</p>
<h1>Step 3 - Connect your email to your student’s account</h1>
<p>
Keep up to date with what your student is working on and receive
updates from Code.org. Have your child sign in to Code.org and then
enter your email in Account Settings or{' '}
<a href={studio('/users/edit')}>click here</a>.
</p>
<h1>Join the weekly “Code Break” every Wednesday</h1>
<p>
The team at Code.org hosts a live interactive classroom for students
of all ages. Anybody can participate, and learn computer science in
a fun format starring very special guests.{' '}
<a href={pegasus('/break')}>More info</a>.
</p>
<h1>Why computer science</h1>
<p>
Computer science teaches students critical thinking, problem
solving, and digital citizenship, and benefits all students, no
matter what opportunities they pursue in the future. And learning to
make interactive animations, code-art, games, and apps on Code.org
encourages creativity and makes learning fun.
</p>
<h1>Code.org’s commitment to student privacy</h1>
<p>
Code.org assigns utmost importance to student safety and security.
Code.org has signed the{' '}
<a href={PRIVACY_PLEDGE_URL}>Student Privacy Pledge</a> and their{' '}
<a href={pegasus('/privacy')}>privacy practices</a> have received{' '}
<a href={COMMON_SENSE_ARTICLE_URL}>
one of the highest overall scores from Common Sense Media
</a>
. You can find further details by viewing Code.org’s{' '}
<a href={pegasus('/privacy/student-privacy')}>Privacy Policy</a>.
</p>
<p>
Please let me know if you have any questions and thank you for your
continued support of your child and of our classroom!
</p>
<p>{teacherName}</p>
</article>
</div>
);
}
}
ParentLetter.propTypes = {
loginType: PropTypes.oneOf(Object.values(SectionLoginType)).isRequired,
secretPicturePath: PropTypes.string,
secretWords: PropTypes.string,
sectionCode: PropTypes.string, // TODO: Conditionally-required
studentName: PropTypes.string,
teacherName: PropTypes.string.isRequired
};

export const UnconnectedParentLetter = ParentLetter;

export default connect(state => ({
section:
state.teacherSections.sections[state.teacherSections.selectedSectionId],
students: state.sectionData.section.students,
teacherName: state.currentUser.userName
}))(ParentLetter);

const Header = () => {
return (
Expand Down Expand Up @@ -193,7 +257,10 @@ const SignInInstructions = ({
{secretPicturePath && (
<span>
<br />
<img src={secretPicturePath} style={{width: 60, margin: 10}} />
<img
src={pegasus(`/images/${secretPicturePath}`)}
style={{width: 60, margin: 10}}
/>
</span>
)}
</li>
Expand Down
39 changes: 31 additions & 8 deletions apps/src/lib/ui/ParentLetter.story.jsx
@@ -1,20 +1,42 @@
import React from 'react';
import PropTypes from 'prop-types';
import ParentLetter from './ParentLetter';
import {UnconnectedParentLetter as ParentLetter} from './ParentLetter';
import {SectionLoginType} from '../../util/sharedConstants';
import wizardPng from '../../../static/skins/studio/wizard_thumb.png';

export default storybook => {
storybook = storybook.storiesOf('ParentLetter', module);

const sampleSection = {
id: 7,
code: 'ABCDEF'
};

const sampleStudents = [
{
id: 100,
name: 'Neville',
secret_picture_path: wizardPng,
secret_words: 'wizarding world'
},
{
id: 101,
name: 'Hermione',
secret_picture_path: wizardPng,
secret_words: 'wizarding world'
}
];

// Make stories for generic letters and personalized letters
// Make a story for every login type
Object.values(SectionLoginType).forEach(loginType => {
storybook = storybook.add(`Generic / ${loginType}`, () => (
<Page>
<ParentLetter
loginType={loginType}
sectionCode="ABCDEF"
section={{
...sampleSection,
loginType: loginType
}}
teacherName="Minerva McGonagall"
/>
</Page>
Expand All @@ -23,12 +45,13 @@ export default storybook => {
storybook = storybook.add(`Personalized / ${loginType}`, () => (
<Page>
<ParentLetter
loginType={loginType}
secretPicturePath={wizardPng}
secretWords="wizarding world"
sectionCode="ABCDEF"
section={{
...sampleSection,
loginType: loginType
}}
teacherName="Minerva McGonagall"
studentName="Neville"
students={sampleStudents}
studentId={'101'}
/>
</Page>
));
Expand Down

0 comments on commit 72bd685

Please sign in to comment.