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

Django Panel with channels for multi apps #928

Merged
merged 9 commits into from
Jan 28, 2020

Conversation

stefjunod
Copy link
Contributor

@stefjunod stefjunod commented Jan 8, 2020

Example use of panel with django for a multi apps / multi panels website
README.md provide a description / tutorial

3 apps:

  • Sliders (sinewave like on panel website)
  • GBM (Geometric Brownian motion simulations, very similar to Sliders)
  • Stockscreener (stock market plotting)...NOT working

Each apps has a:

  • pn.model to code the model / calculation
  • pn.app to layout the apps

Issues/ Not working:

  • Using pn.widgets in Stockscreener ?
  • need to taylor made the plot with a themes
  • moving from one page to another page on the website seems to reset the panel app. Is it possible to keep the status until exiting
  • Everytime a sliders is changing this generate some update. Is it possible to have a button to refresh once all the user update have been made

.idea/.gitignore Outdated Show resolved Hide resolved
@philippjfr
Copy link
Member

@stefjunod Thanks again, this is looking great overall! I've left a few comments but would be happy to address them myself.

My only other comment is that the market_data.csv is pretty big and we might want to find a different way to ship that file or include only a subset.

@stefjunod
Copy link
Contributor Author

Hi,

Another commit with the stockscreener working ....

image

So we have 3 apps:

  • Sliders (sinewave like on panel website)
  • GBM (Geometric Brownian motion simulations, very similar to Sliders)
  • Stockscreener (stock market plotting)...Working with pn.widgets....

In pn_model.py

class StockScreener(param.Parameterized):
    # interface
    df = param.DataFrame(precedence=-1)
    Index = pn.widgets.MultiSelect()
    Rebase = pn.widgets.Checkbox(name='Rebase', value=True)
    From = pn.widgets.DateSlider()
...

In the pn_app.py I had to refer to each widget separately (ss.Index, ss.From, ss.Rebase):

def app(doc):
    data_path = os.path.join(os.path.dirname(__file__), 'datasets/market_data.csv')
    df = pd.read_csv(data_path, index_col=0, parse_dates=True)
    ss = StockScreener(df=df)
    row = pn.Row(pn.Column(ss.Index, ss.From, ss.Rebase), ss.update_plot)
    row.server_doc(doc)

This seems easier than using the widgets =....

For the theming, I have a file in the django project folder themes.py:

import holoviews as hv
import panel as pn


def __disable_logo(plot, element):
    plot.state.toolbar.logo = None


def plot_themes():
    # format
    hv.plotting.bokeh.ElementPlot.finalize_hooks.append(__disable_logo)
    pn.widgets.DatetimeInput.format = '%d %B %Y'
    hv.plotting.bokeh.ElementPlot.bgcolor = "#fbfcfc"
    hv.plotting.bokeh.ElementPlot.gridstyle = {"grid_line_alpha": 0.6, "grid_line_dash": 'dashed'}
    # hv.Palette.colormaps = 'Blues' NOT WORKING

I was not able to set the colormaps of hv plot in the plot_themes(), so I put in settings.py a

COLOR_MAP = 'Paired'

Then in my pn_model.py I had to import it and use it (not pretty....)

from django.conf import settings
import datetime as dt

import panel as pn
import param
import holoviews as hv
import hvplot
import hvplot.pandas


class StockScreener(param.Parameterized):
    # interface
    df = param.DataFrame(precedence=-1)
    Index = pn.widgets.MultiSelect()
    Rebase = pn.widgets.Checkbox(name='Rebase', value=True)
    From = pn.widgets.DateSlider()

    def __init__(self, df, **params):
        super(StockScreener, self).__init__(**params)
        # init df
        self.df = df
        self.start_date = dt.datetime(year=df.index[0].year, month=df.index[0].month, day=df.index[0].day)
        self.end_date = dt.datetime(year=df.index[-1].year, month=df.index[-1].month, day=df.index[-1].day)
        self.col = list(self.df.columns)
        # init interface
        self.Index = pn.widgets.MultiSelect(name='Index', value=self.col[0:5], options=self.col,
                                            size=min(10, len(self.col)))
        self.From = pn.widgets.DateSlider(name='From', start=self.start_date, end=self.end_date, value=self.start_date)

    @param.depends('Index.value', 'Rebase.value', 'From.value', watch=True)
    def update_plot(self, **kwargs):
        unds = list(self.Index.value)
        pos = self.df.index.get_loc(self.From.value, method='bfill')
        dfp = self.df.iloc[pos:][unds]
        if self.Rebase.value:
            dfp = 100 * dfp / dfp.iloc[0]

        # legend positions
        # ['top_right', 'top_left', 'bottom_left', 'bottom_right']
        # ['right', 'left', 'top', 'bottom']

        return dfp.hvplot(value_label="Level", colormap=settings.COLOR_MAP)  # dfp.hvplot().opts(legend_position='top_left', responsive=True) # grid=True, colormap='Paired')

