├── .editorconfig ├── .flake8 ├── .gitattributes ├── .github ├── rename_project.sh └── workflows │ ├── archive-github-pages.yml │ ├── build-sphinx.yml │ └── iga.yml ├── .gitignore ├── .graphics ├── caltech-round.png ├── caltech-round.svg ├── example-splash-screen.png ├── foliage-icon.png ├── foliage-icon.svg ├── foliage-screenshot.png └── status-warning.svg ├── .jsonlintrc.json ├── .markdownlint.json ├── .yamllint.yml ├── CHANGES.md ├── CITATION.cff ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── INSTALL-Python3.md ├── LICENSE ├── Makefile ├── README.md ├── SUPPORT.md ├── bin ├── README.md └── foliage ├── codemeta.json ├── dev ├── dev-docs │ ├── README.md │ ├── creating-a-splash-screen.md │ ├── installing-pyqt5-on-macos.md │ ├── making-a-new-release.md │ ├── system-widget.md │ ├── using-pyinstaller.md │ └── windows-installer.md ├── icon │ ├── README.md │ ├── foliage-icon-white-sm.png │ ├── foliage-icon-white.png │ ├── foliage-icon-white.svg │ ├── foliage-icon.icns │ ├── foliage-icon.ico │ ├── foliage-icon.png │ ├── foliage-icon.svg │ └── noun_Branch_1047074.svg ├── installers │ └── windows │ │ ├── create-innosetup-script.py │ │ ├── create-version.py │ │ ├── create-zip.py │ │ ├── foliage_innosetup_script.iss.tmpl │ │ └── version.py.tmpl ├── misc │ ├── experiments │ │ ├── isbn_checker.py │ │ ├── macos-widget.py │ │ └── settings.ini │ ├── folio-data-samples │ │ ├── README.md │ │ └── sample-2-holdings-3-items │ │ │ ├── holdings-storage-holdings-795b4843-2736-4ccc-ade6-c928f8628831.json │ │ │ ├── holdings-storage-holdings-c0b6d0cf-38d0-43f7-b476-7debbba52bc5.json │ │ │ ├── inventory-instance-848d5d4b-6e49-49d9-b02b-c9eb19d8b07d.json │ │ │ ├── inventory-item-20144f00-d424-4f2f-acf8-340263b16d8f.json │ │ │ ├── inventory-item-ced4bfd1-2f52-4362-93ca-55c4e38b9cf6.json │ │ │ ├── inventory-item-f7031f1b-240b-424c-8f54-79a3e83b97ae.json │ │ │ └── item-storage-item-20144f00-d424-4f2f-acf8-340263b16d8f.json │ └── t.py ├── one-page-docs │ ├── pandoc-template │ │ ├── README.md │ │ └── template.html5 │ ├── read-me-first-macos │ │ ├── control-click.png │ │ ├── file-dialog.png │ │ ├── macos-malicious-warning.png │ │ └── read-me-first.md │ ├── read-me-first-windows │ │ ├── browser-download.png │ │ ├── download-anyway.png │ │ ├── google-keep.png │ │ ├── google-warning.png │ │ ├── hover-over-downloads.png │ │ ├── keep-anyway.png │ │ ├── read-me-first.md │ │ ├── see-more.png │ │ └── trust-foliage.png │ └── sakura-css │ │ ├── README.md │ │ └── sakura.css └── splash-screen │ ├── README.md │ ├── create-splash-screen.py │ └── foliage-splash-screen.svg.tmpl ├── docs ├── .nojekyll ├── Makefile ├── README.md ├── _static │ ├── css │ │ └── custom.css │ ├── media │ │ ├── README.md │ │ ├── authentication.png │ │ ├── caltech-round.png │ │ ├── change-tab-select-field.png │ │ ├── change-tab-selected-field.png │ │ ├── change-tab-value-selector.png │ │ ├── change-tab.png │ │ ├── clean-records-tab.png │ │ ├── delete-tab.png │ │ ├── favicon.ico │ │ ├── foliage-change-case-1.graffle │ │ ├── foliage-change-case-1.svg │ │ ├── foliage-change-case-2.graffle │ │ ├── foliage-change-case-2.svg │ │ ├── foliage-change-case-3.graffle │ │ ├── foliage-change-case-3.svg │ │ ├── foliage-change-case-4.graffle │ │ ├── foliage-change-case-4.svg │ │ ├── foliage-icon-white-sm.png │ │ ├── foliage-icon-white.png │ │ ├── foliage-icon-white.svg │ │ ├── foliage-icon.png │ │ ├── foliage-icon.svg │ │ ├── foliage-macos-systray.png │ │ ├── foliage-windows-taskbar.png │ │ ├── list-uuids-example.png │ │ ├── list-uuids-tab.png │ │ ├── lookup-barcode.png │ │ ├── macos-accept-connections.png │ │ ├── macos-keychain.png │ │ ├── main-page.png │ │ ├── omnigraffle-export-options.png │ │ ├── other-tab.png │ │ └── windows-accept-connections.png │ └── versions.json ├── colophon.md ├── conf.py ├── first-time-usage.md ├── foliage-interface.md ├── glossary.md ├── index.md ├── installation.md └── nitty.md ├── entitlements.plist ├── foliage ├── __init__.py ├── __main__.py ├── base_tab.py ├── change_tab.py ├── clean_tab.py ├── credentials.py ├── data │ ├── foliage-icon-128x128.png │ ├── foliage-icon-256x256.png │ ├── foliage-icon-32x32.png │ ├── foliage-icon-64x64.png │ ├── foliage-icon-r.png │ ├── foliage-icon.ico │ ├── foliage-icon.png │ ├── foliage-icon.svg │ ├── index.tpl │ └── macos-systray-widget │ │ ├── README.md │ │ ├── go.mod │ │ ├── go.sum │ │ ├── icon │ │ ├── README.md │ │ ├── icon-64.png │ │ ├── iconunix.go │ │ └── make_icon.sh │ │ └── macos-systray-widget.go ├── delete_tab.py ├── enum_utils.py ├── exceptions.py ├── export.py ├── folio.py ├── list_tab.py ├── lookup_tab.py ├── other_tab.py ├── system_widget.py └── ui.py ├── make.bat ├── pyinstaller-macos.spec ├── pyinstaller-win32.spec ├── requirements-dev.txt ├── requirements-windows.txt ├── requirements.txt ├── setup.cfg ├── setup.py ├── tests ├── settings.ini ├── test_credentials.py ├── test_folio.py └── test_init.py └── vendor └── github.com └── mhucka └── PyWebIO ├── .drone.yml ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ ├── lint.yml │ ├── release.yml │ ├── sync_repo.yml │ └── test.yml ├── .gitignore ├── .readthedocs.yml ├── LICENSE ├── MANIFEST.in ├── Procfile ├── README.md ├── demos ├── __init__.py ├── __main__.py ├── bmi.py ├── bokeh_app.py ├── chat_room.py ├── config.py ├── doc_demo.py ├── input_usage.py ├── output_usage.py └── set_env_demo.py ├── docs ├── FAQ.rst ├── Makefile ├── _ext │ ├── README.md │ └── codeblock.py ├── arch.rst ├── assets │ ├── architecture.png │ ├── codemirror_textarea.png │ ├── demo.gif │ ├── demo.png │ ├── input_1.png │ ├── input_2.png │ ├── input_demo.gif │ ├── layout.png │ ├── output_demo.gif │ ├── put_table.png │ ├── table_onclick.gif │ └── table_onclick.png ├── conf.py ├── demos.rst ├── exceptions.rst ├── guide.rst ├── index.rst ├── input.rst ├── libraries_support.rst ├── make.bat ├── misc.rst ├── output.rst ├── platform.rst ├── releases.rst ├── releases │ ├── v0.2.0.rst │ ├── v0.3.0.rst │ ├── v1.0.0.rst │ └── v1.1.0.rst ├── session.rst ├── spec.rst └── static │ ├── pywebio.css │ └── pywebio.js ├── lgtm.yml ├── pywebio ├── __init__.py ├── __version__.py ├── exceptions.py ├── html │ ├── codemirror │ │ ├── active-line.js │ │ ├── base16-light.min.css │ │ ├── loadmode.js │ │ ├── matchbrackets.js │ │ └── python.js │ ├── css │ │ ├── app.css │ │ ├── bootstrap.min.css │ │ ├── codemirror.min.css │ │ ├── markdown.min.css │ │ └── toastify.min.css │ ├── image │ │ ├── favicon_closed_16.png │ │ ├── favicon_closed_32.png │ │ ├── favicon_open_16.png │ │ └── favicon_open_32.png │ ├── index.html │ └── js │ │ ├── FileSaver.min.js │ │ ├── bootstrap.min.js │ │ ├── bs-custom-file-input.min.js │ │ ├── codemirror.min.js │ │ ├── jquery.min.js │ │ ├── mustache.min.js │ │ ├── popper.min.js │ │ ├── prism.min.js │ │ ├── purify.min.js │ │ ├── pywebio.min.js │ │ ├── pywebio.min.js.map │ │ ├── require.min.js │ │ └── toastify.min.js ├── input.py ├── io_ctrl.py ├── output.py ├── platform │ ├── __init__.py │ ├── aiohttp.py │ ├── bokeh.py │ ├── django.py │ ├── flask.py │ ├── httpbased.py │ ├── tornado.py │ ├── tpl │ │ └── index.html │ └── utils.py ├── session │ ├── __init__.py │ ├── base.py │ ├── coroutinebased.py │ └── threadbased.py └── utils.py ├── requirements.txt ├── setup.py ├── test ├── .percy.yml ├── 1.basic.py ├── 10.aiohttp_multiple_session_impliment.py ├── 11.charts.py ├── 12.cors.py ├── 13.misc.py ├── 14.django_multiple_session_impliment.py ├── 2.script_mode.py ├── 3.django_backend.py ├── 4.flask_backend.py ├── 5.coroutine_based_session.py ├── 6.flask_coroutine.py ├── 7.multiple_session_impliment.py ├── 8.flask_multiple_session_impliment.py ├── 9.aiohttp_backend.py ├── Readme.md ├── assets │ ├── helloworld.txt │ └── img.png ├── output_diff.py ├── run_all.sh ├── template.py └── util.py ├── tools └── build_dev_version.py └── webiojs ├── .gitignore ├── README.md ├── gulpfile.js ├── package-lock.json ├── package.json ├── src ├── handlers │ ├── base.ts │ ├── download.ts │ ├── env.ts │ ├── input.ts │ ├── output.ts │ ├── popup.ts │ ├── script.ts │ └── toast.ts ├── i18n.ts ├── main.ts ├── models │ ├── input │ │ ├── actions.ts │ │ ├── base.ts │ │ ├── checkbox_radio.ts │ │ ├── file.ts │ │ ├── index.ts │ │ ├── input.ts │ │ ├── select.ts │ │ └── textarea.ts │ └── output.ts ├── session.ts ├── state.ts ├── ui.ts ├── utils.ts └── vendor.d.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Summary: EditorConfig file for this project. -*- conf -*- 2 | # 3 | # For more information, see https://EditorConfig.org 4 | # 5 | # Copyright 2024 California Institute of Technology. 6 | # License: Modified BSD 3-clause – see file "LICENSE" in the project website. 7 | # Website: https://github.com/caltechlibrary/baler 8 | 9 | root = true 10 | 11 | [*] 12 | charset = utf-8 13 | end_of_line = lf 14 | indent_size = 4 15 | indent_style = space 16 | insert_final_newline = true 17 | max_line_length = 90 18 | tab_width = 4 19 | trim_trailing_whitespace = true 20 | 21 | [*.cfg] 22 | indent_size = 2 23 | 24 | [*.json] 25 | indent_size = 2 26 | 27 | [*.{yml, yaml}] 28 | indent_size = 2 29 | 30 | # Shell scripts on Windows. 31 | [*.{cmd, bat}] 32 | end_of_line = crlf 33 | 34 | [Makefile, makefile] 35 | indent_size = 4 36 | indent_style = tab 37 | tab_width = 8 38 | 39 | [.applescript] 40 | indent_size = 4 41 | indent_style = tab 42 | tab_width = 4 43 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | # =========================================================== -*- conf-toml -*- 2 | # @file .flake8 3 | # @brief Project-wide Flake8 configuration 4 | # @created 2022-05-10 5 | # @license Please see the file named LICENSE in the project directory 6 | # @website https://github.com/caltechlibrary/foliage 7 | # 8 | # Note: as of version 4.0, flake8 does NOT read global configuration files 9 | # from ~/.flake8 or ~/.config/flake8. If you had such a config file of your 10 | # own, and you're looking at this config file and wondering how the two will 11 | # interaction, the answer is simple: they won't. Only this file matters. 12 | # 13 | # The following flake8 plugins are assumed to be installed: 14 | # flake8-bugbear 15 | # flake8-builtins 16 | # flake8-comprehensions 17 | # flake8-executable 18 | # flake8-implicit-str-concat 19 | # flake8-pie 20 | # flake8_simplify 21 | # ============================================================================= 22 | 23 | [flake8] 24 | # I try to stick to 80 chars, but sometimes it's more readable to go longer. 25 | max-line-length = 120 26 | 27 | ignore = 28 | # We prefer to put spaces around the = in keyword arg lists. 29 | E251, 30 | # We prefer two lines between methods of a class. 31 | E303, 32 | # Sometimes we want to align keywords, and these rules run counter to it. 33 | E271, 34 | E221, 35 | # In some situations, it's more readable to omit spaces around operators 36 | # and colons. 37 | E203, 38 | E226, 39 | # According to Flake8 docs at https://www.flake8rules.com/rules/W503.html 40 | # line breaks *should* come before a binary operator, but as of version 4, 41 | # Flake8 still flags the breaks as bad. So: 42 | W503 43 | # I disagree wit this one. 44 | B005 45 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # -*- mode: sh; -*- 2 | 3 | # Set the default behavior, in case people don't have core.autocrlf set. 4 | # ............................................................................. 5 | 6 | * text=auto 7 | 8 | # Specify what's text and should be normalized. 9 | # ............................................................................. 10 | 11 | *.py text 12 | *.in text 13 | *.rst text 14 | *.cfg text 15 | *.ini text 16 | *.yml text 17 | *.json text 18 | *.bat text 19 | *.sh text 20 | LICENSE text 21 | CONTRIBUTING text 22 | 23 | # Denote all files that are truly binary and should not be modified. 24 | # ............................................................................. 25 | 26 | *.png binary 27 | *.jpg binary 28 | *.xls binary 29 | *.doc binary 30 | 31 | # This next one is because in other projects, we've had problems with git 32 | # getting confused about line endings when people using Windows and Mac edit 33 | # the same files. 34 | # ............................................................................. 35 | 36 | *.csv binary diff=csv 37 | -------------------------------------------------------------------------------- /.github/rename_project.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # ============================================================================= 3 | # @file rename_project.sh 4 | # @brief Shell script used in GitHub workflow for naming new projects 5 | # @created 2021-10-14 6 | # @license Please see the file named LICENSE in the project directory 7 | # @website https://github.com/caltechlibrary/py-cli-template 8 | # 9 | # This file was originally based on the file of the same name in the repo 10 | # https://github.com/rochacbruno/python-project-template by Bruno Rocha. 11 | # The original file was copied on 2021-10-14. 12 | # ============================================================================= 13 | 14 | while getopts a:n:u:d: flag 15 | do 16 | case "${flag}" in 17 | a) author=${OPTARG};; 18 | n) project_name=${OPTARG};; 19 | u) urlname=${OPTARG};; 20 | d) description=${OPTARG};; 21 | esac 22 | done 23 | 24 | first_name=$(echo $author | /usr/bin/awk '{print $1}' | tr -d '"') 25 | family_name=$(echo $author | /usr/bin/awk '{print $NF}' | tr -d '"') 26 | 27 | creation_date=$(date +"%Y-%m-%d") 28 | creation_year=$(date +"%Y") 29 | 30 | echo "Author name: $author" 31 | echo "Author first name: $first_name" 32 | echo "Author family name: $family_name" 33 | echo "Project name: $project_name" 34 | echo "Project URL name: $urlname" 35 | echo "Description: $description" 36 | echo "Creation date: $creation_date" 37 | echo "Creation year: $creation_year" 38 | 39 | echo "Renaming project ..." 40 | 41 | for filename in $(git ls-files) 42 | do 43 | sed -i "s/Mike Hucka/$author/g" $filename 44 | sed -i "s/Mike/$first_name/g" $filename 45 | sed -i "s/Hucka/$family_name/g" $filename 46 | sed -i "s/foliage/$project_name/g" $filename 47 | sed -i "s/foliage/$urlname/g" $filename 48 | sed -i "s/Foliage is the FOLIo chAnGe Editor, a tool to do bulk changes in FOLIO using the network API/$description/g" $filename 49 | sed -i "s/2021-10-16/$creation_date/g" $filename 50 | sed -i "s/2021/$creation_year/g" $filename 51 | echo "Performed substitutions in $filename" 52 | done 53 | 54 | mv project_name $project_name 55 | rm -f codemeta.json 56 | mv codemeta-TEMPLATE.json codemeta.json 57 | rm -f CITATION.cff 58 | mv CITATION-TEMPLATE.cff CITATION.cff 59 | 60 | # This command runs only once on GHA! 61 | rm -rf .github/template.yml 62 | 63 | echo "Renaming project ... Done." 64 | -------------------------------------------------------------------------------- /.github/workflows/archive-github-pages.yml: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # @file archive-github-pages.yml 3 | # @brief Save the Github pages in the Internet Archive 4 | # @author Michael Hucka 5 | # @license Please see the file named LICENSE in the repository 6 | # @repo https://github.com/caltechlibrary/foliage 7 | # ============================================================================= 8 | 9 | name: Archive latest GitHub Pages in IA 10 | on: 11 | release: 12 | types: [published] 13 | jobs: 14 | Workflow: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: caltechlibrary/waystation@main 18 | with: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | dry_run: false 21 | debug: false 22 | -------------------------------------------------------------------------------- /.github/workflows/build-sphinx.yml: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # @file build-myst.yml 3 | # @brief GitHub Actions workflow to build FOLIAGE docs using MyST 4 | # @author Michael Hucka 5 | # @license Please see the file named LICENSE in the project directory 6 | # @website https://github.com/caltechlibrary/foliage 7 | # 8 | # This workflow file was originally based on work by GitHub user "peaceiris": 9 | # https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-static-site-generators-with-python 10 | # ============================================================================= 11 | 12 | name: Build & publish Sphinx docs 13 | 14 | on: 15 | push: 16 | branches: 17 | - main 18 | pull_request: 19 | 20 | jobs: 21 | Workflow: 22 | runs-on: ubuntu-22.04 23 | permissions: 24 | contents: write 25 | concurrency: 26 | group: ${{ github.workflow }}-${{ github.ref }} 27 | steps: 28 | - uses: actions/checkout@v3 29 | 30 | - name: Set up Python 31 | uses: actions/setup-python@v4 32 | with: 33 | python-version: '3.8' 34 | 35 | - name: Upgrade pip 36 | run: | 37 | # Need pip=>20.1 to use "pip cache dir". 38 | python3 -m pip install --upgrade pip 39 | 40 | - name: Get pip cache dir 41 | id: pip-cache 42 | run: echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT 43 | 44 | - name: Cache dependencies 45 | uses: actions/cache@v3 46 | with: 47 | path: ${{ steps.pip-cache.outputs.dir }} 48 | key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }} 49 | restore-keys: | 50 | ${{ runner.os }}-pip- 51 | 52 | - name: Install dependencies 53 | run: python3 -m pip install -r ./requirements-dev.txt 54 | 55 | - name: Generate HTML files 56 | run: | 57 | cd docs 58 | make html 59 | 60 | - name: Publish HTML files on GitHub pages 61 | uses: peaceiris/actions-gh-pages@v3 62 | if: ${{ github.ref == 'refs/heads/main' }} 63 | with: 64 | github_token: ${{ secrets.GITHUB_TOKEN }} 65 | publish_dir: ./docs/_build/html 66 | -------------------------------------------------------------------------------- /.github/workflows/iga.yml: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # GitHub Action workflow for using the InvenioRDM GitHub Archiver (IGA). 3 | # A copy of this file is available from https://github.com/caltechlibrary/iga/. 4 | # At the time this file was copied, IGA was at version 0.0.12. 5 | # ============================================================================= 6 | 7 | env: 8 | INVENIO_SERVER: https://data.caltech.edu 9 | 10 | draft: false 11 | all_assets: false 12 | all_metadata: false 13 | community: none 14 | parent_record: none 15 | debug: false 16 | 17 | on: 18 | release: 19 | types: [published] 20 | workflow_dispatch: 21 | inputs: 22 | release_tag: 23 | description: "The tag of the release to archive:" 24 | draft: 25 | default: false 26 | description: "Mark the record as a draft:" 27 | all_assets: 28 | default: false 29 | description: "Attach all GitHub assets:" 30 | all_metadata: 31 | default: false 32 | description: "Include additional GitHub metadata:" 33 | community: 34 | description: "Send record to InvenioRDM community:" 35 | parent_record: 36 | description: "ID of parent record (for versioning):" 37 | jobs: 38 | Send_to_InvenioRDM: 39 | runs-on: ubuntu-latest 40 | steps: 41 | - uses: caltechlibrary/iga@main 42 | with: 43 | INVENIO_SERVER: ${{env.INVENIO_SERVER}} 44 | INVENIO_TOKEN: ${{secrets.INVENIO_TOKEN}} 45 | all_assets: ${{github.event.inputs.all_assets || env.all_assets}} 46 | all_metadata: ${{github.event.inputs.all_metadata || env.all_metadata}} 47 | debug: ${{github.event.inputs.debug || 'false'}} 48 | draft: ${{github.event.inputs.draft || env.draft}} 49 | community: ${{github.event.inputs.community || env.community}} 50 | parent_record: ${{github.event.inputs.parent_record || env.parent_record}} 51 | release_tag: ${{github.event.inputs.release_tag || 'latest'}} 52 | 53 | name: InvenioRDM GitHub Archiver 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # =========================================================== -*- gitignore -*- 2 | # @file .gitignore 3 | # @brief Files and patterns for files and subdirs that git should ignore 4 | # @date 2022-07-14 5 | # @license Please see the file named LICENSE in the project directory 6 | # @website https://github.com/caltechlibrary/foliage 7 | # 8 | # The approach we suggest is to add ONLY project-specific rules here. Put 9 | # rules that apply to your way of doing things (and the particular tools you 10 | # happen to use) into a global git ignore file as described in the section 11 | # "Configuring ignored files for all repositories on your computer" here: 12 | # https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files 13 | # (accessed on 2022-07-14). For example, Emacs checkpoint and backup files are 14 | # things that are not specific to a given project; rather, Emacs users will 15 | # see them created everywhere, in all projects, because they're a byproduct 16 | # of using Emacs, not a consequence of working on a particular project. Thus, 17 | # they belong in a user's global ignores list, not in this project .gitignore. 18 | # 19 | # A useful starting point for global .gitignore file contents can be found at 20 | # https://github.com/github/gitignore/tree/main/Global (as of 2022-07-14). 21 | # ============================================================================= 22 | 23 | # Ignore backup files created by our Makefile 24 | # ............................................................................. 25 | 26 | *.bak 27 | 28 | # InnoSetup-specific things to ignore: 29 | # ............................................................................. 30 | 31 | *.~is 32 | 33 | # Python-specific things to ignore (relevant because this is a Python project). 34 | # ............................................................................. 35 | 36 | __pycache__/ 37 | *.py[cod] 38 | *$py.class 39 | *.egg-info/ 40 | .eggs/ 41 | .pytest_cache 42 | .coverage 43 | 44 | # Project-specific things to ignore: 45 | # ............................................................................. 46 | 47 | dev/experiments/settings.ini 48 | build 49 | dist 50 | dev/windows/version.py 51 | dev/splash-screen/foliage-splash-screen.png 52 | docs/_build 53 | foliage/data/macos-systray-widget/macos-systray-widget 54 | ABOUT.md 55 | ABOUT.html 56 | dev/installers/windows/version.py 57 | dev/installers/windows/foliage_innosetup_script.iss 58 | dev/installers/macos/READ THIS PLEASE.html 59 | tests/settings.ini 60 | *.tmp 61 | dev/one-page-docs/read-me-first-macos/read-me-first.html 62 | dev/one-page-docs/read-me-first-windows/read-me-first.html 63 | -------------------------------------------------------------------------------- /.graphics/caltech-round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/.graphics/caltech-round.png -------------------------------------------------------------------------------- /.graphics/caltech-round.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Caltech Icon 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.graphics/example-splash-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/.graphics/example-splash-screen.png -------------------------------------------------------------------------------- /.graphics/foliage-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/.graphics/foliage-icon.png -------------------------------------------------------------------------------- /.graphics/foliage-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/.graphics/foliage-screenshot.png -------------------------------------------------------------------------------- /.graphics/status-warning.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Warning: the code currently makes assumptions about Caltech's 6 | 7 | 8 | FOLIO installation. Using it at another site will require modifying 9 | 10 | 11 | the code in foliage/folio.py. 12 | 13 | 14 | -------------------------------------------------------------------------------- /.jsonlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": false, 3 | "trailing-commas": false, 4 | "duplicate-keys": false, 5 | "log-files": false, 6 | "compact": true, 7 | "continue": true, 8 | "patterns": ["**/*.json"] 9 | } 10 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "blank_lines": { 3 | "maximum": 2 4 | }, 5 | "html": { 6 | "allowed_elements": [ 7 | "a", 8 | "b", 9 | "br", 10 | "code", 11 | "details", 12 | "div", 13 | "em", 14 | "figure", 15 | "figcaption", 16 | "i", 17 | "img", 18 | "ins", 19 | "kbd", 20 | "p", 21 | "picture", 22 | "source", 23 | "span", 24 | "sup", 25 | "summary" 26 | ] 27 | }, 28 | "line-length": { 29 | "line_length": 10000 30 | }, 31 | "no-alt-text": true, 32 | "no-duplicate-heading": { 33 | "allow_different_nesting": true 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.yamllint.yml: -------------------------------------------------------------------------------- 1 | # Summary: configuration file for .github/workflows/yaml-linter.yml. 2 | # 3 | # Copyright 2024 California Institute of Technology. 4 | # License: Modified BSD 3-clause – see file "LICENSE" in the project website. 5 | # Website: https://github.com/caltechlibrary/baler 6 | 7 | rules: 8 | colons: 9 | max-spaces-after: -1 10 | quoted-strings: 11 | required: only-when-needed 12 | document-start: 13 | present: false 14 | document-end: 15 | present: false 16 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2 2 | message: "If you use this software, please cite it using these metadata." 3 | title: "Foliage" 4 | authors: 5 | - 6 | affiliation: "Caltech Library" 7 | given-names: Mike 8 | family-names: Hucka 9 | orcid: "0000-0001-9105-5960" 10 | version: "1.8.0" 11 | abstract: "Foliage (FOLIo chAnGe Editor): a tool to do bulk changes in FOLIO using the OKAPI API" 12 | repository-code: "https://github.com/caltechlibrary/foliage" 13 | type: software 14 | license-url: "https://github.com/caltechlibrary/foliage/blob/main/LICENSE" 15 | keywords: 16 | - FOLIO 17 | - batch operations 18 | - desktop software 19 | date-released: 2024-06-07 20 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Guidelines for contributing to this project 2 | 3 | Any constructive contributions – bug reports, pull requests (code or documentation), suggestions for improvements, and more – are welcome. 4 | 5 | ## Conduct 6 | 7 | Everyone is asked to read and respect the [code of conduct](CODE_OF_CONDUCT.md) before participating in this project. 8 | 9 | ## Coordinating work 10 | 11 | A quick way to find out what is currently in the near-term plans for this project is to look at the [GitHub issue tracker](https://github.com/caltechlibrary/foliage/issues), but the possibilities are not limited to what you see there – if you have ideas for new features and enhancements, please feel free to write them up as a new issue or contact the developers directly! 12 | 13 | ## Submitting contributions 14 | 15 | Please feel free to contact the author directly, or even better, jump right in and use the standard GitHub approach of forking the repo and creating a pull request. When committing code changes and submitting pull requests, please write a clear log message for your commits. 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021-2023, Caltech. 2 | All rights not granted herein are expressly reserved by Caltech. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its contributors 15 | may be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | Support 2 | ======= 3 | 4 | Thank you for your interest in this project. If you are experiencing problems or have questions, the following are the preferred methods of reaching someone: 5 | 6 | 1. Report a new issue using the [issue tracker](https://github.com/caltechlibrary/foliage/issues). 7 | 2. Send email to the Caltech Library: [helpdesk@library.caltech.edu](mailto:helpdesk@library.caltech.edu). 8 | 3. Send email to an individual involved in the project. People's names appear in the top-level `README.md` file in the source code repository. 9 | -------------------------------------------------------------------------------- /bin/README.md: -------------------------------------------------------------------------------- 1 | # About the shell script in this directory 2 | 3 | The shell script in this directory is mainly for testing and development. During development, I run Foliage from a terminal emulator by starting it simply like this: 4 | 5 | ```sh 6 | ./foliage 7 | ``` 8 | 9 | When Foliage is installed on a computer using `pip` or `pipx`, a different wrapper script is installed, not the one that is in this directory. The one here is merely a convenience. 10 | -------------------------------------------------------------------------------- /bin/foliage: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # ============================================================================= 3 | # @file foliage 4 | # @brief Simple interface to run foliage, for testing and exploration 5 | # @author Michael Hucka 6 | # @license Please see the file named LICENSE in the project directory 7 | # @website https://github.com/caltechlibrary/foliage 8 | # ============================================================================= 9 | 10 | # Allow this program to be executed directly from the 'bin' directory. 11 | import os 12 | import sys 13 | import plac 14 | 15 | # Allow this program to be executed directly from the 'bin' directory. 16 | try: 17 | thisdir = os.path.dirname(os.path.abspath(__file__)) 18 | sys.path.append(os.path.join(thisdir, '..')) 19 | except: 20 | sys.path.append('..') 21 | 22 | # Hand over to the command line interface. 23 | import foliage 24 | from foliage.__main__ import main as main 25 | 26 | if __name__ == "__main__": 27 | plac.call(main) 28 | -------------------------------------------------------------------------------- /codemeta.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://doi.org/10.5063/schema/codemeta-2.0", 3 | "@type": "SoftwareSourceCode", 4 | "description": "Foliage: a tool to do bulk changes in FOLIO using the OKAPI API", 5 | "name": "foliage", 6 | "codeRepository": "https://github.com/caltechlibrary/foliage", 7 | "issueTracker": "https://github.com/caltechlibrary/foliage/issues", 8 | "license": "https://github.com/caltechlibrary/foliage/blob/main/LICENSE", 9 | "version": "1.8.0", 10 | "author": [ 11 | { 12 | "@type": "Person", 13 | "givenName": "Mike", 14 | "familyName": "Hucka", 15 | "affiliation": "California Institute of Technology Library", 16 | "email": "mhucka@library.caltech.edu", 17 | "@id": "https://orcid.org/0000-0001-9105-5960" 18 | }], 19 | "developmentStatus": "active", 20 | "downloadUrl": "https://github.com/caltechlibrary/foliage/archive/main.zip", 21 | "keywords": [ 22 | "software", 23 | "science" 24 | ], 25 | "maintainer": "https://orcid.org/0000-0001-9105-5960", 26 | "funder": { 27 | "@id": "https://doi.org/10.13039/100006961", 28 | "@type": "Organization", 29 | "name": "Caltech Library" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /dev/dev-docs/README.md: -------------------------------------------------------------------------------- 1 | # Foliage developer documentation 2 | 3 | This directory constains my attempt at describing everything about how the Foliage application is built. 4 | 5 | To build Foliage for Windows, I use a Windows 10 virtual machine running in [Parallels](https://www.parallels.com) on a Mac. For the command shell where I execute all commands, I use [Cmder](https://cmder.net) instead of the default Windows `cmd.exe`. The Windows environment has a shared file system with the Mac environment, so that any file changes made in one are visible immediately in the other. (All of the build and installation instructions here assume a shared file system between operating environments.) 6 | 7 | Explanations about the software architecture: 8 | 9 | * [How the Foliage splash screen works](creating-a-splash-screen.md) 10 | * [How the taskbar/system tray widget works](system-widget.md) 11 | 12 | Explanations about building the software: 13 | 14 | * [Using PyInstaller](using-pyinstaller.md) 15 | * [Making a new release of Foliage](making-a-new-release.md) 16 | -------------------------------------------------------------------------------- /dev/dev-docs/installing-pyqt5-on-macos.md: -------------------------------------------------------------------------------- 1 | # Notes about install PyQt5 on macOS systems 2 | 3 | As of 2023-04-27, getting PyQt5 installed in Python seems much harder for Python versions 3.10 and later. I ended up using Python 3.9 for this reason for Foliage development. 4 | 5 | On macOS Ventura, When I tried `pip install pyqt5`, it produced errors that implied it couldn't find `qmake`. 6 | 7 | I got a copy of `qmake` by doing 8 | ``` 9 | brew install qt5 10 | ``` 11 | 12 | This put `qmake` in `/opt/homebrew/opt/qt5/bin`. I put that path on my shell `$PATH`, and verified `qmake` existed, then ran 13 | ``` 14 | pip3 install pyqt5 --config-settings --confirm-license= --verbose 15 | ``` 16 | again, and it worked that time. 17 | -------------------------------------------------------------------------------- /dev/dev-docs/making-a-new-release.md: -------------------------------------------------------------------------------- 1 | # Making a new release of Foliage 2 | 3 | Here is the overall sequence of steps I use to create a new release of Foliage. 4 | 5 | In the current workflow, certain steps must be performed on a non-Windows system. (I only use Windows to generate the Windows binaries, not to develop software or do other work.) The steps here assume that you have a shared file system between your Mac or Linux system, and the Windows system. 6 | 7 | ## 1. First generate a working macOS binary 8 | 9 | This step will update some in the Foliage source directory, and those changes are assumed to be visible in your Windows computing environment via a shared file system. (If you don't have a shared file system established between the computers, you will need to copy files to a Windows system, and that's much more error prone and time-consuming that using a shared file system.) 10 | 11 | 1. Cd to the top level of the Foliage source directory 12 | 2. Open `setup.py` in a text editor, update the version number inside, and commit the change to git 13 | 3. Run `make update-init` 14 | 4. Run `make really-clean` 15 | 5. Run `make binary` 16 | 6. Test the binary that gets built: 17 | 1. Run it from the command line by starting `dist/macos/foliage` 18 | 2. Run it by double-clicking `dist/macos/Foliage.app` in the macOS Finder 19 | 20 | 21 | ## 2. Next, generate a working Windows binary 22 | 23 | Start up a Window environment, then in a terminal emulator: 24 | 25 | 1. Cd to the top level of the Foliage source directory 26 | 2. Run `make` 27 | 3. Test the binary that gets built: 28 | 1. Run it from the command line by starting `dist/win/Foliage.exe` 29 | 2. Run it by double-clicking `dist/win/Foliage.exe` in the Windows File Explorer. 30 | 31 | 32 | ## 3. If all seems to work, only then make a release 33 | 34 | 1. Run `make release` 35 | 2. Run `make print-instructions` 36 | 3. Run `make update-doi` 37 | 4. Run `make packages` 38 | 5. Run `make test-pypi` 39 | 6. Check the test release at 40 | 41 | -------------------------------------------------------------------------------- /dev/dev-docs/windows-installer.md: -------------------------------------------------------------------------------- 1 | # Windows installer for Foliage 2 | 3 | about.html needs to be built on mac first 4 | https://www.npmjs.com/package/inliner 5 | 6 | -------------------------------------------------------------------------------- /dev/icon/README.md: -------------------------------------------------------------------------------- 1 | The [vector artwork](https://thenounproject.com/term/branch/1047074/) used as a starting point for the logo for this repository was created by [Alice Noir](https://thenounproject.com/AliceNoir/) for the [Noun Project](https://thenounproject.com). It is licensed under the Creative Commons [Attribution 3.0 Unported](https://creativecommons.org/licenses/by/3.0/deed.en) license. The vector graphics was modified by Mike Hucka to change the color. 2 | -------------------------------------------------------------------------------- /dev/icon/foliage-icon-white-sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/dev/icon/foliage-icon-white-sm.png -------------------------------------------------------------------------------- /dev/icon/foliage-icon-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/dev/icon/foliage-icon-white.png -------------------------------------------------------------------------------- /dev/icon/foliage-icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/dev/icon/foliage-icon.icns -------------------------------------------------------------------------------- /dev/icon/foliage-icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/dev/icon/foliage-icon.ico -------------------------------------------------------------------------------- /dev/icon/foliage-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/dev/icon/foliage-icon.png -------------------------------------------------------------------------------- /dev/installers/windows/create-innosetup-script.py: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # @file create-innosetup-script.py 3 | # @brief Replace version string in foliage_innosetup_script.iss.in 4 | # @author Michael Hucka 5 | # @license Please see the file named LICENSE in the project directory 6 | # @website https://github.com/caltechlibrary/foliage 7 | # ============================================================================= 8 | 9 | import os 10 | from os.path import abspath, dirname, join 11 | from string import Template 12 | 13 | here = abspath(dirname(__file__)) 14 | with open(join(here, '../../../setup.cfg')) as setup_file: 15 | for line in setup_file.readlines(): 16 | if line.startswith('version'): 17 | version = line.split('=')[1].strip() 18 | break 19 | 20 | with open(join(here, 'foliage_innosetup_script.iss.tmpl')) as template_file: 21 | with open(join(here, 'foliage_innosetup_script.iss'), 'w') as output_file: 22 | output_file.write(template_file.read().replace('@@VERSION@@', version)) 23 | -------------------------------------------------------------------------------- /dev/installers/windows/create-version.py: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # @file create_version.py 3 | # @brief Replace version numbers and create version.py file for Foliage 4 | # @author Michael Hucka 5 | # @license Please see the file named LICENSE in the project directory 6 | # @website https://github.com/caltechlibrary/foliage 7 | # 8 | # This expects to be in the same directory as version.py.tmpl. 9 | # ============================================================================= 10 | 11 | import os 12 | from os.path import abspath, dirname, join 13 | from string import Template 14 | 15 | here = abspath(dirname(__file__)) 16 | with open(join(here, '../../../setup.cfg')) as setup_file: 17 | for line in setup_file.readlines(): 18 | if line.startswith('version'): 19 | version = line.split('=')[1].strip() 20 | break 21 | 22 | major, minor, patch = version.split('.') 23 | 24 | with open(join(here, 'version.py.tmpl'), 'r') as template_file: 25 | with open(join(here, 'version.py'), 'w') as output_file: 26 | tmpl = Template(template_file.read()) 27 | text = tmpl.substitute(major = major, minor = minor, patch = patch) 28 | output_file.write(text) 29 | -------------------------------------------------------------------------------- /dev/installers/windows/create-zip.py: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # @file create-zip.py 3 | # @brief Create zip file 4 | # @author Michael Hucka 5 | # @license Please see the file named LICENSE in the project directory 6 | # @website https://github.com/caltechlibrary/foliage 7 | # 8 | # Needed on Windows because Windows doesn't have a command-line zip utility. 9 | # ============================================================================= 10 | 11 | import os 12 | from os.path import exists, dirname, join, basename, abspath, realpath, isdir 13 | import plac 14 | import sys 15 | from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED 16 | 17 | 18 | # Internal constants. 19 | # ............................................................................. 20 | 21 | _ZIP_COMMENT = '''\ 22 | ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ 23 | ┃ This Zip archive file includes a self-contained, runnable ┃ 24 | ┃ version of the program Foliage for macOS. To learn ┃ 25 | ┃ more about Foliage, please visit the following site: ┃ 26 | ┃ ┃ 27 | ┃ https://github.com/caltechlibrary/foliage ┃ 28 | ┃ ┃ 29 | ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ 30 | ''' 31 | 32 | 33 | # Main function. 34 | # ............................................................................. 35 | 36 | @plac.annotations( 37 | dest_dir = ('the destination directory for the output', 'option', 'd'), 38 | overwrite = ('overwrite the destination if it exists', 'flag', 'o'), 39 | files = 'one or more files to be put into the ZIP archive', 40 | ) 41 | 42 | def main(dest_dir = 'D', overwrite = False, *files): 43 | '''Put one or more files into a ZIP archive.''' 44 | 45 | here = abspath(dirname(__file__)) 46 | with open(join(here, '../../../setup.cfg')) as setup_file: 47 | for line in setup_file.readlines(): 48 | if line.startswith('version'): 49 | version = line.split('=')[1].strip() 50 | break 51 | 52 | dest_dir = '.' if dest_dir == 'D' else dest_dir 53 | zip_file = join(dest_dir, f'foliage-{version}-win.zip') 54 | if exists(zip_file) and not overwrite: 55 | raise RuntimeError(f'Output destination already exists: {zip_file}') 56 | 57 | inner_folder = f'foliage-{version}-win' 58 | with ZipFile(zip_file, 'w', ZIP_STORED) as zf: 59 | for file in files: 60 | zf.write(file, arcname = join(inner_folder, basename(file))) 61 | zf.comment = _ZIP_COMMENT.encode() 62 | 63 | 64 | # Main entry point. 65 | # ............................................................................. 66 | 67 | # The following allows users to invoke this using "python3 -m create-zip". 68 | if __name__ == '__main__': 69 | if len(sys.argv) == 1 or (len(sys.argv) > 1 and sys.argv[1] == 'help'): 70 | plac.call(main, ['-h']) 71 | else: 72 | plac.call(main) 73 | -------------------------------------------------------------------------------- /dev/installers/windows/version.py.tmpl: -------------------------------------------------------------------------------- 1 | # ======================================================== -*- mode: python -*- 2 | # @file version.py.tmpl 3 | # @brief Version file template for Windows application 4 | # @author Michael Hucka 5 | # @license Please see the file named LICENSE in the project directory 6 | # @website https://github.com/caltechlibrary/foliage 7 | # 8 | # See https://stackoverflow.com/a/14626175/743730 for more info about this 9 | # file format. 10 | # ============================================================================= 11 | 12 | VSVersionInfo( 13 | ffi=FixedFileInfo( 14 | filevers=($major, $minor, $patch, 0), 15 | prodvers=($major, $minor, $patch, 0), 16 | mask=0x3f, 17 | flags=0x0, 18 | OS=0x40004, 19 | fileType=0x1, 20 | subtype=0x0, 21 | date=(0, 0) 22 | ), 23 | kids=[ 24 | StringFileInfo( 25 | [ 26 | StringTable( 27 | u'040904B0', 28 | [StringStruct(u'CompanyName', u'California Institute of Technology Library'), 29 | StringStruct(u'FileDescription', u'Foliage'), 30 | StringStruct(u'FileVersion', u'$major.$minor.$patch'), 31 | StringStruct(u'ProductVersion', u'$major.$minor.$patch'), 32 | StringStruct(u'InternalName', u'Foliage'), 33 | StringStruct(u'LegalCopyright', u'\xa9 Caltech. All rights reserved.'), 34 | StringStruct(u'OriginalFilename', u'Foliage.Exe'), 35 | StringStruct(u'ProductName', u'Foliage')]) 36 | ]), 37 | VarFileInfo([VarStruct(u'Translation', [1033, 1200])]) 38 | ] 39 | ) 40 | -------------------------------------------------------------------------------- /dev/misc/experiments/macos-widget.py: -------------------------------------------------------------------------------- 1 | from os.path import dirname, join 2 | from PyQt5.QtGui import * 3 | from PyQt5.QtWidgets import * 4 | 5 | # Code originally based on example found on 2021-12-10 at 6 | # https://www.pythonguis.com/tutorials/system-tray-mac-menu-bar-applications-pyqt/ 7 | 8 | app = QApplication([]) 9 | app.setQuitOnLastWindowClosed(False) 10 | 11 | # Create the icon 12 | icon = QIcon(join(dirname(__file__), 'foliage-icon.png')) 13 | 14 | # Create the tray 15 | tray = QSystemTrayIcon() 16 | tray.setIcon(icon) 17 | tray.setVisible(True) 18 | 19 | # Create the menu 20 | menu = QMenu() 21 | 22 | # Add a Quit option to the menu. 23 | quit = QAction("Quit") 24 | quit.triggered.connect(app.quit) 25 | menu.addAction(quit) 26 | 27 | # Add the menu to the tray 28 | tray.setContextMenu(menu) 29 | 30 | app.exec_() 31 | -------------------------------------------------------------------------------- /dev/misc/experiments/settings.ini: -------------------------------------------------------------------------------- 1 | # This settings file is read by the code in isbn_checker.py 2 | 3 | [settings] 4 | FOLIO_OKAPI_URL = 5 | FOLIO_OKAPI_TENANT_ID = 6 | FOLIO_OKAPI_TOKEN = 7 | -------------------------------------------------------------------------------- /dev/misc/folio-data-samples/README.md: -------------------------------------------------------------------------------- 1 | # Folio data samples 2 | 3 | This directory contains samples of records from Caltech's FOLIO server obtained in late 2021. 4 | -------------------------------------------------------------------------------- /dev/misc/folio-data-samples/sample-2-holdings-3-items/holdings-storage-holdings-795b4843-2736-4ccc-ade6-c928f8628831.json: -------------------------------------------------------------------------------- 1 | { '_version': 1, 2 | 'bareHoldingsItems': [], 3 | 'callNumber': 'QC806 .F625 2005', 4 | 'callNumberPrefix': '', 5 | 'callNumberSuffix': '', 6 | 'callNumberTypeId': '95467209-6d7b-468b-94df-0f5d7ad2747d', 7 | 'discoverySuppress': False, 8 | 'electronicAccess': [], 9 | 'formerIds': [], 10 | 'holdingsItems': [], 11 | 'holdingsStatements': [], 12 | 'holdingsStatementsForIndexes': [], 13 | 'holdingsStatementsForSupplements': [], 14 | 'holdingsTypeId': '03c9c400-b9e3-4a07-ac0e-05ab470233ed', 15 | 'hrid': 'ho00000517710', 16 | 'id': '795b4843-2736-4ccc-ade6-c928f8628831', 17 | 'instanceId': '848d5d4b-6e49-49d9-b02b-c9eb19d8b07d', 18 | 'metadata': { 'createdByUserId': '25148b30-565b-4012-8300-451fb5cbe124', 19 | 'createdDate': '2021-09-15T20:22:30.786+00:00', 20 | 'updatedByUserId': '25148b30-565b-4012-8300-451fb5cbe124', 21 | 'updatedDate': '2021-09-15T20:22:30.786+00:00'}, 22 | 'notes': [], 23 | 'permanentLocationId': 'e418607a-70b6-482c-9a94-b2e56ec12110', 24 | 'shelvingTitle': '', 25 | 'statisticalCodeIds': []} 26 | -------------------------------------------------------------------------------- /dev/misc/folio-data-samples/sample-2-holdings-3-items/holdings-storage-holdings-c0b6d0cf-38d0-43f7-b476-7debbba52bc5.json: -------------------------------------------------------------------------------- 1 | { '_version': 1, 2 | 'bareHoldingsItems': [], 3 | 'callNumber': 'QC806 .F625 2005', 4 | 'callNumberPrefix': '', 5 | 'callNumberSuffix': '', 6 | 'callNumberTypeId': '95467209-6d7b-468b-94df-0f5d7ad2747d', 7 | 'discoverySuppress': False, 8 | 'electronicAccess': [], 9 | 'formerIds': [], 10 | 'holdingsItems': [], 11 | 'holdingsStatements': [], 12 | 'holdingsStatementsForIndexes': [], 13 | 'holdingsStatementsForSupplements': [], 14 | 'holdingsTypeId': '03c9c400-b9e3-4a07-ac0e-05ab470233ed', 15 | 'hrid': 'ho00000438575', 16 | 'id': 'c0b6d0cf-38d0-43f7-b476-7debbba52bc5', 17 | 'instanceId': '848d5d4b-6e49-49d9-b02b-c9eb19d8b07d', 18 | 'metadata': { 'createdByUserId': '25148b30-565b-4012-8300-451fb5cbe124', 19 | 'createdDate': '2021-09-15T20:20:33.431+00:00', 20 | 'updatedByUserId': '25148b30-565b-4012-8300-451fb5cbe124', 21 | 'updatedDate': '2021-09-15T20:20:33.431+00:00'}, 22 | 'notes': [], 23 | 'permanentLocationId': '817af180-8807-413a-861e-d483b2b97025', 24 | 'shelvingTitle': '', 25 | 'statisticalCodeIds': []} 26 | -------------------------------------------------------------------------------- /dev/misc/t.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | import sys 5 | import csv 6 | from commonpy.network_utils import net 7 | from decouple import config 8 | 9 | if len(sys.argv[1:]) < 1: 10 | sys.exit('Missing argument: file containing list of users') 11 | 12 | user_ids = [] 13 | try: 14 | with open(sys.argv[1], 'r') as csv_file: 15 | user_ids = [row['id'] for row in csv.DictReader(csv_file)] 16 | except Exception as ex: 17 | sys.exit(ex) 18 | 19 | print(f'Read {len(user_ids)} users from file {sys.argv[1]}') 20 | 21 | 22 | base_url = config('FOLIO_OKAPI_URL', default = None) 23 | 24 | headers = { 25 | "x-okapi-token": config('FOLIO_OKAPI_TOKEN', default = None), 26 | "x-okapi-tenant": config('FOLIO_OKAPI_TENANT_ID', default = None), 27 | "content-type": "application/json", 28 | } 29 | 30 | loans = dict() 31 | items = dict() 32 | users = dict() 33 | 34 | for index, user_id in enumerate(user_ids): 35 | url = f'{base_url}/users?query=id={user_id}' 36 | (result, error) = net('get', url, headers = headers) 37 | if error: 38 | sys.exit('Got error: ' + str(error)) 39 | users[user_id] = json.loads(result.text)['users'][0] 40 | 41 | url = f'{base_url}/loan-storage/loans?query=userId={user_id}' 42 | (result, error) = net('get', url, headers = headers) 43 | if error: 44 | sys.exit('Got error: ' + str(error)) 45 | loans[user_id] = json.loads(result.text)['loans'] 46 | 47 | for loan in loans[user_id]: 48 | item_id = loan['itemId'] 49 | url = f'{base_url}/inventory/items?query=id={item_id}' 50 | (result, error) = net('get', url, headers = headers) 51 | if error: 52 | sys.exit('Got error: ' + str(error)) 53 | items[item_id] = json.loads(result.text)['items'][0] 54 | print(str(index).zfill(3), flush = True) 55 | if index > 1: 56 | break 57 | 58 | 59 | for user_id in users: 60 | r = users[user_id] 61 | cituid = r['barcode'].lstrip('0') 62 | print('\nuser ' + cituid + ' (' + r['personal']['lastName'] + '):') 63 | if user_id in loans: 64 | for loan in loans[user_id]: 65 | item = items[loan['itemId']] 66 | title = item['title'] 67 | if len(title) > 60: 68 | title = title[0:60] + ' ...' 69 | print(' ' + item['barcode'] + ' ' + title) 70 | -------------------------------------------------------------------------------- /dev/one-page-docs/pandoc-template/README.md: -------------------------------------------------------------------------------- 1 | # Pandoc Markdown CSS theme 2 | 3 | The original pandoc HTML5 template file in this directory came from [pandoc-markdown-css-theme](https://github.com/jez/pandoc-markdown-css-theme) by Jake Zimmerman, downloaded on 2022-01-28 (at the time, latest commit 019a482 from Jun 29, 2021). I modified it from the original. 4 | 5 | -------------------------------------------------------------------------------- /dev/one-page-docs/pandoc-template/template.html5: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $for(author-meta)$ 8 | 9 | $endfor$ 10 | $if(date-meta)$ 11 | 12 | $endif$ 13 | $if(keywords)$ 14 | 15 | $endif$ 16 | $if(description-meta)$ 17 | 18 | $endif$ 19 | $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ 20 | $for(css)$ 21 | 22 | $endfor$ 23 | $if(math)$ 24 | $math$ 25 | $endif$ 26 | $for(header-includes)$ 27 | $header-includes$ 28 | $endfor$ 29 | 30 | 31 | $for(include-before)$ 32 | $include-before$ 33 | $endfor$ 34 | 35 | $if(toc)$ 36 | 44 | $endif$ 45 | 46 |
47 | $body$ 48 |
49 | 50 | $if(return-url)$ 51 | 56 | $endif$ 57 | 69 | $for(include-after)$ 70 | $include-after$ 71 | $endfor$ 72 | 73 | 74 | -------------------------------------------------------------------------------- /dev/one-page-docs/read-me-first-macos/control-click.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/dev/one-page-docs/read-me-first-macos/control-click.png -------------------------------------------------------------------------------- /dev/one-page-docs/read-me-first-macos/file-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/dev/one-page-docs/read-me-first-macos/file-dialog.png -------------------------------------------------------------------------------- /dev/one-page-docs/read-me-first-macos/macos-malicious-warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/dev/one-page-docs/read-me-first-macos/macos-malicious-warning.png -------------------------------------------------------------------------------- /dev/one-page-docs/read-me-first-macos/read-me-first.md: -------------------------------------------------------------------------------- 1 | # Important macOS info about Foliage 2 | 3 | There are two issues on macOS that may cause confusion the first time you try to use Foliage: a macOS security error, and slow startup times. 4 | 5 | ## macOS security error 6 | 7 | After you download the Foliage disk image and open it, if you first try to run the application by double-clicking the icon in the Finder, you will get a macOS error dialog: 8 | 9 |

