Пример #1
0
def request(method, url, **kwargs):
    """
    Central place to make any v1 api request.

    :param (str) method: The request method ('get', 'put', 'delete', ...).
    :param (str) url: The full api url to make the request to.
    :param kwargs: These are passed along to requests.
    :return: (requests.Response) The response directly from requests.

    """
    if kwargs.get('json', None) is not None:
        # See plotly.api.v2.utils.request for examples on how to do this.
        raise exceptions.PlotlyError('V1 API does not handle arbitrary json.')
    kwargs['headers'] = dict(kwargs.get('headers', {}), **get_headers())
    kwargs['verify'] = config.get_config()['plotly_ssl_verification']
    try:
        response = requests.request(method, url, **kwargs)
    except RequestException as e:
        # The message can be an exception. E.g., MaxRetryError.
        message = str(getattr(e, 'message', 'No message'))
        response = getattr(e, 'response', None)
        status_code = response.status_code if response else None
        content = response.content if response else 'No content'
        raise exceptions.PlotlyRequestError(message, status_code, content)
    validate_response(response)
    return response
Пример #2
0
def get_headers():
    """
    Using session credentials/config, get headers for a V2 API request.

    Users may have their own proxy layer and so we free up the `authorization`
    header for this purpose (instead adding the user authorization in a new
    `plotly-authorization` header). See pull #239.

    :returns: (dict) Headers to add to a requests.request call.

    """
    creds = config.get_credentials()

    headers = {
        'plotly-client-platform': 'python {}'.format(version.stable_semver()),
        'content-type': 'application/json'
    }

    plotly_auth = basic_auth(creds['username'], creds['api_key'])
    proxy_auth = basic_auth(creds['proxy_username'], creds['proxy_password'])

    if config.get_config()['plotly_proxy_authorization']:
        headers['authorization'] = proxy_auth
        if creds['username'] and creds['api_key']:
            headers['plotly-authorization'] = plotly_auth
    else:
        if creds['username'] and creds['api_key']:
            headers['authorization'] = plotly_auth

    return headers
Пример #3
0
def build_url(resource, id='', route=''):
    """
    Create a url for a request on a V2 resource.

    :param (str) resource: E.g., 'files', 'plots', 'grids', etc.
    :param (str) id: The unique identifier for the resource.
    :param (str) route: Detail/list route. E.g., 'restore', 'lookup', etc.
    :return: (str) The url.

    """
    base = config.get_config()['plotly_api_domain']
    formatter = {'base': base, 'resource': resource, 'id': id, 'route': route}

    # Add path to base url depending on the input params. Note that `route`
    # can refer to a 'list' or a 'detail' route. Since it cannot refer to
    # both at the same time, it's overloaded in this function.
    if id:
        if route:
            url = '{base}/v2/{resource}/{id}/{route}'.format(**formatter)
        else:
            url = '{base}/v2/{resource}/{id}'.format(**formatter)
    else:
        if route:
            url = '{base}/v2/{resource}/{route}'.format(**formatter)
        else:
            url = '{base}/v2/{resource}'.format(**formatter)

    return url
Пример #4
0
def build_url(resource, id='', route=''):
    """
    Create a url for a request on a V2 resource.

    :param (str) resource: E.g., 'files', 'plots', 'grids', etc.
    :param (str) id: The unique identifier for the resource.
    :param (str) route: Detail/list route. E.g., 'restore', 'lookup', etc.
    :return: (str) The url.

    """
    base = config.get_config()['plotly_api_domain']
    formatter = {'base': base, 'resource': resource, 'id': id, 'route': route}

    # Add path to base url depending on the input params. Note that `route`
    # can refer to a 'list' or a 'detail' route. Since it cannot refer to
    # both at the same time, it's overloaded in this function.
    if id:
        if route:
            url = '{base}/v2/{resource}/{id}/{route}'.format(**formatter)
        else:
            url = '{base}/v2/{resource}/{id}'.format(**formatter)
    else:
        if route:
            url = '{base}/v2/{resource}/{route}'.format(**formatter)
        else:
            url = '{base}/v2/{resource}'.format(**formatter)

    return url
