Ya no estamos desarrollando activamente funciones para esta aplicación. Se aceptarán relaciones públicas para correcciones de errores, traducciones y actualizaciones de contenido. El desarrollo activo de funciones se está llevando a cabo en https://github.com/zooniverse/front-end-monorepo/
Para evitar tener que instalar Node.js o cualquier otra dependencia, puedes ejecutar todo con Docker y Docker Compose.
docker-compose build
creará una imagen de Docker local y ejecutará npm ci
. Ejecute esto cada vez que cambie las dependencias en package.json
.
docker-compose up
inicia un servidor web de desarrollo que escucha en el puerto 3735.
docker-compose down
detiene el servidor de desarrollo.
docker-compose run --rm shell
inicia un contenedor que ejecuta un shell, por ejemplo. para ejecutar pruebas.
Asegúrese de tener el Nodo 8 y npm
5 o superior. Se recomienda que administre sus instalaciones de Node con nvm .
npm ci
instala dependencias.
npm start
construye y ejecuta el sitio localmente.
npm ci --legacy-peer-deps
. Consulte el número 6155 para obtener más detalles.
La raíz /
se redirige a www.zooniverse.org porque esta aplicación de interfaz ya no se utiliza para la página de inicio. Dirija su navegador a una ruta secundaria para ver cómo esta aplicación se ejecuta localmente.
Abra el navegador web de su elección y vaya a https://localhost:3735/lab
Si desea iniciar sesión a través de la API de Panoptes y ver páginas autenticadas, deberá configurar y usar https://local.zooniverse.org:3735/lab
en lugar de usar localhost:3735. De lo contrario, se encontrará con errores CORS. (Debe agregar el nombre de host a su archivo de hosts, apuntando a local. Las instrucciones están en nuestro Stackoverflow).
Solución de problemas: el navegador web bloquea el sitio web local
El problema: cuando intento ver localhost:3735 o local.zooniverse.org:3735, mi navegador web me detiene y muestra una pantalla de advertencia.
Errores de ejemplo: "Su conexión no es privada/NET::ERR_CERT_AUTHORITY_INVALID" en Chrome 104; "Advertencia: riesgo potencial de seguridad en el futuro" en Firefox 103; "Esta conexión no es privada" en Safari 15.4.
La causa: el servidor web local ejecuta HTTPS y utiliza un certificado autofirmado. Los navegadores web modernos consideran que estos certificados no son muy confiables y son un posible indicador de un ataque de intermediario.
La(s) solución(es):
thisisunsafe
) en cualquier lugar de la ventana para omitir temporalmente la advertencia; oLa aplicación se puede configurar utilizando las siguientes variables de entorno:
NODE_ENV
: establece el entorno del código y determina si se aplicará alguna optimización de producción al código empaquetado y qué conjunto de valores predeterminados se aplicará, por ejemplo, la URL del host de API, la URL del host de Talk, etc.PANOPTES_API_APPLICATION
: establece el ID de la aplicación que se utilizará al realizar solicitudes de autenticación a la API de Panoptes. El valor predeterminado es el establecido por NODE_ENV
.PANOPTES_API_HOST
: establece la URL de la instancia de la API de Panoptes. El valor predeterminado es el establecido por NODE_ENV
.STAT_HOST
: establece la URL de la instancia de la API de estadísticas. El valor predeterminado es el establecido por NODE_ENV
.SUGAR_HOST
: establece la URL de la instancia de Sugar API. El valor predeterminado es el establecido por NODE_ENV
.TALK_HOST
: establece la URL de la instancia de Talk API. El valor predeterminado es el establecido por NODE_ENV
. scripts
package.json
; Para anularlos, deberá modificar package.json
.NODE_ENV
, consulte config.js
en panoptes-javascript-client.Jenkins organizará nuevas relaciones públicas de GitHub dentro de la organización Zooniverse como parte del proceso de CI. Una vez que finalice CI, sus cambios deben realizarse en https://pr-{PR-Number}.pfe-preview.zooniverse.org. A veces, Jenkins se queda sin tiempo antes de terminar la construcción. Si falla una compilación de PR, use el enlace a Jenkins (de su PR) para iniciar sesión e intentar reiniciar la compilación.
Para realizar pruebas con datos de producción, puede agregar env=production
a su URL de desarrollo, por ejemplo localhost:3735/projects?env=production
. Tenga en cuenta que se elimina cada vez que se actualiza la página.
Todo lo bueno está en ./app . Comience en ./app/main.cjsx
Comparamos nuestro código JavaScript con una versión modificada de la guía de estilo de AirBnB. Vincule sus cambios con eslint, utilizando el archivo .eslintrc en la raíz de este repositorio. Si tiene alguna pregunta, no dude en consultarnos en GitHub.
Mientras edita, haga todo lo posible por seguir las convenciones de estilo y arquitectura que ya se utilizan en el proyecto. La base del código es amplia y los estilos han evolucionado durante su desarrollo. Eche un vistazo a zooniverse/front-end-monorepo para tener una idea de nuestras convenciones para organizar componentes.
Pruebe npm ci
para actualizar sus dependencias. Y lea las advertencias, deberían indicarle si está utilizando la versión incorrecta de Node o npm o si le falta alguna dependencia. Si usa docker-compose
para construir y probar el sitio, no debería tener ningún problema con la versión de Node, pero docker-compose build
creará una nueva imagen con un npm ci
nuevo.
Si escribe un nuevo componente, escriba una prueba. Cada componente debe tener su propio archivo .spec.js
. El ejecutor de pruebas es Mocha y Enzyme está disponible para probar los componentes de React. Mocha arroja un error ( Illegal import declaration
) al compilar archivos CoffeeScript que contienen declaraciones de importación de ES6 con cadenas de plantilla. Convierta estas importaciones para require
declaraciones. Puede ejecutar las pruebas con npm test
.
La implementación está a cargo de Github Action.
Al abrir solicitudes de extracción, se activa una acción de Github para implementarla en una ubicación provisional de sucursal. La ubicación de almacenamiento de blobs depende del número de solicitud de extracción, por ejemplo, https://pr-5926.pfe-preview.zooniverse.org
.
Al enviar al maestro, se activa una acción de Github para implementarla en la etapa maestra que se encuentra en https://master.pfe-preview.zooniverse.org
.
Las implementaciones de producción se activan mediante una actualización a la que apunta la etiqueta production-release
. Esta etiqueta debe actualizarse a través de operaciones de chat y luego se ejecutará una acción de Github que compila y carga los archivos a nuestro proveedor de nube que se encuentra en https://www.zooniverse.org
. La implementación de producción se puede ejecutar ad hoc en la pestaña de acciones según sea necesario si tiene los permisos adecuados en el repositorio, pero hágalo solo en caso de emergencia.
Todo lo relacionado con clasificadores.
Componentes relacionados con colecciones.
Componentes varios genéricos y reutilizables.
El diseño a nivel de aplicación va aquí. Si afecta el encabezado del sitio principal, el pie de página del sitio principal o el diseño del contenido del sitio principal, aquí es donde reside.
Funciones y datos individuales que se reutilizan entre componentes.
Aquí es donde reside la mayor parte de la aplicación. Idealmente, cada ruta apunta a un componente de la página responsable de obtener datos y manejar cualquier acción que el usuario pueda realizar con esos datos. Ese componente de página utiliza esos datos para representar la interfaz de usuario con componentes tontos, transmitiendo acciones según sea necesario.
Originalmente destinado a contener componentes aislados que en realidad no se reutilizarían en ningún lado. Probablemente pertenezcan más cerca de donde realmente se usan.
Vistas de temas (TODOC: ¿Cómo se relaciona esto con Talk/colecciones?)
Componentes relacionados con la conversación.
Los archivos aquí se copiarán en el directorio de salida durante la compilación.
Cada clase de componente de tarea debe tener un par de componentes estáticos:
Summary
: Muestra el resumen posterior a la clasificación de la anotación de las tareas.
Editor
: el componente utilizado para editar la tarea de flujo de trabajo en el generador de proyectos.
También hay algunos enlaces disponibles en el resto de la interfaz de clasificación, si la tarea necesita representarse fuera del área de tareas.
BeforeSubject
: contenido HTML que aparecerá antes de la imagen del sujeto durante la tarea.
InsideSubject
: contenido SVG que aparecerá sobre la imagen del sujeto durante la tarea.
AfterSubject
El contenido HTML aparecerá después de la imagen del sujeto durante la tarea.
Estos ganchos pueden tener el prefijo Persist
, lo que hará que aparezcan con la tarea y persistan incluso después de que el usuario haya pasado a la siguiente tarea.
Persist{Before,After}Task
funciona de la misma manera, pero para el área de tareas. Los ganchos no persistentes son innecesarios para el área de trabajo.
Cada componente también necesita algunos métodos estáticos:
getDefaultTask
: devuelve la descripción de la tarea que se utilizará como predeterminada cuando un usuario agrega la tarea a un flujo de trabajo en el generador de proyectos.
getTaskText
: dada una tarea, esto devuelve una descripción de texto básica de la tarea (por ejemplo, la pregunta en una tarea de preguntas, la instrucción en una tarea de dibujo, etc.)
getDefaultAnnotation
: la anotación que se generará cuando el clasificador comience la tarea.
isAnnotationComplete
: dada una tarea y una anotación, esto determina si el clasificador permitirá o no al usuario pasar a la siguiente tarea.
testAnnotationQuality
: dada la anotación del usuario y una anotación "estándar de oro" conocida para la misma tarea, esto devuelve un número entre 0 (totalmente incorrecto) y 1 (totalmente correcto) que indica qué tan cerca está la anotación del usuario del estándar.
Asegúrese de llamar a this.props.onChange
con la tarea actualizada cuando cambie.
Algunos métodos estáticos, llamados desde el componente MarkInitializer
, que controla los valores de la marca durante la primera acción de creación de marca del usuario:
defaultValues
: solo algunos valores predeterminados para la marca.
initStart
: por cada pulsación del mouse/inicio táctil hasta que isComplete
devuelva verdadero, devuelve los valores de la marca.
initMove
: por cada movimiento del mouse/movimiento táctil, devuelve nuevos valores para la marca.
initRelease
: por cada movimiento del mouse/toque, devuelve nuevos valores para la marca.
isComplete
: ¿Está completa la marca? Algunas marcas requieren múltiples interacciones antes de que el inicializador ceda el control.
initValid
: si una marca no es válida (por ejemplo, un rectángulo con ancho o alto cero), se destruirá automáticamente.
Un par de componentes auxiliares son DrawingToolRoot
, que maneja estados seleccionados/deshabilitados y genera ventanas emergentes de subtareas, y DeleteButton
y DragHandle
, que representan controles consistentes para herramientas de dibujo. También hay una función deleteIfOutOfBounds
que debe llamarse después de cualquier arrastre de marca completa.
React requiere que cada componente de una matriz tenga una key
única para hermanos. Al representar matrices de elementos que no tienen ID (anotaciones, respuestas), proporcione una propiedad _key
aleatoria si no existe. Asegúrese de que las propiedades con prefijo de guión bajo no persistan. Eso es automático con la clase JSONAPIClient.Model
.
< ul >
{ for item in things
item . _key ?= Math . random ()
< li key = { item . _key }>{ item . label }</ li >}
</ ul >
hay algunos lindo componentes desafortunados (en retrospectiva) para ayudar con los valores asíncronos. Toman una función como @props.children
, que parece un poco complicada pero funciona bastante bien. La mayoría de los datos solicitados se almacenan en caché localmente, por lo que suelen ser seguros, pero si observa que se realiza la misma solicitud varias veces seguidas, este es un buen lugar para comenzar a buscar llamadas redundantes. A continuación se muestra un ejemplo de cómo volver a renderizar cuando cambia un proyecto, lo que resulta en la verificación de los propietarios del proyecto.
< ChangeListener target = { @props . project }>{ =>
< PromiseRenderer promise = { @props . project . get ( ' owners ' )}>{([owner]) =>
if owner is @props . user
< p > This project is yours.</ p >
else
< p > This project belongs to { owner . display_name }.</ p >
}</ PromiseRenderer >
}</ ChangeListener >
No escriba código nuevo usando ChangeListener
o PromiseRenderer
.
Si es razonable, reemplace las instancias ChangeListener
y PromiseRenderer
con el estado del componente en el código en el que trabaja. Es más detallado, pero es más legible y nos acercará más al renderizado en el servidor en el futuro.
Incluya cualquier CSS necesario para la funcionalidad de un componente en línea con el componente; de lo contrario, manténgalo en un archivo separado, uno por componente. Para un componente determinado, elija un nombre de clase de nivel superior único para ese componente y anide clases secundarias debajo de él. Mantenga variables y estilos base comunes en common.styl . Formato del lápiz: Sí, dos puntos, sin punto y coma, sin llaves. @extends
hacia arriba, luego las propiedades (alfabéticamente), luego los selectores descendientes. Prefiera el uso de display: flex
y flex-wrap: wrap
a consultas de medios explícitas siempre que sea posible.
Nuestro CSS se ha vuelto realmente enorme, por lo que estamos probando BEM para la organización.
// <special-button.styl>
.special-button
background : red
color : white
.special-button__icon
width : 1 em ;
// <special-container.styl>
.special-container
margin : 1 em 1 vw
.special-container__button
border : 1 px solid
Estamos migrando de CoffeeScript a ES6. Esto se puede hacer de forma incremental escribiendo un nuevo componente o reescribiendo un componente existente en ES6. Se deben mencionar algunas trampas:
El operador existencial no existe en ES6. Compárelo explícitamente con null
o use !!thing
si solo necesita ser veraz.
Se prefieren las clases nativas de ES6 ya que React.createClass()
está en desuso; sin embargo, si el componente existente depende de mixins, considere usar createReactClass()
.
Los mixins están en desuso y no son compatibles con clases nativas, por lo que no los utilices en componentes nuevos.
Utilice comillas invertidas para importar componentes de ES6 a componentes de CoffeeScript:
`import NewComponent from './new-component'`
Se configura un archivo de configuración de ESLint en la raíz del repositorio para que lo use con su editor de texto para vincular ES6 y usar la guía de estilo React de Airbnb.
Una guía sobre cómo escribir clases nativas versus usar createReactClass()
Consulte la biblioteca panoptes-client : https://www.npmjs.com/package/panoptes-client.
El formato del valor de una anotación depende de la tarea utilizada para generarla.
single: El índice de la respuesta elegida.
múltiple: una matriz de los índices de las respuestas elegidas (en el orden en que fueron elegidas).
dibujo: una serie de marcas de herramientas de dibujo (cuyas descripciones se detallan a continuación).
encuesta: una serie de identificaciones como objetos. Cada identificación es una choice
(el ID del animal identificado) y answers
, un objeto. Cada clave en answers
es la identificación de una pregunta. Si esa pregunta permite múltiples respuestas, el valor será una matriz de ID de respuesta; de lo contrario, solo una ID de respuesta.
recorte: objeto que contiene x
, y
, width
y height
de la región recortada.
texto: una cadena.
combo: un subconjunto de anotaciones.
desplegable: Una matriz de objetos donde el value
de la cadena hace referencia a la respuesta a la pregunta correspondiente y la option
booleana indica que la respuesta estaba en la lista de opciones.
Todas las coordenadas son relativas a la parte superior izquierda de la imagen.
Todas las marcas tienen una tool
, que es el índice de la herramienta (por ejemplo, workflow.tasks.T0.tools[0]
) utilizada para realizar la marca.
Todas las marcas contienen un frame
, que es el índice del marco del sujeto (por ejemplo, subject.locations[0]
) en el que se realizó la marca.
Si se definen tareas details
para una herramienta, sus marcas tendrán una matriz details
de subclasificaciones (cada una con un value
, siguiendo las descripciones anteriores).
El valor de la anotación del dibujo es el siguiente:
punto: Las y
x
Línea: Las coordenadas inicial ( x1
, y1
) y final ( x2
, y2
).
polígono: una matriz de objetos, cada uno de los cuales contiene las y
x
un vértice. Si el usuario no cerró explícitamente la marca, auto_closed
es true
.
rectángulo: las coordenadas x
, y
del punto superior izquierdo del rectángulo junto con su width
y height
.
círculo: Las coordenadas x
e y
del centro del círculo y su radio r
.
elipse: las coordenadas x
e y
del centro de la elipse, sus radios rx
y ry
, y el angle
de rx
con respecto al eje x en grados (en el sentido contrario a las agujas del reloj desde las 3:00).
Bézier: Lo mismo que el polígono, pero cada punto con índice impar es la coordenada del punto de control de una curva Bézier cuadrática.
columna: el píxel x
más a la izquierda y el width
de la selección de columna.
Gracias a BrowserStack por admitir el código abierto y permitirnos probar este proyecto en múltiples plataformas.