Skip to content
This repository has been archived by the owner on Oct 6, 2021. It is now read-only.

Beta feedback changes #72

Merged
merged 26 commits into from
Sep 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4501f81
Lighten the background
eatyourgreens Jun 27, 2016
8ad9dbf
Check for selected issue text on new annotations
eatyourgreens Jun 27, 2016
bdf1fa1
Style updates
eatyourgreens Jul 6, 2016
c1de441
Generate subtask values properly
eatyourgreens Jul 13, 2016
0580d98
Nicer styles for annotation summary
eatyourgreens Jul 13, 2016
f61c69c
Grab one subject at a time from the queue
eatyourgreens Jul 13, 2016
bf6ca39
New subject viewer layout
eatyourgreens Jul 13, 2016
8914995
Check that new selections are subject text
eatyourgreens Jul 13, 2016
f7f45b0
Add a filtering step to the marking task
eatyourgreens Jul 27, 2016
2418de1
Update annotation format
eatyourgreens Aug 1, 2016
1970d30
Fix react key error on filter question
eatyourgreens Aug 1, 2016
81a0783
Use consistent names for annotations
eatyourgreens Aug 1, 2016
e29385e
Display previous and next pages during the details step
eatyourgreens Aug 5, 2016
a4fcda4
Select randomly from all subject sets
eatyourgreens Aug 5, 2016
8fd94cd
Load subject sets with subjects
eatyourgreens Aug 12, 2016
21b2468
Add a toggle for scanned image/OCR text
eatyourgreens Aug 12, 2016
469b0da
upgrade to React 14
eatyourgreens Aug 12, 2016
7592aff
Classify page rendering and links
eatyourgreens Aug 15, 2016
d6770b7
Fix sign out
eatyourgreens Aug 15, 2016
af18c4b
Colour code the marking tools
eatyourgreens Aug 24, 2016
700b466
Set the workflow ID
eatyourgreens Aug 24, 2016
34b4c15
towards a more flexible editor
eatyourgreens Aug 22, 2016
1da40c8
Tidy up the annotation summary
eatyourgreens Aug 24, 2016
41673d7
Change health to pollution
eatyourgreens Aug 24, 2016
00143ab
Comment out the Reports page
eatyourgreens Aug 24, 2016
67116c2
current annotation
eatyourgreens Aug 24, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 7 additions & 42 deletions app/classifier.cjsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
React = require 'react'

SubjectTools = require './classify/subject-tools'
SubjectViewer = require './classify/subject-viewer'
Annotation = require './classify/annotation'
ClassificationTask = require './classify/classification-task'
Subject = require './classify/subject'
Subjects = require './lib/subjects'
Classifications = require './lib/classifications'

Expand All @@ -24,66 +23,32 @@ module.exports = React.createClass

componentWillReceiveProps: (newProps)->
{api, project, subject_set, workflow} = newProps
@subjects.update {api, project, subject_set_id: subject_set.id}
@subjects.update {api, project, subject_set_id: subject_set?.id}
@classifications.update {api, project, workflow}

if newProps.user != @props.user || newProps.subject_set.id != @props.subject_set.id
if newProps.user != @props.user
@setState currentSubjects: [], =>
@subjects.flush()
@subjects.fetch()
.then @nextSubject

componentDidUpdate: ->
return unless @subjects.current?
container = @refs.scrollContainer?.getDOMNode()
subject_node = @refs["subject#{@subjects.current.id}"]?.getDOMNode()
return unless container? && subject_node?

container.scrollTop -= subject_node.scrollHeight
distance = subject_node.offsetTop / 20

move_subject = =>
container.scrollTop = container.scrollTop + distance
setTimeout move_subject, 50 unless container.scrollTop > subject_node.offsetTop - 50

setTimeout move_subject, 50

render: ->
<ClassificationTask onChange={@onChangeAnnotation} onFinish={@onFinishPage}>
<div className="readymade-subject-viewer-container">
{
if @state.currentSubjects.length
<div className="readymade-subject-viewer">
<SubjectTools project={@props.project} api={@props.api} talk={@props.talk} user={@props.user} subject_set={@props.subject_set} subject={@state.currentSubjects[0]} />
<div className="scroll-container" ref="scrollContainer">
{<SubjectViewer subject={subject} key={subject.id} ref="subject#{subject.id}" isCurrent={subject.id is @subjects.current.id} /> for subject in @state.currentSubjects}
</div>
</div>
}
</div>
<Subject ref='subject' project={@props.project} api={@props.api} talk={@props.talk} user={@props.user} subject_set={@props.subject_set} subject={@state.currentSubjects[0]} />
</ClassificationTask>

onChangeAnnotation: (annotation) ->
if annotation.issue
@refs["subject#{subject.id}"].getDOMNode().classList.add 'active' for subject in @state.currentSubjects
else
@refs["subject#{subject.id}"].getDOMNode().classList.remove 'active' for subject in @state.currentSubjects
@refs.subject.onChange annotation

