Não estamos mais desenvolvendo ativamente recursos para este aplicativo. PRs serão aceitos para correções de bugs, traduções e atualizações de conteúdo. O desenvolvimento ativo de recursos está acontecendo em https://github.com/zooniverse/front-end-monorepo/
Para evitar a instalação do Node.js ou qualquer outra dependência, você pode executar tudo com Docker e Docker Compose.
docker-compose build
criará uma imagem Docker local e executará npm ci
. Execute isso sempre que alterar as dependências em package.json
.
docker-compose up
inicia um servidor web de desenvolvimento que escuta na porta 3735.
docker-compose down
interrompe o servidor de desenvolvimento.
docker-compose run --rm shell
inicia um contêiner executando um shell, por exemplo. para execução de testes.
Certifique-se de ter o Nó 8 e npm
5 ou superior. É recomendado que você gerencie suas instalações do Node com nvm .
npm ci
instala dependências.
npm start
cria e executa o site localmente.
npm ci --legacy-peer-deps
. Consulte a edição 6155 para obter mais detalhes.
A raiz /
é redirecionada para www.zooniverse.org porque este aplicativo frontend não é mais usado para a página inicial. Aponte seu navegador para um subcaminho para visualizar este aplicativo executado localmente.
Abra o navegador de sua preferência e acesse https://localhost:3735/lab
Se quiser fazer login por meio da API Panoptes e visualizar páginas autenticadas, você precisará configurar e usar https://local.zooniverse.org:3735/lab
em vez de usar localhost:3735. Caso contrário, você encontrará erros de CORS. (Você precisa adicionar o nome do host ao seu arquivo hosts, apontando para local. As instruções estão em nosso Stackoverflow.)
Solução de problemas: o navegador bloqueia o site local
O problema: ao tentar visualizar localhost:3735 ou local.zooniverse.org:3735, meu navegador me interrompe e mostra uma tela de aviso.
Erros de exemplo: “Sua conexão não é privada/NET::ERR_CERT_AUTHORITY_INVALID” no Chrome 104; "Aviso: risco potencial de segurança à frente" no Firefox 103; “Esta conexão não é privada” no Safari 15.4.
A causa: o servidor web local está executando HTTPS e usando um certificado autoassinado. Os navegadores modernos consideram esses certificados pouco confiáveis e um possível indicador de um ataque man-in-the-middle.
A(s) solução(ões):
thisisunsafe
) em qualquer lugar da janela para ignorar temporariamente o aviso; ouO aplicativo pode ser configurado usando as seguintes variáveis de ambiente:
NODE_ENV
- define o ambiente do código e determina se quaisquer otimizações de produção devem ser aplicadas ao código empacotado e qual conjunto de padrões aplicar, por exemplo, URL do host da API, URL do host do Talk, etc.PANOPTES_API_APPLICATION
- define o ID do aplicativo a ser usado ao fazer solicitações de autenticação para a API Panoptes. O padrão é aquele definido por NODE_ENV
.PANOPTES_API_HOST
- define a URL da instância da API Panoptes. O padrão é aquele definido por NODE_ENV
.STAT_HOST
- define a URL da instância da API Stats. O padrão é aquele definido por NODE_ENV
.SUGAR_HOST
- define a URL da instância da API Sugar. O padrão é aquele definido por NODE_ENV
.TALK_HOST
- define a URL da instância da API Talk. O padrão é aquele definido por NODE_ENV
. scripts
package.json
; para substituí-los, você precisará modificar package.json
.NODE_ENV
, consulte config.js
em panoptes-javascript-client.Novos PRs do GitHub de dentro da organização Zooniverse serão organizados por Jenkins como parte do processo de CI. Assim que o CI terminar, suas alterações deverão ser testadas em https://pr-{PR-Number}.pfe-preview.zooniverse.org. Jenkins às vezes atinge o tempo limite antes de terminar a construção. Se uma compilação de PR falhar, use o link para Jenkins (do seu PR) para fazer login e tente reiniciar a compilação.
Para testar com dados de produção, você pode adicionar env=production
ao seu URL de desenvolvimento, por exemplo, localhost:3735/projects?env=production
. Observe que ele é removido a cada atualização da página.
Todas as coisas boas estão em ./app . Comece em ./app/main.cjsx
Comparamos nosso código JavaScript com uma versão modificada do guia de estilo AirBnB. Por favor, lint suas alterações com eslint, usando o arquivo .eslintrc na raiz deste repositório. Se você tiver alguma dúvida, sinta-se à vontade para nos perguntar no GitHub.
Durante a edição, faça o possível para seguir as convenções de estilo e arquitetura já utilizadas pelo projeto. A base de código é grande e os estilos evoluíram durante seu desenvolvimento. Dê uma olhada em zooniverse/front-end-monorepo para ter uma ideia de nossas convenções para organização de componentes.
Experimente npm ci
para atualizar suas dependências. E leia os avisos, eles devem informar se você está usando a versão errada do Node ou npm ou se está faltando alguma dependência. Se você usar docker-compose
para construir e testar o site, não deverá ter problemas com a versão do Node, mas docker-compose build
construirá uma nova imagem com um novo npm ci
.
Se você escrever um novo componente, escreva um teste. Cada componente deve ter seu próprio arquivo .spec.js
. O executor de teste é Mocha e Enzyme está disponível para testar componentes React. Mocha gera um erro ( Illegal import declaration
) ao compilar arquivos coffeescript que contêm instruções de importação ES6 com strings de modelo. Converta essas importações em instruções require
. Você pode executar os testes com npm test
.
A implantação é feita pelo Github Action.
Ao abrir solicitações pull, uma ação do Github é acionada para implantar em um local de teste da filial. O local de armazenamento do blob depende do número da solicitação pull, por exemplo, https://pr-5926.pfe-preview.zooniverse.org
.
No push to master, uma ação do Github é acionada para implantar no master staging encontrado em https://master.pfe-preview.zooniverse.org
.
As implantações de produção são acionadas por uma atualização para a qual a tag production-release
está apontada. Esta tag deve ser atualizada por meio de operações de chat e, em seguida, será executada uma ação do Github que cria e carrega os arquivos para nosso provedor de nuvem encontrado em https://www.zooniverse.org
. A implantação de produção pode ser executada ad hoc na guia de ações conforme necessário se você tiver as permissões apropriadas no repositório, mas faça isso apenas em caso de emergência.
Todas as coisas relacionadas ao classificador.
Componentes relacionados a coleções.
Diversos componentes genéricos e reutilizáveis.
O material de layout no nível do aplicativo vai aqui. Se afetar o cabeçalho principal do site, o rodapé do site principal ou o layout do conteúdo principal do site, é aqui que ele reside.
Funções e dados individuais que são reutilizados entre componentes.
É aqui que reside a maior parte do aplicativo. Idealmente, cada rota aponta para um componente da página responsável por buscar dados e tratar quaisquer ações que o usuário possa realizar nesses dados. Esse componente de página usa esses dados para renderizar a IU com componentes burros, transmitindo ações conforme necessário.
Originalmente planejado para conter componentes isolados que não seriam reutilizados em lugar nenhum. Provavelmente pertencem mais perto de onde são realmente usados.
Visualizações de assunto (TODOC: Como isso está relacionado ao Talk/coleções?)
Componentes relacionados à conversa.
Os arquivos aqui serão copiados para o diretório de saída durante a construção.
Cada classe de componente de tarefa deve ter alguns componentes estáticos:
Summary
: Mostra o resumo pós-classificação da anotação das tarefas.
Editor
: o componente usado para editar a tarefa de fluxo de trabalho no construtor de projetos.
Existem também alguns ganchos disponíveis no restante da interface de classificação, se a tarefa precisar ser renderizada fora da área de tarefa.
BeforeSubject
: Conteúdo HTML que aparecerá antes da imagem do assunto durante a tarefa.
InsideSubject
: Conteúdo SVG que aparecerá sobre a imagem do assunto durante a tarefa.
Conteúdo HTML AfterSubject
para aparecer após a imagem do assunto durante a tarefa.
Esses ganchos podem ser prefixados com Persist
, o que fará com que eles apareçam com a tarefa e persistam mesmo depois que o usuário passar para a próxima tarefa.
Persist{Before,After}Task
funciona da mesma maneira, mas para a área de tarefas. Ganchos não persistentes são desnecessários para a área de tarefas.
Cada componente também precisa de alguns métodos estáticos:
getDefaultTask
: Retorna a descrição da tarefa a ser usada como padrão quando um usuário adiciona a tarefa a um fluxo de trabalho no construtor de projeto.
getTaskText
: Dada uma tarefa, retorna uma descrição de texto básica da tarefa (por exemplo, a pergunta em uma tarefa de pergunta, a instrução em uma tarefa de desenho, etc.)
getDefaultAnnotation
: a anotação a ser gerada quando o classificador inicia a tarefa
isAnnotationComplete
: Dada uma tarefa e uma anotação, determina se o classificador permitirá ou não que o usuário passe para a próxima tarefa.
testAnnotationQuality
: Dada a anotação do usuário e uma anotação "padrão ouro" conhecida para a mesma tarefa, isso retorna um número entre 0 (totalmente errado) e 1 (totalmente correto) indicando o quão próxima a anotação do usuário está do padrão.
Certifique-se de chamar this.props.onChange
com a tarefa atualizada quando ela for alterada.
Alguns métodos estáticos, chamados a partir do componente MarkInitializer
, que controla os valores da marca durante a primeira ação de criação da marca pelo usuário:
defaultValues
: apenas alguns padrões para a marca.
initStart
: Para cada mousedown/touchstart até que isComplete
retorne verdadeiro, retorne os valores da marca.
initMove
: Para cada mousemove/touchmove, retorne novos valores para a marca.
initRelease
: Para cada mouseup/touchend, retorne novos valores para a marca.
isComplete
: A marca está completa? Algumas marcas exigem múltiplas interações antes que o inicializador desista do controle.
initValid
: Se uma marca for inválida (por exemplo, um retângulo com largura ou altura zero), ela será destruída automaticamente.
Alguns componentes auxiliares são o DrawingToolRoot
que manipula estados selecionados/desativados e renderiza pop-ups de subtarefas, e DeleteButton
e DragHandle
, que renderizam controles consistentes para ferramentas de desenho. Há também uma função deleteIfOutOfBounds
que deve ser chamada após qualquer arrastamento de marca inteira.
O React exige que cada componente em um array tenha uma key
exclusiva para irmãos. Ao renderizar matrizes de coisas que não possuem IDs (anotações, respostas), forneça uma propriedade _key
aleatória se ela não existir. Certifique-se de que as propriedades com prefixo de sublinhado não sejam persistidas. Isso é automático com a classe JSONAPIClient.Model
.
< ul >
{ for item in things
item . _key ?= Math . random ()
< li key = { item . _key }>{ item . label }</ li >}
</ ul >
Existem alguns legal componentes infelizes (em retrospectiva) para ajudar com valores assíncronos. Eles assumem uma função como @props.children
, que parece um pouco arrogante, mas funciona muito bem. A maioria dos dados solicitados são armazenados em cache localmente, portanto, geralmente são seguros, mas se você notar a mesma solicitação sendo feita várias vezes seguidas, este é um bom lugar para começar a procurar chamadas redundantes. Aqui está um exemplo de nova renderização quando um projeto é alterado, o que resulta na verificação dos proprietários do projeto.
< 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 >
Não escreva novo código usando ChangeListener
ou PromiseRenderer
.
Se for razoável, substitua as instâncias ChangeListener
e PromiseRenderer
pelo estado do componente no código em que você trabalha. É mais detalhado, mas é mais legível e nos deixará mais perto da renderização no servidor no futuro.
Inclua qualquer CSS necessário para a funcionalidade de um componente em linha com o componente; caso contrário, mantenha-o em um arquivo separado, um por componente. Para um determinado componente, escolha um nome de classe de nível superior exclusivo para esse componente e aninhe as classes filhas nele. Mantenha estilos e variáveis base comuns em common.styl . Formatação da caneta: Sim, dois pontos, sem ponto e vírgula, sem colchetes. @extends
para cima, depois propriedades (em ordem alfabética) e depois seletores descendentes. Prefira o uso de display: flex
e flex-wrap: wrap
para consultas de mídia explícitas sempre que possível.
Nosso CSS ficou muito grande, então estamos testando o BEM para organização.
// <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 do coffeescript para o ES6. Isso pode ser feito de forma incremental, escrevendo um novo componente ou reescrevendo um componente existente no ES6. Algumas dicas devem ser mencionadas:
O operador existencial não existe no ES6. Compare explicitamente com null
ou use !!thing
se precisar ser verdadeiro.
Classes ES6 nativas são preferidas, pois React.createClass()
está obsoleto, no entanto, se o componente existente depende de mixins, considere usar createReactClass()
.
Os mixins estão obsoletos e não são suportados por classes nativas, portanto, não os utilize em novos componentes.
Use crases para importar componentes ES6 para componentes coffeescript:
`import NewComponent from './new-component'`
Um arquivo de configuração ESLint é configurado na raiz do repositório para você usar com seu editor de texto para usar o ES6 e usar o guia de estilo React do Airbnb.
Um guia sobre como escrever classes nativas versus usar createReactClass()
Consulte a biblioteca panoptes-client : https://www.npmjs.com/package/panoptes-client.
O formato do valor de uma anotação depende da tarefa usada para gerá-la.
único: O índice da resposta escolhida.
múltiplo: Uma matriz dos índices das respostas escolhidas (na ordem em que foram escolhidas).
desenho: Uma matriz de marcas de ferramentas de desenho (cujas descrições seguem abaixo).
pesquisa: Uma série de identificações como objetos. Cada identificação é uma choice
(o ID do animal identificado) e answers
, um objeto. Cada chave nas answers
é o ID de uma pergunta. Se essa pergunta permitir múltiplas respostas, o valor será uma matriz de IDs de resposta, caso contrário, apenas um único ID de resposta.
crop: Um objeto contendo x
, y
, width
e height
da região cortada.
texto: uma string.
combo: uma submatriz de anotações.
menu suspenso: uma matriz de objetos onde o value
da string se refere à resposta à pergunta correspondente e a option
booleana indica que a resposta estava na lista de opções.
Todas as coordenadas são relativas ao canto superior esquerdo da imagem.
Todas as marcas possuem uma tool
, que é o índice da ferramenta (ex. workflow.tasks.T0.tools[0]
) usada para fazer a marca.
Todas as marcas contêm um frame
, que é o índice do quadro do assunto (por exemplo, subject.locations[0]
) no qual a marca foi feita.
Se tarefas details
forem definidas para uma ferramenta, suas marcas terão uma matriz details
de subclassificações (cada uma com um value
, seguindo as descrições acima).
Os valores da anotação do desenho são os seguintes:
ponto: as coordenadas x
e y
.
linha: as coordenadas inicial ( x1
, y1
) e final ( x2
, y2
).
polígono: uma matriz de objetos, cada um contendo as y
x
de um vértice. Se a marca não foi explicitamente fechada pelo usuário, auto_closed
é true
.
retângulo: as coordenadas x
, y
do ponto superior esquerdo do retângulo junto com sua width
e height
.
círculo: as coordenadas x
e y
do centro do círculo e seu raio r
.
elipse: As coordenadas x
e y
do centro da elipse, seus raios rx
e ry
e o angle
de rx
em relação ao eixo x em graus (sentido anti-horário a partir de 3:00).
bezier: O mesmo que polígono, mas cada ponto indexado ímpar é a coordenada do ponto de controle de uma curva quadrática de Bézier.
coluna: O pixel x
mais à esquerda e a width
da seleção da coluna.
Obrigado ao BrowserStack por apoiar o código aberto e nos permitir testar este projeto em múltiplas plataformas.