Juicebox 2 to Juicebox 3 Conversion Guide


This help should be accurate and comprehensive. If you see anything missing or that needs to be fixed, see How to Contribute or let us know in the Juice Slack #documentation channel.

What you need to know to convert apps from juicebox2 to juicebox3.

Converting app.yaml

  • Set juicebox_version to 3 in app.yaml


Converting theme.yaml

  • extra_css in theme.yaml doesn’t do anything anymore. Move extra_css from theme.yaml to an extra_css on the slice that you want to affect. If you’re using custom templates you can define inline styles on the template.
  • Logo should be changed to use the new logo syntax.
  • You can now define custom backgrounds (see Styling Backgrounds) at the
    stack level with header and background properties.

Converting stack.yaml

  • Move Metadata to ingredients in your dataservices base class. Each ingredient can have a singular and a plural and a format like this. If you don’t supply a plural value, it will be the same as the singular value. Once you’ve done this you can delete the metadata/ folder in the stack’s directory.
  • Remove filters_config.title property
  • Replace {{=selection(_slug_)}} by <%= _slug_.selectionDisplay() %> in any slice types.

Converting slices in stack.yaml

  • Slugs must only contain letters or underscores. If you have any slice slugs that contain a “-”, replace it with an underscore.


    You don’t have to set a slug for a slice. If you don’t give the slice a slug it will automatically get a slug based on the slice type with “-” removed. For instance, if you have an option-chooser slice, it will automatically get a slug of “optionchooser”.

  • Remove eventmap from every slice config. You only need to add it if you want a slice to not listen to changes in a slice above it.

  • Remove metadata property from any slice definitions. This metadata information is now part of the ingredient definitions.

  • Use <% ... %> instead of {{ }} in the slice title

  • Use <%= templateContext.key %> for retrieving template_context values into the slice tittle instead of {{= responseMessage() }}

Converting templates.html

  • Use <% ... %> instead of {{ }} for javascript templates
  • For more on templates, see Templates

Converting data services

  • Remove any import * and replace with exact imports
  • Make sure Tables are explicitly defined and not reflected (???)

Converting a base service with ingredients

  • Should inherit from RecipeServiceBaseV3 (in juicebox2 this used to be RecipeServiceBase)
  • All ingredients should have singular keyword argument supplied and optionally plural and format keyword arguments (this is what used to be in metadata)
  • Anonymized ingredients should have the anonymize keyword argument set to True and the function used to anonymize the data should be set via the anonymizer keyword argument.
  • Set the dimensions to be used for global filters as an automatic_filter_keys tuple
  • Change dimension_order to be automatic_filter_keys
  • Change local_filter_ids to be custom_filter_keys
  • Remove default_table and default_metric
  • Ingredients can no longer support joins() so remove those and look at blend () on recipes

Converting global Filters

  • Use a RecipePool if you have more than 1 filter dimensions.
  • If not using a RecipePool make sure to use .render() on the recipe.
  • Iterate over the automatic_filter_keys

General notes on converting data services

  • Slices listen to those above them by default.

  • Always use a recipe, avoid writing SQLAlchemy queries

  • Use ingredients for everything you can (which is almost everything), and avoid accessing Table columns directly

  • Establish a pattern of using self.metrics, self.dimensions and self.filters to hold your ingredients for a recipe. Then use them in the recipe like .metrics(*self.metrics)

  • Use a RecipePool if you have need to send more than 1 response/dataset to the slice.

  • If not using a RecipePool make sure to use .render() on the recipe.

  • Most renderers have flavors that allow you to change the response by using

    the flavor keyword (more details in the docs)

  • A few renderer flavors use a render_config dictionary keyword to supply additional data to the renderer (more details in the docs)

  • If you find yourself writing more than 15 lines of python for a slice, reach out to the Core team and let us see if we can help by enhancing a renderer or flavor

  • Limit the query to exactly the data you need. If you are query 4 four metrics/dimensions then outputting only one of those based some factor, narrow down the metrics/dimensions before hand.

  • Use 'templateContext' in self.response for additional contents for the slice, i.e.

    self.response['templateContext'] = {key: value}
  • Replace notes assigned directly to self.response[‘notes’] by self .response['responses'][0]['templateContext'] = {'notes': note}

  • Use self.custom_filters.first('metric', defaultMetric) instead of self .query_metric to get the value of the selected metric in OptionChooser.

  • Use .compare() when comparing the same metrics from time period to time period

  • Use .blend() when pulling in data from multiple tables

  • Remove any reference to self.metadata

  • On TableDetail remove any column definition (self.response[‘columns’]), this is automatic now.