10 | 11 |

12 | 13 | To get around the warning, instead of double-clicking the Foliage app, **control-click on the Foliage icon in the Finder to get the following pop-up menu**: 14 | 15 |

16 | 17 |

18 | 19 | Next, select "Open" from the menu. MacOS will show a similar warning as before, but this time, the warning dialog will have an additional button named "Open": 20 | 21 |

22 | 23 |

24 | 25 | **Click the "Open" button**, and now it will start Foliage. 26 | 27 | After you do these steps, macOS will **not** ask you again, and you will be able to double-click the icon to start it as usual. 28 | 29 | 30 | ## Slow startup time 31 | 32 | You may experience very long startup times on macOS. This is currently a known issue. During the startup, there will unfortunately be no feedback – it will look like nothing is happening. Please be patient and give it a significant time, as much as half a minute depending on your hardware. It should eventually start up. 33 | 34 | -------------------------------------------------------------------------------- /dev/one-page-docs/read-me-first-windows/browser-download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/dev/one-page-docs/read-me-first-windows/browser-download.png -------------------------------------------------------------------------------- /dev/one-page-docs/read-me-first-windows/download-anyway.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/dev/one-page-docs/read-me-first-windows/download-anyway.png -------------------------------------------------------------------------------- /dev/one-page-docs/read-me-first-windows/google-keep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/dev/one-page-docs/read-me-first-windows/google-keep.png -------------------------------------------------------------------------------- /dev/one-page-docs/read-me-first-windows/google-warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/dev/one-page-docs/read-me-first-windows/google-warning.png -------------------------------------------------------------------------------- /dev/one-page-docs/read-me-first-windows/hover-over-downloads.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/dev/one-page-docs/read-me-first-windows/hover-over-downloads.png -------------------------------------------------------------------------------- /dev/one-page-docs/read-me-first-windows/keep-anyway.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/dev/one-page-docs/read-me-first-windows/keep-anyway.png -------------------------------------------------------------------------------- /dev/one-page-docs/read-me-first-windows/read-me-first.md: -------------------------------------------------------------------------------- 1 | # Windows installation info for Foliage 2 | 3 | When you download the installer for Foliage from the Library Google Drive folder, your browser will produce security warnings because it doesn't recognize the origin and nature of the program. Foliage is safe to download and install if you get it from the Library's Google Drive. Here are the steps to get past the various Windows security warnings. 4 | 5 | First, open on the Google Drive folder in your browser and right-click over the installer program's 6 | name. You will get a pop-up menu in which one of the choices is "Download". Select "Download". 7 | 8 |

