Skip to content

API reference

Let it shine!

Creating Shiny Dashboards from Simple Python Mathematical Functions and Sensible Defaults

Imports the standard modules inspect, sys and importlib.

Imports the docstring_parser moudule to generate the input fields of the Shiny app.

The module docstring_to_markdown is recommended but not needed.

doc_to_markdown(docstring)

Convert docstring to markdown for the documentation tab.

If module docstring_to_markdown is present, then it is used to format the docstring to markdown. Otherwise the same docstring is returned.

:param docstring: Docstring to be converted. :type docstring: str

:return: Returns the formated . :rtype: str

len(doc_to_markdown(sum.doc)) 228

Source code in letitshine/__init__.py
def doc_to_markdown(docstring):
    r"""
    Convert docstring to markdown for the documentation tab.

    If module `docstring_to_markdown` is present, then it is used
    to format the docstring to markdown. Otherwise the same 
    docstring is returned.

    :param docstring: Docstring to be converted.
    :type docstring: str

    :return: Returns the formated .
    :rtype: str

    >>> len(doc_to_markdown(sum.__doc__))
    228

    """


    try:
        import docstring_to_markdown
    except ImportError:
        docstring_to_markdown = None 

    if docstring_to_markdown:
        return docstring_to_markdown.convert(docstring)
    else:
        return docstring

letitshine(module_of_function, function_to_interactive, type_of_output=['txt'], tex_output=False)

Create Shiny app from Function in Module

This is the main function of the module.

:param module_of_function: Module of function :type module_of_function: str

:param function_to_interactive: Function to make interactive :type function_to_interactive: str

:return: Returns the code of the shiny app. :rtype: str

Examples

shiny_app=letitshine('letitshine.examples.text_output','say_hello',type_of_output=['txt']) Type of output: ['txt'] Element 0 of output: txt len(shiny_app) 837

Source code in letitshine/__init__.py
def letitshine(module_of_function,function_to_interactive,
               type_of_output=['txt'],
                tex_output=False):
    r"""
    Create Shiny app from Function in Module

    This is the main function of the module. 

    :param module_of_function: Module of function
    :type module_of_function: str

    :param function_to_interactive: Function to make interactive
    :type function_to_interactive: str

    :return: Returns the code of the shiny app.
    :rtype: str

    Examples
    ---------

    >>> shiny_app=letitshine('letitshine.examples.text_output','say_hello',type_of_output=['txt'])
    Type of output: ['txt']
    Element 0 of output: txt
    >>> len(shiny_app)
    837
    """  

    code = ''

    code+=(f'\nfrom  shiny import App, render, ui, reactive \n')


    path.append('./') # to be able to run relative files
    my_module = import_module(module_of_function)
    my_function=getattr(my_module, function_to_interactive)

    code+=(shiny_arguments(my_function,
                             type_of_output,
                             tex_output))
    code+='\n# Created with letitshine. You can customize it using Shiny UI elements\n'
    return code

shiny_arguments(func, type_of_output='txt', tex_output=False)

Returns the arguments of a function with their default values and types.

The function needs to be a named function. Anonymous functions do not work

Examples

def fun(name:str='Kim'): ... return "Hello "+name ... fun() 'Hello Kim' shiny_fun=shiny_arguments(fun,type_of_output=['txt']) Type of output: ['txt'] Element 0 of output: txt len(shiny_fun) 635