Пример #5
0
def get_headers():
    """
    Using session credentials/config, get headers for a V2 API request.

    Users may have their own proxy layer and so we free up the `authorization`
    header for this purpose (instead adding the user authorization in a new
    `plotly-authorization` header). See pull #239.

    :returns: (dict) Headers to add to a requests.request call.

    """
    creds = config.get_credentials()

    headers = {
        'plotly-client-platform': 'python {}'.format(version.stable_semver()),
        'content-type': 'application/json'
    }

    plotly_auth = basic_auth(creds['username'], creds['api_key'])
    proxy_auth = basic_auth(creds['proxy_username'], creds['proxy_password'])

    if config.get_config()['plotly_proxy_authorization']:
        headers['authorization'] = proxy_auth
        if creds['username'] and creds['api_key']:
            headers['plotly-authorization'] = plotly_auth
    else:
        if creds['username'] and creds['api_key']:
            headers['authorization'] = plotly_auth

    return headers
Пример #6
0
def clientresp(data, **kwargs):
    """
    Deprecated endpoint, still used because it can parse data out of a plot.

    When we get around to forcing users to create grids and then create plots,
    we can finally get rid of this.

    :param (list) data: The data array from a figure.

    """
    creds = config.get_credentials()
    cfg = config.get_config()

    dumps_kwargs = {'sort_keys': True, 'cls': utils.PlotlyJSONEncoder}

    payload = {
        'platform': 'python', 'version': version.stable_semver(),
        'args': _json.dumps(data, **dumps_kwargs),
        'un': creds['username'], 'key': creds['api_key'], 'origin': 'plot',
        'kwargs': _json.dumps(kwargs, **dumps_kwargs)
    }

    url = '{plotly_domain}/clientresp'.format(**cfg)
    response = request('post', url, data=payload)

    # Old functionality, just keeping it around.
    parsed_content = response.json()
    if parsed_content.get('warning'):
        warnings.warn(parsed_content['warning'])
    if parsed_content.get('message'):
        print(parsed_content['message'])

    return response
Пример #7
0
def request(method, url, **kwargs):
    """
    Central place to make any v1 api request.

    :param (str) method: The request method ('get', 'put', 'delete', ...).
    :param (str) url: The full api url to make the request to.
    :param kwargs: These are passed along to requests.
    :return: (requests.Response) The response directly from requests.

    """
    if kwargs.get('json', None) is not None:
        # See plotly.api.v2.utils.request for examples on how to do this.
        raise exceptions.PlotlyError('V1 API does not handle arbitrary json.')
    kwargs['headers'] = dict(kwargs.get('headers', {}), **get_headers())
    kwargs['verify'] = config.get_config()['plotly_ssl_verification']
    try:
        response = requests.request(method, url, **kwargs)
    except RequestException as e:
        # The message can be an exception. E.g., MaxRetryError.
        message = str(getattr(e, 'message', 'No message'))
        response = getattr(e, 'response', None)
        status_code = response.status_code if response else None
        content = response.content if response else 'No content'
        raise exceptions.PlotlyRequestError(message, status_code, content)
    validate_response(response)
    return response
Пример #8
0
def test_mimetype_combination(fig1):
    pio.renderers.default = 'png+jupyterlab'

    # Configure renderer so that we can use the same parameters
    # to build expected image below
    pio.renderers['png'].width = 400
    pio.renderers['png'].height = 500
    pio.renderers['png'].scale = 1

    # pdf
    image_bytes = pio.to_image(
        fig1, format='png', width=400, height=500, scale=1)

    image_str = base64.b64encode(image_bytes).decode('utf8')

    # plotly mimetype
    plotly_mimetype_dict = json.loads(
        pio.to_json(fig1, remove_uids=False))

    plotly_mimetype_dict['config'] = {
        'plotlyServerURL': get_config()['plotly_domain']}

    # Build expected bundle
    expected = {
        'image/png': image_str,
        plotly_mimetype: plotly_mimetype_dict,
    }

    pio.renderers.render_on_display = False
    assert fig1._repr_mimebundle_(None, None) is None

    pio.renderers.render_on_display = True
    bundle = fig1._repr_mimebundle_(None, None)
    assert bundle == expected
