Skip to content

Commit a62c986

Browse files
authored
Merge branch 'master' into s3-upload
2 parents 5e548c8 + abcdb07 commit a62c986

File tree

26 files changed

+135
-49
lines changed

26 files changed

+135
-49
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ WEBKNOSSOS is an open-source tool for annotating and exploring large 3D image da
77
* Scale data reconstruction projects with crowdsourcing workflows
88
* Share datasets and annotations with collaborating scientists
99

10-
[Start using WEBKNOSSOS](https://webknossos.org) - [On your own server](https://docs.webknossos.org/webknossos/installation.html) - [User Documentation](https://docs.webknossos.org) - [Contact us](mailto:[email protected])
10+
[Start using WEBKNOSSOS](https://webknossos.org) - [On your own server](https://docs.webknossos.org/webknossos/open_source/installation.html) - [User Documentation](https://docs.webknossos.org) - [Contact us](mailto:[email protected])
1111

1212
[![](https://img.shields.io/circleci/project/github/scalableminds/webknossos/master.svg?logo=circleci)](https://circleci.com/gh/scalableminds/webknossos)
1313
[![](https://img.shields.io/github/release/scalableminds/webknossos.svg)](https://github.com/scalableminds/webknossos/releases/latest)

app/WebknossosModule.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import com.scalableminds.webknossos.datastore.storage.DataVaultService
33
import controllers.{Application, InitialDataService}
44
import files.WkTempFileService
55
import mail.MailchimpTicker
6-
import models.analytics.AnalyticsSessionService
7-
import models.annotation.{AnnotationMutexService, AnnotationStore, AnnotationDataSourceTemporaryStore}
6+
import models.analytics.{AnalyticsService, AnalyticsSessionService}
7+
import models.annotation.{AnnotationDataSourceTemporaryStore, AnnotationMutexService, AnnotationStore}
88
import models.dataset.{DatasetService, ThumbnailCachingService}
99
import models.job.{JobService, WorkerLivenessService}
1010
import models.organization.FreeCreditTransactionService
@@ -43,5 +43,6 @@ class WebknossosModule extends AbstractModule {
4343
bind(classOf[AnnotationDataSourceTemporaryStore]).asEagerSingleton()
4444
bind(classOf[CertificateValidationService]).asEagerSingleton()
4545
bind(classOf[FreeCreditTransactionService]).asEagerSingleton()
46+
bind(classOf[AnalyticsService]).asEagerSingleton()
4647
}
4748
}

app/controllers/Application.scala

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package controllers
22

3-
import com.scalableminds.util.mvc.ApiVersioning
43
import com.scalableminds.util.tools.{Fox, FoxImplicits}
54
import com.typesafe.config.ConfigRenderOptions
65
import mail.{DefaultMails, Send}
@@ -12,43 +11,29 @@ import play.api.mvc.{Action, AnyContent, Result}
1211
import play.silhouette.api.Silhouette
1312
import security.{CertificateValidationService, WkEnv}
1413
import utils.sql.{SimpleSQLDAO, SqlClient}
15-
import utils.{StoreModules, WkConf}
14+
import utils.{BuildInfoService, WkConf}
1615

1716
import javax.inject.Inject
1817
import scala.concurrent.ExecutionContext
1918

2019
class Application @Inject()(actorSystem: ActorSystem,
2120
userService: UserService,
22-
releaseInformationDAO: ReleaseInformationDAO,
21+
buildInfoService: BuildInfoService,
2322
organizationDAO: OrganizationDAO,
2423
conf: WkConf,
2524
defaultMails: DefaultMails,
26-
storeModules: StoreModules,
2725
sil: Silhouette[WkEnv],
2826
certificateValidationService: CertificateValidationService)(implicit ec: ExecutionContext)
29-
extends Controller
30-
with ApiVersioning {
27+
extends Controller {
3128

3229
private lazy val Mailer =
3330
actorSystem.actorSelection("/user/mailActor")
3431

3532
// Note: This route is used by external applications, keep stable
3633
def buildInfo: Action[AnyContent] = sil.UserAwareAction.async {
3734
for {
38-
schemaVersion <- releaseInformationDAO.getSchemaVersion.futureBox
39-
} yield {
40-
addRemoteOriginHeaders(
41-
Ok(
42-
Json.obj(
43-
"webknossos" -> Json.toJson(
44-
webknossos.BuildInfo.toMap.view.mapValues(_.toString).filterKeys(_ != "certificatePublicKey").toMap),
45-
"schemaVersion" -> schemaVersion.toOption,
46-
"httpApiVersioning" -> apiVersioningInfo,
47-
"localDataStoreEnabled" -> storeModules.localDataStoreEnabled,
48-
"localTracingStoreEnabled" -> storeModules.localTracingStoreEnabled
49-
))
50-
)
51-
}
35+
buildInfoJson <- buildInfoService.buildInfoJson
36+
} yield addRemoteOriginHeaders(Ok(buildInfoJson))
5237
}
5338

5439
// This only changes on server restart, so we can cache the full result.

app/controllers/JobController.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ object MovieResolutionSetting extends ExtendedEnumeration {
3131
}
3232

3333
object CameraPositionSetting extends ExtendedEnumeration {
34-
val MOVING, STATIC_XZ, STATIC_YZ = Value
34+
val MOVING, STATIC_ISOMETRIC, STATIC_XY, STATIC_XZ, STATIC_YZ = Value
3535
}
3636

3737
case class AnimationJobOptions(
@@ -378,7 +378,6 @@ class JobController @Inject()(jobDAO: JobDAO,
378378
organization <- organizationDAO.findOne(dataset._organization)(GlobalAccessContext) ?~> Messages(
379379
"organization.notFound",
380380
dataset._organization)
381-
_ <- Fox.fromBool(request.identity._organization == organization._id) ?~> "job.exportTiff.notAllowed.organization" ~> FORBIDDEN
382381
_ <- Fox.runOptional(layerName)(datasetService.assertValidLayerNameLax)
383382
_ <- Fox.runOptional(annotationLayerName)(datasetService.assertValidLayerNameLax)
384383
_ <- jobService.assertBoundingBoxLimits(bbox, mag)
@@ -496,7 +495,6 @@ class JobController @Inject()(jobDAO: JobDAO,
496495
organization <- organizationDAO.findOne(dataset._organization)(GlobalAccessContext) ?~> Messages(
497496
"organization.notFound",
498497
dataset._organization)
499-
_ <- Fox.fromBool(request.identity._organization == organization._id) ?~> "job.renderAnimation.notAllowed.organization" ~> FORBIDDEN
500498
userOrganization <- organizationDAO.findOne(request.identity._organization)
501499
animationJobOptions = request.body
502500
_ <- Fox.runIf(!PricingPlan.isPaidPlan(userOrganization.pricingPlan)) {

app/models/analytics/AnalyticsEvent.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,3 +230,15 @@ case class FrontendAnalyticsEvent(user: User, eventType: String, eventProperties
230230
override def eventProperties(analyticsLookUpService: AnalyticsLookUpService): Fox[JsObject] =
231231
Fox.successful(eventProperties ++ Json.obj("is_frontend_event" -> true))
232232
}
233+
234+
case class WebknossosHeartbeatAnalyticsEvent(user: User, buildInfoJson: JsObject)(implicit ec: ExecutionContext)
235+
extends AnalyticsEvent {
236+
def eventType: String = "webknossos_heartbeat"
237+
def eventProperties(analyticsLookUpService: AnalyticsLookUpService): Fox[JsObject] =
238+
Fox.successful(
239+
Json.obj(
240+
"build_info" -> buildInfoJson,
241+
)
242+
)
243+
244+
}

app/models/analytics/AnalyticsService.scala

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@ import com.scalableminds.webknossos.datastore.rpc.RPC
88
import com.typesafe.scalalogging.LazyLogging
99
import models.user.{MultiUserDAO, UserDAO}
1010
import com.scalableminds.util.tools.Box.tryo
11+
import com.scalableminds.webknossos.datastore.helpers.IntervalScheduler
12+
import org.apache.pekko.actor.ActorSystem
1113
import play.api.http.Status.UNAUTHORIZED
14+
import play.api.inject.ApplicationLifecycle
1215
import play.api.libs.json._
13-
import utils.WkConf
16+
import utils.{BuildInfoService, WkConf}
1417

1518
import javax.inject.Inject
1619
import scala.concurrent.ExecutionContext
@@ -20,8 +23,13 @@ class AnalyticsService @Inject()(rpc: RPC,
2023
wkConf: WkConf,
2124
analyticsLookUpService: AnalyticsLookUpService,
2225
analyticsSessionService: AnalyticsSessionService,
23-
analyticsDAO: AnalyticsDAO)(implicit ec: ExecutionContext)
26+
analyticsDAO: AnalyticsDAO,
27+
userDAO: UserDAO,
28+
buildInfoService: BuildInfoService,
29+
val actorSystem: ActorSystem,
30+
val lifecycle: ApplicationLifecycle)(implicit val ec: ExecutionContext)
2431
extends LazyLogging
32+
with IntervalScheduler
2533
with FoxImplicits {
2634

2735
private lazy val conf = wkConf.BackendAnalytics
@@ -75,6 +83,17 @@ class AnalyticsService @Inject()(rpc: RPC,
7583
}
7684
Fox.successful(())
7785
}
86+
87+
override protected def tickerInterval: FiniteDuration = 24 hours
88+
89+
override protected def tick(): Fox[_] =
90+
for {
91+
oldestUser <- userDAO.findOldestActive
92+
buildInfoJson <- Fox.fromFuture(buildInfoService.buildInfoJson)
93+
event = WebknossosHeartbeatAnalyticsEvent(oldestUser, buildInfoJson)
94+
_ = track(event)
95+
} yield ()
96+
7897
}
7998

8099
class AnalyticsLookUpService @Inject()(userDAO: UserDAO, multiUserDAO: MultiUserDAO, wkConf: WkConf)

app/models/job/JobCommand.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ object JobCommand extends ExtendedEnumeration {
1212
*/
1313

1414
val compute_mesh_file, compute_segment_index_file, convert_to_wkw, export_tiff, find_largest_segment_id,
15-
globalize_floodfills, infer_nuclei, infer_neurons, infer_instances, materialize_volume_annotation, render_animation,
16-
infer_mitochondria, align_sections, train_model, infer_with_model, train_neuron_model, train_instance_model = Value
15+
materialize_volume_annotation, render_animation, align_sections, infer_nuclei, infer_neurons, infer_instances,
16+
infer_mitochondria, train_neuron_model, train_instance_model,
17+
// No-longer supported jobs, kept here to be able to display old existing jobs:
18+
globalize_floodfills, train_model, infer_with_model = Value
1719

1820
val highPriorityJobs: Set[Value] = Set(convert_to_wkw, export_tiff)
1921
val lowPriorityJobs: Set[Value] = values.diff(highPriorityJobs)

app/models/user/User.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,13 @@ class UserDAO @Inject()(sqlClient: SqlClient)(implicit ec: ExecutionContext)
168168
def findIdsByMultiUserId(multiUserId: ObjectId): Fox[Seq[ObjectId]] =
169169
run(q"SELECT _id FROM $existingCollectionName WHERE _multiUser = $multiUserId".as[ObjectId])
170170

171+
def findOldestActive: Fox[User] =
172+
for {
173+
r <- run(
174+
q"SELECT $columns FROM $existingCollectionName WHERE NOT isDeactivated ORDER BY created LIMIT 1".as[UsersRow])
175+
parsed <- parseFirst(r, "oldestActive")
176+
} yield parsed
177+
171178
def buildSelectionPredicates(isEditableOpt: Option[Boolean],
172179
isTeamManagerOrAdminOpt: Option[Boolean],
173180
isAdminOpt: Option[Boolean],

app/utils/BuildInfoService.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package utils
2+
3+
import com.scalableminds.util.mvc.ApiVersioning
4+
import controllers.ReleaseInformationDAO
5+
import play.api.libs.json.{JsObject, Json}
6+
7+
import javax.inject.Inject
8+
import scala.concurrent.{ExecutionContext, Future}
9+
10+
class BuildInfoService @Inject()(releaseInformationDAO: ReleaseInformationDAO, storeModules: StoreModules)
11+
extends ApiVersioning {
12+
13+
def buildInfoJson(implicit ec: ExecutionContext): Future[JsObject] =
14+
for {
15+
schemaVersion <- releaseInformationDAO.getSchemaVersion.futureBox
16+
} yield
17+
Json.obj(
18+
"webknossos" -> Json.toJson(
19+
webknossos.BuildInfo.toMap.view.mapValues(_.toString).filterKeys(_ != "certificatePublicKey").toMap),
20+
"schemaVersion" -> schemaVersion.toOption,
21+
"httpApiVersioning" -> apiVersioningInfo,
22+
"localDataStoreEnabled" -> storeModules.localDataStoreEnabled,
23+
"localTracingStoreEnabled" -> storeModules.localTracingStoreEnabled
24+
)
25+
}

conf/messages

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,13 +344,11 @@ job.inferNeurons.notAllowed.organization = Currently neuron inferral is only all
344344
job.inferNeurons.annotationIdEvalParamsMissing=A evaluation of the neuron inferral jobs was requested but no annotation was supplied.
345345
job.inferMitochondria.notAllowed.onlySuperUsers=Currently mitochondria inferral is only allowed for super users.
346346
job.meshFile.notAllowed.organization = Calculating mesh files is only allowed for datasets of your own organization.
347-
job.exportTiff.notAllowed.organization=Exporting tiff files is only allowed for datasets of your own organization.
348347
job.segmentIndexFile.notAllowed.organization = Calculating segment index files is only allowed for datasets of your own organization.
349348
job.globalizeFloodfill.notAllowed.organization = Globalizing floodfills is only allowed for datasets of your own organization.
350349
job.materializeVolumeAnnotation.notAllowed.organization = Materializing volume annotations is only allowed for datasets of your own organization.
351350
job.noWorkerForDatastoreAndJob = No webknossos-worker supporting the requested job is available for the selected datastore.
352351
job.emailNotifactionsDisabled = Email notifications are not enabled for this job type.
353-
job.renderAnimation.notAllowed.organization = Rendering animations is only allowed for datasets of your own organization.
354352
job.alignSections.notAllowed.organization = Aligning sections is only allowed for datasets of your own organization.
355353
job.additionalCoordinates.invalid = The passed additional coordinates are invalid.
356354
job.trainModel.notAllowed.organization = Training AI models is only allowed for datasets of your own organization.

0 commit comments

Comments
 (0)