Skip to content

Commit

Permalink
Merge pull request #1 from zooniverse/master
Browse files Browse the repository at this point in the history
updates to my forked version if I did this correctly
  • Loading branch information
mschwamb committed Oct 29, 2015
2 parents 32547d4 + 482a5a2 commit 928182d
Show file tree
Hide file tree
Showing 28 changed files with 737 additions and 212 deletions.
6 changes: 6 additions & 0 deletions app/classifier/drawing-tools/circle.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,18 @@ module.exports = React.createClass
r: 0
angle: 0

initStart: ->
_inProgress: true

initMove: ({x, y}, mark) ->
distance = @getDistance mark.x, mark.y, x, y
angle = @getAngle mark.x, mark.y, x, y
r: distance
angle: angle

initRelease: ->
_inProgress: false

initValid: (mark) ->
mark.r > MINIMUM_RADIUS

Expand Down
6 changes: 6 additions & 0 deletions app/classifier/drawing-tools/ellipse.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,19 @@ module.exports = React.createClass
ry: 0
angle: 0

initStart: ->
_inProgress: true

initMove: ({x, y}, mark) ->
distance = @getDistance mark.x, mark.y, x, y
angle = @getAngle mark.x, mark.y, x, y
rx: distance
ry: distance * DEFAULT_SQUASH
angle: angle

initRelease: ->
_inProgress: false

initValid: (mark) ->
mark.rx > MINIMUM_RADIUS

Expand Down
6 changes: 6 additions & 0 deletions app/classifier/drawing-tools/line.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,16 @@ module.exports = React.createClass
x2: x
y2: y

initStart: ->
_inProgress: true

initMove: ({x, y}) ->
x2: x
y2: y

initRelease: ->
_inProgress: false

initValid: (mark) ->
{x1, y1, x2, y2} = mark
DrawingToolRoot.distance(x1, y1, x2, y2) > MINIMUM_LENGTH
Expand Down
6 changes: 6 additions & 0 deletions app/classifier/drawing-tools/point.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,15 @@ module.exports = React.createClass
defaultValues: ({x, y}) ->
{x, y}

initStart: ->
_inProgress: true

initMove: ({x, y}) ->
{x, y}

initRelease: ->
_inProgress: false

getDeleteButtonPosition: ->
theta = (DELETE_BUTTON_ANGLE) * (Math.PI / 180)
x: (SELECTED_RADIUS / @props.scale.horizontal) * Math.cos theta
Expand Down
2 changes: 2 additions & 0 deletions app/classifier/drawing-tools/polygon.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ module.exports = React.createClass
initStart: ({x, y}, mark) ->
mark.points.push {x, y}
points: mark.points
_inProgress: true

initMove: ({x, y}, mark) ->
mark.points[mark.points.length - 1] = {x, y}
Expand Down Expand Up @@ -112,6 +113,7 @@ module.exports = React.createClass
document.removeEventListener 'mousemove', @handleMouseMove

@props.mark.closed = true
@props.mark._inProgress = false
@props.onChange()

handleMainDrag: (e, d) ->
Expand Down
5 changes: 4 additions & 1 deletion app/classifier/drawing-tools/rectangle.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ module.exports = React.createClass

initStart: ({x, y}, mark) ->
@initCoords = {x, y}
{x, y}
{x, y, _inProgress: true}

initMove: (cursor, mark) ->
if cursor.x > @initCoords.x
Expand All @@ -40,6 +40,9 @@ module.exports = React.createClass

{x, y, width, height}

initRelease: ->
_inProgress: false

initValid: (mark) ->
mark.width > MINIMUM_SIZE and mark.height > MINIMUM_SIZE

Expand Down
2 changes: 1 addition & 1 deletion app/classifier/drawing-tools/root.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ module.exports = React.createClass
{@props.children}
</g>

