Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #23665 - Use Patternfly charts in Runtime #5600

Closed
wants to merge 1 commit into from
Closed

Fixes #23665 - Use Patternfly charts in Runtime #5600

wants to merge 1 commit into from

Conversation

boaz0
Copy link
Member

@boaz0 boaz0 commented May 22, 2018

The flot package is deprecated although it's being used
to plot charts in Foreman.

One of those charts that uses flot is the Runtime chart.
In this commit:

  • Added a new component called Timeseries chart that uses Patternfly charts
  • Rendered the component on "hosts/show.html.erb"
  • Added unit tests to test the component.
  • Removed unnecessary files such as "hosts/_runtime.html.erb"
  • Changed the "hosts/:name/runtime" routes to return JSON

Before

before

After

after

@theforeman-bot
Copy link
Member

Issues: #23665

@boaz0
Copy link
Member Author

boaz0 commented May 22, 2018

/cc @ohadlevy @sharvit @amirfefer @lizagilman PTAL
Thanks

Copy link
Member

@ohadlevy ohadlevy left a comment

Choose a reason for hiding this comment

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

initial code review, did not test really as its not mountable.

Also would be happy to see storybook examples on how to use the time series charts.

@@ -300,7 +300,7 @@ def vm
end

def runtime
render :partial => 'runtime'
render :json => helpers.runtime_chart(params["range"].empty? ? 7 : params["range"].to_i)
rescue ActionView::Template::Error => exception
Copy link
Member

Choose a reason for hiding this comment

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

I guess the exception is no longer valid?

@@ -229,7 +229,10 @@ def runtime_chart(timerange = 1.day.ago)
config << [r.reported_at.to_i*1000, r.config_retrieval]
runtime << [r.reported_at.to_i*1000, r.runtime]
end
[{:label=>_("Config Retrieval"), :data=> config, :color=>'#AA4643'}, {:label=>_("Runtime"), :data=> runtime, :color=>'#4572A7'}]
{:results => [
{:label=>_("Config Retrieval"), :data=> config, :color=>'#AA4643'},
Copy link
Member

Choose a reason for hiding this comment

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

i wonder if we need to keep the color overrides, which colors do you get without defining it?

Copy link
Member Author

Choose a reason for hiding this comment

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

#0088ce ,#39a5dc, #3f9c35 and #ec7a08 which are the defaults in c3js

<div class="chart" data-ajax-url='<%= runtime_host_path(@host, :range => @range) %>' data-on-complete='updateChart'>
<%= spinner(_('Loading runtime information ...')) %>
</div>
<div id="runtime_react"></div>
Copy link
Member

Choose a reason for hiding this comment

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

not sure if react is needed as part of the div, just host_runtime or something similar would suffice

@@ -0,0 +1,24 @@

Copy link
Member

Choose a reason for hiding this comment

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

nitpick: extra empty line?

return c3Resp;
};

export default Normalizer;
Copy link
Member

Choose a reason for hiding this comment

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

is it safe to assume this will work the same with trends too?

Copy link
Member Author

@boaz0 boaz0 May 22, 2018

Choose a reason for hiding this comment

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

Yes, looking at the returned value from chart_data, it can normalize the trends too.

import { noop } from '../../common/helpers';
import Normalizer from './Normalizer';


Copy link
Member

Choose a reason for hiding this comment

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

nitpick: extra line

} = this.props;

if (hasError) {
return <Alert>__("Failed loading chart data")</Alert>;
Copy link
Member

Choose a reason for hiding this comment

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

can we reuse chartbox ? it has a loader and a failure state afair?

Copy link
Member Author

Choose a reason for hiding this comment

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

ChartBox does more than what TimeseriesChart does.
I find Loader which is used by ChartBox reusable in here too, though.

Thanks for the help.

axis={{ x: { type: 'timeseries', tick: { fit: false } } }}
tooltip={{ grouped: false, format: { title: d => new Date(d).toUTCString() } }}
zoom={{ enabled: true }}
/>
Copy link
Member

Choose a reason for hiding this comment

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

I'm missing an empty state representation, currently it looks like this
image

Copy link
Member

Choose a reason for hiding this comment

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

on second look, it looks like you forgot to add the changes to the mounter?

</div>

Copy link
Member

Choose a reason for hiding this comment

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

please configure your editor to remove trailing whitespaces, while not visible at github, this include 4 spaces charts (easily viewable with git diff)

Copy link
Member

@amirfefer amirfefer left a comment

Choose a reason for hiding this comment

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

Thanks @ripcurld !
I'm getting a PG error when a details host page renders.
more details in the inline comments.

} = this.props;