9 | 10 |

11 | 12 | After a short pause while Google automatically checks the file for viruses, you will get a dialog saying "This file type might be dangerous" and a button for "Download anyway". **Click "Download anyway"**. 13 | 14 |

15 | 16 |

17 | 18 | What you need to do next will depend on the browser you are using. 19 | 20 | 21 | ## Google Chrome 22 | 23 | Google Chrome will interrupt the download and show a warning in the bottom left of the browser window: 24 | 25 |

26 | 27 |

28 | 29 | Do **not** click the "Discard" button. Instead, click the **⋁** symbol next to the bottom, and it will show a pop-up menu in which there is a "Keep" option. **Click "Keep"**. 30 | 31 |

32 | 33 |

34 | 35 | Google Chrome will save the installer and you can proceed normally. (Find the installer where it was downloaded on your computer and start the installer program.) 36 | 37 | 38 | ## Microsoft Edge 39 | 40 | Microsoft Edge will block the download and you may see a pop-up such as the following: 41 | 42 |

43 | 44 |

45 | 46 | You must move your cursor to **hover over the Foliage item in the list**, at which point, the browser will show a trash can icon and three dots (•••). **Click on the three dots** to show the action menu, and **select "Keep"** from that menu: 47 | 48 |

49 | 50 |

51 | 52 | You will get _yet another menu_ from which it is not obvious what to do: 53 | 54 |

55 | 56 |

57 | 58 | Do **not** click "Delete". Instead, **click "Show more "** to get a list of options, from which you can **select "Keep anyway"**: 59 | 60 |

61 | 62 |

63 | 64 | It is only at this point, _finally_, that the browser will actually download the Foliage installer program to your computer. 65 | -------------------------------------------------------------------------------- /dev/one-page-docs/read-me-first-windows/see-more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/dev/one-page-docs/read-me-first-windows/see-more.png -------------------------------------------------------------------------------- /dev/one-page-docs/read-me-first-windows/trust-foliage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/dev/one-page-docs/read-me-first-windows/trust-foliage.png -------------------------------------------------------------------------------- /dev/one-page-docs/sakura-css/README.md: -------------------------------------------------------------------------------- 1 | # Sakura CSS theme 2 | 3 | The CSS file in this directory originally came from (sakura)[https://github.com/oxalorg/sakura] by Mitesh Shah, downloaded on 2022-01-28 (at the time, sakura version 1.3.0). 4 | -------------------------------------------------------------------------------- /dev/splash-screen/README.md: -------------------------------------------------------------------------------- 1 | # Splash screen creator for Foliage 2 | 3 | A limitation of PyInstaller's splash screen feature is that it can only show an image. For Foliage, I wanted to show a version number in the splash screen so that the user knows which version they're getting, but doing so meant finding a way to generate a new splash screen image every time a new version of Foliage was produced. 4 | 5 |

6 | Example of the Foliage splash screen 7 |