onFinishPage: (task_annotations) ->
@classifications?.set_annotations ({task: key, value: value} for key, value of task_annotations)
@classifications.finish()
# @classifications.finish()
console.log JSON.stringify @classifications.current()
console.log @state.currentSubjects[0]?.metadata.image
@nextSubject()

nextSubject: ->
currentSubjects = @state.currentSubjects
if currentSubjects.length is 0
currentSubjects.push @subjects.next(), @subjects.queue[0]
else
@subjects.next()
currentSubjects.push @subjects.queue[0]
currentSubjects.shift() if currentSubjects.length > 3
currentSubjects = [@subjects.next()]
# create a new classification here
@classifications.create [@subjects.current]
# remove undefined or null subjects from currentSubjects
Expand Down
15 changes: 6 additions & 9 deletions app/classify/annotation.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,20 @@ TextRange = React.createClass
displayName: 'TextRange'

render: ->
annotation = @props.range.annotation
<li className="highlight #{annotation.type}">
annotation = @props.range?.annotation ? ''
<span>
{annotation.text}
</li>
</span>

module.exports = React.createClass
displayName: 'Annotation'

tasks: tasks

render: ->
<div className="annotation">
{@tasks[@props.tool.type].label} <button className="secret-button" aria-label='Edit' onClick={@edit}><span className="fa fa-pencil-square-o"></span></button>
<ul>
<TextRange range={@props.tool.issue} />
</ul>
</div>
<button className="standard-button annotation #{@props.tool.type}" onClick={@edit}>
<TextRange range={@props.tool.issue} />
</button>

edit: (e) ->
@props.edit @props.tool
Expand Down
128 changes: 93 additions & 35 deletions app/classify/classification-task.cjsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
React = require 'react'
FilterTask = require './tasks/filter'
ChooseTask = require './tasks/choose'
EditTask = require './tasks/edit'
Annotation = require './annotation'
Expand All @@ -22,6 +23,7 @@ AnnotationsSummary = React.createClass