Пример #9
0
def get_headers():
    """
    Using session credentials/config, get headers for a v1 API request.

    Users may have their own proxy layer and so we free up the `authorization`
    header for this purpose (instead adding the user authorization in a new
    `plotly-authorization` header). See pull #239.

    :returns: (dict) Headers to add to a requests.request call.

    """
    headers = {}
    creds = config.get_credentials()
    proxy_auth = basic_auth(creds['proxy_username'], creds['proxy_password'])

    if config.get_config()['plotly_proxy_authorization']:
        headers['authorization'] = proxy_auth

    return headers
Пример #10
0
def get_headers():
    """
    Using session credentials/config, get headers for a v1 API request.

    Users may have their own proxy layer and so we free up the `authorization`
    header for this purpose (instead adding the user authorization in a new
    `plotly-authorization` header). See pull #239.

    :returns: (dict) Headers to add to a requests.request call.

    """
    headers = {}
    creds = config.get_credentials()
    proxy_auth = basic_auth(creds['proxy_username'], creds['proxy_password'])

    if config.get_config()['plotly_proxy_authorization']:
        headers['authorization'] = proxy_auth

    return headers
Пример #11
0
def request(method, url, **kwargs):
    """
    Central place to make any api v2 api request.

    :param (str) method: The request method ('get', 'put', 'delete', ...).
    :param (str) url: The full api url to make the request to.
    :param kwargs: These are passed along (but possibly mutated) to requests.
    :return: (requests.Response) The response directly from requests.

    """
    kwargs['headers'] = dict(kwargs.get('headers', {}), **get_headers())

    # Change boolean params to lowercase strings. E.g., `True` --> `'true'`.
    # Just change the value so that requests handles query string creation.
    if isinstance(kwargs.get('params'), dict):
        kwargs['params'] = kwargs['params'].copy()
        for key in kwargs['params']:
            if isinstance(kwargs['params'][key], bool):
                kwargs['params'][key] = _json.dumps(kwargs['params'][key])

    # We have a special json encoding class for non-native objects.
    if kwargs.get('json') is not None:
        if kwargs.get('data'):
            raise exceptions.PlotlyError('Cannot supply data and json kwargs.')
        kwargs['data'] = _json.dumps(kwargs.pop('json'),
                                     sort_keys=True,
                                     cls=utils.PlotlyJSONEncoder)

    # The config file determines whether reuqests should *verify*.
    kwargs['verify'] = config.get_config()['plotly_ssl_verification']

    try:
        response = requests.request(method, url, **kwargs)
    except RequestException as e:
        # The message can be an exception. E.g., MaxRetryError.
        message = str(getattr(e, 'message', 'No message'))
        response = getattr(e, 'response', None)
        status_code = response.status_code if response else None
        content = response.content if response else 'No content'
        raise exceptions.PlotlyRequestError(message, status_code, content)
    validate_response(response)
    return response
