Skip to content

Commit

Permalink
Add APCSP Landing Page
Browse files Browse the repository at this point in the history
When going to /apcsp as a non-verified teacher,
provide information about the curriculum and a
form for requesting access.
  • Loading branch information
sderickson committed Mar 23, 2018
1 parent 7cb998a commit 65c0685
Show file tree
Hide file tree
Showing 10 changed files with 299 additions and 21 deletions.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions app/core/api/contact.coffee
@@ -0,0 +1,9 @@
fetchJson = require './fetch-json'

module.exports = {
sendAPCSPAccessRequest: ({ name, email, message }, options={}) ->
fetchJson('/contact/send-apcsp-access-request', _.assign({}, options, {
method: 'POST'
json: { name, email, message }
}))
}
1 change: 1 addition & 0 deletions app/core/api/index.coffee
Expand Up @@ -5,6 +5,7 @@ module.exports = {
campaigns: require('./campaigns')
clas: require('./clas')
classrooms: require('./classrooms')
contact: require('./contact')
courses: require('./courses')
courseInstances: require('./course-instances')
files: require('./files')
Expand Down
5 changes: 5 additions & 0 deletions app/core/api/prepaids.coffee
Expand Up @@ -11,4 +11,9 @@ module.exports = {

fetchJoiners: ({ prepaidID }, options={}) ->
fetchJson(@url(prepaidID, 'joiners'))

getOwn: (options={}) ->
options.data ?= {}
options.data.creator = me.id
fetchJson('/db/prepaid', options)
}
6 changes: 6 additions & 0 deletions app/core/api/trial-requests.coffee
Expand Up @@ -6,4 +6,10 @@ module.exports = {
method: 'POST',
json: trialRequest
}))

getOwn: (options) ->
options ?= {}
options.data ?= {}
options.data.applicant = me.id
return fetchJson('/db/trial.request', options)
}
4 changes: 3 additions & 1 deletion app/templates/teachers/dynamic-apcsp-view.jade
Expand Up @@ -10,7 +10,9 @@ block content
a(href='/apcsp')
|< Back to AP CS Principles Home

if view.loadingData
if view.cannotAccess()
#apcsp-landing Stub
else if view.loadingData
h4.text-center
i Loading...
else if view.notFound
Expand Down
221 changes: 221 additions & 0 deletions app/views/teachers/APCSPLanding.vue
@@ -0,0 +1,221 @@
<template lang="jade">
#apcsp-landing
h1.text-center
| Teaching AP <sup>®</sup> Computer Science Principles?
h2.text-center We’ve got you covered.

#endorsement
img#ap-provider-badge(src="/images/pages/apcsp/APCSP_ProviderBadge_lg.png")
div CodeCombat is recognized by the College Board as an endorsed provider of curriculum and professional development for AP<sup>®</sup> Computer Science Principles (AP CSP). This endorsement affirms that all components of CodeCombat‘s offerings are aligned to the AP Curriculum Framework standards and the AP CSP assessment. Using an endorsed provider affords schools access to resources including an AP CSP syllabus pre-approved by the College Board’s AP Course Audit, and officially recognized professional development that prepares teachers to teach AP CSP.

p Are you teaching AP<sup>®</sup> Computer Science Principles at your high school for the 2018-2019 school year? CodeCombat’s comprehensive curriculum and professional development program are all you need to offer College Board’s newest computer science course to your students.

p AP<sup>®</sup> Computer Science Principles spotlights how computing is changing the world, and provides ample opportunity for students from all backgrounds to connect abstract concepts to real-world implications of the field. Teachers can use our AP<sup>®</sup> CSP Curriculum and Professional Development Hub as their primary resource for teaching the course and preparing students for the AP<sup>®</sup> exam.

p We’ve designed our resources to support teachers regardless of their programming experience, enabling every educator to facilitate this course with confidence. Our team at CodeCombat will be with you every step of the way.

h4 CodeCombat’s curriculum features:
ul
li A full end-to-end curricular solution for teaching AP<sup>®</sup> Computer Science Principles.
li Free, self-paced professional development with oversight from our educational experts.
li Guidance on all aspects of the course, including the AP<sup>®</sup> CSP Performance Tasks and how to utilize practice opportunities to set your students up for success.
li Real, typed code in either JavaScript or Python to support your students’ creativity.

p We are currently accepting teachers for our summer professional development cohort in preparation for the 2018-2019 school year. Request access to our curriculum to learn more.

.text-center
button.btn.btn-primary.btn-lg.text-center(
data-toggle="modal"
data-target="#request-access-modal"
)
| Request Access

#request-access-modal.modal.fade
.modal-dialog
.modal-content.style-flat
.modal-header
.button.close(type="button", data-dismiss="modal", aria-hidden="true") &times;
h3 Request Access