render: ->
<div className="annotation-summary">
{<h2>Health issues</h2> if @props.annotations.length}
{if @props.annotations.length then @props.annotations.map (tool) =>
<Annotation key={tool.id} tool={tool} delete={@props.deleteTool} edit={@edit} />
else
Expand All @@ -40,98 +42,154 @@ module.exports = React.createClass
description: "Find a health issue on this page that fits one of the categories below. Your task is to collect all the information on the page about the issue you've found."

getInitialState: ->
step: 'choose'
relevant: null
step: 'filter'
type: 'health'
instructions: @defaultInstructions
annotations: []

render: ->
children = React.Children.map @props.children, (child) => React.cloneElement child, task: @state.step
<div>
<TaskInstructions instructions={@state.instructions} />
<div className="readymade-classification-interface">
<div className="readymade-decision-tree-container">
<div className="decision-tree">
{switch @state.step
when 'filter'
<FilterTask onComplete={@filter} />
when 'choose'
<div>
<ChooseTask onChooseTask={@create} onFinish={@finish} />
<AnnotationsSummary annotations={@state.annotations} deleteTool={@deleteAnnotation} onEdit={@edit} />
<ChooseTask onChooseTask={@create} onBack={@reset} onFinish={@finish} />
</div>
when 'edit'
<EditTask annotation={@state.annotations[0]} onChange={@props.onChange} onComplete={@choose}/>
<EditTask annotation={@currentAnnotation()} onChange={@onChange} onComplete={@choose}/>
}
</div>
</div>
{children}
<div className="readymade-decision-tree-container">
<div className="decision-tree">
{switch @state.step
when 'choose', 'edit'
<div>
<AnnotationsSummary annotations={@state.annotations} deleteTool={@deleteAnnotation} onEdit={@edit} />
</div>
}
</div>
</div>
{@props.children}
</div>
</div>

onChange: (annotation) ->
@props.onChange annotation

annotations = @state.annotations
annotations.shift()
annotations.unshift annotation
@setState {annotations}

filter: (choice) ->
switch choice
when 'yes'
@setState
relevant: 'yes'
step: 'choose'
type: null
instructions: @defaultInstructions
when 'no'
@setState relevant: 'no', @finish

create: (type) ->
@newAnnotation type
@setState
step: 'edit'
type: type
instructions: tasks[type]

edit: (tool) ->
tool.issue?.el.classList.remove 'complete'
for type, ranges of tool.subtasks
ranges.map (range) -> range.el.classList.remove 'complete'
@editAnnotation tool
@setState
step: 'edit'
type: tool.type
instructions: tasks[tool.type]
@props.onChange tool
edit: (annotation) ->

if @state.step is 'edit'
@completeAnnotation @currentAnnotation(), => @editAnnotation annotation
else
@editAnnotation annotation
@props.onChange annotation

choose: (annotation) ->
annotations = @state.annotations
annotations.shift()

if annotation.empty()
annotation.destroy()
else
annotation.issue?.el.classList.add 'complete'
for type, ranges of annotation.subtasks
ranges.map (range) -> range.el.classList.add 'complete'
annotations.unshift annotation
@completeAnnotation annotation

@setState
step: 'choose'
type: null
instructions: @defaultInstructions
annotations: annotations

@props.onChange new AnnotationTool

reset: ->
annotation.destroy() for annotation in @state.annotations
step = 'filter'
annotations = []
@setState {annotations, step}

finish: ->
task_annotations = {}
marking_annotations = []
@state.annotations.map (annotation, i) ->
task_annotations[annotation.type] ?= []
task_annotations[annotation.type].push annotation.value()
task_annotations[task] ?= [] for task of tasks
@props.onFinish task_annotations
marking_annotations.push annotation.value()
annotations =
T1: @state.relevant
T2: marking_annotations
@props.onFinish annotations
annotations = @state.annotations
annotation.destroy() for annotation in annotations
annotations = []
@setState {annotations}
step = 'filter'
@setState {annotations, step}

newAnnotation: (type) ->
annotations = @state.annotations
annotations.unshift new AnnotationTool type
annotation = new AnnotationTool type
annotation.addIssue()
annotations.unshift annotation
@setState {annotations}
@props.onChange annotation

editAnnotation: (annotation) ->
annotation.issue?.el.classList.remove 'complete'
for type, ranges of annotation.subtasks
ranges.map (range) -> range.el.classList.remove 'complete'

annotations = @state.annotations
index = annotations.indexOf annotation
annotations.splice index, 1
annotations.unshift annotation
@setState {annotations}

@setState
step: 'edit'
type: annotation.type
instructions: tasks[annotation.type]
annotations: annotations

deleteAnnotation: (annotation) ->
annotations = @state.annotations
index = annotations.indexOf annotation
annotations.splice index, 1
annotation.destroy()
@setState {annotations}

currentAnnotation: ->
@state.annotations[0]

completeAnnotation: (annotation, callback = () -> ) ->
annotations = @state.annotations
annotations.shift()

if annotation.empty()
annotation.destroy()
else
annotation.issue?.el.classList.add 'complete'
for type, ranges of annotation.subtasks
ranges.map (range) -> range.el.classList.add 'complete'
annotations.unshift annotation

@setState {annotations}, callback


20 changes: 19 additions & 1 deletion app/classify/subject-tools.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,25 @@ module.exports = React.createClass

getInitialState: ->
fieldGuideHidden: true
subject_set:
display_name: ''
metadata:
BOROUGH: ''
Date: ''

componentWillMount: ->
subject_set_id = @props.subject?.links.subject_sets[0]
@updateSubjectSet subject_set_id

componentWillReceiveProps: (newProps) ->
new_id = newProps.subject.links.subject_sets[0]
if new_id != @props.subject.links.subject_sets[0]
@updateSubjectSet new_id

render: ->
<div>
<div className="drawing-controls">
<h2>{@props.subject_set.metadata.BOROUGH} {@props.subject_set.metadata.Date} ({@props.subject_set.display_name})</h2>
<h2>{@state.subject_set.metadata.BOROUGH} {@state.subject_set.metadata.Date} ({@state.subject_set.display_name}) {<span>Page {@props.subject.metadata.page}</span> if @props.subject?}</h2>
<span className="tools">
<label className="readymade-has-clickable">
<input type="checkbox" name="examples" checked={[email protected]} onChange={@toggleFieldGuide} />
Expand Down Expand Up @@ -50,3 +64,7 @@ module.exports = React.createClass
.then ([tutorial]) =>
alert (resolve) =>
<Tutorial tutorial={tutorial} api={@props.api} user={@props.user} project={@props.project} onFinish={resolve} />
updateSubjectSet: (subject_set_id) ->
@props.api.type('subject_sets').get subject_set_id
.then (subject_set) =>
@setState {subject_set}
7 changes: 3 additions & 4 deletions app/classify/subject-viewer.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,15 @@ module.exports = React.createClass

render: ->
classList=["readymade-marking-surface-container"]
classList.push "current" if @props.isCurrent
classList.push @props.mode
classList.push 'active' if @props.active
image = @mediaSrcs['image/jpeg']
<div className={classList.join ' '}>
<div className="text-viewer">
<h3>Page {@props.subject.metadata.page}</h3>
<div data-subject={@props.subject.id}>{@state.text}</div>
</div>
<div className="subject-image">
<h3>Scanned page</h3>
{<img src={image} alt="" /> if image}
{<img src={image} alt={@state.text} /> if image}
</div>
</div>

Expand Down
Loading