Пример #12
0
def request(method, url, **kwargs):
    """
    Central place to make any api v2 api request.

    :param (str) method: The request method ('get', 'put', 'delete', ...).
    :param (str) url: The full api url to make the request to.
    :param kwargs: These are passed along (but possibly mutated) to requests.
    :return: (requests.Response) The response directly from requests.

    """
    kwargs['headers'] = dict(kwargs.get('headers', {}), **get_headers())

    # Change boolean params to lowercase strings. E.g., `True` --> `'true'`.
    # Just change the value so that requests handles query string creation.
    if isinstance(kwargs.get('params'), dict):
        kwargs['params'] = kwargs['params'].copy()
        for key in kwargs['params']:
            if isinstance(kwargs['params'][key], bool):
                kwargs['params'][key] = _json.dumps(kwargs['params'][key])

    # We have a special json encoding class for non-native objects.
    if kwargs.get('json') is not None:
        if kwargs.get('data'):
            raise exceptions.PlotlyError('Cannot supply data and json kwargs.')
        kwargs['data'] = _json.dumps(kwargs.pop('json'), sort_keys=True,
                                     cls=utils.PlotlyJSONEncoder)

    # The config file determines whether reuqests should *verify*.
    kwargs['verify'] = config.get_config()['plotly_ssl_verification']

    try:
        response = requests.request(method, url, **kwargs)
    except RequestException as e:
        # The message can be an exception. E.g., MaxRetryError.
        message = str(getattr(e, 'message', 'No message'))
        response = getattr(e, 'response', None)
        status_code = response.status_code if response else None
        content = response.content if response else 'No content'
        raise exceptions.PlotlyRequestError(message, status_code, content)
    validate_response(response)
    return response
    def _markdown_to_presentation(self, markdown_string, style, imgStretch):
        list_of_slides = _list_of_slides(markdown_string)

        for slide_num, slide in enumerate(list_of_slides):
            lines_in_slide = slide.split('\n')
            title_lines = []

            # validate blocks of code
            if slide.count('```') % 2 != 0:
                raise exceptions.PlotlyError(CODE_ENV_ERROR)

            # find code blocks
            code_indices = []
            code_blocks = []
            wdw_size = len('```')
            for j in range(len(slide)):
                if slide[j:j+wdw_size] == '```':
                    code_indices.append(j)

            for k in range(int(len(code_indices) / 2)):
                code_blocks.append(
                    slide[code_indices[2 * k]:code_indices[(2 * k) + 1]]
                )

            lang_and_code_tuples = []
            for code_block in code_blocks:
                # validate code blocks
                code_by_lines = code_block.split('\n')
                language = _remove_extra_whitespace_from_line(
                    code_by_lines[0][3:]
                ).lower()
                if language == '' or language not in VALID_LANGUAGES:
                    raise exceptions.PlotlyError(
                        "The language of your code block should be "
                        "clearly indicated after the first ``` that "
                        "begins the code block. The valid languages to "
                        "choose from are" + list_of_options(
                            VALID_LANGUAGES
                        )
                    )
                lang_and_code_tuples.append(
                    (language, '\n'.join(code_by_lines[1:]))
                )

            # collect text, code and urls
            title_lines = []
            url_lines = []
            text_lines = []
            inCode = False

            for line in lines_in_slide:
                # inCode handling
                if line[:3] == '```' and len(line) > 3:
                    inCode = True
                if line == '```':
                    inCode = False

                if not inCode and line != '```':
                    if len(line) > 0 and line[0] == '#':
                        title_lines.append(line)
                    elif (_url_parens_contained('Plotly', line) or
                          _url_parens_contained('Image', line)):
                        if (line.startswith('Plotly(') and
                            get_config()['plotly_domain'] not in line):
                            raise exceptions.PlotlyError(
                                "You are attempting to insert a Plotly Chart "
                                "in your slide but your url does not have "
                                "your plotly domain '{}' in it.".format(
                                    get_config()['plotly_domain']
                                )
                            )
                        url_lines.append(line)
                    else:
                        # find and set transition properties
                        trans = 'transition:'
                        if line.startswith(trans) and title_lines == []:
                            slide_trans = line[len(trans):]
                            slide_trans = _remove_extra_whitespace_from_line(
                                slide_trans
                            )
                            slide_transition_list = []
                            for key in VALID_TRANSITIONS:
                                if key in slide_trans:
                                    slide_transition_list.append(key)

                            if slide_transition_list == []:
                                slide_transition_list.append('slide')
                            self._set_transition(
                                slide_transition_list, slide_num
                            )

                        else:
                            text_lines.append(line)

            # make text block
            for i in range(2):
                try:
                    while text_lines[-i] == '':
                        text_lines.pop(-i)
                except IndexError:
                    pass

            text_block = '\n'.join(text_lines)
            num_of_boxes = len(url_lines) + len(lang_and_code_tuples)

            (specs_for_boxes, specs_for_title, specs_for_text, bkgd_color,
             title_style_attr, text_style_attr,
             code_theme) = _return_layout_specs(
                num_of_boxes, url_lines, title_lines, text_block, code_blocks,
                slide_num, style
            )

            # background color
            self._color_background(bkgd_color, slide_num)

            # insert title, text, code, and images
            if len(title_lines) > 0:
                # clean titles
                title = title_lines[0]
                num_hashes = 0
                while title[0] == '#':
                    title = title[1:]
                    num_hashes += 1
                title = _remove_extra_whitespace_from_line(title)

                self._insert(
                    box='Text', text_or_url=title,
                    left=specs_for_title[0], top=specs_for_title[1],
                    height=specs_for_title[2], width=specs_for_title[3],
                    slide=slide_num, style_attr=title_style_attr,
                    paragraphStyle='Heading 1'.format(
                        min(num_hashes, 3)
                    )
                )

            # text
            if len(text_lines) > 0:
                self._insert(
                    box='Text', text_or_url=text_block,
                    left=specs_for_text[0], top=specs_for_text[1],
                    height=specs_for_text[2], width=specs_for_text[3],
                    slide=slide_num, style_attr=text_style_attr,
                    paragraphStyle='Body'
                )

            url_and_code_blocks = list(url_lines + lang_and_code_tuples)
            for k, specs in enumerate(specs_for_boxes):
                url_or_code = url_and_code_blocks[k]
                if isinstance(url_or_code, tuple):
                    # code
                    language = url_or_code[0]
                    code = url_or_code[1]
                    box_name = 'CodePane'

                    # code style
                    props_attr = {}
                    props_attr['language'] = language
                    props_attr['theme'] = code_theme

                    self._insert(box=box_name, text_or_url=code,
                                 left=specs[0], top=specs[1],
                                 height=specs[2], width=specs[3],
                                 slide=slide_num, props_attr=props_attr)
                else:
                    # url
                    if get_config()['plotly_domain'] in url_or_code:
                        box_name = 'Plotly'
                    else:
                        box_name = 'Image'
                    url = url_or_code[len(box_name) + 1: -1]

                    self._insert(box=box_name, text_or_url=url,
                                 left=specs[0], top=specs[1],
                                 height=specs[2], width=specs[3],
                                 slide=slide_num)

        if not imgStretch:
            for s, slide in enumerate(self['presentation']['slides']):
                for c, child in enumerate(slide['children']):
                    if child['type'] in ['Image', 'Plotly']:
                        deep_child = child['props']['style']
                        width = deep_child['width']
                        height = deep_child['height']

                        if width >= height:
                            deep_child['max-width'] = deep_child.pop('width')
                        else:
                            deep_child['max-height'] = deep_child.pop('height')
