Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEM-compatible Project Builder (aka Lab 2 aka /lab-fem) #6037

Merged
merged 25 commits into from
Nov 17, 2021
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c8670d8
Add /lab-fem route
shaunanoordin Oct 26, 2021
b6b4c73
Add app/pages/lab-fem folder with README
shaunanoordin Oct 26, 2021
32111f1
Add lab-fem Workflows editor. URLs links are still wonky though
shaunanoordin Oct 26, 2021
1f68139
Apply changes from #6306
shaunanoordin Oct 27, 2021
bf46094
WIP remove /lab-fem route. Add FEMLabSplitter component
shaunanoordin Oct 29, 2021
dace641
Fix import typo
shaunanoordin Oct 29, 2021
6a32c6f
Propagate changes from PR #6040
shaunanoordin Nov 1, 2021
7b13dc5
Merge branch 'master' into lab-2
shaunanoordin Nov 1, 2021
3ee3ce8
Update FEMLabSplitter into FEMLabRouter
shaunanoordin Nov 1, 2021
0f34ca5
Add FEMLabRouter logic
shaunanoordin Nov 1, 2021
e64c083
Update README
shaunanoordin Nov 2, 2021
00b8f0a
Add functional FEM-lab checks
shaunanoordin Nov 2, 2021
ad4be4a
Update README. Add option to toggle FEM-lab via experimental tools
shaunanoordin Nov 2, 2021
d892873
Merge branch 'master' into lab-2
eatyourgreens Nov 2, 2021
8cb711b
Merge branch 'master' into lab-2
shaunanoordin Nov 4, 2021
f10139e
Synch changes from #6042
shaunanoordin Nov 4, 2021
03710ca
Merge branch 'master' into lab-2
shaunanoordin Nov 4, 2021
441bc42
Add deprecated Shortcut Editor option.
shaunanoordin Nov 4, 2021
ec294e0
Remove 'Project is already using the FEM Classifier' clause in FEMLab…
shaunanoordin Nov 4, 2021
d9b7daa
Merge branch 'master' into lab-2
shaunanoordin Nov 9, 2021
a5dbd6e
Merge branch 'master' into lab-2
shaunanoordin Nov 16, 2021
f510647
Remove ShortcutEditor completely from FEM-lab
shaunanoordin Nov 16, 2021
b55bb47
Change checks for ?pfeLab and ?femLab
shaunanoordin Nov 16, 2021
63852e8
Merge branch 'master' into lab-2
shaunanoordin Nov 17, 2021
eec8cf1
Merge branch 'master' into lab-2
shaunanoordin Nov 17, 2021
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
1 change: 1 addition & 0 deletions app/pages/admin/project-status/experimental-features.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const experimentalFeatures = [
'enable subject flags',
'expert comparison summary',
'fan',
'femLab', // Indicates that the Project should use the FEM-compatible Project Builder (lab) pages
'freehandLine',
'freehandSegmentLine',
'freehandSegmentShape',
Expand Down
40 changes: 40 additions & 0 deletions app/pages/lab-fem/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
## FEM-Compatible Project Builder (Lab)

This folder contains code for a modified Project Builder interface. This Project
Builder is used to build projects that are compatible with the
[Front-End-Monorepo](https://github.com/zooniverse/front-end-monorepo) classifier.

This is how the FEM-Compatible Project Builder works:
- The Project Builder now _automatically switches_ between PFE-compatible pages
and FEM-compatible pages, _depending on the project._
- e.g. https://master.pfe-preview.zooniverse.org/lab/1873/workflows/3532 will
give you the FEM-compatible workflow editor, IF project 1873 is configured to
use FEM. Otherwise, you'll get the PFE-compatible workflow editor.
- The lynchpin for this behaviour is `FEMLabRouter`, which checks for FEM
compatibility and performs the component switching.
- Any code that's not the FEMLabRouter is based on the original Project Builder
in the adjacent `./lab` folder. Compare the modifications by running, e.g.
`diff app/pages/lab/workflow.cjsx app/pages/lab-fem/workflow.cjsx`

At the moment, this is how the FEMLabRouter determine if we should use a
FEM-compatible pages:
- Use FEM Lab if ANY one or more of the following conditions are met:
- Project has `experimental_tools.femLab = true`
- `?femLab=true` query param is set
- Use PFE Lab in all other cases, OR if `?pfeLab=true` query param is set.

Context: in 2019(-ish?), the Zooniverse started to migrate its front end
website/classifier from the Panoptes-Front-End (PFE) codebase to the newer
Front-End-Monorepo (FEM) codebase. As of 2021, the migration is still ongoing.

As we want to transition fully from PFE to FEM, we need to encourage project
owners to build projects that are compatible with FEM. To do so, we need to
give them a Project Builder that's compatible with FEM.

**This is the short-term solution:** a modified _PFE Project Builder_ that
simply strips out (and in some minor cases, modifies) Tasks and features that
are incompatible with FEM projects.

Long-term, we really want to create a new Project Builder interface in the
FEM codebase, so we can retire the PFE codebase altogether. But that's outside
the scope of this readme.
140 changes: 140 additions & 0 deletions app/pages/lab-fem/_classifier/tasks/shortcut/editor.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import PropTypes from 'prop-types';
shaunanoordin marked this conversation as resolved.
Show resolved Hide resolved
import React from 'react';
import AutoSave from '../../../../../components/auto-save';
import handleInputChange from '../../../../../lib/handle-input-change';
import Shortcut from '../../../../../classifier/tasks/shortcut';

export default class ShortcutEditor extends React.Component {
constructor(props) {
super(props);
this.addAnswer = this.addAnswer.bind(this);
this.toggleShortcut = this.toggleShortcut.bind(this);
}

toggleShortcut(e) {
let nextTaskID;

if (e.target.checked) {
const taskCount = Object.keys(this.props.workflow.tasks).length;

let taskIDNumber = -1;

while (!((nextTaskID != null) && !(nextTaskID in this.props.workflow.tasks))) {
taskIDNumber += 1;
nextTaskID = `T${taskCount + taskIDNumber}`;
}

const changes = {};
changes[`tasks.${nextTaskID}`] = Shortcut.getDefaultTask(this.props.task.question);

this.props.task.unlinkedTask = nextTaskID;
this.props.workflow.update(changes);
this.addAnswer();
} else {
delete this.props.workflow.tasks[this.props.task.unlinkedTask];
delete this.props.task.unlinkedTask;
this.props.workflow.update('tasks');
}
}

addAnswer() {
this.props.workflow.tasks[this.props.task.unlinkedTask].answers.push({ label: 'Nothing Here' });
this.props.workflow.update('tasks');
}

removeChoice(index) {
if (this.props.task) {
const answers = this.props.workflow.tasks[this.props.task.unlinkedTask].answers;
answers.splice(index, 1);
if (answers.length === 0) {
delete this.props.workflow.tasks[this.props.task.unlinkedTask];
delete this.props.task.unlinkedTask;
}
}
this.props.workflow.update('tasks');
}

render() {
let shortcuts;
if (this.props.task) {
shortcuts = this.props.workflow.tasks[this.props.task.unlinkedTask];
}
const handleChange = handleInputChange.bind(this.props.workflow);
const children = React.Children.map(this.props.children, (child) => {
return React.cloneElement(child);
});

return (
<div>

{children}
<hr />

<div className="deprecated">
<label htmlFor="shortcut" title="Shortcut Options to End Classification">
<AutoSave resource={this.props.workflow}>
<span className="form-label">Shortcut Option</span>{' '}
<input id="shortcut" type="checkbox" checked={shortcuts !== undefined} onChange={this.toggleShortcut} />
</AutoSave>
</label>

<br />

<small className="form-help">
Give volunteers the choice to skip to the end
of a classification if one of the following options is selected.
</small>

{shortcuts && (
<div className="workflow-task-editor-choices">
{shortcuts.answers.map((shortcut, index) => {
if (shortcut._key === undefined) { shortcut._key = Math.random(); }
return (
<div key={shortcut._key} className="workflow-choice-editor">
<AutoSave resource={this.props.workflow}>
<textarea name={`tasks.${this.props.task.unlinkedTask}.answers.${index}.label`} value={shortcut.label} onChange={handleChange} />
</AutoSave>

<AutoSave resource={this.props.workflow}>
<button type="button" className="workflow-choice-remove-button" title="Remove choice" onClick={this.removeChoice.bind(this, index)}>&times;</button>
</AutoSave>
</div>
);
})}

<AutoSave resource={this.props.workflow}>
<button type="button" className="workflow-choice-add-button" title="Add Shortcut" onClick={this.addAnswer}>+</button>
</AutoSave>

</div>
)}
</div>

{' '}
</div>

);
}

}

ShortcutEditor.propTypes = {
children: PropTypes.node,
task: PropTypes.shape(
{
question: PropTypes.string,
unlinkedTask: PropTypes.string
}
),
workflow: PropTypes.shape(
{
tasks: PropTypes.object,
update: PropTypes.func
}
)
};

ShortcutEditor.defaultProps = {
task: { },
workflow: { }
};
29 changes: 29 additions & 0 deletions app/pages/lab-fem/fem-lab-router.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react'

import Workflow from '../lab/workflow'
import FEMWorkflow from './workflow'

/**
The Front End Monorepo Lab (Project Builder) Router returns a different
component, depending on the path AND whether or not the associated Zooniverse
Project is FEM-compatible.
*/
export default function FEMLabRouter (props) {
// TODO: the "is it FEM-compatible?" check is pretty arbitrary, as of
// 2 Nov 2011. We need to come back to this once we have a better foundational
// solution to figuring out whether a project is FEM-compatible.
const thisProjectUsesFEM = ( // Use FEM-compatible pages if...
props?.project?.experimental_tools?.includes('femLab') // ...the project has the femLab experimental tool
|| !!props?.location?.query?.femLab // ...OR ?femLab=true query param is set
shaunanoordin marked this conversation as resolved.
Show resolved Hide resolved
) && !props?.location?.query?.pfeLab // ...UNLESS ?pfeLab=true query param is set

const thisRoute = props?.routes?.map(r=>r.path).join('/').replace('//', '/') || ''
switch (thisRoute) {
case '/lab/:projectID/workflows/:workflowID': // WARNING: assumes there are no child routes.
return (thisProjectUsesFEM)
? <FEMWorkflow {...props} />
: <Workflow {...props} />
}

return <div>ERROR: This path is not registered in the FEMLabRouter.</div>
}
Loading