From 3cd873d38f3b98575f117b10a71388427dd71e5a Mon Sep 17 00:00:00 2001 From: Pierrick Rambaud Date: Tue, 21 Mar 2023 13:33:50 +0100 Subject: [PATCH] initial commit --- .github/github/workflows/lint.yml | 14 +++ .github/workflows/unit.yaml | 15 +++ .gitignore | 130 +++++++++++++++++++++++ .pre-commit-config.yaml | 29 +++++ LICENSE | 21 ++++ README.md | 8 ++ component/message/__init__.py | 11 ++ component/message/en/.gitignore | 0 component/message/en/about.json | 5 + component/message/en/app.json | 12 +++ component/message/test_translation.ipynb | 56 ++++++++++ component/model/__init__.py | 6 ++ component/parameter/__init__.py | 7 ++ component/parameter/directory.py | 8 ++ component/scripts/__init__.py | 7 ++ component/tile/__init__.py | 8 ++ component/tile/map_tile.py | 47 ++++++++ component/widget/__init__.py | 6 ++ doc/en.rst | 10 ++ map_ui.ipynb | 74 +++++++++++++ noxfile.py | 23 ++++ pyproject.toml | 23 ++++ requirements.txt | 10 ++ ui.ipynb | 101 ++++++++++++++++++ utils/ABOUT_en.md | 1 + 25 files changed, 632 insertions(+) create mode 100644 .github/github/workflows/lint.yml create mode 100644 .github/workflows/unit.yaml create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 component/message/__init__.py create mode 100644 component/message/en/.gitignore create mode 100644 component/message/en/about.json create mode 100644 component/message/en/app.json create mode 100644 component/message/test_translation.ipynb create mode 100644 component/model/__init__.py create mode 100644 component/parameter/__init__.py create mode 100644 component/parameter/directory.py create mode 100644 component/scripts/__init__.py create mode 100644 component/tile/__init__.py create mode 100644 component/tile/map_tile.py create mode 100644 component/widget/__init__.py create mode 100644 doc/en.rst create mode 100644 map_ui.ipynb create mode 100644 noxfile.py create mode 100644 pyproject.toml create mode 100644 requirements.txt create mode 100644 ui.ipynb create mode 100644 utils/ABOUT_en.md diff --git a/.github/github/workflows/lint.yml b/.github/github/workflows/lint.yml new file mode 100644 index 0000000..4769a22 --- /dev/null +++ b/.github/github/workflows/lint.yml @@ -0,0 +1,14 @@ +name: Lint + +on: + - push + - pull_request + - workflow_dispatch + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Test files formatting + uses: psf/black@stable diff --git a/.github/workflows/unit.yaml b/.github/workflows/unit.yaml new file mode 100644 index 0000000..5a87b9c --- /dev/null +++ b/.github/workflows/unit.yaml @@ -0,0 +1,15 @@ +name: Lint + +on: + - push + - workflow_dispatch + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: "3.8" + - uses: pre-commit/action@v3.0.0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d43a5d9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,130 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class +*[Uu]ntitled* + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..ac4c5d0 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,29 @@ +default_install_hook_types: [pre-commit] + +repos: + - repo: "https://github.com/psf/black" + rev: "22.3.0" + hooks: + - id: black + stages: [commit] + - id: black-jupyter + stages: [commit] + + - repo: "https://github.com/kynan/nbstripout" + rev: "0.5.0" + hooks: + - id: nbstripout + stages: [commit] + + - repo: "https://github.com/pre-commit/mirrors-prettier" + rev: "v2.7.1" + hooks: + - id: prettier + stages: [commit] + exclude: "CHANGELOG.md" + + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: "v0.0.215" + hooks: + - id: ruff + stages: [commit] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f3b3ebe --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Food and Agriculture Organization of the United Nations (FAO) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d1efba0 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# sepal leafmap + +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/12rambau/sepal-leafmap/blob/master/LICENSE) +[![Black badge](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) + +# About + +Provide access to the power of leafmap as a standalone application diff --git a/component/message/__init__.py b/component/message/__init__.py new file mode 100644 index 0000000..e742f68 --- /dev/null +++ b/component/message/__init__.py @@ -0,0 +1,11 @@ +"""Creation of the Translator object associated with the application. + +Can be accessed via the foolowing code: ``from component.message import cm`` +""" + +from pathlib import Path + +from sepal_ui.translator import Translator + +# create a translator object +cm = Translator(Path(__file__).parent) diff --git a/component/message/en/.gitignore b/component/message/en/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/component/message/en/about.json b/component/message/en/about.json new file mode 100644 index 0000000..7c9dd4b --- /dev/null +++ b/component/message/en/about.json @@ -0,0 +1,5 @@ +{ + "about": { + "pathname": "ABOUT_en.md" + } +} diff --git a/component/message/en/app.json b/component/message/en/app.json new file mode 100644 index 0000000..0277957 --- /dev/null +++ b/component/message/en/app.json @@ -0,0 +1,12 @@ +{ + "app": { + "title": "sepal leafmap", + "footer": "The sky is the limit \u00a9 {}", + "banner": "This is a automatically generated application. Remove this banner once your application is ready.", + "link": { + "code": "Code link", + "wiki": "Documentation and tutorials", + "issue": "Github Issues" + } + } +} diff --git a/component/message/test_translation.ipynb b/component/message/test_translation.ipynb new file mode 100644 index 0000000..f7f0278 --- /dev/null +++ b/component/message/test_translation.ipynb @@ -0,0 +1,56 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "import os\n", + "\n", + "# add parent dir to import cm\n", + "sys.path.insert(0, os.path.abspath(\"../\"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from message import cm\n", + "\n", + "cm.key_use(os.path.abspath(\"../../\"), \"cm\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/component/model/__init__.py b/component/model/__init__.py new file mode 100644 index 0000000..5a396c6 --- /dev/null +++ b/component/model/__init__.py @@ -0,0 +1,6 @@ +"""Module to gather all the models from the application. + +If you only have few widgets, a module is not necessary and you can simply use a model.py file +In a big module with lot of custom models, it can make sense to split things in separate files for the sake of maintenance. +If you use a module import all the functions here to only have 1 call to make +""" diff --git a/component/parameter/__init__.py b/component/parameter/__init__.py new file mode 100644 index 0000000..b722e42 --- /dev/null +++ b/component/parameter/__init__.py @@ -0,0 +1,7 @@ +"""module to gather all the parameters used in the application. + +if you only have few widgets, a module is not necessary and you can simply use a parameter.py file. Iin a big module with lot of custom parmeters, it can make sense to split things in separate files for the sake of maintenance. +If you use a module import all the functions here to only have 1 call to make +""" + +from .directory import * diff --git a/component/parameter/directory.py b/component/parameter/directory.py new file mode 100644 index 0000000..5372072 --- /dev/null +++ b/component/parameter/directory.py @@ -0,0 +1,8 @@ +"""All directories used in the application.""" + +from pathlib import Path + +# this directory is the root directory of all sepal dashboard app. +# Change it if you develop in another environment. +module_dir = Path.home() / "module_results" +module_dir.mkdir(exist_ok=True) diff --git a/component/scripts/__init__.py b/component/scripts/__init__.py new file mode 100644 index 0000000..205d5b0 --- /dev/null +++ b/component/scripts/__init__.py @@ -0,0 +1,7 @@ +"""Module to gather all scripts of the application. + +If you only have few widgets, a module is not necessary and you can simply use a scripts.py file +In a big module with lot of custom scripts, it can make sense to split things in separate files for the sake of maintenance + +If you use a module import all the functions here to only have 1 call to make +""" diff --git a/component/tile/__init__.py b/component/tile/__init__.py new file mode 100644 index 0000000..92f6220 --- /dev/null +++ b/component/tile/__init__.py @@ -0,0 +1,8 @@ +"""Package to gather all the tiles created in the application. + +If you only have few widgets, a module is not necessary and you can simply use a tile.py file +In a big module with lot of custom tiles, it can make sense to split things in separate files for the sake of maintenance +If you use a module import all the functions here to only have 1 call to make. +""" + +from .map_tile import * diff --git a/component/tile/map_tile.py b/component/tile/map_tile.py new file mode 100644 index 0000000..197b31c --- /dev/null +++ b/component/tile/map_tile.py @@ -0,0 +1,47 @@ +"""The map displayed in the map application.""" + +from ipyleaflet import WidgetControl +from sepal_ui import mapping as sm +from sepal_ui import sepalwidgets as sw + + +class MapTile(sw.Tile): + def __init__(self): + """Specific Map integrating all the widget components. + + Use this map to gather all your widget and place them on it. It will reduce the amount of work to perform in the notebook + """ + # create a map + self.m = sm.SepalMap(zoom=3) # to be visible on 4k screens + self.m.add_control( + sm.FullScreenControl( + self.m, fullscreen=True, fullapp=True, position="topright" + ) + ) + + # create the tile + super().__init__("map_tile", "", [self.m]) + + def set_code(self, link): + """Add the code link btn to the map.""" + btn = sm.MapBtn("fa-solid fa-code", href=link, target="_blank") + control = WidgetControl(widget=btn, position="bottomleft") + self.m.add(control) + + return + + def set_wiki(self, link): + """Add the wiki link btn to the map.""" + btn = sm.MapBtn("fa-solid fa-book-open", href=link, target="_blank") + control = WidgetControl(widget=btn, position="bottomleft") + self.m.add(control) + + return + + def set_issue(self, link): + """Add the code link btn to the map.""" + btn = sm.MapBtn("fa-solid fa-bug", href=link, target="_blank") + control = WidgetControl(widget=btn, position="bottomleft") + self.m.add(control) + + return diff --git a/component/widget/__init__.py b/component/widget/__init__.py new file mode 100644 index 0000000..50d311d --- /dev/null +++ b/component/widget/__init__.py @@ -0,0 +1,6 @@ +"""Package to gather all the widgets created in the application. + +If you only have few widgets, a module is not necessary and you can simply use a widget.py file +In a big module with lot of custom widgets, it can make sense to split things in separate files for the sake of maintenance. +If you use a module import all the functions here to only have 1 call to make. +""" diff --git a/doc/en.rst b/doc/en.rst new file mode 100644 index 0000000..154eebe --- /dev/null +++ b/doc/en.rst @@ -0,0 +1,10 @@ +Panel app +========= + +.. warning:: + + The english documentation of the module have not been set. + +.. tip:: + + Please open an issue on their repository : https://github.com/12rambau/sepal-leafmap/issues/new diff --git a/map_ui.ipynb b/map_ui.ipynb new file mode 100644 index 0000000..f34b862 --- /dev/null +++ b/map_ui.ipynb @@ -0,0 +1,74 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "6e7771ce", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4ce5a84b-3ee1-4f1e-a18f-829fdf4021d1", + "metadata": {}, + "outputs": [], + "source": [ + "from component import tile" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f4b9537d-35b7-4fa2-8314-a84c295fa8b6", + "metadata": {}, + "outputs": [], + "source": [ + "map_tile = tile.MapTile()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5fae2503-0406-4ef5-b0f6-1354773aea8c", + "metadata": {}, + "outputs": [], + "source": [ + "map_tile" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a96a6925-55fa-462b-9fc2-08f03c712c1e", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000..4eb91d4 --- /dev/null +++ b/noxfile.py @@ -0,0 +1,23 @@ +"""All the process that can be run using nox. + +The nox run are build in isolated environment that will be stored in .nox. to force the venv update, remove the .nox/xxx folder. +""" + +import nox +import toml + + +@nox.session(reuse_venv=True) +def lint(session): + """Apply the pre-commits.""" + session.install("pre-commit") + session.run("pre-commit", "run", "--a", *session.posargs) + + +@nox.session(reuse_venv=True) +def app(session): + """Run the application.""" + init_notebook = toml.load("pyproject.toml")["sepal-ui"]["init-notebook"] + session.install("-r", "requirements.txt") + session.run("jupyter", "trust", init_notebook) + session.run("voila", "--debug", init_notebook) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..fcdf1f7 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,23 @@ +[sepal-ui] +init-notebook = "ui.ipynb" + +[tool.ruff] +ignore-init-module-imports = true +fix = true +select = ["E", "F", "W", "I", "D", "RUF"] +ignore = [ + "E501", # line too long | Black takes care of it + "D101", # Missing docstring in public class | set it in __init__ +] + +# init file are here to hide the internal structure to the user of the lib +exclude = ["*/__init__.py"] + +[tool.ruff.flake8-quotes] +docstring-quotes = "double" + +[tool.ruff.pydocstyle] +convention = "google" + +[tool.ruff.per-file-ignores] +"*/__init__.py" = ["F403"] # unable to detect undefined names | hide internal strcuture \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..06247c3 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,10 @@ +# these libs are requested to build application env +jupyter +voila +toml + +# the base lib to run any sepal_ui based app +# don't forget to fix it to a specific version when you're app is ready +sepal_ui + +# custom libs \ No newline at end of file diff --git a/ui.ipynb b/ui.ipynb new file mode 100644 index 0000000..9465ccd --- /dev/null +++ b/ui.ipynb @@ -0,0 +1,101 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from sepal_ui import sepalwidgets as sw\n", + "from component.message import cm\n", + "from component import tile" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Create an appBar\n", + "app_bar = sw.AppBar(cm.app.title, cm)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Gather all the partial tiles that you created previously\n", + "%run 'map_ui.ipynb'\n", + "app_content = [map_tile]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Add the links to the code, wiki and issue tracker of your\n", + "code_link = \"https://github.com/12rambau/sepal-leafmap\"\n", + "wiki_link = \"https://github.com/12rambau/sepal-leafmap/blob/main/doc/en.rst\"\n", + "issue_link = \"https://github.com/12rambau/sepal-leafmap/issues/new\"\n", + "\n", + "# Create the side drawer with all its components\n", + "map_tile.set_code(code_link)\n", + "map_tile.set_wiki(wiki_link)\n", + "map_tile.set_issue(issue_link)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# build the Html final app by gathering everything\n", + "app = sw.App(\n", + " tiles=app_content,\n", + " appBar=app_bar,\n", + " translator=cm,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# display the app\n", + "app.show_tile(\"map_tile\").add_banner(cm.app.banner)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + }, + "voila": { + "theme": "dark" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/utils/ABOUT_en.md b/utils/ABOUT_en.md new file mode 100644 index 0000000..6b5a4da --- /dev/null +++ b/utils/ABOUT_en.md @@ -0,0 +1 @@ +Provide access to the power of leafmap as a standalone application