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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
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
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
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))
    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
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
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
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
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)