{if toolProps.selected and toolProps.details? and toolProps.details.length isnt 0
{if toolProps.selected and not toolProps.mark._inProgress and toolProps.details? and toolProps.details.length isnt 0
tasks = require '../tasks'

detailsAreComplete = toolProps.details.every (detailTask, i) =>
Expand Down
7 changes: 2 additions & 5 deletions app/classifier/index.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ tasks = require './tasks'
preloadSubject = require '../lib/preload-subject'
PromiseRenderer = require '../components/promise-renderer'
TriggeredModalForm = require 'modal-form/triggered'
TutorialButton = require './tutorial-button'
isAdmin = require '../lib/is-admin'
Tutorial = require '../lib/tutorial'

Expand Down Expand Up @@ -122,11 +123,7 @@ Classifier = React.createClass
<hr />

<nav className="task-nav">
{if @props.project?.configuration?.tutorial? # TODO: The tutorial will eventually be linked from `project.get('tutorial')`.
<button type="button" className="secret-button" title="Project tutorial" aria-label="Show the project tutorial" onClick={Tutorial.start.bind(Tutorial, @props.user, @props.project)}>
<i className="fa fa-graduation-cap fa-fw" />
</button>}

<TutorialButton user={@props.user} project={@props.project} />
<button type="button" className="back minor-button" disabled={onFirstAnnotation} onClick={@destroyCurrentAnnotation}>Back</button>
{if nextTaskKey
<button type="button" className="continue major-button" disabled={waitingForAnswer} onClick={@addAnnotationForTask.bind this, classification, nextTaskKey}>Next</button>
Expand Down
51 changes: 24 additions & 27 deletions app/classifier/mock-data.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,27 @@ BLANK_IMAGE = ['data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgAQMAAAA',
'PH06nAAAABlBMVEXMzMyWlpYU2uzLAAAAPUlEQVR4nO3BAQ0AAADCoPdPbQ43oAAAAAAAAAAAAA',
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgzwCX4AAB9Dl2RwAAAABJRU5ErkJggg=='].join ''

MISC_DETAILS = [{
type: 'single'
required: true
question: 'Cool?'
answers: [
{label: 'Yeah'}
{label: 'Nah'}
]
}, {
type: 'multiple'
question: 'Cool stuff?'
answers: [
{label: 'Ice'}
{label: 'Snow'}
]
}]

workflow = apiClient.type('workflows').create
id: 'MOCK_WORKFLOW_FOR_CLASSIFIER'

first_task: 'crop'
first_task: 'draw'
tasks:
crop:
type: 'crop'
Expand Down Expand Up @@ -161,32 +178,12 @@ workflow = apiClient.type('workflows').create
* Draw something
'''
tools: [
{
type: 'point'
label: 'Point'
color: 'red'
details: [{
type: 'single'
required: true
question: 'Cool?'
answers: [
{label: 'Yeah'}
{label: 'Nah'}
]
}, {
type: 'multiple'
question: 'Cool stuff?'
answers: [
{label: 'Ice'}
{label: 'Snow'}
]
}]
}
{type: 'line', label: 'Line', color: 'yellow', details: []}
{type: 'rectangle', label: 'Rectangle', color: 'lime', details: []}
{type: 'polygon', label: 'Polygon', color: 'cyan', details: []}
{type: 'circle', label: 'Circle', color: 'blue', details: []}
{type: 'ellipse', label: 'Ellipse', color: 'magenta', details: []}
{type: 'point', label: 'Point', color: 'red', details: MISC_DETAILS}
{type: 'line', label: 'Line', color: 'yellow', details: MISC_DETAILS}
{type: 'rectangle', label: 'Rectangle', color: 'lime', details: MISC_DETAILS}
{type: 'polygon', label: 'Polygon', color: 'cyan', details: MISC_DETAILS}
{type: 'circle', label: 'Circle', color: 'blue', details: MISC_DETAILS}
{type: 'ellipse', label: 'Ellipse', color: 'magenta', details: MISC_DETAILS}
]
next: 'cool'

Expand Down
31 changes: 31 additions & 0 deletions app/classifier/tutorial-button.cjsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
React = require 'react'
apiClient = require '../api/client'
Tutorial = require '../lib/tutorial'

module.exports = React.createClass
getDefaultProps: ->
user: null
project: null

getInitialState: ->
tutorial: null

componentDidMount: ->
@fetchTutorialFor @props.project

componentWillReceiveProps: (nextProps) ->
unless nextProps.project is @props.project
@fetchTutorialFor nextProps.project

fetchTutorialFor: (project) ->
apiClient.type('tutorials').get project_id: project.id
.then ([tutorial]) =>
@setState {tutorial}

render: ->
if @state.tutorial? and @state.tutorial.steps.length isnt 0
<button type="button" className="secret-button" title="Project tutorial" aria-label="Show the project tutorial" onClick={Tutorial.start.bind(Tutorial, @props.user, @props.project)}>
<i className="fa fa-graduation-cap fa-fw" />
</button>
else
null
2 changes: 1 addition & 1 deletion app/components/media-card.cjsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
React = require 'react'

IMAGE_EXTENSIONS = ['gif', 'jpg', 'png', 'svg']
IMAGE_EXTENSIONS = ['gif', 'jpeg', 'jpg', 'png', 'svg']

VIDEO_EXTENSIONS = ['mp4']

Expand Down
52 changes: 29 additions & 23 deletions app/lib/tutorial.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ Dialog = require 'modal-form/dialog'
StepThrough = require '../components/step-through'
MediaCard = require '../components/media-card'
{Markdown} = require 'markdownz'
apiClient = require '../api/client'

completedThisSession = {}
window?.tutorialsCompletedThisSession = completedThisSession

module.exports = React.createClass
displayName: 'Tutorial'
Expand All @@ -24,31 +26,34 @@ module.exports = React.createClass
if isNaN completedAt.valueOf()
false
else
# TODO: Check if the completion date is greater than the most recent tutorial_step's modified_at date.
# TODO: Check if the completion date is greater than the tutorial's modified_at date.
# Return `null` to mean "Completed, but not with the most recent version".
true

start: (user, project) ->
# TODO: `project.get('tutorial')`
getSteps = Promise.resolve project.configuration?.tutorial ? []
apiClient.type('tutorials').get project_id: project.id
.then ([tutorial]) =>
if tutorial? and tutorial.steps.length isnt 0
tutorial.get 'attached_images'
.catch =>
[]
.then (mediaResources) =>
mediaByID = {}
for mediaResource in mediaResources
mediaByID[mediaResource.id] = mediaResource

doingTutorial = getSteps.then (steps) =>
unless steps.length is 0
Tutorial = this
Dialog.alert <Tutorial steps={steps} />, className: 'tutorial-dialog'

# We don't really care if the user canceled or completed the tutorial.
doneDoingTutorial = doingTutorial.catch =>
null

doneDoingTutorial.then =>
now = new Date().toISOString()
if user?
user.get('project_preferences', project_id: project.id).then ([projectPreferences]) =>
projectPreferences.update 'preferences.tutorial_completed_at': now
projectPreferences.save()
else
completedThisSession[project.id] = now
TutorialComponent = this
Dialog.alert <TutorialComponent steps={tutorial.steps} media={mediaByID} />, className: 'tutorial-dialog'
.catch =>
null # We don't really care if the user canceled or completed the tutorial.
.then =>
now = new Date().toISOString()
if user?
user.get('project_preferences', project_id: project.id).then ([projectPreferences]) =>
projectPreferences.update 'preferences.tutorial_completed_at': now
projectPreferences.save()
else
completedThisSession[project.id] = now

startIfNecessary: (user, project) ->
@checkIfCompleted user, project
Expand All @@ -63,15 +68,16 @@ module.exports = React.createClass

getDefaultProps: ->
steps: []
media: {}

render: ->
<StepThrough ref="stepThrough" className="tutorial-steps">
{for step, i in @props.steps
step._key ?= Math.random()
<MediaCard key={step._key} className="tutorial-step" src={step.media}>
<MediaCard key={step._key} className="tutorial-step" src={@props.media[step.media]?.src}>
<Markdown>{step.content}</Markdown>
<hr key="hr" />
<p key="p" style={textAlign: 'center'}>
<hr />
<p style={textAlign: 'center'}>
{if i is @props.steps.length - 1
<button type="submit" className="major-button">Let’s go!</button>
else
Expand Down
2 changes: 2 additions & 0 deletions app/pages/about/publications-page.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ publicationCategories =
},
{slug: "zooniverse/solar-stormwatch"
publications: [
{citation: "Differences between the CME fronts tracked by an expert, an automated algorithm, and the Solar Stormwatch project, Barnard+ 2015."
href: "http://onlinelibrary.wiley.com/doi/10.1002/2015SW001280/full"},
{citation: "Observational Tracking of the 2D Structure of Coronal Mass Ejections Between the Sun and 1 AU, Savani+ 2015."
href: "http://arxiv.org/abs/1503.08774"},
{citation: "Validation of a priori CME arrival predictions made using real-time heliospheric imager observations, Tucker-Hood+ 2015."
Expand Down
8 changes: 7 additions & 1 deletion app/pages/admin/project-status.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ ProjectIcon = require '../../components/project-icon'
AutoSave = require '../../components/auto-save'
handleInputChange = require '../../lib/handle-input-change'

EXPERIMENTAL_FEATURES = [
'survey'
'crop'
'tutorial'
]

ProjectToggle = React.createClass
displayName: "ProjectToggle"

Expand Down Expand Up @@ -87,7 +93,7 @@ ProjectExperimentalFeatures = React.createClass
render: ->
<div>
<AutoSave resource={@props.project}>
{["survey", "crop"].map (task) =>
{EXPERIMENTAL_FEATURES.map (task) =>
<label key={task}>
<input type="checkbox" name={task} checked={@setting(task)} onChange={@updateTasks.bind @, task} />
{task.charAt(0).toUpperCase() + task.slice(1)}
Expand Down
4 changes: 4 additions & 0 deletions app/pages/lab/project.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ EditProjectPage = React.createClass
<li><Link to="edit-project-collaborators" params={linkParams} className="nav-list-item" title="Add people to your team and specify what their roles are so that they have the right access to the tools they need (including access to the project while it’s private).">
Collaborators
</Link></li>
{if 'tutorial' in (@props.project.experimental_tools ? [])
<li><Link to="edit-project-tutorial" params={linkParams} className="nav-list-item" title="Create a pop-up tutorial for your project’s classification interface">
Tutorial
</Link></li>}
<li><Link to="edit-project-media" params={linkParams} className="nav-list-item" title="Add any images you’d like to use in this project’s introduction, science case, results, FAQ, or education content pages.">
Media
</Link></li>
Expand Down
4 changes: 2 additions & 2 deletions app/pages/lab/subject-set.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ isAdmin = require '../../lib/is-admin'
NOOP = Function.prototype

VALID_SUBJECT_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.svg']
INVALID_FILENAME_CHARS = ['/', '\\', ':']
INVALID_FILENAME_CHARS = ['/', '\\', ':', ',']
MAX_FILE_SIZE = 600000

announceSetChange = ->
Expand Down Expand Up @@ -273,7 +273,7 @@ EditSubjectSetPage = React.createClass
_findFilesInMetadata: (metadata) ->
filesInMetadata = []
for key, value of metadata
extensions = if isAdmin() then '' else "(?:#{VALID_SUBJECT_EXTENSIONS.join '|'})"
extensions = if isAdmin() then '\\.\\w{2,4}' else "(?:#{VALID_SUBJECT_EXTENSIONS.join '|'})"
filesInValue = value.match? ///([^#{INVALID_FILENAME_CHARS.join ''}]+#{extensions})///gi
if filesInValue?
filesInMetadata.push filesInValue...
Expand Down
Loading

0 comments on commit 928182d

Please sign in to comment.