if (hasError) {
return <Alert>__("Failed loading chart data")</Alert>;
Copy link
Member

Choose a reason for hiding this comment

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

Should be wrapped by {}

if (colorItems.length === 0) {
return c3Resp;
}
const colorReducer = (acc, cur) => Object.assign(acc, { [cur.label]: cur.color });
Copy link
Member

Choose a reason for hiding this comment

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

be aware that acc is going to be mutated, if you'd like just a copy, use Object.assign({}, copied data), or use object spread operator, which I tend to prefer because of its readability.



class TimeseriesChart extends React.Component {
componentWillMount() {
Copy link
Member

Choose a reason for hiding this comment

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

API calls should be in componentDidMount

return (
<Spinner loading={isLoadingData} size='md'>
<PfLineChart
data={{ ...data, type: chartType }}
Copy link
Member

Choose a reason for hiding this comment

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

Why not using a service module like donut chart does with ChartService ?

className="line-chart-pf"
color={
Object {
"pattern": Array [
Copy link
Member

Choose a reason for hiding this comment

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

is that the default pf colors scheme? or some random colors?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes it is or more likely pf is using c3js default color scheme

<%= spinner(_('Loading runtime information ...')) %>
</div>
<div id="runtime_react"></div>
<%= mount_react_component('TimeseriesChart', '#runtime_react', {
Copy link
Member

@amirfefer amirfefer May 22, 2018

Choose a reason for hiding this comment

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

doesn't work with PG:
i'm getting this :
faild_loading_data
( the error string is not extracted )

with this error:

ActiveRecord::StatementInvalid (PG::UndefinedFunction: ERROR:  operator does not exist: timestamp without time zone > integer
 | LINE 1: ...t') AND "reports"."host_id" = $1 AND (reported_at > 7) ORDER...
 |                                                              ^
 | HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
 | : SELECT "reports".* FROM "reports" WHERE "reports"."type" IN ('ConfigReport') AND "reports"."host_id" = $1 AND (reported_at > 7) ORDER BY "reports"."reported_at" ASC):

@boaz0
Copy link
Member Author

boaz0 commented May 30, 2018

[test foreman] 🤞

@ohadlevy
Copy link
Member

@ripcurld can you please add it to the storybook and publish it somehwhere? thanks!

@boaz0
Copy link
Member Author

boaz0 commented May 30, 2018

Sure thing. 👍

link_to_function(icon_text('random'), 'randomizeName()', :class => 'btn btn-default',
:title => _('Generate new random name. Visit Settings to disable this feature.'))
end
link_to_function(icon_text('random'), 'randomizeName()', :class => 'btn btn-default',

Choose a reason for hiding this comment

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

Style/MultilineIfModifier: Favor a normal if-statement over a modifier clause in a multiline statement.

else
status = '<i class="glyphicon glyphicon glyphicon-arrow-down interface-down" title="' + _('Interface is down') + '"></i>'
status = '<i class="glyphicon glyphicon glyphicon-arrow-down interface-down" title="'+ _('Interface is down') +'"></i>'

Choose a reason for hiding this comment

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

Layout/SpaceAroundOperators: Surrounding space missing for operator +.

@@ -446,9 +439,9 @@ def link_status(nic)
return '' if nic.new_record?

if nic.link
status = '<i class="glyphicon glyphicon glyphicon-arrow-up interface-up" title="' + _('Interface is up') + '"></i>'
status = '<i class="glyphicon glyphicon glyphicon-arrow-up interface-up" title="'+ _('Interface is up') +'"></i>'

Choose a reason for hiding this comment

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

Layout/SpaceAroundOperators: Surrounding space missing for operator +.

@@ -354,7 +347,7 @@ def host_title_actions(host)
link_to_if_authorized(_("Run puppet"), hash_for_puppetrun_host_path(:id => host).merge(:auth_object => host, :permission => 'puppetrun_hosts'),
:disabled => !Setting[:puppetrun],
:class => 'btn btn-default',
:title => _("Trigger a puppetrun on a node; requires that puppet run is enabled"))
:title => _("Trigger a puppetrun on a node; requires that puppet run is enabled"))

Choose a reason for hiding this comment

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

Layout/SpaceAroundOperators: Operator => should be surrounded by a single space.

{ :days => select(nil, 'range', 1..number_of_days,
{:selected => @range}, {:style => "float:none; width: #{width}em;", :onchange => "$('#days_filter').submit();$(this).disabled();"}),
{ :days => select(nil, 'range', 1..number_of_days,
{:selected => @range}, {:style=>"float:none; width: #{width}em;", :onchange =>"$('#days_filter').submit();$(this).disabled();"}),

Choose a reason for hiding this comment

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

Layout/SpaceAroundOperators: Surrounding space missing for operator =>.

{:label=>_("Applied"), :data=>applied, :color =>'#89A54E'},
{:label=>_("Failed"), :data=>failed, :color =>'#AA4643'},
{:label=>_("Failed restarts"), :data=>failed_restarts, :color =>'#EC971F'},
{:label=>_("Skipped"), :data=>skipped, :color =>'#80699B'},

Choose a reason for hiding this comment

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

Layout/SpaceAroundOperators: Surrounding space missing for operator =>.

{:results => [
{:label=>_("Applied"), :data=>applied, :color =>'#89A54E'},
{:label=>_("Failed"), :data=>failed, :color =>'#AA4643'},
{:label=>_("Failed restarts"), :data=>failed_restarts, :color =>'#EC971F'},

Choose a reason for hiding this comment

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

Layout/SpaceAroundOperators: Surrounding space missing for operator =>.

{:label => _("Restarted"), :data => restarted, :color => '#4572A7'}]
{:results => [
{:label=>_("Applied"), :data=>applied, :color =>'#89A54E'},
{:label=>_("Failed"), :data=>failed, :color =>'#AA4643'},

Choose a reason for hiding this comment

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

Layout/SpaceAroundOperators: Surrounding space missing for operator =>.

{:label => _("Skipped"), :data => skipped, :color => '#80699B'},
{:label => _("Restarted"), :data => restarted, :color => '#4572A7'}]
{:results => [
{:label=>_("Applied"), :data=>applied, :color =>'#89A54E'},

Choose a reason for hiding this comment

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

Layout/SpaceAroundOperators: Surrounding space missing for operator =>.

failed << [r.reported_at.to_i*1000, r.failed ]
restarted << [r.reported_at.to_i*1000, r.restarted ]
failed_restarts << [r.reported_at.to_i*1000, r.failed_restarts ]
skipped << [r.reported_at.to_i*1000, r.skipped ]

Choose a reason for hiding this comment

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

Layout/SpaceAroundOperators: Surrounding space missing for operator *.

@@ -513,3 +518,4 @@ def host_breadcrumb
breadcrumbs(resource_url: "/api/v2/hosts?thin=true'")
end
end

Choose a reason for hiding this comment

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

Layout/TrailingBlankLines: 1 trailing blank lines detected.

@boaz0
Copy link
Member Author

boaz0 commented Jul 9, 2018

@ohadlevy the storybook can be found here

@ohadlevy
Copy link
Member

ohadlevy commented Jul 9, 2018 via email

@boaz0
Copy link
Member Author

boaz0 commented Jul 9, 2018

Unfortunately, I didn't find a way to do that in c3.js. You can zoom in and out but can't select a time-frame and zoom in.

@boaz0
Copy link
Member Author

boaz0 commented Sep 18, 2018

@ohadlevy I had some free time, so I revisited c3.js documents and found how to enable brush selection.
It's a little bit different from the old one but it keeps the old behaviour the user was used to.
You can experiment it through the storybook here.

@boaz0
Copy link
Member Author

boaz0 commented Sep 18, 2018

gr... katello dislikes me.

@boaz0
Copy link
Member Author

boaz0 commented Sep 18, 2018

Lets hope pull --rebase from upstream HEAD will fix this.

@boaz0
Copy link
Member Author

boaz0 commented Sep 19, 2018

[test katello]

@boaz0
Copy link
Member Author

boaz0 commented Sep 19, 2018

[test foreman]

@ohadlevy
Copy link
Member

ohadlevy commented Oct 3, 2018

@boaz1337 can you rebase please? thanks!

@ohadlevy
Copy link
Member

ohadlevy commented Oct 3, 2018

@boaz1337 I just tried to use it (in the storybook) and I'm not sure how?

@coveralls
Copy link

Coverage Status

Coverage increased (+0.7%) to 80.236% when pulling 408a38b on boaz1337:timeserieschart into d4e81e9 on theforeman:develop.

@boaz0
Copy link
Member Author

boaz0 commented Oct 4, 2018

Uhm, I guess it's not as intuitive as I thought.
Anyway, the chart consists of two small charts:

  • top: the real chart that shows the data and displays a tooltip with the values. Also can be zoomed in/out by scrolling the wheel in the mouse
  • bottom: a chart that displays the big chart and the brush-selection. Selecting a specific area will render the chart above.

I hope this will demonstrate it better

select

@boaz0
Copy link
Member Author

boaz0 commented Oct 4, 2018

CI failure is unrelated to changes.

@ohadlevy
Copy link
Member

ohadlevy commented Oct 4, 2018

ping @theforeman/ui-ux ?

@terezanovotna
Copy link

this looks great! as it's pretty interactive, could I see a storybook to play with it? @boaz1337

@boaz0
Copy link
Member Author

boaz0 commented Oct 5, 2018

@terezanovotna sure you can: storybook 📖

@terezanovotna
Copy link

looks good!

not sure how it's exactly applied, but when I open it for the first time, there is no grey rectangle.
screen shot 2018-10-05 at 10 41 27 Once I start playing with it, the rectangle appears and I get how to use it.

Can we have the grey rectangle on the screen by when opened for the first time?

The flot package is deprecated although it's being used
to plot charts in Foreman.

In this commit:

 - Added TimeseriesChart that uses Patternfly's LineChart
   under common/charts
 - Added HostChart under hosts
 - Rendered HostChart for runtime and resources
   in "hosts/show.html.erb"
 - Added unit tests
 - Removed unnecessary files such as "hosts/_runtime.html.erb"
 - Changed routes "hosts/:name/{runtime,resources}" to
   render JSON

Signed-off-by: Boaz Shuster <[email protected]>
@boaz0
Copy link
Member Author

boaz0 commented Oct 7, 2018

@terezanovotna sorry but right now there is no way to set the grey rectangle when chart is rendered for the first time.

EDIT looking into it more - in the new version of c3js you can do that by setting initialRange under zoom.

The problem is that bcbcarl/react-c3js#41 is still waiting on review.

@ares
Copy link
Member

ares commented Oct 8, 2018

Sorry if I'm confused, but based on the storybook, this will be the way we displaye all charts? Do I have to activate the bottom chart somehow? The fact there are two charts for one, each for different time span would IMHO be quite confusing.

@lizagilman
Copy link
Member

@boaz1337
Looks like this repository is barely maintained..

@Rohoover
Copy link

Rohoover commented Oct 8, 2018

Sorry if I'm confused, but based on the storybook, this will be the way we displaye all charts? Do I have to activate the bottom chart somehow? The fact there are two charts for one, each for different time span would IMHO be quite confusing.

It starts out of with a view of the longest history. Activitating the bottom will allow a user to zoom in. Maybe not the most helpful or runtime but can be very beneficial if one would want to focus on the details of a specific section of chart.

I'm not seeing them being in different times? They are always representative of each other.

@boaz0
Copy link
Member Author

boaz0 commented Feb 20, 2019

I'm closing this as we are stuck. The current UX is not good enough and the Chart library that PF3 is using is no longer up to date which makes it impossible to have something working.

If someone wants to take it, feel free to do it. I would love to help as much as I can.

Thanks everyone for your feedback and hard work. 😄

@boaz0 boaz0 closed this Feb 20, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants