Merged PR 131145: [Jupyter Wrapper]: Initial code structure
Initial code structure for powerbi Jupyter widget This is based on official boilerplate: https://github.com/jupyter-widgets/widget-ts-cookiecutter Related work items: #473925
This commit is contained in:
Родитель
b9bedf8941
Коммит
1990df9355
|
@ -0,0 +1,2 @@
|
|||
[run]
|
||||
omit = powerbi_widget/tests/*
|
|
@ -0,0 +1,5 @@
|
|||
node_modules
|
||||
dist
|
||||
coverage
|
||||
**/*.d.ts
|
||||
tests
|
|
@ -0,0 +1,28 @@
|
|||
module.exports = {
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended'
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
sourceType: 'module'
|
||||
},
|
||||
plugins: ['@typescript-eslint'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': ['warn', { args: 'none' }],
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-namespace': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'@typescript-eslint/quotes': [
|
||||
'error',
|
||||
'single',
|
||||
{ avoidEscape: true, allowTemplateLiterals: false }
|
||||
],
|
||||
curly: ['error', 'all'],
|
||||
eqeqeq: 'error',
|
||||
'prefer-arrow-callback': 'error'
|
||||
}
|
||||
};
|
|
@ -0,0 +1,156 @@
|
|||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*,cover
|
||||
.hypothesis/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
|
||||
# Flask instance folder
|
||||
instance/
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
docs/source/_static/embed-bundle.js
|
||||
docs/source/_static/embed-bundle.js.map
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# IPython Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# dotenv
|
||||
.env
|
||||
|
||||
# virtualenv
|
||||
venv/
|
||||
ENV/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# =========================
|
||||
# Operating System Files
|
||||
# =========================
|
||||
|
||||
# OSX
|
||||
# =========================
|
||||
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# Windows
|
||||
# =========================
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
|
||||
# NPM
|
||||
# ----
|
||||
|
||||
**/node_modules/
|
||||
powerbi_widget/nbextension/static/index.*
|
||||
powerbi_widget/labextension/*.tgz
|
||||
|
||||
# Coverage data
|
||||
# -------------
|
||||
**/coverage/
|
||||
|
||||
# Packed lab extensions
|
||||
powerbi_widget/labextension
|
|
@ -0,0 +1,7 @@
|
|||
.DS_Store
|
||||
node_modules/
|
||||
tests/
|
||||
.jshintrc
|
||||
# Ignore any build output from python:
|
||||
dist/*.tar.gz
|
||||
dist/*.wheel
|
|
@ -0,0 +1,4 @@
|
|||
node_modules
|
||||
**/node_modules
|
||||
**/lib
|
||||
**/package.json
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"singleQuote": true
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
language: python
|
||||
python:
|
||||
- 3.7
|
||||
- 3.6
|
||||
- 3.5
|
||||
sudo: false
|
||||
dist: xenial
|
||||
services:
|
||||
- xvfb
|
||||
addons:
|
||||
apt_packages:
|
||||
- pandoc
|
||||
env:
|
||||
matrix:
|
||||
- GROUP=python
|
||||
matrix:
|
||||
include:
|
||||
- python: 3.5
|
||||
env: GROUP=js
|
||||
include:
|
||||
- python: 3.6
|
||||
env: GROUP=docs
|
||||
cache:
|
||||
pip: true
|
||||
directories:
|
||||
- node_modules # NPM packages
|
||||
- $HOME/.npm
|
||||
before_install:
|
||||
- pip install -U pip setuptools
|
||||
- nvm install 12
|
||||
- |
|
||||
if [[ $GROUP == python ]]; then
|
||||
pip install codecov
|
||||
elif [[ $GROUP == js ]]; then
|
||||
npm install -g codecov
|
||||
fi
|
||||
install:
|
||||
- |
|
||||
if [[ $GROUP == python ]]; then
|
||||
pip install --upgrade ".[test]" -v
|
||||
elif [[ $GROUP == js ]]; then
|
||||
pip install --upgrade -e ".[test]" -v
|
||||
elif [[ $GROUP == docs ]]; then
|
||||
pip install --upgrade ".[test, examples, docs]" -v
|
||||
fi
|
||||
before_script:
|
||||
# Set up a virtual screen for Firefox browser testing:
|
||||
- |
|
||||
if [[ $GROUP == js ]]; then
|
||||
export CHROME_BIN=chromium-browser
|
||||
fi
|
||||
git config --global user.email travis@fake.com
|
||||
git config --global user.name "Travis CI"
|
||||
script:
|
||||
- |
|
||||
if [[ $GROUP == python ]]; then
|
||||
EXIT_STATUS=0
|
||||
pushd $(mktemp -d)
|
||||
py.test -l --cov-report xml:$TRAVIS_BUILD_DIR/coverage.xml --cov=powerbi_widget --pyargs powerbi_widget || EXIT_STATUS=$?
|
||||
popd
|
||||
(exit $EXIT_STATUS)
|
||||
elif [[ $GROUP == js ]]; then
|
||||
npm test
|
||||
elif [[ $GROUP == docs ]]; then
|
||||
EXIT_STATUS=0
|
||||
cd docs
|
||||
make html || EXIT_STATUS=$?
|
||||
make linkcheck || EXIT_STATUS=$?
|
||||
cd ..
|
||||
python -m pytest_check_links --links-ext=.md -o testpaths=. -o addopts= || EXIT_STATUS=$?
|
||||
(exit $EXIT_STATUS)
|
||||
fi
|
||||
after_success:
|
||||
- codecov
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2020 Microsoft
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,40 @@
|
|||
include LICENSE.txt
|
||||
include README.md
|
||||
|
||||
include setupbase.py
|
||||
include pytest.ini
|
||||
include .coverage.rc
|
||||
|
||||
include tsconfig.json
|
||||
include package.json
|
||||
include webpack.config.js
|
||||
include powerbi_widget/labextension/*.tgz
|
||||
|
||||
# Documentation
|
||||
graft docs
|
||||
exclude docs/\#*
|
||||
prune docs/build
|
||||
prune docs/gh-pages
|
||||
prune docs/dist
|
||||
|
||||
# Examples
|
||||
graft examples
|
||||
|
||||
# Tests
|
||||
graft tests
|
||||
prune tests/build
|
||||
|
||||
# Javascript files
|
||||
graft powerbi_widget/nbextension
|
||||
graft src
|
||||
graft css
|
||||
prune **/node_modules
|
||||
prune coverage
|
||||
prune lib
|
||||
|
||||
# Patterns to exclude from any directory
|
||||
global-exclude *~
|
||||
global-exclude *.pyc
|
||||
global-exclude *.pyo
|
||||
global-exclude .git
|
||||
global-exclude .ipynb_checkpoints
|
88
README.md
88
README.md
|
@ -1,20 +1,76 @@
|
|||
# Introduction
|
||||
TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project.
|
||||
|
||||
# Getting Started
|
||||
TODO: Guide users through getting your code up and running on their own system. In this section you can talk about:
|
||||
1. Installation process
|
||||
2. Software dependencies
|
||||
3. Latest releases
|
||||
4. API references
|
||||
# powerbi-jupyter
|
||||
|
||||
# Build and Test
|
||||
TODO: Describe and show how to build your code and run the tests.
|
||||
A Custom Jupyter Widget Library
|
||||
|
||||
# Contribute
|
||||
TODO: Explain how other users and developers can contribute to make your code better.
|
||||
## Installation
|
||||
|
||||
If you want to learn more about creating good readme files then refer the following [guidelines](https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops). You can also seek inspiration from the below readme files:
|
||||
- [ASP.NET Core](https://github.com/aspnet/Home)
|
||||
- [Visual Studio Code](https://github.com/Microsoft/vscode)
|
||||
- [Chakra Core](https://github.com/Microsoft/ChakraCore)
|
||||
You can install using `pip`:
|
||||
|
||||
```bash
|
||||
pip install powerbi_widget
|
||||
```
|
||||
|
||||
Or if you use jupyterlab:
|
||||
|
||||
```bash
|
||||
pip install powerbi_widget
|
||||
jupyter labextension install @jupyter-widgets/jupyterlab-manager
|
||||
```
|
||||
|
||||
If you are using Jupyter Notebook 5.2 or earlier, you may also need to enable
|
||||
the nbextension:
|
||||
```bash
|
||||
jupyter nbextension enable --py [--sys-prefix|--user|--system] powerbi_widget
|
||||
```
|
||||
|
||||
## Development Installation
|
||||
|
||||
|
||||
```bash
|
||||
# First install the python package. This will also build the JS packages.
|
||||
pip install -e ".[test, examples]"
|
||||
```
|
||||
|
||||
When developing your extensions, you need to manually enable your extensions with the
|
||||
notebook / lab frontend. For lab, this is done by the command:
|
||||
|
||||
```
|
||||
jupyter labextension install @jupyter-widgets/jupyterlab-manager --no-build
|
||||
jupyter labextension install .
|
||||
```
|
||||
|
||||
For classic notebook, you can run:
|
||||
|
||||
```
|
||||
jupyter nbextension install --sys-prefix --symlink --overwrite --py powerbi_widget
|
||||
jupyter nbextension enable --sys-prefix --py powerbi_widget
|
||||
```
|
||||
|
||||
__Note__ that the `--symlink` flag doesn't work on Windows, so you will here have to run
|
||||
the `install` command every time that you rebuild your extension. For certain installations
|
||||
you might also need another flag instead of `--sys-prefix`, but we won't cover the meaning
|
||||
of those flags here.
|
||||
|
||||
### How to see your changes
|
||||
#### Typescript:
|
||||
To continuously monitor the project for changes and automatically trigger a rebuild, start Jupyter in watch mode:
|
||||
```bash
|
||||
jupyter lab --watch
|
||||
```
|
||||
|
||||
To execute the project in Jupyter Notebook:
|
||||
```bash
|
||||
python setup.py sdist
|
||||
jupyter notebook
|
||||
```
|
||||
|
||||
And in a separate session, begin watching the source directory for changes:
|
||||
```bash
|
||||
npm run watch
|
||||
```
|
||||
|
||||
After a change wait for the build to finish and then refresh your browser and the changes should take effect.
|
||||
|
||||
#### Python:
|
||||
If you make a change to the python code then you will need to restart the notebook kernel to have it take effect.
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
# Do not build feature branch with open Pull Requests
|
||||
skip_branch_with_pr: true
|
||||
|
||||
# environment variables
|
||||
environment:
|
||||
nodejs_version: "8"
|
||||
matrix:
|
||||
- PYTHON: "C:\\Miniconda3-x64"
|
||||
PYTHON_VERSION: "3.7"
|
||||
PYTHON_MAJOR: 3
|
||||
PYTHON_ARCH: "64"
|
||||
- PYTHON: "C:\\Miniconda3"
|
||||
PYTHON_VERSION: "3.4"
|
||||
PYTHON_MAJOR: 3
|
||||
PYTHON_ARCH: "32"
|
||||
|
||||
# build cache to preserve files/folders between builds
|
||||
cache:
|
||||
- '%AppData%/npm-cache'
|
||||
- '%PYTHON%/pkgs'
|
||||
- '%LOCALAPPDATA%\pip\Cache'
|
||||
|
||||
# scripts that run after cloning repository
|
||||
install:
|
||||
# Install node:
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
# Ensure python scripts are from right version:
|
||||
- 'SET "PATH=%PYTHON%\Scripts;%PYTHON%;%PATH%"'
|
||||
# Setup conda:
|
||||
- 'conda list'
|
||||
- 'conda update conda -y'
|
||||
# If 32 bit, force conda to use it:
|
||||
- 'IF %PYTHON_ARCH% EQU 32 SET CONDA_FORCE_32BIT=1'
|
||||
- 'conda create -n test_env python=%PYTHON_VERSION% -y'
|
||||
- 'activate test_env'
|
||||
# Update install tools:
|
||||
- 'conda install setuptools pip -y'
|
||||
- 'python -m pip install --upgrade pip'
|
||||
- 'python -m easy_install --upgrade setuptools'
|
||||
# Install coverage utilities:
|
||||
- 'pip install codecov'
|
||||
# Install our package:
|
||||
- 'pip install --upgrade ".[test]" -v'
|
||||
|
||||
build: off
|
||||
|
||||
# scripts to run before tests
|
||||
before_test:
|
||||
- git config --global user.email appveyor@fake.com
|
||||
- git config --global user.name "AppVeyor CI"
|
||||
- set "tmptestdir=%tmp%\powerbi_widget-%RANDOM%"
|
||||
- mkdir "%tmptestdir%"
|
||||
- cd "%tmptestdir%"
|
||||
|
||||
|
||||
# to run your custom scripts instead of automatic tests
|
||||
test_script:
|
||||
- 'py.test -l --cov-report xml:"%APPVEYOR_BUILD_FOLDER%\coverage.xml" --cov=powerbi_widget --pyargs powerbi_widget'
|
||||
|
||||
on_success:
|
||||
- cd "%APPVEYOR_BUILD_FOLDER%"
|
||||
- codecov -X gcov --file "%APPVEYOR_BUILD_FOLDER%\coverage.xml"
|
|
@ -0,0 +1,12 @@
|
|||
comment: off
|
||||
# show coverage in CI status, but never consider it a failure
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: 0%
|
||||
patch:
|
||||
default:
|
||||
target: 0%
|
||||
ignore:
|
||||
- "powerbi_widget/tests"
|
|
@ -0,0 +1,4 @@
|
|||
.custom-widget {
|
||||
background-color: lightseagreen;
|
||||
padding: 0px 2px;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
SPHINXPROJ = powerbi_widget
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
name: powerbi_widget_docs
|
||||
channels:
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- python=3.5
|
||||
- nodejs
|
||||
- numpy
|
||||
- sphinx
|
||||
- nbsphinx
|
||||
- jupyter_sphinx
|
|
@ -0,0 +1,36 @@
|
|||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=source
|
||||
set BUILDDIR=build
|
||||
set SPHINXPROJ=powerbi_widget
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
|
||||
:end
|
||||
popd
|
|
@ -0,0 +1,5 @@
|
|||
var cache_require = window.require;
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
window.require = cache_require;
|
||||
});
|
|
@ -0,0 +1,209 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# powerbi_widget documentation build configuration file
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.viewcode',
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.napoleon',
|
||||
'sphinx.ext.todo',
|
||||
'nbsphinx',
|
||||
'jupyter_sphinx.execute',
|
||||
'nbsphinx_link',
|
||||
]
|
||||
|
||||
# Ensure our extension is available:
|
||||
import sys
|
||||
from os.path import dirname, join as pjoin
|
||||
docs = dirname(dirname(__file__))
|
||||
root = dirname(docs)
|
||||
sys.path.insert(0, root)
|
||||
sys.path.insert(0, pjoin(docs, 'sphinxext'))
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'powerbi_widget'
|
||||
copyright = '2020, Microsoft'
|
||||
author = 'Microsoft'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
|
||||
|
||||
# get version from python package:
|
||||
import os
|
||||
here = os.path.dirname(__file__)
|
||||
repo = os.path.join(here, '..', '..')
|
||||
_version_py = os.path.join(repo, 'powerbi_widget', '_version.py')
|
||||
version_ns = {}
|
||||
with open(_version_py) as f:
|
||||
exec(f.read(), version_ns)
|
||||
|
||||
# The short X.Y version.
|
||||
version = '%i.%i' % version_ns['version_info'][:2]
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version_ns['__version__']
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['**.ipynb_checkpoints']
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'powerbi_widgetdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'powerbi_widget.tex', 'powerbi_widget Documentation',
|
||||
'Microsoft', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc,
|
||||
'powerbi_widget',
|
||||
'powerbi_widget Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc,
|
||||
'powerbi_widget',
|
||||
'powerbi_widget Documentation',
|
||||
author,
|
||||
'powerbi_widget',
|
||||
'A Custom Jupyter Widget Library',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {'https://docs.python.org/': None}
|
||||
|
||||
# Read The Docs
|
||||
# on_rtd is whether we are on readthedocs.org, this line of code grabbed from
|
||||
# docs.readthedocs.org
|
||||
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
|
||||
|
||||
if not on_rtd: # only import and set the theme if we're building docs locally
|
||||
import sphinx_rtd_theme
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||
|
||||
# otherwise, readthedocs.org uses their theme by default, so no need to specify it
|
||||
|
||||
|
||||
# Uncomment this line if you have know exceptions in your included notebooks
|
||||
# that nbsphinx complains about:
|
||||
#
|
||||
nbsphinx_allow_errors = True # exception ipstruct.py ipython_genutils
|
||||
|
||||
from sphinx.util import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def setup(app):
|
||||
def add_scripts(app):
|
||||
for fname in ['helper.js', 'embed-bundle.js']:
|
||||
if not os.path.exists(os.path.join(here, '_static', fname)):
|
||||
logger.warning('missing javascript file: %s' % fname)
|
||||
app.add_js_file(fname)
|
||||
app.connect('builder-inited', add_scripts)
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
Developer install
|
||||
=================
|
||||
|
||||
|
||||
To install a developer version of powerbi_widget, you will first need to clone
|
||||
the repository::
|
||||
|
||||
git clone https://github.com/Microsoft/powerbi-jupyter
|
||||
cd powerbi-jupyter
|
||||
|
||||
Next, install it with a develop install using pip::
|
||||
|
||||
pip install -e .
|
||||
|
||||
|
||||
If you are planning on working on the JS/frontend code, you should also do
|
||||
a link installation of the extension::
|
||||
|
||||
jupyter nbextension install [--sys-prefix / --user / --system] --symlink --py powerbi_widget
|
||||
|
||||
jupyter nbextension enable [--sys-prefix / --user / --system] --py powerbi_widget
|
||||
|
||||
with the `appropriate flag`_. Or, if you are using Jupyterlab::
|
||||
|
||||
jupyter labextension install .
|
||||
|
||||
|
||||
.. links
|
||||
|
||||
.. _`appropriate flag`: https://jupyter-notebook.readthedocs.io/en/stable/extending/frontend_extensions.html#installing-and-enabling-extensions
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
Examples
|
||||
========
|
||||
|
||||
This section contains several examples generated from Jupyter notebooks.
|
||||
The widgets have been embedded into the page for demonstrative pruposes.
|
||||
|
||||
.. todo::
|
||||
|
||||
Add links to notebooks in examples folder similar to the initial
|
||||
one. This is a manual step to ensure only those examples that
|
||||
are suited for inclusion are used.
|
||||
|
||||
|
||||
.. toctree::
|
||||
:glob:
|
||||
|
||||
*
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"path": "../../../examples/introduction.ipynb"
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
|
||||
powerbi_widget
|
||||
=====================================
|
||||
|
||||
Version: |release|
|
||||
|
||||
A Custom Jupyter Widget Library
|
||||
|
||||
|
||||
Quickstart
|
||||
----------
|
||||
|
||||
To get started with powerbi_widget, install with pip::
|
||||
|
||||
pip install powerbi_widget
|
||||
|
||||
or with conda::
|
||||
|
||||
conda install powerbi_widget
|
||||
|
||||
|
||||
Contents
|
||||
--------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Installation and usage
|
||||
|
||||
installing
|
||||
introduction
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
examples/index
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Development
|
||||
|
||||
develop-install
|
||||
|
||||
|
||||
.. links
|
||||
|
||||
.. _`Jupyter widgets`: https://jupyter.org/widgets.html
|
||||
|
||||
.. _`notebook`: https://jupyter-notebook.readthedocs.io/en/latest/
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
.. _installation:
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
|
||||
The simplest way to install powerbi_widget is via pip::
|
||||
|
||||
pip install powerbi_widget
|
||||
|
||||
or via conda::
|
||||
|
||||
conda install powerbi_widget
|
||||
|
||||
|
||||
If you installed via pip, and notebook version < 5.3, you will also have to
|
||||
install / configure the front-end extension as well. If you are using classic
|
||||
notebook (as opposed to Jupyterlab), run::
|
||||
|
||||
jupyter nbextension install [--sys-prefix / --user / --system] --py powerbi_widget
|
||||
|
||||
jupyter nbextension enable [--sys-prefix / --user / --system] --py powerbi_widget
|
||||
|
||||
with the `appropriate flag`_. If you are using Jupyterlab, install the extension
|
||||
with::
|
||||
|
||||
jupyter labextension install powerbi-widget-client
|
||||
|
||||
If you are installing using conda, these commands should be unnecessary, but If
|
||||
you need to run them the commands should be the same (just make sure you choose the
|
||||
`--sys-prefix` flag).
|
||||
|
||||
|
||||
.. links
|
||||
|
||||
.. _`appropriate flag`: https://jupyter-notebook.readthedocs.io/en/stable/extending/frontend_extensions.html#installing-and-enabling-extensions
|
|
@ -0,0 +1,7 @@
|
|||
=============
|
||||
Introduction
|
||||
=============
|
||||
|
||||
.. todo::
|
||||
|
||||
add prose explaining project purpose and usage here
|
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Introduction"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import powerbi_widget"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"w = powerbi_widget.ExampleWidget()\n",
|
||||
"w"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"assert w.value == 'Hello World'"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.6.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,94 @@
|
|||
{
|
||||
"name": "powerbi-widget-client",
|
||||
"version": "0.1.0",
|
||||
"description": "A Custom Jupyter Widget Library",
|
||||
"keywords": [
|
||||
"jupyter",
|
||||
"jupyterlab",
|
||||
"jupyterlab-extension",
|
||||
"widgets"
|
||||
],
|
||||
"files": [
|
||||
"lib/**/*.js",
|
||||
"dist/*.js",
|
||||
"css/*.css"
|
||||
],
|
||||
"homepage": "https://github.com/Microsoft/powerbi-jupyter",
|
||||
"bugs": {
|
||||
"url": "https://github.com/Microsoft/powerbi-jupyter/issues"
|
||||
},
|
||||
"license": "BSD-3-Clause",
|
||||
"author": {
|
||||
"name": "Microsoft"
|
||||
},
|
||||
"main": "lib/index.js",
|
||||
"types": "./lib/index.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Microsoft/powerbi-jupyter"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npm run build:lib && npm run build:nbextension",
|
||||
"build:labextension": "npm run clean:labextension && mkdirp powerbi_widget/labextension && cd powerbi_widget/labextension && npm pack ../..",
|
||||
"build:lib": "tsc",
|
||||
"build:nbextension": "webpack -p",
|
||||
"build:all": "npm run build:labextension && npm run build:nbextension",
|
||||
"clean": "npm run clean:lib && npm run clean:nbextension",
|
||||
"clean:lib": "rimraf lib",
|
||||
"clean:labextension": "rimraf powerbi_widget/labextension",
|
||||
"clean:nbextension": "rimraf powerbi_widget/nbextension/static/index.js",
|
||||
"lint": "eslint . --ext .ts,.tsx --fix",
|
||||
"lint:check": "eslint . --ext .ts,.tsx",
|
||||
"prepack": "npm run build:lib",
|
||||
"test": "npm run test:firefox",
|
||||
"test:chrome": "karma start --browsers=Chrome tests/karma.conf.js",
|
||||
"test:debug": "karma start --browsers=Chrome --singleRun=false --debug=true tests/karma.conf.js",
|
||||
"test:firefox": "karma start --browsers=Firefox tests/karma.conf.js",
|
||||
"test:ie": "karma start --browsers=IE tests/karma.conf.js",
|
||||
"watch": "npm-run-all -p watch:*",
|
||||
"watch:lib": "tsc -w",
|
||||
"watch:nbextension": "webpack --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jupyter-widgets/base": "^1.1.10 || ^2 || ^3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@phosphor/application": "^1.6.0",
|
||||
"@phosphor/widgets": "^1.6.0",
|
||||
"@types/expect.js": "^0.3.29",
|
||||
"@types/mocha": "^5.2.5",
|
||||
"@types/node": "^10.11.6",
|
||||
"@types/webpack-env": "^1.13.6",
|
||||
"@typescript-eslint/eslint-plugin": "^3.6.0",
|
||||
"@typescript-eslint/parser": "^3.6.0",
|
||||
"acorn": "^7.2.0",
|
||||
"css-loader": "^3.2.0",
|
||||
"eslint": "^7.4.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-plugin-prettier": "^3.1.4",
|
||||
"expect.js": "^0.3.1",
|
||||
"fs-extra": "^7.0.0",
|
||||
"karma": "^3.1.0",
|
||||
"karma-chrome-launcher": "^2.2.0",
|
||||
"karma-firefox-launcher": "^1.1.0",
|
||||
"karma-ie-launcher": "^1.0.0",
|
||||
"karma-mocha": "^1.3.0",
|
||||
"karma-mocha-reporter": "^2.2.5",
|
||||
"karma-typescript": "^5.0.3",
|
||||
"karma-typescript-es6-transform": "^5.0.3",
|
||||
"mkdirp": "^0.5.1",
|
||||
"mocha": "^5.2.0",
|
||||
"npm-run-all": "^4.1.3",
|
||||
"prettier": "^2.0.5",
|
||||
"rimraf": "^2.6.2",
|
||||
"source-map-loader": "^0.2.4",
|
||||
"style-loader": "^1.0.0",
|
||||
"ts-loader": "^5.2.1",
|
||||
"typescript": "~3.8",
|
||||
"webpack": "^4.20.2",
|
||||
"webpack-cli": "^3.1.2"
|
||||
},
|
||||
"jupyterlab": {
|
||||
"extension": "lib/plugin"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"load_extensions": {
|
||||
"powerbi_widget/extension": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# Copyright (c) Microsoft.
|
||||
|
||||
# TODO: Remove example import
|
||||
from .example import ExampleWidget
|
||||
|
||||
from .report import Report
|
||||
from ._version import __version__, version_info
|
||||
|
||||
from .nbextension import _jupyter_nbextension_paths
|
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# Copyright (c) Microsoft.
|
||||
|
||||
"""
|
||||
Information about the frontend package of the widgets.
|
||||
"""
|
||||
|
||||
module_name = "powerbi-widget-client"
|
||||
module_version = "^0.1.0"
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# Copyright (c) Microsoft.
|
||||
|
||||
version_info = (0, 1, 0, 'dev')
|
||||
__version__ = ".".join(map(str, version_info))
|
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# Copyright (c) Microsoft.
|
||||
|
||||
"""
|
||||
TODO: Add module docstring
|
||||
"""
|
||||
|
||||
from ipywidgets import DOMWidget
|
||||
from traitlets import Unicode
|
||||
from ._frontend import module_name, module_version
|
||||
|
||||
|
||||
class ExampleWidget(DOMWidget):
|
||||
"""TODO: Add docstring here
|
||||
"""
|
||||
_model_name = Unicode('ReportModel').tag(sync=True)
|
||||
_model_module = Unicode(module_name).tag(sync=True)
|
||||
_model_module_version = Unicode(module_version).tag(sync=True)
|
||||
_view_name = Unicode('ReportView').tag(sync=True)
|
||||
_view_module = Unicode(module_name).tag(sync=True)
|
||||
_view_module_version = Unicode(module_version).tag(sync=True)
|
||||
|
||||
value = Unicode('Hello World').tag(sync=True)
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# Copyright (c) Microsoft
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
def _jupyter_nbextension_paths():
|
||||
return [{
|
||||
'section': 'notebook',
|
||||
'src': 'nbextension/static',
|
||||
'dest': 'powerbi_widget',
|
||||
'require': 'powerbi_widget/extension'
|
||||
}]
|
|
@ -0,0 +1,17 @@
|
|||
// Entry point for the notebook bundle containing custom model definitions.
|
||||
//
|
||||
define(function() {
|
||||
"use strict";
|
||||
|
||||
window['requirejs'].config({
|
||||
map: {
|
||||
'*': {
|
||||
'powerbi-widget-client': 'nbextensions/powerbi_widget/index',
|
||||
},
|
||||
}
|
||||
});
|
||||
// Export the required load_ipython_extension function
|
||||
return {
|
||||
load_ipython_extension : function() {}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,17 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# Copyright (c) Microsoft.
|
||||
|
||||
"""
|
||||
TODO: Add module docstring
|
||||
"""
|
||||
|
||||
from ipywidgets import DOMWidget
|
||||
from traitlets import Unicode
|
||||
from ._frontend import module_name, module_version
|
||||
|
||||
|
||||
class Report(DOMWidget):
|
||||
"""TODO: Add docstring here
|
||||
"""
|
|
@ -0,0 +1,54 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# Copyright (c) Microsoft.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import pytest
|
||||
|
||||
from ipykernel.comm import Comm
|
||||
from ipywidgets import Widget
|
||||
|
||||
class MockComm(Comm):
|
||||
"""A mock Comm object.
|
||||
|
||||
Can be used to inspect calls to Comm's open/send/close methods.
|
||||
"""
|
||||
comm_id = 'a-b-c-d'
|
||||
kernel = 'Truthy'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.log_open = []
|
||||
self.log_send = []
|
||||
self.log_close = []
|
||||
super(MockComm, self).__init__(*args, **kwargs)
|
||||
|
||||
def open(self, *args, **kwargs):
|
||||
self.log_open.append((args, kwargs))
|
||||
|
||||
def send(self, *args, **kwargs):
|
||||
self.log_send.append((args, kwargs))
|
||||
|
||||
def close(self, *args, **kwargs):
|
||||
self.log_close.append((args, kwargs))
|
||||
|
||||
_widget_attrs = {}
|
||||
undefined = object()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_comm():
|
||||
_widget_attrs['_comm_default'] = getattr(Widget, '_comm_default', undefined)
|
||||
Widget._comm_default = lambda self: MockComm()
|
||||
_widget_attrs['_ipython_display_'] = Widget._ipython_display_
|
||||
def raise_not_implemented(*args, **kwargs):
|
||||
raise NotImplementedError()
|
||||
Widget._ipython_display_ = raise_not_implemented
|
||||
|
||||
yield MockComm()
|
||||
|
||||
for attr, value in _widget_attrs.items():
|
||||
if value is undefined:
|
||||
delattr(Widget, attr)
|
||||
else:
|
||||
setattr(Widget, attr, value)
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# Copyright (c) Microsoft.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import pytest
|
||||
|
||||
from ..example import ExampleWidget
|
||||
|
||||
|
||||
def test_example_creation_blank():
|
||||
w = ExampleWidget()
|
||||
assert w.value == 'Hello World'
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# Copyright (c) Microsoft.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
|
||||
def test_nbextension_path():
|
||||
# Check that magic function can be imported from package root:
|
||||
from powerbi_widget import _jupyter_nbextension_paths
|
||||
# Ensure that it can be called without incident:
|
||||
path = _jupyter_nbextension_paths()
|
||||
# Some sanity checks:
|
||||
assert len(path) == 1
|
||||
assert isinstance(path[0], dict)
|
|
@ -0,0 +1,4 @@
|
|||
[pytest]
|
||||
testpaths = powerbi_widget/tests examples
|
||||
norecursedirs = node_modules .ipynb_checkpoints
|
||||
addopts = --nbval --current-env
|
|
@ -0,0 +1,9 @@
|
|||
type: sphinx
|
||||
python:
|
||||
version: 3.5
|
||||
pip_install: true
|
||||
extra_requirements:
|
||||
- examples
|
||||
- docs
|
||||
conda:
|
||||
file: docs/environment.yml
|
|
@ -0,0 +1,6 @@
|
|||
[bdist_wheel]
|
||||
universal=1
|
||||
|
||||
[metadata]
|
||||
description-file = README.md
|
||||
license_file = LICENSE.txt
|
|
@ -0,0 +1,116 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from __future__ import print_function
|
||||
from glob import glob
|
||||
from os.path import join as pjoin
|
||||
|
||||
|
||||
from setupbase import (
|
||||
create_cmdclass, install_npm, ensure_targets,
|
||||
find_packages, combine_commands, ensure_python,
|
||||
get_version, HERE
|
||||
)
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
|
||||
# The name of the project
|
||||
name = 'powerbi_widget'
|
||||
|
||||
# Ensure a valid python version
|
||||
ensure_python('>=3.4')
|
||||
|
||||
# Get our version
|
||||
version = get_version(pjoin(name, '_version.py'))
|
||||
|
||||
nb_path = pjoin(HERE, name, 'nbextension', 'static')
|
||||
lab_path = pjoin(HERE, name, 'labextension')
|
||||
|
||||
# Representative files that should exist after a successful build
|
||||
jstargets = [
|
||||
pjoin(nb_path, 'index.js'),
|
||||
pjoin(HERE, 'lib', 'plugin.js'),
|
||||
]
|
||||
|
||||
package_data_spec = {
|
||||
name: [
|
||||
'nbextension/static/*.*js*',
|
||||
'labextension/*.tgz'
|
||||
]
|
||||
}
|
||||
|
||||
data_files_spec = [
|
||||
('share/jupyter/nbextensions/powerbi_widget',
|
||||
nb_path, '*.js*'),
|
||||
('share/jupyter/lab/extensions', lab_path, '*.tgz'),
|
||||
('etc/jupyter/nbconfig/notebook.d' , HERE, 'powerbi_widget.json')
|
||||
]
|
||||
|
||||
|
||||
cmdclass = create_cmdclass('jsdeps', package_data_spec=package_data_spec,
|
||||
data_files_spec=data_files_spec)
|
||||
cmdclass['jsdeps'] = combine_commands(
|
||||
install_npm(HERE, build_cmd='build:all'),
|
||||
ensure_targets(jstargets),
|
||||
)
|
||||
|
||||
|
||||
setup_args = dict(
|
||||
name = name,
|
||||
description = 'A Custom Jupyter Widget Library',
|
||||
version = version,
|
||||
scripts = glob(pjoin('scripts', '*')),
|
||||
cmdclass = cmdclass,
|
||||
packages = find_packages(),
|
||||
author = 'Microsoft',
|
||||
author_email = '',
|
||||
url = 'https://github.com/Microsoft/powerbi-jupyter',
|
||||
license = 'BSD',
|
||||
platforms = "Linux, Mac OS X, Windows",
|
||||
keywords = ['Jupyter', 'Widgets', 'IPython'],
|
||||
classifiers = [
|
||||
'Intended Audience :: Developers',
|
||||
'Intended Audience :: Science/Research',
|
||||
'License :: OSI Approved :: BSD License',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Framework :: Jupyter',
|
||||
],
|
||||
include_package_data = True,
|
||||
install_requires = [
|
||||
'ipywidgets>=7.0.0',
|
||||
],
|
||||
extras_require = {
|
||||
'test': [
|
||||
'pytest>=4.6',
|
||||
'pytest-cov',
|
||||
'nbval',
|
||||
],
|
||||
'examples': [
|
||||
# Any requirements for the examples to run
|
||||
],
|
||||
'docs': [
|
||||
'sphinx>=1.5',
|
||||
'recommonmark',
|
||||
'sphinx_rtd_theme',
|
||||
'nbsphinx>=0.2.13,<0.4.0',
|
||||
'jupyter_sphinx',
|
||||
'nbsphinx-link',
|
||||
'pytest_check_links',
|
||||
'pypandoc',
|
||||
],
|
||||
},
|
||||
entry_points = {
|
||||
},
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
setup(**setup_args)
|
|
@ -0,0 +1,718 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""
|
||||
This file originates from the 'jupyter-packaging' package, and
|
||||
contains a set of useful utilities for including npm packages
|
||||
within a Python package.
|
||||
"""
|
||||
from collections import defaultdict
|
||||
from os.path import join as pjoin
|
||||
import io
|
||||
import os
|
||||
import functools
|
||||
import pipes
|
||||
import re
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
# BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
|
||||
# update it when the contents of directories change.
|
||||
if os.path.exists('MANIFEST'): os.remove('MANIFEST')
|
||||
|
||||
|
||||
from distutils.cmd import Command
|
||||
from distutils.command.build_py import build_py
|
||||
from distutils.command.sdist import sdist
|
||||
from distutils import log
|
||||
|
||||
from setuptools.command.develop import develop
|
||||
from setuptools.command.bdist_egg import bdist_egg
|
||||
|
||||
try:
|
||||
from wheel.bdist_wheel import bdist_wheel
|
||||
except ImportError:
|
||||
bdist_wheel = None
|
||||
|
||||
if sys.platform == 'win32':
|
||||
from subprocess import list2cmdline
|
||||
else:
|
||||
def list2cmdline(cmd_list):
|
||||
return ' '.join(map(pipes.quote, cmd_list))
|
||||
|
||||
|
||||
__version__ = '0.2.0'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Top Level Variables
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
HERE = os.path.abspath(os.path.dirname(__file__))
|
||||
is_repo = os.path.exists(pjoin(HERE, '.git'))
|
||||
node_modules = pjoin(HERE, 'node_modules')
|
||||
|
||||
SEPARATORS = os.sep if os.altsep is None else os.sep + os.altsep
|
||||
|
||||
npm_path = ':'.join([
|
||||
pjoin(HERE, 'node_modules', '.bin'),
|
||||
os.environ.get('PATH', os.defpath),
|
||||
])
|
||||
|
||||
if "--skip-npm" in sys.argv:
|
||||
print("Skipping npm install as requested.")
|
||||
skip_npm = True
|
||||
sys.argv.remove("--skip-npm")
|
||||
else:
|
||||
skip_npm = False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Public Functions
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def get_version(file, name='__version__'):
|
||||
"""Get the version of the package from the given file by
|
||||
executing it and extracting the given `name`.
|
||||
"""
|
||||
path = os.path.realpath(file)
|
||||
version_ns = {}
|
||||
with io.open(path, encoding="utf8") as f:
|
||||
exec(f.read(), {}, version_ns)
|
||||
return version_ns[name]
|
||||
|
||||
|
||||
def ensure_python(specs):
|
||||
"""Given a list of range specifiers for python, ensure compatibility.
|
||||
"""
|
||||
if not isinstance(specs, (list, tuple)):
|
||||
specs = [specs]
|
||||
v = sys.version_info
|
||||
part = '%s.%s' % (v.major, v.minor)
|
||||
for spec in specs:
|
||||
if part == spec:
|
||||
return
|
||||
try:
|
||||
if eval(part + spec):
|
||||
return
|
||||
except SyntaxError:
|
||||
pass
|
||||
raise ValueError('Python version %s unsupported' % part)
|
||||
|
||||
|
||||
def find_packages(top=HERE):
|
||||
"""
|
||||
Find all of the packages.
|
||||
"""
|
||||
packages = []
|
||||
for d, dirs, _ in os.walk(top, followlinks=True):
|
||||
if os.path.exists(pjoin(d, '__init__.py')):
|
||||
packages.append(os.path.relpath(d, top).replace(os.path.sep, '.'))
|
||||
elif d != top:
|
||||
# Do not look for packages in subfolders if current is not a package
|
||||
dirs[:] = []
|
||||
return packages
|
||||
|
||||
|
||||
def update_package_data(distribution):
|
||||
"""update build_py options to get package_data changes"""
|
||||
build_py = distribution.get_command_obj('build_py')
|
||||
build_py.finalize_options()
|
||||
|
||||
|
||||
class bdist_egg_disabled(bdist_egg):
|
||||
"""Disabled version of bdist_egg
|
||||
|
||||
Prevents setup.py install performing setuptools' default easy_install,
|
||||
which it should never ever do.
|
||||
"""
|
||||
def run(self):
|
||||
sys.exit("Aborting implicit building of eggs. Use `pip install .` "
|
||||
" to install from source.")
|
||||
|
||||
|
||||
def create_cmdclass(prerelease_cmd=None, package_data_spec=None,
|
||||
data_files_spec=None):
|
||||
"""Create a command class with the given optional prerelease class.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
prerelease_cmd: (name, Command) tuple, optional
|
||||
The command to run before releasing.
|
||||
package_data_spec: dict, optional
|
||||
A dictionary whose keys are the dotted package names and
|
||||
whose values are a list of glob patterns.
|
||||
data_files_spec: list, optional
|
||||
A list of (path, dname, pattern) tuples where the path is the
|
||||
`data_files` install path, dname is the source directory, and the
|
||||
pattern is a glob pattern.
|
||||
|
||||
Notes
|
||||
-----
|
||||
We use specs so that we can find the files *after* the build
|
||||
command has run.
|
||||
|
||||
The package data glob patterns should be relative paths from the package
|
||||
folder containing the __init__.py file, which is given as the package
|
||||
name.
|
||||
e.g. `dict(foo=['./bar/*', './baz/**'])`
|
||||
|
||||
The data files directories should be absolute paths or relative paths
|
||||
from the root directory of the repository. Data files are specified
|
||||
differently from `package_data` because we need a separate path entry
|
||||
for each nested folder in `data_files`, and this makes it easier to
|
||||
parse.
|
||||
e.g. `('share/foo/bar', 'pkgname/bizz, '*')`
|
||||
"""
|
||||
wrapped = [prerelease_cmd] if prerelease_cmd else []
|
||||
if package_data_spec or data_files_spec:
|
||||
wrapped.append('handle_files')
|
||||
wrapper = functools.partial(_wrap_command, wrapped)
|
||||
handle_files = _get_file_handler(package_data_spec, data_files_spec)
|
||||
|
||||
if 'bdist_egg' in sys.argv:
|
||||
egg = wrapper(bdist_egg, strict=True)
|
||||
else:
|
||||
egg = bdist_egg_disabled
|
||||
|
||||
cmdclass = dict(
|
||||
build_py=wrapper(build_py, strict=is_repo),
|
||||
bdist_egg=egg,
|
||||
sdist=wrapper(sdist, strict=True),
|
||||
handle_files=handle_files,
|
||||
)
|
||||
|
||||
if bdist_wheel:
|
||||
cmdclass['bdist_wheel'] = wrapper(bdist_wheel, strict=True)
|
||||
|
||||
cmdclass['develop'] = wrapper(develop, strict=True)
|
||||
return cmdclass
|
||||
|
||||
|
||||
def command_for_func(func):
|
||||
"""Create a command that calls the given function."""
|
||||
|
||||
class FuncCommand(BaseCommand):
|
||||
|
||||
def run(self):
|
||||
func()
|
||||
update_package_data(self.distribution)
|
||||
|
||||
return FuncCommand
|
||||
|
||||
|
||||
def run(cmd, **kwargs):
|
||||
"""Echo a command before running it. Defaults to repo as cwd"""
|
||||
log.info('> ' + list2cmdline(cmd))
|
||||
kwargs.setdefault('cwd', HERE)
|
||||
kwargs.setdefault('shell', os.name == 'nt')
|
||||
if not isinstance(cmd, (list, tuple)) and os.name != 'nt':
|
||||
cmd = shlex.split(cmd)
|
||||
cmd_path = which(cmd[0])
|
||||
if not cmd_path:
|
||||
sys.exit("Aborting. Could not find cmd (%s) in path. "
|
||||
"If command is not expected to be in user's path, "
|
||||
"use an absolute path." % cmd[0])
|
||||
cmd[0] = cmd_path
|
||||
return subprocess.check_call(cmd, **kwargs)
|
||||
|
||||
|
||||
def is_stale(target, source):
|
||||
"""Test whether the target file/directory is stale based on the source
|
||||
file/directory.
|
||||
"""
|
||||
if not os.path.exists(target):
|
||||
return True
|
||||
target_mtime = recursive_mtime(target) or 0
|
||||
return compare_recursive_mtime(source, cutoff=target_mtime)
|
||||
|
||||
|
||||
class BaseCommand(Command):
|
||||
"""Empty command because Command needs subclasses to override too much"""
|
||||
user_options = []
|
||||
|
||||
def initialize_options(self):
|
||||
pass
|
||||
|
||||
def finalize_options(self):
|
||||
pass
|
||||
|
||||
def get_inputs(self):
|
||||
return []
|
||||
|
||||
def get_outputs(self):
|
||||
return []
|
||||
|
||||
|
||||
def combine_commands(*commands):
|
||||
"""Return a Command that combines several commands."""
|
||||
|
||||
class CombinedCommand(Command):
|
||||
user_options = []
|
||||
|
||||
def initialize_options(self):
|
||||
self.commands = []
|
||||
for C in commands:
|
||||
self.commands.append(C(self.distribution))
|
||||
for c in self.commands:
|
||||
c.initialize_options()
|
||||
|
||||
def finalize_options(self):
|
||||
for c in self.commands:
|
||||
c.finalize_options()
|
||||
|
||||
def run(self):
|
||||
for c in self.commands:
|
||||
c.run()
|
||||
return CombinedCommand
|
||||
|
||||
|
||||
def compare_recursive_mtime(path, cutoff, newest=True):
|
||||
"""Compare the newest/oldest mtime for all files in a directory.
|
||||
|
||||
Cutoff should be another mtime to be compared against. If an mtime that is
|
||||
newer/older than the cutoff is found it will return True.
|
||||
E.g. if newest=True, and a file in path is newer than the cutoff, it will
|
||||
return True.
|
||||
"""
|
||||
if os.path.isfile(path):
|
||||
mt = mtime(path)
|
||||
if newest:
|
||||
if mt > cutoff:
|
||||
return True
|
||||
elif mt < cutoff:
|
||||
return True
|
||||
for dirname, _, filenames in os.walk(path, topdown=False):
|
||||
for filename in filenames:
|
||||
mt = mtime(pjoin(dirname, filename))
|
||||
if newest: # Put outside of loop?
|
||||
if mt > cutoff:
|
||||
return True
|
||||
elif mt < cutoff:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def recursive_mtime(path, newest=True):
|
||||
"""Gets the newest/oldest mtime for all files in a directory."""
|
||||
if os.path.isfile(path):
|
||||
return mtime(path)
|
||||
current_extreme = None
|
||||
for dirname, dirnames, filenames in os.walk(path, topdown=False):
|
||||
for filename in filenames:
|
||||
mt = mtime(pjoin(dirname, filename))
|
||||
if newest: # Put outside of loop?
|
||||
if mt >= (current_extreme or mt):
|
||||
current_extreme = mt
|
||||
elif mt <= (current_extreme or mt):
|
||||
current_extreme = mt
|
||||
return current_extreme
|
||||
|
||||
|
||||
def mtime(path):
|
||||
"""shorthand for mtime"""
|
||||
return os.stat(path).st_mtime
|
||||
|
||||
|
||||
def install_npm(path=None, build_dir=None, source_dir=None, build_cmd='build', force=False, npm=None):
|
||||
"""Return a Command for managing an npm installation.
|
||||
|
||||
Note: The command is skipped if the `--skip-npm` flag is used.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path: str, optional
|
||||
The base path of the node package. Defaults to the repo root.
|
||||
build_dir: str, optional
|
||||
The target build directory. If this and source_dir are given,
|
||||
the JavaScript will only be build if necessary.
|
||||
source_dir: str, optional
|
||||
The source code directory.
|
||||
build_cmd: str, optional
|
||||
The npm command to build assets to the build_dir.
|
||||
npm: str or list, optional.
|
||||
The npm executable name, or a tuple of ['node', executable].
|
||||
"""
|
||||
|
||||
class NPM(BaseCommand):
|
||||
description = 'install package.json dependencies using npm'
|
||||
|
||||
def run(self):
|
||||
if skip_npm:
|
||||
log.info('Skipping npm-installation')
|
||||
return
|
||||
node_package = path or HERE
|
||||
node_modules = pjoin(node_package, 'node_modules')
|
||||
is_yarn = os.path.exists(pjoin(node_package, 'yarn.lock'))
|
||||
|
||||
npm_cmd = npm
|
||||
|
||||
if npm is None:
|
||||
if is_yarn:
|
||||
npm_cmd = ['yarn']
|
||||
else:
|
||||
npm_cmd = ['npm']
|
||||
|
||||
if not which(npm_cmd[0]):
|
||||
log.error("`{0}` unavailable. If you're running this command "
|
||||
"using sudo, make sure `{0}` is available to sudo"
|
||||
.format(npm_cmd[0]))
|
||||
return
|
||||
|
||||
if force or is_stale(node_modules, pjoin(node_package, 'package.json')):
|
||||
log.info('Installing build dependencies with npm. This may '
|
||||
'take a while...')
|
||||
run(npm_cmd + ['install'], cwd=node_package)
|
||||
if build_dir and source_dir and not force:
|
||||
should_build = is_stale(build_dir, source_dir)
|
||||
else:
|
||||
should_build = True
|
||||
if should_build:
|
||||
run(npm_cmd + ['run', build_cmd], cwd=node_package)
|
||||
|
||||
return NPM
|
||||
|
||||
|
||||
def ensure_targets(targets):
|
||||
"""Return a Command that checks that certain files exist.
|
||||
|
||||
Raises a ValueError if any of the files are missing.
|
||||
|
||||
Note: The check is skipped if the `--skip-npm` flag is used.
|
||||
"""
|
||||
|
||||
class TargetsCheck(BaseCommand):
|
||||
def run(self):
|
||||
if skip_npm:
|
||||
log.info('Skipping target checks')
|
||||
return
|
||||
missing = [t for t in targets if not os.path.exists(t)]
|
||||
if missing:
|
||||
raise ValueError(('missing files: %s' % missing))
|
||||
|
||||
return TargetsCheck
|
||||
|
||||
|
||||
# `shutils.which` function copied verbatim from the Python-3.3 source.
|
||||
def which(cmd, mode=os.F_OK | os.X_OK, path=None):
|
||||
"""Given a command, mode, and a PATH string, return the path which
|
||||
conforms to the given mode on the PATH, or None if there is no such
|
||||
file.
|
||||
`mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result
|
||||
of os.environ.get("PATH"), or can be overridden with a custom search
|
||||
path.
|
||||
"""
|
||||
|
||||
# Check that a given file can be accessed with the correct mode.
|
||||
# Additionally check that `file` is not a directory, as on Windows
|
||||
# directories pass the os.access check.
|
||||
def _access_check(fn, mode):
|
||||
return (os.path.exists(fn) and os.access(fn, mode) and
|
||||
not os.path.isdir(fn))
|
||||
|
||||
# Short circuit. If we're given a full path which matches the mode
|
||||
# and it exists, we're done here.
|
||||
if _access_check(cmd, mode):
|
||||
return cmd
|
||||
|
||||
path = (path or os.environ.get("PATH", os.defpath)).split(os.pathsep)
|
||||
|
||||
if sys.platform == "win32":
|
||||
# The current directory takes precedence on Windows.
|
||||
if os.curdir not in path:
|
||||
path.insert(0, os.curdir)
|
||||
|
||||
# PATHEXT is necessary to check on Windows.
|
||||
pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
|
||||
# See if the given file matches any of the expected path extensions.
|
||||
# This will allow us to short circuit when given "python.exe".
|
||||
matches = [cmd for ext in pathext if cmd.lower().endswith(ext.lower())]
|
||||
# If it does match, only test that one, otherwise we have to try
|
||||
# others.
|
||||
files = [cmd] if matches else [cmd + ext.lower() for ext in pathext]
|
||||
else:
|
||||
# On other platforms you don't have things like PATHEXT to tell you
|
||||
# what file suffixes are executable, so just pass on cmd as-is.
|
||||
files = [cmd]
|
||||
|
||||
seen = set()
|
||||
for dir in path:
|
||||
dir = os.path.normcase(dir)
|
||||
if dir not in seen:
|
||||
seen.add(dir)
|
||||
for thefile in files:
|
||||
name = os.path.join(dir, thefile)
|
||||
if _access_check(name, mode):
|
||||
return name
|
||||
return None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Private Functions
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _wrap_command(cmds, cls, strict=True):
|
||||
"""Wrap a setup command
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cmds: list(str)
|
||||
The names of the other commands to run prior to the command.
|
||||
strict: boolean, optional
|
||||
Wether to raise errors when a pre-command fails.
|
||||
"""
|
||||
class WrappedCommand(cls):
|
||||
|
||||
def run(self):
|
||||
if not getattr(self, 'uninstall', None):
|
||||
try:
|
||||
[self.run_command(cmd) for cmd in cmds]
|
||||
except Exception:
|
||||
if strict:
|
||||
raise
|
||||
else:
|
||||
pass
|
||||
# update package data
|
||||
update_package_data(self.distribution)
|
||||
|
||||
result = cls.run(self)
|
||||
return result
|
||||
return WrappedCommand
|
||||
|
||||
|
||||
def _get_file_handler(package_data_spec, data_files_spec):
|
||||
"""Get a package_data and data_files handler command.
|
||||
"""
|
||||
class FileHandler(BaseCommand):
|
||||
|
||||
def run(self):
|
||||
package_data = self.distribution.package_data
|
||||
package_spec = package_data_spec or dict()
|
||||
|
||||
for (key, patterns) in package_spec.items():
|
||||
package_data[key] = _get_package_data(key, patterns)
|
||||
|
||||
self.distribution.data_files = _get_data_files(
|
||||
data_files_spec, self.distribution.data_files
|
||||
)
|
||||
|
||||
return FileHandler
|
||||
|
||||
|
||||
def _glob_pjoin(*parts):
|
||||
"""Join paths for glob processing"""
|
||||
if parts[0] in ('.', ''):
|
||||
parts = parts[1:]
|
||||
return pjoin(*parts).replace(os.sep, '/')
|
||||
|
||||
|
||||
def _get_data_files(data_specs, existing, top=HERE):
|
||||
"""Expand data file specs into valid data files metadata.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data_specs: list of tuples
|
||||
See [create_cmdclass] for description.
|
||||
existing: list of tuples
|
||||
The existing distrubution data_files metadata.
|
||||
|
||||
Returns
|
||||
-------
|
||||
A valid list of data_files items.
|
||||
"""
|
||||
# Extract the existing data files into a staging object.
|
||||
file_data = defaultdict(list)
|
||||
for (path, files) in existing or []:
|
||||
file_data[path] = files
|
||||
|
||||
# Extract the files and assign them to the proper data
|
||||
# files path.
|
||||
for (path, dname, pattern) in data_specs or []:
|
||||
if os.path.isabs(dname):
|
||||
dname = os.path.relpath(dname, top)
|
||||
dname = dname.replace(os.sep, '/')
|
||||
offset = 0 if dname in ('.', '') else len(dname) + 1
|
||||
files = _get_files(_glob_pjoin(dname, pattern), top=top)
|
||||
for fname in files:
|
||||
# Normalize the path.
|
||||
root = os.path.dirname(fname)
|
||||
full_path = _glob_pjoin(path, root[offset:])
|
||||
print(dname, root, full_path, offset)
|
||||
if full_path.endswith('/'):
|
||||
full_path = full_path[:-1]
|
||||
file_data[full_path].append(fname)
|
||||
|
||||
# Construct the data files spec.
|
||||
data_files = []
|
||||
for (path, files) in file_data.items():
|
||||
data_files.append((path, files))
|
||||
return data_files
|
||||
|
||||
|
||||
def _get_files(file_patterns, top=HERE):
|
||||
"""Expand file patterns to a list of paths.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
file_patterns: list or str
|
||||
A list of glob patterns for the data file locations.
|
||||
The globs can be recursive if they include a `**`.
|
||||
They should be relative paths from the top directory or
|
||||
absolute paths.
|
||||
top: str
|
||||
the directory to consider for data files
|
||||
|
||||
Note:
|
||||
Files in `node_modules` are ignored.
|
||||
"""
|
||||
if not isinstance(file_patterns, (list, tuple)):
|
||||
file_patterns = [file_patterns]
|
||||
|
||||
for i, p in enumerate(file_patterns):
|
||||
if os.path.isabs(p):
|
||||
file_patterns[i] = os.path.relpath(p, top)
|
||||
|
||||
matchers = [_compile_pattern(p) for p in file_patterns]
|
||||
|
||||
files = set()
|
||||
|
||||
for root, dirnames, filenames in os.walk(top):
|
||||
# Don't recurse into node_modules
|
||||
if 'node_modules' in dirnames:
|
||||
dirnames.remove('node_modules')
|
||||
for m in matchers:
|
||||
for filename in filenames:
|
||||
fn = os.path.relpath(_glob_pjoin(root, filename), top)
|
||||
fn = fn.replace(os.sep, '/')
|
||||
if m(fn):
|
||||
files.add(fn.replace(os.sep, '/'))
|
||||
|
||||
return list(files)
|
||||
|
||||
|
||||
def _get_package_data(root, file_patterns=None):
|
||||
"""Expand file patterns to a list of `package_data` paths.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
root: str
|
||||
The relative path to the package root from `HERE`.
|
||||
file_patterns: list or str, optional
|
||||
A list of glob patterns for the data file locations.
|
||||
The globs can be recursive if they include a `**`.
|
||||
They should be relative paths from the root or
|
||||
absolute paths. If not given, all files will be used.
|
||||
|
||||
Note:
|
||||
Files in `node_modules` are ignored.
|
||||
"""
|
||||
if file_patterns is None:
|
||||
file_patterns = ['*']
|
||||
return _get_files(file_patterns, _glob_pjoin(HERE, root))
|
||||
|
||||
|
||||
def _compile_pattern(pat, ignore_case=True):
|
||||
"""Translate and compile a glob pattern to a regular expression matcher."""
|
||||
if isinstance(pat, bytes):
|
||||
pat_str = pat.decode('ISO-8859-1')
|
||||
res_str = _translate_glob(pat_str)
|
||||
res = res_str.encode('ISO-8859-1')
|
||||
else:
|
||||
res = _translate_glob(pat)
|
||||
flags = re.IGNORECASE if ignore_case else 0
|
||||
return re.compile(res, flags=flags).match
|
||||
|
||||
|
||||
def _iexplode_path(path):
|
||||
"""Iterate over all the parts of a path.
|
||||
|
||||
Splits path recursively with os.path.split().
|
||||
"""
|
||||
(head, tail) = os.path.split(path)
|
||||
if not head or (not tail and head == path):
|
||||
if head:
|
||||
yield head
|
||||
if tail or not head:
|
||||
yield tail
|
||||
return
|
||||
for p in _iexplode_path(head):
|
||||
yield p
|
||||
yield tail
|
||||
|
||||
|
||||
def _translate_glob(pat):
|
||||
"""Translate a glob PATTERN to a regular expression."""
|
||||
translated_parts = []
|
||||
for part in _iexplode_path(pat):
|
||||
translated_parts.append(_translate_glob_part(part))
|
||||
os_sep_class = '[%s]' % re.escape(SEPARATORS)
|
||||
res = _join_translated(translated_parts, os_sep_class)
|
||||
return '{res}\\Z(?ms)'.format(res=res)
|
||||
|
||||
|
||||
def _join_translated(translated_parts, os_sep_class):
|
||||
"""Join translated glob pattern parts.
|
||||
|
||||
This is different from a simple join, as care need to be taken
|
||||
to allow ** to match ZERO or more directories.
|
||||
"""
|
||||
res = ''
|
||||
for part in translated_parts[:-1]:
|
||||
if part == '.*':
|
||||
# drop separator, since it is optional
|
||||
# (** matches ZERO or more dirs)
|
||||
res += part
|
||||
else:
|
||||
res += part + os_sep_class
|
||||
|
||||
if translated_parts[-1] == '.*':
|
||||
# Final part is **
|
||||
res += '.+'
|
||||
# Follow stdlib/git convention of matching all sub files/directories:
|
||||
res += '({os_sep_class}?.*)?'.format(os_sep_class=os_sep_class)
|
||||
else:
|
||||
res += translated_parts[-1]
|
||||
return res
|
||||
|
||||
|
||||
def _translate_glob_part(pat):
|
||||
"""Translate a glob PATTERN PART to a regular expression."""
|
||||
# Code modified from Python 3 standard lib fnmatch:
|
||||
if pat == '**':
|
||||
return '.*'
|
||||
i, n = 0, len(pat)
|
||||
res = []
|
||||
while i < n:
|
||||
c = pat[i]
|
||||
i = i + 1
|
||||
if c == '*':
|
||||
# Match anything but path separators:
|
||||
res.append('[^%s]*' % SEPARATORS)
|
||||
elif c == '?':
|
||||
res.append('[^%s]?' % SEPARATORS)
|
||||
elif c == '[':
|
||||
j = i
|
||||
if j < n and pat[j] == '!':
|
||||
j = j + 1
|
||||
if j < n and pat[j] == ']':
|
||||
j = j + 1
|
||||
while j < n and pat[j] != ']':
|
||||
j = j + 1
|
||||
if j >= n:
|
||||
res.append('\\[')
|
||||
else:
|
||||
stuff = pat[i:j].replace('\\', '\\\\')
|
||||
i = j + 1
|
||||
if stuff[0] == '!':
|
||||
stuff = '^' + stuff[1:]
|
||||
elif stuff[0] == '^':
|
||||
stuff = '\\' + stuff
|
||||
res.append('[%s]' % stuff)
|
||||
else:
|
||||
res.append(re.escape(c))
|
||||
return ''.join(res)
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) Jupyter Development Team.
|
||||
// Distributed under the terms of the Modified BSD License.
|
||||
|
||||
// Entry point for the notebook bundle containing custom model definitions.
|
||||
//
|
||||
// Setup notebook base URL
|
||||
//
|
||||
// Some static assets may be required by the custom widget javascript. The base
|
||||
// url for the notebook is not known at build time and is therefore computed
|
||||
// dynamically.
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
(window as any).__webpack_public_path__ =
|
||||
document.querySelector('body')!.getAttribute('data-base-url') +
|
||||
'nbextensions/powerbi_widget';
|
||||
|
||||
export * from './index';
|
|
@ -0,0 +1,5 @@
|
|||
// Copyright (c) Microsoft
|
||||
// Distributed under the terms of the Modified BSD License.
|
||||
|
||||
export * from './version';
|
||||
export * from './widget';
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) Microsoft
|
||||
// Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import { Application, IPlugin } from '@phosphor/application';
|
||||
|
||||
import { Widget } from '@phosphor/widgets';
|
||||
|
||||
import { IJupyterWidgetRegistry } from '@jupyter-widgets/base';
|
||||
|
||||
import * as widgetExports from './widget';
|
||||
|
||||
import { MODULE_NAME, MODULE_VERSION } from './version';
|
||||
|
||||
const EXTENSION_ID = 'powerbi-widget-client:plugin';
|
||||
|
||||
/**
|
||||
* The example plugin.
|
||||
*/
|
||||
const examplePlugin: IPlugin<Application<Widget>, void> = ({
|
||||
id: EXTENSION_ID,
|
||||
requires: [IJupyterWidgetRegistry],
|
||||
activate: activateWidgetExtension,
|
||||
autoStart: true,
|
||||
} as unknown) as IPlugin<Application<Widget>, void>;
|
||||
// the "as unknown as ..." typecast above is solely to support JupyterLab 1
|
||||
// and 2 in the same codebase and should be removed when we migrate to Lumino.
|
||||
|
||||
export default examplePlugin;
|
||||
|
||||
/**
|
||||
* Activate the widget extension.
|
||||
*/
|
||||
function activateWidgetExtension(
|
||||
app: Application<Widget>,
|
||||
registry: IJupyterWidgetRegistry
|
||||
): void {
|
||||
registry.registerWidget({
|
||||
name: MODULE_NAME,
|
||||
version: MODULE_VERSION,
|
||||
exports: widgetExports,
|
||||
});
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) Microsoft
|
||||
// Distributed under the terms of the Modified BSD License.
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const data = require('../package.json');
|
||||
|
||||
/**
|
||||
* The _model_module_version/_view_module_version this package implements.
|
||||
*
|
||||
* The html widget manager assumes that this is the same as the npm package
|
||||
* version number.
|
||||
*/
|
||||
export const MODULE_VERSION = data.version;
|
||||
|
||||
/*
|
||||
* The current package name.
|
||||
*/
|
||||
export const MODULE_NAME = data.name;
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright (c) Microsoft
|
||||
// Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import {
|
||||
DOMWidgetModel,
|
||||
DOMWidgetView,
|
||||
ISerializers,
|
||||
} from '@jupyter-widgets/base';
|
||||
|
||||
import { MODULE_NAME, MODULE_VERSION } from './version';
|
||||
|
||||
// Import the CSS
|
||||
import '../css/widget.css';
|
||||
|
||||
export class ReportModel extends DOMWidgetModel {
|
||||
defaults() {
|
||||
return {
|
||||
...super.defaults(),
|
||||
_model_name: ReportModel.model_name,
|
||||
_model_module: ReportModel.model_module,
|
||||
_model_module_version: ReportModel.model_module_version,
|
||||
_view_name: ReportModel.view_name,
|
||||
_view_module: ReportModel.view_module,
|
||||
_view_module_version: ReportModel.view_module_version,
|
||||
value: 'Hello World',
|
||||
};
|
||||
}
|
||||
|
||||
static serializers: ISerializers = {
|
||||
...DOMWidgetModel.serializers,
|
||||
// Add any extra serializers here
|
||||
};
|
||||
|
||||
static model_name = 'ReportModel';
|
||||
static model_module = MODULE_NAME;
|
||||
static model_module_version = MODULE_VERSION;
|
||||
static view_name = 'ReportView'; // Set to null if no view
|
||||
static view_module = MODULE_NAME; // Set to null if no view
|
||||
static view_module_version = MODULE_VERSION;
|
||||
}
|
||||
|
||||
export class ReportView extends DOMWidgetView {
|
||||
render() {
|
||||
this.el.classList.add('custom-widget');
|
||||
|
||||
this.value_changed();
|
||||
this.model.on('change:value', this.value_changed, this);
|
||||
}
|
||||
|
||||
value_changed() {
|
||||
this.el.textContent = this.model.get('value');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import powerbi_widget"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "a03c468f7a384a6b9a30f8f7cc080ab7",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
"text/plain": [
|
||||
"ExampleWidget()"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"powerbi_widget.ExampleWidget()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.0"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '..',
|
||||
frameworks: ['mocha', 'karma-typescript'],
|
||||
reporters: ['mocha', 'karma-typescript'],
|
||||
client: {
|
||||
mocha: {
|
||||
timeout : 10000, // 10 seconds - upped from 2 seconds
|
||||
retries: 3 // Allow for slow server on CI.
|
||||
}
|
||||
},
|
||||
files: [
|
||||
{ pattern: "tests/src/**/*.ts" },
|
||||
{ pattern: "src/**/*.ts" },
|
||||
],
|
||||
exclude: [
|
||||
"src/extension.ts",
|
||||
],
|
||||
preprocessors: {
|
||||
'**/*.ts': ['karma-typescript']
|
||||
},
|
||||
browserNoActivityTimeout: 31000, // 31 seconds - upped from 10 seconds
|
||||
port: 9876,
|
||||
colors: true,
|
||||
singleRun: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
|
||||
|
||||
karmaTypescriptConfig: {
|
||||
tsconfig: 'tests/tsconfig.json',
|
||||
reports: {
|
||||
"text-summary": "",
|
||||
"html": "coverage",
|
||||
"lcovonly": {
|
||||
"directory": "coverage",
|
||||
"filename": "coverage.lcov"
|
||||
}
|
||||
},
|
||||
bundlerOptions: {
|
||||
transforms: [
|
||||
require("karma-typescript-es6-transform")()
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) Jupyter Development Team.
|
||||
// Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import expect = require('expect.js');
|
||||
|
||||
import {
|
||||
// Add any needed widget imports here (or from controls)
|
||||
} from '@jupyter-widgets/base';
|
||||
|
||||
import {
|
||||
createTestModel
|
||||
} from './utils.spec';
|
||||
|
||||
import {
|
||||
ReportModel, ReportView
|
||||
} from '../../src/'
|
||||
|
||||
|
||||
describe('Example', () => {
|
||||
|
||||
describe('ReportModel', () => {
|
||||
|
||||
it('should be createable', () => {
|
||||
let model = createTestModel(ReportModel);
|
||||
expect(model).to.be.an(ReportModel);
|
||||
expect(model.get('value')).to.be('Hello World');
|
||||
});
|
||||
|
||||
it('should be createable with a value', () => {
|
||||
let state = { value: 'Foo Bar!' }
|
||||
let model = createTestModel(ReportModel, state);
|
||||
expect(model).to.be.an(ReportModel);
|
||||
expect(model.get('value')).to.be('Foo Bar!');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,113 @@
|
|||
// Copyright (c) Jupyter Development Team.
|
||||
// Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import * as widgets from '@jupyter-widgets/base';
|
||||
import * as services from '@jupyterlab/services';
|
||||
import * as Backbone from 'backbone';
|
||||
|
||||
let numComms = 0;
|
||||
|
||||
export
|
||||
class MockComm {
|
||||
target_name = 'dummy';
|
||||
|
||||
constructor() {
|
||||
this.comm_id = `mock-comm-id-${numComms}`;
|
||||
numComms += 1;
|
||||
}
|
||||
on_close(fn: Function | null) {
|
||||
this._on_close = fn;
|
||||
}
|
||||
on_msg(fn: Function | null) {
|
||||
this._on_msg = fn;
|
||||
}
|
||||
_process_msg(msg: services.KernelMessage.ICommMsgMsg) {
|
||||
if (this._on_msg) {
|
||||
return this._on_msg(msg);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
close(): string {
|
||||
if (this._on_close) {
|
||||
this._on_close();
|
||||
}
|
||||
return 'dummy';
|
||||
}
|
||||
send(): string {
|
||||
return 'dummy';
|
||||
}
|
||||
|
||||
open(): string {
|
||||
return 'dummy';
|
||||
}
|
||||
comm_id: string;
|
||||
_on_msg: Function | null = null;
|
||||
_on_close: Function | null = null;
|
||||
}
|
||||
|
||||
export
|
||||
class DummyManager extends widgets.ManagerBase<HTMLElement> {
|
||||
constructor() {
|
||||
super();
|
||||
this.el = window.document.createElement('div');
|
||||
}
|
||||
|
||||
display_view(msg: services.KernelMessage.IMessage, view: Backbone.View<Backbone.Model>, options: any) {
|
||||
// TODO: make this a spy
|
||||
// TODO: return an html element
|
||||
return Promise.resolve(view).then(view => {
|
||||
this.el.appendChild(view.el);
|
||||
view.on('remove', () => console.log('view removed', view));
|
||||
return view.el;
|
||||
});
|
||||
}
|
||||
|
||||
protected loadClass(className: string, moduleName: string, moduleVersion: string): Promise<any> {
|
||||
if (moduleName === '@jupyter-widgets/base') {
|
||||
if ((widgets as any)[className]) {
|
||||
return Promise.resolve((widgets as any)[className]);
|
||||
} else {
|
||||
return Promise.reject(`Cannot find class ${className}`)
|
||||
}
|
||||
} else if (moduleName === 'jupyter-datawidgets') {
|
||||
if (this.testClasses[className]) {
|
||||
return Promise.resolve(this.testClasses[className]);
|
||||
} else {
|
||||
return Promise.reject(`Cannot find class ${className}`)
|
||||
}
|
||||
} else {
|
||||
return Promise.reject(`Cannot find module ${moduleName}`);
|
||||
}
|
||||
}
|
||||
|
||||
_get_comm_info() {
|
||||
return Promise.resolve({});
|
||||
}
|
||||
|
||||
_create_comm() {
|
||||
return Promise.resolve(new MockComm());
|
||||
}
|
||||
|
||||
el: HTMLElement;
|
||||
|
||||
testClasses: { [key: string]: any } = {};
|
||||
}
|
||||
|
||||
|
||||
export
|
||||
interface Constructor<T> {
|
||||
new (attributes?: any, options?: any): T;
|
||||
}
|
||||
|
||||
export
|
||||
function createTestModel<T extends widgets.WidgetModel>(constructor: Constructor<T>, attributes?: any): T {
|
||||
let id = widgets.uuid();
|
||||
let widget_manager = new DummyManager();
|
||||
let modelOptions = {
|
||||
widget_manager: widget_manager,
|
||||
model_id: id,
|
||||
}
|
||||
|
||||
return new constructor(attributes, modelOptions);
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"noImplicitAny": true,
|
||||
"lib": ["dom", "es5", "es2015.promise", "es2015.iterable"],
|
||||
"noEmitOnError": true,
|
||||
"strictNullChecks": true,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"target": "ES5",
|
||||
"outDir": "build",
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": [
|
||||
"src/*.ts",
|
||||
"../src/**/*.ts"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"esModuleInterop":true,
|
||||
"lib": ["es2015", "dom"],
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"noEmitOnError": true,
|
||||
"noUnusedLocals": true,
|
||||
"outDir": "lib",
|
||||
"resolveJsonModule": true,
|
||||
"rootDir": "src",
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"strictPropertyInitialization": false,
|
||||
"target": "es2015"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
]
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
const path = require('path');
|
||||
const version = require('./package.json').version;
|
||||
|
||||
// Custom webpack rules
|
||||
const rules = [
|
||||
{ test: /\.ts$/, loader: 'ts-loader' },
|
||||
{ test: /\.js$/, loader: 'source-map-loader' },
|
||||
{ test: /\.css$/, use: ['style-loader', 'css-loader']}
|
||||
];
|
||||
|
||||
// Packages that shouldn't be bundled but loaded at runtime
|
||||
const externals = ['@jupyter-widgets/base'];
|
||||
|
||||
const resolve = {
|
||||
// Add '.ts' and '.tsx' as resolvable extensions.
|
||||
extensions: [".webpack.js", ".web.js", ".ts", ".js"]
|
||||
};
|
||||
|
||||
module.exports = [
|
||||
/**
|
||||
* Notebook extension
|
||||
*
|
||||
* This bundle only contains the part of the JavaScript that is run on load of
|
||||
* the notebook.
|
||||
*/
|
||||
{
|
||||
entry: './src/extension.ts',
|
||||
output: {
|
||||
filename: 'index.js',
|
||||
path: path.resolve(__dirname, 'powerbi_widget', 'nbextension', 'static'),
|
||||
libraryTarget: 'amd'
|
||||
},
|
||||
module: {
|
||||
rules: rules
|
||||
},
|
||||
devtool: 'source-map',
|
||||
externals,
|
||||
resolve,
|
||||
},
|
||||
|
||||
/**
|
||||
* Embeddable powerbi-widget-client bundle
|
||||
*
|
||||
* This bundle is almost identical to the notebook extension bundle. The only
|
||||
* difference is in the configuration of the webpack public path for the
|
||||
* static assets.
|
||||
*
|
||||
* The target bundle is always `dist/index.js`, which is the path required by
|
||||
* the custom widget embedder.
|
||||
*/
|
||||
{
|
||||
entry: './src/index.ts',
|
||||
output: {
|
||||
filename: 'index.js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
libraryTarget: 'amd',
|
||||
library: "powerbi-widget-client",
|
||||
publicPath: 'https://unpkg.com/powerbi-widget-client@' + version + '/dist/'
|
||||
},
|
||||
devtool: 'source-map',
|
||||
module: {
|
||||
rules: rules
|
||||
},
|
||||
externals,
|
||||
resolve,
|
||||
},
|
||||
|
||||
/**
|
||||
* Documentation widget bundle
|
||||
*
|
||||
* This bundle is used to embed widgets in the package documentation.
|
||||
*/
|
||||
{
|
||||
entry: './src/index.ts',
|
||||
output: {
|
||||
filename: 'embed-bundle.js',
|
||||
path: path.resolve(__dirname, 'docs', 'source', '_static'),
|
||||
library: "powerbi-widget-client",
|
||||
libraryTarget: 'amd'
|
||||
},
|
||||
module: {
|
||||
rules: rules
|
||||
},
|
||||
devtool: 'source-map',
|
||||
externals,
|
||||
resolve,
|
||||
}
|
||||
];
|
Загрузка…
Ссылка в новой задаче