diff --git a/frontend/src/metabase/admin/datamodel/components/FieldRemapping.jsx b/frontend/src/metabase/admin/datamodel/components/FieldRemapping.jsx
index 7d736a34ddfdc4414d07fa767d9d31542ace0caa..dc5fffbe361e03876949ebf23be71b9138f7d7f2 100644
--- a/frontend/src/metabase/admin/datamodel/components/FieldRemapping.jsx
+++ b/frontend/src/metabase/admin/datamodel/components/FieldRemapping.jsx
@@ -280,7 +280,7 @@ export default class FieldRemapping extends React.Component {
                 dimensions: [],
                 fks: this.getForeignKeys(),
               }}
-              tableMetadata={table}
+              table={table}
               onFieldChange={this.onForeignKeyFieldChange}
               hideSectionHeader
             />
diff --git a/frontend/src/metabase/components/CollectionLanding.jsx b/frontend/src/metabase/components/CollectionLanding.jsx
index b9c789e77a59b32bcb22703ceff01fe28ca067bd..a12581ded9c5b74e941fb0022bce14715545dbc2 100644
--- a/frontend/src/metabase/components/CollectionLanding.jsx
+++ b/frontend/src/metabase/components/CollectionLanding.jsx
@@ -153,7 +153,7 @@ class DefaultLanding extends React.Component {
       await Promise.all(
         this.state.selectedItems.map(item => item.setCollection(collection)),
       );
-      this.setState({ selectedItems: null, selectedAction: null });
+      this.handleCloseModal();
     } finally {
       this.handleBulkActionSuccess();
     }
@@ -166,6 +166,10 @@ class DefaultLanding extends React.Component {
     this.props.onSelectNone();
   };
 
+  handleCloseModal = () => {
+    this.setState({ selectedItems: null, selectedAction: null });
+  };
+
   render() {
     const {
       ancestors,
@@ -499,14 +503,12 @@ class DefaultLanding extends React.Component {
         </Box>
         {!_.isEmpty(selectedItems) &&
           selectedAction == "copy" && (
-            <Modal>
+            <Modal onClose={this.handleCloseModal}>
               <CollectionCopyEntityModal
                 entityObject={selectedItems[0]}
-                onClose={() =>
-                  this.setState({ selectedItems: null, selectedAction: null })
-                }
+                onClose={this.handleCloseModal}
                 onSaved={newEntityObject => {
-                  this.setState({ selectedItems: null, selectedAction: null });
+                  this.handleCloseModal();
                   this.handleBulkActionSuccess();
                 }}
               />
@@ -514,16 +516,14 @@ class DefaultLanding extends React.Component {
           )}
         {!_.isEmpty(selectedItems) &&
           selectedAction == "move" && (
-            <Modal>
+            <Modal onClose={this.handleCloseModal}>
               <CollectionMoveModal
                 title={
                   selectedItems.length > 1
                     ? t`Move ${selectedItems.length} items?`
                     : t`Move "${selectedItems[0].getName()}"?`
                 }
-                onClose={() =>
-                  this.setState({ selectedItems: null, selectedAction: null })
-                }
+                onClose={this.handleCloseModal}
                 onMove={this.handleBulkMove}
               />
             </Modal>
diff --git a/frontend/src/metabase/components/Header.jsx b/frontend/src/metabase/components/Header.jsx
index e097f45daf5990f1871797301514c6603e82f8df..d4060974d33201c7ec08a8b23e533abb38f516cd 100644
--- a/frontend/src/metabase/components/Header.jsx
+++ b/frontend/src/metabase/components/Header.jsx
@@ -30,7 +30,7 @@ export default class Header extends Component {
     this.updateHeaderHeight();
   }
 
-  componentWillUpdate() {
+  componentDidUpdate() {
     const modalIsOpen = !!this.props.headerModalMessage;
     if (modalIsOpen) {
       this.updateHeaderHeight();
diff --git a/frontend/src/metabase/containers/CollectionMoveModal.jsx b/frontend/src/metabase/containers/CollectionMoveModal.jsx
index 8792b8b6bbada1f33815c94efaad6c84a3592939..e2b6cc855781a4cbfae4a48bf7888834b39ce6ba 100644
--- a/frontend/src/metabase/containers/CollectionMoveModal.jsx
+++ b/frontend/src/metabase/containers/CollectionMoveModal.jsx
@@ -3,10 +3,9 @@ import PropTypes from "prop-types";
 
 import { t } from "c-3po";
 
-import { Flex, Box } from "grid-styled";
-import Subhead from "metabase/components/Subhead";
+import { Flex } from "grid-styled";
 import Button from "metabase/components/Button";
-import Icon from "metabase/components/Icon";
+import ModalContent from "metabase/components/ModalContent";
 
 import CollectionPicker from "metabase/containers/CollectionPicker";
 
@@ -41,15 +40,7 @@ class CollectionMoveModal extends React.Component {
     const { selectedCollectionId } = this.state;
 
     return (
-      <Box p={3}>
-        <Flex align="center" mb={2}>
-          <Subhead>{this.props.title}</Subhead>
-          <Icon
-            name="close"
-            className="ml-auto"
-            onClick={() => this.props.onClose()}
-          />
-        </Flex>
+      <ModalContent title={this.props.title} onClose={this.props.onClose}>
         <CollectionPicker
           value={selectedCollectionId}
           onChange={selectedCollectionId =>
@@ -78,7 +69,7 @@ class CollectionMoveModal extends React.Component {
             {t`Move`}
           </Button>
         </Flex>
-      </Box>
+      </ModalContent>
     );
   }
 }
diff --git a/frontend/src/metabase/dashboard/components/grid/GridLayout.jsx b/frontend/src/metabase/dashboard/components/grid/GridLayout.jsx
index 92e6426cbad4a8976333f7b81698caf83db92909..49887e293226adafeb6fc94f74c38d7f8648c3ea 100644
--- a/frontend/src/metabase/dashboard/components/grid/GridLayout.jsx
+++ b/frontend/src/metabase/dashboard/components/grid/GridLayout.jsx
@@ -228,11 +228,12 @@ export default class GridLayout extends Component {
     }
   }
 
+  // generate one row of the grid, it will repeat because it's a background image
   getGridBackground() {
-    let { margin, cols } = this.props;
-    let cellSize = this.getCellSize();
-    return (
-      `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='${cellSize.width *
+    const { margin, cols } = this.props;
+    const cellSize = this.getCellSize();
+    const svg =
+      `<svg xmlns='http://www.w3.org/2000/svg' width='${cellSize.width *
         cols}' height='${cellSize.height}'>` +
       _(cols)
         .times(
@@ -246,8 +247,8 @@ export default class GridLayout extends Component {
             )}' height='${cellSize.height - margin - 3}'/>`,
         )
         .join("") +
-      `</svg>")`
-    );
+      `</svg>`;
+    return `url("data:image/svg+xml;utf8,${encodeURIComponent(svg)}")`;
   }
 
   render() {
diff --git a/frontend/src/metabase/modes/components/actions/PivotByAction.jsx b/frontend/src/metabase/modes/components/actions/PivotByAction.jsx
index 3916a206742668eedf6cc009b06b5266ba691456..2f01016402006c9070fba27f87d102d960cdbc5c 100644
--- a/frontend/src/metabase/modes/components/actions/PivotByAction.jsx
+++ b/frontend/src/metabase/modes/components/actions/PivotByAction.jsx
@@ -62,7 +62,6 @@ export default (name: string, icon: string, fieldFilter: FieldFilter) => ({
           breakoutOptions={breakoutOptions}
           onCommitBreakout={breakout => {
             const nextCard = question.pivot([breakout], dimensions).card();
-
             if (nextCard) {
               onChangeCardAndRun({ nextCard });
             }
diff --git a/frontend/src/metabase/query_builder/components/AggregationPopover.jsx b/frontend/src/metabase/query_builder/components/AggregationPopover.jsx
index a834d1e479a24d6ad8d2e843d84811ac4eeb8129..f1953b94c762b7ee45fcfa3a0fffa2bd12e332c2 100644
--- a/frontend/src/metabase/query_builder/components/AggregationPopover.jsx
+++ b/frontend/src/metabase/query_builder/components/AggregationPopover.jsx
@@ -348,7 +348,7 @@ export default class AggregationPopover extends Component {
           <FieldList
             className={"text-green"}
             maxHeight={this.props.maxHeight - (this.state.headerHeight || 0)}
-            tableMetadata={tableMetadata}
+            table={tableMetadata}
             field={fieldId}
             fieldOptions={query.aggregationFieldOptions(agg)}
             customFieldOptions={customFields}
diff --git a/frontend/src/metabase/query_builder/components/BreakoutPopover.jsx b/frontend/src/metabase/query_builder/components/BreakoutPopover.jsx
index 5570f0325b7245d1ae4ba1dd29b80595fd2f1519..11ebc48250a1f279e499e56a19c32b19a323f860 100644
--- a/frontend/src/metabase/query_builder/components/BreakoutPopover.jsx
+++ b/frontend/src/metabase/query_builder/components/BreakoutPopover.jsx
@@ -26,22 +26,29 @@ const BreakoutPopover = ({
   onClose,
   maxHeight,
   alwaysExpanded,
-}: Props) => (
-  <FieldList
-    className="text-green"
-    maxHeight={maxHeight}
-    field={breakout}
-    fieldOptions={breakoutOptions || query.breakoutOptions()}
-    onFieldChange={field => {
-      onCommitBreakout(field);
-      if (onClose) {
-        onClose();
-      }
-    }}
-    tableMetadata={query.tableMetdata()}
-    enableSubDimensions
-    alwaysExpanded={alwaysExpanded}
-  />
-);
+}: Props) => {
+  const table = query.table();
+  // FieldList requires table
+  if (!table) {
+    return null;
+  }
+  return (
+    <FieldList
+      className="text-green"
+      maxHeight={maxHeight}
+      field={breakout}
+      fieldOptions={breakoutOptions || query.breakoutOptions()}
+      onFieldChange={field => {
+        onCommitBreakout(field);
+        if (onClose) {
+          onClose();
+        }
+      }}
+      table={table}
+      enableSubDimensions
+      alwaysExpanded={alwaysExpanded}
+    />
+  );
+};
 
 export default BreakoutPopover;
diff --git a/frontend/src/metabase/query_builder/components/BreakoutWidget.jsx b/frontend/src/metabase/query_builder/components/BreakoutWidget.jsx
index 647ccabf2f6f9ce96d0624092620e5e0cab93f66..5055b3aa4a41d49a35f942bebc82d4099b9506ca 100644
--- a/frontend/src/metabase/query_builder/components/BreakoutWidget.jsx
+++ b/frontend/src/metabase/query_builder/components/BreakoutWidget.jsx
@@ -58,7 +58,7 @@ export default class BreakoutWidget extends Component {
         >
           <FieldList
             className={"text-green"}
-            tableMetadata={this.props.tableMetadata}
+            table={this.props.tableMetadata}
             field={this.props.field}
             fieldOptions={this.props.fieldOptions}
             customFieldOptions={this.props.customFieldOptions}
diff --git a/frontend/src/metabase/query_builder/components/FieldList.jsx b/frontend/src/metabase/query_builder/components/FieldList.jsx
index 325c4bf5e05271debb44f51f73f5c5621d6b4658..4fccc60588a56d4cec9882cecb8f5eaec5fa9660 100644
--- a/frontend/src/metabase/query_builder/components/FieldList.jsx
+++ b/frontend/src/metabase/query_builder/components/FieldList.jsx
@@ -35,7 +35,7 @@ type Props = {
   // HACK: for segments
   onFilterChange?: (filter: any) => void,
 
-  tableMetadata: Table,
+  table: Table,
 
   alwaysExpanded?: boolean,
   enableSubDimensions?: boolean,
@@ -61,13 +61,8 @@ export default class FieldList extends Component {
   }
 
   componentWillReceiveProps(newProps) {
-    let {
-      tableMetadata,
-      fieldOptions,
-      segmentOptions,
-      hideSectionHeader,
-    } = newProps;
-    let tableName = tableMetadata.display_name;
+    let { table, fieldOptions, segmentOptions, hideSectionHeader } = newProps;
+    let tableName = table.display_name;
 
     let specialOptions = [];
     if (segmentOptions) {
@@ -110,11 +105,7 @@ export default class FieldList extends Component {
   };
 
   renderItemExtra = item => {
-    const {
-      field,
-      enableSubDimensions,
-      tableMetadata: { metadata },
-    } = this.props;
+    const { field, enableSubDimensions, table: { metadata } } = this.props;
 
     return (
       <div className="Field-extra flex align-center">
@@ -167,7 +158,7 @@ export default class FieldList extends Component {
   };
 
   renderSubDimensionTrigger(dimension) {
-    const { field, tableMetadata: { metadata } } = this.props;
+    const { field, table: { metadata } } = this.props;
     const subDimension = dimension.isSameBaseDimension(field)
       ? Dimension.parseMBQL(field, metadata)
       : dimension.defaultDimension();
@@ -181,16 +172,11 @@ export default class FieldList extends Component {
   }
 
   renderSegmentTooltip(segment) {
-    let { tableMetadata } = this.props;
+    let { table } = this.props;
     return (
       <div className="p1">
         <Tooltip
-          tooltip={
-            <QueryDefinitionTooltip
-              object={segment}
-              tableMetadata={tableMetadata}
-            />
-          }
+          tooltip={<QueryDefinitionTooltip object={segment} table={table} />}
         >
           <span className="QuestionTooltipTarget" />
         </Tooltip>
diff --git a/frontend/src/metabase/query_builder/components/FieldWidget.jsx b/frontend/src/metabase/query_builder/components/FieldWidget.jsx
index f724c3795ce1474f80a13aeb775e2965b7be0580..8392c9e0fdb9d5686f7ef2467a34dd89ee63dc93 100644
--- a/frontend/src/metabase/query_builder/components/FieldWidget.jsx
+++ b/frontend/src/metabase/query_builder/components/FieldWidget.jsx
@@ -56,7 +56,7 @@ export default class FieldWidget extends Component {
         <Popover ref="popover" className="FieldPopover" onClose={this.toggle}>
           <FieldList
             className={"text-" + this.props.color}
-            tableMetadata={this.props.tableMetadata}
+            table={this.props.tableMetadata}
             field={this.props.field}
             fieldOptions={this.props.fieldOptions}
             customFieldOptions={this.props.customFieldOptions}
diff --git a/frontend/src/metabase/query_builder/components/filters/FilterPopover.jsx b/frontend/src/metabase/query_builder/components/filters/FilterPopover.jsx
index 8b0fa84cc486a8a18e653ecd421a52d8ab59eb37..43f3466e563d3fa1fe2d6a0924d2bc33b62003f2 100644
--- a/frontend/src/metabase/query_builder/components/filters/FilterPopover.jsx
+++ b/frontend/src/metabase/query_builder/components/filters/FilterPopover.jsx
@@ -319,7 +319,7 @@ export default class FilterPopover extends Component {
             field={fieldRef}
             fieldOptions={query.filterFieldOptions(filter)}
             segmentOptions={query.filterSegmentOptions(filter)}
-            tableMetadata={query.table()}
+            table={query.table()}
             onFieldChange={this.setField}
             onFilterChange={this.commitFilter}
           />
diff --git a/frontend/src/metabase/questions/components/CollectionBadge.jsx b/frontend/src/metabase/questions/components/CollectionBadge.jsx
index c8f84a6623bd3755873b7ef3b609960cc027e747..33bdbaa4d60d55e40e088b4d19b957b1d7e35015 100644
--- a/frontend/src/metabase/questions/components/CollectionBadge.jsx
+++ b/frontend/src/metabase/questions/components/CollectionBadge.jsx
@@ -13,14 +13,19 @@ import cx from "classnames";
   entityType: "collections",
   entityId: (state, props) => props.collectionId || "root",
   wrapped: true,
+  loadingAndErrorWrapper: false,
+  properties: ["name"],
 })
 class CollectionBadge extends React.Component {
   render() {
-    const { analyticsContext, object } = this.props;
+    const { analyticsContext, object, className } = this.props;
+    if (!object) {
+      return null;
+    }
     return (
       <Link
         to={Urls.collection(object.id)}
-        className={cx("inline-block link")}
+        className={cx(className, "block link")}
         data-metabase-event={`${analyticsContext};Collection Badge Click`}
       >
         <Flex align="center">
diff --git a/frontend/test/metabase/modes/components/actions/PivotByAction.unit.spec.js b/frontend/test/metabase/modes/components/actions/PivotByAction.unit.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..598c042db6bbecaac9d05f6b0f8abffb316a939f
--- /dev/null
+++ b/frontend/test/metabase/modes/components/actions/PivotByAction.unit.spec.js
@@ -0,0 +1,51 @@
+/* eslint-disable flowtype/require-valid-file-annotation */
+
+import React from "react";
+
+import { question } from "__support__/sample_dataset_fixture";
+
+import { mount } from "enzyme";
+import { click } from "__support__/enzyme_utils";
+
+import PivotByAction from "metabase/modes/components/actions/PivotByAction";
+
+describe("PivotByAction", () => {
+  it("should return a broken out card", () => {
+    const TestPivotByAction = PivotByAction("Test", "test", () => true);
+
+    const countQuestion = question
+      .query()
+      .addAggregation(["count"])
+      .question()
+      .setDisplay("scalar");
+
+    const actions = TestPivotByAction({ question: countQuestion });
+    expect(actions).toHaveLength(1);
+
+    const PopoverComponent = actions[0].popover;
+
+    const onChangeCardAndRun = jest.fn();
+    const wrapper = mount(
+      <PopoverComponent onChangeCardAndRun={onChangeCardAndRun} />,
+    );
+
+    click(wrapper.find(".List-item a").first());
+
+    expect(onChangeCardAndRun).toHaveBeenLastCalledWith({
+      nextCard: {
+        dataset_query: {
+          database: 1,
+          query: {
+            aggregation: [["count"]],
+            breakout: [["datetime-field", ["field-id", 1], "day"]],
+            "source-table": 1,
+          },
+          type: "query",
+        },
+        display: "line",
+        name: null,
+        visualization_settings: {},
+      },
+    });
+  });
+});
diff --git a/frontend/test/metabase/query_builder/components/FieldList.e2e.spec.js b/frontend/test/metabase/query_builder/components/FieldList.e2e.spec.js
index d36cf498f30b06a4512efae5374278301410f080..70aa5db39ec5fbdb93949700bbfbaeaa71744ee8 100644
--- a/frontend/test/metabase/query_builder/components/FieldList.e2e.spec.js
+++ b/frontend/test/metabase/query_builder/components/FieldList.e2e.spec.js
@@ -32,7 +32,7 @@ import Filter from "metabase/query_builder/components/Filter";
 
 const getFieldList = (query, fieldOptions, segmentOptions) => (
   <FieldList
-    tableMetadata={query.tableMetadata()}
+    table={query.table()}
     fieldOptions={fieldOptions}
     segmentOptions={segmentOptions}
     customFieldOptions={query.expressions()}
diff --git a/modules/drivers/bigquery/src/metabase/driver/bigquery.clj b/modules/drivers/bigquery/src/metabase/driver/bigquery.clj
index a7aa9d5e0ece735d243ebb7730aa24ddf21dfd41..e86fa902f1cd64fd848747cec80c45626a178de6 100644
--- a/modules/drivers/bigquery/src/metabase/driver/bigquery.clj
+++ b/modules/drivers/bigquery/src/metabase/driver/bigquery.clj
@@ -147,6 +147,7 @@
     "DATETIME"  :type/DateTime
     "TIMESTAMP" :type/DateTime
     "TIME"      :type/Time
+    "NUMERIC"   :type/Decimal
     :type/*))
 
 (defn- table-schema->metabase-field-info [^TableSchema schema]
@@ -217,6 +218,7 @@
   {"BOOLEAN"   (constantly #(Boolean/parseBoolean %))
    "FLOAT"     (constantly #(Double/parseDouble %))
    "INTEGER"   (constantly #(Long/parseLong %))
+   "NUMERIC"   (constantly #(bigdec %))
    "RECORD"    (constantly identity)
    "STRING"    (constantly identity)
    "DATE"      parse-timestamp-str
diff --git a/modules/drivers/bigquery/test/metabase/test/data/bigquery.clj b/modules/drivers/bigquery/test/metabase/test/data/bigquery.clj
index 0f2d0721a32ae5075a18ca65c92f0b30df8c00ab..8e817cb3a725e0be2f02c5ebfe9a7eab9099cc1f 100644
--- a/modules/drivers/bigquery/test/metabase/test/data/bigquery.clj
+++ b/modules/drivers/bigquery/test/metabase/test/data/bigquery.clj
@@ -137,7 +137,7 @@
    :type/Date           :TIMESTAMP
    :type/DateTime       :TIMESTAMP
    :type/DateTimeWithTZ :TIMESTAMP
-   :type/Decimal        :FLOAT
+   :type/Decimal        :NUMERIC
    :type/Dictionary     :RECORD
    :type/Float          :FLOAT
    :type/Integer        :INTEGER
diff --git a/src/metabase/api/activity.clj b/src/metabase/api/activity.clj
index d27a9c5dee0976628a8d578853df5eba5d14b618..187eca09980338ec448a6ded46d715f03ca8dd3c 100644
--- a/src/metabase/api/activity.clj
+++ b/src/metabase/api/activity.clj
@@ -13,8 +13,8 @@
              [hydrate :refer [hydrate]]]))
 
 (defn- dashcard-activity? [activity]
-  (contains? #{:dashboard-add-cards :dashboard-remove-cards}
-             (:topic activity)))
+  (#{:dashboard-add-cards :dashboard-remove-cards}
+   (:topic activity)))
 
 (defn- activities->referenced-objects
   "Get a map of model name to a set of referenced IDs in these ACTIVITIES.
diff --git a/src/metabase/automagic_dashboards/core.clj b/src/metabase/automagic_dashboards/core.clj
index 2a19fbf7ef52d643acc04d996998dc516553c3f0..73f9b2ca4e943884a27148c7481c84a5a9b7ba50 100644
--- a/src/metabase/automagic_dashboards/core.clj
+++ b/src/metabase/automagic_dashboards/core.clj
@@ -1230,6 +1230,7 @@
                                         :group-by [:table_id]
                                         :having   [:= :%count.* 1]}))
                            (into #{} (map :table_id)))
+          ;; Table comprised entierly of join keys
           link-table? (->> (db/query {:select   [:table_id [:%count.* "count"]]
                                       :from     [Field]
                                       :where    [:and [:in :table_id (keys field-count)]
@@ -1269,7 +1270,7 @@
                    schema (concat [:schema schema])))
           (filter mi/can-read?)
           enhance-table-stats
-          (remove (comp (some-fn :link-table? :list-like? (comp zero? :num-fields)) :stats))
+          (remove (comp (some-fn :link-table? (comp zero? :num-fields)) :stats))
           (map (fn [table]
                  (let [root      (->root table)
                        rule      (->> root
@@ -1279,7 +1280,10 @@
                    {:url         (format "%stable/%s" public-endpoint (u/get-id table))
                     :title       (:full-name root)
                     :score       (+ (math/sq (:specificity rule))
-                                    (math/log (-> table :stats :num-fields)))
+                                    (math/log (-> table :stats :num-fields))
+                                    (if (-> table :stats :list-like?)
+                                      -10
+                                      0))
                     :description (:description dashboard)
                     :table       table
                     :rule        (:rule rule)})))
diff --git a/src/metabase/events/activity_feed.clj b/src/metabase/events/activity_feed.clj
index aad5306c961b1fede7ab226e31979af27c6a06e1..228c76dacefe25411b6cc653e49598cde04f18d3 100644
--- a/src/metabase/events/activity_feed.clj
+++ b/src/metabase/events/activity_feed.clj
@@ -15,7 +15,7 @@
             [toucan.db :as db]))
 
 (def ^:private activity-feed-topics
-  "The `Set` of event topics which are subscribed to for use in the Metabase activity feed."
+  "The set of event topics which are subscribed to for use in the Metabase activity feed."
   #{:alert-create
     :alert-delete
     :card-create
@@ -34,7 +34,7 @@
     :segment-create
     :segment-update
     :segment-delete
-    :user-login})
+    :user-login}) ; this is only used these days the first time someone logs in to record 'user-joined' events
 
 (def ^:private activity-feed-channel
   "Channel for receiving event notifications we want to subscribe to for the activity feed."
diff --git a/src/metabase/models/activity.clj b/src/metabase/models/activity.clj
index db2bf1da1807dae091fb16de150eef5fdd14dafa..c88a0e8cb79451273c6cb0d7ee79cd8236ecb999 100644
--- a/src/metabase/models/activity.clj
+++ b/src/metabase/models/activity.clj
@@ -2,6 +2,7 @@
   (:require [metabase
              [events :as events]
              [util :as u]]
+            [metabase.api.common :as api]
             [metabase.models
              [card :refer [Card]]
              [dashboard :refer [Dashboard]]
@@ -23,10 +24,26 @@
    "pulse"     Pulse
    "segment"   Segment})
 
-(defn- can-? [f {model :model, model-id :model_id, :as activity}]
+(defmulti can-?
+  "Implementation for `can-read?`/`can-write?` for items in the activity feed. Dispatches off of the activity `:topic`,
+  e.g. `:user-joined`. `perms-check-fn` is `can-read?` or `can-write?` and should be called as needed on models the
+  activity records."
+  {:arglists '([perms-check-fn activity])}
+  (fn [_ {:keys [topic]}]
+    topic))
+
+;; For now only admins can see when another user joined -- we don't want every user knowing about every other user. In
+;; the future we might want to change this and come up with some sort of system where we can determine which users get
+;; to see other users -- perhaps if they are in a group together other than 'All Users'
+(defmethod can-? :user-joined [_ _]
+  api/*is-superuser?*)
+
+;; For every other activity topic we'll look at the read/write perms for the object the activty is about (e.g. a Card
+;; or Dashboard). For all other activity feed items with no model everyone can read/write
+(defmethod can-? :default [perms-check-fn {model :model, model-id :model_id, :as activity}]
   (if-let [object (when-let [entity (model->entity model)]
                     (entity model-id))]
-    (f object)
+    (perms-check-fn object)
     true))
 
 
@@ -47,6 +64,7 @@
   i/IObjectPermissions
   (merge i/IObjectPermissionsDefaults
          {:can-read?  (partial can-? i/can-read?)
+          ;; TODO - when do people *write* activities?
           :can-write? (partial can-? i/can-write?)}))
 
 
diff --git a/src/metabase/sync/analyze/classifiers/category.clj b/src/metabase/sync/analyze/classifiers/category.clj
index 92a59f93209e59289c17d65ca7fe2c4f40b526f5..6548238d41c9bda4e83fe590c4072331258108d9 100644
--- a/src/metabase/sync/analyze/classifiers/category.clj
+++ b/src/metabase/sync/analyze/classifiers/category.clj
@@ -23,8 +23,9 @@
 
 (defn- cannot-be-category-or-list?
   [{:keys [base_type special_type]}]
-  (or (isa? base_type    :type/DateTime)
-      (isa? base_type    :type/Collection)
+  (or (isa? base_type :type/DateTime)
+      (isa? base_type :type/Collection)
+      (isa? base_type :type/Float)
       ;; Don't let IDs become list Fields (they already can't become categories, because they already have a special
       ;; type). It just doesn't make sense to cache a sequence of numbers since they aren't inherently meaningful
       (isa? special_type :type/PK)
diff --git a/src/metabase/sync/analyze/fingerprint/insights.clj b/src/metabase/sync/analyze/fingerprint/insights.clj
index 2e6e4d2e0bf3d89ffea26ec455535e14c0d0debe..8285f270577de65fa67587c364f29c1a54d3fc1e 100644
--- a/src/metabase/sync/analyze/fingerprint/insights.clj
+++ b/src/metabase/sync/analyze/fingerprint/insights.clj
@@ -1,6 +1,9 @@
 (ns metabase.sync.analyze.fingerprint.insights
   "Deeper statistical analysis of results."
-  (:require [kixi.stats
+  (:require [clj-time
+              [coerce :as t.coerce]
+              [core :as t]]
+            [kixi.stats
              [core :as stats]
              [math :as math]]
             [metabase.models.field :as field]
@@ -171,7 +174,7 @@
                      ;; unit=year workaround. While the field is in this case marked as :type/Text,
                      ;; at this stage in the pipeline the value is still an int, so we can use it
                      ;; directly.
-                     (comp (stats/somef ms->day) #(nth % x-position)))]
+                     #(some-> % (nth x-position) t/date-time t.coerce/to-long ms->day))]
     (apply redux/juxt
            (for [number-col numbers]
              (redux/post-complete
diff --git a/test/metabase/api/activity_test.clj b/test/metabase/api/activity_test.clj
index f5bd666940bb601b5c67c575625eaeecaa79bf6b..72497b5e1f071f3b2ad02842e4ab6c5868f2fde3 100644
--- a/test/metabase/api/activity_test.clj
+++ b/test/metabase/api/activity_test.clj
@@ -97,8 +97,8 @@
       :details      $})]
   ;; clear any other activities from the DB just in case; not sure this step is needed any more
   (do (db/delete! Activity :id [:not-in #{(:id activity1)
-                                                  (:id activity2)
-                                                  (:id activity3)}])
+                                          (:id activity2)
+                                          (:id activity3)}])
       (for [activity ((user->client :crowberto) :get 200 "activity")]
         (dissoc activity :timestamp))))
 
@@ -211,3 +211,17 @@
                                          {:model "card",      :model_id 0}
                                          {:model "dashboard", :model_id 0, :topic :dashboard-remove-cards, :details {:dashcards [{:card_id card-id}
                                                                                                                                  {:card_id 0}]}}]))
+
+;; Only admins should get to see user-joined activities
+(defn- user-can-see-user-joined-activity? [user]
+  ;; clear out all existing Activity entries
+  (db/delete! Activity)
+  (-> (tt/with-temp Activity [activity {:topic     "user-joined"
+                                        :details   {}
+                                        :timestamp (du/->Timestamp #inst "2019-02-15T11:55:00.000Z")}]
+        ((user->client user) :get 200 "activity"))
+      seq
+      boolean))
+
+(expect true  (user-can-see-user-joined-activity? :crowberto))
+(expect false (user-can-see-user-joined-activity? :rasta))
diff --git a/test/metabase/automagic_dashboards/core_test.clj b/test/metabase/automagic_dashboards/core_test.clj
index 6ca8193f7fc1e114ec845aeaa8f582810ff970fc..62840668b83bf8ec35fada26047e606c68d881cc 100644
--- a/test/metabase/automagic_dashboards/core_test.clj
+++ b/test/metabase/automagic_dashboards/core_test.clj
@@ -319,7 +319,7 @@
 ;;; ------------------- /candidates -------------------
 
 (expect
-  3
+  4
   (with-rasta
     (->> (Database (data/id)) candidate-tables first :tables count)))