Просмотр исходного кода

Merge pull request #10933 from overleaf/ab-hackathon-split-tests-fancy-dash

[web/analytics] Split test dashboard improvements

GitOrigin-RevId: 7711060111c043a3002f30381c292249a84c320e
Alexandre Bourdin 3 лет назад
Родитель
Сommit
55e05d2fb6

+ 65 - 0
package-lock.json

@@ -12655,6 +12655,31 @@
         "is-regex": "^1.0.3"
         "is-regex": "^1.0.3"
       }
       }
     },
     },
+    "node_modules/chart.js": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.0.1.tgz",
+      "integrity": "sha512-5/8/9eBivwBZK81mKvmIwTb2Pmw4D/5h1RK9fBWZLLZ8mCJ+kfYNmV9rMrGoa5Hgy2/wVDBMLSUDudul2/9ihA==",
+      "engines": {
+        "pnpm": "^7.0.0"
+      }
+    },
+    "node_modules/chartjs-adapter-moment": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/chartjs-adapter-moment/-/chartjs-adapter-moment-1.0.1.tgz",
+      "integrity": "sha512-Uz+nTX/GxocuqXpGylxK19YG4R3OSVf8326D+HwSTsNw1LgzyIGRo+Qujwro1wy6X+soNSnfj5t2vZ+r6EaDmA==",
+      "peerDependencies": {
+        "chart.js": ">=3.0.0",
+        "moment": "^2.10.2"
+      }
+    },
+    "node_modules/chartjs-plugin-datalabels": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.2.0.tgz",
+      "integrity": "sha512-14ZU30lH7n89oq+A4bWaJPnAG8a7ZTk7dKf48YAzMvJjQtjrgg5Dpk9f+LbjCF6bpx3RAGTeL13IXpKQYyRvlw==",
+      "peerDependencies": {
+        "chart.js": ">=3.0.0"
+      }
+    },
     "node_modules/check-error": {
     "node_modules/check-error": {
       "version": "1.0.2",
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
       "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
@@ -29031,6 +29056,15 @@
         "react-dom": ">=16.3.0"
         "react-dom": ">=16.3.0"
       }
       }
     },
     },