def _return_layout_specs(num_of_boxes, url_lines, title_lines, text_block,
                         code_blocks, slide_num, style):
    # returns specs of the form (left, top, height, width)
    code_theme = 'tomorrowNight'
    if style == 'martik':
        specs_for_boxes = []
        margin = 18  # in pxs

        # set Headings styles
        paragraph_styles['Heading 1'].update(
            {'color': '#0D0A1E',
             'fontFamily': 'Raleway',
             'fontSize': 55,
             'fontWeight': fontWeight_dict['Bold']['fontWeight']}
        )

        paragraph_styles['Heading 2'] = copy.deepcopy(
            paragraph_styles['Heading 1']
        )
        paragraph_styles['Heading 2'].update({'fontSize': 36})
        paragraph_styles['Heading 3'] = copy.deepcopy(
            paragraph_styles['Heading 1']
        )
        paragraph_styles['Heading 3'].update({'fontSize': 30})

        # set Body style
        paragraph_styles['Body'].update(
            {'color': '#96969C',
             'fontFamily': 'Roboto',
             'fontSize': 16,
             'fontWeight': fontWeight_dict['Regular']['fontWeight']}
        )

        bkgd_color = '#F4FAFB'
        title_font_color = '#0D0A1E'
        text_font_color = '#96969C'
        if num_of_boxes == 0 and slide_num == 0:
            text_textAlign = 'center'
        else:
            text_textAlign = 'left'
        if num_of_boxes == 0:
            specs_for_title = (0, 50, 20, 100)
            specs_for_text = (15, 60, 50, 70)

            bkgd_color = '#0D0A1E'
            title_font_color = '#F4FAFB'
            text_font_color = '#F4FAFB'
        elif num_of_boxes == 1:
            if code_blocks != [] or (url_lines != [] and
                                     get_config()['plotly_domain'] in
                                     url_lines[0]):
                if code_blocks != []:
                    w_range = 40
                else:
                    w_range = 60
                text_top = _top_spec_for_text_at_bottom(
                    text_block, 80,
                    per_from_bottom=(margin / HEIGHT) * 100
                )
                specs_for_title = (0, 3, 20, 100)
                specs_for_text = (10, text_top, 30, 80)
                specs_for_boxes = _box_specs_gen(
                    num_of_boxes, grouptype='middle', width_range=w_range,
                    height_range=60, margin=margin, betw_boxes=4
                )
                bkgd_color = '#0D0A1E'
                title_font_color = '#F4FAFB'
                text_font_color = '#F4FAFB'
                code_theme = 'tomorrow'
            elif title_lines == [] and text_block == '':
                specs_for_title = (0, 50, 20, 100)
                specs_for_text = (15, 60, 50, 70)
                specs_for_boxes = _box_specs_gen(
                    num_of_boxes, grouptype='middle', width_range=50,
                    height_range=80, margin=0, betw_boxes=0
                )
            else:
                title_text_width = 40 - (margin / WIDTH) * 100

                text_top = _top_spec_for_text_at_bottom(
                    text_block, title_text_width,
                    per_from_bottom=(margin / HEIGHT) * 100
                )
                specs_for_title = (60, 3, 20, 40)
                specs_for_text = (60, text_top, 1, title_text_width)
                specs_for_boxes = _box_specs_gen(
                    num_of_boxes, grouptype='leftgroup_v', width_range=60,
                    margin=margin, betw_boxes=4
                )
                bkgd_color = '#0D0A1E'
                title_font_color = '#F4FAFB'
                text_font_color = '#F4FAFB'
        elif num_of_boxes == 2 and url_lines != []:
            text_top = _top_spec_for_text_at_bottom(
                text_block, 46, per_from_bottom=(margin / HEIGHT) * 100,
                min_top=50
            )
            specs_for_title = (0, 3, 20, 50)
            specs_for_text = (52, text_top, 40, 46)
            specs_for_boxes = _box_specs_gen(
                num_of_boxes, grouptype='checkerboard_topright'
            )
        elif num_of_boxes >= 2 and url_lines == []:
            text_top = _top_spec_for_text_at_bottom(
                text_block, 92, per_from_bottom=(margin / HEIGHT) * 100,
                min_top=15
            )
            if num_of_boxes == 2:
                betw_boxes = 90
            else:
                betw_boxes = 10
            specs_for_title = (0, 3, 20, 100)
            specs_for_text = (4, text_top, 1, 92)
            specs_for_boxes = _box_specs_gen(
                num_of_boxes, grouptype='middle', width_range=92,
                height_range=60, margin=margin, betw_boxes=betw_boxes
            )
            code_theme = 'tomorrow'
        else:
            text_top = _top_spec_for_text_at_bottom(
                text_block, 40 - (margin / WIDTH) * 100,
                per_from_bottom=(margin / HEIGHT) * 100
            )
            specs_for_title = (0, 3, 20, 40 - (margin / WIDTH) * 100)
            specs_for_text = (
                (margin / WIDTH) * 100, text_top, 50,
                40 - (margin / WIDTH) * 100
            )
            specs_for_boxes = _box_specs_gen(
                num_of_boxes, grouptype='rightgroup_v', width_range=60,
                margin=margin, betw_boxes=4
            )

    elif style == 'moods':
        specs_for_boxes = []
        margin = 18
        code_theme = 'tomorrowNight'

        # set Headings styles
        paragraph_styles['Heading 1'].update(
            {'color': '#000016',
             'fontFamily': 'Roboto',
             'fontSize': 55,
             'fontWeight': fontWeight_dict['Black']['fontWeight']}
        )

        paragraph_styles['Heading 2'] = copy.deepcopy(
            paragraph_styles['Heading 1']
        )
        paragraph_styles['Heading 2'].update({'fontSize': 36})
        paragraph_styles['Heading 3'] = copy.deepcopy(
            paragraph_styles['Heading 1']
        )
        paragraph_styles['Heading 3'].update({'fontSize': 30})

        # set Body style
        paragraph_styles['Body'].update(
            {'color': '#000016',
             'fontFamily': 'Roboto',
             'fontSize': 16,
             'fontWeight': fontWeight_dict['Thin']['fontWeight']}
        )

        bkgd_color = '#FFFFFF'
        title_font_color = None
        text_font_color = None
        if num_of_boxes == 0 and slide_num == 0:
            text_textAlign = 'center'
        else:
            text_textAlign = 'left'
        if num_of_boxes == 0:
            if slide_num == 0 or text_block == '':
                bkgd_color = '#F7F7F7'
                specs_for_title = (0, 50, 20, 100)
                specs_for_text = (15, 60, 50, 70)
            else:
                bkgd_color = '#F7F7F7'
                text_top = _top_spec_for_text_at_bottom(
                    text_block, width_per=90,
                    per_from_bottom=(margin / HEIGHT) * 100,
                    min_top=20
                )
                specs_for_title = (0, 2, 20, 100)
                specs_for_text = (5, text_top, 50, 90)

        elif num_of_boxes == 1:
            if code_blocks != []:
                # code
                if text_block == '':
                    margin = 5
                    specs_for_title = (0, 3, 20, 100)
                    specs_for_text = (0, 0, 0, 0)
                    top = 12
                    specs_for_boxes = [
                        (margin, top, 100 - top - margin, 100 - 2 * margin)
                    ]

                elif slide_num % 2 == 0:
                    # middle center
                    width_per = 90
                    height_range = 60
                    text_top = _top_spec_for_text_at_bottom(
                        text_block, width_per=width_per,
                        per_from_bottom=(margin / HEIGHT) * 100,
                        min_top=100 - height_range / 2.
                    )
                    specs_for_boxes = _box_specs_gen(
                        num_of_boxes, grouptype='middle',
                        width_range=50, height_range=60, margin=margin,
                    )
                    specs_for_title = (0, 3, 20, 100)
                    specs_for_text = (
                        5, text_top, 2, width_per
                    )
                else:
                    # right
                    width_per = 50
                    text_top = _top_spec_for_text_at_bottom(
                        text_block, width_per=width_per,
                        per_from_bottom=(margin / HEIGHT) * 100,
                        min_top=30
                    )
                    specs_for_boxes = _box_specs_gen(
                        num_of_boxes, grouptype='rightgroup_v',
                        width_range=50, margin=40,
                    )
                    specs_for_title = (0, 3, 20, 50)
                    specs_for_text = (
                        2, text_top, 2, width_per - 2
                    )
            elif (url_lines != [] and
                  get_config()['plotly_domain'] in url_lines[0]):
                # url
                if slide_num % 2 == 0:
                    # top half
                    width_per = 95
                    text_top = _top_spec_for_text_at_bottom(
                        text_block, width_per=width_per,
                        per_from_bottom=(margin / HEIGHT) * 100,
                        min_top=60
                    )
                    specs_for_boxes = _box_specs_gen(
                        num_of_boxes, grouptype='middle',
                        width_range=100, height_range=60,
                        middle_center=30
                    )
                    specs_for_title = (0, 60, 20, 100)
                    specs_for_text = (
                        2.5, text_top, 2, width_per
                    )
                else:
                    # middle across
                    width_per = 95
                    text_top = _top_spec_for_text_at_bottom(
                        text_block, width_per=width_per,
                        per_from_bottom=(margin / HEIGHT) * 100,
                        min_top=60
                    )
                    specs_for_boxes = _box_specs_gen(
                        num_of_boxes, grouptype='middle',
                        width_range=100, height_range=60
                    )
                    specs_for_title = (0, 3, 20, 100)
                    specs_for_text = (
                        2.5, text_top, 2, width_per
                    )
            else:
                # image
                if slide_num % 2 == 0:
                    # right
                    width_per = 50
                    text_top = _top_spec_for_text_at_bottom(
                        text_block, width_per=width_per,
                        per_from_bottom=(margin / HEIGHT) * 100,
                        min_top=30
                    )
                    specs_for_boxes = _box_specs_gen(
                        num_of_boxes, grouptype='rightgroup_v',
                        width_range=50, margin=0,
                    )
                    specs_for_title = (0, 3, 20, 50)
                    specs_for_text = (
                        2, text_top, 2, width_per - 2
                    )
                else:
                    # left
                    width_per = 50
                    text_top = _top_spec_for_text_at_bottom(
                        text_block, width_per=width_per,
                        per_from_bottom=(margin / HEIGHT) * 100,
                        min_top=30
                    )
                    specs_for_boxes = _box_specs_gen(
                        num_of_boxes, grouptype='leftgroup_v',
                        width_range=50, margin=0,
                    )
                    specs_for_title = (50, 3, 20, 50)
                    specs_for_text = (
                        52, text_top, 2, width_per - 2
                    )
        elif num_of_boxes == 2:
            # right stack
            width_per = 50
            text_top = _top_spec_for_text_at_bottom(
                text_block, width_per=width_per,
                per_from_bottom=(margin / HEIGHT) * 100,
                min_top=30
            )
            specs_for_boxes = [(50, 0, 50, 50), (50, 50, 50, 50)]
            specs_for_title = (0, 3, 20, 50)
            specs_for_text = (
                2, text_top, 2, width_per - 2
            )
        elif num_of_boxes == 3:
            # middle top
            width_per = 95
            text_top = _top_spec_for_text_at_bottom(
                text_block, width_per=width_per,
                per_from_bottom=(margin / HEIGHT) * 100,
                min_top=40
            )
            specs_for_boxes = _box_specs_gen(
                num_of_boxes, grouptype='middle',
                width_range=100, height_range=40, middle_center=30
            )
            specs_for_title = (0, 0, 20, 100)
            specs_for_text = (
                2.5, text_top, 2, width_per
            )
        else:
            # right stack
            width_per = 40
            text_top = _top_spec_for_text_at_bottom(
                text_block, width_per=width_per,
                per_from_bottom=(margin / HEIGHT) * 100,
                min_top=30
            )
            specs_for_boxes = _box_specs_gen(
                num_of_boxes, grouptype='rightgroup_v',
                width_range=60, margin=0,
            )
            specs_for_title = (0, 3, 20, 40)
            specs_for_text = (
                2, text_top, 2, width_per - 2
            )

    # set text style attributes
    title_style_attr = {}
    text_style_attr = {'textAlign': text_textAlign}

    if text_font_color:
        text_style_attr['color'] = text_font_color
    if title_font_color:
        title_style_attr['color'] = title_font_color

    return (specs_for_boxes, specs_for_title, specs_for_text, bkgd_color,
            title_style_attr, text_style_attr, code_theme)