8 | 9 | I automated this process using code in this subdirectory. The artwork is in SVG format. Since an SVG file is written in plain text, it's possible to put placeholders in the SVG file itself and then substitute the values when needed. The scheme for Foliage works like this: 10 | 11 | 1. The file `foliage-splash-screen.svg.tmpl` contains placeholders. 12 | 2. The small program [`create-splash-screen.py`](create-splash-screen.py) is run by the top level `make.bat` file before PyInstaller is run, and performs two steps: 13 | 1. substitute values for the placeholders and produce a new file, `foliage-splash-screen.svg` 14 | 2. convert `foliage-splash-screen.svg` to a PNG file (`foliage-splash-screen.png`) written in the same directory 15 | 3. The PyInstaller [specification file](../../pyinstaller-win32.spec) points to the `.png` file in the splash screen configuration, so that PyInstaller reads an image with an up-to-date version number. 16 | 17 | The splash screen image for a given Foliage release is static. The small program is run at application build time, not at run-time. The image is embedded in the self-contained Foliage application created by PyInstaller. 18 | -------------------------------------------------------------------------------- /dev/splash-screen/create-splash-screen.py: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # @file create-splash-screen.py 3 | # @brief Replace version number in splash screen SVG and generate a PNG file 4 | # @author Michael Hucka 5 | # @license Please see the file named LICENSE in the project directory 6 | # @website https://github.com/caltechlibrary/foliage 7 | # 8 | # This expects to be in the same directory as foliage-splash-screen.svg.tmpl. 9 | # ============================================================================= 10 | 11 | import os 12 | from os.path import abspath, dirname, join 13 | from string import Template 14 | from wand.image import Image 15 | 16 | here = abspath(dirname(__file__)) 17 | with open(join(here, '../../setup.cfg')) as setup_file: 18 | for line in setup_file.readlines(): 19 | if line.startswith('version'): 20 | version = line.split('=')[1].strip() 21 | break 22 | 23 | major, minor, patch = version.split('.') 24 | 25 | with open(join(here, 'foliage-splash-screen.svg.tmpl'), 'r') as template_file: 26 | tmpl = Template(template_file.read()) 27 | svg = tmpl.substitute(major = major, minor = minor, patch = patch) 28 | with Image(blob = svg.encode(), format = "svg") as image: 29 | image.format = 'png' 30 | image.save(filename = join(here, 'foliage-splash-screen.png')) 31 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/.nojekyll -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # @file Makefile 3 | # @brief Makefile for building docs using Sphinx and MyST 4 | # @created 2021-01-25 5 | # @license Please see the file named LICENSE in the project directory 6 | # @website https://github.com/caltechlibrary/foliage 7 | # ============================================================================= 8 | 9 | # Before we go any further, test if certain programs are available. 10 | # The following is based on the approach posted by Jonathan Ben-Avraham to 11 | # Stack Overflow in 2014 at https://stackoverflow.com/a/25668869 12 | 13 | PROGRAMS_NEEDED = sphinx-build 14 | TEST := $(foreach p,$(PROGRAMS_NEEDED),\ 15 | $(if $(shell which $(p)),_,$(error Cannot find program "$(p)"))) 16 | 17 | 18 | # Gather values that we need ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | 20 | # You can set the following variables from the command line, and also from 21 | # the environment for the first two. 22 | 23 | SPHINXOPTS ?= 24 | SPHINXBUILD ?= sphinx-build 25 | SPHINXAUTO = sphinx-autobuild 26 | SOURCEDIR = . 27 | BUILDDIR = _build 28 | 29 | # Actions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 30 | 31 | # Put it first so that "make" without argument is like "make help". 32 | help: 33 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 34 | 35 | auto autobuild live livehtml: 36 | @$(SPHINXAUTO) "$(SOURCEDIR)" "$(BUILDDIR)"/html $(SPHINXOPTS) $(O) 37 | 38 | # Catch-all target: route all unknown targets to Sphinx using the new 39 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 40 | %: Makefile 41 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 42 | 43 | 44 | # Cleanup and miscellaneous directives ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 45 | 46 | .PHONY: help auto autobuild livehtml Makefile 47 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Foliage user documentation 2 | 3 | This directory contains the **user** documentation for Foliage. The **developer** documentation is in a separate directory, [`../dev/dev-docs`](../dev/dev-docs). The rest of this page describes how to (re)create the formatted Foliage user documentation. 4 | 5 | ## Building the docs locally 6 | 7 | First, install [MyST](https://myst-parser.readthedocs.io/en/latest/index.html) and [Sphinx](https://www.sphinx-doc.org): 8 | 9 | ```sh 10 | python3 -m pip install "myst-parser[linkify]" 11 | python3 -m pip install sphinx-material 12 | python3 -m pip install sphinx-autobuild 13 | ``` 14 | 15 | After that, rebuilding the docs should be simply a matter of running `make html` in the current directory: 16 | 17 | ```sh 18 | make html 19 | ``` 20 | 21 | The output will be put in [`_build/html`](_build/html). Instead of running `make` deliberately, you can also get auto-rebuilds and live preview using `sphinx-autobuild` by running the following command in the current directory (preferably in a new terminal window, because it will generate continuous output): 22 | 23 | ```sh 24 | make auto 25 | ``` 26 | 27 | 28 | ## Writing documentation 29 | 30 | This documentation is written in [MyST-flavored Markdown](https://myst-parser.readthedocs.io/en/latest/) and the [Napoleon](https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html) extension to Sphinx. What this means is that the documentation is written in Markdown (not reStructuredText), with essentially all the features of Sphinx and reStructuredText having MyST equivalents and some additional features – things like [pandoc](https://pandoc.org)-style footnotes, LaTeX math, and more. 31 | -------------------------------------------------------------------------------- /docs/_static/media/README.md: -------------------------------------------------------------------------------- 1 | # Foliage diagrams 2 | 3 | The diagrams used in the Foliage documentation were created by Michael Hucka in 2022, using OmniGraffle Pro 7.18.5 on a macOS 10.4.6 computer. The SVG versions were produced using the following steps: 4 | 5 | 6 | 7 | 1. Export from OmniGraffle to SVG format, using the options "transparent background" and `0`inch margin. (See screen image at the right.) 8 | 2. Run [svg-buddy](https://github.com/phauer/svg-buddy) on the SVG file produced from step #1. This will embed the fonts used in the SVG file, overcoming a limitation of OmniGraffle's SVG output. The following is an example of the command used: 9 | ```sh 10 | java -jar svg-buddy.jar foliage-change-case-1.svg 11 | ``` 12 | -------------------------------------------------------------------------------- /docs/_static/media/authentication.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/authentication.png -------------------------------------------------------------------------------- /docs/_static/media/caltech-round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/caltech-round.png -------------------------------------------------------------------------------- /docs/_static/media/change-tab-select-field.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/change-tab-select-field.png -------------------------------------------------------------------------------- /docs/_static/media/change-tab-selected-field.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/change-tab-selected-field.png -------------------------------------------------------------------------------- /docs/_static/media/change-tab-value-selector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/change-tab-value-selector.png -------------------------------------------------------------------------------- /docs/_static/media/change-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/change-tab.png -------------------------------------------------------------------------------- /docs/_static/media/clean-records-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/clean-records-tab.png -------------------------------------------------------------------------------- /docs/_static/media/delete-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/delete-tab.png -------------------------------------------------------------------------------- /docs/_static/media/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/favicon.ico -------------------------------------------------------------------------------- /docs/_static/media/foliage-change-case-1.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/foliage-change-case-1.graffle -------------------------------------------------------------------------------- /docs/_static/media/foliage-change-case-2.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/foliage-change-case-2.graffle -------------------------------------------------------------------------------- /docs/_static/media/foliage-change-case-3.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/foliage-change-case-3.graffle -------------------------------------------------------------------------------- /docs/_static/media/foliage-change-case-4.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/foliage-change-case-4.graffle -------------------------------------------------------------------------------- /docs/_static/media/foliage-icon-white-sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/foliage-icon-white-sm.png -------------------------------------------------------------------------------- /docs/_static/media/foliage-icon-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/foliage-icon-white.png -------------------------------------------------------------------------------- /docs/_static/media/foliage-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/foliage-icon.png -------------------------------------------------------------------------------- /docs/_static/media/foliage-macos-systray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/foliage-macos-systray.png -------------------------------------------------------------------------------- /docs/_static/media/foliage-windows-taskbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/foliage-windows-taskbar.png -------------------------------------------------------------------------------- /docs/_static/media/list-uuids-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/list-uuids-example.png -------------------------------------------------------------------------------- /docs/_static/media/list-uuids-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/list-uuids-tab.png -------------------------------------------------------------------------------- /docs/_static/media/lookup-barcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/lookup-barcode.png -------------------------------------------------------------------------------- /docs/_static/media/macos-accept-connections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/macos-accept-connections.png -------------------------------------------------------------------------------- /docs/_static/media/macos-keychain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/macos-keychain.png -------------------------------------------------------------------------------- /docs/_static/media/main-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/main-page.png -------------------------------------------------------------------------------- /docs/_static/media/omnigraffle-export-options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/omnigraffle-export-options.png -------------------------------------------------------------------------------- /docs/_static/media/other-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/other-tab.png -------------------------------------------------------------------------------- /docs/_static/media/windows-accept-connections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/docs/_static/media/windows-accept-connections.png -------------------------------------------------------------------------------- /docs/_static/versions.json: -------------------------------------------------------------------------------- 1 | {"release": "", "development": "devel"} 2 | -------------------------------------------------------------------------------- /docs/colophon.md: -------------------------------------------------------------------------------- 1 | # Colophon 2 | 3 | The Foliage documentation has been written by [Michael Hucka](https://www.cds.caltech.edu/~mhucka/) using the [Sphinx](https://www.sphinx-doc.org) document generator together with [MyST-flavored Markdown](https://myst-parser.readthedocs.io/en/latest/). The theme is the [Material theme for Sphinx](https://bashtage.github.io/sphinx-material/), with light customizations such as the use of Google's [Atkinson Hyperlegible](https://fonts.google.com/specimen/Atkinson+Hyperlegible) font. A [GitHub Action](https://github.com/caltechlibrary/foliage/blob/main/.github/workflows/build-sphinx.yml) takes care of creating the formatted version of the documentation and hosting it on GitHub.io at https://caltechlibrary.github.io/foliage. The formatted output can also be produced manually using commands implemented in the `Makefile` located in the [`docs/`](https://github.com/caltechlibrary/foliage/tree/main/docs) subdirectory of the Foliage source code repository. 4 | 5 | The [vector artwork](https://thenounproject.com/term/branch/1047074/) used as a starting point for the logo and icon for Foliage was created by [Alice Noir](https://thenounproject.com/AliceNoir/) for the [Noun Project](https://thenounproject.com). The artwork is licensed under the Creative Commons [Attribution 3.0 Unported](https://creativecommons.org/licenses/by/3.0/deed.en) license. The vector graphics was modified by Michael Hucka to change the color. 6 | 7 | Unless indicated otherwise, all other artwork in this documentation was created using [OmniGraffle Pro](https://www.omnigroup.com/omnigraffle) on a macOS computer. SVG versions of the diagrams were produced with the help of [svg-buddy](https://github.com/phauer/svg-buddy) to embed fonts into the SVG files and overcome a limitation of OmniGraffle's SVG output. 8 | -------------------------------------------------------------------------------- /docs/glossary.md: -------------------------------------------------------------------------------- 1 | # Glossary 2 | 3 | A glossary of common terms used in Foliage and FOLIO. 4 | 5 | ```{glossary} 6 | API 7 | "Application programm interface." An API is a well-defined interface 8 | through which interactions between software can take place. It is an 9 | approach for software systems to communicate with each other. In the 10 | context of FOLIO and Foliage, the APIs used are network-based: 11 | Foliage makes calls to network APIs provided by FOLIO. 12 | 13 | CQL 14 | "[Contextual Query Language](http://zing.z3950.org/cql/intro.html)." 15 | A syntax for writing information queries. Here is an example of what 16 | a CQL expression looks like: 17 | `(username=="ab*" or personal.firstName=="ab*" or personal.lastName=="ab*")` 18 | 19 | CSV 20 | "Comma-separated values." A spreadsheet format stored as text, in which 21 | values of different columns are separated by commas. 22 | 23 | Foliage 24 | **FOLI**O ch**a**n**g**e **e**ditor. 25 | 26 | FOLIO 27 | "The Future Of Libraries Is Open." FOLIO is an open-source library services 28 | platform that integrates print and electronic resource management. 29 | 30 | HRID 31 | "Human readable identifier" (_not_ a holdings identifier!). Many 32 | of the record types in FOLIO have an `hrid` field. 33 | 34 | 35 | JSON 36 | "JavaScript Object Notation." An open-standard format for storing 37 | data. The format uses a textual notation that is more or less 38 | human readable. It can be used to store lists, texts, numbers, 39 | attribute-value pairs, and more. 40 | 41 | LSP 42 | "Library services platform." The kind of system that FOLIO is. 43 | 44 | Microservices 45 | A type of software system architecture in which a single server provides 46 | a service to multiple tenants. 47 | 48 | OKAPI 49 | "Okay API." The FOLIO API system. (Technically, the FOLIO middleware and 50 | API gateway.) 51 | 52 | SRS 53 | FOLIO's "Source Record Storage." The server-side storage system 54 | underlying FOLIO's database facilities. 55 | 56 | UUID 57 | "Universally unique identifier." A label used for information in computer 58 | systems. In FOLIO, UUIDs are the values of the `id` fields in records. An 59 | example UUID is `e9559bdb-868e-4f8b-bebc-6b482b0d91ea`. 60 | 61 | ``` 62 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Foliage 2 | 3 | Foliage (_**Foli**o ch**a**n**g**e **E**ditor_) is a desktop computer application that can perform operations in [FOLIO](https://www.folio.org), a library services platform ([LSP](https://journals.ala.org/index.php/ltr/article/view/5686/7063)) used by Caltech and other institutions. Foliage allows a user to look up records of various kinds, perform bulk changes in the values of record fields, delete records, and more. It communicates with a FOLIO server using the [OKAPI network API](https://github.com/folio-org/okapi/blob/master/doc/guide.md). The program is cross-platform compatible and currently in use on Windows and macOS computers at the Caltech Library. 4 | 5 | ## Sections 6 | 7 | ```{toctree} 8 | --- 9 | maxdepth: 2 10 | --- 11 | installation.md 12 | first-time-usage.md 13 | foliage-interface.md 14 | nitty.md 15 | glossary.md 16 | colophon.md 17 | ``` 18 | -------------------------------------------------------------------------------- /entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | com.apple.security.cs.allow-jit 10 | 11 | com.apple.security.cs.allow-unsigned-executable-memory 12 | 13 | com.apple.security.cs.disable-library-validation 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /foliage/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | __init__.py for foliage 3 | 4 | Copyright 5 | --------- 6 | 7 | Copyright (c) 2021-2022 by the California Institute of Technology. This code 8 | is open-source software released under a 3-clause BSD license. Please see the 9 | file "LICENSE" for more information. 10 | ''' 11 | 12 | # Package metadata 13 | # ............................................................................. 14 | # 15 | # ╭────────────────────── Notice ── Notice ── Notice ─────────────────────╮ 16 | # | The following values are automatically updated at every release | 17 | # | by the Makefile. Manual changes to these values will be lost. | 18 | # ╰────────────────────── Notice ── Notice ── Notice ─────────────────────╯ 19 | 20 | __version__ = '1.8.0' 21 | __description__ = 'Foliage: a tool to do bulk changes in FOLIO using the OKAPI API' 22 | __url__ = 'https://github.com/caltechlibrary/foliage' 23 | __author__ = 'Mike Hucka' 24 | __email__ = 'helpdesk@library.caltech.edu' 25 | __license__ = 'BSD 3-clause license' 26 | 27 | 28 | # Miscellaneous utilities. 29 | # ............................................................................. 30 | 31 | def print_version(): 32 | print(f'{__name__} version {__version__}') 33 | print(f'Authors: {__author__}') 34 | print(f'URL: {__url__}') 35 | print(f'License: {__license__}') 36 | -------------------------------------------------------------------------------- /foliage/base_tab.py: -------------------------------------------------------------------------------- 1 | ''' 2 | base_tab.py: base class for tabs 3 | 4 | The tabs in Foliage are conceptually pretty simple: there's a function to 5 | create the tab contents, and another function to set up optional watchers 6 | for detecting and acting on designated PyWebIO "pin" objects. The class 7 | FoliageTab is a base class used by all the Foliage tab classes. This common 8 | base class makes it possible to implement tab creation and pin watching in 9 | __main__.py's foliage_page() as a loop over a list of objects, rather than 10 | by hardcoding calls to every tab directly. 11 | 12 | Copyright 13 | --------- 14 | 15 | Copyright (c) 2021-2022 by the California Institute of Technology. This code 16 | is open-source software released under a 3-clause BSD license. Please see the 17 | file "LICENSE" for more information. 18 | ''' 19 | 20 | 21 | class FoliageTab(): 22 | def contents(self): 23 | '''Return a dict of elements {'title': '...', 'content': [objects]}.''' 24 | raise NotImplementedError() 25 | 26 | def pin_watchers(self): 27 | '''Return a dict of elements {'pin_name': callback_function}.''' 28 | raise NotImplementedError() 29 | -------------------------------------------------------------------------------- /foliage/data/foliage-icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/foliage/data/foliage-icon-128x128.png -------------------------------------------------------------------------------- /foliage/data/foliage-icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/foliage/data/foliage-icon-256x256.png -------------------------------------------------------------------------------- /foliage/data/foliage-icon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/foliage/data/foliage-icon-32x32.png -------------------------------------------------------------------------------- /foliage/data/foliage-icon-64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/foliage/data/foliage-icon-64x64.png -------------------------------------------------------------------------------- /foliage/data/foliage-icon-r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/foliage/data/foliage-icon-r.png -------------------------------------------------------------------------------- /foliage/data/foliage-icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/foliage/data/foliage-icon.ico -------------------------------------------------------------------------------- /foliage/data/foliage-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/foliage/data/foliage-icon.png -------------------------------------------------------------------------------- /foliage/data/macos-systray-widget/README.md: -------------------------------------------------------------------------------- 1 | # Systray widget for macOS 2 | 3 | This directory contains a widget for putting an icon in the macOS system tray (the row of icons in the upper right of the screen on macOS, in the system menubar). The widget has only one function: offer a _Quit_ menu option. It is started when Foliage first starts up, and while Foliage runs, the widget will stay in the system tray. Its purpose is to make it possible for the user to quit Foliage if they have lost track of Foliage's web page. 4 | 5 | The widget is written in [go](https://go.dev) rather than Python so that it is possible to create a self-contained executable that can be bundled inside the Foliage application created using PyInstaller. It is not possible to use a Python script for this purpose because PyInstaller [does not bundle a Python interpreter](https://github.com/pyinstaller/pyinstaller/wiki/FAQ) in the application it creates, and we can't assume that the user's environment has a Python interpreter (or where it might be installed). Give this constraint, I searched for a solution that would allow a self-contained binary to be bundled inside the application we create using PyInstaller, so that Foliage could run this application as a subprocess. Go is well suited to this purpose because the binaries it creates are statically-linked. 6 | 7 | This widget code is based on the example widget with [systray](https://github.com/getlantern/systray), a cross-platform Go library to create system tray widgets. I simply copied the example's [main.go](https://github.com/getlantern/systray/blob/master/example/main.go) file and the icon code, and adapted them to create what's in this directory. 8 | 9 | The [systray](https://github.com/getlantern/systray) code by [Lantern](https://github.com/getlantern) is licensed under the Apache 2.0 open-source license. 10 | -------------------------------------------------------------------------------- /foliage/data/macos-systray-widget/go.mod: -------------------------------------------------------------------------------- 1 | module macos-systray-widget 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 // indirect 7 | github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 // indirect 8 | github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7 // indirect 9 | github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect 10 | github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect 11 | github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect 12 | github.com/getlantern/systray v1.1.0 // indirect 13 | github.com/go-stack/stack v1.8.0 // indirect 14 | github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect 15 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /foliage/data/macos-systray-widget/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4= 3 | github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY= 4 | github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 h1:6uJ+sZ/e03gkbqZ0kUG6mfKoqDb4XMAzMIwlajq19So= 5 | github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A= 6 | github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7 h1:guBYzEaLz0Vfc/jv0czrr2z7qyzTOGC9hiQ0VC+hKjk= 7 | github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7/go.mod h1:zx/1xUUeYPy3Pcmet8OSXLbF47l+3y6hIPpyLWoR9oc= 8 | github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 h1:micT5vkcr9tOVk1FiH8SWKID8ultN44Z+yzd2y/Vyb0= 9 | github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o= 10 | github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 h1:XYzSdCbkzOC0FDNrgJqGRo8PCMFOBFL9py72DRs7bmc= 11 | github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA= 12 | github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA= 13 | github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA= 14 | github.com/getlantern/systray v1.1.0 h1:U0wCEqseLi2ok1fE6b88gJklzriavPJixZysZPkZd/Y= 15 | github.com/getlantern/systray v1.1.0/go.mod h1:AecygODWIsBquJCJFop8MEQcJbWFfw/1yWbVabNgpCM= 16 | github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= 17 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 18 | github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= 19 | github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= 20 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 21 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 22 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 23 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9 h1:YTzHMGlqJu67/uEo1lBv0n3wBXhXNeUbB1XfN2vmTm0= 24 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 25 | -------------------------------------------------------------------------------- /foliage/data/macos-systray-widget/icon/README.md: -------------------------------------------------------------------------------- 1 | # Systray icon for Foliage macOS systray widget 2 | 3 | The files [make_icon.bat](make_icon.bat) and [make_icon.sh](make_icon.sh) originally came from the repository [systray](https://github.com/getlantern/systray) by [Latern](https://github.com/getlantern). 4 | 5 | Install Go, then install [2goarray](https://github.com/cratonica/2goarray) like this: 6 | 7 | ```sh 8 | setenv GOPATH /usr/local/go 9 | go install github.com/cratonica/2goarray@latest 10 | ``` 11 | 12 | The icon file is the 64x64 Foliage icon from the foliage/data directory. Generate the icon here like this: 13 | 14 | ```sh 15 | ./make_icon.sh icon-64.png 16 | ``` 17 | 18 | That will produce the file [iconunix.go](iconunix.go). 19 | -------------------------------------------------------------------------------- /foliage/data/macos-systray-widget/icon/icon-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/foliage/data/macos-systray-widget/icon/icon-64.png -------------------------------------------------------------------------------- /foliage/data/macos-systray-widget/icon/make_icon.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # To install 2goarray: 4 | # 5 | # setenv GOPATH /usr/local/go 6 | # go install github.com/cratonica/2goarray@latest 7 | 8 | if [ -z "$1" ]; then 9 | echo Please specify a PNG file 10 | exit 11 | fi 12 | 13 | if [ ! -f "$1" ]; then 14 | echo $1 is not a valid file 15 | exit 16 | fi 17 | 18 | OUTPUT=iconunix.go 19 | echo Generating $OUTPUT 20 | echo "//+build linux darwin" > $OUTPUT 21 | echo >> $OUTPUT 22 | cat "$1" | 2goarray Data icon >> $OUTPUT 23 | if [ $? -ne 0 ]; then 24 | echo Failure generating $OUTPUT 25 | exit 26 | fi 27 | echo Finished 28 | -------------------------------------------------------------------------------- /foliage/data/macos-systray-widget/macos-systray-widget.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/getlantern/systray" 5 | "macos-systray-widget/icon" 6 | ) 7 | 8 | func main() { 9 | onExit := func() { } 10 | systray.Run(onReady, onExit) 11 | } 12 | 13 | func onReady() { 14 | systray.SetTemplateIcon(icon.Data, icon.Data) 15 | systray.SetTooltip("Foliage") 16 | mQuit := systray.AddMenuItem("Quit", "Quit Foliage") 17 | go func() { 18 | <-mQuit.ClickedCh 19 | systray.Quit() 20 | }() 21 | } 22 | -------------------------------------------------------------------------------- /foliage/enum_utils.py: -------------------------------------------------------------------------------- 1 | ''' 2 | enum_utils.py: extensions and utilities for working with enums 3 | 4 | Copyright 5 | --------- 6 | 7 | Copyright (c) 2021-2022 by the California Institute of Technology. This code 8 | is open-source software released under a 3-clause BSD license. Please see the 9 | file "LICENSE" for more information. 10 | ''' 11 | 12 | from enum import Enum, EnumMeta 13 | 14 | 15 | # The following class was based in part on the posting by user "Pierre D" at 16 | # https://stackoverflow.com/a/65225753/743730 made on 2020-12-09. 17 | 18 | class MetaEnum(EnumMeta): 19 | '''Meta class for enums that implements "contain" test ability.''' 20 | def __contains__(cls, item): 21 | try: 22 | cls(item) 23 | except ValueError: 24 | return False 25 | return True 26 | 27 | 28 | # Inheriting from str solves the problem that PyWebIO otherwise complains about 29 | # the type values not being json serializable. This brilliant approach is due 30 | # to a posting by "Justin Carter" @ https://stackoverflow.com/a/51976841/743730 31 | 32 | class ExtendedEnum(str, Enum, metaclass = MetaEnum): 33 | '''Extend Enum class with a function allowing a test for containment.''' 34 | -------------------------------------------------------------------------------- /foliage/exceptions.py: -------------------------------------------------------------------------------- 1 | ''' 2 | exceptions.py: exceptions defined by Foliage 3 | 4 | Authors 5 | ------- 6 | 7 | Michael Hucka -- Caltech Library 8 | 9 | Copyright 10 | --------- 11 | 12 | Copyright (c) 2021-2022 by the California Institute of Technology. This code is 13 | open-source software released under a 3-clause BSD license. Please see the 14 | file "LICENSE" for more information. 15 | ''' 16 | 17 | 18 | # Base class. 19 | # ............................................................................. 20 | # The base class makes it possible to use a single test to distinguish between 21 | # exceptions generated by Foliage and exceptions generated by something else. 22 | 23 | class FoliageException(Exception): 24 | '''Base class for Foliage exceptions.''' 25 | 26 | 27 | # Exception classes. 28 | # ............................................................................. 29 | 30 | class FolioOpFailed(FoliageException): 31 | '''Requested operation was unsuccessful.''' 32 | 33 | 34 | class FolioError(FoliageException): 35 | '''Unrecoverable problem involving interactions with the FOLIO server.''' 36 | -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | @ECHO off 2 | REM =========================================================================== 3 | REM @file make.bat 4 | REM @brief Build a .exe using PyInstaller 5 | REM @author Michael Hucka 6 | REM @license Please see the file named LICENSE in the project directory 7 | REM @website https://github.com/caltechlibrary/holdit 8 | REM 9 | REM Usage: 10 | REM 1. start a terminal shell (e.g., cmd.exe) 11 | REM 2. cd into this directory 12 | REM 3. run "make" 13 | REM =========================================================================== 14 | 15 | ECHO Removing "dist/win" and "build/win" subdirectories. 16 | 17 | IF EXIST dist\win RD /S /Q dist\win 18 | IF EXIST build\win RD /S /Q build\win 19 | 20 | ECHO Making sure all Python packages are the right version 21 | 22 | python -m pip install -r requirements.txt 23 | 24 | ECHO Generating version.py ... 25 | 26 | python dev/installers/windows/create-version.py 27 | 28 | ECHO Generating InnoSetup script. 29 | 30 | python dev/installers/windows/create-innosetup-script.py 31 | 32 | ECHO Generating splash screen file ... 33 | 34 | python dev/splash-screen/create-splash-screen.py 35 | 36 | ECHO Running PyInstaller ... 37 | 38 | python -m PyInstaller --distpath dist/win --clean --noconfirm pyinstaller-win32.spec 39 | 40 | ECHO "make.bat" finished. 41 | ECHO The .exe will be in the "dist" subdirectory. 42 | ECHO Now run Innosetup to create an installer. 43 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # @file requirements-dev.txt 3 | # @brief Python dependencies for Foliage for development 4 | # @created 2021-10-16 5 | # @license Please see the file named LICENSE in the project directory 6 | # @website https://github.com/caltechlibrary/foliage 7 | # ============================================================================= 8 | 9 | -r requirements.txt 10 | 11 | pytest >= 6.2.5 12 | pytest-cov >= 3.0.0 13 | pytest-mock >= 3.7.0 14 | 15 | flake8 >= 4.0.1 16 | flake8-bugbear >= 22.4.25 17 | flake8-builtins >= 1.5.3 18 | flake8-comprehensions >= 3.8.0 19 | flake8-executable >= 2.1.1 20 | flake8_implicit_str_concat >= 0.3.0 21 | flake8-pie >= 0.15.0 22 | flake8-simplify >= 0.19.2 23 | 24 | pyinstaller 25 | 26 | linkify-it-py 27 | myst-parser 28 | sphinx-autobuild 29 | sphinx-material 30 | sphinxcontrib-mermaid 31 | twine 32 | wheel 33 | -------------------------------------------------------------------------------- /requirements-windows.txt: -------------------------------------------------------------------------------- 1 | # Summary: requirements when using Windows. 2 | 3 | -r requirements.txt 4 | 5 | pywin32 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # @file requirements.txt 3 | # @brief Python dependencies for Foliage 4 | # @created 2021-10-16 5 | # @license Please see the file named LICENSE in the project directory 6 | # @website https://github.com/caltechlibrary/foliage 7 | # ============================================================================= 8 | 9 | appdirs == 1.4.4 10 | boltons == 21.0.0 11 | commonpy == 1.13.0 12 | fastnumbers == 3.1.0 13 | keyring == 23.2.1 14 | openpyxl == 3.0.7 15 | plac == 1.3.4 16 | pyperclip == 1.8.2 17 | PyQt5 == 5.15.9 18 | python-decouple == 3.5 19 | python_dateutil == 2.8.2 20 | rich >= 13.3.5 21 | setuptools 22 | sidetrack >= 2.0.1 23 | tornado == 6.1 24 | python-slugify == 8.0.1 25 | validators == 0.20.0 26 | wand == 0.6.11 27 | 28 | # The following is a fork of PyWebIo with a couple of minor but crucial 29 | # modifications. It cannot be put as a requirement in this file because PyPI 30 | # will not accept a distribution with a dependency in this form. I have to 31 | # install it by hand when I create a python environment to run Foliage. 32 | #pywebio @ git+https://github.com/mhucka/PyWebIO.git@2af53fc 33 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # @file setup.cfg 3 | # @brief Package metadata and PyPI configuration 4 | # @created 2021-10-16 5 | # @license Please see the file named LICENSE in the project directory 6 | # @website https://github.com/caltechlibrary/foliage 7 | # ============================================================================= 8 | 9 | [metadata] 10 | name = foliage 11 | version = 1.8.0 12 | description = Foliage: a tool to do bulk changes in FOLIO using the OKAPI API 13 | author = Mike Hucka 14 | author_email = helpdesk@library.caltech.edu 15 | license = BSD 3-clause license 16 | license_files = LICENSE 17 | url = https://github.com/caltechlibrary/foliage 18 | # The remaining items below are used by PyPI. 19 | project_urls = 20 | Source Code = https://github.com/caltechlibrary/foliage 21 | Bug Tracker = https://github.com/caltechlibrary/foliage/issues 22 | keywords = Python, applications 23 | classifiers = 24 | Development Status :: 5 - Production/Stable 25 | Environment :: Console 26 | License :: OSI Approved :: BSD License 27 | Intended Audience :: Science/Research 28 | Operating System :: MacOS :: MacOS X 29 | Operating System :: POSIX 30 | Operating System :: POSIX :: Linux 31 | Operating System :: Unix 32 | Programming Language :: Python 33 | Programming Language :: Python :: 3.9 34 | long_description = file:README.md 35 | long_description_content_type = text/markdown 36 | 37 | [options] 38 | packages = find: 39 | zip_safe = False 40 | python_requires = >= 3.9 41 | 42 | [options.entry_points] 43 | console_scripts = 44 | foliage = foliage.__main__:console_scripts_main 45 | 46 | [tool:pytest] 47 | pythonpath = . 48 | 49 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # ============================================================================= 3 | # @file setup.py 4 | # @brief Installation setup file 5 | # @created 2021-10-16 6 | # @license Please see the file named LICENSE in the project directory 7 | # @website https://github.com/caltechlibrary/foliage 8 | # 9 | # Note: configuration metadata is maintained in setup.cfg. This file exists 10 | # primarily to hook in setup.cfg and requirements.txt. 11 | # ============================================================================= 12 | 13 | from setuptools import setup 14 | 15 | 16 | def requirements(file): 17 | from os import path 18 | required = [] 19 | requirements_file = path.join(path.abspath(path.dirname(__file__)), file) 20 | if path.exists(requirements_file): 21 | with open(requirements_file, encoding='utf-8') as f: 22 | required = [ln for ln in filter(str.strip, f.read().splitlines()) 23 | if not ln.startswith('#')] 24 | if any(item.startswith(('-', '.', '/')) for item in required): 25 | # The requirements.txt uses pip features. Try to use pip's parser. 26 | try: 27 | from pip._internal.req import parse_requirements 28 | from pip._internal.network.session import PipSession 29 | parsed = parse_requirements(requirements_file, PipSession()) 30 | required = [item.requirement for item in parsed] 31 | except ImportError: 32 | # No pip, or not the expected version. Give up & return as-is. 33 | pass 34 | return required 35 | 36 | 37 | setup( 38 | setup_requires = ['wheel'], 39 | install_requires = requirements('requirements.txt'), 40 | extras_require={'dev': requirements('requirements-dev.txt')}, 41 | ) 42 | -------------------------------------------------------------------------------- /tests/settings.ini: -------------------------------------------------------------------------------- 1 | # This settings file is read by the code in folio.py 2 | 3 | [settings] 4 | FOLIO_OKAPI_URL = 5 | FOLIO_OKAPI_TENANT_ID = 6 | FOLIO_OKAPI_TOKEN = 7 | -------------------------------------------------------------------------------- /tests/test_credentials.py: -------------------------------------------------------------------------------- 1 | def test_encoding(): 2 | from foliage.credentials import _encoded, _decoded 3 | assert _decoded(_encoded('a', '1', 'c')) == ('a', '1', 'c') 4 | 5 | 6 | def test_current_credentials(monkeypatch): 7 | from foliage.credentials import Credentials, current_credentials 8 | monkeypatch.setenv('FOLIO_OKAPI_URL', 'https://foo') 9 | monkeypatch.setenv('FOLIO_OKAPI_TENANT_ID', '1') 10 | monkeypatch.setenv('FOLIO_OKAPI_TOKEN', 'abc') 11 | assert current_credentials() == Credentials('https://foo', '1', 'abc') 12 | 13 | 14 | def test_credentials_complete(): 15 | from foliage.credentials import Credentials, credentials_complete 16 | assert credentials_complete(Credentials('https://foo', '1', 'abc')) 17 | 18 | 19 | def test_credentials_from_file(monkeypatch, tmp_path): 20 | from foliage.credentials import Credentials, credentials_from_file 21 | d = tmp_path / 'sub' 22 | d.mkdir() 23 | p = d / 'settings.ini' 24 | p.write_text(''' 25 | [settings] 26 | FOLIO_OKAPI_URL = https://foo 27 | FOLIO_OKAPI_TENANT_ID = 1 28 | FOLIO_OKAPI_TOKEN = abc 29 | ''') 30 | monkeypatch.chdir(d) 31 | assert credentials_from_file(str(p)) == Credentials('https://foo', '1', 'abc') 32 | 33 | 34 | def test_credentials_from_env(monkeypatch): 35 | from foliage.credentials import Credentials, credentials_from_env 36 | monkeypatch.setenv('FOLIO_OKAPI_URL', 'https://foo') 37 | monkeypatch.setenv('FOLIO_OKAPI_TENANT_ID', '1') 38 | monkeypatch.setenv('FOLIO_OKAPI_TOKEN', 'abc') 39 | assert credentials_from_env() == Credentials('https://foo', '1', 'abc') 40 | -------------------------------------------------------------------------------- /tests/test_folio.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.fixture 5 | def define_env_vars(monkeypatch): 6 | from os import path 7 | monkeypatch.chdir(path.dirname(__file__)) 8 | 9 | from decouple import Config, RepositoryIni 10 | source = Config(RepositoryIni(source = 'settings.ini')) 11 | url = source.get('FOLIO_OKAPI_URL') 12 | tenant = source.get('FOLIO_OKAPI_TENANT_ID') 13 | token = source.get('FOLIO_OKAPI_TOKEN') 14 | 15 | monkeypatch.setenv('FOLIO_OKAPI_URL', url) 16 | monkeypatch.setenv('FOLIO_OKAPI_TENANT_ID', tenant) 17 | monkeypatch.setenv('FOLIO_OKAPI_TOKEN', token) 18 | 19 | 20 | def test_record_id_type(define_env_vars): 21 | from foliage.folio import Folio, IdKind 22 | folio = Folio() 23 | assert folio.id_kind('35047019219716') == IdKind.ITEM_BARCODE 24 | assert folio.id_kind('d893839b-0309-4856-b496-0db89a0a6a04') == IdKind.ITEM_ID 25 | assert folio.id_kind('it00002135242') == IdKind.ITEM_HRID 26 | assert folio.id_kind('946cce1b-0451-460e-816f-51436182efaa') == IdKind.HOLDINGS_ID 27 | assert folio.id_kind(r'\35047018212589') == IdKind.ITEM_BARCODE 28 | assert folio.id_kind(r'\35047019565233') == IdKind.ITEM_BARCODE 29 | assert folio.id_kind('nobarcode1') == IdKind.ITEM_BARCODE 30 | assert folio.id_kind('nobarcode10') == IdKind.ITEM_BARCODE 31 | assert folio.id_kind('nobarcode10000') == IdKind.ITEM_BARCODE 32 | assert folio.id_kind('nobarcode10001') == IdKind.ITEM_BARCODE 33 | assert folio.id_kind('nobarcode10002') == IdKind.ITEM_BARCODE 34 | assert folio.id_kind('350470106306') == IdKind.ITEM_BARCODE 35 | assert folio.id_kind('350470106969') == IdKind.ITEM_BARCODE 36 | assert folio.id_kind('93') == IdKind.ITEM_BARCODE 37 | assert folio.id_kind('95') == IdKind.ITEM_BARCODE 38 | assert folio.id_kind('101') == IdKind.ITEM_BARCODE 39 | assert folio.id_kind('TEMP-D1234') == IdKind.ITEM_BARCODE 40 | assert folio.id_kind('TEMP-FFF123') == IdKind.ITEM_BARCODE 41 | assert folio.id_kind('tmp-21924070') == IdKind.ITEM_BARCODE 42 | assert folio.id_kind('cit.oai.caltech.folio.ebsco.com.fs00001057.17c5c348.8796.4b11.90a8.6b31ff9509ed') == IdKind.ACCESSION 43 | assert folio.id_kind('cit.oai.edge.caltech.folio.ebsco.com.fs00001057.17c5c348.8796.4b11.90a8.6b31ff9509ed') == IdKind.ACCESSION 44 | 45 | 46 | def test_extracting_instance_id(): 47 | from foliage.folio import instance_id_from_accession 48 | instance_id_from_accession('cit.oai.caltech.folio.ebsco.com.fs00001057.17c5c348.8796.4b11.90a8.6b31ff9509ed') == '17c5c348-8796-4b11-90a8-6b31ff9509ed' 49 | instance_id_from_accession('cit.oai.edge.caltech.folio.ebsco.com.fs00001057.17c5c348.8796.4b11.90a8.6b31ff9509ed') == '17c5c348-8796-4b11-90a8-6b31ff9509ed' 50 | -------------------------------------------------------------------------------- /tests/test_init.py: -------------------------------------------------------------------------------- 1 | def test_version(): 2 | """Test version import.""" 3 | from foliage import __version__ 4 | assert __version__ 5 | 6 | 7 | def test_print_version(capsys): 8 | from foliage import print_version 9 | print_version() 10 | captured = capsys.readouterr() 11 | assert 'URL' in captured.out 12 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/.drone.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | type: exec 3 | name: default 4 | 5 | clone: 6 | disable: true 7 | 8 | trigger: 9 | branch: 10 | - dev 11 | event: 12 | - push 13 | 14 | steps: 15 | - name: clone 16 | commands: 17 | - sleep 300 # wait aliyun repo to sync 18 | - git init 19 | - git remote add aliyun "https://code.aliyun.com/wang0618/pywebio.git" 20 | - git fetch --no-tags --prune --progress --no-recurse-submodules --depth=1 aliyun $DRONE_BRANCH 21 | - git checkout --progress --force -B $DRONE_BRANCH aliyun/$DRONE_BRANCH 22 | - git log -1 23 | - name: deploy demos 24 | commands: 25 | - docker rm -f pywebio-demos || true 26 | - > 27 | docker run --restart=always --name=pywebio-demos -v $PWD:/app_tmp 28 | --label="traefik.http.services.pywebiodemos.loadbalancer.server.port=80" 29 | -d python:3 bash -c "cp -r /app_tmp /app && cd /app && pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple . && python3 -m demos --port=80" 30 | - sleep 5 # wait container start -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/.gitattributes: -------------------------------------------------------------------------------- 1 | # https://github.com/github/linguist#overrides 2 | 3 | pywebio/html/js/* linguist-vendored 4 | pywebio/html/css/* linguist-vendored 5 | pywebio/html/codemirror/* linguist-vendored 6 | 7 | pywebio/html/css/app.css -linguist-vendored 8 | 9 | docs/* linguist-documentation 10 | 11 | webiojs/package* linguist-generated -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 注: 10 | 11 | 对于PyWebIO使用咨询或对于其他人也可能有帮助的问题,请考虑移至 [Discussions](https://github.com/wang0618/PyWebIO/discussions) 进行发帖。 12 | 13 | **BUG描述** 14 | 描述BUG表现以及复现方式。 15 | 如果浏览器控制台有报错以及脚本抛出异常也请将报错信息附上 16 | 17 | **环境信息** 18 | - 操作系统: 19 | - 浏览器及版本: 20 | - Python版本: 使用 `python3 --version` 查看 21 | - PyWebIO版本: 使用 `python3 -c "import pywebio;print(pywebio.__version__)"` 查看 22 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies and lint with a variety of Python versions 2 | 3 | name: Python lint 4 | 5 | on: [push, pull_request] 6 | 7 | jobs: 8 | lint: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | python-version: [3.5, 3.6, 3.7, 3.8] 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up Python ${{ matrix.python-version }} 17 | uses: actions/setup-python@v1 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | - name: Install dependencies 21 | run: | 22 | python -m pip install --upgrade pip 23 | pip install -r requirements.txt 24 | - name: Lint with flake8 25 | run: | 26 | pip install flake8 27 | # stop the build if there are Python syntax errors or undefined names 28 | flake8 pywebio --count --select=E9,F63,F7,F82 --show-source --statistics 29 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 30 | flake8 pywebio --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | create: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@master 14 | - name: Set up Python 3 15 | uses: actions/setup-python@v1 16 | with: 17 | python-version: 3.7 18 | - name: Set up Node.js v13.5 19 | uses: actions/setup-node@v1 20 | with: 21 | node-version: 13.5 22 | - name: Build frontend 23 | working-directory: ./webiojs 24 | run: | 25 | npm install 26 | gulp 27 | cp dist/pywebio.min.js ../pywebio/html/js 28 | - name: PyPi Upload 29 | run: | 30 | pip3 install twine 31 | python3 setup.py sdist 32 | twine upload --username "__token__" --disable-progress-bar --verbose dist/* 33 | env: 34 | TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} 35 | - name: Set tag 36 | run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV 37 | - name: Push asset 38 | run: | 39 | git config --global user.email "wang0.618@qq.com" 40 | git config --global user.name "${{ github.actor }}" 41 | git config --global credential.helper '!f() { sleep 1; echo "username=${{ github.actor }}"; echo "password=${GH_TOKEN}"; }; f' 42 | 43 | git clone https://github.com/wang0618/pywebio-assets.git 44 | rm -rf pywebio-assets/* 45 | cp -r pywebio/html/* pywebio-assets 46 | 47 | cd pywebio-assets 48 | git add . 49 | git commit -m "Build from https://github.com/wang0618/PyWebIO/commit/$GITHUB_SHA" 50 | git tag $RELEASE_VERSION 51 | 52 | git push -u origin --tags 53 | git push -u origin 54 | env: 55 | GH_TOKEN: ${{ secrets.ASSET_REPO_TOKEN }} 56 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/.github/workflows/sync_repo.yml: -------------------------------------------------------------------------------- 1 | name: Sync with mirror repo 2 | on: 3 | push: 4 | branches: 5 | - dev 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@master 12 | - name: Set up Python 3.7 13 | uses: actions/setup-python@v1 14 | with: 15 | python-version: 3.7 16 | - name: Set up Node.js v13.5 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: 13.5 20 | - name: Build frontend 21 | working-directory: ./webiojs 22 | run: | 23 | npm install 24 | DEV=1 gulp 25 | cp dist/pywebio.min.* ../pywebio/html/js 26 | - name: Build doc demos 27 | run: | 28 | pip3 install -e ".[all]" 29 | cd docs && CODE_EXPORT_PATH=../demos/doc_demos make clean text 30 | - name: Set dev version 31 | run: python3 tools/build_dev_version.py 32 | - name: Push 33 | run: | 34 | git fetch --unshallow origin 35 | git remote add aliyun "https://code.aliyun.com/wang0618/pywebio.git" 36 | git config credential.helper '!f() { sleep 1; echo "username=${ALIYUN_GIT_USER}"; echo "password=${ALIYUN_GIT_PASSWORD}"; }; f' 37 | rm .gitignore 38 | git add pywebio/__version__.py 39 | git add pywebio/html/js 40 | git add demos/doc_demos 41 | git config user.email "${ALIYUN_GIT_USER}" 42 | git config user.name "${ALIYUN_GIT_USER}" 43 | git commit --amend --no-edit 44 | git push -f -u aliyun --tags || exit 0 45 | git push -f -u aliyun || exit 0 46 | env: 47 | ALIYUN_GIT_USER: ${{ secrets.ALIYUN_GIT_USER }} 48 | ALIYUN_GIT_PASSWORD: ${{ secrets.ALIYUN_GIT_PASSWORD }} -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | on: [push, pull_request] 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: Checkout 8 | uses: actions/checkout@master 9 | - name: Set up Python 3.7 10 | uses: actions/setup-python@v1 11 | with: 12 | python-version: 3.7 13 | - name: Set up Node.js v13.5 14 | uses: actions/setup-node@v1 15 | with: 16 | node-version: 13.5 17 | - name: Build frontend 18 | working-directory: ./webiojs 19 | run: | 20 | npm install 21 | gulp 22 | cp dist/pywebio.min.* ../pywebio/html/js 23 | - name: Install Test JS deps 24 | run: npm install -D @percy/agent 25 | - name: Install package 26 | run: pip3 install ".[all]" 27 | - name: Install dev dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | pip install -r requirements.txt 31 | - name: Install env 32 | run: echo "PERCY_TOKEN=${{ secrets.PERCY_TOKEN }}" >> $GITHUB_ENV 33 | - name: Percy Test 34 | uses: percy/exec-action@v0.3.1 35 | with: 36 | working-directory: ./test 37 | command: "./run_all.sh" 38 | - name: Upload test output 39 | uses: actions/upload-artifact@v1 40 | if: failure() 41 | with: 42 | name: test output 43 | path: test/output 44 | - name: Upload Codecov Report 45 | working-directory: ./test 46 | run: bash <(curl -s https://codecov.io/bash) 47 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.py[cod] 3 | *$py.class 4 | *.db 5 | *.sqlite3 6 | 7 | .idea/ 8 | 9 | # 2022-05-09 I'm checking in the JS files so that I 10 | # can use my fork of PyWebIO for installations (for use with Foliage). 11 | # pywebio/html/js/pywebio.min.* 12 | /no_git 13 | /build 14 | /dist 15 | /*.egg-info 16 | /docs/_build 17 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Build documentation in the docs/ directory with Sphinx 9 | sphinx: 10 | configuration: docs/conf.py 11 | 12 | # Build documentation with MkDocs 13 | #mkdocs: 14 | # configuration: mkdocs.yml 15 | 16 | # Optionally build your docs in additional formats such as PDF and ePub 17 | formats: all 18 | 19 | # Optionally set the version of Python and requirements required to build your docs 20 | python: 21 | version: 3.7 22 | install: 23 | - requirements: requirements.txt 24 | - method: pip 25 | path: . 26 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 WangWeimin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include demos *.py 2 | prune docs 3 | graft pywebio/html 4 | graft pywebio/platform/tpl -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/Procfile: -------------------------------------------------------------------------------- 1 | web: python -m demos --port=$PORT -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/demos/__init__.py: -------------------------------------------------------------------------------- 1 | r""" 2 | .. automodule:: demos.bmi 3 | .. automodule:: demos.input_usage 4 | .. automodule:: demos.output_usage 5 | .. automodule:: demos.chat_room 6 | """ -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/demos/bmi.py: -------------------------------------------------------------------------------- 1 | """ 2 | BMI指数计算 3 | ^^^^^^^^^^^ 4 | 5 | 计算 `BMI指数 `_ 的简单应用 6 | 7 | :demo_host:`Demo地址 ` `源码 `_ 8 | """ 9 | from pywebio import start_server 10 | from pywebio.input import * 11 | from pywebio.output import * 12 | from pywebio.session import set_env 13 | 14 | 15 | def main(): 16 | """BMI Calculation 17 | 18 | 计算BMI指数的简单应用 19 | """ 20 | 21 | put_markdown("""# BMI指数 22 | 23 | [`BMI指数`](https://baike.baidu.com/item/%E4%BD%93%E8%B4%A8%E6%8C%87%E6%95%B0/1455733)(Body Mass Index,BMI),是用体重千克数除以身高米数的平方得出的数字,是国际上常用的衡量人体胖瘦程度以及是否健康的一个标准。 24 | 25 | 成年人的BMI值处于以下阶段 26 | 27 | | 体形分类 | BMI值范围 | 28 | | -------- | --------- | 29 | | 极瘦 | BMI<14.9 | 30 | | 偏瘦 | 14.9≤BMI<18.4 | 31 | | 正常 | 18.4≤BMI<22.9 | 32 | | 过重 | 22.9≤BMI<27.5 | 33 | | 肥胖 | 27.5≤BMI<40 | 34 | | 非常肥胖 | BMI≥40 | 35 | 36 | ## BMI指数计算器 37 | 本程序的源代码[链接](https://github.com/wang0618/PyWebIO/blob/dev/demos/bmi.py) 38 | 39 | """, strip_indent=4) 40 | 41 | info = input_group('计算BMI:', [ 42 | input("请输入你的身高(cm)", name="height", type=FLOAT), 43 | input("请输入你的体重(kg)", name="weight", type=FLOAT), 44 | ]) 45 | 46 | BMI = info['weight'] / (info['height'] / 100) ** 2 47 | 48 | top_status = [(14.9, '极瘦'), (18.4, '偏瘦'), 49 | (22.9, '正常'), (27.5, '过重'), 50 | (40.0, '肥胖'), (float('inf'), '非常肥胖')] 51 | 52 | for top, status in top_status: 53 | if BMI <= top: 54 | put_markdown('你的 BMI 值: `%.1f`,身体状态:`%s`' % (BMI, status)) 55 | break 56 | 57 | 58 | if __name__ == '__main__': 59 | start_server(main, debug=True, port=8080, cdn=False) 60 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/demos/bokeh_app.py: -------------------------------------------------------------------------------- 1 | from bokeh.io import output_notebook 2 | from bokeh.io import show 3 | from bokeh.layouts import column 4 | from bokeh.models import ColumnDataSource, Slider 5 | from bokeh.plotting import figure 6 | from bokeh.sampledata.sea_surface_temperature import sea_surface_temperature 7 | 8 | from pywebio import start_server 9 | from pywebio.output import * 10 | 11 | 12 | def bkapp(doc): 13 | df = sea_surface_temperature.copy() 14 | source = ColumnDataSource(data=df) 15 | 16 | plot = figure(x_axis_type='datetime', y_range=(0, 25), 17 | y_axis_label='Temperature (Celsius)', 18 | title="Sea Surface Temperature at 43.18, -70.43") 19 | plot.line('time', 'temperature', source=source) 20 | 21 | def callback(attr, old, new): 22 | if new == 0: 23 | data = df 24 | else: 25 | data = df.rolling('{0}D'.format(new)).mean() 26 | source.data = ColumnDataSource.from_df(data) 27 | 28 | slider = Slider(start=0, end=30, value=0, step=1, title="Smoothing by N Days") 29 | slider.on_change('value', callback) 30 | 31 | doc.add_root(column([slider, plot], sizing_mode='stretch_width')) 32 | 33 | 34 | def main(): 35 | output_notebook(verbose=False, notebook_type='pywebio') 36 | 37 | put_markdown("""# Bokeh Applications in PyWebIO 38 | 39 | [Bokeh Applications](https://docs.bokeh.org/en/latest/docs/user_guide/server.html) 支持向图表的添加按钮、输入框等交互组件,并向组件添加Python回调,从而创建可以与Python代码交互的可视化图表。 40 | 41 | 在PyWebIO中,你也可以使用 `bokeh.io.show()` 来显示一个Bokeh App,和输出普通图表一样,只需要在会话开始时调用 `bokeh.io.output_notebook(notebook_type='pywebio')` 来设置PyWebIO输出环境。 42 | 43 | 以下为一个 Bokeh App demo: 44 | 45 | """, lstrip=True) 46 | 47 | show(bkapp) 48 | 49 | 50 | if __name__ == '__main__': 51 | start_server(main, port=8080, debug=True, cdn=False) 52 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/demos/chat_room.py: -------------------------------------------------------------------------------- 1 | """ 2 | 聊天室 3 | ^^^^^^^^^^^ 4 | 和当前所有在线的人聊天 5 | 6 | :demo_host:`Demo地址 ` `源码 `_ 7 | 8 | * 使用基于协程的会话 9 | * 使用 `run_async() ` 启动后台协程 10 | """ 11 | import asyncio 12 | 13 | from pywebio import start_server, run_async 14 | from pywebio.input import * 15 | from pywebio.output import * 16 | from pywebio.session import defer_call, set_env, run_js 17 | 18 | # 最大消息记录保存 19 | MAX_MESSAGES_CNT = 10 ** 4 20 | 21 | chat_msgs = [] # 聊天记录 (name, msg) 22 | online_users = set() # 在线用户 23 | 24 | 25 | async def refresh_msg(my_name, msg_box): 26 | """刷新聊天消息""" 27 | global chat_msgs 28 | last_idx = len(chat_msgs) 29 | while True: 30 | await asyncio.sleep(0.5) 31 | for m in chat_msgs[last_idx:]: 32 | if m[0] != my_name: # 仅刷新其他人的新信息 33 | msg_box.append(put_markdown('`%s`: %s' % m)) 34 | 35 | # 清理聊天记录 36 | if len(chat_msgs) > MAX_MESSAGES_CNT: 37 | chat_msgs = chat_msgs[len(chat_msgs) // 2:] 38 | 39 | last_idx = len(chat_msgs) 40 | 41 | 42 | async def main(): 43 | """PyWebIO聊天室 44 | 45 | 和当前所有在线的人聊天 46 | """ 47 | global chat_msgs 48 | 49 | put_markdown("## PyWebIO聊天室\n欢迎来到聊天室,你可以和当前所有在线的人聊天。你可以在浏览器的多个标签页中打开本页面来测试聊天效果。" 50 | "本应用使用不到80行代码实现,源代码[链接](https://github.com/wang0618/PyWebIO/blob/dev/demos/chat_room.py)", lstrip=True) 51 | 52 | msg_box = output() 53 | put_scrollable(msg_box, height=300, keep_bottom=True) 54 | nickname = await input("请输入你的昵称", required=True, validate=lambda n: '昵称已被使用' if n in online_users or n == '📢' else None) 55 | 56 | online_users.add(nickname) 57 | chat_msgs.append(('📢', '`%s`加入聊天室. 当前在线人数 %s' % (nickname, len(online_users)))) 58 | msg_box.append(put_markdown('`📢`: `%s`加入聊天室. 当前在线人数 %s' % (nickname, len(online_users)))) 59 | 60 | @defer_call 61 | def on_close(): 62 | online_users.remove(nickname) 63 | chat_msgs.append(('📢', '`%s`退出聊天室. 当前在线人数 %s' % (nickname, len(online_users)))) 64 | 65 | refresh_task = run_async(refresh_msg(nickname, msg_box)) 66 | 67 | while True: 68 | data = await input_group('发送消息', [ 69 | input(name='msg', help_text='消息内容支持行内Markdown语法'), 70 | actions(name='cmd', buttons=['发送', '多行输入', {'label': '退出', 'type': 'cancel'}]) 71 | ], validate=lambda d: ('msg', '消息不为空') if d['cmd'] == '发送' and not d['msg'] else None) 72 | if data is None: 73 | break 74 | if data['cmd'] == '多行输入': 75 | data['msg'] = '\n' + await textarea('消息内容', help_text='消息内容支持Markdown语法') 76 | msg_box.append(put_markdown('`%s`: %s' % (nickname, data['msg']), sanitize=True)) 77 | chat_msgs.append((nickname, data['msg'])) 78 | 79 | refresh_task.close() 80 | toast("你已经退出聊天室") 81 | 82 | 83 | if __name__ == '__main__': 84 | start_server(main, debug=True, port=8080, cdn=False) 85 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/demos/config.py: -------------------------------------------------------------------------------- 1 | # demos 模块的部署地址 2 | demo_host = 'http://pywebio-demos.demo.wangweimin.site' 3 | 4 | # https://github.com/wang0618/pywebio-chart-gallery 的部署地址 5 | charts_demo_host = 'http://pywebio-charts.demo.wangweimin.site' 6 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/demos/set_env_demo.py: -------------------------------------------------------------------------------- 1 | """ 2 | `pywebio.session.set_env()` demo 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | """ 5 | from pywebio import start_server 6 | from pywebio.input import * 7 | from pywebio.output import * 8 | from pywebio.session import * 9 | import datetime 10 | import asyncio 11 | 12 | 13 | async def main(): 14 | """`pywebio.session.set_env()` demo""" 15 | 16 | set_scope('time') 17 | put_markdown('> 可用于观察 `output_animation` 项的动画效果') 18 | put_markdown('---') 19 | 20 | async def bg_task(): 21 | while 1: 22 | with use_scope('time', clear=True): 23 | put_text('当前时间:', datetime.datetime.now()) 24 | 25 | await asyncio.sleep(1) 26 | 27 | run_async(bg_task()) 28 | 29 | put_buttons(['输出文本'], [lambda: put_text(datetime.datetime.now())]) 30 | put_markdown('> 可用于观察 `auto_scroll_bottom` 项的自动滚动效果') 31 | put_markdown('---') 32 | put_text('Some text.\n' * 10) 33 | 34 | state = { 35 | 'title': 'PyWebIO set_env() Demo', 36 | 'output_animation': True, 37 | 'auto_scroll_bottom': False, 38 | } 39 | set_env(**state) 40 | 41 | while 1: 42 | curr_state_info = ', '.join('%s=%r' % (k, v) for k, v in state.items()) 43 | key = await actions('选择要更改的会话环境设置项', list(state.keys()), help_text='当前状态:' + curr_state_info) 44 | if key == 'title': 45 | state['title'] = await input('请输入标题', value=state['title']) 46 | set_env(title=state['title']) 47 | toast('已将标题设置为%r' % state['title']) 48 | elif key in state: 49 | state[key] = not (state[key]) 50 | set_env(**{key: state[key]}) 51 | toast('已将`%s`设置为%r' % (key, state[key])) 52 | 53 | 54 | if __name__ == '__main__': 55 | start_server(main, debug=True, port=8080, cdn=False) 56 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/FAQ.rst: -------------------------------------------------------------------------------- 1 | 常见问题 2 | ========================== 3 | 4 | .. contents:: 5 | :local: 6 | 7 | 如何让输入框在提交后不消失,并可以持续性地输入 8 | ---------------------------------------------------------- 9 | PyWebIO 的设计就是输入表单在成功提交后就销毁,因为 PyWebIO 的输入是阻塞式的,一旦提交表单,输入函数就返回了,此时表单还留在界面上是没有意义的。如果想实现持续性的输入,可以将接收输入以及后续操作放到一个 ``while`` 循环中。 10 | 11 | 12 | 如何输出一个诸如搜索栏的输入框 13 | ---------------------------------------------------------- 14 | 很遗憾,PyWebIO并不支持将输入框作为一般性的内容输出到页面。因为这样就相当于又回到了基于回调获取输入的方式了,会导致应用开发的复杂性提高,PyWebIO不太推荐过多依赖回调机制,所以对此仅提供了非常少的支持。 15 | 不过也可以使用另一种方式实现近似的效果:只需要在需要显示输入框的地方放置一个button( `put_buttons() ` ),然后在button的回调函数中调用输入函数来获取输入并进行后续操作。 16 | 17 | 18 | 为什么 ``put_buttons()`` 的回调不起作用 19 | ---------------------------------------------------------- 20 | 一般情况下,在Server模式下,任务函数一旦返回(或在Script模式下,脚本运行结束),会话就结束了,此时事件回调也将不起作用,可以在任务函数(或脚本)末尾处使用 `pywebio.session.hold()` 函数来将会话保持,这样在用户关闭浏览器页面前,事件回调将一直可用。 参见 :ref:`Server模式与Script模式 ` 21 | 22 | 23 | 为什么 ``put_file()`` 无法下载文件 24 | ---------------------------------------------------------- 25 | 原因同上。 ``put_file()`` 的文件链接被点击后,也是需要和服务端通信获取文件数据的,所以会话关闭后下载链接会不可用。可以在任务函数末尾处使用 `pywebio.session.hold()` 函数来将会话保持。 -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/_ext/README.md: -------------------------------------------------------------------------------- 1 | ## sphinx示例代码添加在线Demo链接 2 | 3 | ### 自定义sphinx `exportable-codeblock` directive 4 | 5 | 代码实现:`PyWebIO/docs/_ext/codeblock.py` 6 | 7 | `exportable-codeblock` 指令可以像 `codeblock` 指令一样使用,用于展示代码: 8 | 9 | ```rest 10 | ..exportable-codeblock:: 11 | :name: test 12 | :summary: 描述 13 | 14 | put_text('hello world') 15 | 16 | ``` 17 | 18 | 当设置环境变量 `CODE_EXPORT_PATH` 后进行文档构建时,使用`exportable-codeblock`指令展示的示例代码会被导出到环境变量 `CODE_EXPORT_PATH`指定的目录中。 19 | 20 | 比如: 21 | ```bash 22 | CODE_EXPORT_PATH=/Users/wangweimin/repos/PyWebIO/demos/doc_domes make clean html 23 | ``` 24 | 25 | 使用`exportable-codeblock`指令展示的示例代码被导出后,可以使用 `PyWebIO/demos/doc_demo.py` 来运行。 26 | 27 | 为了在运行示例代码时,可以有更多选项,定义了一些特殊注释,这些特殊不会出现在生成的文档中,但会被导出并在运行示例代码时被特殊处理。 28 | 29 | 特殊注释如下: 30 | 31 | - `## ----` : 表示分割示例代码,将示例代码分割成不同的部分来分别运行。该注释主要放到行首 32 | - `# ..demo-only` : 表示该行代码仅在Demo页面中显示 33 | - `# ..doc-only` : 表示该行代码仅在文档中显示 -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/_ext/codeblock.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from docutils.parsers.rst import directives 4 | from sphinx.directives.code import CodeBlock 5 | import hashlib 6 | 7 | 8 | class ExportableCodeBlock(CodeBlock): 9 | option_spec = { 10 | 'summary': directives.unchanged, 11 | 'name': directives.unchanged, 12 | } 13 | 14 | names = set() 15 | 16 | @staticmethod 17 | def md5(str_data): 18 | t = hashlib.md5() 19 | t.update(str_data.encode('utf-8')) 20 | return t.hexdigest() 21 | 22 | def run(self): 23 | code_save_path = os.environ.get('CODE_EXPORT_PATH') 24 | caption = self.options.get('summary', '') 25 | 26 | if code_save_path and not os.path.exists(code_save_path): 27 | os.mkdir(code_save_path) 28 | 29 | code_id = self.md5('\n'.join(self.content))[-5:] 30 | if self.options.get('name', None) is None: 31 | # 设置name属性,从而让生成的代码html块具有id属性 32 | self.options.update({'name': 'demo-' + code_id}) 33 | 34 | name = self.options.get('name').replace('_','-') 35 | if name in type(self).names: 36 | name += '-' + code_id 37 | self.options.update({'name': name}) 38 | else: 39 | type(self).names.add(name) 40 | 41 | # 设置特殊class值,用于在js中搜索 42 | classes = self.options.get('class', []) 43 | classes.append('demo-cb') 44 | self.options.update({'class': classes}) 45 | 46 | raw_content_text = '\n'.join(self.content) 47 | 48 | content, self.content = self.content, [] 49 | for c in content: 50 | if '..demo-only' in c or '## ----' in c: 51 | continue 52 | c = c.replace('# ..doc-only', '') 53 | self.content.append(c) 54 | 55 | nodes = super().run() 56 | 57 | try: 58 | elem_id = nodes[0]['ids'][0] 59 | except IndexError: 60 | elem_id = None 61 | 62 | if code_save_path and elem_id: 63 | fpath = os.path.join(code_save_path, elem_id) 64 | open(fpath, 'w').write(caption + '\n\n' + raw_content_text) 65 | 66 | return nodes 67 | 68 | 69 | def setup(app): 70 | app.add_directive("exportable-codeblock", ExportableCodeBlock) 71 | app.add_js_file('pywebio.js') 72 | 73 | return { 74 | 'version': '0.1', 75 | 'parallel_read_safe': True, 76 | 'parallel_write_safe': True, 77 | } 78 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/assets/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/vendor/github.com/mhucka/PyWebIO/docs/assets/architecture.png -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/assets/codemirror_textarea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/vendor/github.com/mhucka/PyWebIO/docs/assets/codemirror_textarea.png -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/vendor/github.com/mhucka/PyWebIO/docs/assets/demo.gif -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/assets/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/vendor/github.com/mhucka/PyWebIO/docs/assets/demo.png -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/assets/input_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/vendor/github.com/mhucka/PyWebIO/docs/assets/input_1.png -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/assets/input_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/vendor/github.com/mhucka/PyWebIO/docs/assets/input_2.png -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/assets/input_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/vendor/github.com/mhucka/PyWebIO/docs/assets/input_demo.gif -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/assets/layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/vendor/github.com/mhucka/PyWebIO/docs/assets/layout.png -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/assets/output_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/vendor/github.com/mhucka/PyWebIO/docs/assets/output_demo.gif -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/assets/put_table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/vendor/github.com/mhucka/PyWebIO/docs/assets/put_table.png -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/assets/table_onclick.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/vendor/github.com/mhucka/PyWebIO/docs/assets/table_onclick.gif -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/assets/table_onclick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/vendor/github.com/mhucka/PyWebIO/docs/assets/table_onclick.png -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/demos.rst: -------------------------------------------------------------------------------- 1 | 示例Demos 2 | ========== 3 | 4 | 基本demo 5 | ------------ 6 | 使用PyWebIO编写的示例应用 7 | 8 | .. automodule:: demos 9 | 10 | 数据可视化demo 11 | ----------------- 12 | PyWebIO支持使用第三方库进行数据可视化,详情见 :ref:`使用PyWebIO进行数据可视化 ` -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/exceptions.rst: -------------------------------------------------------------------------------- 1 | ``pywebio.exceptions`` 2 | ==================================================== 3 | 4 | .. automodule:: pywebio.exceptions 5 | :members: 6 | 7 | 8 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/input.rst: -------------------------------------------------------------------------------- 1 | ``pywebio.input`` --- 输入模块 2 | ==================================================== 3 | 4 | .. automodule:: pywebio.input 5 | :members: 6 | 7 | 8 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/libraries_support.rst: -------------------------------------------------------------------------------- 1 | 第三方库生态 2 | ============== 3 | 4 | .. _visualization: 5 | 6 | 数据可视化 7 | ------------- 8 | PyWebIO支持使用第三方库进行数据可视化 9 | 10 | Bokeh 11 | ^^^^^^^^^^^^^^^^^^^^^^ 12 | `Bokeh `_ 是一个支持创建实时交互的数据可视化库。 13 | 14 | 在 PyWebIO 会话中调用 ``bokeh.io.output_notebook(notebook_type='pywebio')`` 来设置Bokeh输出到PyWebIO:: 15 | 16 | from bokeh.io import output_notebook 17 | from bokeh.io import show 18 | 19 | output_notebook(notebook_type='pywebio') 20 | fig = figure(...) 21 | ... 22 | show(fig) 23 | 24 | 相应demo见 :charts_demo_host:`bokeh demo ` 25 | 26 | 除了创建普通图表,Bokeh还可以通过启动Bokeh server来显示Bokeh app,Bokeh app支持向图表的添加按钮、输入框等交互组件,并向组件注册Python回调,从而创建可以与Python代码交互的图表。 27 | 28 | 在PyWebIO中,你也可以使用 ``bokeh.io.show()`` 来显示一个Bokeh App,代码示例见 `bokeh_app.py `_。 29 | 30 | .. image:: https://cdn.jsdelivr.net/gh/wang0618/pywebio-chart-gallery@master/assets/bokeh.png 31 | 32 | pyecharts 33 | ^^^^^^^^^^^^^^^^^^^^^^ 34 | `pyecharts `_ 是一个使用Python创建 `Echarts `_ 可视化图表的库。 35 | 36 | 在 PyWebIO 中使用 `put_html() ` 可以输出 pyecharts 库创建的图表:: 37 | 38 | # chart 为 pyecharts 的图表实例 39 | pywebio.output.put_html(chart.render_notebook()) 40 | 41 | 相应demo见 :charts_demo_host:`pyecharts demo ` 42 | 43 | .. only:: not latex 44 | 45 | .. image:: https://cdn.jsdelivr.net/gh/wang0618/pywebio-chart-gallery@master/assets/pyecharts.gif 46 | 47 | plotly 48 | ^^^^^^^^^^^^^^^^^^^^^^ 49 | `plotly.py `_ 是一个非常流行的Python数据可视化库,可以生成高质量的交互式图表。 50 | 51 | PyWebIO 支持输出使用 plotly 库创建的图表。使用方式为在PyWebIO会话中调用:: 52 | 53 | # fig 为 plotly 的图表实例 54 | html = fig.to_html(include_plotlyjs="require", full_html=False) 55 | pywebio.output.put_html(html) 56 | 57 | 相应demo见 :charts_demo_host:`plotly demo ` 58 | 59 | .. image:: https://cdn.jsdelivr.net/gh/wang0618/pywebio-chart-gallery@master/assets/plotly.png 60 | 61 | cutecharts.py 62 | ^^^^^^^^^^^^^^^^^^^^^^ 63 | 64 | `cutecharts.py `_ 是一个可以创建具有卡通风格的可视化图表的python库。 65 | 底层使用了 `chart.xkcd `_ Javascript库。 66 | 67 | 在 PyWebIO 中使用 `put_html() ` 可以输出 cutecharts.py 库创建的图表:: 68 | 69 | # chart 为 cutecharts 的图表实例 70 | pywebio.output.put_html(chart.render_notebook()) 71 | 72 | 相应demo见 :charts_demo_host:`cutecharts demo ` 73 | 74 | .. image:: https://cdn.jsdelivr.net/gh/wang0618/pywebio-chart-gallery@master/assets/cutecharts.png 75 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/misc.rst: -------------------------------------------------------------------------------- 1 | 其他 2 | ============ 3 | 4 | .. _codemirror_options: 5 | 6 | 常用的Codemirror选项 7 | -------------------- 8 | 9 | * ``mode`` (str): 代码语言。支持的语言有:https://codemirror.net/mode/index.html 10 | * ``theme`` (str): 编辑器主题。可使用的主题:https://codemirror.net/demo/theme.html 11 | * ``lineNumbers`` (bool): 是否显示行号 12 | * ``indentUnit`` (int): 缩进使用的空格数 13 | * ``tabSize`` (int): 制表符宽度 14 | * ``lineWrapping`` (bool): 是否换行以显示长行 15 | 16 | 完整的Codemirror选项请见 https://codemirror.net/doc/manual.html#config 17 | 18 | .. _nginx_ws_config: 19 | 20 | Nginx WebSocket配置示例 21 | ----------------------- 22 | 23 | 假设后端服务器运行在 ``localhost:5000`` 地址,并将PyWebIO的后端接口绑定到 ``/tool`` 路径上,则通过Nginx访问PyWebIO服务的配置如下:: 24 | 25 | map $http_upgrade $connection_upgrade { 26 | default upgrade; 27 | '' close; 28 | } 29 | 30 | server { 31 | listen 80; 32 | 33 | location / { 34 | alias /path/to/pywebio/static/dir/; 35 | } 36 | location /tool { 37 | proxy_read_timeout 300s; 38 | proxy_send_timeout 300s; 39 | proxy_http_version 1.1; 40 | proxy_set_header Upgrade $http_upgrade; 41 | proxy_set_header Connection $connection_upgrade; 42 | proxy_pass http://localhost:5000; 43 | } 44 | } 45 | 46 | 以上配置文件将PyWebIO的静态文件托管到 ``/`` 目录下, 并将 ``/tool`` 反向代理到 ``localhost:5000`` 47 | 48 | PyWebIO的静态文件的路径可使用命令 ``python3 -c "import pywebio; print(pywebio.STATIC_PATH)"`` 获得,你也可以将静态文件复制到其他目录下:: 49 | 50 | cp -r `python3 -c "import pywebio; print(pywebio.STATIC_PATH)"` ~/web 51 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/output.rst: -------------------------------------------------------------------------------- 1 | ``pywebio.output`` --- 输出模块 2 | ==================================================== 3 | 4 | .. automodule:: pywebio.output 5 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/platform.rst: -------------------------------------------------------------------------------- 1 | ``pywebio.platform`` --- Web框架支持 2 | =============================================================== 3 | 4 | .. automodule:: pywebio.platform 5 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/releases.rst: -------------------------------------------------------------------------------- 1 | Release notes 2 | ============= 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | releases/v1.1.0 8 | releases/v1.0.0 9 | releases/v0.3.0 10 | releases/v0.2.0 11 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/releases/v0.2.0.rst: -------------------------------------------------------------------------------- 1 | What's new in PyWebIO 0.2 2 | ========================== 3 | 4 | 2020 4/30 5 | ---------- 6 | 7 | Highlights 8 | ^^^^^^^^^^ 9 | 10 | * 支持与Django、aiohttp Web框架整合 11 | * 支持使用 plotly、pyecharts 等第三方库进行数据可视化 12 | * 与Web框架整合时支持同时使用基于线程和协程的会话实现 13 | * 添加 `defer_call() ` 、 `hold() ` 会话控制函数 14 | * 添加 `put_image() ` 输出图像、 `remove(anchor) ` 移除内容 15 | * 加入动画提升UI体验 16 | * 添加测试用例,构建CI工作流 17 | 18 | Detailed changes by module 19 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 20 | 21 | UI 22 | ~~~~~~~~~~~~~~ 23 | 24 | * 添加元素显示动画 25 | * 页面底部添加footer 26 | 27 | `pywebio.input` 28 | ~~~~~~~~~~~~~~~~ 29 | 30 | * `input_group() ` 添加 ``cancelable`` 参数来允许用户取消输入 31 | * `actions() ` 函数 ``button`` 参数支持 ``reset`` 和 ``cancel`` 按钮类型 32 | 33 | `pywebio.output` 34 | ~~~~~~~~~~~~~~~~ 35 | 36 | * 输出函数使用 ``anchor`` 参数指定输出锚点时,若锚点已经存在,则将锚点处的内容替换为当前内容。 37 | * `clear_range() ` 添加添加锚点存在检查 38 | * `scroll_to(anchor, position) ` 添加 ``position`` 参数精细化控制滚动位置 39 | 40 | `pywebio.platform` 41 | ~~~~~~~~~~~~~~~~~~~ 42 | 43 | * `start_server` 和 `webio_view` 、 `webio_handle` 添加跨域支持 44 | 45 | `pywebio.session` 46 | ~~~~~~~~~~~~~~~~~~~ 47 | 48 | * Session 关闭时,清理更彻底:任何还在进行的PyWebIO调用都会抛出 ``SessionClosedException`` 异常 49 | * fix: Session 对象构造函数无法识别 ``functools.partial`` 处理的任务函数 50 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/releases/v0.3.0.rst: -------------------------------------------------------------------------------- 1 | What's new in PyWebIO 0.3 2 | ========================== 3 | 4 | 2020 5/13 5 | ---------- 6 | 7 | Highlights 8 | ^^^^^^^^^^ 9 | 10 | * 支持输出 bokeh 数据可视化图表, :ref:`文档 ` 11 | * 添加 :func:`session.get_info() ` 获取会话相关信息 12 | * 前端js代码迁移typescript 13 | * `output.put_table() ` 支持跨行/列单元格, 单元格内容支持使用 ``put_xxx`` 类输出函数 14 | 15 | Detailed changes by module 16 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 17 | 18 | UI 19 | ~~~~~~~~~~~~~~ 20 | 21 | * 当与服务器连接断开时,点击前端的交互式按钮会报错提示。 22 | 23 | 24 | `pywebio.output` 25 | ~~~~~~~~~~~~~~~~ 26 | 27 | * 锚点名字支持使用空格 28 | * 弃用 `table_cell_buttons() ` 29 | 30 | 31 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/releases/v1.0.0.rst: -------------------------------------------------------------------------------- 1 | What's new in PyWebIO 1.0 2 | ========================== 3 | 4 | 2021 1/17 5 | ---------- 6 | 7 | 经过快一年的开发,PyWebIO 1.0 终于完成了。与上一版本 v0.3 相比有非常多的变化: 8 | 9 | Highlights 10 | ^^^^^^^^^^^ 11 | * ``start_server`` 对多任务函数的支持,PyWebIO应用可以包含多个任务函数,并提供了 `go_app() ` 用于任务函数之间的跳转 12 | * 不再使用基于锚点的输出控制模型,改用基于Scope的模型 13 | * 添加布局支持( `put_grid() ` , `put_row() ` , `put_column() ` )和自定义样式支持( `style() ` ) 14 | * 添加新的输出函数: `toast() ` , `popup() ` , `put_widget() ` , 15 | `put_collapse() ` , `put_link() ` , `put_scrollable() ` , 16 | `put_loading() ` , `put_processbar() ` 17 | * 添加 `span() ` , `output() ` 输出控制函数 18 | * 添加JS执行函数: `run_js() ` , `eval_js() ` 19 | * 更新UI: 显示输入时,使用浮动式输入框;发生未捕获异常时,前端使用console日志记录异常 20 | 21 | Backwards-incompatible changes 22 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 23 | * 不再使用基于锚点的输出控制模型 24 | * 不支持固定高度的输出区,移除 `pywebio.output.set_output_fixed_height()` 25 | * 移除 `pywebio.output.set_title()` , `pywebio.output.set_auto_scroll_bottom()`,改用 `pywebio.session.set_env()` 进行控制 26 | * 移除 `pywebio.output.table_cell_buttons()` ,使用 `pywebio.output.put_buttons()` 替代 27 | 28 | Detailed changes by module 29 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 30 | * `input() ` 支持 ``action`` 参数动态设置输入项的值 31 | * `file_upload() ` 支持多文件上传,支持限制上传文件大小,添加上传进度显示 32 | * `put_buttons() ` 支持指定按钮颜色 33 | * `put_widget() ` 、 `popup() ` 、 `put_table() ` 将字符串内容不再视作Html,而是作为纯文本 34 | * `put_text() ` 支持输出多个对象 35 | * `put_image() ` 支持使用Url指定图片 36 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/releases/v1.1.0.rst: -------------------------------------------------------------------------------- 1 | What's new in PyWebIO 1.1 2 | ========================== 3 | 4 | 2021 2/7 5 | ---------- 6 | 7 | 距离写下PyWebIO的第一行代码过去已经整整一年了🎂 ,2020年发生了太多的事情,但对我来说又多了一份特殊的意义。新的一年继续努力💪 ,将PyWebIO做得越来越好。 8 | 9 | Highlights 10 | ^^^^^^^^^^^ 11 | * 添加安全性支持: `put_html() `, `put_markdown() ` 中支持使用 ``sanitize`` 参数开启防 XSS 攻击 12 | * UI国际化支持 13 | * 添加SEO支持: 通过任务函数的注释或 `pywebio.platform.seo()` 来设置SEO信息 14 | * CDN支持,Web框架整合更加方便,仅需引入一条路由即可 15 | * 应用访问速度提升,不再使用探测请求的方式确定通信协议 16 | 17 | Backwards-incompatible changes 18 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 19 | * 移除使用 django 和 flask 框架 `start_server()` 中的 `disable_asyncio` 参数 20 | * 废弃 `pywebio.session.data()` ,使用 `pywebio.session.local` 作为会话本地状态存储对象 21 | * 整合到Web框架的应用,访问地址发生变化,参见 :ref:`Web框架整合文档 ` 22 | * `put_scrollable() ` 废弃 `max_height` 参数,使用 `height` 替代 23 | 24 | Detailed changes 25 | ^^^^^^^^^^^^^^^^^ 26 | * `put_code() ` 支持使用 `rows` 参数限制最大显示行数 27 | * `put_scrollable() ` 支持使用 `keep_bottom` 参数设定自动滚动到底部 28 | * `put_markdown() ` 支持配置Markdown解析参数 29 | * 为 `put_code() `, `put_image() `, `put_link() `, `put_row() `, `put_grid() ` 中的参数添加转义 30 | * `output() ` 的 ``reset()``, ``append()``, ``insert()`` 方法接受字符串作为输出内容 31 | * 修复: `file_upload() ` 的 `max_size` and `max_total_size` 参数解析错误 32 | * 修复: py3.6自动打开浏览器失败 33 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/session.rst: -------------------------------------------------------------------------------- 1 | ``pywebio.session`` --- 会话相关 2 | ==================================================== 3 | 4 | .. automodule:: pywebio.session 5 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/static/pywebio.css: -------------------------------------------------------------------------------- 1 | /* Tabs */ 2 | 3 | .ui.menu { 4 | font-family: Helvetica; 5 | min-height: 0; 6 | } 7 | 8 | .ui.tabular.menu .item { 9 | padding: 9px 1em; 10 | } 11 | 12 | .ui.menu .item { 13 | padding: 0; 14 | } 15 | 16 | .sphinx-tabs { 17 | margin-bottom: 1em; 18 | } 19 | 20 | /* Table cell */ 21 | td .line-block { 22 | margin-bottom: 0 !important; 23 | } 24 | 25 | .rst-content img { 26 | box-shadow: 0px 4px 20px 0px rgba(0, 0, 0, .35); 27 | -moz-box-shadow: 0px 4px 20px 0px rgba(0, 0, 0, .35); 28 | -webkit-box-shadow: 0px 4px 20px 0px rgba(0, 0, 0, .35); 29 | } 30 | 31 | #hello-world img { 32 | box-shadow: none; 33 | -moz-box-shadow: none; 34 | -webkit-box-shadow: none; 35 | } 36 | 37 | .demo-cb .highlight{ 38 | position: relative; 39 | } 40 | 41 | .demo-cb .viewcode-back{ 42 | position: absolute; 43 | right: 10px; 44 | top: 4px; 45 | padding-left: 0px; 46 | background-color: #f8f8f8; 47 | } -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/docs/static/pywebio.js: -------------------------------------------------------------------------------- 1 | let DEMO_URL; 2 | if (localStorage.getItem('pywebio_doc_demo_url')) 3 | DEMO_URL = localStorage.getItem('pywebio_doc_demo_url'); 4 | else 5 | DEMO_URL = 'http://pywebio-demos.demo.wangweimin.site/doc_demo'; 6 | 7 | var parseHTML = function (str) { 8 | let tmp = document.implementation.createHTMLDocument(); 9 | tmp.body.innerHTML = str; 10 | return tmp.body.children; 11 | }; 12 | 13 | function ready(fn) { 14 | if (document.readyState != 'loading') { 15 | fn(); 16 | } else { 17 | document.addEventListener('DOMContentLoaded', fn); 18 | } 19 | } 20 | 21 | let demo_url = new URL(DEMO_URL); 22 | 23 | ready(function () { 24 | let codes = document.querySelectorAll('.demo-cb'); 25 | for (let c of codes) { 26 | let id = c.getAttribute('id'); 27 | let ele = c.querySelector('.highlight'); 28 | demo_url.searchParams.set("app", id); 29 | let node = parseHTML(`[Demo]`)[0]; 30 | ele.insertBefore(node, ele.firstChild); 31 | } 32 | }); 33 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/lgtm.yml: -------------------------------------------------------------------------------- 1 | ########################################################################################## 2 | # Use the `path_classifiers` block to define changes to the default classification of # 3 | # files. # 4 | ########################################################################################## 5 | 6 | path_classifiers: 7 | test: 8 | - exclude: / 9 | - test 10 | - exclude: test/util.py 11 | 12 | library: 13 | - pywebio/html 14 | - exclude: pywebio/html/css/app.css 15 | - exclude: pywebio/html/index.html 16 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/pywebio/__init__.py: -------------------------------------------------------------------------------- 1 | from . import input 2 | from . import output 3 | from .__version__ import __author__, __author_email__, __license__ 4 | from .__version__ import __description__, __url__, __version__ 5 | from .exceptions import SessionException, SessionClosedException, SessionNotFoundException 6 | from .platform import start_server 7 | from .platform.bokeh import try_install_bokeh_hook 8 | from .session import * 9 | from .utils import STATIC_PATH 10 | 11 | try_install_bokeh_hook() 12 | del try_install_bokeh_hook 13 | 14 | # Set default logging handler to avoid "No handler found" warnings. 15 | import logging 16 | 17 | logging.getLogger(__name__).addHandler(logging.NullHandler()) 18 | 19 | 20 | def enable_debug(level=logging.DEBUG): 21 | """Output PyWebIO logging message to sys.stderr""" 22 | ch = logging.StreamHandler() 23 | ch.setLevel(level) 24 | formatter = logging.Formatter('[%(levelname)s %(asctime)s %(module)s:%(lineno)d %(funcName)s] %(message)s', 25 | datefmt='%y%m%d %H:%M:%S') 26 | ch.setFormatter(formatter) 27 | logger = logging.getLogger(__name__) 28 | logger.handlers = [ch] 29 | logger.setLevel(level) 30 | logger.propagate = False 31 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/pywebio/__version__.py: -------------------------------------------------------------------------------- 1 | __package__ = 'pywebio' 2 | __description__ = 'Write interactive web app in script way.' 3 | __url__ = 'https://pywebio.readthedocs.io' 4 | __version__ = "1.1.0" 5 | __version_info__ = (1, 1, 0, 0) 6 | __author__ = 'WangWeimin' 7 | __author_email__ = 'wang0.618@qq.com' 8 | __license__ = 'MIT' -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/pywebio/exceptions.py: -------------------------------------------------------------------------------- 1 | """ 2 | pywebio.exceptions 3 | ~~~~~~~~~~~~~~~~~~~ 4 | 5 | This module contains the set of PyWebIO's exceptions. 6 | """ 7 | 8 | 9 | class SessionException(Exception): 10 | """PyWebIO会话相关异常的基类""" 11 | 12 | 13 | class SessionClosedException(SessionException): 14 | """会话已经关闭异常""" 15 | 16 | 17 | class SessionNotFoundException(SessionException): 18 | """会话未找到异常""" 19 | 20 | 21 | class PyWebIOWarning(UserWarning): 22 | pass 23 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/pywebio/html/codemirror/active-line.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | var WRAP_CLASS = "CodeMirror-activeline"; 14 | var BACK_CLASS = "CodeMirror-activeline-background"; 15 | var GUTT_CLASS = "CodeMirror-activeline-gutter"; 16 | 17 | CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { 18 | var prev = old == CodeMirror.Init ? false : old; 19 | if (val == prev) return 20 | if (prev) { 21 | cm.off("beforeSelectionChange", selectionChange); 22 | clearActiveLines(cm); 23 | delete cm.state.activeLines; 24 | } 25 | if (val) { 26 | cm.state.activeLines = []; 27 | updateActiveLines(cm, cm.listSelections()); 28 | cm.on("beforeSelectionChange", selectionChange); 29 | } 30 | }); 31 | 32 | function clearActiveLines(cm) { 33 | for (var i = 0; i < cm.state.activeLines.length; i++) { 34 | cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS); 35 | cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS); 36 | cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS); 37 | } 38 | } 39 | 40 | function sameArray(a, b) { 41 | if (a.length != b.length) return false; 42 | for (var i = 0; i < a.length; i++) 43 | if (a[i] != b[i]) return false; 44 | return true; 45 | } 46 | 47 | function updateActiveLines(cm, ranges) { 48 | var active = []; 49 | for (var i = 0; i < ranges.length; i++) { 50 | var range = ranges[i]; 51 | var option = cm.getOption("styleActiveLine"); 52 | if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty()) 53 | continue 54 | var line = cm.getLineHandleVisualStart(range.head.line); 55 | if (active[active.length - 1] != line) active.push(line); 56 | } 57 | if (sameArray(cm.state.activeLines, active)) return; 58 | cm.operation(function() { 59 | clearActiveLines(cm); 60 | for (var i = 0; i < active.length; i++) { 61 | cm.addLineClass(active[i], "wrap", WRAP_CLASS); 62 | cm.addLineClass(active[i], "background", BACK_CLASS); 63 | cm.addLineClass(active[i], "gutter", GUTT_CLASS); 64 | } 65 | cm.state.activeLines = active; 66 | }); 67 | } 68 | 69 | function selectionChange(cm, sel) { 70 | updateActiveLines(cm, sel.ranges); 71 | } 72 | }); 73 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/pywebio/html/codemirror/base16-light.min.css: -------------------------------------------------------------------------------- 1 | .cm-s-base16-light.CodeMirror{background:#f5f5f5;color:#202020}.cm-s-base16-light div.CodeMirror-selected{background:#e0e0e0}.cm-s-base16-light .CodeMirror-line::selection,.cm-s-base16-light .CodeMirror-line>span::selection,.cm-s-base16-light .CodeMirror-line>span>span::selection{background:#e0e0e0}.cm-s-base16-light .CodeMirror-line::-moz-selection,.cm-s-base16-light .CodeMirror-line>span::-moz-selection,.cm-s-base16-light .CodeMirror-line>span>span::-moz-selection{background:#e0e0e0}.cm-s-base16-light .CodeMirror-gutters{background:#f5f5f5;border-right:0}.cm-s-base16-light .CodeMirror-guttermarker{color:#ac4142}.cm-s-base16-light .CodeMirror-guttermarker-subtle{color:#b0b0b0}.cm-s-base16-light .CodeMirror-linenumber{color:#b0b0b0}.cm-s-base16-light .CodeMirror-cursor{border-left:1px solid #505050}.cm-s-base16-light span.cm-comment{color:#8f5536}.cm-s-base16-light span.cm-atom{color:#aa759f}.cm-s-base16-light span.cm-number{color:#aa759f}.cm-s-base16-light span.cm-attribute,.cm-s-base16-light span.cm-property{color:#90a959}.cm-s-base16-light span.cm-keyword{color:#ac4142}.cm-s-base16-light span.cm-string{color:#f4bf75}.cm-s-base16-light span.cm-variable{color:#90a959}.cm-s-base16-light span.cm-variable-2{color:#6a9fb5}.cm-s-base16-light span.cm-def{color:#d28445}.cm-s-base16-light span.cm-bracket{color:#202020}.cm-s-base16-light span.cm-tag{color:#ac4142}.cm-s-base16-light span.cm-link{color:#aa759f}.cm-s-base16-light span.cm-error{background:#ac4142;color:#505050}.cm-s-base16-light .CodeMirror-activeline-background{background:#dddcdc}.cm-s-base16-light .CodeMirror-matchingbracket{color:#f5f5f5!important;background-color:#6a9fb5!important} -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/pywebio/html/codemirror/loadmode.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror"), "cjs"); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], function(CM) { mod(CM, "amd"); }); 9 | else // Plain browser env 10 | mod(CodeMirror, "plain"); 11 | })(function(CodeMirror, env) { 12 | if (!CodeMirror.modeURL) CodeMirror.modeURL = "../mode/%N/%N.js"; 13 | 14 | var loading = {}; 15 | function splitCallback(cont, n) { 16 | var countDown = n; 17 | return function() { if (--countDown == 0) cont(); }; 18 | } 19 | function ensureDeps(mode, cont) { 20 | var deps = CodeMirror.modes[mode].dependencies; 21 | if (!deps) return cont(); 22 | var missing = []; 23 | for (var i = 0; i < deps.length; ++i) { 24 | if (!CodeMirror.modes.hasOwnProperty(deps[i])) 25 | missing.push(deps[i]); 26 | } 27 | if (!missing.length) return cont(); 28 | var split = splitCallback(cont, missing.length); 29 | for (var i = 0; i < missing.length; ++i) 30 | CodeMirror.requireMode(missing[i], split); 31 | } 32 | 33 | CodeMirror.requireMode = function(mode, cont) { 34 | if (typeof mode != "string") mode = mode.name; 35 | if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont); 36 | if (loading.hasOwnProperty(mode)) return loading[mode].push(cont); 37 | 38 | var file = CodeMirror.modeURL.replace(/%N/g, mode); 39 | if (env == "plain") { 40 | var script = document.createElement("script"); 41 | script.src = file; 42 | var others = document.getElementsByTagName("script")[0]; 43 | var list = loading[mode] = [cont]; 44 | CodeMirror.on(script, "load", function() { 45 | ensureDeps(mode, function() { 46 | for (var i = 0; i < list.length; ++i) list[i](); 47 | }); 48 | }); 49 | others.parentNode.insertBefore(script, others); 50 | } else if (env == "cjs") { 51 | require(file); 52 | cont(); 53 | } else if (env == "amd") { 54 | requirejs([file], cont); 55 | } 56 | }; 57 | 58 | CodeMirror.autoLoadMode = function(instance, mode) { 59 | if (!CodeMirror.modes.hasOwnProperty(mode)) 60 | CodeMirror.requireMode(mode, function() { 61 | instance.setOption("mode", instance.getOption("mode")); 62 | }); 63 | }; 64 | }); 65 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/pywebio/html/css/toastify.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Minified by jsDelivr using clean-css v4.2.3. 3 | * Original file: /npm/toastify-js@1.9.3/src/toastify.css 4 | * 5 | * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files 6 | */ 7 | /*! 8 | * Toastify js 1.9.3 9 | * https://github.com/apvarun/toastify-js 10 | * @license MIT licensed 11 | * 12 | * Copyright (C) 2018 Varun A P 13 | */ 14 | .toastify{padding:12px 20px;color:#fff;display:inline-block;box-shadow:0 3px 6px -1px rgba(0,0,0,.12),0 10px 36px -4px rgba(77,96,232,.3);background:-webkit-linear-gradient(315deg,#73a5ff,#5477f5);background:linear-gradient(135deg,#73a5ff,#5477f5);position:fixed;opacity:0;transition:all .4s cubic-bezier(.215,.61,.355,1);border-radius:2px;cursor:pointer;text-decoration:none;max-width:calc(50% - 20px);z-index:2147483647}.toastify.on{opacity:1}.toast-close{opacity:.4;padding:0 5px}.toastify-right{right:15px}.toastify-left{left:15px}.toastify-top{top:-150px}.toastify-bottom{bottom:-150px}.toastify-rounded{border-radius:25px}.toastify-avatar{width:1.5em;height:1.5em;margin:-7px 5px;border-radius:2px}.toastify-center{margin-left:auto;margin-right:auto;left:0;right:0;max-width:fit-content;max-width:-moz-fit-content}@media only screen and (max-width:360px){.toastify-left,.toastify-right{margin-left:auto;margin-right:auto;left:0;right:0;max-width:fit-content}} 15 | /*# sourceMappingURL=/sm/40f738e33ed5dbe7907b48c3be4b63e977eab6cb49c8df4f76f3edc3f1f2fb0d.map */ -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/pywebio/html/image/favicon_closed_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/vendor/github.com/mhucka/PyWebIO/pywebio/html/image/favicon_closed_16.png -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/pywebio/html/image/favicon_closed_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/vendor/github.com/mhucka/PyWebIO/pywebio/html/image/favicon_closed_32.png -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/pywebio/html/image/favicon_open_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/vendor/github.com/mhucka/PyWebIO/pywebio/html/image/favicon_open_16.png -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/pywebio/html/image/favicon_open_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/vendor/github.com/mhucka/PyWebIO/pywebio/html/image/favicon_open_32.png -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/pywebio/html/js/FileSaver.min.js: -------------------------------------------------------------------------------- 1 | (function(a,b){if("function"==typeof define&&define.amd)define([],b);else if("undefined"!=typeof exports)b();else{b(),a.FileSaver={exports:{}}.exports}})(this,function(){"use strict";function b(a,b){return"undefined"==typeof b?b={autoBom:!1}:"object"!=typeof b&&(console.warn("Deprecated: Expected third argument to be a object"),b={autoBom:!b}),b.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(a.type)?new Blob(["\uFEFF",a],{type:a.type}):a}function c(b,c,d){var e=new XMLHttpRequest;e.open("GET",b),e.responseType="blob",e.onload=function(){a(e.response,c,d)},e.onerror=function(){console.error("could not download file")},e.send()}function d(a){var b=new XMLHttpRequest;b.open("HEAD",a,!1);try{b.send()}catch(a){}return 200<=b.status&&299>=b.status}function e(a){try{a.dispatchEvent(new MouseEvent("click"))}catch(c){var b=document.createEvent("MouseEvents");b.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),a.dispatchEvent(b)}}var f="object"==typeof window&&window.window===window?window:"object"==typeof self&&self.self===self?self:"object"==typeof global&&global.global===global?global:void 0,a=f.saveAs||("object"!=typeof window||window!==f?function(){}:"download"in HTMLAnchorElement.prototype?function(b,g,h){var i=f.URL||f.webkitURL,j=document.createElement("a");g=g||b.name||"download",j.download=g,j.rel="noopener","string"==typeof b?(j.href=b,j.origin===location.origin?e(j):d(j.href)?c(b,g,h):e(j,j.target="_blank")):(j.href=i.createObjectURL(b),setTimeout(function(){i.revokeObjectURL(j.href)},4E4),setTimeout(function(){e(j)},0))}:"msSaveOrOpenBlob"in navigator?function(f,g,h){if(g=g||f.name||"download","string"!=typeof f)navigator.msSaveOrOpenBlob(b(f,h),g);else if(d(f))c(f,g,h);else{var i=document.createElement("a");i.href=f,i.target="_blank",setTimeout(function(){e(i)})}}:function(a,b,d,e){if(e=e||open("","_blank"),e&&(e.document.title=e.document.body.innerText="downloading..."),"string"==typeof a)return c(a,b,d);var g="application/octet-stream"===a.type,h=/constructor/i.test(f.HTMLElement)||f.safari,i=/CriOS\/[\d]+/.test(navigator.userAgent);if((i||g&&h)&&"undefined"!=typeof FileReader){var j=new FileReader;j.onloadend=function(){var a=j.result;a=i?a:a.replace(/^data:[^;]*;/,"data:attachment/file;"),e?e.location.href=a:location=a,e=null},j.readAsDataURL(a)}else{var k=f.URL||f.webkitURL,l=k.createObjectURL(a);e?e.location=l:location.href=l,e=null,setTimeout(function(){k.revokeObjectURL(l)},4E4)}});f.saveAs=a.saveAs=a,"undefined"!=typeof module&&(module.exports=a)}); 2 | 3 | //# sourceMappingURL=FileSaver.min.js.map -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/pywebio/html/js/bs-custom-file-input.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * bsCustomFileInput v1.3.4 (https://github.com/Johann-S/bs-custom-file-input) 3 | * Copyright 2018 - 2020 Johann-S 4 | * Licensed under MIT (https://github.com/Johann-S/bs-custom-file-input/blob/master/LICENSE) 5 | */ 6 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).bsCustomFileInput=t()}(this,function(){"use strict";var s={CUSTOMFILE:'.custom-file input[type="file"]',CUSTOMFILELABEL:".custom-file-label",FORM:"form",INPUT:"input"},l=function(e){if(0` 小节 5 | 6 | Tornado相关 7 | -------------- 8 | .. autofunction:: pywebio.platform.tornado.start_server 9 | .. autofunction:: pywebio.platform.tornado.webio_handler 10 | 11 | Flask相关 12 | -------------- 13 | .. autofunction:: pywebio.platform.flask.webio_view 14 | .. autofunction:: pywebio.platform.flask.start_server 15 | 16 | Django相关 17 | -------------- 18 | .. autofunction:: pywebio.platform.django.webio_view 19 | .. autofunction:: pywebio.platform.django.start_server 20 | 21 | aiohttp相关 22 | -------------- 23 | .. autofunction:: pywebio.platform.aiohttp.webio_handler 24 | .. autofunction:: pywebio.platform.aiohttp.start_server 25 | 26 | 其他 27 | -------------- 28 | .. autofunction:: pywebio.platform.seo 29 | .. autofunction:: pywebio.platform.run_event_loop 30 | 31 | """ 32 | 33 | from .httpbased import run_event_loop 34 | from .tornado import start_server 35 | from .utils import seo -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/requirements.txt: -------------------------------------------------------------------------------- 1 | tornado>=5.0 2 | user-agents 3 | 4 | # extra support 5 | flask 6 | django 7 | aiohttp 8 | bokeh 9 | pandas 10 | cutecharts 11 | pyecharts 12 | plotly 13 | Pillow 14 | 15 | # test requirements 16 | selenium==3.* 17 | percy-python-selenium 18 | coverage 19 | 20 | # doc building requirements 21 | sphinx 22 | sphinx-tabs -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/test/.percy.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | snapshot: 3 | widths: [1000] -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/test/1.basic.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | import time 4 | from selenium.webdriver import Chrome 5 | 6 | import pywebio 7 | import template 8 | import util 9 | from pywebio import start_server 10 | from pywebio.input import * 11 | from pywebio.output import * 12 | from pywebio.session import set_env 13 | from pywebio.utils import run_as_function 14 | 15 | 16 | def target(): 17 | set_env(auto_scroll_bottom=True) 18 | template.set_defer_call() 19 | 20 | template.basic_output() 21 | template.background_output() 22 | 23 | run_as_function(template.basic_input()) 24 | actions(buttons=['Continue']) 25 | template.background_input() 26 | 27 | 28 | def test(server_proc: subprocess.Popen, browser: Chrome): 29 | template.test_output(browser, enable_percy=True) 30 | 31 | template.test_input(browser, enable_percy=True) 32 | 33 | time.sleep(1) 34 | template.save_output(browser, '1.basic.html') 35 | 36 | template.test_defer_call() 37 | 38 | 39 | def start_test_server(): 40 | pywebio.enable_debug() 41 | start_server(target, port=8080, host='127.0.0.1', auto_open_webbrowser=False, cdn=False) 42 | 43 | 44 | if __name__ == '__main__': 45 | util.run_test(start_test_server, test) 46 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/test/10.aiohttp_multiple_session_impliment.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | import time 4 | from aiohttp import web 5 | from selenium.webdriver import Chrome 6 | 7 | import pywebio 8 | import template 9 | import util 10 | from pywebio import STATIC_PATH 11 | from pywebio.input import * 12 | from pywebio.platform.aiohttp import static_routes, webio_handler 13 | from pywebio.utils import to_coroutine, run_as_function 14 | 15 | 16 | def target(): 17 | template.basic_output() 18 | template.background_output() 19 | 20 | run_as_function(template.basic_input()) 21 | actions(buttons=['Continue']) 22 | template.background_input() 23 | 24 | 25 | async def async_target(): 26 | template.basic_output() 27 | await template.coro_background_output() 28 | 29 | await to_coroutine(template.basic_input()) 30 | await actions(buttons=['Continue']) 31 | await template.coro_background_input() 32 | 33 | 34 | def test(server_proc: subprocess.Popen, browser: Chrome): 35 | template.test_output(browser) 36 | time.sleep(1) 37 | template.test_input(browser) 38 | time.sleep(1) 39 | template.save_output(browser, '10.aiohttp_multiple_session_impliment_p1.html') 40 | 41 | browser.get('http://localhost:8080/io2?_pywebio_debug=1') 42 | template.test_output(browser) 43 | time.sleep(1) 44 | template.test_input(browser) 45 | 46 | time.sleep(1) 47 | template.save_output(browser, '10.aiohttp_multiple_session_impliment_p2.html') 48 | 49 | 50 | def start_test_server(): 51 | pywebio.enable_debug() 52 | 53 | app = web.Application() 54 | app.add_routes([web.get('/io', webio_handler(target, cdn=False))]) 55 | app.add_routes([web.get('/io2', webio_handler(async_target, cdn=False))]) 56 | app.add_routes(static_routes()) 57 | 58 | web.run_app(app, host='127.0.0.1', port=8080) 59 | 60 | 61 | if __name__ == '__main__': 62 | util.run_test(start_test_server, test, 'http://localhost:8080/io?_pywebio_debug=1') 63 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/test/2.script_mode.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | 4 | import time 5 | from percy import percySnapshot 6 | from selenium.webdriver import Chrome 7 | 8 | import template 9 | import util 10 | from pywebio.input import * 11 | from pywebio.output import * 12 | from pywebio.utils import run_as_function 13 | 14 | 15 | def target(): 16 | template.basic_output() 17 | template.background_output() 18 | 19 | run_as_function(template.basic_input()) 20 | actions(buttons=['Continue']) 21 | template.background_input() 22 | 23 | 24 | def test(server_proc: subprocess.Popen, browser: Chrome): 25 | template.test_output(browser) 26 | 27 | time.sleep(1) 28 | 29 | template.test_input(browser) 30 | 31 | # script mode 下,此时 server 应停止 32 | server_proc.wait(timeout=8) 33 | 34 | time.sleep(1) 35 | template.save_output(browser, '2.script_mode.html', 36 | process_func=lambda i: i.replace('::1', '127.0.0.1')) # because tornado default bind ipv4 and ipv6 in script mode 37 | 38 | 39 | if __name__ == '__main__': 40 | # 设置监听端口,并关闭自动打开浏览器 41 | os.environ["PYWEBIO_SCRIPT_MODE_PORT"] = "8080" 42 | 43 | util.run_test(target, test) 44 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/test/3.django_backend.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | import time 4 | from selenium.webdriver import Chrome 5 | 6 | import pywebio 7 | import template 8 | import util 9 | from pywebio.input import * 10 | from pywebio.platform.django import start_server 11 | from pywebio.utils import run_as_function 12 | 13 | 14 | def target(): 15 | template.basic_output() 16 | template.background_output() 17 | 18 | run_as_function(template.basic_input()) 19 | actions(buttons=['Continue']) 20 | template.background_input() 21 | 22 | 23 | def test(server_proc: subprocess.Popen, browser: Chrome): 24 | template.test_output(browser) 25 | 26 | time.sleep(1) 27 | 28 | template.test_input(browser) 29 | 30 | time.sleep(1) 31 | template.save_output(browser, '3.django_backend.html') 32 | 33 | 34 | def start_test_server(): 35 | pywebio.enable_debug() 36 | 37 | start_server(target, port=8080, host='127.0.0.1', cdn=False) 38 | 39 | 40 | if __name__ == '__main__': 41 | util.run_test(start_test_server, test) 42 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/test/4.flask_backend.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | import time 4 | from selenium.webdriver import Chrome 5 | 6 | import pywebio 7 | import template 8 | import util 9 | from pywebio.input import * 10 | from pywebio.platform.flask import start_server 11 | from pywebio.utils import run_as_function 12 | 13 | 14 | def target(): 15 | template.basic_output() 16 | template.background_output() 17 | 18 | run_as_function(template.basic_input()) 19 | actions(buttons=['Continue']) 20 | template.background_input() 21 | 22 | 23 | def test(server_proc: subprocess.Popen, browser: Chrome): 24 | template.test_output(browser) 25 | 26 | time.sleep(1) 27 | 28 | template.test_input(browser) 29 | 30 | time.sleep(1) 31 | template.save_output(browser, '4.flask_backend.html') 32 | 33 | 34 | def start_test_server(): 35 | pywebio.enable_debug() 36 | start_server(target, port=8080, host='127.0.0.1', cdn=False) 37 | 38 | 39 | if __name__ == '__main__': 40 | util.run_test(start_test_server, test, address='http://localhost:8080?_pywebio_debug=1&_pywebio_http_pull_interval=400') 41 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/test/5.coroutine_based_session.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | import time 4 | from percy import percySnapshot 5 | from selenium.webdriver import Chrome 6 | 7 | import pywebio 8 | import template 9 | import util 10 | from pywebio.input import * 11 | from pywebio.output import * 12 | from pywebio.utils import to_coroutine 13 | from pywebio import start_server 14 | 15 | 16 | async def target(): 17 | template.basic_output() 18 | await template.coro_background_output() 19 | 20 | await to_coroutine(template.basic_input()) 21 | await actions(buttons=['Continue']) 22 | await template.coro_background_input() 23 | 24 | 25 | def test(server_proc: subprocess.Popen, browser: Chrome): 26 | template.test_output(browser) 27 | 28 | time.sleep(1) 29 | 30 | template.test_input(browser) 31 | 32 | time.sleep(1) 33 | template.save_output(browser, '5.coroutine_based_session.html') 34 | 35 | 36 | def start_test_server(): 37 | pywebio.enable_debug() 38 | start_server(target, port=8080, host='127.0.0.1', cdn=False) 39 | 40 | 41 | if __name__ == '__main__': 42 | util.run_test(start_test_server, test) 43 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/test/6.flask_coroutine.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | import time 4 | from selenium.webdriver import Chrome 5 | 6 | import pywebio 7 | import template 8 | import util 9 | from pywebio.input import * 10 | from pywebio.output import * 11 | from pywebio.platform.flask import start_server 12 | from pywebio.utils import to_coroutine 13 | 14 | 15 | async def target(): 16 | template.basic_output() 17 | await template.coro_background_output() 18 | 19 | await to_coroutine(template.basic_input()) 20 | await actions(buttons=['Continue']) 21 | await template.flask_coro_background_input() 22 | 23 | 24 | def test(server_proc: subprocess.Popen, browser: Chrome): 25 | template.test_output(browser) 26 | 27 | time.sleep(1) 28 | 29 | template.test_input(browser) 30 | 31 | time.sleep(1) 32 | template.save_output(browser, '6.flask_coroutine.html') 33 | 34 | 35 | def start_test_server(): 36 | pywebio.enable_debug() 37 | start_server(target, port=8080, host='127.0.0.1', cdn=False) 38 | 39 | 40 | if __name__ == '__main__': 41 | util.run_test(start_test_server, test, address='http://localhost:8080?_pywebio_debug=1&_pywebio_http_pull_interval=400') 42 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/test/7.multiple_session_impliment.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from selenium.webdriver import Chrome 4 | 5 | import pywebio 6 | import template 7 | import util, time 8 | from pywebio.input import * 9 | from pywebio.output import * 10 | from pywebio.utils import to_coroutine, run_as_function 11 | 12 | 13 | def target(): 14 | template.basic_output() 15 | template.background_output() 16 | 17 | run_as_function(template.basic_input()) 18 | actions(buttons=['Continue']) 19 | template.background_input() 20 | 21 | 22 | async def async_target(): 23 | template.basic_output() 24 | await template.coro_background_output() 25 | 26 | await to_coroutine(template.basic_input()) 27 | await actions(buttons=['Continue']) 28 | await template.coro_background_input() 29 | 30 | 31 | def test(server_proc: subprocess.Popen, browser: Chrome): 32 | template.test_output(browser) 33 | time.sleep(1) 34 | template.test_input(browser) 35 | time.sleep(1) 36 | template.save_output(browser, '7.multiple_session_impliment_p1.html') 37 | 38 | browser.get('http://localhost:8080/io2?_pywebio_debug=1') 39 | template.test_output(browser) 40 | time.sleep(1) 41 | template.test_input(browser) 42 | 43 | time.sleep(1) 44 | template.save_output(browser, '7.multiple_session_impliment_p2.html') 45 | 46 | 47 | def start_test_server(): 48 | pywebio.enable_debug() 49 | 50 | import tornado.ioloop 51 | import tornado.web 52 | from pywebio.platform.tornado import webio_handler 53 | from pywebio import STATIC_PATH 54 | 55 | application = tornado.web.Application([ 56 | (r"/", webio_handler(async_target, cdn=False)), 57 | (r"/io2", webio_handler(target, cdn=False)), 58 | (r"/(.*)", tornado.web.StaticFileHandler, 59 | {"path": STATIC_PATH, 'default_filename': 'index.html'}) 60 | ]) 61 | application.listen(port=8080, address='127.0.0.1') 62 | tornado.ioloop.IOLoop.current().start() 63 | 64 | 65 | if __name__ == '__main__': 66 | util.run_test(start_test_server, test) 67 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/test/8.flask_multiple_session_impliment.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from selenium.webdriver import Chrome 4 | 5 | import pywebio 6 | import template 7 | import time 8 | import util 9 | from pywebio.input import * 10 | from pywebio.output import * 11 | from pywebio.utils import to_coroutine, run_as_function 12 | 13 | 14 | def target(): 15 | template.basic_output() 16 | template.background_output() 17 | 18 | run_as_function(template.basic_input()) 19 | actions(buttons=['Continue']) 20 | template.background_input() 21 | 22 | 23 | async def async_target(): 24 | template.basic_output() 25 | await template.coro_background_output() 26 | 27 | await to_coroutine(template.basic_input()) 28 | await actions(buttons=['Continue']) 29 | await template.coro_background_input() 30 | 31 | 32 | def test(server_proc: subprocess.Popen, browser: Chrome): 33 | template.test_output(browser) 34 | time.sleep(1) 35 | template.test_input(browser) 36 | time.sleep(1) 37 | template.save_output(browser, '8.flask_multiple_session_impliment_p1.html') 38 | 39 | browser.get('http://localhost:8080/io2?_pywebio_debug=1&_pywebio_http_pull_interval=400') 40 | template.test_output(browser) 41 | time.sleep(1) 42 | template.test_input(browser) 43 | 44 | time.sleep(1) 45 | template.save_output(browser, '8.flask_multiple_session_impliment_p2.html') 46 | 47 | 48 | def start_test_server(): 49 | pywebio.enable_debug() 50 | from flask import Flask, send_from_directory 51 | from pywebio.platform.flask import webio_view, run_event_loop 52 | from pywebio import STATIC_PATH 53 | import threading 54 | import logging 55 | 56 | app = Flask(__name__) 57 | app.add_url_rule('/io', 'webio_view', webio_view(target, cdn=False), methods=['GET', 'POST', 'OPTIONS']) 58 | app.add_url_rule('/io2', 'webio_view_async_target', webio_view(async_target, cdn=False), methods=['GET', 'POST', 'OPTIONS']) 59 | 60 | @app.route('/') 61 | @app.route('/') 62 | def serve_static_file(static_file='index.html'): 63 | return send_from_directory(STATIC_PATH, static_file) 64 | 65 | threading.Thread(target=run_event_loop, daemon=True).start() 66 | 67 | logging.getLogger('werkzeug').setLevel(logging.WARNING) 68 | 69 | app.run(port=8080, host='127.0.0.1') 70 | 71 | 72 | if __name__ == '__main__': 73 | util.run_test(start_test_server, test, address='http://localhost:8080/io?_pywebio_debug=1&_pywebio_http_pull_interval=400') 74 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/test/9.aiohttp_backend.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | import time 4 | from selenium.webdriver import Chrome 5 | 6 | import pywebio 7 | import template 8 | import util 9 | from pywebio.input import * 10 | from pywebio.platform.aiohttp import start_server 11 | from pywebio.utils import run_as_function 12 | 13 | 14 | def target(): 15 | template.basic_output() 16 | template.background_output() 17 | 18 | run_as_function(template.basic_input()) 19 | actions(buttons=['Continue']) 20 | template.background_input() 21 | 22 | 23 | def test(server_proc: subprocess.Popen, browser: Chrome): 24 | template.test_output(browser) 25 | 26 | time.sleep(1) 27 | 28 | template.test_input(browser) 29 | 30 | time.sleep(1) 31 | template.save_output(browser, '9.aiohttp_backend.html') 32 | 33 | 34 | def start_test_server(): 35 | pywebio.enable_debug() 36 | 37 | start_server(target, port=8080, host='127.0.0.1', cdn=False) 38 | 39 | 40 | if __name__ == '__main__': 41 | util.run_test(start_test_server, test) 42 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/test/Readme.md: -------------------------------------------------------------------------------- 1 | ## Test 2 | 使用 selenium 进行 + percy 进行测试。 3 | 4 | 测试的原理为使用selenium打开编写的PyWebIO测试服务,在页面上进行模拟操作, 5 | 将一些时刻的网页快照使用percy进行保存,percy可以比较不同提交之间的相同页面的区别。 6 | 7 | ### 编写测试用例 8 | // todo 9 | 10 | ### 运行测试用例 11 | 12 | ```bash 13 | pip3 install -e ".[dev]" 14 | npm install -D @percy/agent 15 | export PERCY_TOKEN=[projects-token] 16 | 17 | npx percy exec -- python3 1.basic_output.py auto 18 | ``` 19 | 20 | 21 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/test/assets/helloworld.txt: -------------------------------------------------------------------------------- 1 | hello world -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/test/assets/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caltechlibrary/foliage/eaf2f39b77059d656cb7a68a2bc765c23e109b2b/vendor/github.com/mhucka/PyWebIO/test/assets/img.png -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/test/output_diff.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | 3 | 4 | def diff_file(file_a, file_b): 5 | if open(file_a).read() != open(file_b).read(): 6 | cmd = 'diff %s %s' % (file_a, file_b) 7 | print('#' * 4, cmd, '#' * 4) 8 | os.system(cmd) 9 | return True 10 | return False 11 | 12 | 13 | def diff_dir(dir): 14 | files = [os.path.join(dir, f) for f in os.listdir(dir) if os.path.isfile(os.path.join(dir, f))] 15 | has_diff = any(diff_file(files[idx - 1], files[idx]) for idx in range(1, len(files))) 16 | if has_diff: 17 | sys.exit(1) 18 | 19 | 20 | if __name__ == '__main__': 21 | here_dir = os.path.dirname(os.path.abspath(__file__)) 22 | diff_dir(os.path.join(here_dir, 'output')) 23 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/test/run_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o xtrace 4 | 5 | mkdir output 6 | 7 | exit_code=0 8 | 9 | for file in ./[0-9]*.*.py 10 | do 11 | python3 "$file" auto || exit_code=1 12 | done 13 | 14 | python3 output_diff.py && exit "$exit_code" -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/test/util.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import signal 3 | import subprocess 4 | import sys 5 | import threading 6 | from functools import partial 7 | from urllib.parse import urlparse 8 | 9 | from selenium import webdriver 10 | 11 | from pywebio import STATIC_PATH 12 | from pywebio.utils import wait_host_port 13 | 14 | default_chrome_options = webdriver.ChromeOptions() 15 | default_chrome_options.add_argument('--no-sandbox') 16 | default_chrome_options.add_argument('--disable-extensions') 17 | default_chrome_options.add_argument('--disable-dev-shm-usage') 18 | default_chrome_options.add_argument('--disable-setuid-sandbox') 19 | 20 | USAGE = """ 21 | python {name} 22 | 启动PyWebIO服务器 23 | 24 | python {name} auto 25 | 使用无头浏览器进行自动化测试,并使用coverage检测代码覆盖率 26 | 27 | python {name} debug 28 | 使用带界面的浏览器自动化测试 29 | """ 30 | 31 | 32 | def run_test(server_func, test_func, address='http://localhost:8080?_pywebio_debug=1', chrome_options=None): 33 | """ 34 | :param server_func: 启动PyWebIO服务器的函数 35 | :param test_func: 测试的函数。人工测试时不会被运行 (server_proc, browser) 36 | :param port: 启动的PyWebIO服务器的端口 37 | """ 38 | if len(sys.argv) not in (1, 2) or (len(sys.argv) == 2 and sys.argv[-1] not in ('server', 'auto', 'debug')): 39 | print(USAGE.format(name=sys.argv[0])) 40 | return 41 | 42 | if len(sys.argv) != 2: 43 | try: 44 | server_func() 45 | except KeyboardInterrupt: 46 | pass 47 | sys.exit() 48 | 49 | if chrome_options is None: 50 | chrome_options = default_chrome_options 51 | 52 | if sys.argv[-1] == 'auto': 53 | default_chrome_options.add_argument('--headless') 54 | proc = subprocess.Popen(['coverage', 'run', '--source', 'pywebio', '--append', 55 | sys.argv[0]], stdout=sys.stdout, stderr=subprocess.STDOUT, text=True) 56 | elif sys.argv[-1] == 'debug': 57 | proc = subprocess.Popen(['python3', sys.argv[0]], stdout=sys.stdout, stderr=subprocess.STDOUT, text=True) 58 | 59 | browser = None 60 | try: 61 | browser = webdriver.Chrome(chrome_options=chrome_options) 62 | browser.set_window_size(1000, 900) 63 | port_str = urlparse(address).netloc.split(':', 1)[-1] or '80' 64 | asyncio.run(wait_host_port('localhost', int(port_str))) 65 | browser.get(address) 66 | browser.implicitly_wait(10) 67 | test_func(proc, browser) 68 | finally: 69 | if browser: 70 | if sys.argv[-1] == 'debug': 71 | input('press ENTER to exit') 72 | 73 | browser.quit() 74 | 75 | # 不要使用 proc.terminate() ,因为coverage会无法保存分析数据 76 | proc.send_signal(signal.SIGINT) 77 | print("Closed browser and PyWebIO server") 78 | 79 | 80 | def start_static_server(port=5000): 81 | from http.server import SimpleHTTPRequestHandler, test 82 | 83 | handler_class = partial(SimpleHTTPRequestHandler, directory=STATIC_PATH) 84 | threading.Thread(target=test, kwargs=dict(HandlerClass=handler_class, port=port, bind='localhost'), daemon=True).start() 85 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/tools/build_dev_version.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import os 3 | import re 4 | 5 | proj_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 6 | 7 | version = datetime.datetime.now().strftime('.dev%y%m%d%H%M') 8 | version_path = os.path.join(proj_dir, 'pywebio', '__version__.py') 9 | 10 | content = open(version_path).read() 11 | new_content = re.sub(r'__version__ = "(.*)?"', r'__version__ = "\g<1>%s"' % version, content) 12 | new_content += '\n__commit_hash__ = %r' % os.environ.get('GITHUB_SHA', '')[:8] 13 | open(version_path, 'w').write(new_content) 14 | 15 | about = {} 16 | exec(new_content, about) 17 | print(about['__version__']) 18 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/webiojs/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | out 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and not Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | 108 | # Stores VSCode versions used for testing VSCode extensions 109 | .vscode-test 110 | 111 | # yarn v2 112 | 113 | .yarn/cache 114 | .yarn/unplugged 115 | .yarn/build-state.yml 116 | .pnp.* 117 | 118 | .idea -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/webiojs/README.md: -------------------------------------------------------------------------------- 1 | # PyWebIO JS library 2 | 3 | ## Build 4 | 5 | ```bash 6 | npm install 7 | DEV=1 gulp 8 | ``` 9 | 10 | ## Use built js 11 | 12 | ```bash 13 | cp dist/pywebio.min.* ../pywebio/html/js 14 | ``` -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/webiojs/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var browserify = require('browserify'); 3 | var source = require('vinyl-source-stream'); 4 | var tsify = require('tsify'); 5 | var sourcemaps = require('gulp-sourcemaps'); 6 | var buffer = require('vinyl-buffer'); 7 | var uglify = require('gulp-uglify-es').default; 8 | 9 | 10 | gulp.task('default', function () { 11 | return browserify({ 12 | basedir: '.', 13 | debug: true, 14 | entries: ['src/main.ts'], 15 | cache: {}, 16 | packageCache: {} 17 | }) 18 | .plugin(tsify) 19 | .transform('babelify', { 20 | presets: ['es2015'], 21 | extensions: ['.ts'] 22 | }) 23 | .bundle() 24 | .pipe(source('pywebio.min.js')) 25 | .pipe(buffer()) 26 | .pipe(sourcemaps.init({loadMaps: true})) 27 | .pipe(uglify()) 28 | .pipe(sourcemaps.write('./',{addComment: !!process.env.DEV})) 29 | .pipe(gulp.dest('dist')); 30 | }); -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/webiojs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webiojs", 3 | "version": "0.2.0", 4 | "description": "", 5 | "main": "src/main.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "@types/jquery": "^3.3.38", 13 | "@types/marked": "^1.2.2", 14 | "babel-core": "^6.26.3", 15 | "babel-preset-es2015": "^6.24.1", 16 | "babelify": "^8.0.0", 17 | "browserify": "^16.5.1", 18 | "fancy-log": "^1.3.3", 19 | "gulp": "^4.0.0", 20 | "gulp-sourcemaps": "^2.6.5", 21 | "gulp-typescript": "^6.0.0-alpha.1", 22 | "gulp-uglify": "^3.0.2", 23 | "gulp-uglify-es": "^2.0.0", 24 | "highlight.js": "^10.5.0", 25 | "marked": "^1.2.8", 26 | "tsify": "^4.0.1", 27 | "typescript": "^3.8.3", 28 | "vinyl-buffer": "^1.0.1", 29 | "vinyl-source-stream": "^2.0.0", 30 | "watchify": "^3.11.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/webiojs/src/handlers/base.ts: -------------------------------------------------------------------------------- 1 | import {Command, Session} from "../session"; 2 | 3 | 4 | export interface CommandHandler { 5 | accept_command: string[], 6 | 7 | handle_message(msg: Command): void 8 | } 9 | 10 | export class CloseHandler implements CommandHandler { 11 | accept_command: string[] = ['close_session']; 12 | 13 | constructor(readonly session: Session) { 14 | } 15 | 16 | handle_message(msg: Command) { 17 | this.session.close_session(); 18 | } 19 | } 20 | 21 | export class CommandDispatcher { 22 | command2handler: { [cmd: string]: CommandHandler } = {}; 23 | 24 | constructor(...handlers: CommandHandler[]) { 25 | for (let h of handlers) { 26 | for (let cmd of h.accept_command) { 27 | if (cmd in this.command2handler) 28 | throw new Error(`Conflict command handler: both ${this.command2handler[cmd]} and ${h} accepts '${cmd}' command`); 29 | this.command2handler[cmd] = h; 30 | } 31 | } 32 | } 33 | 34 | dispatch_message(msg: Command): boolean { 35 | if (msg.command in this.command2handler) { 36 | this.command2handler[msg.command].handle_message(msg); 37 | return true; 38 | } 39 | return false 40 | } 41 | } -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/webiojs/src/handlers/download.ts: -------------------------------------------------------------------------------- 1 | import {Command} from "../session"; 2 | import {CommandHandler} from "./base"; 3 | import {b64toBlob} from "../utils"; 4 | 5 | export class DownloadHandler implements CommandHandler { 6 | accept_command: string[] = ['download']; 7 | 8 | constructor() { 9 | } 10 | 11 | handle_message(msg: Command) { 12 | let blob = b64toBlob(msg.spec.content); 13 | saveAs(blob, msg.spec.name, {}, false); 14 | } 15 | } -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/webiojs/src/handlers/env.ts: -------------------------------------------------------------------------------- 1 | import {Command, HttpSession} from "../session"; 2 | import {CommandHandler} from "./base"; 3 | import {config, state} from "../state"; 4 | 5 | export class EnvSettingHandler implements CommandHandler { 6 | accept_command: string[] = ['set_env']; 7 | 8 | constructor() { 9 | } 10 | 11 | handle_message(msg: Command) { 12 | let spec = msg.spec; 13 | if (spec.title !== undefined) { 14 | document.title = spec.title; 15 | } 16 | 17 | if (spec.auto_scroll_bottom !== undefined) 18 | state.AutoScrollBottom = spec.auto_scroll_bottom; 19 | 20 | if (spec.output_animation !== undefined) { 21 | config.outputAnimation = spec.output_animation; 22 | } 23 | 24 | if (spec.http_pull_interval !== undefined) { 25 | if (state.CurrentSession instanceof HttpSession) 26 | state.CurrentSession.change_pull_interval(spec.http_pull_interval); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/webiojs/src/handlers/script.ts: -------------------------------------------------------------------------------- 1 | import {Command, Session} from "../session"; 2 | import {CommandHandler} from "./base"; 3 | 4 | 5 | export class ScriptHandler implements CommandHandler { 6 | session: Session; 7 | 8 | accept_command = ['run_script']; 9 | 10 | constructor(session: Session) { 11 | this.session = session; 12 | } 13 | 14 | handle_message(msg: Command) { 15 | let script = msg.spec.code as string; 16 | let args = msg.spec.args as { [i: string]: any }; 17 | let arg_names:string[] = ['WebIOCurrentTaskID'], arg_vals:any[] = [msg.task_id]; 18 | for(let key in args){ 19 | arg_names.push(key); 20 | arg_vals.push(args[key]); 21 | } 22 | const script_func = new Function(...arg_names, script); 23 | script_func(...arg_vals); 24 | } 25 | } -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/webiojs/src/handlers/toast.ts: -------------------------------------------------------------------------------- 1 | import {Command} from "../session"; 2 | import {CommandHandler} from "./base"; 3 | import {state} from "../state"; 4 | 5 | export class ToastHandler implements CommandHandler { 6 | accept_command: string[] = ['toast']; 7 | 8 | constructor() { 9 | } 10 | 11 | handle_message(msg: Command) { 12 | let spec = msg.spec; 13 | let toast = Toastify({ 14 | text: Mustache.escape(spec.content), 15 | duration: spec.duration === 0 ? -1 : spec.duration, // -1 for permanent toast 16 | close: spec.duration === 0,//To show the close icon or not 17 | gravity: "top", // `top` or `bottom` 18 | position: spec.position, // `left`, `center` or `right` 19 | backgroundColor: spec.color, 20 | stopOnFocus: true, // Prevents dismissing of toast on hover 21 | onClick: function () { 22 | if (!spec.callback_id) 23 | return; 24 | 25 | if (state.CurrentSession === null) 26 | return console.error("Error: WebIOController is not instantiated"); 27 | state.CurrentSession.send_message({ 28 | event: "callback", 29 | task_id: spec.callback_id, 30 | data: null 31 | }); 32 | toast.hideToast(); 33 | } 34 | }); 35 | toast.showToast(); 36 | } 37 | } -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/webiojs/src/i18n.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | const userLangCode: string = (navigator.language || navigator.userLanguage || 'en').toLowerCase(); 3 | 4 | const userLang: string = userLangCode.split('-')[0]; 5 | 6 | 7 | const translations: { [lang: string]: { [msgid: string]: string } } = { 8 | "en": { 9 | "disconnected_with_server": "Disconnected from the server, please refresh the page", 10 | "connect_fail": "Failed to connect to server!", 11 | "error_in_input": "There is an error with the input, please fix the error first", 12 | "file_size_exceed": 'File "%1" size exceeds limit: the size of a single file must not exceed %2', 13 | "file_total_size_exceed": "The total file size exceeds the limit: the total file size must not exceed %1", 14 | "submit": "Submit", 15 | "reset": "Reset", 16 | "cancel": "Cancel", 17 | }, 18 | "zh": { 19 | "disconnected_with_server": "与服务器连接已断开,请刷新页面重新操作", 20 | "connect_fail": "连接服务器失败!", 21 | "error_in_input": "输入项存在错误,请消除错误后再提交", 22 | "file_size_exceed": '文件"%1"大小超过限制: 单个文件大小不超过%2', 23 | "file_total_size_exceed": "文件总大小超过限制: 文件总大小不超过%1", 24 | "submit": "提交", 25 | "reset": "重置", 26 | "cancel": "取消", 27 | }, 28 | }; 29 | 30 | 31 | // sprintf equivalent, takes a string and some arguments to make a computed string 32 | // eg: strfmt("%1 dogs are in %2", 7, "the kitchen"); => "7 dogs are in the kitchen" 33 | // eg: strfmt("I like %1, bananas and %1", "apples"); => "I like apples, bananas and apples" 34 | function strfmt(fmt: string) { 35 | let args = arguments; 36 | 37 | return fmt 38 | // put space after double % to prevent placeholder replacement of such matches 39 | .replace(/%%/g, '%% ') 40 | // replace placeholders 41 | .replace(/%(\d+)/g, function (str, p1) { 42 | return args[p1]; 43 | }) 44 | // replace double % and space with single % 45 | .replace(/%% /g, '%') 46 | } 47 | 48 | export function t(msgid: string, ...args:string[]): string { 49 | let fmt = null; 50 | for (let lang of [userLangCode, userLang, 'en']) { 51 | if (translations[lang] && translations[lang][msgid]){ 52 | fmt = translations[lang][msgid]; 53 | break; 54 | } 55 | } 56 | if (fmt === null) 57 | throw Error(`No translation for "${msgid}" in "${userLangCode}"`); 58 | 59 | return strfmt.apply(null, [fmt, ...args]); 60 | } -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/webiojs/src/models/input/actions.ts: -------------------------------------------------------------------------------- 1 | import {Session} from "../../session"; 2 | import {InputItem} from "./base"; 3 | 4 | const buttons_tpl = ` 5 |
6 | {{#label}}
{{/label}} 7 | {{#buttons}} 8 | 9 | {{/buttons}} 10 |
{{invalid_feedback}}
11 |
{{valid_feedback}}
12 | {{help_text}} 13 |
`; 14 | 15 | export class Actions extends InputItem { 16 | static accept_input_types: string[] = ["actions"]; 17 | 18 | submit_value: string = null; // 提交表单时按钮组的value 19 | 20 | constructor(session: Session, task_id: string, spec: any) { 21 | super(session, task_id, spec); 22 | } 23 | 24 | create_element(): JQuery { 25 | for (let b of this.spec.buttons) b['btn_type'] = b.type === "submit" ? "submit" : "button"; 26 | 27 | const html = Mustache.render(buttons_tpl, this.spec); 28 | this.element = $(html); 29 | let btns = this.element.find('button'); 30 | for(let idx =0; idx= 0) 79 | input_elem = input_elem.eq(input_idx); 80 | 81 | if ('valid_status' in attributes) { 82 | let class_name = attributes.valid_status ? 'is-valid' : 'is-invalid'; 83 | if(attributes.valid_status===0) class_name = ''; // valid_status为0时,表示清空valid_status标志 84 | input_elem.removeClass('is-valid is-invalid').addClass(class_name); 85 | delete attributes.valid_status; 86 | } 87 | input_elem.prop(attributes); 88 | } 89 | } 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/webiojs/src/models/input/index.ts: -------------------------------------------------------------------------------- 1 | import {Input} from "./input" 2 | import {Actions} from "./actions" 3 | import {CheckboxRadio} from "./checkbox_radio" 4 | import {Textarea} from "./textarea" 5 | import {File} from "./file" 6 | import {Select} from "./select" 7 | 8 | 9 | export const all_input_items = [Input, Actions, CheckboxRadio, Textarea, File, Select]; -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/webiojs/src/models/input/select.ts: -------------------------------------------------------------------------------- 1 | import {InputItem} from "./base"; 2 | import {Session} from "../../session"; 3 | import {deep_copy} from "../../utils" 4 | 5 | 6 | const select_input_tpl = ` 7 |
8 | {{#label}}{{/label}} 9 | 14 |
{{invalid_feedback}}
15 |
{{valid_feedback}}
16 | {{help_text}} 17 |
`; 18 | 19 | export class Select extends InputItem { 20 | static accept_input_types: string[] = ["select"]; 21 | 22 | constructor(session: Session, task_id: string, spec: any) { 23 | super(session, task_id, spec); 24 | } 25 | 26 | create_element(): JQuery { 27 | let spec = deep_copy(this.spec); 28 | const id_name = spec.name + '-' + Math.floor(Math.random() * Math.floor(9999)); 29 | spec['id_name'] = id_name; 30 | 31 | let html = Mustache.render(select_input_tpl, spec); 32 | 33 | this.element = $(html); 34 | let input_elem = this.element.find('#' + id_name); 35 | 36 | let opts = input_elem.find('option'); 37 | for (let idx = 0; idx < spec.options.length; idx++) 38 | opts.eq(idx).val(JSON.stringify(spec.options[idx].value)); 39 | 40 | // blur事件时,发送当前值到服务器 41 | input_elem.on("blur", (e) => { 42 | this.send_value_listener(this, e) 43 | }); 44 | 45 | // 将额外的html参数加到input标签上 46 | const ignore_keys = { 47 | 'type': '', 48 | 'label': '', 49 | 'invalid_feedback': '', 50 | 'valid_feedback': '', 51 | 'help_text': '', 52 | 'options': '', 53 | 'datalist': '', 54 | 'multiple': '' 55 | }; 56 | for (let key in this.spec) { 57 | if (key in ignore_keys) continue; 58 | input_elem.attr(key, this.spec[key]); 59 | } 60 | 61 | return this.element; 62 | } 63 | 64 | update_input(spec: any): any { 65 | let attributes = spec.attributes; 66 | 67 | this.update_input_helper(-1, attributes); 68 | } 69 | 70 | get_value(): any { 71 | let raw_val = this.element.find('select').val(); 72 | if (this.spec.multiple) { 73 | let res: any[] = []; 74 | for (let i of (raw_val as string[])) 75 | res.push(JSON.parse(i)); 76 | return res; 77 | } else { 78 | return JSON.parse(raw_val as string); 79 | } 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/webiojs/src/state.ts: -------------------------------------------------------------------------------- 1 | import {Session} from "./session"; 2 | 3 | // 运行时状态 4 | export let state = { 5 | AutoScrollBottom: false, // 是否有新内容时自动滚动到底部 6 | CurrentSession: null as Session, // 当前正在活跃的会话 7 | ShowDuration: 200, // ms, 显示表单的过渡动画时长 8 | }; 9 | 10 | // 应用配置 11 | export let config = { 12 | codeMirrorModeURL: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/mode/%N/%N.min.js", 13 | codeMirrorThemeURL: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/theme/%N.min.css", 14 | outputAnimation: true, // 启用内容输出动画 15 | httpPullInterval: 1000, // HttpSession 拉取消息的周期(ms) 16 | debug: false, // 调试模式, 打印所有交互的消息 17 | }; 18 | -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/webiojs/src/vendor.d.ts: -------------------------------------------------------------------------------- 1 | // declare var $: any; 2 | declare let Mustache: any; 3 | declare let saveAs: any; 4 | declare let CodeMirror: any; 5 | declare let bsCustomFileInput: any; 6 | declare let Toastify: any; 7 | declare let Prism: any; // Prism.js 8 | declare let DOMPurify: any; // DOMPurify.js -------------------------------------------------------------------------------- /vendor/github.com/mhucka/PyWebIO/webiojs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "src/**/*.ts" 4 | ], 5 | "compilerOptions": { 6 | "noImplicitAny": true, 7 | "target": "es2015", 8 | "module": "commonjs" 9 | } 10 | } --------------------------------------------------------------------------------