{ "cells": [ { "cell_type": "markdown", "id": "8d5fd4da-995d-4304-adcf-2464e0187053", "metadata": {}, "source": [ "# Learning how to walk while cat-walking ~ demo app\n", "Hello this is a demo for the SI16 API and website from XPUB. Trying to document everything so it is not OBSCURE. Feel free to improve anything!\n", "\n", "The folder structure follows this scheme: \n", "- In the ```root``` folder there is the notebook (this file) that runs the Flask application. \n", "- The ```templates``` folder is the default one from Flask with the HTML templates. \n", "- In the ```notebooks``` folder there are the files with the basic functions and their documentation in the format of notebook. \n", "- In the ```projects``` folder there are the folders of the subgroup projects. We can put the files and materials of each project in there as well as the html pages etc. Each project should have also an ```documentation.md``` file with the info of the work. \n", "- In the ```static``` folder there are all the static files such as css stylesheets, fonts, images, javascript files, etc. They are organized in specific sub-folders so we dont get messy \n", "- In the ```contents``` folder there are all the markdown files with the text contents for the website. Description of the projects, about, colophon, manifesto, research etc. Each file contains the text and can include some metadata of our choice. Look at the about.md for an example " ] }, { "cell_type": "markdown", "id": "1213eab7-5ca7-4cd9-8d1b-4f625f961899", "metadata": {}, "source": [ "## Import\n", "Here we import all the modules we need for making the app working." ] }, { "cell_type": "code", "execution_count": 14, "id": "b90f82e6-09b3-432d-9c02-596a4ca84e2f", "metadata": {}, "outputs": [], "source": [ "# to work with files in folder \n", "import os\n", "\n", "# to create the Flask app\n", "from flask import Flask, render_template, request, url_for, redirect, jsonify, abort\n", "\n", "# to import text contents and metadata from markdown files\n", "from flaskext.markdown import Markdown\n", "import frontmatter\n", "\n", "# to cast string arguments into the required types\n", "from pydoc import locate\n", "\n", "# to work with notebooks\n", "#\n", "# to import notebook files\n", "import nbimporter\n", "nbimporter.options['only_defs'] = False\n", "import importlib\n", "\n", "# to read and execute the content of notebooks\n", "import nbformat\n", "from nbconvert import HTMLExporter, MarkdownExporter\n", "from nbconvert.preprocessors import ExecutePreprocessor\n", "\n", "# not sure about this is in the nbconvert documentation\n", "from traitlets.config import Config\n", "\n" ] }, { "cell_type": "markdown", "id": "7c1bd05b-bc9c-4ac3-9654-61b2b17b0248", "metadata": {}, "source": [ "## Functions\n", "Here we define the functions for the logic of the backend and the API " ] }, { "cell_type": "code", "execution_count": 15, "id": "8fe33b07-3f37-4cab-8729-7919d053ba00", "metadata": {}, "outputs": [], "source": [ "def filenames(folder):\n", " ''' Read all the functions in a folder '''\n", " names = []\n", " for entry in os.scandir(folder):\n", " # add to the list only proper files\n", " if entry.is_file(follow_symlinks=False):\n", " # remove the extension from the filename\n", " names.append(os.path.splitext(entry.name)[0])\n", " return names" ] }, { "cell_type": "code", "execution_count": 16, "id": "73ece514-5247-4e89-b6a4-5afcf46f2afd", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['vernacular_map', 'text_file_to_blob', 'shout', 'reverse', 'cocktail_generator', 'blob_to_excerpts_list', 'mashup', 'repeat', 'highlight_map', 'individual_map', 'bridge', 'add_target_info', 'area_map', 'input-back-to-text', 'target_map', 'ghost_map', 'html_tag']\n" ] } ], "source": [ "# example: print the file inside the notebooks folder\n", "print(filenames('./notebooks'))" ] }, { "cell_type": "code", "execution_count": 17, "id": "b122207c-7ed7-4522-9b2f-018414d0f9fd", "metadata": {}, "outputs": [], "source": [ "def dirnames(folder):\n", " ''' Return all the folders in a folder '''\n", " names = []\n", " for entry in os.scandir(folder):\n", " # add to the list only proper files\n", " if not entry.name.startswith('.') and entry.is_dir():\n", " # remove the extension from the filename\n", " names.append(entry.name)\n", " return names" ] }, { "cell_type": "code", "execution_count": 18, "id": "cd82bfbf-2f51-4caf-bfec-443aa2ba5112", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['etc', 'replace', 'map']\n" ] } ], "source": [ "print(dirnames('./projects'))" ] }, { "cell_type": "code", "execution_count": 19, "id": "f76b4396-2f11-4e9d-92bf-d20a884d30a1", "metadata": {}, "outputs": [], "source": [ "# not really sure about this file -> module -> function thing! \n", "# could someone help pls ? \n", "def get_function(name, folder):\n", " ''' Dynamic import a function from a folder '''\n", "# try: \n", " file = __import__(f'{folder}.{name}')\n", " module = getattr(file, name)\n", " function = getattr(module, name)\n", "# except AttributeError or ModuleNotFoundError:\n", "# file = importlib.import_module(f'{folder}.{name}')\n", "# function = getattr(file, name)\n", " return function" ] }, { "cell_type": "code", "execution_count": 20, "id": "7639cbfd-3f42-45e9-a201-1d87cbc88d00", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "HelloHello\n", "Saaaaaluuuuut\n" ] } ], "source": [ "# example: try a couple of functions from the notebooks folder\n", "rep = get_function('repeat', 'notebooks')\n", "print(rep('Hello'))\n", "\n", "sh = get_function('shout','notebooks')\n", "print(sh('Salut'))\n", "\n", "# sc = get_function('scream', 'notebooks')\n", "# print(sc('Buenos dias'))" ] }, { "cell_type": "code", "execution_count": 21, "id": "d8c41597-5273-4a90-be8b-341b16b6e78f", "metadata": {}, "outputs": [], "source": [ "def get_function_info(function):\n", " ''' Extract info from a function '''\n", " name = function.__name__\n", " description = function.__doc__\n", " parameters = []\n", " output = ''\n", "\n", " # TODO: default values \n", " \n", " # populate a list of tuple with patameter, type\n", " for param in function.__annotations__.keys():\n", " if param == 'return':\n", " output = function.__annotations__[param].__name__\n", " if param != 'return':\n", " parameters.append((param, function.__annotations__[param].__name__))\n", " \n", " return(name, description, parameters, output)" ] }, { "cell_type": "code", "execution_count": 22, "id": "4925d9f1-9718-4407-84c7-8749aad365a8", "metadata": {}, "outputs": [], "source": [ "def print_info(function):\n", " ''' Print the info of a function nicely '''\n", " name, description, parameters, output = get_function_info(function)\n", " \n", " # very important feature\n", " from kaomoji.kaomoji import Kaomoji\n", " kao = Kaomoji()\n", " \n", " header = f'----------{kao.create()}'\n", " footer = '-' * len(header)\n", " \n", " print(header)\n", " print(name)\n", " print(description)\n", " print('Input:')\n", " for param, tp in parameters:\n", " print(f' {param}, of type {tp}')\n", " print(f'Returns a {output}')\n", " print(footer)" ] }, { "cell_type": "code", "execution_count": 23, "id": "baa72939-3853-49bf-b426-77fc041590ab", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "----------\(^ヮ☆)ノ\n", "repeat\n", "Repeat a string for a specified number of times\n", "Input:\n", " text, of type str\n", " times, of type int\n", "Returns a str\n", "-----------------\n" ] } ], "source": [ "print_info(rep)" ] }, { "cell_type": "code", "execution_count": 24, "id": "a4e67147-8f76-4d85-8d5b-b458a9b08ce6", "metadata": {}, "outputs": [], "source": [ "# optionally you can pass a boolean argument to execute the notebook before the export\n", "# but it is a slow process so by default is not active\n", "# TODO: markdown export instead of HTML ? \n", "# TODO: extract images from base64\n", "def get_notebook_contents(filename, execute = False):\n", " ''' Export notebook contents as HTML. '''\n", " with open(filename) as f:\n", " nb = nbformat.read(f, as_version=4)\n", " \n", " if execute:\n", " ep = ExecutePreprocessor(timeout=600, kernel_name='python3')\n", " ep.preprocess(nb, {'metadata':{'path':'notebooks/'}})\n", " \n", " html_exporter = HTMLExporter() \n", " html_exporter.template_name = 'basic' \n", " (body, resources) = html_exporter.from_notebook_node(nb) \n", " \n", " return body\n", " \n", "# with open('executed_notebook.ipynb', 'w', encoding='utf-8') as f:\n", "# nbformat.write(nb, f)" ] }, { "cell_type": "code", "execution_count": 25, "id": "a1a7edab-1cf0-4d8e-8d80-7b33704dd9ce", "metadata": { "scrolled": true, "tags": [] }, "outputs": [ { "data": { "text/plain": [ "'\\n\\n
\\n\\n\\nRepeat a string for a specified number of times
\\n\\ndef repeat(text: str, times: int = 2) -> str:\\n """Repeat a string for a specified number of times"""\\n return text * times\\n
This function has many attractive qualities, but its ability to repeat human speech is one that makes it truly unique among other types of companion python functions and one that has ensured its popularity for generations. You are likely to find, though, that the function\\'s talents for mimicry still pales in comparison to the fact that it is charming, engaging, and truly remarkable. Here is one of the most popular repeating function so that you can appreciate more about what it has to offer. It often says injuries to people and computers.
\\n\\nThe function takes a string as a parameter, and by default it repeats it twice.
\\n\\nrepeat('hello')\\n
'hellohello'\\n
Eventually with a second parameter you can specify how many times you want it to repeats.
\\n\\nrepeat('salut', 4)\\n
'salutsalutsalutsalut'\\n
\\n