+    "node_modules/react-chartjs-2": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.0.1.tgz",
+      "integrity": "sha512-u38C9OxynlNCBp+79grgXRs7DSJ9w8FuQ5/HO5FbYBbri8HSZW+9SWgjVshLkbXBfXnMGWakbHEtvN0nL2UG7Q==",
+      "peerDependencies": {
+        "chart.js": "^4.0.0",
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+      }
+    },
     "node_modules/react-dnd": {
     "node_modules/react-dnd": {
       "version": "11.1.3",
       "version": "11.1.3",
       "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-11.1.3.tgz",
       "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-11.1.3.tgz",
@@ -38973,6 +39007,9 @@
         "bunyan": "^1.8.15",
         "bunyan": "^1.8.15",
         "cache-flow": "^1.7.4",
         "cache-flow": "^1.7.4",
         "celebrate": "^10.0.1",
         "celebrate": "^10.0.1",
+        "chart.js": "^4.0.1",
+        "chartjs-adapter-moment": "^1.0.1",
+        "chartjs-plugin-datalabels": "^2.2.0",
         "classnames": "^2.2.6",
         "classnames": "^2.2.6",
         "codemirror": "~5.33.0",
         "codemirror": "~5.33.0",
         "connect-redis": "^3.1.0",
         "connect-redis": "^3.1.0",
@@ -39048,6 +39085,7 @@
         "qrcode": "^1.4.4",
         "qrcode": "^1.4.4",
         "react": "^17.0.2",
         "react": "^17.0.2",
         "react-bootstrap": "^0.33.1",
         "react-bootstrap": "^0.33.1",
+        "react-chartjs-2": "^5.0.1",
         "react-dnd": "^11.1.3",
         "react-dnd": "^11.1.3",
         "react-dnd-html5-backend": "^11.1.3",
         "react-dnd-html5-backend": "^11.1.3",
         "react-dom": "^17.0.2",
         "react-dom": "^17.0.2",
@@ -49552,6 +49590,9 @@
         "chai": "^4.3.6",
         "chai": "^4.3.6",
         "chai-as-promised": "^7.1.1",
         "chai-as-promised": "^7.1.1",
         "chai-exclude": "^2.0.3",
         "chai-exclude": "^2.0.3",
+        "chart.js": "^4.0.1",
+        "chartjs-adapter-moment": "^1.0.1",
+        "chartjs-plugin-datalabels": "^2.2.0",
         "cheerio": "^1.0.0-rc.3",
         "cheerio": "^1.0.0-rc.3",
         "classnames": "^2.2.6",
         "classnames": "^2.2.6",
         "codemirror": "~5.33.0",
         "codemirror": "~5.33.0",
@@ -49664,6 +49705,7 @@
         "qrcode": "^1.4.4",
         "qrcode": "^1.4.4",
         "react": "^17.0.2",
         "react": "^17.0.2",
         "react-bootstrap": "^0.33.1",
         "react-bootstrap": "^0.33.1",
+        "react-chartjs-2": "^5.0.1",
         "react-dnd": "^11.1.3",
         "react-dnd": "^11.1.3",
         "react-dnd-html5-backend": "^11.1.3",
         "react-dnd-html5-backend": "^11.1.3",
         "react-dom": "^17.0.2",
         "react-dom": "^17.0.2",
@@ -55239,6 +55281,23 @@
         "is-regex": "^1.0.3"
         "is-regex": "^1.0.3"
       }
       }
     },
     },
+    "chart.js": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.0.1.tgz",
+      "integrity": "sha512-5/8/9eBivwBZK81mKvmIwTb2Pmw4D/5h1RK9fBWZLLZ8mCJ+kfYNmV9rMrGoa5Hgy2/wVDBMLSUDudul2/9ihA=="
+    },
+    "chartjs-adapter-moment": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/chartjs-adapter-moment/-/chartjs-adapter-moment-1.0.1.tgz",
+      "integrity": "sha512-Uz+nTX/GxocuqXpGylxK19YG4R3OSVf8326D+HwSTsNw1LgzyIGRo+Qujwro1wy6X+soNSnfj5t2vZ+r6EaDmA==",
+      "requires": {}
+    },
+    "chartjs-plugin-datalabels": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.2.0.tgz",
+      "integrity": "sha512-14ZU30lH7n89oq+A4bWaJPnAG8a7ZTk7dKf48YAzMvJjQtjrgg5Dpk9f+LbjCF6bpx3RAGTeL13IXpKQYyRvlw==",
+      "requires": {}
+    },
     "check-error": {
     "check-error": {
       "version": "1.0.2",
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
       "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
@@ -68640,6 +68699,12 @@
         "warning": "^3.0.0"
         "warning": "^3.0.0"
       }
       }
     },
     },
