Wir entwickeln keine aktiven Funktionen mehr für diese App. PRs werden für Fehlerbehebungen, Übersetzungen und Inhaltsaktualisierungen akzeptiert. Die aktive Funktionsentwicklung findet unter https://github.com/zooniverse/front-end-monorepo/ statt.
Um die Installation von Node.js oder anderen Abhängigkeiten zu vermeiden, können Sie alles mit Docker und Docker Compose ausführen.
docker-compose build
erstellt ein lokales Docker-Image und führt npm ci
aus. Führen Sie dies aus, wenn Sie Abhängigkeiten in package.json
ändern.
docker-compose up
startet einen Entwicklungs-Webserver, der Port 3735 überwacht.
docker-compose down
stoppt den Entwicklungsserver.
docker-compose run --rm shell
startet einen Container, auf dem eine Shell ausgeführt wird, z. zum Ausführen von Tests.
Stellen Sie sicher, dass Sie Knoten 8 und npm
5 oder höher haben. Es wird empfohlen, Ihre Node-Installationen mit nvm zu verwalten.
npm ci
installiert Abhängigkeiten.
npm start
erstellt die Site und führt sie lokal aus.
npm ci --legacy-peer-deps
ausführen. Weitere Informationen finden Sie in Ausgabe 6155.
Der Root /
wird auf www.zooniverse.org umgeleitet, da diese Frontend-App nicht mehr für die Homepage verwendet wird. Zeigen Sie mit Ihrem Browser auf einen Unterpfad, um zu sehen, wie diese App lokal ausgeführt wird.
Öffnen Sie den Webbrowser Ihrer Wahl und gehen Sie zu https://localhost:3735/lab
Wenn Sie sich über die Panoptes-API anmelden und authentifizierte Seiten anzeigen möchten, müssen Sie https://local.zooniverse.org:3735/lab
anstelle von localhost:3735 einrichten und verwenden. Andernfalls treten CORS-Fehler auf. (Sie müssen den Hostnamen zu Ihrer Hosts-Datei hinzufügen und auf „local“ verweisen. Anweisungen finden Sie in unserem Stackoverflow.)
Fehlerbehebung: Webbrowser blockiert lokale Website
Das Problem: Beim Versuch, localhost:3735 oder local.zooniverse.org:3735 anzuzeigen, stoppt mein Webbrowser und zeigt einen Warnbildschirm an.
Beispielfehler: „Ihre Verbindung ist nicht privat / NET::ERR_CERT_AUTHORITY_INVALID“ auf Chrome 104; „Warnung: Potenzielles Sicherheitsrisiko voraus“ auf Firefox 103; „Diese Verbindung ist nicht privat“ in Safari 15.4.
Die Ursache: Der lokale Webserver führt HTTPS aus und verwendet ein selbstsigniertes Zertifikat. Moderne Webbrowser betrachten diese Zertifikate als sehr unglaubwürdig und als möglichen Indikator für einen Man-in-the-Middle-Angriff.
Die Lösung(en):
thisisunsafe
) ein, um die Warnung vorübergehend zu umgehen. oderDie App kann mithilfe der folgenden Umgebungsvariablen konfiguriert werden:
NODE_ENV
– legt die Umgebung des Codes fest und bestimmt, ob Produktionsoptimierungen auf den gebündelten Code angewendet werden sollen und welche Standardeinstellungen z. B. für API-Host-URL, Talk-Host-URL usw. angewendet werden sollen.PANOPTES_API_APPLICATION
– legt die Anwendungs-ID fest, die bei Authentifizierungsanfragen an die Panoptes-API verwendet werden soll. Standardmäßig wird der durch NODE_ENV
festgelegte Wert verwendet.PANOPTES_API_HOST
– legt die URL der Instanz der Panoptes-API fest. Standardmäßig wird der durch NODE_ENV
festgelegte Wert verwendet.STAT_HOST
– legt die URL der Instanz der Statistik-API fest. Standardmäßig wird der durch NODE_ENV
festgelegte Wert verwendet.SUGAR_HOST
– legt die URL der Instanz der Sugar API fest. Standardmäßig wird der durch NODE_ENV
festgelegte Wert verwendet.TALK_HOST
– legt die URL der Instanz der Talk-API fest. Standardmäßig wird der durch NODE_ENV
festgelegte Wert verwendet. scripts
package.json
festgelegt. Um sie zu überschreiben, müssen Sie package.json
ändern.NODE_ENV
festgelegten Standardwerten finden Sie in config.js
in panoptes-javascript-client.Neue GitHub-PRs aus der Zooniverse-Organisation werden von Jenkins im Rahmen des CI-Prozesses bereitgestellt. Sobald CI abgeschlossen ist, sollten Ihre Änderungen unter https://pr-{PR-Number}.pfe-preview.zooniverse.org bereitgestellt werden. Bei Jenkins tritt manchmal eine Zeitüberschreitung auf, bevor der Build abgeschlossen ist. Wenn ein PR-Build fehlschlägt, melden Sie sich über den Link zu Jenkins (von Ihrem PR) an und versuchen Sie, den Build neu zu starten.
Zum Testen mit Produktionsdaten können Sie env=production
zu Ihrer Entwicklungs-URL hinzufügen, z. B. localhost:3735/projects?env=production
. Beachten Sie, dass es bei jeder Seitenaktualisierung entfernt wird.
All die guten Sachen sind in ./app . Beginnen Sie bei ./app/main.cjsx
Wir vergleichen unseren JavaScript-Code mit einer modifizierten Version des AirBnB-Styleguides. Bitte binden Sie Ihre Änderungen mit eslint, indem Sie die .eslintrc-Datei im Stammverzeichnis dieses Repos verwenden. Wenn Sie Fragen haben, können Sie uns diese gerne auf GitHub stellen.
Achten Sie bei der Bearbeitung darauf, die im Projekt bereits verwendeten Stil- und Architekturkonventionen einzuhalten. Die Codebasis ist umfangreich und die Stile haben sich im Laufe der Entwicklung weiterentwickelt. Werfen Sie einen Blick auf zooniverse/front-end-monorepo, um einen Eindruck von unseren Konventionen für die Organisation von Komponenten zu bekommen.
Probieren Sie npm ci
aus, um Ihre Abhängigkeiten aufzufrischen. Und lesen Sie die Warnungen, sie sollten Ihnen sagen, ob Sie die falsche Version von Node oder npm verwenden oder ob Ihnen Abhängigkeiten fehlen. Wenn Sie docker-compose
zum Erstellen und Testen der Site verwenden, sollten Sie mit der Node-Version keine Probleme haben, aber docker-compose build
erstellt ein neues Image mit einem neuen npm ci
.
Wenn Sie eine neue Komponente schreiben, schreiben Sie einen Test. Jede Komponente sollte über eine eigene .spec.js
Datei verfügen. Der Testläufer ist Mocha und Enzyme steht zum Testen von React-Komponenten zur Verfügung. Mocha gibt beim Kompilieren von Coffeescript-Dateien, die ES6-Importanweisungen mit Vorlagenzeichenfolgen enthalten, einen Fehler aus ( Illegal import declaration
). Konvertieren Sie diese Importe in require
. Sie können die Tests mit npm test
ausführen.
Die Bereitstellung wird von Github Action übernommen.
Beim Öffnen von Pull-Requests wird eine Github-Aktion ausgelöst, um sie an einem Branch-Staging-Standort bereitzustellen. Der Blob-Speicherort hängt von der Pull-Request-Nummer ab, z. B. https://pr-5926.pfe-preview.zooniverse.org
.
Beim Push-to-Master wird eine Github-Aktion ausgelöst, die im Master-Staging unter https://master.pfe-preview.zooniverse.org
bereitgestellt wird.
Produktionsbereitstellungen werden durch ein Update ausgelöst, auf dessen Commit das production-release
-Tag verweist. Dieses Tag sollte über Chat-Operationen aktualisiert werden und dann wird eine Github-Aktion ausgeführt, die die Dateien erstellt und zu unserem Cloud-Anbieter unter https://www.zooniverse.org
hochlädt. Die Produktionsbereitstellung kann bei Bedarf ad hoc in der Registerkarte „Aktionen“ ausgeführt werden, wenn Sie über die entsprechenden Berechtigungen für das Repository verfügen. Tun Sie dies jedoch nur im Notfall.
Alles, was mit Klassifikatoren zu tun hat.
Sammlungsbezogene Komponenten.
Verschiedene generische, wiederverwendbare Komponenten.
Hier finden Sie Layout-Elemente auf App-Ebene. Wenn es sich auf die Kopfzeile der Hauptseite, die Fußzeile der Hauptseite oder das Layout des Inhalts der Hauptseite auswirkt, befindet es sich hier.
Einzelne Funktionen und Daten, die komponentenübergreifend wiederverwendet werden.
Hier befindet sich der Großteil der App. Im Idealfall verweist jede Route auf eine Seitenkomponente, die für das Abrufen von Daten und die Verarbeitung aller Aktionen verantwortlich ist, die der Benutzer an diesen Daten ausführen kann. Diese Seitenkomponente verwendet diese Daten, um die Benutzeroberfläche mit dummen Komponenten darzustellen und leitet bei Bedarf Aktionen weiter.
Ursprünglich zur Aufnahme isolierter Komponenten gedacht, die eigentlich nirgendwo wiederverwendet werden sollten. Diese gehören wahrscheinlich näher an den Ort, an dem sie tatsächlich verwendet werden.
Themenansichten (TODOC: Wie hängt das mit Talk/Sammlungen zusammen?)
Gesprächsbezogene Komponenten.
Die Dateien hier werden während des Builds in das Ausgabeverzeichnis kopiert.
Jede Aufgabenkomponentenklasse sollte einige statische Komponenten haben:
Summary
: Zeigt die Zusammenfassung der Aufgabenanmerkungen nach der Klassifizierung an.
Editor
: Die Komponente, die zum Bearbeiten der Workflow-Aufgabe im Projekt-Builder verwendet wird.
Für den Fall, dass die Aufgabe außerhalb des Aufgabenbereichs gerendert werden muss, stehen auch einige Einbindungen in den Rest der Klassifizierungsschnittstelle zur Verfügung.
BeforeSubject
: HTML-Inhalt, der während der Aufgabe vor dem Betreffbild angezeigt wird.
InsideSubject
: SVG-Inhalt, der während der Aufgabe über dem Betreffbild angezeigt wird.
AfterSubject
HTML-Inhalt, der während der Aufgabe nach dem Betreffbild angezeigt wird.
Diesen Hooks kann Persist
vorangestellt werden, was dazu führt, dass sie mit der Aufgabe angezeigt werden und auch dann bestehen bleiben, wenn der Benutzer mit der nächsten Aufgabe fortgefahren ist.
Persist{Before,After}Task
funktioniert auf die gleiche Weise, jedoch für den Aufgabenbereich. Für den Aufgabenbereich sind nicht persistente Hooks nicht erforderlich.
Jede Komponente benötigt außerdem einige statische Methoden:
getDefaultTask
: Gibt die Aufgabenbeschreibung zurück, die als Standard verwendet werden soll, wenn ein Benutzer die Aufgabe zu einem Workflow im Projekt-Builder hinzufügt.
getTaskText
: Bei einer gegebenen Aufgabe wird eine grundlegende Textbeschreibung der Aufgabe zurückgegeben (z. B. die Frage in einer Frageaufgabe, die Anweisung in einer Zeichenaufgabe usw.).
getDefaultAnnotation
: Die Anmerkung, die generiert werden soll, wenn der Klassifikator mit der Aufgabe beginnt
isAnnotationComplete
: Bei einer gegebenen Aufgabe und einer Anmerkung bestimmt dies, ob der Klassifikator es dem Benutzer ermöglicht, mit der nächsten Aufgabe fortzufahren.
testAnnotationQuality
: Bei der Annotation des Benutzers und einer bekanntermaßen guten „Goldstandard“-Annotation für dieselbe Aufgabe wird eine Zahl zwischen 0 (völlig falsch) und 1 (völlig korrekt) zurückgegeben, die angibt, wie nah die Annotation des Benutzers am Standard ist.
Stellen Sie sicher, dass Sie this.props.onChange
mit der aktualisierten Aufgabe aufrufen, wenn diese sich ändert.
Einige statische Methoden, die von der MarkInitializer
Komponente aufgerufen werden und die Werte der Markierung während der ersten Markierungserstellungsaktion des Benutzers steuern:
defaultValues
: Nur einige Standardwerte für die Markierung.
initStart
: Für jeden Mousedown/Touchstart, bis isComplete
„true“ zurückgibt, werden die Werte für die Markierung zurückgegeben.
initMove
: Für jede Mausbewegung/Berührungsbewegung werden neue Werte für die Markierung zurückgegeben.
initRelease
: Für jedes Mouseup/Touchend werden neue Werte für die Markierung zurückgegeben.
isComplete
: Ist die Markierung vollständig? Einige Markierungen erfordern mehrere Interaktionen, bevor der Initialisierer die Kontrolle aufgibt.
initValid
: Wenn eine Markierung ungültig ist (z. B. ein Rechteck mit einer Breite oder Höhe von Null), wird sie automatisch zerstört.
Ein paar Hilfskomponenten sind DrawingToolRoot
, das ausgewählte/deaktivierte Zustände verarbeitet und Unteraufgaben-Popups rendert, sowie DeleteButton
und DragHandle
, die konsistente Steuerelemente für Zeichenwerkzeuge rendern. Es gibt auch eine deleteIfOutOfBounds
-Funktion, die nach dem Ziehen ganzer Markierungen aufgerufen werden sollte.
React erfordert, dass jede Komponente in einem Array einen Geschwister-eindeutigen key
hat. Geben Sie beim Rendern von Arrays von Dingen, die keine IDs haben (Anmerkungen, Antworten), eine zufällige _key
Eigenschaft an, falls diese nicht vorhanden ist. Stellen Sie sicher, dass Eigenschaften mit Unterstrich-Präfix nicht beibehalten werden. Das geschieht automatisch mit der Klasse JSONAPIClient.Model
.
< ul >
{ for item in things
item . _key ?= Math . random ()
< li key = { item . _key }>{ item . label }</ li >}
</ ul >
Es gibt einige Hübsch unglückliche (im Nachhinein) Komponenten, die bei asynchronen Werten helfen. Sie übernehmen die Funktion @props.children
, was ein wenig kitschig aussieht, aber recht gut funktioniert. Die meisten angeforderten Daten werden lokal zwischengespeichert, sodass diese normalerweise sicher sind. Wenn Sie jedoch feststellen, dass dieselbe Anforderung mehrmals hintereinander gestellt wird, ist dies ein guter Ausgangspunkt für die Suche nach redundanten Aufrufen. Hier ist ein Beispiel für ein erneutes Rendern, wenn sich ein Projekt ändert, was zur Überprüfung der Projekteigentümer führt.
< 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 >
Schreiben Sie keinen neuen Code mit ChangeListener
oder PromiseRenderer
.
Wenn es sinnvoll ist, ersetzen Sie ChangeListener
und PromiseRenderer
-Instanzen durch den Komponentenstatus im Code, an dem Sie arbeiten. Es ist ausführlicher, aber besser lesbar und wird uns in Zukunft dem Rendern auf dem Server näher bringen.
Binden Sie sämtliches CSS, das für die Funktionalität einer Komponente erforderlich ist , inline in die Komponente ein, andernfalls bewahren Sie es in einer separaten Datei auf, eine pro Komponente. Wählen Sie für eine bestimmte Komponente einen eindeutigen Klassennamen der obersten Ebene für diese Komponente und verschachteln Sie untergeordnete Klassen darunter. Behalten Sie gemeinsame Basisstile und Variablen in common.styl bei. Stiftformatierung: Ja, Doppelpunkte, keine Semikolons, keine geschweiften Klammern. @extends
nach oben, dann Eigenschaften (alphabetisch) und dann Nachkommenselektoren. Bevorzugen Sie die Verwendung von display: flex
und flex-wrap: wrap
.
Unser CSS ist wirklich riesig geworden, also probieren wir BEM für die Organisation aus.
// <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
Wir migrieren von Coffeescript auf ES6. Dies kann inkrementell erfolgen, indem eine neue Komponente geschrieben oder eine vorhandene Komponente in ES6 neu geschrieben wird. Einige Fallstricke sollten erwähnt werden:
Der existentielle Operator existiert in ES6 nicht. Vergleichen Sie entweder explizit mit null
oder verwenden Sie !!thing
, wenn es nur wahr sein muss.
Native ES6-Klassen werden bevorzugt, da React.createClass()
veraltet ist. Wenn die vorhandene Komponente jedoch auf Mixins basiert, sollten Sie die Verwendung von createReactClass()
in Betracht ziehen.
Mixins sind veraltet und werden von nativen Klassen nicht unterstützt. Verwenden Sie sie daher nicht in neuen Komponenten.
Verwenden Sie Backticks, um ES6-Komponenten in Coffeescript-Komponenten zu importieren:
`import NewComponent from './new-component'`
Im Stammverzeichnis des Repositorys wird eine ESLint-Konfigurationsdatei eingerichtet, die Sie mit Ihrem Texteditor verwenden können, um sowohl ES6 zu linten als auch den React-Styleguide von Airbnb zu verwenden.
Eine Anleitung zum Schreiben nativer Klassen im Vergleich zur Verwendung von createReactClass()
Siehe die panoptes-client- Bibliothek: https://www.npmjs.com/package/panoptes-client.
Das Format des Werts einer Anmerkung hängt von der Aufgabe ab, mit der sie generiert wurde.
single: Der Index der gewählten Antwort.
multiple: Ein Array der Indizes der ausgewählten Antworten (in der Reihenfolge, in der sie ausgewählt wurden).
Zeichnung: Ein Array von Zeichenwerkzeugmarkierungen (Beschreibungen dazu folgen weiter unten).
Umfrage: Eine Reihe von Identifikationen als Objekte. Jede Identifizierung ist eine choice
(die ID des identifizierten Tieres) und answers
auf ein Objekt. Jeder Schlüssel in answers
ist die ID einer Frage. Wenn diese Frage mehrere Antworten zulässt, ist der Wert ein Array von Antwort-IDs, andernfalls nur eine einzelne Antwort-ID.
Crop: Ein Objekt, das x
, y
, width
und height
des zugeschnittenen Bereichs enthält.
text: Eine Zeichenfolge.
Combo: Ein Unterarray von Anmerkungen.
Dropdown: Ein Array von Objekten, bei dem sich der value
auf die Antwort auf die entsprechende Frage bezieht und die boolesche option
angibt, dass die Antwort in der Optionsliste enthalten war.
Alle Koordinaten beziehen sich auf den oberen linken Rand des Bildes.
Alle Markierungen verfügen über ein tool
, das der Index des Werkzeugs ist (z. B. workflow.tasks.T0.tools[0]
), mit dem die Markierung erstellt wurde.
Alle Markierungen enthalten einen frame
, der den Index des Betreffrahmens darstellt (z. B. subject.locations[0]
), auf dem die Markierung vorgenommen wurde.
Wenn details
für ein Werkzeug definiert sind, verfügen seine Markierungen über ein details
von Unterklassifizierungen (jede mit einem value
, entsprechend den obigen Beschreibungen).
Der Zeichnungsanmerkungswert ist wie folgt:
Punkt: Die x
und y
-Koordinaten.
Zeile: Die Start- ( x1
, y1
) und Endkoordinaten ( x2
, y2
).
Polygon: Ein Array von Objekten, die jeweils die x
und y
-Koordinate eines Scheitelpunkts enthalten. Wenn die Markierung nicht explizit vom Benutzer geschlossen wurde, ist auto_closed
true
.
Rechteck: Die x
und y
Koordinate des oberen linken Punktes des Rechtecks zusammen mit seiner width
und height
.
Kreis: Die x
und y
Koordinate des Mittelpunkts des Kreises und sein Radius r
.
Ellipse: Die x
und y
Koordinate des Mittelpunkts der Ellipse, ihre Radien rx
und ry
und der angle
von rx
relativ zur x-Achse in Grad (ab 3:00 Uhr gegen den Uhrzeigersinn).
Bezier: Das Gleiche wie Polygon, aber jeder Punkt mit ungeradem Index ist die Koordinate des Kontrollpunkts einer quadratischen Bezier-Kurve.
Spalte: Das am weitesten links stehende x
Pixel und die width
der Spaltenauswahl.
Vielen Dank an BrowserStack für die Unterstützung von Open Source und die Möglichkeit, dieses Projekt auf mehreren Plattformen zu testen.