Questions ?

  • Is there a better way to set the colormap ? directly in themes.py
    I haven't work on these two yet:

  • moving from one page to another page on the website seems to reset the panel app. Is it possible to keep the status until exiting

  • Everytime a sliders is changing this generate some update. Is it possible to have a button to refresh once all the user update have been made (I have some idea how to do it....next week(

Cheers,
Stef

@philippjfr
Copy link
Member

Using pn.widgets in Stockscreener ?

Let me know if you want me to look at that, just describe what you want.

need to taylor made the plot with a themes

That seems fine although I'll look at cleaning that up.

moving from one page to another page on the website seems to reset the panel app. Is it possible to keep the status until exiting

In theory it's possible but I haven't tried persisting sessions in that way.

Everytime a sliders is changing this generate some update. Is it possible to have a button to refresh once all the user update have been made

Yes, let me know where and I can make those changes.

@philippjfr
Copy link
Member

Is there a better way to set the colormap ?

hvPlot overrides the defaults from HoloViews so we'd probably have to add a way to override hvPlot defaults. Therefore I think the current answer is no.

@philippjfr
Copy link
Member

Btw, I really appreciate your efforts here, this is great stuff!

@stefjunod
Copy link
Contributor Author

Using pn.widgets in Stockscreener ?

Let me know if you want me to look at that, just describe what you want.

Seems to work, not sure if it is best practice....

moving from one page to another page on the website seems to reset the panel app. Is it possible to keep the status until exiting

In theory it's possible but I haven't tried persisting sessions in that way.

Could be interesting to investigate, but not priority...at least for me

Everytime a sliders is changing this generate some update. Is it possible to have a button to refresh once all the user update have been made

Yes, let me know where and I can make those changes.

For the change you could look into the gbm app pn_models.py as It may take a while to compute and there is a lot sliders.

I will clean everything next week.

Have a nice week end.
Cheers,
Stef

@stefjunod
Copy link
Contributor Author

stefjunod commented Jan 13, 2020

Hi,

Hopefully we are getting there. Hence my final commit...
Try to clean as much as I could. Extended the Readme.md to include all as a walk through documentation. Don't know how the documentation is done, but happy to work on a smaller one, other format...just let me know if you need something from me.

I was not able to add a button to recalc the gbm app pn_models.py:

class GBM(param.Parameterized):
    # interface
   ...
    # TODO: Add a button param.Action, pn.widgets.Button... only refresh when onclick
    # refresh = pn.widgets.Button(name="refresh", button_type='primary')
    # refresh.onclick(update_plot)... does not work yet...

Otherwise, it works.

Hope this helps,
Stef

@stefjunod
Copy link
Contributor Author

Hi,

I finally succeeded in getting the refresh button to work.

The gbm app refresh the plot only when the refresh button is clicked.

  • gbm app in the pn_models.py:
    # update the plot for every changes
    # @param.depends('mean', 'volatility', 'maturity', 'n_observations', 'n_simulations', watch=True)
    # refresh the plot only on button refresh click
    @param.depends('refresh.clicks')

I left the comment to show the other pattern.
Seems fairly final now.

Cheers,
Stef

@philippjfr
Copy link
Member

Perfect! Sorry I haven't gotten around to working on this yet. I'm hoping to clean it up and merge it by the end of the week.

@codecov
Copy link

codecov bot commented Jan 28, 2020

Codecov Report

Merging #928 into master will decrease coverage by 0.04%.
The diff coverage is 0%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #928      +/-   ##
==========================================
- Coverage      86%   85.95%   -0.05%     
==========================================
  Files         105      105              
  Lines       11893    11909      +16     
==========================================
+ Hits        10228    10236       +8     
- Misses       1665     1673       +8
Impacted Files Coverage Δ
panel/io/server.py 40.62% <0%> (ø) ⬆️
panel/pane/holoviews.py 83.21% <0%> (-1.35%) ⬇️
panel/io/embed.py 88.52% <0%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a4bb0d7...6f4710b. Read the comment docs.

@philippjfr philippjfr merged commit 2a452c6 into holoviz:master Jan 28, 2020
@philippjfr
Copy link
Member

Thanks again @stefjunod! Hugely appreciate the effort you've put in here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants