Skip to content
This repository has been archived by the owner on Nov 1, 2019. It is now read-only.

Add a central module to handle the metric data #34

Merged
merged 20 commits into from
Aug 8, 2018
Merged

Add a central module to handle the metric data #34

merged 20 commits into from
Aug 8, 2018

Conversation

georgf
Copy link
Contributor

@georgf georgf commented Jul 25, 2018

Having more conversations on the usage of Redux, it does the job of centralizing the data state but might be more complex than what we need.

As an alternative to the Redux migration #26 i'm testing out moving the metric data handling into a separate module, MetricData / metricdata.js.

This centrally handles:

  • mapping the metric/channel/version selection to a data request
  • async fetching of metric data
  • getting summary values out of it

From there we can:

  • hold an instance of MetricData in App
  • pass around this data store in props instead of always passing many individual props
  • keep metric data handling logic in a central place

georgf added 15 commits July 19, 2018 19:16
- to avoid case-sensitivity issues, change paths to be all lower-case
- also use .jsx for components for automatic syntax highlighting in
editors
React likes things to be named index.js apparently, let’s go with it.
- to avoid case-sensitivity issues, change paths to be all lower-case
- also use .jsx for components for automatic syntax highlighting in
editors
React likes things to be named index.js apparently, let’s go with it.
Also save some repetition from re-using variables.
@georgf georgf requested a review from openjck July 25, 2018 09:37
@georgf
Copy link
Contributor Author

georgf commented Jul 25, 2018

John, does this approach make sense to you?

@georgf georgf requested a review from spasovski July 27, 2018 16:36
@georgf
Copy link
Contributor Author

georgf commented Jul 27, 2018

Or to you Davor?

prevState.activeVersion !== this.state.activeVersion) {
this.dataStore.loadDataFor(this.state.activeMetric, this.state.activeChannel, this.state.activeVersion)
.then(() => this.setState({activeData: this.dataStore.active.data}));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is interesting, thanks.
I may have to digest that a bit and look into it in a follow-up.

@openjck
Copy link
Contributor

openjck commented Jul 27, 2018

I can't say I'm an expert at state management in React, but this seems fine to me. It's pretty straightforward. @spasovski any other feedback?


let metric = this.props.dataStore.active.metric;
let channel = this.props.dataStore.active.channel;
let version = this.props.activeVersion;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: these look like they cold be const

let bucketHits = 0;
let linearTerm = (buckets[buckets.length - 1] - buckets[buckets.length -2]) / 2;
let exponentialFactor = Math.sqrt(buckets[buckets.length - 1] / buckets[buckets.length - 2]);
let useLinearBuckets = this.kind === "linear" || this.kind === "flag" || this.kind === "boolean" || this.kind === "enumerated";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could also do let useLinearBuckets = ['linear', 'flag', 'boolean', 'enumerated'].includes(this.kind);

}

loadDataFor(metric, channel, version) {
return fetch("https://mozilla.github.io/mdv2/data/" +
Copy link

@spasovski spasovski Jul 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the fetch url could be "https://mozilla.github.io/mdv2/data/${metric}_${channel}_${version}.json" with string interpolation

];
}

get versionOptions() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't seen this get syntax before. Very java-ey

@spasovski
Copy link

Am I wrong in thinking Redux wasn't used here? Not trying to vote for or against...that's a slightly more involved conversation. John's momoization link is good reading and some of the components could be pure.

I don't have greater insight into this project but the logic itself seems fine and I like the trimming of the previously massive component into something that gets its state externally.

@georgf georgf requested a review from chutten July 30, 2018 15:03
@georgf
Copy link
Contributor Author

georgf commented Jul 30, 2018

Thanks John & Davor, looking into it.
Flagging Chris for feedback from the project side.

@georgf
Copy link
Contributor Author

georgf commented Jul 30, 2018

Am I wrong in thinking Redux wasn't used here? Not trying to vote for or against...that's a slightly more involved conversation.

This is correct. I heard some light feedback that Redux would solve the problem at hand (centralizing data handling) but is probably more complex than required. Thus i'm testing this out as an alternative, more straight-forward solution.

- use more `const`
- more readable template string
- more readable metric kind comparison
nfifthPercentile: "",
median: "",
mean: "",
lastMedian: 315,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's oddly specific

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is copied code, let's get to that later.

metric: this._active.metric,
channel: this._active.channel,
version: this._active.version,
data: this._active.data,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we're having fun with ES6, this can be rewritten as return {...this._active};

}

updateCachedData() {
this.cached.mean = this.getMean().toFixed(2);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this.mean returns full-precision numbers, then I think this.cached.mean should be full-precision as well.

}

async loadDataFor(metric, channel, version) {
const response = await fetch(`https://mozilla.github.io/mdv2/data/${metric}_${channel}_${version}.json`);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A relative URL could make testing easier

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's get to that when the need comes up.

this._active.metric = metric;
this._active.channel = channel;
this._active.version = version;
this._active.data = data;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More ES6 wizardry: this._active = {...this._active, metric, channel, version, data};

return this.getChange();
}

updateCachedData() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We aren't caching data, though, we're caching aggregates?

Copy link
Contributor Author

@georgf georgf Aug 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's still cached data though?
This caching we may want to revisit soon, currently this is mostly copied over and adopted to make work.

}

getMean() {
let currentData = this._active.data;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a proponent of making const whatever we can make const. So here that'd be currentData, buckets, values, linearTerm, exponentialFactor, useLinearBuckets, centralX or so.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohhh, wait this is copied from erin's impl elsewhere. Nevermind, then.

- buckets[buckets.length -2];
}*/

return buckets[buckets.length - 1] * buckets[buckets.length - 1] / buckets[buckets.length - 2];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's two spaces after return here


getChange() {
let rawChange = this.getPercentile(50) - this.cached.lastMedian;
let pctChange = (rawChange / this.cached.lastMedian) * 100;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.cached.lastMedian is never written to? Oh, I guess that's a TODO.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the lastMedian approach needs revisiting anyway (currently it doesn't show it from the preceding version, but from the last-loaded version).

@georgf
Copy link
Contributor Author

georgf commented Aug 6, 2018

@chutten Please take a look over it. Let's aim for identifying improvements as follow-ups where possible.

One follow-up thing (existing issue) i've identified is that switching to scalars_devtools_onboarding_is_devtools_user results (in the summary view) in the median value of infinity.
This seems to come from getLastBucketUpper() doing a div by 0 as buckets[buckets.length - 2] is 0.
This is not a new issue, it just is visible now because we're actively loading different data files now (we didn't before).

Copy link
Collaborator

@chutten chutten left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I leave you to decide which comments are appropriate to address now and which as follow-ups (and which to ignore and evaluate later as part of a generic cleanup sweep).

getLastBucketUpper has commented out code that requires information about what the type of measurement is. We don't have that information, but scalars_devtools_onboarding_is_devtools_user is a metric that requires the special handling. I'm not worried about that at this state as it will require broader changes to make correct.

In general I'm happy with the move and the reorganization, so in my opinion it is merge-able.

@georgf
Copy link
Contributor Author

georgf commented Aug 8, 2018

Ok, moving forward with the merge.
Let's plan a review of the code base to identify what we want to improve.

@georgf georgf merged commit d0dc82e into mozilla:master Aug 8, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants