diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 3784c9075c..f069e844ba 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -11,10 +11,12 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released [Commits](https://github.com/scalableminds/webknossos/compare/24.11.1...HEAD) ### Added +- When exploring remote URIs pasted from Neuroglancer, the format prefixes like `precomputed://` are now ignored, so users don’t have to remove them. [#8195](https://github.com/scalableminds/webknossos/pull/8195) ### Changed - Reading image files on datastore filesystem is now done asynchronously. [#8126](https://github.com/scalableminds/webknossos/pull/8126) - Improved error messages for starting jobs on datasets from other organizations. [#8181](https://github.com/scalableminds/webknossos/pull/8181) +- Removed bounding box size restriction for inferral jobs for super users. [#8200](https://github.com/scalableminds/webknossos/pull/8200) ### Fixed - Fix performance bottleneck when deleting a lot of trees at once. [#8176](https://github.com/scalableminds/webknossos/pull/8176) @@ -22,5 +24,6 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released - Fix a bug where trying to delete a non-existing node (via the API, for example) would delete the whole active tree. [#8176](https://github.com/scalableminds/webknossos/pull/8176) ### Removed +- Removed Google Analytics integration. [#8201](https://github.com/scalableminds/webknossos/pull/8201) ### Breaking Changes diff --git a/MIGRATIONS.unreleased.md b/MIGRATIONS.unreleased.md index 20414e596e..de99ce01d1 100644 --- a/MIGRATIONS.unreleased.md +++ b/MIGRATIONS.unreleased.md @@ -8,4 +8,6 @@ User-facing changes are documented in the [changelog](CHANGELOG.released.md). ## Unreleased [Commits](https://github.com/scalableminds/webknossos/compare/24.11.1...HEAD) +- The config option `googleAnalytics.trackingId` is no longer used and can be removed. [#8201](https://github.com/scalableminds/webknossos/pull/8201) + ### Postgres Evolutions: diff --git a/app/controllers/AnnotationController.scala b/app/controllers/AnnotationController.scala index 9279822fa1..7a5b610240 100755 --- a/app/controllers/AnnotationController.scala +++ b/app/controllers/AnnotationController.scala @@ -545,8 +545,7 @@ class AnnotationController @Inject()( annotationInfos <- annotationDAO.findAllListableExplorationals( isFinished, None, - isForOwnDashboard = true, - AnnotationType.Explorational, + filterOwnedOrShared = true, limit.getOrElse(annotationService.DefaultAnnotationListLimit), pageNumber.getOrElse(0) ) diff --git a/app/controllers/InitialDataController.scala b/app/controllers/InitialDataController.scala index d2c736a064..ec928ec179 100644 --- a/app/controllers/InitialDataController.scala +++ b/app/controllers/InitialDataController.scala @@ -5,6 +5,7 @@ import com.scalableminds.util.accesscontext.GlobalAccessContext import com.scalableminds.util.time.Instant import com.scalableminds.util.tools.{Fox, FoxImplicits} import com.typesafe.scalalogging.LazyLogging +import models.aimodels.{AiModel, AiModelCategory, AiModelDAO} import models.annotation.{TracingStore, TracingStoreDAO} import models.dataset._ import models.folder.{Folder, FolderDAO, FolderService} @@ -42,6 +43,7 @@ class InitialDataService @Inject()(userService: UserService, taskTypeDAO: TaskTypeDAO, dataStoreDAO: DataStoreDAO, folderDAO: FolderDAO, + aiModelDAO: AiModelDAO, folderService: FolderService, tracingStoreDAO: TracingStoreDAO, teamDAO: TeamDAO, @@ -139,6 +141,19 @@ Samplecountry Some( "This is a wonderful dummy publication, it has authors, it has a link, it has a doi number, those could go here.\nLorem [ipsum](https://github.com/scalableminds/webknossos) dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.") ) + private val defaultDataStore = + DataStore(conf.Datastore.name, conf.Http.uri, conf.Datastore.publicUri.getOrElse(conf.Http.uri), conf.Datastore.key) + private val defaultAiModel = AiModel( + ObjectId("66544a56d20000af0e42ba0f"), + defaultOrganization._id, + defaultDataStore.name, + defaultUser._id, + None, + List.empty, + "sample_ai_model", + Some("Works if model files are manually placed at binaryData/sample_organization/66544a56d20000af0e42ba0f/"), + Some(AiModelCategory.em_neurons) + ) def insert: Fox[Unit] = for { @@ -158,6 +173,7 @@ Samplecountry _ <- insertTaskType() _ <- insertProject() _ <- insertPublication() + _ <- insertAiModel() } yield () private def assertInitialDataEnabled: Fox[Unit] = @@ -266,16 +282,18 @@ Samplecountry } else Fox.successful(()) } + private def insertAiModel(): Fox[Unit] = aiModelDAO.findAll.flatMap { aiModels => + if (aiModels.isEmpty) { + aiModelDAO.insertOne(defaultAiModel) + } else Fox.successful(()) + } + def insertLocalDataStoreIfEnabled(): Fox[Unit] = if (storeModules.localDataStoreEnabled) { dataStoreDAO.findOneByUrl(conf.Http.uri).futureBox.flatMap { maybeStore => if (maybeStore.isEmpty) { logger.info("Inserting local datastore") - dataStoreDAO.insertOne( - DataStore(conf.Datastore.name, - conf.Http.uri, - conf.Datastore.publicUri.getOrElse(conf.Http.uri), - conf.Datastore.key)) + dataStoreDAO.insertOne(defaultDataStore) } else Fox.successful(()) } } else Fox.successful(()) diff --git a/app/controllers/UserController.scala b/app/controllers/UserController.scala index 6fbd048f64..8e5b92af16 100755 --- a/app/controllers/UserController.scala +++ b/app/controllers/UserController.scala @@ -63,8 +63,7 @@ class UserController @Inject()(userService: UserService, annotations <- annotationDAO.findAllListableExplorationals( isFinished, Some(request.identity._id), - isForOwnDashboard = true, - AnnotationType.Explorational, + filterOwnedOrShared = true, limit.getOrElse(annotationService.DefaultAnnotationListLimit), pageNumber.getOrElse(0) ) @@ -118,8 +117,7 @@ class UserController @Inject()(userService: UserService, annotations <- annotationDAO.findAllListableExplorationals( isFinished, Some(userIdValidated), - isForOwnDashboard = false, - AnnotationType.Explorational, + filterOwnedOrShared = false, limit.getOrElse(annotationService.DefaultAnnotationListLimit), pageNumber.getOrElse(0) ) diff --git a/app/models/annotation/Annotation.scala b/app/models/annotation/Annotation.scala index 887bf7439c..cd0f0e38c2 100755 --- a/app/models/annotation/Annotation.scala +++ b/app/models/annotation/Annotation.scala @@ -359,20 +359,34 @@ class AnnotationDAO @Inject()(sqlClient: SqlClient, annotationLayerDAO: Annotati // format: on } + /** + * Find all annotations which are listable by the user specified in 'forUser' + * + * @param isFinished + * If set to `true`, only finished annotations are returned. If set to `false`, only active annotations are returned. + * If set to `None`, all non-cancelled annotations are returned. + * @param forUser + * If set, only annotations of this user are returned. If not set, all annotations are returned. + * @param filterOwnedOrShared + * If `true`, the function lists only annotations owned by the user or explicitly shared with them (used for the + * user's own dashboard). If `false`, it lists all annotations the viewer is allowed to see. + * @param limit + * The maximum number of annotations to return. + * @param pageNumber + * The page number to return. The first page is 0. + */ def findAllListableExplorationals( isFinished: Option[Boolean], forUser: Option[ObjectId], - // In dashboard, list only own + explicitly shared annotations. When listing those of another user, list all of their annotations the viewer is allowed to see - isForOwnDashboard: Boolean, - typ: AnnotationType, + filterOwnedOrShared: Boolean, limit: Int, pageNumber: Int = 0)(implicit ctx: DBAccessContext): Fox[List[AnnotationCompactInfo]] = for { - accessQuery <- if (isForOwnDashboard) accessQueryFromAccessQWithPrefix(listAccessQ, q"a.") + accessQuery <- if (filterOwnedOrShared) accessQueryFromAccessQWithPrefix(listAccessQ, q"a.") else accessQueryFromAccessQWithPrefix(readAccessQWithPrefix, q"a.") stateQuery = getStateQuery(isFinished) userQuery = forUser.map(u => q"a._user = $u").getOrElse(q"TRUE") - typQuery = q"a.typ = $typ" + typQuery = q"a.typ = ${AnnotationType.Explorational}" query = q"""WITH -- teams_agg is extracted to avoid left-join fanout. diff --git a/app/utils/WkConf.scala b/app/utils/WkConf.scala index c71a644185..0272635bbf 100644 --- a/app/utils/WkConf.scala +++ b/app/utils/WkConf.scala @@ -200,10 +200,6 @@ class WkConf @Inject()(configuration: Configuration) extends ConfigReader with L val environment: String = get[String]("airbrake.environment") } - object GoogleAnalytics { - val trackingId: String = get[String]("googleAnalytics.trackingId") - } - object SlackNotifications { val uri: String = get[String]("slackNotifications.uri") val verboseLoggingEnabled: Boolean = get[Boolean]("slackNotifications.verboseLoggingEnabled") @@ -259,7 +255,6 @@ class WkConf @Inject()(configuration: Configuration) extends ConfigReader with L Silhouette, Jobs, Airbrake, - GoogleAnalytics, BackendAnalytics, Slick, Voxelytics, diff --git a/app/views/main.scala.html b/app/views/main.scala.html index f78be6c1aa..921ea8c5e1 100755 --- a/app/views/main.scala.html +++ b/app/views/main.scala.html @@ -6,9 +6,6 @@ - @if(conf.GoogleAnalytics.trackingId.nonEmpty) { - - } @(conf.WebKnossos.tabTitle) @openGraphTitle.map { ogt => @@ -52,25 +49,5 @@
- - @if(conf.GoogleAnalytics.trackingId.nonEmpty) { - - - } diff --git a/conf/application.conf b/conf/application.conf index 0ae8b6f25d..2cfdcccb0c 100644 --- a/conf/application.conf +++ b/conf/application.conf @@ -293,9 +293,6 @@ airbrake { projectID = "insert-valid-projectID-here" } -# Front-end analytics -googleAnalytics.trackingId = "" - # Slack notification slackNotifications { uri = "" diff --git a/frontend/javascripts/admin/dataset/dataset_add_remote_view.tsx b/frontend/javascripts/admin/dataset/dataset_add_remote_view.tsx index eb12f66bc1..3afe7e82e5 100644 --- a/frontend/javascripts/admin/dataset/dataset_add_remote_view.tsx +++ b/frontend/javascripts/admin/dataset/dataset_add_remote_view.tsx @@ -483,6 +483,15 @@ function AddRemoteLayer({ }; function validateUrls(userInput: string) { + const removePrefix = (value: string, prefix: string) => + value.startsWith(prefix) ? value.slice(prefix.length) : value; + + // If pasted from neuroglancer, uris have these prefixes even before the protocol. The backend ignores them. + userInput = removePrefix(userInput, "zarr://"); + userInput = removePrefix(userInput, "zarr3://"); + userInput = removePrefix(userInput, "n5://"); + userInput = removePrefix(userInput, "precomputed://"); + if (userInput.startsWith("https://") || userInput.startsWith("http://")) { setSelectedProtocol("https"); } else if (userInput.startsWith("s3://")) { diff --git a/frontend/javascripts/admin/dataset/dataset_upload_view.tsx b/frontend/javascripts/admin/dataset/dataset_upload_view.tsx index 653d2ff124..46b46d4116 100644 --- a/frontend/javascripts/admin/dataset/dataset_upload_view.tsx +++ b/frontend/javascripts/admin/dataset/dataset_upload_view.tsx @@ -54,7 +54,6 @@ import { import Toast from "libs/toast"; import * as Utils from "libs/utils"; import messages from "messages"; -import { trackAction } from "oxalis/model/helpers/analytics"; import Zip from "libs/zipjs_wrapper"; import { AllowedTeamsFormItem, @@ -350,7 +349,6 @@ class DatasetUploadView extends React.Component { }); finishDatasetUpload(datastoreUrl, uploadInfo).then( async () => { - trackAction("Upload dataset"); Toast.success(messages["dataset.upload_success"]); let maybeError; diff --git a/frontend/javascripts/dashboard/advanced_dataset/dataset_table.tsx b/frontend/javascripts/dashboard/advanced_dataset/dataset_table.tsx index 80631b46f8..ffabefa87b 100644 --- a/frontend/javascripts/dashboard/advanced_dataset/dataset_table.tsx +++ b/frontend/javascripts/dashboard/advanced_dataset/dataset_table.tsx @@ -14,7 +14,6 @@ import type { } from "types/api_flow_types"; import type { DatasetFilteringMode } from "dashboard/dataset_view"; import { stringToColor } from "libs/format_utils"; -import { trackAction } from "oxalis/model/helpers/analytics"; import CategorizationLabel from "oxalis/view/components/categorization_label"; import DatasetActionView, { getDatasetActionContextMenu, @@ -791,7 +790,6 @@ export function DatasetTags({ }; } - trackAction("Edit dataset tag"); updateDataset(dataset, updater); }; diff --git a/frontend/javascripts/dashboard/dataset/dataset_settings_view.tsx b/frontend/javascripts/dashboard/dataset/dataset_settings_view.tsx index 7567b7fe75..5b593afcd1 100644 --- a/frontend/javascripts/dashboard/dataset/dataset_settings_view.tsx +++ b/frontend/javascripts/dashboard/dataset/dataset_settings_view.tsx @@ -32,7 +32,6 @@ import { updateDatasetPartial, } from "admin/admin_rest_api"; import { handleGenericError } from "libs/error_handling"; -import { trackAction } from "oxalis/model/helpers/analytics"; import Toast from "libs/toast"; import messages from "messages"; import features from "features"; @@ -390,7 +389,6 @@ class DatasetSettingsView extends React.PureComponent { const newTracing = await editLockedState(tracing.id, tracing.typ, locked); Toast.success(messages["annotation.was_edited"]); this.updateTracingInLocalState(tracing, (_t) => newTracing); - trackAction("Lock/Unlock explorative annotation"); } catch (error) { handleGenericError(error as Error, "Could not update the annotation lock state."); } @@ -475,7 +473,6 @@ class ExplorativeAnnotationsView extends React.PureComponent { editAnnotation(newAnnotation.id, newAnnotation.typ, { tags: newAnnotation.tags, }); - trackAction("Edit annotation tag"); } return newAnnotation; @@ -699,7 +696,7 @@ class ExplorativeAnnotationsView extends React.PureComponent {
- {teamTags.length > 0 ? : null} + {teamTags.length > 0 ? : null}
{teamTags}
diff --git a/frontend/javascripts/main.tsx b/frontend/javascripts/main.tsx index 4e99785015..31d59217e4 100644 --- a/frontend/javascripts/main.tsx +++ b/frontend/javascripts/main.tsx @@ -6,7 +6,6 @@ import UnthrottledStore, { startSagas } from "oxalis/store"; import { message } from "antd"; import { getActiveUser, checkAnyOrganizationExists, getOrganization } from "admin/admin_rest_api"; -import { googleAnalyticsLogClicks } from "oxalis/model/helpers/analytics"; import { load as loadFeatureToggles } from "features"; import { setActiveUserAction } from "oxalis/model/actions/user_actions"; import { setHasOrganizationsAction, setThemeAction } from "oxalis/model/actions/ui_actions"; @@ -97,7 +96,6 @@ document.addEventListener("DOMContentLoaded", async () => { throwAssertions: false, }); message.config({ top: 30 }); - document.addEventListener("click", googleAnalyticsLogClicks); checkBrowserFeatures(); await Promise.all([loadFeatureToggles(), loadActiveUser(), loadHasOrganizations()]); await Promise.all([loadOrganization()]); diff --git a/frontend/javascripts/navbar.tsx b/frontend/javascripts/navbar.tsx index 7d9d357108..6c09b28fc8 100644 --- a/frontend/javascripts/navbar.tsx +++ b/frontend/javascripts/navbar.tsx @@ -45,7 +45,6 @@ import { sendAnalyticsEvent, } from "admin/admin_rest_api"; import { logoutUserAction, setActiveUserAction } from "oxalis/model/actions/user_actions"; -import { trackVersion } from "oxalis/model/helpers/analytics"; import { useFetch, useInterval } from "libs/react_helpers"; import LoginForm from "admin/auth/login_form"; import Request from "libs/request"; @@ -739,13 +738,9 @@ function AnonymousAvatar() { ); } -async function getAndTrackVersion(dontTrack: boolean = false) { +async function getVersion() { const buildInfo = await getBuildInfo(); - const { version } = buildInfo.webknossos; - if (dontTrack) { - trackVersion(version); - } - return version; + return buildInfo.webknossos.version; } function AnnotationLockedByUserTag({ @@ -831,7 +826,7 @@ function Navbar({ location.href = "/"; }; - const version = useFetch(getAndTrackVersion, null, []); + const version = useFetch(getVersion, null, []); const [isHelpMenuOpen, setIsHelpMenuOpen] = useState(false); const [polledVersion, setPolledVersion] = useState(null); const [isHelpModalOpen, setIsHelpModalOpen] = useState(false); @@ -839,7 +834,7 @@ function Navbar({ useInterval( async () => { if (isHelpMenuOpen) { - setPolledVersion(await getAndTrackVersion(true)); + setPolledVersion(await getVersion()); } }, 2000, diff --git a/frontend/javascripts/oxalis/model/helpers/analytics.ts b/frontend/javascripts/oxalis/model/helpers/analytics.ts deleted file mode 100644 index 43af3d697b..0000000000 --- a/frontend/javascripts/oxalis/model/helpers/analytics.ts +++ /dev/null @@ -1,44 +0,0 @@ -import window from "libs/window"; -import Store from "oxalis/store"; - -function getOrganization() { - const { activeUser } = Store.getState(); - return activeUser != null ? activeUser.organization : null; -} - -// @ts-expect-error ts-migrate(7019) FIXME: Rest parameter 'params' implicitly has an 'any[]' ... Remove this comment to see the full error message -function gtagGuard(...params) { - // @ts-expect-error ts-migrate(2339) FIXME: Property 'gtag' does not exist on type '(Window & ... Remove this comment to see the full error message - if (typeof window.gtag !== "undefined" && window.gtag !== null) { - // @ts-expect-error ts-migrate(2339) FIXME: Property 'gtag' does not exist on type '(Window & ... Remove this comment to see the full error message - window.gtag(...params); - } -} - -export function trackAction(action: string): void { - gtagGuard("event", "action", action, getOrganization()); -} -export function trackVersion(version: string): void { - gtagGuard("event", "version", { - event_category: "page_load", - event_label: version, - }); -} -export function googleAnalyticsLogClicks(evt: MouseEvent) { - // This function logs all clicks on elements that contain text to google analytics - // Flow doesn't allow to check for the textContent property otherwise - const target = evt.target as any as Node; - - if (target.textContent != null) { - // Restrict the textContent to a maximum length - const textContent = target.textContent.trim().slice(0, 50); - - if (textContent.length > 0) { - gtagGuard("event", "click", { - event_category: textContent, - event_label: getOrganization(), - }); - } - } -} -export default {}; diff --git a/frontend/javascripts/oxalis/model/sagas/settings_saga.ts b/frontend/javascripts/oxalis/model/sagas/settings_saga.ts index da0666c79e..bc7ef8e907 100644 --- a/frontend/javascripts/oxalis/model/sagas/settings_saga.ts +++ b/frontend/javascripts/oxalis/model/sagas/settings_saga.ts @@ -5,7 +5,6 @@ import { import { type Saga, take, select } from "oxalis/model/sagas/effect-generators"; import { all, takeEvery, debounce, call, retry } from "typed-redux-saga"; import type { UpdateUserSettingAction } from "oxalis/model/actions/settings_actions"; -import { trackAction } from "oxalis/model/helpers/analytics"; import { updateUserConfiguration, updateDatasetConfiguration } from "admin/admin_rest_api"; import ErrorHandling from "libs/error_handling"; import Toast from "libs/toast"; @@ -97,12 +96,6 @@ function* prepareDatasetSettingsForSaving( return maskedDatasetConfiguration; } -function* trackUserSettingsAsync(action: UpdateUserSettingAction): Saga { - if (action.propertyName === "newNodeNewTree") { - yield* call(trackAction, `${action.value ? "Enabled" : "Disabled"} soma clicking`); - } -} - function* showUserSettingToast(action: UpdateUserSettingAction): Saga { const { propertyName } = action; @@ -129,7 +122,6 @@ export default function* watchPushSettingsAsync(): Saga { pushDatasetSettingsAsync(originalDatasetSettings), ), debounce(2500, "UPDATE_LAYER_SETTING", () => pushDatasetSettingsAsync(originalDatasetSettings)), - takeEvery("UPDATE_USER_SETTING", trackUserSettingsAsync), takeEvery("UPDATE_USER_SETTING", showUserSettingToast), ]); } diff --git a/frontend/javascripts/oxalis/view/action-bar/starting_job_modals.tsx b/frontend/javascripts/oxalis/view/action-bar/starting_job_modals.tsx index a3f071a2fb..40640a56de 100644 --- a/frontend/javascripts/oxalis/view/action-bar/starting_job_modals.tsx +++ b/frontend/javascripts/oxalis/view/action-bar/starting_job_modals.tsx @@ -111,6 +111,7 @@ type StartJobFormProps = Props & { type BoundingBoxSelectionProps = { isBoundingBoxConfigurable?: boolean; userBoundingBoxes: UserBoundingBox[]; + isSuperUser: boolean; onChangeSelectedBoundingBox: (bBoxId: number | null) => void; value: number | null; }; @@ -183,6 +184,7 @@ export function BoundingBoxSelection({ function BoundingBoxSelectionFormItem({ isBoundingBoxConfigurable, userBoundingBoxes, + isSuperUser, onChangeSelectedBoundingBox, value: selectedBoundingBoxId, }: BoundingBoxSelectionProps): JSX.Element { @@ -218,7 +220,7 @@ function BoundingBoxSelectionFormItem({ }, { validator: (_rule, value) => { - if (!isBoundingBoxConfigurable) return Promise.resolve(); + if (!isBoundingBoxConfigurable || isSuperUser) return Promise.resolve(); const selectedBoundingBox = userBoundingBoxes.find((bbox) => bbox.id === value); let rejectionReason = ""; @@ -533,6 +535,7 @@ function StartJobForm(props: StartJobFormProps) { const dataset = useSelector((state: OxalisState) => state.dataset); const tracing = useSelector((state: OxalisState) => state.tracing); const activeUser = useSelector((state: OxalisState) => state.activeUser); + const isActiveUserSuperUser = activeUser?.isSuperUser || false; const colorLayers = getColorLayers(dataset); const layers = chooseSegmentationLayer ? getSegmentationLayers(dataset) : colorLayers; const [useCustomWorkflow, setUseCustomWorkflow] = React.useState(false); @@ -643,6 +646,7 @@ function StartJobForm(props: StartJobFormProps) { form.setFieldsValue({ boundingBoxId: bBoxId })} value={form.getFieldValue("boundingBoxId")} /> diff --git a/frontend/javascripts/oxalis/view/action_bar_view.tsx b/frontend/javascripts/oxalis/view/action_bar_view.tsx index a10ecb1df1..0f33c15c2c 100644 --- a/frontend/javascripts/oxalis/view/action_bar_view.tsx +++ b/frontend/javascripts/oxalis/view/action_bar_view.tsx @@ -9,7 +9,6 @@ import { getLayoutConfig, addNewLayout, } from "oxalis/view/layouting/layout_persistence"; -import { trackAction } from "oxalis/model/helpers/analytics"; import AddNewLayoutModal from "oxalis/view/action-bar/add_new_layout_modal"; import { withAuthentication } from "admin/auth/authentication_modal"; import { type ViewMode, type ControlMode, MappingStatusEnum } from "oxalis/constants"; @@ -196,7 +195,6 @@ class ActionBarView extends React.PureComponent { fallbackLayerName, maybeMappingName, ); - trackAction("Create hybrid tracing (from view mode)"); location.href = `${location.origin}/annotations/${annotation.typ}/${annotation.id}${location.hash}`; }; diff --git a/frontend/javascripts/oxalis/view/nml_upload_zone_container.tsx b/frontend/javascripts/oxalis/view/nml_upload_zone_container.tsx index 07ce3751d8..5d37fa67e4 100644 --- a/frontend/javascripts/oxalis/view/nml_upload_zone_container.tsx +++ b/frontend/javascripts/oxalis/view/nml_upload_zone_container.tsx @@ -8,7 +8,6 @@ import type { Dispatch } from "redux"; import type { OxalisState } from "oxalis/store"; import { setDropzoneModalVisibilityAction } from "oxalis/model/actions/ui_actions"; import FormattedDate from "components/formatted_date"; -import { trackAction } from "oxalis/model/helpers/analytics"; type State = { files: Array; @@ -115,7 +114,6 @@ class NmlUploadZoneContainer extends React.PureComponent { files, dropzoneActive: false, }); - trackAction("NML drag and drop"); this.props.hideDropzoneModal(); }; diff --git a/frontend/javascripts/router.tsx b/frontend/javascripts/router.tsx index dcbc7815c4..4e8d088609 100644 --- a/frontend/javascripts/router.tsx +++ b/frontend/javascripts/router.tsx @@ -46,7 +46,6 @@ import window from "libs/window"; import _ from "lodash"; import Navbar from "navbar"; import { ControlModeEnum } from "oxalis/constants"; -import { trackAction } from "oxalis/model/helpers/analytics"; import type { OxalisState } from "oxalis/store"; import HelpButton from "oxalis/view/help_modal"; import TracingLayoutView from "oxalis/view/layouting/tracing_layout_view"; @@ -85,26 +84,6 @@ type StateProps = { }; type Props = StateProps; const browserHistory = createBrowserHistory(); -browserHistory.listen((location) => { - // @ts-ignore - if (typeof window.ga !== "undefined" && window.ga !== null && window.ga.getByName != null) { - // t0 is the default tracker name - // @ts-ignore - const tracker = window.ga.getByName("t0"); - if (tracker == null) return; - const lastPage = tracker.get("page"); - const newPage = location.pathname; - - // The listener is called repeatedly for a single page change, don't send repeated pageviews - if (lastPage !== newPage) { - // Update the tracker state first, so that subsequent pageviews AND events use the correct page - // @ts-ignore - window.gtag("set", "page_path", newPage); - // @ts-ignore - window.gtag("event", "page_view"); - } - } -}); function PageNotFoundView() { return ( @@ -673,7 +652,6 @@ class ReactRouter extends React.Component { null, resolutionRestrictions, ); - trackAction(`Create ${type} tracing`); return `/annotations/${annotation.id}`; }} /> diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/explore/ExploreLayerUtils.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/explore/ExploreLayerUtils.scala index 5af8788385..907e45e2f6 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/explore/ExploreLayerUtils.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/explore/ExploreLayerUtils.scala @@ -137,14 +137,15 @@ trait ExploreLayerUtils extends FoxImplicits { } def removeHeaderFileNamesFromUriSuffix(uri: String): String = - if (uri.endsWith(N5Header.FILENAME_ATTRIBUTES_JSON)) uri.dropRight(N5Header.FILENAME_ATTRIBUTES_JSON.length) - else if (uri.endsWith(ZarrHeader.FILENAME_DOT_ZARRAY)) uri.dropRight(ZarrHeader.FILENAME_DOT_ZARRAY.length) - else if (uri.endsWith(NgffMetadata.FILENAME_DOT_ZATTRS)) uri.dropRight(NgffMetadata.FILENAME_DOT_ZATTRS.length) - else if (uri.endsWith(NgffGroupHeader.FILENAME_DOT_ZGROUP)) - uri.dropRight(NgffGroupHeader.FILENAME_DOT_ZGROUP.length) - else if (uri.endsWith(PrecomputedHeader.FILENAME_INFO)) uri.dropRight(PrecomputedHeader.FILENAME_INFO.length) - else if (uri.endsWith(Zarr3ArrayHeader.FILENAME_ZARR_JSON)) - uri.dropRight(Zarr3ArrayHeader.FILENAME_ZARR_JSON.length) - else uri + uri + .stripSuffix(N5Header.FILENAME_ATTRIBUTES_JSON) + .stripSuffix(ZarrHeader.FILENAME_DOT_ZARRAY) + .stripSuffix(NgffMetadata.FILENAME_DOT_ZATTRS) + .stripSuffix(NgffGroupHeader.FILENAME_DOT_ZGROUP) + .stripSuffix(PrecomputedHeader.FILENAME_INFO) + .stripSuffix(Zarr3ArrayHeader.FILENAME_ZARR_JSON) + + def removeNeuroglancerPrefixesFromUri(uri: String): String = + uri.stripPrefix("zarr3://").stripPrefix("zarr://").stripPrefix("precomputed://").stripPrefix("n5://") } diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/explore/ExploreRemoteLayerService.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/explore/ExploreRemoteLayerService.scala index 1cc8ce8a0e..84cd905863 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/explore/ExploreRemoteLayerService.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/explore/ExploreRemoteLayerService.scala @@ -77,7 +77,7 @@ class ExploreRemoteLayerService @Inject()(dataVaultService: DataVaultService, reportMutable: ListBuffer[String])( implicit ec: ExecutionContext): Fox[List[(DataLayerWithMagLocators, VoxelSize)]] = for { - uri <- tryo(new URI(removeHeaderFileNamesFromUriSuffix(layerUri))) ?~> s"Received invalid URI: $layerUri" + uri <- tryo(new URI(removeNeuroglancerPrefixesFromUri(removeHeaderFileNamesFromUriSuffix(layerUri)))) ?~> s"Received invalid URI: $layerUri" _ <- bool2Fox(uri.getScheme != null) ?~> s"Received invalid URI: $layerUri" _ <- assertLocalPathInWhitelist(uri) credentialOpt: Option[DataVaultCredential] <- Fox.runOptional(credentialId)(remoteWebknossosClient.getCredential)