Package: manganite
ManganiteManganite is a dashboarding tool built on the powerfulPanelframework. Its primary objective is to allow for an easy transition from a linear Jupyter notebook (load data → build a model → run it → plot results) to an interactive web application, without requiring the user to manually convert most of their existing code to callbacks.InstallationMangnite can be installed on Linux, Windows, or Mac withpip:pipinstallmanganiteThe above command should also take care of installing and enabling the necessary Jupyter Server extensions: one for Panel, to enable the preview pane, and one for Manganite, to preprocess the magic commands before passing the notebook to Panel preview. To ensure proper functioning, remember torestart your Jupyter Serverafter installing the package.UsageYou can create a servable dashboard within minutes straight from inside your Jupyter notebook projectHow it worksManganite parses your Jupyter notebook and builds a dependency tree between its cells. Scalar variables and Pandas DataFrames are transparently wrapped inParamclasses, which lets them watch their values for changes and propagate these changes downstream.Any variable of asupported typecan be bound to a dashboard widget. The binding is bidirectional, so any change to the variable's value through the user interface will be reflected in the code and vice versa. Every time one of these variables is modified, any other cell that reads its value is re-evaluated and all the related widgets are updated, creating an interactive experience for the end user.Cells that contain long-running function calls can also be marked as executed on demand, and all their dependent cells will also wait for them to finish, making it possible to control complex optimization processes and plot their results.InstallationTo start using Manganite, import the package and load the IPython extension, initializing the dashboard builder and enabling the%%mnnmagic command. Typically this should be the first cell in the notebook.importmanganite%load_extmanganiteFor each subsequent cell, you have the option to annotate it with%%mnn widgetif it should display something,%%mnn executeif it should run only after a button is pressed, or skip the annotation altogether for no side effects. See thereferencefor a detailed description of the magic command.User interfaceA typical Manganite dashboard is divided into a header, a main area with tabs, and a sidebar for optimization logs.The title in the header is taken from the first level-1 header (a line starting with a single#sign) in the notebook's Markdown cells.TheDescriptiontab collects all the Markdown cells from the notebook, allowing you to describe the dashboard and introduce the user to the solution you are presenting. You can freely mix Markdown and Python cells in the notebook to make it more readable when opened in Jupyter. If some parts of your Markdown should not be displayed (e.g. when describing an accompanying code cell), you can exclude them by adding anmnn-ignoretag to the notebook cell.The other tabs contain a 6-column grid on which you can place various widgets, both for collecting user inputs and visualizing them automatically as they change.Step 1: Describing inputsSuppose you're modeling a supplier sourcing problem, using a Pandas DataFrame as one of its inputs. You might write something similar to the following code to load the initial data:importcvxpyascpimportpandasaspdimportplotly.expressaspxsupplier_df=pd.read_excel('SupplierSourcing.xlsx',index_col=0)If you'd like the dashboard user to be able to modify the input values, you can turn a DataFrame into a widget by adding a magic command at the beginning of the second cell:+%%mnn widget --type table --var supplier_df --tab "Inputs" --header "Supplier data"supplier_df = pd.read_excel('SupplierSourcing.xlsx', index_col=0)Let's break down the magic command above:%%mnn widgettells Manganite that this cell defines a dashboard widget--type tablespecifies what the widget will be, in this case a table, currently the only available choice for representing a DataFrame--var supplier_dfpoints to the variable that will be bound to the widget--tab "Inputs"places the widget on a tab calledInputs– if it doesn't exist at this point, it will be created--header "Supplier data"gives the table a header, so that the user knows what data they are modifyingStep 2: Adding reactive visualizationsIf you were writing a regular notebook, at this point you could do something like this to plot the loaded data:fig=px.bar(data_frame=(supplier_df.reset_index().melt(id_vars='index').loc[lambdax:x['index']=='Capacity (mt/yr)']),x='variable',y='value',labels={'variable':'Supplier','value':'Capacity'},template='plotly_white')figHowever, since you're building an interactive application, the contents of the DataFrame might change if the user edits some values using the widget. It would make sense to rerun the above code cell so that the plot always reflects the current state of the data. Manganite's dependency tree takes care of that. With a few small changes you can display figures that respond to data changes.First, give the plot variable a unique name – this ensures it won't get overwritten by other plots:-px.bar(+capacity_chart = px.bar(data_frame = (supplier_df.reset_index().melt(id_vars='index').loc[lambda x:x['index']=='Capacity (mt/yr)']),x='variable',y='value',labels={'variable':'Supplier', 'value':'Capacity'},template='plotly_white')+capacity_chartNext, you add the%%mnn widgetinvocation at the beginning, like this:+%%mnn widget --type plot --var capacity_chart --tab "Inputs" --header "Capacity"capacity_chart = px.bar(data_frame = (supplier_df.reset_index().melt(id_vars='index').loc[lambda x:x['index']=='Capacity (mt/yr)']),x='variable',y='value',labels={'variable':'Supplier', 'value':'Capacity'},template='plotly_white')
capacity_chartSince this cell accesses the variablesupplier_df, Manganite knows that any timesupplier_dfchanges, it needs to be evaluated again. Thecapacity_chartvariable will be wrapped in a Panel widget and placed on the grid, next to the table we created in the previous step.Cells without an annotation behave just the same, except they don't display anything on recalculation. This is useful if you need to compute a number of helper variables for later use:# this is just a regular notebook cell# but Manganite will still rerun it if supplier_df is changed# because x depends on n depends on supplierdata depends on supplier_dfsupplierdata=supplier_df.to_numpy()n=len(supplierdata[0])# number of suppliersp=(supplierdata[0,:])# pricesu=(supplierdata[1,:])# whether from uniont=(supplierdata[2,:])# whether rail transportationv=(supplierdata[3,:])# volatilityc=(supplierdata[4,:])# capacitiesx=cp.Variable(n)Step 3: Defining modelsTo run an optimization model with Manganite, put the calculations in a single cell and add the%%mnn executecommand as the first line. The code will be transformed into a function that will be called when the user presses a button. If you use the--tabargument, you can place the button on a tab like any other widget, otherwise it will show in the page header on the right.During the execution of a cell annotated with%%mnn execute, all standard output and exceptions will be redirected to theLogwidget in the sidebar.%%mnnexecute--onbutton"Optimize"--returnsxobjective=cp.Minimize(p@x)constraints=[cp.sum(x)==1225]constraints.append(v@x>=(0.19)*cp.sum(x))constraints.append(u@x>=(1/2)*cp.sum(x))constraints.append(t@x<=650)constraints.append((1-t)@x<=720)constraints.append(x[:]<=c[:])constraints.append(x>=0)prob=cp.Problem(objective,constraints)prob.solve(solver=cp.GUROBI)print('Solved!')# this will be displayed in the logStep 4: Visualizing resultsPlotting the results is yet another use of the%%mnn widgetmagic command.
Since in the%%mnn executecell above the--returnsargument was set tox, all further cells that referencexwill hold off their first execution until the above cell finishes at least once. This way the entireResultstab can be hidden until the optimization succeeds.%%mnnwidget--typeplot--varsolution_chart--tab"Results"--header"Solution"solution_chart=px.bar(data_frame=pd.DataFrame({'Suppliers':supplier_df.columns,'Order volume':x.value}),# we access x herex='Suppliers',y='Order volume',template='plotly_white')You can further explore this example by openingsupplier_sourcing.ipynb.Step 5: Deploy the dashboard as a web applicationThe final step is to serve the dashboard as a web application so that you can view it with a web browser. Assuming the file name issupplier_selection.ipynb, you simply add a cell containing the following line and execute the notebook.!mnn serve manganite-demo.ipynbOf course, this will block the notebook from further use. An alternative is to call the following from the terminalmnn serve manganite-demo.ipynb --autoreloadThe resulting dashboard will automatically reload each time you save the notebook, which allows you to make changes and view the results live in the browser.Reference%%mnnmagic command:bulb: square brackets indicate optionality%%mnn widget --type TYPE [PARAMS] --var VAR_NAME --tab TAB [--position ROW COL SPAN] --header HEADERnamerequiredvalueTYPEyesone ofsupported typesPARAMSnocontext-dependent parameters, seesupported typesVAR_NAMEyesname of the variable to be bound to the widgetTAByesany quoted string; if no tab with such label exists, it will be createdROW,COL,SPANnothree integers representing row/column coordinates (0-based) and width in columns on a 6-column grid%%mnn execute --on TRIGGER PARAMS [--tab TAB] --returns VAR_NAMEnamerequiredvalueTRIGGERyescurrently alwaysbuttonPARAMSyescurrently always a quoted string (button label)TABnoany quoted string; if no tab with such label exists, it will be created; if not present, the button will be added to the app headerVAR_NAMEyesname of the variable representing the main result of the process; all further cells referencing it will hold off their first execution until the current cell finishes at least onceWidget typesWidgets in Manganite are strictly tied to the types of their bound variables. The table below lists all possible configurations.variable typewidgetTYPEwidgetPARAMSboolcheckboxn/aboolswitchn/astrtextn/astrselectname of the variable holding the options (a collection of strings, can be any oflist,tuple,set)strradiosame as forselectstrfileoptional comma-separated list ofunique file type specifiersfor filtering available filesintsliderMIN:MAX:STEPwhere all values are integer literalsinttextn/afloatsliderMIN:MAX:STEPwhere all values are number literalsfloattextn/adatetime.datedaten/adatetime.datetimedatetimen/apandas.DataFrametablen/aServing the applicationThemnn servecommand is a simple wrapper for thepanel servecommand. For available options, we refer to thePanel documentation.Running Manganite in GitHub CodespacesGitHub Codespaces provides a seamless environment for running and experimenting with Manganite. To get started, follow these simple steps:Create a GitHub Codespace:Using Codespces requires a github login. Log in to your account or create a new one.Open theManganite Codespace repositoryClick on the "Code" button and then select "Open with Codespaces."Access Manganite in Codespaces:GitHub Codespaces will create a development environment for you. Once the environment is ready, you will find Manganite available to use in the Codespaces terminal.Explore Manganite and app Galery:You can now start experimenting with Manganite! Check out the app galery with different usecases and explore the widgets, and learn how to create dashboards with ease.