.modal-body
div(v-if='state === "sending"') Sending...
div(v-else-if='state === "sent"') Thank you for expressing interest in our curriculum for AP<sup>®</sup> Computer Science Principles. Our school specialists will be in touch shortly with next steps.
form(@submit.prevent="onSubmit" v-else)
.form-group
label(for="name") Name
input#name.form-control(v-model="name" autocomplete="name")
.form-group
label(for="email") Email
input#email.form-control(type="email" v-model="email" autocomplete="email")
nces-search-input(@navSearchChoose="onChooseSchool", :initialValue="organization", label="School", @updateValue="onUpdateSchoolValue")
.form-group
label(for="num-apcsp-students") Estimated # of AP<sup>®</sup> CSP students for 2018-2019 school year
input#num-apcsp-students.form-control(type="number", v-model="numAPCSPStudents")
.form-group
label(for="apcsp-experience") Brief summary of your previous AP<sup>®</sup> CSP experience
textarea#apcsp-experience.form-control(v-model="apcspExperience")

.modal-footer(v-if='state === "entering"')
button.btn.btn-primary.btn-lg(
type="button",
aria-hidden="true",
@click="onSubmit",
:disabled="submitButtonDisabled"
) Submit

hr

h4.text-center
strong
Curriculum Specifications

h5 Programming Languages
p CodeCombat’s curiculum can be used to learn programming in either JavaScript or Python, which teachers will choose upon creation of their classroom inside the CodeCombat platform.

h5 Teacher Verification
p Teachers who approved through CodeCombat’s verification process will be given access to our full AP® Computer Science Principles curriculum, including all professional development and assessment materials. The verification process is free, and is designed to maximize our professional development efforts.

h5 Minimum Hardware/Software Specifications
p CodeCombat runs best on computers with at least 4GB of RAM, on a modern browser such as Chrome, Safari, Firefox, or Edge. Chromebooks with 2GB of RAM may have minor graphics issues in courses beyond Computer Science 3, though there should be minimal issues with the recommended content for AP® Computer Science Principles as outlined. A minimum of 200 Kbps bandwidth per student is required, although 1+ Mbps is recommended.

h5 Professional Development
p Our professional development materials are made available to all verified teachers at no cost. Professional development is self-directed and self-paced, taking place online and in collaborative forums where teachers can ask questions and participate in discussions with other verified teachers and CodeCombat content experts. We strongly encourage all teachers to go through professional development before using CodeCombat as their AP® Computer Science Principles curriculum, and we provide support throughout the year as their classes progress through the curriculum.

h5 Student Licenses
p
| In order for teachers to be able to assign the required CodeCombat courses to students in their class, each student will need a License. Information on license pricing and structure can be obtained by speaking to CodeCombat’s school specialists (email
=" "
a(mailto="schools@codecombat.com") schools@codecombat.com
=""
| ). We recommend that licenses are obtained by early August of the coming school year so students can begin as early as the first day of the fall semester.

p#registered
i AP<sup>®</sup> and Advanced Placement<sup>®</sup> are registered trademarks of the College Board. Used with permission.


</template>

<script lang="coffee">
api = require 'core/api'
NcesSearchInput = require 'views/core/CreateAccountModal/teacher/NcesSearchInput'
module.exports = Vue.extend({
data: -> {
email: '',
name: '',
organization: '',
nces_phone : '',
nces_students : '',
nces_district_students : '',
nces_district_schools : '',
nces_district_id : '',
nces_district : '',
nces_name : '',
nces_id : '',
numAPCSPStudents: '',
apcspExperience: '',
hasPrepaids: '',
state: 'entering' # or 'sending', 'sent'
}
components: {
NcesSearchInput
}
methods:
onSubmit: ->
lines = []
_.forIn(@$data, (value, key) =>
return if key in ['state']
if value
lines.push "#{_.string.humanize(key)}: #{value}"
)
message = lines.join('\n')
email = @email
name = @name
console.log 'Sending message:\n\n' + message
@state = 'sending'
api.contact.sendAPCSPAccessRequest({message, email, name}).then(() => @state = 'sent')
onChooseSchool: (displayKey, choice) ->
@organization = choice.name
_.forIn(choice, (value, key) =>
@["nces_#{key}"] = value
)
onUpdateSchoolValue: (name, newValue) ->
@organization = newValue
computed:
submitButtonDisabled: ->
not _.every([@email, @name, @organization, @apcspExperience, @numAPCSPStudents])
created: ->
api.trialRequests.getOwn().then((trialRequests) =>
trialRequests = _.sortBy(trialRequests, (t) -> t.id)
lastTrialRequest = _.last(trialRequests)
properties = lastTrialRequest?.properties || {}
_.assign(@, _.pick(properties, 'email', 'organization'))
@name = _.string.trim((properties.firstName || '') + ' ' + (properties.lastName || ''))
)
unless me.isAnonymous()
api.prepaids.getOwn().then((prepaids) =>
@hasPrepaids = _.uniq(prepaids.map((p) -> p.type)).join(', ')
)
})
</script>

<style lang="sass">
#apcsp-landing
max-width: 800px
margin: 0 auto
h1
margin: 30px 0 0px
font-size: 36px
h2
margin-bottom: 20px
h3
margin: 30px 0 10px
h4
margin: 10px 0
h5
margin: 30px 0 5px
font-weight: normal
ul
margin: 10px 0 25px 0
p
margin: 0 0 25px
#endorsement
margin: 30px 0
font-style: italic
display: flex
background-color: #f7f7f7
border: 1px solid #bbbbbb
padding: 20px
font-size: 14px
line-height: 22px
#ap-provider-badge
width: 150px
margin-right: 30px
align-self: center
#registered
margin-top: 60px
font-size: 16px
.modal h3
margin-top: 0
.modal span
font-weight: bold
</style>
48 changes: 28 additions & 20 deletions app/views/teachers/DynamicAPCSPView.coffee
Expand Up @@ -3,6 +3,7 @@ RootView = require 'views/core/RootView'
api = require 'core/api'
ace = require('lib/aceContainer')
aceUtils = require 'core/aceUtils'
APCSPLanding = require('./APCSPLanding').default

module.exports = class DynamicAPCSPView extends RootView
id: 'dynamic-apcsp-view'
Expand All @@ -16,32 +17,39 @@ module.exports = class DynamicAPCSPView extends RootView
@content = ''
@loadingData = true

if _.string.startsWith(@name, 'markdown/')
unless _.string.endsWith(@name, '.md')
@name = @name + '.md'
promise = api.markdown.getMarkdownFile(@name.replace('markdown/', ''))
else
promise = api.apcsp.getAPCSPFile(@name)

promise.then((data) =>
@content = marked(data, sanitize: false)
@loadingData = false
@render()
).catch((error) =>
@loadingData = false
if error.code is 404
@notFound = true
@render()
unless @cannotAccess()
if _.string.startsWith(@name, 'markdown/')
unless _.string.endsWith(@name, '.md')
@name = @name + '.md'
promise = api.markdown.getMarkdownFile(@name.replace('markdown/', ''))
else
console.error(error)
@error = error.message
@render()
promise = api.apcsp.getAPCSPFile(@name)

)
promise.then((data) =>
@content = marked(data, sanitize: false)
@loadingData = false
@render()
).catch((error) =>
@loadingData = false
if error.code is 404
@notFound = true
@render()
else
console.error(error)
@error = error.message
@render()
)

cannotAccess: ->
return me.isAnonymous() or !me.isTeacher() or !me.get('verifiedTeacher')

afterRender: ->
super()
if @cannotAccess()
new APCSPLanding({
el: @$('#apcsp-landing')[0]
})

@$el.find('pre>code').each ->
els = $(@)
c = els.parent()
Expand Down
25 changes: 25 additions & 0 deletions server/middleware/contact.coffee
Expand Up @@ -4,8 +4,33 @@ errors = require '../commons/errors'
wrap = require 'co-express'
database = require '../commons/database'
parse = require '../commons/parse'
config = require '../../server_config'

module.exports =
sendAPCSPAccessRequest: wrap (req, res, next) ->
{ message, email, name } = req.body
context =
email_id: sendwithus.templates.plain_text_email
recipient:
address: 'jane@codecombat.com'
cc:[
{address:'robin@codecombat.com'},
{address:'scott@codecombat.com'}
]
sender:
address: config.mail.username
reply_to: email
name: name
email_data:
subject: 'New APCSP Access Request - ' + email
content: message
contentHTML: message.replace /\n/g, '\n<br>'
sendwithus.api.send context, (err, result) ->
if err
return next(new errors.InternalServerError("Error sending email. Check that it's valid and try again."))
else
res.status(200).send()

sendParentSignupInstructions: wrap (req, res, next) ->
context =
email_id: sendwithus.templates.coppa_deny_parent_signup
Expand Down
1 change: 1 addition & 0 deletions server/routes/index.coffee
Expand Up @@ -46,6 +46,7 @@ module.exports.setup = (app) ->
app.post('/contact/send-parent-signup-instructions', mw.contact.sendParentSignupInstructions)
app.post('/contact/send-teacher-game-dev-project-share', mw.contact.sendTeacherGameDevProjectShare)
app.post('/contact/send-teacher-signup-instructions', mw.contact.sendTeacherSignupInstructions)
app.post('/contact/send-apcsp-access-request', mw.contact.sendAPCSPAccessRequest)

app.delete('/db/*', mw.auth.checkHasUser())
app.patch('/db/*', mw.auth.checkHasUser())
Expand Down

0 comments on commit 65c0685

Please sign in to comment.