Source code in letitshine/__init__.py
def shiny_arguments(func,type_of_output='txt',tex_output=False):
    r"""Returns the arguments of a function with their default values and types.

    The function needs to be a named function. Anonymous functions do not work

    Examples
    ---------
    >>> def fun(name:str='Kim'):
    ...     return "Hello "+name
    ... 
    >>> fun()
    'Hello Kim'
    >>> shiny_fun=shiny_arguments(fun,type_of_output=['txt'])
    Type of output: ['txt']
    Element 0 of output: txt
    >>> len(shiny_fun)
    635
    """

    # Obtain the function necessary information. 
    my_signature = signature(func)
    parameters = my_signature.parameters
    docstring=parse(func.__doc__)

    # Printing the UI part of the input
    code=''
    code+=(f'app_ui = ui.page_sidebar(\n')
    code+=(f'\tui.sidebar(\n')
    code+=(f'\t\tui.card(\n')
    code+=(f'\t\t\tui.card_header("Input arguments:"),\n')

    code+=(ui_from_function(func))
    code+=(f'\t\t\t),\n')
    code+=(f'\t\t),\n')


    #######################################   
    # This is the  app_ui part
    #######################################   

    print(f'# Type of output: {type_of_output}')

    # If documentation is to be displayed as a tab
    code+="# *LetItShine:* use columns. See: https://shiny.posit.co/py/api/core/#ui-layouts \n"
    print("# *LetItShine:* use columns. See: https://shiny.posit.co/py/api/core/#ui-layouts \n")
    code+="ui.layout_columns(\n" 
    for position,element_of_output in enumerate(type_of_output):

        print(f'# Element {position} of output: {element_of_output}')

        # We print the output code for every element
        code+=output_ui_from_function(element_of_output, 
                                tex_output,
                                func.__name__,
                                docstring,0,position)

    code+=")\n#*LetItShine:* End of column_wrap"
    # We end the different elements
    code+=(f'\ttitle="{docstring.short_description}",\n)\n')


    #######################################   
    # This is the server part
    #######################################   

    # Common title of the input card

    # Here comes the server part
    code+=("def server(input, output, session):\n")

    # First, run the computation
    code+=(f"\t@reactive.calc")+"\n"
    code+=(f"\tdef do_computation():")+"\n"
    code+=(f"\t\treturn {func.__name__}(")+"\n"

    for name,param in parameters.items():
        code+=("\t\t\tinput."+name+"(),")+"\n"
    code+=("\t)\n")+"\n"

    # CHECK!! THIS IS FALSE

    # single_output=True if (('doc' in type_of_output) & (len(type_of_output)==2)) else False
    # single_output=True if len(type_of_output)==1 else False

    single_output=False
    if len(type_of_output)==1:
        single_output=True # There is only one argument
    elif (('doc' in type_of_output) & (len(type_of_output)==2)):
        single_output= True # One of the 2 arguments is doc


    for position,element_of_output in enumerate(type_of_output):
    # Here come the @render parts of the output
        code+=render_from_function(func,element_of_output,position,single_output)



    # Here comes the App part - Closing
    code+=("\napp = App(app_ui, server)\n")
    return code

ui_from_function(func)

Takes a Python Function and generates a Shiny UI input

Example:

ui_from_function(lambda my_name: f'Hello {my_name}') '\t\t\tui.input_text("my_name", "my_name?", "Input Text"),\n'

Source code in letitshine/__init__.py
def ui_from_function(func):
    r"""
    Takes a Python Function and generates a Shiny UI input


    Example:
    -------
    >>> ui_from_function(lambda my_name: f'Hello {my_name}')
    '\t\t\tui.input_text("my_name", "my_name?", "Input Text"),\n'
    """

    code=''
    my_signature = signature(func)
    parameters = my_signature.parameters
    docstring=parse(func.__doc__)
    docstring_param_names=[param.arg_name for param in docstring.params]
    docstring_param_descriptions=[param.description for param in docstring.params]
    for name, param in parameters.items():
        # We look for a description of the argument in the docstring
        if name in docstring_param_names:
            description=docstring_param_descriptions[docstring_param_names.index(name)]
        else:
            description=None
        code+=(ui_from_param(name,param,description))
    return code

__main__

Use Letitshine module to make Shiny app from function

This script allows the user to generate a shiny python script from a user-supplied function in a Python module.

The requirements are in Python Standard Library: argparse, shutil sys. The corresponding module is letitshine, see the documentation of the module for further information.

cli()

Use LetItShine as a command line utility

usage: letitshine_make_app [-h][-i FUNCTION_TO_INTERACTIVE] [-type TYPE_OF_OUTPUT][-create]

This program generates an app.py file to interact with a python code