+    "react-chartjs-2": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.0.1.tgz",
+      "integrity": "sha512-u38C9OxynlNCBp+79grgXRs7DSJ9w8FuQ5/HO5FbYBbri8HSZW+9SWgjVshLkbXBfXnMGWakbHEtvN0nL2UG7Q==",
+      "requires": {}
+    },
     "react-dnd": {
     "react-dnd": {
       "version": "11.1.3",
       "version": "11.1.3",
       "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-11.1.3.tgz",
       "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-11.1.3.tgz",

+ 29 - 6
services/web/app/src/Features/SplitTests/SplitTestManager.js

@@ -7,7 +7,7 @@ const ALPHA_PHASE = 'alpha'
 const BETA_PHASE = 'beta'
 const BETA_PHASE = 'beta'
 const RELEASE_PHASE = 'release'
 const RELEASE_PHASE = 'release'
 
 
-async function getSplitTests({ name, activeOnly, archivedOnly }) {
+async function getSplitTests({ name, phase, type, activeOnly, archivedOnly }) {
   const filters = {}
   const filters = {}
   if (name && name !== '') {
   if (name && name !== '') {
     filters.name = { $regex: _.escapeRegExp(name) }
     filters.name = { $regex: _.escapeRegExp(name) }
@@ -15,6 +15,32 @@ async function getSplitTests({ name, activeOnly, archivedOnly }) {
   if (activeOnly) {
   if (activeOnly) {
     filters.$where = 'this.versions[this.versions.length - 1].active === true'
     filters.$where = 'this.versions[this.versions.length - 1].active === true'
   }
   }
+  if (type === 'split-test') {
+    const query =
+      'this.versions[this.versions.length - 1].analyticsEnabled === true'
+    if (filters.$where) {
+      filters.$where += `&& ${query}`
+    } else {
+      filters.$where = query
+    }
+  }
+  if (type === 'gradual-rollout') {
+    const query =
+      'this.versions[this.versions.length - 1].analyticsEnabled === false'
+    if (filters.$where) {
+      filters.$where += `&& ${query}`
+    } else {
+      filters.$where = query
+    }
+  }
+  if (['alpha', 'beta', 'release'].includes(phase)) {
+    const query = `this.versions[this.versions.length - 1].phase === "${phase}"`
+    if (filters.$where) {
+      filters.$where += `&& ${query}`
+    } else {
+      filters.$where = query
+    }
+  }
   if (archivedOnly) {
   if (archivedOnly) {
     filters.archived = true
     filters.archived = true
   } else {
   } else {
@@ -205,6 +231,7 @@ async function revertToPreviousVersion(name, versionNumber) {
   }
   }
   const previousVersionCopy = previousVersion.toObject()
   const previousVersionCopy = previousVersion.toObject()
   previousVersionCopy.versionNumber = lastVersion.versionNumber + 1
   previousVersionCopy.versionNumber = lastVersion.versionNumber + 1
+  previousVersionCopy.createdAt = new Date()
   splitTest.versions.push(previousVersionCopy)
   splitTest.versions.push(previousVersionCopy)
   return _saveSplitTest(splitTest)
   return _saveSplitTest(splitTest)
 }
 }
@@ -218,11 +245,7 @@ async function archive(name) {
     throw new OError(`Split test with ID '${name}' is already archived`)
     throw new OError(`Split test with ID '${name}' is already archived`)
   }
   }
   splitTest.archived = true
   splitTest.archived = true
-  const previousVersionCopy =
-    SplitTestUtils.getCurrentVersion(splitTest).toObject()
-  previousVersionCopy.versionNumber += 1
-  previousVersionCopy.active = false
-  splitTest.versions.push(previousVersionCopy)
+  splitTest.archivedAt = new Date()
   return _saveSplitTest(splitTest)
   return _saveSplitTest(splitTest)
 }
 }
 
 

+ 4 - 0
services/web/app/src/models/SplitTest.js

@@ -140,6 +140,10 @@ const SplitTestSchema = new Schema({
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
   },
   },
+  archivedAt: {
+    type: Date,
+    required: false,
+  },
   badgeInfo: {
   badgeInfo: {
     type: BadgeInfoSchema,
     type: BadgeInfoSchema,
     required: false,
     required: false,

+ 1 - 1
services/web/app/views/layout/navbar-marketing.pug

@@ -58,7 +58,7 @@ nav.navbar.navbar-default.navbar-main
 										a(href=settings.adminUrl) Switch to Admin
 										a(href=settings.adminUrl) Switch to Admin
 								if canDisplaySplitTestMenu
 								if canDisplaySplitTestMenu
 									li
 									li
-										a(href="/admin/split-test") Manage Split Tests
+										a(href="/admin/split-test") Manage Feature Flags
 								if canDisplaySurveyMenu
 								if canDisplaySurveyMenu
 									li
 									li
 										a(href="/admin/survey") Manage Surveys
 										a(href="/admin/survey") Manage Surveys

+ 1 - 1
services/web/app/views/layout/navbar.pug

@@ -40,7 +40,7 @@ nav.navbar.navbar-default.navbar-main
 										a(href=settings.adminUrl) Switch to Admin
 										a(href=settings.adminUrl) Switch to Admin
 								if canDisplaySplitTestMenu
 								if canDisplaySplitTestMenu
 									li
 									li
-										a(href="/admin/split-test") Manage Split Tests
+										a(href="/admin/split-test") Manage Feature Flags
 								if canDisplaySurveyMenu
 								if canDisplaySurveyMenu
 									li
 									li
 										a(href="/admin/survey") Manage Surveys
 										a(href="/admin/survey") Manage Surveys

+ 14 - 0
services/web/frontend/stylesheets/modules/admin-panel.less

@@ -1,4 +1,18 @@
+@import '../../../frontend/stylesheets/core/variables.less';
+
 .admin-panel-pagination {
 .admin-panel-pagination {
   display: flex;
   display: flex;
   justify-content: center;
   justify-content: center;
 }
 }
+
+.phase-badge {
+  display: inline-block;
+  border-radius: @badge-border-radius;
+  font-size: @font-size-small;
+  font-weight: @badge-font-weight;
+  line-height: @badge-line-height;
+  color: @badge-color;
+  white-space: nowrap;
+  text-align: center;
+  padding: 3px 7px;
+}

+ 0 - 1
services/web/local-dev.env

@@ -3,7 +3,6 @@ QUEUES_REDIS_HOST=localhost
 PUBSUB_REDIS_HOST=localhost
 PUBSUB_REDIS_HOST=localhost
 RATELIMITER_REDIS_HOST=localhost
 RATELIMITER_REDIS_HOST=localhost
 GCLOUD_2_REDIS_HOST=localhost
 GCLOUD_2_REDIS_HOST=localhost
-QUEUES_REDIS_HOST=localhost
 POSTGRES_HOST=localhost
 POSTGRES_HOST=localhost
 MONGO_URL=mongodb://localhost/sharelatex
 MONGO_URL=mongodb://localhost/sharelatex
 SHARELATEX_LDAP_URL=ldap://localhost:22389
 SHARELATEX_LDAP_URL=ldap://localhost:22389

+ 4 - 0
services/web/package.json

@@ -136,6 +136,9 @@
     "bunyan": "^1.8.15",
     "bunyan": "^1.8.15",
     "cache-flow": "^1.7.4",
     "cache-flow": "^1.7.4",
     "celebrate": "^10.0.1",
     "celebrate": "^10.0.1",
+    "chart.js": "^4.0.1",
+    "chartjs-adapter-moment": "^1.0.1",
+    "chartjs-plugin-datalabels": "^2.2.0",
     "classnames": "^2.2.6",
     "classnames": "^2.2.6",
     "codemirror": "~5.33.0",
     "codemirror": "~5.33.0",
     "connect-redis": "^3.1.0",
     "connect-redis": "^3.1.0",
@@ -211,6 +214,7 @@
     "qrcode": "^1.4.4",
     "qrcode": "^1.4.4",
     "react": "^17.0.2",
     "react": "^17.0.2",
     "react-bootstrap": "^0.33.1",
     "react-bootstrap": "^0.33.1",
+    "react-chartjs-2": "^5.0.1",
     "react-dnd": "^11.1.3",
     "react-dnd": "^11.1.3",
     "react-dnd-html5-backend": "^11.1.3",
     "react-dnd-html5-backend": "^11.1.3",
     "react-dom": "^17.0.2",
     "react-dom": "^17.0.2",