options: -h, --help show this help message and exit

-i FUNCTION_TO_INTERACTIVE, --function_to_interactive FUNCTION_TO_INTERACTIVE Name of the function and module: name_of_module.funcion_in_module

-type TYPE_OF_OUTPUT, --type_of_output TYPE_OF_OUTPUT Type of Output ('txt', 'tex', 'plot', 'data_frame', 'doc'). If there is more than one output separe them with a comma.

-create, --create_app Create an app file with app_[NAMEOFFUNCTION].py

Source code in letitshine/__main__.py
def cli():
    """Use LetItShine as a command line utility

    usage: letitshine_make_app [-h] [-i FUNCTION_TO_INTERACTIVE] [-type TYPE_OF_OUTPUT] [-create]

    This program generates an app.py file to interact with a python code

    options:
    -h, --help            show this help message and exit

    -i FUNCTION_TO_INTERACTIVE, --function_to_interactive FUNCTION_TO_INTERACTIVE
                            Name of the function and module: name_of_module.funcion_in_module

    -type TYPE_OF_OUTPUT, --type_of_output TYPE_OF_OUTPUT
                            Type of Output ('txt', 'tex', 'plot', 'data_frame', 'doc'). If there is more than one output separe them with a comma.

    -create, --create_app
                            Create an app file with app_[NAMEOFFUNCTION].py
    """
    parser = ArgumentParser(
                    prog='letitshine_make_app',
                    description='This program generates an app.py file to interact with a python code',
                    epilog='Use it at your own risk') 
    parser.add_argument("-i","--function_to_interactive", 
                        type=str, help="Name of the function and module: name_of_module.funcion_in_module")
    parser.add_argument("-type", "--type_of_output", type=str,
                    help="Type of Output ('txt', 'tex', 'plot', 'data_frame','doc'). If there is more than one output separe them with a comma.")
    parser.add_argument("-create","--create_app", 
                    help="Create an app file with app_[NAMEOFFUNCTION].py",
                        action="store_true")

    args = parser.parse_args(args=None if argv[1:] else ['--help'])


    module_of_function      = args.function_to_interactive.split('.')[0]
    function_to_interactive = args.function_to_interactive.split('.')[1]
    type_of_output=args.type_of_output

    if type_of_output is not None:
        tex_output= True if 'tex' in type_of_output else False
        type_of_output = [s.strip() if s !='tex' else 'txt' for s in type_of_output.split(",")]


    if(len(type_of_output)==1):
        type_of_output=type_of_output

        code= letitshine(module_of_function,function_to_interactive,
                    type_of_output,
                    tex_output)


        file_of_source=module_of_function+'.py'
        file_of_output='app_'+function_to_interactive+'.py'
        if(args.create_app):
            print(f'# Creating the selfcontained shiny app {file_of_output}')
            copyfile(file_of_source,file_of_output)
            with open(file_of_output, "a") as fp:
                fp.write(code)
            print(f'# You can run it with the terminal command: ')
            print(f'# shiny run {file_of_output}')
        else:
            print(f'# Appending the following code to the end of your module {module_of_function}')
            print(f'# and run it with ')
            print(f'# shiny run {file_of_output}')
            print('# on the file location')
            print(code)
    else:
        print("# Your input had multiple arguments")
        print(f'# Type of output={type_of_output}')
        print(f'# tex_output={tex_output}')
        code= letitshine(module_of_function,function_to_interactive,
                    type_of_output,
                    tex_output)


        file_of_source=module_of_function+'.py'
        file_of_output='app_'+function_to_interactive+'.py'
        if(args.create_app):
            print(f'# Creating the selfcontained shiny app {file_of_output}')
            copyfile(file_of_source,file_of_output)
            with open(file_of_output, "a") as fp:
                fp.write(code)
            print(f'# You can run it with the terminal command: ')
            print(f'# shiny run {file_of_output}')
        else:
            print(f'# Appending the following code to the end of your module {module_of_function}')
            print(f'# and run it with ')
            print(f'# shiny run {file_of_output}')
            print('# on the file location')
            print(code)