diff --git a/.buildinfo b/.buildinfo new file mode 100644 index 0000000..7d08cc5 --- /dev/null +++ b/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 30bb7cd181cdb7683f68dc9df09b2c07 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/.doctrees/changelog.doctree b/.doctrees/changelog.doctree new file mode 100644 index 0000000..b3e3978 Binary files /dev/null and b/.doctrees/changelog.doctree differ diff --git a/.doctrees/contact.doctree b/.doctrees/contact.doctree new file mode 100644 index 0000000..324b66b Binary files /dev/null and b/.doctrees/contact.doctree differ diff --git a/.doctrees/dev.doctree b/.doctrees/dev.doctree new file mode 100644 index 0000000..1ed5ecf Binary files /dev/null and b/.doctrees/dev.doctree differ diff --git a/.doctrees/environment.pickle b/.doctrees/environment.pickle new file mode 100644 index 0000000..5c5c3ec Binary files /dev/null and b/.doctrees/environment.pickle differ diff --git a/.doctrees/examples/asset_scripts.doctree b/.doctrees/examples/asset_scripts.doctree new file mode 100644 index 0000000..c0f6092 Binary files /dev/null and b/.doctrees/examples/asset_scripts.doctree differ diff --git a/.doctrees/examples/demo_reports.doctree b/.doctrees/examples/demo_reports.doctree new file mode 100644 index 0000000..f093cb2 Binary files /dev/null and b/.doctrees/examples/demo_reports.doctree differ diff --git a/.doctrees/examples/index.doctree b/.doctrees/examples/index.doctree new file mode 100644 index 0000000..051833d Binary files /dev/null and b/.doctrees/examples/index.doctree differ diff --git a/.doctrees/index.doctree b/.doctrees/index.doctree new file mode 100644 index 0000000..72b43d6 Binary files /dev/null and b/.doctrees/index.doctree differ diff --git a/.doctrees/installation.doctree b/.doctrees/installation.doctree new file mode 100644 index 0000000..a635035 Binary files /dev/null and b/.doctrees/installation.doctree differ diff --git a/.doctrees/intro.doctree b/.doctrees/intro.doctree new file mode 100644 index 0000000..d349bdc Binary files /dev/null and b/.doctrees/intro.doctree differ diff --git a/.doctrees/plugins/index.doctree b/.doctrees/plugins/index.doctree new file mode 100644 index 0000000..00b0446 Binary files /dev/null and b/.doctrees/plugins/index.doctree differ diff --git a/.doctrees/plugins/plugin.doctree b/.doctrees/plugins/plugin.doctree new file mode 100644 index 0000000..ff9d716 Binary files /dev/null and b/.doctrees/plugins/plugin.doctree differ diff --git a/.doctrees/reference/api.doctree b/.doctrees/reference/api.doctree new file mode 100644 index 0000000..639669f Binary files /dev/null and b/.doctrees/reference/api.doctree differ diff --git a/.doctrees/reference/assets.doctree b/.doctrees/reference/assets.doctree new file mode 100644 index 0000000..c91526d Binary files /dev/null and b/.doctrees/reference/assets.doctree differ diff --git a/.doctrees/reference/building.doctree b/.doctrees/reference/building.doctree new file mode 100644 index 0000000..1148825 Binary files /dev/null and b/.doctrees/reference/building.doctree differ diff --git a/.doctrees/reference/cli.doctree b/.doctrees/reference/cli.doctree new file mode 100644 index 0000000..3b6aae3 Binary files /dev/null and b/.doctrees/reference/cli.doctree differ diff --git a/.doctrees/reference/components.doctree b/.doctrees/reference/components.doctree new file mode 100644 index 0000000..a68d9f8 Binary files /dev/null and b/.doctrees/reference/components.doctree differ diff --git a/.doctrees/reference/directive.doctree b/.doctrees/reference/directive.doctree new file mode 100644 index 0000000..f8501f2 Binary files /dev/null and b/.doctrees/reference/directive.doctree differ diff --git a/.doctrees/reference/index.doctree b/.doctrees/reference/index.doctree new file mode 100644 index 0000000..792d131 Binary files /dev/null and b/.doctrees/reference/index.doctree differ diff --git a/.doctrees/reference/project_structure.doctree b/.doctrees/reference/project_structure.doctree new file mode 100644 index 0000000..98aa883 Binary files /dev/null and b/.doctrees/reference/project_structure.doctree differ diff --git a/.doctrees/reference/settings.doctree b/.doctrees/reference/settings.doctree new file mode 100644 index 0000000..d5010d1 Binary files /dev/null and b/.doctrees/reference/settings.doctree differ diff --git a/.doctrees/reference/templating.doctree b/.doctrees/reference/templating.doctree new file mode 100644 index 0000000..d2db249 Binary files /dev/null and b/.doctrees/reference/templating.doctree differ diff --git a/.doctrees/template_designer_guide.doctree b/.doctrees/template_designer_guide.doctree new file mode 100644 index 0000000..1e1cff3 Binary files /dev/null and b/.doctrees/template_designer_guide.doctree differ diff --git a/.doctrees/user_guide.doctree b/.doctrees/user_guide.doctree new file mode 100644 index 0000000..4e7c5c9 Binary files /dev/null and b/.doctrees/user_guide.doctree differ diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/_images/build_flow.svg b/_images/build_flow.svg new file mode 100644 index 0000000..f3fd92e --- /dev/null +++ b/_images/build_flow.svg @@ -0,0 +1,16 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1daVfi2Lr+Xr/CVffLvedcdTAwMWM5e1x1MDAxZfrLXag4XHUwMDAwooI4cOssV4BcdTAwMDBRXGJTXHUwMDEwoVf/97s3KoRMhMlKVzfVbZVcdTAwMTkgJM/7vPO7f/92cPDdmfTM779cdTAwMWR8N99qRtuqXHUwMDBmjPH3f+ntr+ZgaHVttVx1MDAwYs1+XHUwMDFmdkeD2uzIluP0hr/9+9+LM1JWrZey7IZlm107Vet23t/CbJtcdTAwMWTTdobqpP9Tv1x1MDAxZlx1MDAxY/w+++n60IFZc1xmu9k2ZyfMdi0+V1x1MDAwMujdWujas2uAXHUwMDE0S8YgQ2x+hDU8UZ/nmHW1u2G0h+Zij970XHUwMDFkXHUwMDE3qseHPVx1MDAwYtR4r/H0fGJdWlx1MDAwNlx1MDAxMIuPbVjtdsmZtGeXNeyq77XYN3RcdTAwMDbdXHUwMDE3896qOy392Z7t87PqxrBluk5cdTAwMWJ0R82WbVx1MDAwZYdLJ3V7Rs1yJnpcdTAwMWJcdTAwMDDzre834beDxZY39ZvAi+83O0OKXHUwMDE09Xz8cbfdXHUwMDFk6I//L1wiKaB88flVo/bSVFx1MDAxN2HXXHUwMDE3xzSA/rM4ZvzxpTDF820t02q2XHUwMDFjtZFcdTAwMTGSXHUwMDAyrpfrm5uz+4xcdTAwMTHFXHUwMDEwSLC4Sv15vYv67JH/x3ujWsag93E/vlx1MDAwZvUvrmvVl5l5x8vH6Vx1MDAxZrvUTsRcdTAwMWGF3FNl+NLF+XapmpuKUsOen6tcdTAwMGWoo6pcdTAwMDOu0j3ZrzFBn4v1a5G5dFx1MDAxZpDOkvLg4fomd/VQKYGKNXrCReg+oPZ2k33swsar83A8rtDu02T4tPRcdTAwMGX8On9RXHUwMDAy9/h4NOg8PlXotdOu5b9/7P/P7O8//rVcdTAwMDHIXHUwMDA14WEgXHUwMDE3XHUwMDEyISpcdTAwMThDsTGORH3KbZyptthcdTAwMDXJZszK+Wv3eqdcdTAwMTj3nLVcdTAwMDOIM+aCtD6HYlx1MDAxY1x1MDAwMXIoXHUwMDE4MaqRIFx1MDAxZrcsx/RDXHUwMDFjXHUwMDExuYRmXHUwMDE3aueIVzfch3FBXHUwMDEwZlx1MDAxY4P9YnwlXHUwMDAwR09vo07nRZ5kjqdXV/XWc9ZsdNxcdTAwMDf0y0/nQ2c0vsiWb0sgc9hNT4pnq4Vge1xiM1x1MDAxYVxuYSSoQFx1MDAxMkJcdTAwMTJcdTAwMWLCj4/W09XJ+O753M61ipOXVlx1MDAxM1x1MDAxOIWE0zSEUnp5ei8sTVx0TUn3K4izuVx1MDAxN8GQU1x1MDAwMlx1MDAwMNozSa/kYPM1/XJcdTAwMDTI9LbaLL5cdTAwMGVcdTAwMWH5xlPu7HlcdTAwMDfwQyhcdTAwMTR+UCCEXHUwMDAwhyQ+hbbGTetcdTAwMTJcdTAwMGXB+cXVIbhLXHUwMDFi2d7RIdxcdTAwMDR/KFxmfzunUIhcdTAwMDT3cChi4VxmXG6qXFyJ5SZcZkpAXHUwMDAw4Fx1MDAxMGDLtIp9+INcdTAwMDBhqJTZnlx1MDAxMfh4Wm1b2ev64bhXPsn3ruWzeG65XHUwMDExmHtcdTAwMWXBQlx1MDAwNTxC6y1bt8XRuJ8hLFx1MDAxYYHvoCCD2/GNM35p0cp9fXqFMGKVo8VVRcBUP1x1MDAxZsK87IB9t1x1MDAxNivltCTZxH+nOYEh0u/Hhe+hz17rsc7PNVx1MDAxNzaFyFx1MDAwN+hcdTAwMTAgSnEj4Vx1MDAxMv1cdTAwMDVlYCy9W+eUwSRcdTAwMTFcdTAwMThQXHUwMDE2x7PYTqNvoI/f4Vir3j9cdTAwMTRMJ0/Ox+d3Jy+H6d7h3dRcdTAwMGZHx3xzPEhcdTAwMTRgXHSJXGL4kehcdTAwMTbTOfQw3S3WnIFhXHUwMDBme8ZA3bdcdTAwMTC8tYxaazQw/0SII5r7MFx1MDAxNoGAQ6GAY8o+klx1MDAxY3KxXHUwMDEx3uxRu7148PqBq+u7VtdndFtcdTAwMDfXg+6zoiTXXHUwMDFk7tpOyZq+W2ZLW0+NjtWeLN0k/W7pttXU1/+9bTacJeXrWMr5n+92um6SNoZm27Jnn0IjYNwwbk5uh/bL6232nrf7Lyc35/lBbFalXHUwMDAwKDZcdTAwMTSQXHUwMDAxwDFVd9CjXHUwMDAwgVx1MDAxZtlcdTAwMDKlMFFcdTAwMGVcdTAwMDdcdTAwMDPKx6A8QJlcdTAwMTHJU5BcdTAwMDPF0FpfcUTWwX0sy272+kU4VkJcdTAwMDQ5VtZlXHUwMDAw4ilcZqdYSDmh6v+NXHUwMDExXHUwMDFmXHUwMDAxqvurx9KEg/Z0XHUwMDA00lWrOL7s2vAtXHUwMDBlN1LOl1x1MDAxMcSRXHUwMDBmQW5HZmFcdTAwMDDtXHUwMDE4I79cIjcyXHUwMDAxMcMujnNRo8tU9UX5iGSYQLCZLlx1MDAwZeTG426n17XVMVx1MDAwNzCYXHUwMDE3wdLWPfFcIlx1MDAxNIFcdTAwMTCO5e9QXHUwMDE0fsOQkkag2Fx1MDAxMMS4YVx1MDAxZiT8anRe+lx1MDAxOXF3islF2nw8vVx1MDAwNeVcXFL4KdjfoWAhmfpcZi5oilxuxCVjQuFcZm1cdTAwMTkh9TB0qPZcYlximFx1MDAxMpKSkCurXG5DKFx1MDAxOKeIeFx1MDAwNUGJgfLEdVRkXHUwMDFmvlC0XHUwMDE3vcx5Llx1MDAwN5qFx9khXHUwMDA2jFx1MDAwMIjiO9Anhecsl7U3I+O08i+5w9Jr+ryYXHUwMDFjXCJcdTAwMGKBlMDLkJJ0u0B7IInP1Vxign7s+COPOtSp9Kske/GbXHUwMDE3p6/Nfss8WlxiYbiNXHQzQrv3ytZjOts/fKm0T/uF9FH65mTc8mv32TUsPWBGyPJcdTAwMDNmKEU4U1xcSpVcdTAwMDXJ/G5cdTAwMTDwPyEoUlx1MDAxNGHCiXJcdTAwMTAlZq5cdTAwMThfwn2ietdxNo14xkbc7Mv4XHUwMDE0P1x1MDAxNJBSSrhLI7mIh4faiJJLIVx1MDAwNY+lyFZo/l7Xcucg9Wvxr4PFM5v9Mv/3f/61+mhcdTAwMWYgXHUwMDE2Z3/zvMv3tjF0lMx0LP0orvUlea9z6Fx1MDAxOFx1MDAwM+fIsuuW3fTuM+16yJ7ZWenBoDtumUY94Dzvvlxi6SpiXHQ7ddB9vZF5YMLzK/iSLvilK8whw8tcdTAwMWOKXHUwMDAx90lcdTAwMTVy0cJCa8I1XHUwMDA0XHRVhYHNnyRIPzGcRVx1MDAwMOJcdTAwMTC5LGGXQch9IYd5/lx1MDAwNeo0OYpcdTAwMTX/XtvVXHUwMDFhyWrm+vKoWMlBxlx1MDAxZo+Iad91yrFcXC22XHUwMDFjhsKQuLTtXFxLsqBcdTAwMDTdOs7WX1x1MDAxNCtcbiZcdTAwMTKIJaPN5W5cdTAwMDGfXHUwMDEz9lx0XHUwMDE2KpUzz+LZejG9rWLp9uDUapvDg//+R2owdP5nXHUwMDE3PldNfaY5WGFE7NrrUtZ8qJHMlLJcIlx1MDAxOMdcblx1MDAxOb+LznR6e3V1n51mwah8nG5cdTAwMTRcdTAwMWXyXHUwMDE2SL6R7CV4V97XK3om43xcdTAwMTN7Z6EoeFhWaWGPXHUwMDAxtpxcdTAwMGZhXkkggFx1MDAwMySIK9K0d2fr/flmXHUwMDA3Ts146V+1XHUwMDFmJnd2plfOXGaO+1ZsTVxukfTEoqifXHUwMDFl103GxbBR105cZv7cbOiWNEk5XHUwMDA1XGJzXHUwMDFjXHUwMDE0lIL+tNG8KkdcdTAwMTBAKaabRS/3nK5cXOXqXHUwMDEz5DMgXHUwMDE2wTahK13oXHUwMDFhJXVV+vz0cEuG6d64fZkxW1x1MDAxM6uZrySexVxi8STLMYdcdTAwMTHO/rZMXHUwMDA2g1x1MDAwMkXcX2ZEXHUwMDE0nVwiXHT2XHUwMDEzXHUwMDE32lxc6+pcdTAwMGbId5V2PThWXHUwMDA3qVxy7/r8h61cdTAwMWNcdTAwMGaldX87+EdqYnTaP+z6xFbvMNvQm1x1MDAwNKpfXHUwMDE2XHUwMDEyXHUwMDEyiKXPI8hWjm6rR/eMpW+fM60puCs0qjggKFx1MDAxMOq2LEdcdTAwMDZcYlx1MDAwZsiJXHUwMDA2xv3XskVj1K39mrYox1hAXHUwMDFh5P5jXHUwMDEynlwiXHUwMDAymIqdsGxwXHUwMDE2PpJE31ElLttccvQysi5ANj16bVs3+c79Y7xE0nLRrjvpPidcdTAwMDW8bVwi6S+KJ4xcdTAwMDHnksiglCPiPpTN8SSVU1x1MDAwNFx1MDAxOaU7iCd9smJ6ODSdg1JtYPWcYfJySe8onuTGXHUwMDFkcV9B1ew5en6tnVxuzOrn8bmRLmda3KmnOZT5tvVcIn9RKCtLXHUwMDBiceEuNHJ76aHUiKVgTKdlNkZyXHUwMDA0XFzqtD/u39uFo3rZXHUwMDFhXHUwMDE2wElcdTAwMTHkXTXEXHUwMDExpMdcdTAwMDD2lXH7aY9cdTAwMDeYQn+zXlxmLUqBwIr1YJCvgpiPXGbnXHUwMDExXHUwMDFkJiQnYIdcdTAwMDGdXrvrXGaXXGa8PcZxOla93jbXIb1YoVx1MDAxYyxCQzlCcMwkXHUwMDE38X2gLMhb+exZXHUwMDE3Xtdey5OGfdyziiQ5wFx1MDAwYvGBvLzOwXYtXHUwMDE30Vx1MDAxZdBcblxy8cmHmEuE4H6aiTZzl7ErfuJcdTAwMTUtKIBkUsYp23tHXG5cdTAwMWZfXHUwMDBmKjWUXHUwMDFk1cvd51x1MDAxYbhrkSxcZoulJFx1MDAwNylcdTAwMTJ4eZ2zPWJFcD9U/JlxXHUwMDAxMZSAU7FcdTAwMTeobE5ts69nVJV/XHUwMDFj5lx1MDAwM4elxeNy4easR1AolqGyJyBQ+iU+mK23XHUwMDE351x0PZ2Tq5I9zt307oty+px4MNPlXCJRRuTPpz116zlBjMr9gHkz3qOh4c9ZXp7CNaKEdqWMjKer1mXhwbxcdTAwMDZ27tA5T5eSXHUwMDBlXHUwMDE1XHUwMDA2l5145TruXHUwMDExKiuiXHUwMDAxn0WUXGZcdTAwMTAs8H6SXHUwMDE525He//5cdTAwMTS+Wy7ziVx1MDAxN1iCXHUwMDE0QYH4XHUwMDFhXHUwMDFkifa4WyzeXlxcZIft24eqczko3YrejvG7TYVOXGKAXHRIMUaJMqgopHjZ3mOQpCSWVLmfQHmh1Jud2Vx1MDAxZLJcdTAwMDPKqFx1MDAxME5cdTAwMTFlPFHMlFdDXHUwMDE59HWVc0zV8yFyXHUwMDAz98lThFx1MDAxNFx09M3qbWLW8axTkTPft79KJd89X5z9zf33RpZcdTAwMDVHoVx1MDAwZaguyVVcdTAwMDYjjm9YvN2NLvH4cFJcdTAwMTHp64dC9qhYXHUwMDFiXHUwMDE5eJOM41x1MDAxN7awXHUwMDBij4UsttRcdTAwMTW7bWCHkmj7TrK9pJbmQEp4XHUwMDA3e5ipQ8OjJ1xiqJtGXHUwMDA0XFwjXHUwMDFhYHVvb8lpfZzrdVx1MDAwNtljo5S+zVx1MDAxNVtJt3WEy/Sc4Vfu08NTXG4plrGDOaWSJDFcdTAwMWQ6XHUwMDBi/Fx1MDAwZlx1MDAwZlx1MDAwZVx1MDAwZlx1MDAwMntJ9lhcdTAwMDNcdTAwMWSvUClialx1MDAwZUSIwKVs3CpAy9Jxn5G7fGN6z+32681cdTAwMTM0z8ImiiRcdTAwMDbQXHUwMDEyLFx1MDAxNyopLO3Tz1x1MDAwM2hlqZLf7UNQXHUwMDAyIFiSvD5cbsJL3HRcdTAwMGWXYUTjK/KTO3JcXLs54qV2XHUwMDExXHUwMDAx1EdccvuyMUo8cqDHUCZon8GuWExcYlx1MDAwNSWYUJ7I0lx1MDAxMJ1cdTAwMGaAqeFrc7fe35ZcdTAwMTRcYkPDXHUwMDE3kFxiJJTsrVx1MDAxMb9cdTAwMThNR6jTXHUwMDE5vTQzV4+1SteE7TNaTTyQvVx1MDAxNOhqcNq9RserptJcdTAwMDSEvVx1MDAwNKWEXGJJv7A2cyVcdTAwMDG6TFx1MDAxM1+IlHFFY/FaUT9wY/RbuCzG1Uc0ebXv6/JkZIVN10hcdTAwMGVusLc6jlx0uTfkQJetXHUwMDEyxYBcdTAwMDTp8jiQyEa4WbxcdTAwMWamWk6nnbCIP1x1MDAwYlx1MDAwZoMpXVx1MDAwZYlcXIdcdTAwMDXvnLFZsK4urcFZ8fjs8Y7A9HU58Wj2dlxus/1Fulx1MDAxMKR+LLvibnPek5hcdTAwMGLO9z0z8ZKlx81JdXpeb47RZbP0yC7br6ud7Y/9m/rSoZO4JEOAKVx1MDAxMzJ+XHUwMDFm8fSlbJZz+O3s6aFYLp3cd1Hp8jTxgMPQQ5+chY+C25o+SSz6VF40UUbPfuI/W7Jnx3SMuuFcdTAwMTip52HX/nr+XGbLIFBcdTAwMTLqXG5hLDmHOFZRzTuSz2tcdTAwMTJWKjdcdTAwMDa86PSfLnifX53JTOJcdTAwMTNcYlLCpVx1MDAwNMJy6pQjnlwiic0gqJ2CQkngXHUwMDA23vXfKYTgozdNIXyMLItcZlx1MDAwZc/l0dDfa7mykOuoN1HWt5BcXFx1MDAwN7GX87JU8Fx1MDAxNMdcbqBKYpFcdTAwMWXN4cOPQCnCMOZcdTAwMDQwJtW/XHUwMDAyguZRouCFc01cIlx1MDAwM1x1MDAxOZFw/rNcdTAwMGV5XG7p4Fx1MDAwN4pcdTAwMDAkXCKBg/QgpKFEyVx1MDAxOVx1MDAxN1KCXHJcdTAwMDfp7Vx1MDAwZtWhgPCc/c3zLuu38P8+f7PPUehcdTAwMTcxXHUwMDFhVmZKszbSX1x1MDAxN6RcdTAwMDCUglx0PctQ/VTWXHUwMDA1wa7DmkZPP4Ml8aBcXLKPI/5cYuaisKuKXHUwMDFlcum6qkN1WUxpQVx1MDAwNlx1MDAxOUBEciWDyH9VKEU1dDDAQlx1MDAwMm30+q5qLZb7oIZIbVx1MDAxZe1ccvnboVx1MDAxN91cdTAwMDFA/VnKkK3S6aetPH1hXHUwMDA1c9i+Nu/o+eRR3lqHSbdO1fPwVLXgfWZ6Nlx1MDAwYoxcdTAwMGJcdTAwMDR0pFx1MDAxOScpMI5DoSOp5JLwNVx1MDAxY2mDXHUwMDBlb1x1MDAwZTPVQfntaFgvXHUwMDE3MzawujT5yEHewHhUz+TWgfHoyVx1MDAwZvPeMiWylLurtZPj2OjAOEr17ERcdTAwMDXGqYyYe6tDQlSukeFcdTAwMTl1nvqgf31d6pnNxsnlbTvfLF7s1OrZw6h2XGaXXHUwMDFkXHUwMDE5ZVx1MDAwNURcdTAwMDA5xlx1MDAwNJGwYo1cdTAwMTWTbuaz2THRQ0w3KXZaJ1x1MDAxY1RcdTAwMWU0isXH/pA3T1x1MDAxZqcvxVxcTtz3S27lvrI447U/yV+8ZW+fj3rli5LVPnqeyIH7gMz4hlxyqm/XTXreemHTyjWr3OTcXHUwMDA3vEG7dPx4OH54y2fg+TSN317u09tFnHDE7HeijFx1MDAxMyyJiK/UX8+advtcdTAwMTiL+zFsXHIymcNyrj1cdKtUTUjtXHUwMDExxHi5eIO4Klx1MDAxYneH5lWzeFx1MDAxNmjGQmnL/TDyXHUwMDAySXbz/rRwNK5kr41cdTAwMGL6gFtcdTAwMGUr9421sLgh3H0+wUa6IWIqzs/XXHUwMDExzFUk71x1MDAxZi+KpETI1dm3spyvY1xcXHUwMDBlm/Q+Xa1I0K+ddsHwZJp0kYLLuVPEyc9UXHUwMDExTF3y8pySn25cdTAwMTBcdTAwMTNcdTAwMTC+4lx1MDAwYlx1MDAxNVxmXHUwMDAzXHUwMDAw40OkeNhsvfaPXHUwMDFjM4sv85m3e+uwbXeSXHUwMDBlXHUwMDEx4l21XGKJ8Fx1MDAxMOhcdTAwMTa8XHUwMDFitCxXXHUwMDAw7+oh6NTt4SaJdndHmrdmp9c2XHUwMDFjc/hFhPnR4mn1h8WiVWXTy4vL5oidVqadO3/s0t9cdTAwMTStYOKZNON6QvM1fVx1MDAwMsolXFzjYXZcdTAwMTKR3HhsU1x1MDAxMnuhXHUwMDExhMrhxjRwbpN/0MhiNTUsdet8XGZeitlcdTAwMGLdMSY/bMtumVx1MDAwM8v5YTdcdTAwMDZdN2tt3Fx1MDAxNT2YYSBcdTAwMWGsQehnUWMgou2hqPA7JFxipHS+i6nbLij1xFx1MDAwMbBcdTAwMWWUjFx0XHUwMDA3kDOiI6pcdTAwMDGTdFPKXHUwMDFllFhcYlxuXHThXFw57Fx1MDAwMT2euqk0+G3+wlx1MDAwMlx1MDAxMFx1MDAxNpBXt0mo+1x1MDAxODhcZlx1MDAwMIaWs0OImNJSYsOZOntMM4VcdTAwMDNEv1x1MDAwZX3YWLyhT7XsLEZcdTAwMWZcdTAwMWTcOFiK0Vx1MDAwM6qXs0CMM0pcdTAwMTTNXHUwMDAw4jrsI1x1MDAxYVx1MDAwZVKSUSSF4i0q9ajIjyPWjNFH20rLV4WIRFxch1x1MDAxNrDQIXhcdTAwMTJcdTAwMTSj913F18bkuVxmz1x1MDAxZSG9vCtep2CkNb3pt8vXRyOQu6g616hyc1x1MDAxM1anmZxwXHUwMDE0QN6CXHUwMDExsOU0ulBvY1x1MDAxN1x1MDAxMzU5oEwwslx1MDAxZmdkgc/gcNWntEdcdTAwMDIuNIhPQlx1MDAwN99CJZSIY0LiR/Gdy5Px6KxcdJhznjbLpjFcdTAwMWVcdTAwMDKU8GUzlF/rmYHvylx1MDAwNu1cdTAwMGVnMedcdTAwMWRcbkBcYlwicN8uy/CsW3iQ5YL1evVyPrVcbv00XHUwMDE5TnfkkSR8LGJ0iCvS5sN6nVEgiLL4JFx1MDAxMp6AOeEpSCRFkKqnp/5cdTAwMGJcdTAwMWGXXGLcp4OgKl9IdD1cdOVMsVxm5lx1MDAxY9FcdTAwMWRbfL9WXHUwMDExXHUwMDA24YQq2yFw7Fx1MDAxZFx093lcdTAwMTSxKbZeyi4kw+Q7XGaHiH75wfFcdTAwMTUmX3T4f8m4XCJEeUNYV75cdTAwMDG9XHUwMDAyhOviP0wr4ZFcdTAwMTG/pVx1MDAxNcvei+6GPliqyVD2vV7qXHUwMDE2XHUwMDAyRLXqxlx1MDAwMfbe/sy9d8qJbuBdQTk4pYDK9Vx1MDAxZFx1MDAwM8hcdTAwMTOBVVx1MDAxZUFcbqsjXHUwMDA04VRcYoFcdTAwMDMoR1ku6qFcdTAwMTCI1blQSFx1MDAxMqCEZjY4XHUwMDAwXGJcdTAwMDEsXHUwMDE4V6Kxzlx1MDAwMqp/OcpBQFCOMVxuXkUg3M1EjHKCY6VcYr6WcUJcdTAwMTEyO9+HjV0yTnhh6NaCr4MzXHUwMDE0U+XnqYvW5bo+qUcpjlx1MDAwNFx1MDAwNnpgXHUwMDE20KvVXGK/7/m1Xp+koVVcYkq+daPPXHUwMDFhxdVXoNmiXHUwMDE39vNwcGi+XFx3XHUwMDAwn9iX/aSIYKjT5+1cdTAwMTJQyv2nOX1EeHw+/6J1SlsjXHUwMDAw91Ros8DrylwihZU1XGLWoazfnOemb51cdTAwMTHkhNdcdTAwMWFOurOl38hcImJcdTAwMTRUz7ZcdTAwMDZrtNNcdTAwMTNhNqql7lxyf7iaVJuXx9lhhj0kXHUwMDFkrZ7Sfyq3WvYjzG1cZopcblx1MDAwNzVDK3KAXHUwMDEywu3zXHQ/zW8sKkI1XHUwMDA3iv8/fccvzWZlyOuwVcygXHUwMDE2mFbqw4czMjRcdTAwMDa230ZcdTAwMGLIZiHgiVx1MDAxZrhcIsmfj3HrNZt+YTsrOJ/FXHUwMDExVDYv9S/MMXOSI1x1MDAxYeqEMlIwjFOuXHUwMDE3M59l1Gr6SjdcdTAwMDf31jmsyFHmwVwiXHUwMDE5y7kgQKSE9tWUm6FMPE9W1j2EY05HKaRsKlx1MDAwZZVzKnWte0Bv5985qnDngTLlilxuXHUwMDA0gnxcdTAwMDdcdTAwMTSuUJVRXHUwMDBliHI7XHUwMDEy5zyEwmG292syUtEh54OlWFx1MDAwMFx1MDAwMFx1MDAxNEkkMdNjXHUwMDBiOFx1MDAwNYtO8LlbXHUwMDAwP1erXFy3TSTShDlYTkFxZThKXHQxU7dOXHUwMDE5S9x/XHUwMDE12HdcdTAwMTU7jklE24wraIOn9Ko8Qlx1MDAwMIKl9Kg/olxmZ8S55OruXG69JFRcdTAwMDCJ6M5cdTAwMDBB8GzGK1xmXFz0XHUwMDAzXCKh/DMlKZIqXHUwMDE3jbhcYv9vUvHafkRwQGVw4lx1MDAxYod37OqbXHUwMDBipeS70JU7pZVwfMz2epHxXHUwMDE1JLNGXGZcdTAwMTQyXHUwMDBlXHUwMDA0XHUwMDAxmDBMXHUwMDE0cVOfbMtl8fBHXHUwMDFmY/FNtIPvpT3OXHUwMDAwUGRDpSTK2aX+i6Ipuj/GWbnSOlxyn8HNXHUwMDA1XHUwMDE0XHUwMDE0c1x1MDAxY38wbdae1llcdTAwMTXhcTVrTMpyyDuyXHUwMDE1NpstKc4kgcu2l1x1MDAxMumIwMf6XHUwMDA0N5+oXHUwMDFjq25SzCxCvFx1MDAxZmdySy9xNFxmW/lic5t7I9CS8LVo9FxmO6ItovhNQ2mjzTk6KlZcdTAwMGI3942zUYXlrlx1MDAxZe9cdTAwMTNcdTAwMGVaJGnKXHUwMDBiW7FcdTAwMWRsfT7s3IGOXHUwMDA1XFw9xlFcdTAwMDC5p+mB21x1MDAwMdeya+1R/YvA+1x1MDAwZapGsXBXrJqEtfpdOaJP5Gr0NvTbeEExXHKX7M/KuF1zeeZmXFzQnL61Vrh6j71GXHUwMDAyYvWyRck110JcbnWV50FcdTAwMDRcIoFrlqPwWZNcdTAwMTRC5bLILdYs90HyetB9NmvOwdHIartuhlx1MDAxYt5cIlx1MDAwZby3XHUwMDBlcCBcdTAwMWFJwdHNm1wiauVuZY2JNeqXit1qaWydtTs3XHUwMDE1XHUwMDAyeieOQVxunbCh9lx0oWFcbkBKIKpcXFqp85LuUXz6XHKE7tIhXHUwMDEwS4FnVaLh7Vx1MDAxOLSmjMP6ZvSMXHUwMDA0SmFcIphcdTAwMWU1gpVvXHUwMDE4VOxEcEoq8Fx1MDAwM4whXHUwMDE0jFPkS6ggzKDUXHUwMDA23z7oe2HxXCLWKOSeKsOXLs63S9XcVJRcdTAwMWF2nLUvj69y1Vxcb1KtvHLSODqThaPyST5cdTAwMGWfMs/ANSHcrVMx556u5tNcdTAwMThP8NfjU4ZcdTAwMDTRa2RcdTAwMDX2PeBQx1x1MDAwMkFcdTAwMDaEoodcdTAwMWTSacl0XHUwMDFj5bTtJFS8h0Xg3jH8bFq33dZZ/86uT26GQ8rR6f2pXHUwMDFmw3XL6HTdwYaZWUCZJ+flSlkvpq95XHUwMDEyp0HRXHUwMDFkSlNcdTAwMDRcYiVcdTAwMTaQKsJwr1j1c9djTyC+sWZUjmhgjVx1MDAxYlx1MDAwZm1KXHUwMDE1QIFbqYTNYsZ7Xo/9o1j40Zne8O7FdHxcXFx1MDAxYo/HjUp3clx1MDAxZVCJXHUwMDE5YJ5SsVxcXGKlXHUwMDE0fVxuXHUwMDAxglx1MDAwMVx1MDAwNEqvuEezRo5UXauf7C9cdTAwMDY7xlx1MDAxNe6W5vnF6nOVXGYoS1x1MDAwM224XGbrSth9osrHvee3l/lcdTAwMWb2cddutEemupRcdTAwMWZ23rg1XHUwMDFmvoSKN2o9uyTPTSsz6lx1MDAxNWxYuK9cdTAwMWKs9Fx1MDAwMJtcdTAwMDGtZ0Hgx8LjdrvXY1xctMhcdTAwMDY0UEKQXHUwMDE0xHvN0Fx1MDAwNCBe+bNYXHUwMDAySPw1fN9n4y1DLVx0ZYBcYlx1MDAwNjdcZqO7IFx1MDAxZk2kPsh/dP0qi+OH/c+DXHUwMDFmdqnXsuy33TlzXHUwMDFi415u483J8FAwpYRj5c/Ed+eG6eOsXX1yXu9cdTAwMGadnCGPxH3zuFx1MDAxZVx1MDAwMtfEXGaV8q5KxiNH8VxihkxZW9/mX3PaXHUwMDAyXHUwMDAyUqr/3EX1e/HN9rCUkSu/6oVcdTAwMTRcdTAwMTNIMkjXmMrwWFx1MDAxZZdyzeJVo3Xfssd5LIfHhbC6yuQgintcdTAwMTBcdTAwMTUxXGZnWzxBXHUwMDEya/VcdTAwMGVcdFx1MDAwNNWLSidxRlnRXHUwMDFjdkeDmvneu/RFVWgr01xyKLxPXHUwMDBmzVx1MDAxYVr4OnNcdTAwMWGPSiev3UmVvbZbpcfbN3ZNblx1MDAxMlx1MDAxM1x1MDAxMFxiW07OM35cdTAwMDZG1Vx1MDAwNm9cdTAwMWFaXiwqXHUwMDE34C/4pzIqXHUwMDAywUDi/bBiMIxjKXH9XHUwMDAxs/W4XHUwMDBlzkzbXHUwMDFjKCshZIy4SzJ3mzbzXHUwMDE0jrhiMoiFOq2QMJ1cdTAwMTJfZ9pesMJINJI51ZFQwVxiZ1j9cHmrnyPEIdSRVCSVYVx1MDAwZsNcdTAwMTeo2SafxmVcblx1MDAxM8pcdTAwMTRcdTAwMGIjXGJcdTAwMDRiXHUwMDAxRlx1MDAwMEpcdTAwMDFGiICSXHUwMDAzLCCWLjX6WaMpXGKSiPNcctaq2WKceFhFRLSxd7DcXHUwMDE0puxcdTAwMTk9wFmxptJEKKBcdTAwMDLL84iA8Fx1MDAwZurd+fhgglx1MDAxOEdQ53iwjmbQgKHGJIVcdTAwMThBVFx1MDAxOS1cXP+Qvova8Vx1MDAxNHWvXHUwMDFj77TQJ1x1MDAxNIT65Yff4u1cIu3Bd15cdTAwMDDnjcdcdTAwMWVcdTAwMTiU2cnzkXy2TvKsa1x1MDAwN1x1MDAwNJeWVzp4e1x1MDAwN/WSPFxuV13/p/RcdTAwMTCJdC5cdTAwMDNyXGaUu1xiXFxcdTAwMDb7RsPTY2VegP7zp3O1w0rWsCZcdTAwMGX/ulJa8MJXS1FCq/xD6V6YM1x1MDAxOVx1MDAxNWuhePCc/c3zLrtsmdtupYRcYkni1/mLXHUwMDEyuMfHo0Hn8alCr512LSDrXHUwMDE1VClcbjzrXHUwMDA2ioCS8oBMId1x0utXXHUwMDEzXHUwMDFlwlx1MDAxNeBE4KSv8CmVglGoMFx1MDAxYWvu65dcdTAwMTaRLzWcXHUwMDFlUro4YZ/iXHUwMDEypq9xoXp82LNAjfdcdTAwMWFPzyfWpWVcdTAwMDBcdTAwMTFS6Vx09Fx1MDAxYVx1MDAwNJgzvWxcdTAwMDJ0LS148GWd5cGNfbFkXHUwMDEzueqFw/RcdTAwMWNCKaw9XHUwMDFjZZNcdTAwMTNBdC9tgJnIsLJJ1F1AurRcdTAwMWW5XHUwMDFhdv6WXb/sQuXGSiWKQZpcdTAwMGbB8KnNlFxuXHUwMDAwXHUwMDE4StyqIeFcYtGvQ1x1MDAxZjh+rnDHLppcdTAwMDYpZXZyZWR//vRXTKN9Vky/S7f5mn45XHUwMDAyZHpbbVx1MDAxNl9cdTAwMDeNfOMpd/ZcdTAwMWNXurlHuv1cdTAwMDV8XHUwMDAxiXmyzuyHv5706lhcdTAwMGYngFx1MDAwN+biw6NcdTAwMWGMYFxiMYy1XHUwMDFhxpearUuySn7ybIfHR+vp6mR893xu51rFyUurXHSMQrBwUqItXHUwMDE5ZdBwXTAmmV849yeaK1x1MDAwN6pJX2HnXHUwMDFjXGK6TYfpcYcxgPAxvW+cXHUwMDE52M+5yoPjnFFhoTuja50nPd8gPLOxKKF7XFxcdTAwMTUlMIPlzzhoLcQpTuRy4bNQ7fDg8ECJWK9rq/c9KLhvyMq8Q9ts7Cdai8NcdTAwMTcyU2SorFx1MDAxMvfk21VgXHUwMDBlTnBvoFx1MDAxYr6wyUFZNMqC4ZxCKjlmnvXDXHRjqZkrhvSMXHUwMDFjxfGhKN+8Z1x1MDAwN6WU1mFcdTAwMWNcdTAwMTFcdTAwMDIgV1pcdTAwMDRcdTAwMDXMXHUwMDEyXHUwMDEwXCKFpFJPXHUwMDE0carzar71dblcdTAwMTRSQpmMSG124NSMl/5V+2FyZ2d65czguG+FxESVbyWh4EjdYlx1MDAwNFx1MDAxOfRcdTAwMDdq6dJcdTAwMDNQutlvlsVSP9GFkUvqXHUwMDA3UCBcdTAwMTGUulx1MDAwN1EqSlx0iNPilF5cdTAwMDFcdTAwMGJJXHUwMDAwXHUwMDAw51x1MDAwNFNEfFx1MDAxN/VnXG7UhkNQv3zg89tcdTAwMTFcdTAwMWJpUoTDhzaTWWeou+d1XHUwMDE1+Ty8PZ5cdTAwMGVG03p2RJ+f0qLJJ81jknDyoVj6OqyiikE2J1x1MDAxOVx1MDAxMeTZ+/ur9Hg/IVx1MDAwMEpi2r75nuncV4fVt1x1MDAwZun4bvR6JUd/zieXfH+1zPFRePLt21x1MDAwN/Q1xkz9TX//49tcdTAwMWb/XHUwMDBm5n1cdTAwMWGRIn0= + + + + Pharaoh ProjectComponent 1Component NRST Files (*.rst)Local Context Filesstatic: *.yamldynamic: *.pyAsset Scriptsplots.pytables.py?.pyAssets - Component 1plot1.svgtable1.htmlmetadata.jsonplot2.pngRST Files (*.rst)RST TemplatesmayinheritfromLocal Context Filesstatic: *.yamldynamic: *.pyRendering ContextaccessuseincludeProject BuildSettingsHTMLConfluenceLaTeXTemplating+ Sphinx BuildResource FilesAsset GenerationAssets - Component Ngenerate diff --git a/_images/chirping_crickets.gif b/_images/chirping_crickets.gif new file mode 100644 index 0000000..39fa040 Binary files /dev/null and b/_images/chirping_crickets.gif differ diff --git a/_images/generation_time_templating.svg b/_images/generation_time_templating.svg new file mode 100644 index 0000000..250435a --- /dev/null +++ b/_images/generation_time_templating.svg @@ -0,0 +1,16 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO2dW3PayLbH3/MpXFzeL2fvXG5M3y/zclxu44DBXHUwMDA0MFx1MDAxOIw5maIwXGJcdTAwMTBcdTAwMDaBJWEuU/PdT1x1MDAwYjtGgCSEMVjZM56pJEa3Rvr1v9davXrpzy9nZ+f2fKyd/352rs1azYHeNpvT86/O58+aaekjQ21Cy9+t0cRsLffs2fbY+v2331ZHxPXWOK5cdTAwMWJcdTAwMWTd0EZGvDVcdTAwMWG+nEJcdTAwMWJoQ82wLXXQ/6nfz87+XFz+6bqoqbXsptFcdTAwMWRoy1x1MDAwM5abVtfFQmx+mlx1MDAxZlx1MDAxOcs2QMFcdTAwMDXChFD+toduXarr2Vpbbe40XHUwMDA3lrba4nx0zvRZtfd0Mcg93LZcdTAwMTfVXHUwMDFiXHUwMDAzd6ed8eqyXHUwMDFkfTAo2/PBslnWSH2v1TbLNkeP2p3etnvOtTc+9zvKXHUwMDFjTbo9Q7OstWNG42ZLt+fOZ1x1MDAwMLx9+nJcdTAwMGZ+P1t9MnP2wFx1MDAxMsSB64e+bXZOXHUwMDEwY1DEpftno2XJ0WBkOi37V1tcdTAwMTJcdTAwMDE6q7Y9NFuPXdVAo73ap1x1MDAwM5z/VvtMX78vJnL9KuRtl56md3u22odcdTAwMTPo11x1MDAxMm35RFx1MDAwNFx1MDAwMJhJurpcdTAwMGbO1ceZ9pKNPzbvaK9pjl/v3Lnl/OJqudPoby9gvVx1MDAxZf66SW1szW6y9yPYebZryWmdjlx1MDAxYXOr8f3tWLVDXCJLKmateHNdqNXLoK5PXHUwMDFhuFx1MDAwNN076NqzXHUwMDE4XFwsXG65cnqeuPpeqVx1MDAxNq9l6vx1+1x1MDAxZsu///rqzbKtzWxPjDn1xVx1MDAxOFxiSVx1MDAxOcWuXHUwMDA3u1x1MDAwYuOpc7nJXUxcdTAwMTe5ziyLrfucOS75YNxrtnpcdTAwMTNTi1x1MDAwMMhcdTAwMDTAQJBBWJBfXHUwMDBlXHUwMDBmXHUwMDA02TabhjVumlxukG2YXHUwMDExYtv0YrqJq2ow5UIyeVxmXleHd0aGXdZcdTAwMTfOXHJDYu3TVHOoXHUwMDBm5ms3fMmXukBRXaA56p1cdTAwMTXNUV/Jp/teWNpASbBzNrp2WGKgd1x1MDAxZFx1MDAxNM9cdTAwMDdaZ51RW1dcdTAwMWH+ttlcdTAwMWWNz1x1MDAwM1x1MDAxOVx1MDAwZtRrRbJcdTAwMWboWFx1MDAxMFx1MDAwMVx0XHUwMDEx4eU6V0zYMHdDulx1MDAxYbzqli/7iUmhlI24XFxcdTAwMTPkqKCAXGZcdTAwMDCOqeRcdTAwMWKUU+orklx1MDAxYpRcdTAwMTNJXHUwMDE1fzvkevnjQbhAcUxcdTAwMDRjXGZQjCmX27xcdTAwMTPJ45BcdTAwMDMoMZRcYnFEtvmXglEmKONH6Vx1MDAwMO/SUVx1MDAwMqFcdTAwMWZekEtcdTAwMDIwICi8jrZNdHNp2rFBI/F4Pcv0zGHZYtHXUSmCdJRi+XGEXHUwMDA16iiEXHUwMDFlVlx1MDAwMNriXGJcdTAwMDGGJcNcdTAwMTTi01x0KVj7NEBIk6PheGSo051BT1x1MDAxMYVi7ZBcdTAwMTOJKPelXHUwMDFjqT5LqUThRbSu5crNeVx1MDAwMiTsPEyNnoynx06yXHUwMDFheVx1MDAxMcVBiCNcdTAwMTYnkCOAMYSCKbvzNFwiirdpJyQuXS2hiGzSXHUwMDBmMceCXHUwMDEyNVx1MDAxNERHRTHgvnxcdTAwMDE1RqtvxHFowMziYpJ/1tL1+/5D+TpcdTAwMDU6lVmrXHUwMDEzeVx1MDAxNaWAXHUwMDA0IVx1MDAwNlx1MDAxNWIuXHJcdTAwMTWCXHUwMDFkwliwjFwiuFxy1raMXG5cdTAwMDFcdTAwMTFSQ9xR3KdcdTAwMGZT0fxJVXR5ei/Aka9cdTAwMTWKXHUwMDAwXHUwMDA3XHUwMDFjYlxmwlx1MDAwM35cdTAwMGJcdTAwMTZ67ebCmuZ6qfG8OqTZzmjxwYC3R7bTgFx1MDAwZiWc0SDClUdPXHUwMDExJlxcWeRYYqZ07mhcdTAwMGVcdTAwMTfY5lx1MDAxYoq1q0uxiTthXHUwMDAyKyNavMP4XFy2LCTult007Vx1MDAwYt1o60ZXbTEmg8HbNs1o+2xcdTAwMTk0LVsxP9Sdh1ZcdTAwMWPphr25x/K8XHTTXHUwMDFjTXtas+1xZt9tY+d06zGN1b/OVvdy+cvbv//4unvvrXu+OvqL++932SxcZvpcdTAwMDbqOKeEXCJJ9+hx6HrwcGlcdTAwMTVcdTAwMWKpYeNhfFx1MDAxNatPe/OHyFx1MDAwZimEXHUwMDA2XHUwMDBlKc6IXHUwMDEzzjBHXHUwMDBmoom19/c35HpcdTAwMTYrU1x1MDAwNW52Mci4eirOXHUwMDFmXHUwMDExMk0o8cNcYlwiZ6SVXGaF54hMa4hqxUy2Mlx1MDAxYVx1MDAwZjKN2sAoXHUwMDE0zehzJFx1MDAwMlx1MDAwM2WO505Owlx1MDAxMWRcdTAwMWWBsm3LXHUwMDA0YijVrvBIgYLDTJNS+fYspVx1MDAwZjTr7H/+XHUwMDEzNy3733tcdTAwMTkoLdVcdTAwMGXNPMREXHRcdTAwMTRNjnxcdTAwMWQ9QTFSns1cdTAwMWVR4e/z+1SlVm5AZjUoxcXrJ5CYR1x1MDAxZvZg0XQ2h1x1MDAxM02NcX5QVFx1MDAxOPK1hlx1MDAwMFx1MDAwZndcdTAwMGZcdTAwMDK23lx1MDAxYbbZXHUwMDE3kCCqL3B2XHUwMDFjK/1dkko48pVUXHUwMDAxlGvKXHRcdTAwMGIvqaX7b1x1MDAwNdon9Ulvjp+um5Pcola5iD5lylx1MDAxNqZcdTAwMDGUXHUwMDExXHUwMDEwUlJcdTAwMGalXGZ6RVx1MDAxMfiWpHJl+1xuQfBcdOdcdTAwMWVCK2pupFx1MDAwNPAsqXZSXHUwMDFmvGjrXHUwMDBmQ9m7Slx1MDAxOH8/+0983lx1MDAxY1x1MDAwZX5cdTAwMTjtuaHOsPxgPPeUXFz2OZKLXX12ayZOXHUwMDAyzDHGe8xQTMZGp0duuVXpJWlcbl9cXHaqiej3XHUwMDA2XHUwMDFh6Fx1MDAxOWJcdTAwMWN2Jlx1MDAwZVxuRppcdTAwMGaHaK5XXHUwMDA02cPCXHUwMDEwkFEnivwpc8ev299p0EJf3jgmTN1cdTAwMDFcdTAwMTl+iM9b+eebx2KrW85cXGXMQmlaKGWG0cdN8mDcwk6JXHUwMDFkilx1MDAxYsShJiwgXHUwMDA2jKlH43ow0VHfhGVp9lm5Zepj24rOlFx1MDAwNab+XHRcdTAwMGXqZkLhXHUwMDA0XHUwMDFjwssqn1RoLZVt1qjWrqpbX++3cfQ558GzXHUwMDE2gp2Kc1x1MDAxZS7BgVwiQZ1nc1x1MDAxY87fKZfAlyPIXGKCXHUwMDAw7WGsXlx1MDAxNyaxXHUwMDE2eWjPqiOhXHUwMDE3J0YmWXxcdTAwMWVFniOKSJCxisSp9JJ72Kpe87tcXFA1NFx1MDAxZiex60C5XHUwMDFjXHUwMDBmRrblZ4K+2+tcdTAwMWbq7bZ6cu+XS6g8XHUwMDEyP85cdTAwMTHEUnImYXi9nFx1MDAxNe8rmZRRuH3KN24748t7ym8n0Z7jZSDQXHUwMDA2pVx1MDAxMMSpL9ghUlx1MDAxOac93dY8LE7hMVx1MDAxOeFcdTAwMTFcdTAwMWHFXHUwMDE0OzFr6O2ArSzGdFxmldrFp8uGTnHMvGo+5e6B9WYxXHUwMDFlU0F38SX9+KKMXHUwMDEzXHUwMDA09lx0xyfwfYxcdTAwMThXIFx1MDAwNlh5cFVJ5VxmNixEXHUwMDFjL1x1MDAxOGhzOjk0n4lcdTAwMTfnXHUwMDAyK8akd0rML4BcdTAwMTfwNfdcYlHqxdX/ofHidJhq9jrZmZRsciXgnIuCXHUwMDE1cbyQjFx1MDAwYsQpYEJ9W0TwXHUwMDA2XkLEgXAmK5yBXHUwMDExuyyWk6MmJOCEUVx1MDAxZlx1MDAxYu80pK2uYpXqs7Z1fVxyrJZZMe9pt7CIkcNcdTAwMWNsXHUwMDAxfMObUj1cdTAwMWXCWHj/Okvv8pOCNIGc8FZnjNCMV/zsxaiQSHAwiWpcdTAwMWP9KFx1MDAxMoNjOdSDx21rUVx1MDAxOfhcdTAwMDJcdTAwMTDEP3VgPcymLI97ujH7mXt9dqtccseDpq3t541cdTAwMWY5zEmwf6IsVGO/klpXkuGufvHt+mZOXHUwMDEzoEbvLJjJXHUwMDE2q/ogNbyNvFx1MDAxZrXLXHUwMDFmJzBk0P9U/jhcdTAwMTRYXHUwMDAwXGZYlPxxfztcdTAwMTJRKCRcdTAwMDdcIjxGxUo+WSqn20/124vLOlxcWNVMMvrT8Vx1MDAxNMJAd1x1MDAxY9NcdTAwMTNh5MqFXG5cdTAwMTBYpfSYXHUwMDAxKaKYbW03XHUwMDFmXHUwMDA2Wlx1MDAwNN1xwvwp55Bcbuhex7HTXHUwMDE5XHUwMDFmX8pKt5Z6bMhi9lu/YD2bi2TkKSdcdTAwMWNcdTAwMDXnw6JoiSWTXGJjRMhxgk7v00rpv1ZcdTAwMTVJ1Vrl9O2RXHUwMDAzx4flWmLa5jPZXHUwMDFhplLmXHUwMDFkm5Wij1x1MDAxMcWBfjek7ERcdTAwMTiFm+ohWHDn0URxpud/P0UnfVOqpa/Hz5XJXCIpx+Elspdq1PLfL265+b2bLXat+lxi8MdcdTAwMGZm+yhcdTAwMTnVMK7cSWdcdTAwMTaCKtJdy0hf4HbmOZlybpxcdTAwMTlPzFx0P1x1MDAxYdxenlx1MDAxNo5cdTAwMTMnnVx1MDAwNzNcboRq6HbMyYn7cSboP1x1MDAxOdVcdTAwMWaWUb1101dHf3H//b6Mauw/peqskFx1MDAxMpLsMZyAST89a3Sy6fHzMEPHhadMttWPdmhjuZZWIMpcdTAwMThT/Vx0u5NFnFx1MDAxMyiLhFx1MDAxMqhcdTAwMDZdTFxiYO67tdndaEtcdTAwMTLQXHUwMDBl7G6HrVx1MDAwMsO7V4FcdOVyXHUwMDEz99zlUVx1MDAxMlhcdTAwMTDr5K9cdTAwMWJ163GEc4Pyw/VClDvGYfE1yH1tYyeSgJT2y/AzVVx1MDAxONyVb3KpmMVzuKTJ6kLaLb/0wahgSFx1MDAwM1cjKrvZ7Vx1MDAxZMLNhu2FYbD/XHUwMDE3KsDGoVCYsSNcdTAwMTnGh5k0Zc22lfh/cKTsYPePXHUwMDEx/6RcdTAwMDMmhCCK8vCIQ9sulkTxkqJcdTAwMTLrWZXH5+9cXEORN9xcdFxyXGaWKYtnfUGkv+UuXHUwMDE40mTrkKTAcKtXXHUwMDA04JDzY5eT8a5cdTAwMTZzkKJiV2xmXHUwMDBiNzWKIChh+KBcdTAwMWG1S+MszN9b6KqTqVx1MDAxN8r565b+XHUwMDBiJGTLQNyUJS1PQ1x1MDAxYiThpi1cYpdcdTAwMDJgXHUwMDEyRUexpL3U5XrJxY7SLFx1MDAwNFx1MDAwNNjXeFCGXHUwMDEyXHUwMDE3ksrwpMfscdU2eY3XUT1V5ZOp2Vx1MDAxOVxyom07MLBuwW5iTuOSXG61XHUwMDEzo1RiLJAv5lx1MDAxMTBhKSdAXCK+M2chl75J4sWkIVPJqdXqJ1x1MDAxNncjnP/wmeRcdTAwMDMs3UBkJfOdTkaUYudcdTAwMGXsUVx1MDAxYWFRa8pWvvJkXFzPNP5sdjLZSj3ixEJcdTAwMTLgc8VcdTAwMTRKv1x1MDAwZbGYQVx1MDAwZYDfYqz/XHUwMDE2YoV/2Fx1MDAxOTFcdLiEe2hsYp7NPj4z1lx1MDAxYubzpJ3JPsJa5irixFwiXHUwMDE2RCxcdTAwMDe/XHUwMDBlsVx1MDAwMjBcYlx0lLuSIz5cdTAwMTPYr6tcdTAwMWTsTL9dS6FmV5s+TvvV+UU7Xbs4zDYmwj/agFx1MDAwNcROuCe8/qbzg4fYxUPmJsvG057eT9yxZNRcdTAwMTNcdTAwMTd54EpFdrzwXHUwMDAydM3kXHUwMDA3TZlwXHUwMDA1N0PEO4x8XHUwMDFhRj8mXGZcdTAwMTHNzFx1MDAxZIWI/3pdgpdJyXtcYrpcdTAwMWR7zJbUc8+2XHUwMDEzsNHQW8wmzyDiXVx1MDAwMK2bIFx1MDAxYtMs2Im4uVLagH/g96DyX1xm7KqhXGIlW6uhuJ3RXHUwMDAzOFx1MDAwNJRSsstmZp3n2V15mCx9n+RjZauZNG5q95+f51x1MDAxYmBcZiu/XHUwMDFiXHUwMDExykX4ST87fbtoxExcdTAwMWFrI61RmV7X+5V0O+IkYlx1MDAxYUhcIlx1MDAwM1x1MDAxMSFcdTAwMTFcdTAwMDFcdTAwMTBMXCKiXHUwMDE4SYXiL1xuovCfhGCqj0GK6Fx1MDAxZbPP/Vxmm1xikCmUv1/cplx1MDAxZud1Xlx1MDAxOdpcdTAwMTFcdTAwMDeRiEBcdTAwMTCdYl5cdTAwMTFcdTAwMDFcdTAwMTHuXHUwMDAwXHUwMDExYOjUn5K7nLJjkri6yjCt+jhupurJauF+dq1cciGY9Vx1MDAwZjNhmX9cdCNcdKmUhJM9kslcbmKgXHUwMDBmOzN82/t2l67z51x1MDAxOe1cdTAwMWI+rEYmust4YE66MuI/kNZcdTAwMWRzXHShortcdTAwMTBB5SZcdTAwMDOJd1x1MDAxObVHlcfDjNpVXHLG95m1R11cdTAwMWUu/KvDQUIgXHUwMDA1XHUwMDFj7VHWy0pfzsxRXHUwMDFhfZPtRFx1MDAwN9o3ODZO3ke/U6DATlx1MDAwMUJbtScqXHUwMDBmh1x1MDAxOCOOhu9cbkZ8nsngp8DUX4FcdTAwMTFaTiftUeJlYSV6i8vEVZ1OvsHkfHKfvW9Evrwtk4Hr05Cka+vTjlxiW8hcdTAwMWFyRCkwXHUwMDExQHwqbIdcdHCkK81J4lx1MDAxZlSQypRzwo7hrZKc3nnq9M1ywvqGqiA9rF59m1ZcIt8nglx1MDAwNVx1MDAxONHQXHUwMDAyfIpSc4RcdTAwMDe/TIdyoIybX1CcXHUwMDE596/wyZUhJoTcI6iQv81cdTAwMTbGvafU86zYT99cdTAwMTVcdTAwMWVamadZ5CtcIjFB4jiARCzCqvNRytF52MdcdTAwMTRzSSFGn+m0fXzZuihcdHRQt1x1MDAwMKpTQFx1MDAwMUB4oyV5qd0mclrMmuRq48eYPnvijafId4tggXairSFcdTAwMDX68Lp0bKdAbyx0XHUwMDAx26VAVVx1MDAxZpZCfK5cdTAwMDG9uspcdTAwMTFq21Hin6GuvjnhTrgqNLNgQvBCXHUwMDBlrupX15XSXW5opm4qfi8qiVxms3yZLuHPrFxiXHUwMDFk6jhVcTuKmETCr+jXLyHlp6+B90JouaVcckE3q08vLL39YN3Pntv95up0P7tLW29cdTAwMGVH7vUyy4xHgFx1MDAwM1/bhDn005M3VfKSIfWh35tcdTAwMTm3id1kXHUwMDBlPHAkaCBzXHUwMDFi1UfCXHUwMDA3xzfzLD6iXHUwMDBmvjfn8u2NfkqUhFx1MDAwNHDr9TdLXHUwMDBm1D/zQEBEXHUwMDE5hTBMiCpQhL0zJr6udvCOR7t28K6g4trhXHUwMDFkb698obu1mFx1MDAxN1x1MDAxZpO9pymc5y+4pY/NYsKD7vXB4Fx1MDAwNW0ug3JcdTAwMTYwkLvQhtAjSsCDxPfvjjKQXHUwMDE4cOyuvuB+26i/s49cdTAwMDHEiuZQdeW3UV5bV7fxMspcdTAwMWZGopj5LZnLuG7xvu+23HvRh5f4M1x1MDAxMED5bWJcZrSLtJXnrFXNXHUwMDBlrqr4O+2Fo1x1MDAxY+PAXHUwMDFh4my9aqOXgCNcdTAwMGZcdTAwMDH/h3J/ytXgxp1cdTAwMDW3wItyKnzfYlx1MDAwNlx1MDAxMVJcdTAwMGZAulx1MDAxN4dcdTAwMWNM+Y/ztGZoZtNW14nZ+lD7YbxOtOhG98f5SaB37JO9iVx1MDAwZlx1MDAxZXPeiG86y2fXkZdcdTAwMWJu0Na7TD103GNqXHUwMDAxXHUwMDAx9H5cdTAwMTMlxFxm4K9J/Maa6p/EcyUzwv22QPfLT/14p0hcdTAwMDJJRKiKTjtw/9DF0i5cdTAwMWOcn9g2XHSrM2x5XHUwMDE2IZeEr1x1MDAxNpT/+Xayn2+dz4TIaV72zdbE+cogLrFQgqOcdiAkpMy1T7c5djraWlx1MDAxNlx1MDAwNuT8dYe/3tq0tsTdr0XBjsR6izgj0Mn5o4BcdTAwMGJXjYW3JjmFUiUhwClSwzhAaKtJe62cf9WBXHUwMDAwPVx0NlGD9SSw5ivixE8nXHUwMDAy7cTVoPqPnGxccqBcZikswPb7XHUwMDEwz9dKpGzNyUvBICahojenXHUwMDE1XHUwMDE05t5cdTAwMWafQj6C08VcXJ01XHUwMDA24khgSSFVRpqyQNzZkW/ddS2hUIm2PLqCXHUwMDAwXHUwMDAwlcvLXHUwMDEw4k6pXG5cYlxchf/eWsXjgHMpqZBSOOXDXGI7topcdTAwMDT7sYEqgkBgaV9Gd6uI17udXFwrQ/7RkVxyXHUwMDFkIVQxXHLk9uvKXHUwMDFjdvwrTVwizJ21LqHecHJaXHUwMDE5we791aM/hZBcdTAwMDTXOV5cdTAwMTdcdTAwMTIl2lx1MDAwMuGXTosg8Vx1MDAxY/YhR0RiZ71cdTAwMDBnkGyP+1x1MDAxZiskTqMoV9RTXCJcdTAwMThcdTAwMTaUejWKxCFcItzJKFx1MDAxMFx1MDAxOGF6dFx1MDAxNfFcdTAwMGV2hVFcdTAwMTEnolx1MDAxN/yOcblTRrzc+X28+b+ZiEBAlV2tbNit8lx1MDAwN057/aOvlEIukFxiNf15Ulx1MDAxNUHYZ//jacg+3Vx1MDAxNVx1MDAwMKdkgVx1MDAxMm3KXHUwMDA0Q8pcdTAwMWEh21x1MDAxZFx1MDAxNsYxVlx1MDAwM796KJIwJdU/XHUwMDFksj1VhOmzau/pYpB7uG0vqjdcdTAwMDbuTjtjXHUwMDFmc4RKJJ01qc57XHUwMDA3XHUwMDE5Xlx1MDAxNVZxNepcdTAwMDNl48vrYzhvjsdlu2lrb9/i/FnXplx1MDAxN/7p719eXHUwMDFi4HQ6zWn7n399+ev/XHUwMDAxj2E9SyJ9 + + + + Pharaoh ProjectComponent 1Component NRST Files (*.rst)Local Context Filesstatic: *.yamldynamic: *.pyAsset Scriptsplots.pySphinx Project Templatestables.py?.pySettingsResource FilesSetting TemplatesComponent TemplatesRST Files (*.rst)Local Context FilesAsset ScriptsPharaohAPI/CLI"Generation-timeTemplating" diff --git a/_images/overview.png b/_images/overview.png new file mode 100644 index 0000000..cbd1fa4 Binary files /dev/null and b/_images/overview.png differ diff --git a/_images/pptx.png b/_images/pptx.png new file mode 100644 index 0000000..f0dbb2d Binary files /dev/null and b/_images/pptx.png differ diff --git a/_modules/index.html b/_modules/index.html new file mode 100644 index 0000000..2745a0c --- /dev/null +++ b/_modules/index.html @@ -0,0 +1,377 @@ + + + + + + Overview: module code — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+ + +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/pharaoh/assetlib/api.html b/_modules/pharaoh/assetlib/api.html new file mode 100644 index 0000000..4eb842d --- /dev/null +++ b/_modules/pharaoh/assetlib/api.html @@ -0,0 +1,477 @@ + + + + + + pharaoh.assetlib.api — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for pharaoh.assetlib.api

+# flake8: noqa: F401,F403
+"""
+This module contains API functions related to asset generation.
+When asset scripts are accessing those API functions, the functions can access the project information by themselves.
+"""
+# Don't remove unused imports. They may be unused here but maybe in user code!
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from pharaoh.assetlib.context import metadata_context
+from pharaoh.assetlib.generation import register_asset, register_templating_context
+from pharaoh.assetlib.matlab_engine import Matlab
+from pharaoh.assetlib.resource import *
+
+if TYPE_CHECKING:
+    from pharaoh.assetlib.finder import AssetFinder
+
+
+def __get_pharaoh_project():
+    import inspect
+    from pathlib import Path
+
+    import pharaoh
+    from pharaoh.project import get_project
+
+    from .util import is_relative_to
+
+    try:
+        return get_project()
+    except RuntimeError:
+        pass
+
+    pharaoh_src = Path(pharaoh.__file__).parents[1]  # also works for site-packages
+    # todo: maybe we have to consider additional paths
+
+    # Find the first frame whose filename is not inside the pharaoh library
+    for frame in inspect.stack():
+        fname = Path(frame.filename)
+        if is_relative_to(fname, pharaoh_src) or "/pharaoh_contrib/" in fname.as_posix():
+            continue
+        return get_project(Path(frame.filename))
+    return None
+
+
+
+[docs] +def get_resource(alias: str, component: str | None = None): + """ + Get a Resource instance by its component name and resource alias. + """ + proj = __get_pharaoh_project() + if not component: + component = get_current_component() + return proj.get_resource(alias, component)
+ + + +
+[docs] +def find_components(expression: str = ""): + """ + Find components by their metadata using an evaluated expression. + + The expression must be a valid Python expression and following local variables may be used: + + - name: The name of the component + - templates: A list of templates used to render the component + - metadata: A dict of metadata specified + - render_context: The Jinja rendering context specified + - resources: A list of resource definitions + + :param expression: A Python expression that will be evaluated. If it evaluates to a truthy result, + the component name is included in the returned list. + + Example: ``name == "dummy" and metadata.foo in (1,2,3)``. + + A failing evaluation will be treated as False. An empty expression will always match. + """ + proj = __get_pharaoh_project() + return proj.find_components(expression)
+ + + +
+[docs] +def get_current_component() -> str: + """ + If executed from within a script that is placed inside a Pharaoh component, the function returns the components + name by analyzing the call stack. + """ + from pharaoh.assetlib import util + + proj = __get_pharaoh_project() + return util.get_component_name_by_callstack(proj.sphinx_report_project_components)
+ + + +
+[docs] +def get_asset_finder() -> AssetFinder: + """ + Returns the :class:`AssetFinder <pharaoh.assetlib.finder.AssetFinder>` instance for the current project + """ + proj = __get_pharaoh_project() + return proj.asset_finder
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/pharaoh/assetlib/finder.html b/_modules/pharaoh/assetlib/finder.html new file mode 100644 index 0000000..b5e2551 --- /dev/null +++ b/_modules/pharaoh/assetlib/finder.html @@ -0,0 +1,653 @@ + + + + + + pharaoh.assetlib.finder — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for pharaoh.assetlib.finder

+from __future__ import annotations
+
+import hashlib
+import json
+import shutil
+from pathlib import Path
+from typing import TYPE_CHECKING
+
+import omegaconf
+
+from pharaoh.log import log
+
+from .util import obj_groupby
+
+if TYPE_CHECKING:
+    from collections.abc import Iterable, Iterator
+
+
+class AssetFileLinkBrokenError(LookupError):
+    pass
+
+
+
+[docs] +class Asset: + """ + Holds information about a generated asset. + + :ivar id: An MD5 hash of the asset's filename, prefixed with "__ID__". + Since a unique suffix is included in the filename, this ID hash is also unique. + Can be used to quickly find this Asset instance. + :ivar Path infofile: Absolute path to the ``*.assetinfo`` file + :ivar Path assetfile: Absolute path to the actual asset file + :ivar omegaconf.DictConfig context: The content of *infofile* parsed into a OmegaConf dict. + """ + + def __init__(self, info_file: Path): + assert info_file.suffix == ".assetinfo" + self.id: str = "__ID__" + hashlib.md5(bytes(info_file.name, "utf-8")).hexdigest() + self.infofile: Path = info_file + self.assetfile: Path | None = None + self.context = omegaconf.OmegaConf.create(json.loads(self.infofile.read_text())) + for file in self.infofile.parent.glob(f"{self.infofile.stem}*"): + if file.suffix != ".assetinfo": + self.assetfile = file + break + if self.assetfile is None: + msg = f"There is no asset for inventory file {self.infofile}!" + raise AssetFileLinkBrokenError(msg) + + def __str__(self): + return repr(self) + + def __repr__(self): + return f"Asset[{self.infofile.stem}]" + + def __eq__(self, other: Asset): + return self.id == other.id and self.infofile == other.infofile and self.assetfile == other.assetfile + + def __hash__(self): + return hash(self.infofile.name) + + def __lt__(self, other: Asset): + return self.infofile < other.infofile + + def copy_to(self, target_dir: Path) -> Path: + """ + Copy the asset plus info-file. + + :param target_dir: The target directory to copy to. Will be created if it does not exist. + :returns: True if files were copied, False otherwise (files already exist) + """ + target_dir.mkdir(exist_ok=True, parents=True) + target_info_file = target_dir / self.infofile.name + if target_info_file.exists(): + return target_dir / self.assetfile.name + + log.debug(f"Copying asset {self} to build directory") + shutil.copy(self.infofile, target_info_file) + + if Path(self.assetfile).is_file(): + return Path(shutil.copy(self.assetfile, target_dir)) + + if Path(self.assetfile).is_dir(): + shutil.copytree(self.assetfile, target_dir / self.assetfile.name) + return target_dir / self.assetfile.name + + raise NotImplementedError + + def read_json(self): + if self.assetfile.suffix.lower() != ".json": + msg = "Can only read .json files!" + raise Exception(msg) + return json.loads(self.assetfile.read_text("utf-8")) + + def read_yaml(self): + import yaml + + if self.assetfile.suffix.lower() != ".yaml": + msg = "Can only read .yaml files!" + raise Exception(msg) + with open(self.assetfile, encoding="utf-8") as fp: + return yaml.load(fp, yaml.Loader) + + def read_text(self, encoding: str = "utf-8"): + return self.assetfile.read_text(encoding) + + def read_bytes(self): + return self.assetfile.read_bytes()
+ + + +
+[docs] +class AssetFinder: +
+[docs] + def __init__(self, lookup_path: Path): + """ + A class for discovering and searching generated assets. + + An instance of this class will be created by the Pharaoh project, where ``lookup_path`` will be set to + ``report_project/.asset_build``. + + :param lookup_path: The root directory to look for assets. It will be searched recursively for assets. + """ + self._lookup_path = lookup_path + self._assets: dict[str, list[Asset]] = {} + self.discover_assets()
+ + +
+[docs] + def discover_assets(self, components: list[str] | None = None) -> dict[str, list[Asset]]: + """ + Discovers all assets by recursively searching for ``*.assetinfo`` files and stores + the collection as instance variable (`_assets`). + + :param components: A list of components to search for assets. + If None (the default), all components will be searched. + :return: A dictionary that maps component names to a list of :class:`Asset` instances. + """ + if isinstance(components, list) and len(components): + for component in components: + self._assets[component] = [Asset(file) for file in (self._lookup_path / component).glob("*.assetinfo")] + else: + self._assets.clear() + for asset in (Asset(file) for file in self._lookup_path.glob("*/*.assetinfo")): + component = asset.assetfile.parent.name + if component not in self._assets: + self._assets[component] = [] + self._assets[component].append(asset) + + return self._assets
+ + +
+[docs] + def search_assets(self, condition: str, components: str | Iterable[str] | None = None) -> list[Asset]: + """ + Searches already discovered assets (see :func:`discover_assets`) that match a condition. + + :param condition: A Python expression that is evaluated using the content of the ``*.assetinfo`` JSON file + as namespace. If the evaluation returns a truthy result, the asset is returned. + + Refer to :ref:`this example assetinfo file <example_asset_info>` to see the available default namespace. + + Example:: + + # All HTML file where the "label" metadata ends with "_plot" + finder.search_assets('asset.suffix == ".html" and label.endswith("_plot")') + + :param components: A list of component names to search. If None (the default), all components will be searched. + :return: A list of assets whose metadata match the condition. + """ + if not condition.strip(): + return [] + + code = compile(condition, "<string>", "eval") + found = [] + + for asset in self.iter_assets(components): + try: + result = eval(code, {}, asset.context.copy()) + except Exception: + result = False + if result: + found.append(asset) + + def sort_key(asset): + try: + return asset.context.asset.index + except AttributeError: + return 0 + + # Sort by asset index, which reflects the order in which the assets were generated in the asset script + return sorted(found, key=sort_key)
+ + +
+[docs] + def iter_assets(self, components: str | Iterable[str] | None = None) -> Iterator[Asset]: + """ + Iterates over all discovered assets. + + :param components: A list of component names to search. If None (the default), all components will be searched. + :return: An iterator over all discovered assets. + """ + if not self._assets: + self.discover_assets() + + if isinstance(components, str): + components = [components] + components = components or list(self._assets.keys()) + for component in components: + if component in self._assets: + yield from self._assets[component]
+ + +
+[docs] + def get_asset_by_id(self, id: str) -> Asset | None: + """ + Returns the corresponding :class:`Asset` instance for a certain ID. + + :param id: The ID of the asset to return + :return: An :class:`Asset` instance if found, None otherwise. + """ + for asset in self.iter_assets(): + if asset.id == id: + return asset + return None
+
+ + + +
+[docs] +def asset_groupby( + seq: Iterable[Asset], key: str, sort_reverse: bool = False, default: str | None = None +) -> dict[str, list[Asset]]: + """ + Groups an iterable of Assets by a certain metadata key. + + During build-time rendering this function will be available as Jinja global function + ``asset_groupby`` and alias ``agroupby``. + + Example: + + .. code-block:: none + + We have following 4 assets (simplified notation of specified metadata): + Asset[a="1", b="3"] + Asset[a="1", c="4"] + Asset[a="2", b="3"] + Asset[a="2", c="4"] + + Grouping by "a": + asset_groupby(assets, "a") + will yield + { + "1": [Asset[a="1", b="3"], Asset[a="1", c="4"]], + "2": [Asset[a="2", b="3"], Asset[a="2", c="4"]], + } + + + Grouping by "b" and default "default": + asset_groupby(assets, "b", default="default") + will yield + { + "3": [Asset[a="1", b="3"], Asset[a="2", b="3"]], + "default": [Asset[a="1", c="4"], Asset[a="2", c="4"]], + } + + + :param seq: The iterable of assets to group + :param key: The nested attribute to use for grouping, e.g. "A.B.C" + :param sort_reverse: Reverse-sort the keys in the returned dictionary + :param default: Sort each item, where "key" is not an existing attribute, into this default group + :return: A dictionary that maps the group names (values of A.B.C) to a list of items out of the input iterable + """ + return obj_groupby(seq=seq, key=key, sort_reverse=sort_reverse, attr="context", default=default)
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/pharaoh/assetlib/generation.html b/_modules/pharaoh/assetlib/generation.html new file mode 100644 index 0000000..51a0a2c --- /dev/null +++ b/_modules/pharaoh/assetlib/generation.html @@ -0,0 +1,778 @@ + + + + + + pharaoh.assetlib.generation — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for pharaoh.assetlib.generation

+from __future__ import annotations
+
+import concurrent.futures
+import contextlib
+import io
+import json
+import logging.handlers
+import multiprocessing
+import os
+import re
+import shutil
+import traceback
+from functools import partial
+from pathlib import Path
+from types import ModuleType
+from typing import TYPE_CHECKING, Union
+
+import pharaoh.log
+from pharaoh import project
+from pharaoh.assetlib import patches
+from pharaoh.assetlib.context import context_stack
+from pharaoh.assetlib.finder import Asset
+from pharaoh.templating.second_level.sphinx_ext.asset_ext_templates import find_template
+from pharaoh.util.contextlib_chdir import chdir
+from pharaoh.util.json_encoder import CustomJSONEncoder
+
+if TYPE_CHECKING:
+    from collections.abc import Iterable
+    from queue import Queue
+
+log = pharaoh.log.log
+
+PathLike = Union[str, Path]
+
+
+def generate_assets(project_root: Path, asset_src: Path, component_name: str = "", mp_log_queue: Queue | None = None):
+    # Since this function is always called in a process by generate_assets_parallel,
+    # we need to remove at least all file handlers so child-processes don't log to the same file
+    # as the parent process, otherwise race conditions may occur.
+    # So we just remove all handlers and add a QueueHandler
+    # to send all log records to the parent in order to handle them.
+    if mp_log_queue is not None:  # pragma: no cover
+        for hdl in log.handlers:
+            log.removeHandler(hdl)
+        log.addHandler(logging.handlers.QueueHandler(mp_log_queue))
+
+    # Also forbid the project instance to add loggers we just removed
+    proj = project.PharaohProject(project_root=project_root, logging_add_filehandler=False)
+    context_stack.reset()
+
+    try:
+        script_path = asset_src.relative_to(project_root).as_posix()
+    except ValueError:
+        script_path = asset_src.as_posix()
+
+    if asset_src.suffix.lower() == ".py":
+        script_ignore_pattern = proj.get_setting("asset_gen.script_ignore_pattern")
+
+        with patches.patch_3rd_party_libraries(), context_stack.new_context(
+            context_name="generate_assets",
+            asset={
+                "script_name": asset_src.name,
+                "script_path": asset_src,
+                "index": 0,
+                "component_name": component_name,
+            },
+        ):
+            code = asset_src.read_text(encoding="utf-8")
+
+            first_line = code.split("\n", maxsplit=1)[0].strip()
+            if re.fullmatch(script_ignore_pattern, asset_src.name) or re.fullmatch(
+                r"^# *pharaoh?: *ignore *", first_line, re.IGNORECASE
+            ):
+                log.info(f"Ignoring file {script_path}")
+                return
+
+            log.info(f"Generating assets from script {script_path!r}...")
+            asset_module = module_from_file(asset_src)
+
+            WAVEWATSON_LEGACY_INPLACE = os.environ.get("WAVEWATSON_LEGACY_INPLACE")
+            os.environ["WAVEWATSON_LEGACY_INPLACE"] = "0"
+            try:
+                run_module(asset_module, code)
+            except Exception as e:
+                msg = (
+                    f"An exception was raised when executing module "
+                    f"{str(asset_src)!r}:\n\n{e}\n\nTraceback:\n{traceback.format_exc()}"
+                )
+                raise Exception(msg) from None
+            finally:
+                if WAVEWATSON_LEGACY_INPLACE is None:
+                    del os.environ["WAVEWATSON_LEGACY_INPLACE"]
+                else:
+                    os.environ["WAVEWATSON_LEGACY_INPLACE"] = WAVEWATSON_LEGACY_INPLACE
+
+    elif asset_src.suffix.lower() == ".ipynb":
+        # import locally, otherwise Sphinx autodoc on pharaoh.assetlib.api fails because of nbformat package and this
+        # issue: https://github.com/sphinx-doc/sphinx/issues/11662
+        import nbformat
+        from nbconvert.preprocessors import CellExecutionError, ExecutePreprocessor
+
+        log.info(f"Generating assets from notebook {script_path!r}...")
+
+        with open(asset_src) as file:
+            nb = nbformat.read(file, as_version=4)
+        initial_node = nbformat.notebooknode.from_dict(
+            {
+                "cell_type": "code",
+                "execution_count": None,
+                "id": "000000",
+                "outputs": [],
+                "metadata": {},
+                "source": f"""
+import os
+from pharaoh.api import PharaohProject
+from pharaoh.assetlib.api import metadata_context
+from pharaoh.assetlib.patches import patch_3rd_party_libraries
+
+proj = PharaohProject(project_root="{project_root.as_posix()}")
+patcher = patch_3rd_party_libraries()
+patcher.__enter__()
+metadata_context(
+    context_name="generate_assets",
+    asset=dict(
+        script_name="{asset_src.name}",
+        script_path="{asset_src.as_posix()}",
+        component_name="{component_name}",
+        index=0
+    )
+).activate()
+
+os.environ["WAVEWATSON_LEGACY_INPLACE"] = "0"
+"""[1:-1],
+            }
+        )
+
+        nb.cells.insert(0, initial_node)
+
+        ep = ExecutePreprocessor(timeout=600)
+        subdir = component_name or "default"
+        try:
+            ep.preprocess(nb, {"metadata": {"path": str(asset_src.parent)}})
+
+            completed_notebooks_path = proj.asset_build_dir / "completed_notebooks" / subdir / asset_src.name
+            completed_notebooks_path.parent.mkdir(parents=True, exist_ok=True)
+            with open(completed_notebooks_path, "w") as file:
+                nbformat.write(nb, file)
+        except CellExecutionError as e:
+            failed_notebooks_path = proj.asset_build_dir / "failed_notebooks" / subdir / asset_src.name
+            failed_notebooks_path.parent.mkdir(parents=True, exist_ok=True)
+            with open(failed_notebooks_path, "w") as file:
+                nbformat.write(nb, file)
+            msg = (
+                f"An exception was raised when executing notebook '{asset_src.stem}': {e}\n"
+                f"Check the notebook for errors/traces: {failed_notebooks_path}"
+            )
+            raise Exception(msg) from e
+
+
+def generate_assets_parallel(
+    project_root: PathLike, asset_sources: Iterable[tuple[str, Path]], workers: str | int = "auto"
+):
+    project_root = Path(project_root)
+    if isinstance(workers, int):
+        workers = max(1, workers)
+    if isinstance(workers, str):
+        if workers.lower() == "auto":
+            workers = multiprocessing.cpu_count()
+        else:
+            msg = "Argument worker may only be an integer number or the string 'auto'!"
+            raise ValueError(msg)
+
+    log.info(f"Executing asset generation with {workers} worker processes")
+    generate_asset_partial = partial(generate_assets, project_root=project_root)
+
+    mp_manager = multiprocessing.Manager()
+    mp_log_queue = mp_manager.Queue(-1)
+
+    results = []
+    # The queue listener collects all log records handled via the queue handler (defined inside generate_assets)
+    # in order to log them in the parent process
+    ql = logging.handlers.QueueListener(mp_log_queue, *log.handlers, respect_handler_level=True)
+    ql.start()
+    with concurrent.futures.ProcessPoolExecutor(max_workers=workers) as executor:
+        futures_map = {
+            executor.submit(
+                generate_asset_partial, asset_src=asset_source, component_name=component_name, mp_log_queue=mp_log_queue
+            ): asset_source
+            for component_name, asset_source in asset_sources
+        }
+        for future in concurrent.futures.as_completed(futures_map.keys()):
+            result = None
+            try:
+                result = future.result()
+                results.append((futures_map[future], result))
+            except SystemExit as e:
+                if e.code == 0:
+                    results.append((futures_map[future], result))
+                else:
+                    results.append((futures_map[future], e))
+            except Exception as e:
+                results.append((futures_map[future], e))
+    ql.stop()
+    return results
+
+
+def module_from_file(path: str | Path) -> ModuleType:
+    """
+    Creates a module from file at runtime.
+
+    :param path: Path to the module source code.
+    :return: A fresh module
+    """
+
+    module_path = Path(path)
+    module_name = f"asset_module_{module_path.stem.replace(' ', '_').replace('-', '_')}"
+    module = ModuleType(module_name)
+    module.__dict__["__file__"] = str(module_path.absolute())
+    module.__path__ = [module_path.parent]
+    module.__dict__["__name__"] = "__main__"
+    module.__dict__["__module_name__"] = module_name
+
+    return module
+
+
+def run_module(module: ModuleType, code: str):
+    """
+    Execute the configured source code in a module.
+
+    :param module: A module object.
+    :param code: The code to be run inside the module
+    """
+    compiled_code = compile(code, module.__dict__["__file__"], "exec")
+    with chdir(Path(module.__dict__["__file__"]).parent):
+        exec(compiled_code, module.__dict__)
+
+
+
+[docs] +def register_asset( + file: PathLike, + metadata: dict | None = None, + template: str | None = None, + data: io.BytesIO | None = None, + copy2build: bool = False, + **kwargs, +) -> Asset: + """ + Register an asset manually. The file will be copied (if data is None and 'file' is a real file) or + written (if data is given) to the asset build folder of the current Pharaoh project. + + :param file: The filename. Must exist even if data is set, to have a filename to store the asset and to + automatically determine the template (if not set via template argument). + :param metadata: Additional metadata to store on the asset. + :param template: The template used to render the asset. If omitted, it is inferred by the file extension. + + .. seealso:: :ref:`reference/directive:Asset Templates` + + :param data: An io.BytesIO instance. Used if the asset is generated in memory and should be stored to disk by + this function. + :param copy2build: If True, the asset will be copied to the asset build directory, + even if not referenced in the template. + + Background: Pharaoh stores all assets in the project directory and copies them to the build directory only if + copy2build is set to True or on-demand by Pharaoh. For example if an HTML file is rendered using an iframe, + the HTML file has to be copied to the build folder where the iframe can later include it. + + :returns: The file path where the asset will be actually stored + """ + component_name = kwargs.pop("component", None) + if kwargs: + raise Exception("Unknown keyword arguments " + ",".join(kwargs.keys())) + + file = Path(file) + if not template: + suffix = file.suffix.lower() + template = { + ".html": "iframe", # todo: use raw_html if this is not a full HTML file, iframe otherwise + ".rst": "raw_rst", + ".txt": "raw_txt", + ".svg": "image", + ".png": "image", + ".jpg": "image", + ".jpeg": "image", + ".gif": "image", + ".md": "markdown", + }.get(suffix) + + if template is not None: + find_template(template) # will fail if template does not exist + + if template in ("iframe",): + copy2build = True + + file = Path(file) + try: + active_app = project.get_project() + except Exception: + # If there is no Pharaoh application yet, the calling file is presumably executed standalone, + # so we have to skip exporting any files + return None + + # If this function is used in an asset script that is executed directly, the component name is not added to the + # metadata context, so we have to find the component via the callstack and pass it to _build_asset_filepath + from pharaoh.assetlib.api import get_current_component + + if component_name is None: + try: + component_name = get_current_component() + except LookupError: # raised if method is not executed from inside a component + msg = ( + "When register_asset is called outside a component of a Pharaoh project, keyword argument " + "'component' must be set!" + ) + raise Exception(msg) from None + asset_file_path = active_app._build_asset_filepath(file, component_name) + + metadata = metadata or {} + metadata.pop("context_name", None) + metadata.pop("asset", None) + with context_stack.new_context( + context_name="manual_registry", + asset={ + "user_filepath": str(file), + "file": str(asset_file_path), + "name": asset_file_path.name, + "stem": asset_file_path.stem, + "suffix": asset_file_path.suffix, + "template": template, + "copy2build": copy2build, + }, + **metadata, + ): + with contextlib.suppress(LookupError): + # If asset scripts are executed directly, this context does not exist + context_stack.get_parent_context(name="generate_assets")["asset"]["index"] += 1 + if isinstance(data, io.BytesIO): + with open(asset_file_path, "wb") as fp: + fp.write(bytes(data.getbuffer())) + else: + if file.is_file(): + shutil.copy(file.absolute(), asset_file_path) + elif file.is_dir(): + shutil.copytree(file, asset_file_path) + else: + msg = f"{file} does not exist!" + raise FileNotFoundError(msg) + info_file = context_stack.dump(asset_file_path) + return Asset(info_file)
+ + + +
+[docs] +def register_templating_context(name: str, context: str | Path | dict | list, metadata: dict | None = None, **kwargs): + """ + Register a data context for the build-time templating stage. + The data may be given directly as dict/list or via a json or yaml file. + + This function is designed to be used within asset scripts, to easily register data you extract from resources + for the templating process. + + Example:: + + from pharaoh.assetlib.api import register_templating_context + + register_templating_context(name="foo", context={"bar": "baz"}) + # will be accessed like this: {{ ctx.local.foo.bar.baz }} + + :param name: The name under which the data context is available inside Jinja templates. + Access like this (name: mycontext):: + + {% set mycontext = ctx.local.mycontext %} + + :param context: Either a str or :external:class:`Path <pathlib.Path>` instance pointing to a json or yaml + file, or a dict or list. All data must contain only json-compatible types, otherwise the data cannot be stored. + :param metadata: The given context will be internally registered as an asset with following metadata: + ``dict(pharaoh_templating_context=name, **metadata)`` + :param kwargs: Keyword arguments that are mostly (except ``component``) passed to ``json.dumps(...)``, in case + ``context`` is a dict or list. + """ + component = kwargs.pop("component", None) + metadata = metadata or {} + metadata.pop("pharaoh_templating_context", None) + kwargs.setdefault("cls", CustomJSONEncoder) + + if not name: + msg = "name must be a non-empty string!" + raise ValueError(msg) + + if isinstance(context, (str, Path)): + file = Path(context) + if file.suffix.lower() not in (".json", ".yaml"): + msg = "If context is a file path, it's suffix must be either .json or .yaml!" + raise ValueError(msg) + register_asset(file, metadata=dict(pharaoh_templating_context=name, **metadata), component=component) + elif isinstance(context, (list, dict)): + data = io.BytesIO(json.dumps(context, **kwargs).encode("utf-8")) + register_asset( + "pharaoh_templating_context.json", + metadata=dict(pharaoh_templating_context=name, **metadata), + data=data, + component=component, + ) + else: + msg = f"Unsupported type {type(context)}!" + raise TypeError(msg)
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/pharaoh/assetlib/matlab_engine.html b/_modules/pharaoh/assetlib/matlab_engine.html new file mode 100644 index 0000000..c848b41 --- /dev/null +++ b/_modules/pharaoh/assetlib/matlab_engine.html @@ -0,0 +1,560 @@ + + + + + + pharaoh.assetlib.matlab_engine — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for pharaoh.assetlib.matlab_engine

+from __future__ import annotations
+
+import datetime
+import io
+import os
+import typing as t
+from pathlib import Path
+
+from pharaoh.log import log
+
+if t.TYPE_CHECKING:
+    import matlab.engine as me
+
+
+def ensure_engine():
+    try:
+        import matlab.engine  # noqa: F401
+    except Exception:
+        log.error(
+            "The Matlab Engine for Python is not installed.\n"
+            "Please install the corresponding version via PIP:\n"
+            "  R2020B: pip install matlabengine==9.9.*\n"
+            "  R2021A: pip install matlabengine==9.10.*\n"
+            "  R2021B: pip install matlabengine==9.11.*\n"
+            "  R2022A: pip install matlabengine==9.12.*\n"
+            "  R2022B: pip install matlabengine==9.13.*\n"
+            "  R2023A: pip install matlabengine==9.14.*\n"
+            "  R2023B: pip install matlabengine==9.15.*\n"
+        )
+        raise
+
+
+PathLike = t.Union[str, Path]
+
+
+
+[docs] +class Matlab: + """ + Matlab engine for Pharaoh asset generation. + + Usage:: + + from pharaoh.assetlib.api import Matlab + + eng = Matlab() + with eng: + out, err = eng.execute_script("myscript.m") + result, out, err = eng.execute_function("myfunc", [800.0], nargout=1) + + """ + +
+[docs] + def __init__(self, start_options="-nodesktop"): + """ + Matlab engine for Pharaoh asset generation. + + :param start_options: See options at https://de.mathworks.com/help/matlab/ref/matlabwindows.html + """ + self._engine: me.MatlabEngine | None = None + self._start_options = start_options
+ + + @property + def eng(self) -> me.MatlabEngine: + """ + Returns the matlab engine instance + """ + return self._engine + +
+[docs] + def __enter__(self): + """ + Context manager enter. Connects to Matlab. + """ + self.connect() + return self
+ + +
+[docs] + def __exit__(self, exc_type, exc_val, exc_tb): + """ + Context manager exit. Disconnects from Matlab. + """ + if self.eng is not None: + self.eng.exit() + self._engine = None
+ + +
+[docs] + def connect(self): + """ + Connects to a Matlab engine. If an engine is running it will be connected, otherwise a new Matlab instance + will be started and connected. + """ + ensure_engine() + import matlab.engine as me + + active_engines = me.find_matlab() + if len(active_engines): + self._engine = me.connect_matlab(name=active_engines[0], background=False) + else: + self._engine = me.start_matlab(option=self._start_options, background=False) + + # Write connection info to Matlab workspace + info = datetime.datetime.now(tz=datetime.timezone.utc).strftime("%Y-%m-%d %H:%M:%S") + f", PID {os.getpid()}" + self.eng.workspace["PharaohConnected"] = info
+ + +
+[docs] + def show_gui(self): + """ + Makes the Matlab GUI visible. + """ + self.eng.desktop(nargout=0)
+ + +
+[docs] + def execute_script(self, script_path: PathLike) -> tuple[str, str]: + """ + Executes a Matlab script. + + Returns a tuple of strings containing the stdout and stderr streams of the script execution. + """ + script_path = Path(script_path).absolute() + + # Call the script + fname = script_path.stem + workdir = str(script_path.parent) if script_path.exists() else None + _, out, err = self.execute_function(function_name=fname, args=None, nargout=0, workdir=workdir) + return out, err
+ + +
+[docs] + def execute_function( + self, + function_name: str, + args: list[t.Any] | None = None, + nargout: int = 0, + workdir: PathLike | None = None, + ) -> tuple[t.Any, str, str]: + """ + Executes a Matlab function + + Returns a tuple of result, stdout stream and stderr stream of the function execution. + + The shape/type of result depends on the argument 'nargout'. See below. + + :param function_name: + The name of the function to execute. + + The function must be in the Matlab path to execute it. + + Alternatively the workdir argument can be set to the parent directory of the function. + :param args: A list of positional input arguments to the function + :param nargout: + The number of output arguments. + + If 0, the result return value of this function will be None. + + If 1, the result return value will be a single value. + + If greater than 1, the result return value will be a tuple containing nargout values. + :param workdir: The Matlab working directory to be changed to during function execution. Skipped if None. + """ + old_cwd = self.eng.cd(str(workdir)) if workdir is not None else None + try: + out = io.StringIO() + err = io.StringIO() + result = getattr(self.eng, function_name)( + *(args or []), background=False, nargout=max(0, int(nargout)), stdout=out, stderr=err + ) + out = out.getvalue() + err = err.getvalue() + return result, out, err + except Exception as e: + log.error(f"Execution of Matlab function/script {function_name!r} failed: {e}", exc_info=True) + raise + finally: + if old_cwd: + self.eng.cd(old_cwd, background=False)
+
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/pharaoh/assetlib/resource.html b/_modules/pharaoh/assetlib/resource.html new file mode 100644 index 0000000..5529ec3 --- /dev/null +++ b/_modules/pharaoh/assetlib/resource.html @@ -0,0 +1,583 @@ + + + + + + pharaoh.assetlib.resource — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for pharaoh.assetlib.resource

+from __future__ import annotations
+
+import abc
+import copy
+import glob
+from pathlib import Path
+from typing import ClassVar
+
+import attrs
+import natsort
+import omegaconf
+from attrs.validators import deep_iterable, in_, instance_of, min_len
+
+__all__ = ["Resource", "LocalResource", "TransformedResource", "FileResource", "CustomResource"]
+
+
+
+[docs] +@attrs.define +class Resource: + """ + The base class for all resources. Provides serialization/deserialization methods. + """ + + _registry: ClassVar[dict] = {} + + alias: str = attrs.field(validator=(instance_of(str), min_len(1))) + _cachedir: str = attrs.field(validator=instance_of(str), kw_only=True, default="") + + @staticmethod + def from_dict(definition: dict | omegaconf.DictConfig) -> Resource: + if isinstance(definition, omegaconf.DictConfig): + definition = omegaconf.OmegaConf.to_container(definition, resolve=True) + else: + definition = copy.deepcopy(definition) + resource_class = definition.pop("__class__") + registry = collect_resources() + if resource_class not in registry: + msg = ( + f"No such resource type {resource_class!r} from definition of resource {definition}. " + f"Available types are {','.join(Resource._registry.keys())!r}!" + ) + raise LookupError(msg) + return registry[resource_class](**definition) + + def to_dict(self): + dikt = dict(**attrs.asdict(self, filter=lambda attr, value: not attr.name.startswith("_"))) + dikt["__class__"] = self.__class__.__name__ + return dikt
+ + + +_collected_resources_cache = None + + +def collect_resources() -> dict[str, type[Resource]]: + global _collected_resources_cache # noqa: PLW0603 + if _collected_resources_cache is None: + from pharaoh.plugins.plugin_manager import PM + + _collected_resources_cache = {type_.__name__: type_ for type_ in PM.pharaoh_collect_resource_types()} + return _collected_resources_cache + + +
+[docs] +@attrs.define +class LocalResource(abc.ABC, Resource): + """ + Represents a resource that is located on the user's hard-drive. + """ + +
+[docs] + @abc.abstractmethod + def locate(self) -> Path: + """ + Returns the path to the resource file. + """ + raise NotImplementedError
+
+ + + +
+[docs] +@attrs.define +class TransformedResource(LocalResource, abc.ABC): + """ + A resource that is depending on another resource and maybe transforms it dynamically into another resource. + """ + + sources: list[str] = attrs.field( + validator=( + deep_iterable( + member_validator=instance_of(str), + iterable_validator=instance_of(list), + ), + min_len(1), + ) + ) + +
+[docs] + @abc.abstractmethod + def transform(self, resources: dict[str, Resource]): + """ + Executed by Pharaoh before asset generation. + Transforms the linked resource into a new one. + The resulting resource may be cached or recreated each time. + """ + raise NotImplementedError
+
+ + + +
+[docs] +@attrs.define +class CustomResource(Resource): + """ + A resource that can hold arbitrary information. It may be used as a temporary replacement for + more specific resources that are not yet implemented. + For example, it could hold database connection properties or other custom data. + + Example:: + + CustomResource(alias="foo", traits=dict(a=1, b=[2, 3], c=dict(d=[4, 5]))) + + + :ivar traits: A dict with arbitrary data. + """ + + traits: dict
+ + + +
+[docs] +@attrs.define +class FileResource(LocalResource): + """ + A resource that matches files/directories using a wildcard pattern. + + Example:: + + FileResource(alias="mycsvfile", pattern="C:/temp/*.csv", sort="ascending") + + :ivar pattern: A pathname pattern. + The pattern may contain simple shell-style wildcards. + Filenames starting with a dot are special cases that are not matched by '*' and '?' patterns. + :ivar sort: descending (default) or ascending. + Sorting is done using the natsort library to sort filesystem paths naturally. Example:: + + 0.txt, 01.txt, 1.txt, 10.txt, 2.txt # default sorting (ascending) + 0.txt, 01.txt, 1.txt, 2.txt, 10.txt # natural sorting (ascending) + + """ + + pattern: str | Path = attrs.field(converter=str) + + sort: str = attrs.field(validator=in_(("ascending", "descending")), default="descending") + +
+[docs] + def locate(self) -> Path: + """ + Returns the first match for the file pattern, depending on the chosen sort order. + """ + return self.first_match()
+ + +
+[docs] + def first_match(self) -> Path: + """ + Returns the first match for the file pattern, depending on the chosen sort order. + """ + return self.get_files()[0]
+ + +
+[docs] + def last_match(self) -> Path: + """ + Returns the last match for the file pattern, depending on the chosen sort order. + """ + return self.get_files()[-1]
+ + +
+[docs] + def get_match(self, index) -> Path: + """ + Returns the n-th match for the file pattern, depending on the chosen sort order. + """ + return self.get_files()[index]
+ + +
+[docs] + def get_files(self, recursive=True) -> list[Path]: + """ + Returns all matches for the file pattern, depending on the chosen sort order. + + :param recursive: If recursive is true, the pattern '**' will match any files and + zero or more directories and subdirectories. + """ + files = [Path(p).resolve() for p in glob.glob(self.pattern, recursive=recursive)] + sort = {"ascending": False, "descending": True} + return natsort.natsorted(files, reverse=sort[self.sort])
+
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/pharaoh/project.html b/_modules/pharaoh/project.html new file mode 100644 index 0000000..3b7ac05 --- /dev/null +++ b/_modules/pharaoh/project.html @@ -0,0 +1,1507 @@ + + + + + + pharaoh.project — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for pharaoh.project

+from __future__ import annotations
+
+import copy
+import datetime
+import functools
+import logging
+import os
+import re
+import shutil
+import sys
+import traceback
+import uuid
+from pathlib import Path
+from typing import TYPE_CHECKING, Any, Union
+
+import attr
+import attrs
+import omegaconf
+import omegaconf.errors
+
+import pharaoh
+import pharaoh.log
+import pharaoh.util.oc_resolvers
+from pharaoh.assetlib import finder, resource
+from pharaoh.assetlib.context import context_stack
+from pharaoh.errors import AssetGenerationError, ProjectInconsistentError
+from pharaoh.plugins.plugin_manager import PM
+from pharaoh.util.contextlib_chdir import chdir
+
+if TYPE_CHECKING:
+    from collections.abc import Iterable, Iterator
+
+    import jinja2
+
+try:
+    PHARAOH_CLI_PATH = Path(shutil.which("pharaoh.exe")).as_posix()
+except Exception:
+    PHARAOH_CLI_PATH = "pharaoh.exe"
+
+PathLike = Union[str, Path]
+
+LRU_PHARAOH_PROJECT: PharaohProject = None
+
+DEFAULT_MISSING = object()
+
+log = pharaoh.log.log
+
+
+
+[docs] +def get_project(lookup_path: str | Path | None = None) -> PharaohProject: + """ + Returns an instance of PharaohProject. + + :param lookup_path: If None, the function tries to return the Pharaoh singleton instance that is already loaded in + memory and raises and exception if there is none. + If given a string or Path instance, searches the given path and all its parent folders + for a ``pharaoh.yaml`` project file and returns a PharaohProject instance. + """ + if lookup_path is None: + # Enforce having a singleton per process + if LRU_PHARAOH_PROJECT is None: + msg = "No PharaohProject instance created yet!" + raise RuntimeError(msg) + return LRU_PHARAOH_PROJECT + + if isinstance(lookup_path, (str, Path)): + lookup_path = Path(lookup_path) + if lookup_path.is_file(): + lookup_path = lookup_path.parent + else: + msg = "Unsupported value for argument 'lookup_path'." + raise ValueError(msg) + + path = Path(lookup_path).absolute() + if path.is_file(): + path = path.parent + while True: + if (path / PharaohProject.PROJECT_SETTINGS_YAML).exists(): + return PharaohProject(project_root=path) + + if path == path.parent: + # We reached the path's anchor level + msg = ( + f"Cannot find a Pharaoh project file {PharaohProject.PROJECT_SETTINGS_YAML} in " + f"{lookup_path} or one of its parent directories!" + ) + raise LookupError(msg) + + path = path.parent
+ + + +
+[docs] +class PharaohProject: + PROJECT_SETTINGS_YAML = "pharaoh.yaml" + + def __new__(cls, *args, **kwargs): + # Cache the last recently used project instance in a global variable, + # so it can be accessed from anywhere after creation + global LRU_PHARAOH_PROJECT # noqa: PLW0603 + LRU_PHARAOH_PROJECT = object.__new__(cls) + return LRU_PHARAOH_PROJECT + + def __del__(self): + try: # noqa: SIM105 + pharaoh.log.remove_filehandlers(self._project_root) + except Exception: + pass + +
+[docs] + def __init__( + self, + project_root: PathLike, + overwrite: bool = False, + templates: str | Iterable[str] = ("pharaoh.default_project",), + template_context: dict[str, Any] | None = None, + **kwargs, + ): + """ + Instantiates a Pharaoh project instance using either an existing project root + (directory containing pharaoh.yaml) or creates a new project. + + :param project_root: A directory containing a pharaoh.yaml file + :param overwrite: If the project directory already contains files, overwrite them. + :param templates: The project template(s) to use for creating the Sphinx project files. + Maybe be any number of template names or paths to directories. + :param template_context: The Jinja rendering context for the selected project templates. + :keyword custom_settings: A path to a YAML file containing settings to overwrite the default project settings. + """ + self._settings_map: dict[str, omegaconf.DictConfig] = {} + self._project_root: Path = Path(project_root).absolute().resolve() + self._asset_finder: finder.AssetFinder = None + + logging_add_filehandler = kwargs.pop("logging_add_filehandler", True) + custom_settings = kwargs.pop("custom_settings", None) + if kwargs: + msg = f"Unknown keyword arguments {tuple(kwargs.keys())}!" + raise ValueError(msg) + + pharaoh.log.add_streamhandler() + if logging_add_filehandler: + pharaoh.log.add_filehandler(self._project_root) + pharaoh.log.add_warning_filehandler(self._project_root) + + if isinstance(templates, str): + templates = [templates] + self._ensure_project( + templates=templates, template_context=template_context, recreate=overwrite, custom_settings=custom_settings + ) + + self.load_settings(namespace="all") + log.setLevel(self.get_setting("logging.level").upper())
+ + +
+[docs] + def load_settings(self, namespace: str = "all"): + """ + Loads settings from various namespaces. + + default + The default setting in the Pharaoh library + + project + The project settings that may be modified by the user + + env + The settings defined by environment variables starting with PHARAO + + all + Loads all of the above + + :param namespace: The namespace to load settings from. all, default, project or env. + """ + if namespace in ("all", "default"): + self._settings_map["default"] = self._load_default_settings() + + if namespace in ("all", "project"): + project_settings = self._project_root / self.PROJECT_SETTINGS_YAML + with open(project_settings) as fp: + self._settings_map["project"] = omegaconf.OmegaConf.load(fp) + + if namespace in ("all", "env"): + dotlist = [] + env_pat = re.compile(r"^PHARAOH?((\.)|(__)).+$", re.IGNORECASE) + for k, v in os.environ.items(): + if env_pat.fullmatch(k): + keys = re.split(r"\.|(?<!_)__(?!_)", k) + key = ".".join(keys[1:]).lower() + dotlist.append(f"{key}={v}") + if len(dotlist): + self._settings_map["env"] = omegaconf.OmegaConf.from_dotlist(dotlist) + else: + self._settings_map["env"] = omegaconf.OmegaConf.create({}) + self._update_merged_settings()
+ + + def _update_merged_settings(self) -> omegaconf.DictConfig: + """ + Merges settings from all namespaces. + """ + self._merged_settings = omegaconf.OmegaConf.merge( + self._settings_map["default"], + self._settings_map["project"], + self._settings_map["env"], + ) + +
+[docs] + def get_settings(self) -> omegaconf.DictConfig: + """ + Returns merged settings from all namespaces. + """ + if self._merged_settings is None: + self._update_merged_settings() + return self._merged_settings
+ + +
+[docs] + def put_setting(self, key: str, value: Any): + """ + Sets a setting by its dot-separated name, e.g. "core.debug". + + This has no effect, if there is an environment variable defining the same setting! + """ + # todo: Let put_setting be able to overwrite settings defined in env vars. + # Currently env vars have priority. + paths = key.lower().split(".") + node = self._project_settings + for path in paths[:-1]: + node = getattr(node, path) + setattr(node, paths[-1], value) + self._update_merged_settings()
+ + +
+[docs] + def get_setting(self, key: str, default=DEFAULT_MISSING, to_container: bool = False, resolve: bool = True): + """ + Gets a setting by its dot-separated name, e.g. "core.debug". + + The settings are preferably taken from environment variables (e.g. PHARAOH.CORE.DEBUG), whereas the values + must be TOML compatible values (e.g. "true" for boolean). + + If no environment variable is found, the user defined settings in the pharaoh.yaml file in the + project root will be used. If the setting is not specified in there or the file is not existing, + the Pharaoh default settings are queried. + + If the setting is not found, a LookupError is returned unless "default" is set. + + Examples:: + + proj.get_setting("report.title") + proj.get_setting("cloud.metadata", to_container=True, resolve=False) == { + "author": "${report.author}", + "title": "${report.title}", + } + proj.get_setting("cloud.metadata", to_container=True, resolve=True) == { + "author": "loibljoh", + "title": "Pharaoh Report", + } + proj.get_setting("cloud.metadata.title") == "Pharaoh Report" + proj.get_setting("cloud.metadata") == omegaconf.dictconfig.DictConfig(...) + + :param key: Dot-separated name of the setting value to return + :param default: The returned default value if the key does not exist. + :param to_container: If set, recursively converts an OmegaConf config to a primitive Python container type + (dict or list). + :param resolve: True to resolve all values if "to_container" is set to True + """ + if not key: + msg = "Must be a string with a minimum length of 1!" + raise ValueError(msg) + merged_settings = self.get_settings() + key = key.lower() + try: + paths = key.split(".") + ret = merged_settings + for path in paths: + ret = ret[path] + + if omegaconf.OmegaConf.is_config(ret) and to_container: + ret = omegaconf.OmegaConf.to_container(ret, resolve=resolve) + return ret + except Exception: + if default is DEFAULT_MISSING: + msg = f"No setting key called {key!r}!" + raise LookupError(msg) from None + return default
+ + +
+[docs] + def save_settings(self, include_env: bool = False): + """ + Saves back the project settings to the project YAML file. + + :param include_env: If True, Pharaoh settings that are set via environment variables will be persisted + to the project settings YAML file. + """ + project_settings = self._project_root / self.PROJECT_SETTINGS_YAML + if include_env: + settings = omegaconf.OmegaConf.merge( + self._settings_map["project"], + self._settings_map["env"], + ) + else: + settings = self._project_settings + with open(project_settings, "w") as fp: + omegaconf.OmegaConf.save(config=settings, f=fp)
+ + + @property + def _project_settings(self): + return self._settings_map["project"] + + @property + def settings_file(self): + return self._project_root / self.PROJECT_SETTINGS_YAML + + @property + def project_root(self): + return self._project_root + + @property + def sphinx_report_project(self): + return self.project_root / "report-project" + + @property + def sphinx_report_project_components(self): + return self.sphinx_report_project / "components" + + @property + def sphinx_report_build(self): + return self.project_root / "report-build" + + @property + def asset_build_dir(self): + return self.sphinx_report_project / ".asset_build" + + @property + def asset_finder(self) -> finder.AssetFinder: + if self._asset_finder is None: + self._asset_finder = finder.AssetFinder(self.asset_build_dir) + return self._asset_finder + +
+[docs] + def add_component( + self, + component_name: str, + templates: str | (Path | (Iterable[str] | Iterable[Path])) = ("pharaoh.empty",), + render_context: dict | None = None, + resources: list[resource.Resource] | None = None, + metadata: dict[str, Any] | None = None, + index: int = -1, + overwrite: bool = False, + ): + """ + Adds a new component to the Pharaoh project + + Example:: + + from pharaoh.api import FileResource, PharaohProject + + proj = PharaohProject(".") + proj.add_component( + component_name="component_ABC", + templates=[ + "plugin_abc.template_xyz", # plugin template + "path/to/template/directory", # template directory + "path/to/template/file/tmpl.pharaoh.py" # template file + ], + render_context={"foo": "bar"}, + resources=[FileResource(alias="dlh5_result", pattern="C:/temp/**/*.dlh5")], + metadata={"some tag": "some value"}, + index=-1, # append + overwrite=False + ) + + :param component_name: The name of the component. Must be a valid Python identifier. + :param templates: A list of component templates to use for creating the components project files. + Those may be the template identifier (e.g. ``plugin_abc.template_xyz``) of a registered plugin template, + a path to a template directory or a path to a template file (single-file template). + + Since multiple template may be specified, their order matters in cases where different templates + create equally-named files, thus templates might overwrite files of the previous templates. + + This enables *template-composition*, where template designers can chunk their bigger templates into + smaller re-usable building blocks. + + If omitted, an empty default template is used. + + :param render_context: The Jinja rendering context for the selected template. + The actual template rendering context will have ``component_name``, ``resources`` and + ``metadata`` available under the respective keys. + :param resources: A list of Resource instances, defining the component's resources used in asset scripts + :param metadata: A dictionary of metadata that may be used to find the component via + :func:`PharaohProject.find_component() + <pharaoh.project.PharaohProject.find_component>` method. + :param overwrite: If True, an already existing component will be overwritten + """ + from pharaoh.templating.first_level.find import find_template + from pharaoh.templating.first_level.render import render_template + + # Check if component with this name already added + components = [] + for comp in self.iter_components(): + if comp["name"] == component_name: + if overwrite: + pass + else: + msg = f"Component {component_name!r} already exists!" + raise KeyError(msg) + else: + components.append(comp) + + render_context = render_context or {} + render_context["pharaoh_cli_path"] = PHARAOH_CLI_PATH + + if isinstance(templates, (str, Path)): + templates = [templates] + templates = [str(t) for t in templates] + + if not templates: + msg = "No templates specified!" + raise ValueError(msg) + + comp = Component( + name=component_name, + templates=templates, + render_context=render_context, + resources=resources or [], + metadata=metadata or {}, + ) + + # Support negative indexing where -1 is the last item. Normal insert with -1 will insert at second last index. + if index < 0: + index = max(0, len(components) + 1 + index) + + components.insert( + index, + attrs.asdict(comp), + ) + + for templ in templates: + template_path = find_template(templ) + if (template_path / "[[ component_name ]]").exists(): # pragma: no cover + msg = ( + "The usage of [[ component_name ]] as top level directory name of component " + "templates is not required anymore. " + ) + raise RuntimeError(msg) + + render_template( + template_path=template_path, + outputdir=self.sphinx_report_project_components / component_name, + context=comp.get_render_context(), + ) + + self._project_settings.components = components + self.save_settings() + log.info(f"Added component {component_name!r}. Saved project.")
+ + +
+[docs] + def add_template_to_component( + self, component_name: str, templates: str | (Path | Iterable[str]), render_context: dict | None = None + ): + """ + Adds additional templates to an existing component, that may overwrite existing files during rendering. + + :param component_name: The name of the component. Must be a valid Python identifier. + :param templates: The component template(s) to use for creating the components project files. + Maybe be the a single or multiple template names or paths to a directory. + :param render_context: The Jinja rendering context for the selected template. + The actual template rendering context will have component_name, resources and metadata + available under the respective keys. + """ + from pharaoh.templating.first_level.find import find_template + from pharaoh.templating.first_level.render import render_template + + # Check if component with this name already added + for component_index, comp in enumerate(self.iter_components()): # noqa: B007 + if comp["name"] == component_name: + component = Component.from_dictconfig(comp) + break + else: + msg = f"Component {component_name!r} does not exist!" + raise KeyError(msg) + + component.render_context.update(render_context or {}) + + if isinstance(templates, (str, Path)): + templates = [templates] + templates = [str(t) for t in templates] + + if not templates: + msg = "No templates specified!" + raise ValueError(msg) + + component.templates.extend(templates) + + for templ in templates: + template_path = find_template(templ) + render_template( + template_path=template_path, + outputdir=self.sphinx_report_project_components / component_name, + context=component.get_render_context(), + ) + + self._project_settings.components[component_index] = attrs.asdict(component) + + self._update_merged_settings() + self.save_settings() + log.info(f"Updated component {component_name!r}. Saved project.")
+ + +
+[docs] + def remove_component( + self, + filter: str, + regex: bool = False, + ) -> list[str]: + """ + Removes one or multiple existing components. + + :param filter: A case-insensitive component filter. Either a full-match or regular expression, depending on + regex argument. + :param regex: If True, the filter argument will be treated as regular expression. Components + that partially match the regular expression are removed. + :returns: A list of component names that got removed + """ + # Check if component with this name already added + if regex: + rex = re.compile(filter, flags=re.IGNORECASE) + else: + filter = filter.lower() + removed = [] + components = [] + for comp in self.iter_components(): + if regex and rex.match(comp["name"]) is not None or comp["name"].lower() == filter: + comp_files = self.sphinx_report_project_components / comp["name"] + if comp_files.exists() and comp_files.is_dir(): # pragma: no cover + shutil.rmtree(comp_files) + removed.append(comp["name"]) + else: + components.append(comp) + + if len(removed): + self._project_settings.components = components + self.save_settings() + log.info(f"Removed components {','.join(removed)}. Saved project.") + return removed
+ + +
+[docs] + def iter_components(self) -> Iterator[omegaconf.DictConfig]: + """ + Returns an iterator over all components from a project. + """ + yield from self._project_settings.get("components", []) or []
+ + +
+[docs] + def find_components(self, expression: str = "") -> list[omegaconf.DictConfig]: + """ + Find components by their metadata using an evaluated expression. + + The expression must be a valid Python expression and following local variables may be used: + + - name: The name of the component + - templates: A list of templates used to render the component + - metadata: A dict of metadata specified + - render_context: The Jinja rendering context specified + - resources: A list of resource definitions + + :param expression: A Python expression that will be evaluated. If it evaluates to a truthy result, + the component name is included in the returned list. + + Example: ``name == "dummy" and metadata.foo in (1,2,3)``. + + A failing evaluation will be treated as False. An empty expression will always match. + """ + found = [] + for comp in self.iter_components(): + if not expression: + found.append(comp) + continue + try: + result = eval(expression, {}, copy.deepcopy(comp)) + except Exception: + result = False + if result: + found.append(comp) + return found
+ + +
+[docs] + def get_resource(self, alias: str, component: str) -> resource.Resource: + """ + Finds a Resource from a project component by its alias. + + :param component: The component's name + :param alias: The resource's alias + """ + for r in self.iter_resources(component): + if r["alias"] == alias: + obj = resource.Resource.from_dict(r) + obj._cachedir = str(self.sphinx_report_project / ".resource_cache" / component) + return obj + + msg = f"Cannot find resource {alias!r} in component {component!r}!" + raise LookupError(msg)
+ + +
+[docs] + def iter_resources(self, component: str) -> Iterator[omegaconf.DictConfig]: + """ + Returns an iterator over all resources from a project component. + + :param component: The component's name + """ + for comp in self.iter_components(): + if comp["name"] == component: + yield from comp["resources"] + return + + msg = f"Component {component} does not exist!" + raise LookupError(msg)
+ + +
+[docs] + def update_resource(self, alias: str, component: str, resource: resource.Resource): + """ + Updates a Resource from a project component by its alias. + + :param component: The component's name + :param alias: The resource's alias + :param resource: The resource's alias + """ + for comp in self.iter_components(): + if comp["name"] == component: + for res in comp["resources"]: + if res["alias"] == alias: + res.update(resource.to_dict()) + self.save_settings() + return + comp["resources"].append(resource.to_dict()) + self.save_settings() + return + + msg = f"Component {component} does not exist!" + raise LookupError(msg)
+ + +
+[docs] + def get_default_sphinx_configuration(self, confdir: PathLike): + """ + Provides Sphinx project configurations, that will be dynamically included by the generated conf.py. + """ + confdir = Path(confdir) + + config = { + "pharaoh_jinja_templates": [], + "pharaoh_jinja_context": {}, + "extensions": [ + "sphinx_design", # https://github.com/executablebooks/sphinx-design + "pharaoh.templating.second_level.sphinx_ext.jinja_ext", + "pharaoh.templating.second_level.sphinx_ext.asset_ext", + ], + "project": self.get_setting("report.title"), + "author": self.get_setting("report.author"), + "copyright": f"{datetime.datetime.now(tz=datetime.timezone.utc).date()}", + # The suffix(es) of source filenames. + # You can specify multiple suffix as a list of string: + "source_suffix": [".rst", ".txt"], + "master_doc": "index", + "templates_path": ["_templates"], + # List of patterns, relative to source directory, that match files and + # directories to ignore when looking for source files. + # This pattern also affects html_static_path and html_extra_path. + "exclude_patterns": [".asset_build", "**/asset_scripts"], + # Make sure the target is unique + "autosectionlabel_prefix_document": True, + # Latex Builder (PDF) + } + # HTML Builder + static_path = confdir / "_static" + assert static_path.exists() + css_files = list((static_path / "css").glob("*.css")) + js_files = list((static_path / "js").glob("*.js")) + config.update( + { + "html_title": self.get_setting("report.title"), + "html_use_smartypants": True, + "html_last_updated_fmt": "%b %d, %Y", + "html_split_index": False, + "html_sidebars": { + "**": ["searchbox.html", "globaltoc.html", "sourcelink.html"], + }, + # Custom index page + # https://ofosos.org/2018/12/28/landing-page-template/ + # html_additional_pages={"index": "index.html"}, + # 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 "pharaoh.css" will overwrite the builtin "pharaoh.css". + "html_static_path": ["_static"], + "html_css_files": [f"css/{cssfile.name}" for cssfile in css_files], + "html_js_files": [f"js/{jsfile.name}" for jsfile in js_files], + "html_show_sphinx": False, + "html_show_copyright": True, + "html_context": { + "pharaoh_version": pharaoh.__version__, + "pharaoh_logo": "_static/html_logo.png", + "pharaoh_show_logo": self.get_setting("report.html.show_pharaoh_logo"), + "pharaoh_homepage": "", # todo: change to RTD + }, + "html_style": "sphinx_rtd_theme_overrides.css", + "html_theme": "sphinx_rtd_theme", + "html_theme_options": { + "display_version": False, + "prev_next_buttons_location": "both", + "style_external_links": False, + "collapse_navigation": False, + "sticky_navigation": True, + "navigation_depth": 5, + "includehidden": False, + }, + } + ) + + PM.pharaoh_configure_sphinx(self, config, confdir) + + return config
+ + +
+[docs] + def generate_assets(self, component_filters: Iterable[str] = (".*",)) -> list[Path]: + """ + Generate all assets by executing the asset scripts of a selected or all components. + + All asset scripts are executed in separate parallel child processes + (number of workers determined by ``asset_gen.worker_processes`` setting; + setting 0 executes all asset scripts sequentially in the current process). + + Setting ``asset_gen.script_ignore_pattern`` determines if a script is ignored. + + Putting the comment ``# pharaoh: ignore`` at the start of a script will also ignore the file. + + :param component_filters: A list of regular expressions that are matched against each component name. + If a component name matches any of the regular expressions, the component's + assets are regenerated (containing directory will be cleared) + """ + from pharaoh.assetlib.generation import generate_assets, generate_assets_parallel + + pharaoh.log.log_version_info() + log.info("Generating assets...") + + PM.pharaoh_asset_gen_started(self) + sources = [] + for comp in self.iter_components(): + comp_name = comp["name"] + comp_asset_build_dir = self.asset_build_dir / comp_name + for cfilter in component_filters: + if re.match(cfilter, comp_name, re.IGNORECASE) is not None: + if comp_asset_build_dir.exists(): + shutil.rmtree(comp_asset_build_dir, ignore_errors=False) + + cachedir = self.sphinx_report_project / ".resource_cache" / comp_name + log.info(f"Preparing resources of component {comp_name!r} for asset generation...") + cachedir.mkdir(parents=True, exist_ok=True) + resources: dict[str, resource.Resource] = { + resource_dict["alias"]: resource.Resource.from_dict(resource_dict) + for resource_dict in comp["resources"] + } + for r in resources.values(): + r._cachedir = str(cachedir) + + PM.pharaoh_asset_gen_prepare_resources(project=self, resources=resources) + + # TransformedResource may depend on others, so process them first. + for r in resources.values(): + r._cachedir = str(cachedir) + if isinstance(r, resource.TransformedResource): + r.transform(resources=resources) + + for script in (self.sphinx_report_project_components / comp_name / "asset_scripts").rglob("*"): + sources.append((comp_name, script)) + break + + workers = self.get_setting("asset_gen.worker_processes", 0) + if workers == 0: # Run in same process - used for easier debugging + results = [] + for component_name, asset_source in sources: + try: + generate_assets(self.project_root, asset_src=asset_source, component_name=component_name) + results.append((asset_source, None)) + except Exception: + results.append((asset_source, traceback.format_exc())) + else: + results = generate_assets_parallel(self.project_root, asset_sources=sources, workers=workers) + + msg = "At least one error occurred while asset script execution:\n" + i = 1 + processed_asset_scripts = [] + for script, ex in results: + if ex: + msg += f"\n\nError #{i}: {ex}\n" + i += 1 + else: + processed_asset_scripts.append(script) + if i > 1: + pharaoh.log.log_debug_info() + raise AssetGenerationError(msg) + + return processed_asset_scripts
+ + +
+[docs] + def build_report(self, catch_errors=True) -> int: + """ + Builds the Sphinx project and returns the status code. + + :param catch_errors: If True, Sphinx build errors will not raise an exception but return a -1 instead. + """ + import multiprocessing + + from sphinx.util.docutils import docutils_namespace, patch_docutils + + from pharaoh.sphinx_app import PharaohSphinx + + builder = self.get_setting("report.builder") + PM.pharaoh_build_started(self, builder) + self._check_template_dependencies() + + with chdir(self.sphinx_report_project): + pharaoh.log.log_version_info() + log.info(f"Building Sphinx project using {builder.upper()} builder...") + + resolved_settings = omegaconf.OmegaConf.merge( + self._settings_map["default"], + self._settings_map["project"], + self._settings_map["env"], + ) + omegaconf.OmegaConf.resolve(resolved_settings) + self.sphinx_report_build.mkdir(parents=True, exist_ok=True) + with open(self.sphinx_report_build / "pharaoh.resolved.yaml", "w") as fp: + omegaconf.OmegaConf.save(config=resolved_settings, f=fp) + + # Sphinx is used via its API instead of the CLI because we want to modify the info/warning stream + try: + sourcedir = "." + confdir = sourcedir + outputdir = str(self.sphinx_report_build) + doctreedir = f"{outputdir}\\.doctrees" + confoverrides = {} + status = pharaoh.log.SphinxLogRedirector(logging.DEBUG) + warning = pharaoh.log.SphinxLogRedirector(logging.WARNING) + freshenv = True + warningiserror = True + tags = None + verbosity = int(self.get_setting("report.verbosity", 0)) + jobs = multiprocessing.cpu_count() + keep_going = True + pdb = False + + with patch_docutils(confdir), docutils_namespace(): + app = PharaohSphinx( + sourcedir, + confdir, + outputdir, + doctreedir, + builder, + confoverrides, + status, + warning, + freshenv, + warningiserror, + tags, + verbosity, + jobs, + keep_going, + pdb, + ) + app.build(force_all=False, filenames=None) + if app.statuscode: + log.error( + f"Sphinx build finished with non-zero exit code {app.statuscode}. " + f"See warnings/errors in log above!" + ) + else: + log.info(f"Sphinx build finished successfully! Report at:\n{self.sphinx_report_build}") + + PM.pharaoh_build_finished(self, None) + + return app.statuscode + except Exception: + log.error("Errors occurred during Sphinx build:", exc_info=True) + pharaoh.log.log_debug_info() + + PM.pharaoh_build_finished(self, sys.exc_info()) + + if not catch_errors: + raise + return -1
+ + +
+[docs] + def open_report(self): # pragma: no cover + """ + Opens the generated report (if possible, e.g. for local HTML reports). + """ + builder = self.get_setting("report.builder").lower() + if builder == "html" and os.name == "nt": + report = self.sphinx_report_build / "index.html" + if not report.is_file(): + log.warning(f"Could not find HTML report {report}!") + return + import webbrowser + + webbrowser.WindowsDefault().open_new_tab(f"file://{report}") + else: + log.warning(f"The open_report method is not implemented for builder {builder}!")
+ + +
+[docs] + def archive_report(self, dest: PathLike | None = None) -> Path: + """ + Create an archive from the build folder. + + :param dest: A destination path to create the archive. Relative paths are relative to the project root. + If omitted, the filename will be taken from the ``report.archive_name`` setting. + :return: The path to the archive + """ + if not self.sphinx_report_build.exists(): + msg = "Pharaoh report not built yet!" + raise Exception(msg) + dest = Path(self.get_setting("report.archive_name")) if dest is None else Path(dest) + + if not dest.is_absolute(): + dest = (self.project_root / dest).absolute() + + if not dest.suffix: + dest /= self.get_setting("report.archive_name") + + if dest.exists(): + os.remove(dest) + dest.parent.mkdir(parents=True, exist_ok=True) + + base_name = dest.parent / dest.stem + fmt = dest.suffix.replace(".", "") + shutil.make_archive(str(base_name), fmt, self.sphinx_report_build) + log.info(f"Created archive at {dest}") + + return dest
+ + + def _check_template_dependencies(self): + """ + Checks if the used templates of all components are have dependencies to templates that are not used in any + component. + + E.g. "plugin_abc.template_A" requires an additional component that renders the "plugin_abc.template_B" template, + in order for all glossary references to work properly. + """ + l1_templates = PM.pharaoh_collect_l1_templates() + used_templates = set() + dependent_templates = set() + for comp in self._project_settings.get("components", []): + for template in comp.templates: + if template in l1_templates: + used_templates.add(template) + [dependent_templates.add(need) for need in l1_templates[template].needs] + if not dependent_templates.issubset(used_templates): + msg = ( + f"The used project templates requires the existence of components that render " + f"following templates: {','.join(dependent_templates - used_templates)}" + ) + raise ProjectInconsistentError(msg) + + def _ensure_project( + self, + templates: list[str], + template_context: dict[str, Any] | None = None, + recreate=False, + custom_settings: str | Path | None = None, + ): + """ + Creates a new project by rendering the Sphinx default project at the desired project location. + """ + self.project_root.mkdir(exist_ok=True, parents=True) + existing_files = tuple(self.project_root.glob("*")) + if len(existing_files): + if recreate: + shutil.rmtree( + self.project_root, onerror=lambda func, path, exc_info: log.warning(f"Could not delete file {path}") + ) + else: + # There are existing files, check if those are the required pharaoh project files/folders + for path in ( + self.settings_file, + self.project_root, + self.sphinx_report_project, + ): + if not path.exists(): + msg = f"Project structure inconsistent. Missing {path}!" + raise ProjectInconsistentError(msg) + self.asset_build_dir.mkdir(exist_ok=True, parents=True) + return + + # Create a new project + self.project_root.mkdir(exist_ok=True, parents=True) + self.asset_build_dir.mkdir(exist_ok=True, parents=True) + + default_settings = self._load_default_settings() + if isinstance(custom_settings, (str, Path)): + custom_settings = omegaconf.OmegaConf.load(custom_settings) + default_settings = omegaconf.OmegaConf.unsafe_merge(default_settings, custom_settings) + + self.settings_file.write_text(omegaconf.OmegaConf.to_yaml(default_settings, resolve=False)) + + gitignore = self.project_root / ".gitignore" + gitignore_lines = [ + "# Auto-generated by Pharaoh", + "/log*.txt", + "/report-build", + "/report-project/.asset_build", + "/report-project/.resource_cache", + "/*.zip", + "/*.idea", + ] + PM.pharaoh_modify_gitignore(gitignore_lines) + gitignore.write_text("\n".join(gitignore_lines), encoding="utf-8") + + from .templating.first_level.render import render_sphinx_base_project + + render_sphinx_base_project( + outputdir=self.sphinx_report_project, + templates=templates, + context={**(template_context or {}), **{"pharaoh_cli_path": PHARAOH_CLI_PATH}}, # noqa: PIE800 + ) + + PM.pharaoh_project_created(self) + return + + def _load_default_settings(self) -> omegaconf.DictConfig: + default_settings_collection = PM.pharaoh_collect_default_settings() + loaded = [] + for default_settings in default_settings_collection: + if isinstance(default_settings, (str, Path)): + if not Path(default_settings).exists(): + msg = f"Custom setting YAMl file {default_settings} does not exist!" + raise FileNotFoundError(msg) + loaded.append(omegaconf.OmegaConf.load(default_settings)) + elif isinstance(default_settings, dict): + loaded.append(omegaconf.OmegaConf.create(default_settings)) + else: + msg = ( + f"Unsupported settings type {type(default_settings)} collected from plugin hook " + f"pharaoh_collect_default_settings!" + ) + raise TypeError(msg) + + return functools.reduce(omegaconf.OmegaConf.unsafe_merge, loaded) + + def _update_template_env(self, env: jinja2.Environment): + """ + Called by PharaohTemplateEnv.sphinx_config_inited_hook to update the template env by + project specific Jinja globals or filters + + :param env: the Jinja env to update + """ + env.globals["get_setting"] = self.get_setting + env.globals["search_assets_global"] = self.asset_finder.search_assets + + def _build_asset_filepath(self, file: PathLike, component_name: str | None = None) -> Path: + """ + Returns a new file name inside the asset build directory with the same filename as the input file, + except its file stem is suffixed with a unique uuid4 hash (first 8 chars). + + E.g. `foo/bar/iris_scatter_plot.html` --> `<asset-build-dir>/<component_name>/iris_scatter_plot_ab8b4081.html` + """ + file = Path(file) + try: + component = component_name or context_stack.get_parent_context("generate_assets")["asset"]["component_name"] + except Exception: + component = "unknown_component" + (self.asset_build_dir / component).mkdir(parents=True, exist_ok=True) + return self.asset_build_dir / component / f"{file.stem}_{str(uuid.uuid4())[:8]}{file.suffix}"
+ + + +@attrs.define(frozen=True, slots=False) +class Component: + name: str = attrs.field(validator=(attr.validators.instance_of(str), attr.validators.matches_re(r"\w+"))) + templates: list[str] = attrs.field( + validator=attrs.validators.deep_iterable( + member_validator=attrs.validators.instance_of(str), + iterable_validator=attrs.validators.instance_of(list), + ) + ) + render_context: dict = attrs.field(factory=dict) + resources: list[dict] = attrs.field(factory=list, converter=lambda resources: [r.to_dict() for r in resources]) + metadata: dict = attrs.field(factory=dict) + + def get_render_context(self) -> dict: + for reserved_key in ("component_name", "resources", "metadata"): + if reserved_key in self.render_context: + msg = f"{reserved_key!r} is a reserved context key!" + raise ValueError(msg) + + return dict(**self.render_context, component_name=self.name, resources=self.resources, metadata=self.metadata) + + @staticmethod + def from_dictconfig(cfg: omegaconf.DictConfig): + return Component(**omegaconf.OmegaConf.to_container(cfg, resolve=False)) + + +if __name__ == "__main__": + pass +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/pharaoh/templating/second_level/env_filters.html b/_modules/pharaoh/templating/second_level/env_filters.html new file mode 100644 index 0000000..defdc1c --- /dev/null +++ b/_modules/pharaoh/templating/second_level/env_filters.html @@ -0,0 +1,519 @@ + + + + + + pharaoh.templating.second_level.env_filters — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for pharaoh.templating.second_level.env_filters

+from pathlib import Path
+
+import omegaconf
+from jinja2.exceptions import UndefinedError
+from jinja2.utils import is_undefined
+
+DEFAULT = object()
+
+
+
+[docs] +def required(value): + """ + Raises an UndefinedError if the context variable is undefined. + The actual name of the Jinja global function is ``req``. + + Example:: + + {{ h1(ctx.local.test.test_name|req) }} + """ + if is_undefined(value): + raise UndefinedError + return value
+ + + +
+[docs] +def rep(value) -> str: + """ + Returns ``repr(value)``. The actual name of the Jinja global function is ``repr``. + """ + return repr(value)
+ + + +
+[docs] +def or_default(value, default): + """ + Returns the result of ``value or default``. + + Alias: ``or_d`` + """ + return value or default
+ + + +
+[docs] +def oc_resolve(value: omegaconf.DictConfig): + """ + Recursively converts an OmegaConf config to a primitive container (dict or list) and returns it. + """ + conf = value.copy() + return omegaconf.OmegaConf.to_container(conf, resolve=True)
+ + + +
+[docs] +def oc_get(cfg: omegaconf.DictConfig, key, default=DEFAULT): + """ + Returns an object inside an omegaconf.DictConfig container. + If the object does not exist, the default is returned. + + Example:: + + {% set plot_title = asset.context|oc_get("plot.title", "") %} + + :param cfg: The omegaconf.DictConfig container to search + :param key: The name of the object to extract, e.g. ``plot.title``. + :param default: The default to return if key is not in cfg + """ + try: + obj = cfg + for k in key.split("."): + ret = getattr(obj, k) + obj = ret + return ret + except Exception: + if default is DEFAULT: + msg = f"Not attribute {key} in {cfg}!" + raise AttributeError(msg) from None + return default
+ + + +
+[docs] +def exists(path: str) -> bool: + """ + Returns if path exists. + """ + return Path(path).exists()
+ + + +
+[docs] +def to_path(path: str) -> Path: + """ + Returns path as a pathlib.Path instance. + """ + return Path(path)
+ + + +
+[docs] +def hasattr_(obj, name): + _hasattr = hasattr(obj, name) + _haskey = False + if isinstance(obj, (dict, omegaconf.DictConfig)): + _haskey = name in obj + return _hasattr or _haskey
+ + + +
+[docs] +def md2html(text): + """ + Converts the Markdown text (CommonMark syntax) to HTML. + """ + import mistletoe + + return mistletoe.markdown(text)
+ + + +env_filters = { + "hasattr": hasattr_, + "req": required, + "repr": rep, + "or_d": or_default, + "or_default": or_default, + "oc_resolve": oc_resolve, + "oc_get": oc_get, + "bool": bool, + "exists": exists, + "to_path": to_path, + "md2html": md2html, +} + +# Only document the functions defined in here +to_document = { + k: func for k, func in env_filters.items() if hasattr(func, "__module__") and func.__module__ == __name__ +} +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_modules/pharaoh/templating/second_level/env_globals.html b/_modules/pharaoh/templating/second_level/env_globals.html new file mode 100644 index 0000000..ebcc447 --- /dev/null +++ b/_modules/pharaoh/templating/second_level/env_globals.html @@ -0,0 +1,513 @@ + + + + + + pharaoh.templating.second_level.env_globals — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for pharaoh.templating.second_level.env_globals

+from __future__ import annotations
+
+import uuid
+from functools import partial
+from pathlib import Path
+
+from pharaoh.assetlib.finder import asset_groupby
+
+
+
+[docs] +def raise_helper(msg): + """ + Raises an Exception with a certain message. The actual name of the Jinja global function is ``raise``. + """ + raise Exception(msg)
+ + + +
+[docs] +def heading(text: str, level: int) -> str: + """ + Renders a heading for a certain level. + + Shortcuts for this function are automatically generated with names h1...h7. + + Examples:: + + {{ heading("Page Title", 1) }} + {{ h2("Sub-Title") }} + + :param text: The heading + :param level: The heading level. From 1 (top-level) to 7. + """ + default = {1: "#", 2: "*", 3: "=", 4: "-", 5: "^", 6: '"', 7: "~"} + if isinstance(level, (float, int)): + if int(level) not in default: + msg = "Default heading characters only available from index 1 to 7!" + raise LookupError(msg) + character = default[level] + else: + character = str(level) + if character not in default.values(): + raise ValueError("character must be an integer from 1-7 or one of " + ",".join(default.values())) + + text = text.strip().replace("\r", "").replace("\n", "") + if len(text) < 2: + msg = "Heading text must be at least 2 characters" + raise ValueError(msg) + return f"{text}\n{character*len(text)}"
+ + + +
+[docs] +def rand_id(chars: int | None = None) -> str: + """ + Returns a unique id for usage in HTML as it is made sure it starts with no number + """ + id = "i" + uuid.uuid4().hex + return id[:chars]
+ + + +
+[docs] +def read_text(file) -> str: + """ + Reads a file content using utf-8 encoding. + """ + file = Path(file) + return file.read_text("utf-8")
+ + + +
+[docs] +def hrule(): + """ + Renders a 1px gray horizontal rule using a raw html directive. + """ + return '.. raw:: html\n\n <hr style="height:1px;border-width:0;color:gray;background-color:gray">\n\n'
+ + + +
+[docs] +def fglob(pattern: str, root: str = ".") -> list[Path]: + """ + Returns a list of relative paths of all files relative to a root directory that match a certain pattern. + + Examples:: + + {% set index_files1 = fglob("index_*.rst") %} + {% set index_files2 = fglob("subdir/index_*.rst") %} + + :param pattern: A glob pattern to match files. + :param root: The root directory for discovering files and anchor for relative paths. + Default is the current working directory (the parent directory of the currently rendered file). + """ + rootpath = Path(root).absolute() + matches = rootpath.glob(pattern) + return [file.relative_to(rootpath) for file in matches]
+ + + +
+[docs] +def assert_true(statement: bool, message: str = ""): + """ + Wrapper for Python's assert builtin. + + :param statement: A boolean statement + :param message: The message for the AssertionError, if statement is False. + """ + if message: + assert statement, message + else: + assert statement
+ + + +env_globals = { + "assert_true": assert_true, + "fglob": fglob, + "hrule": hrule, + "raise": raise_helper, + "heading": heading, + "rand_id": rand_id, + "read_text": read_text, + "agroupby": asset_groupby, + "asset_groupby": asset_groupby, +} +# Shortcuts for headings +for i in range(1, 8): + env_globals[f"h{i}"] = partial(heading, level=i) + + +# Only document the functions defined in here +to_document = { + k: func for k, func in env_globals.items() if hasattr(func, "__module__") and func.__module__ == __name__ +} +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/_sources/changelog.rst.txt b/_sources/changelog.rst.txt new file mode 100644 index 0000000..ee139d8 --- /dev/null +++ b/_sources/changelog.rst.txt @@ -0,0 +1,5 @@ +Release History +=============== + +v0.5.0 +------ diff --git a/_sources/contact.rst.txt b/_sources/contact.rst.txt new file mode 100644 index 0000000..50f2af8 --- /dev/null +++ b/_sources/contact.rst.txt @@ -0,0 +1,27 @@ +======= +Contact +======= + +Help! +----- + +todo + + +Bug reports +----------- + +If you think you found a bug, please provide following information when contacting us: + + - Your operating system name and version + - Infos about your Pharaoh installation: + + Open a terminal with your Pharaoh env loaded (e.g. in PyCharm) and type + ``pharaoh info`` or ``python -m pharaoh info``. + + That should return something like ``Pharaoh vX.Y.Z [Python 3.9.13, Windows-10-10.0.19045-SP0]``. + - Any details about your local setup that might be helpful in troubleshooting + - The complete error message and traceback (not just a screenshot of parts of the error) + - Log files + - Detailed steps to reproduce the bug + - Maybe also a list of packages in your Python env: ``pip freeze > pharaoh_env_packages.txt`` diff --git a/_sources/dev.rst.txt b/_sources/dev.rst.txt new file mode 100644 index 0000000..351abe9 --- /dev/null +++ b/_sources/dev.rst.txt @@ -0,0 +1,5 @@ +=========== +Development +=========== + +todo diff --git a/_sources/examples/asset_scripts.rst.txt b/_sources/examples/asset_scripts.rst.txt new file mode 100644 index 0000000..936ea1d --- /dev/null +++ b/_sources/examples/asset_scripts.rst.txt @@ -0,0 +1,4 @@ +Asset Scripts +============= + +.. image:: /_static/chirping_crickets.gif diff --git a/_sources/examples/demo_reports.rst.txt b/_sources/examples/demo_reports.rst.txt new file mode 100644 index 0000000..12b3df9 --- /dev/null +++ b/_sources/examples/demo_reports.rst.txt @@ -0,0 +1,13 @@ +Demo Reports +============ + +HTML +---- + +.. image:: /_static/chirping_crickets.gif + + +Confluence +---------- + +.. image:: /_static/chirping_crickets.gif diff --git a/_sources/examples/index.rst.txt b/_sources/examples/index.rst.txt new file mode 100644 index 0000000..1945146 --- /dev/null +++ b/_sources/examples/index.rst.txt @@ -0,0 +1,9 @@ +======== +Examples +======== + +.. toctree:: + :maxdepth: 3 + + demo_reports + asset_scripts diff --git a/_sources/index.rst.txt b/_sources/index.rst.txt new file mode 100644 index 0000000..731f154 --- /dev/null +++ b/_sources/index.rst.txt @@ -0,0 +1,35 @@ +======= +Pharaoh +======= + +Pharaoh is a |Sphinx|-based Python framework for +generating reports in various format (HTML, Confluence, PDF) by combining the power of configurable +|Jinja| templates and Python scripts for asset (tables, plots, pictures, etc...) generation. + +.. image:: _static/overview.png + :scale: 75% + +================= +Table of Contents +================= + +.. toctree:: + :maxdepth: 3 + + intro + installation + user_guide + template_designer_guide + reference/index + plugins/index + examples/index + contact + changelog + dev + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/_sources/installation.rst.txt b/_sources/installation.rst.txt new file mode 100644 index 0000000..894e3ec --- /dev/null +++ b/_sources/installation.rst.txt @@ -0,0 +1,85 @@ +============ +Installation +============ + +Requirements +============ + +Any of the following Python versions are compatible. +If you don't have any installed, please download and execute a version by clicking the links below. + + - **Python 3.11 (recommended)** + [`Download Python 3.11.4 `_] + - **Python 3.10** + [`Download Python 3.10.11 `_] + - **Python 3.9** (min. 3.9.2) + [`Download Python 3.9.13 `_] + + +Installation via Pip +==================== + +Create a virtual environment +---------------------------- + +A virtual environment has its own Python binary (which matches the version of the binary that was +used to create this environment) and can have its own independent set of installed Python packages +in its site directories. + +The easiest way to create a virtual environment, is to let the IDE PyCharm (recommended) create one for you. +`Configure a virtual environment `_ shows how to do that. + +The other option is to create it via command line like follows and then configure PyCharm to use this as +project interpreter. + +.. seealso:: `venv — Creation of virtual environments `_ + +#. Open a shell (e.g. an Inicio shell or PowerShell) +#. Change into your workspace: :code:`cd C:\\temp` +#. Create a virtual environment: :code:`C:\\Python311\\python.exe -m venv pharaoh_venv` +#. Activate it: + + - Powershell :code:`.\\pharaoh_venv\\Scripts\\Activate.ps1` + - Bash: :code:`source ./pharaoh_venv/Scripts/activate` + - Cmd/Bat: :code:`.\\pharaoh_venv\\Scripts\\activate.bat` +#. You should be getting a prefix to your command line like this: :code:`(pharaoh_venv) PS C:\\temp>` +#. Ensure you use binary packages where possible by installing `wheel` first :code:`pip install wheel` + +You can find more information about `virtual environments here +`_. + + +Install +------- + +After the virtual environment has been created, activate it and install ifx-pharaoh via pip:: + + pip install pharaoh-report[] + +.. note:: Refer to section :ref:`Extras ` to install additional dependencies. + +.. note:: For updating Pharaoh to the latest version, append the ``-U`` flag to the pip command + +Then you would be able to run:: + + pip install pharaoh-report[] + + +.. _section_extras: + +Extras +------ + +The ``extras`` field is a comma-separated list of additional dependency groups. + +Following extra dependencies may be installed, depending on your use case: + +- ``bokeh``: Installs the Bokeh plotting framework and exporters +- ``holoview``: Installs the Holoviews plotting framework +- ``plotly``: Installs the Plotly plotting framework and exporters +- ``matplotlib``: Installs the Matplotlib plotting framework +- ``jupyter``: Installs Jupyter for asset script implementation +- ``pandas``: Installs Pandas Data Analysis Library +- ``pdf``: Installs an SVG to PDF converter for the LaTeX builder +- ``all-plotting``: Installs all supported plotting frameworks diff --git a/_sources/intro.rst.txt b/_sources/intro.rst.txt new file mode 100644 index 0000000..4204dbc --- /dev/null +++ b/_sources/intro.rst.txt @@ -0,0 +1,88 @@ +============ +Introduction +============ + +Pharaoh is a |Sphinx|-based Python framework for +generating reports in various formats (HTML, Confluence, PDF) by combining the power of configurable +|Jinja| templates and Python scripts for asset (tables, plots, pictures, etc...) generation. + +.. image:: _static/overview.png + :scale: 75% + :target: _static/showcase_slides.pptx + + +Click the PowerPoint icon to download the latest presentation slides: + + .. image:: _static/pptx.png + :scale: 50% + :target: _static/showcase_slides.pptx + + +.. _example_report: + +.. seealso:: :ref:`examples/demo_reports:Demo Reports` (might not reflect latest development version) + + +Pharaoh mainly serves two user groups: + +**Template Designers** + They are creating pre-defined configurable templates & data post-processing methods and for your project and + therefore provide a simplification layer for the end user. + +**End-Users** + They will build reports based on the templates the template designers created. + They provide the final content (assets like plots, table, etc...) and configuration + (context to fill template variables) for the templates. + + +Working Principle +================= + +The standard Pharaoh workflow consists of following major steps: + +#. **Project Generation** + + Generates a Pharaoh project with one or multiple components based on predefined templates. + Its core is a fully-preconfigured |Sphinx| documentation project. + + This generation is called **first-level templating** or **generation-time templating** + further on in the documentation. + + It may be checked into GIT for incremental modifications or generated each time, depending on your project setup. + + Now the templates and asset scripts in the generated components as well as the project settings + can be modified by the user. + + .. image:: _static/generation_time_templating.svg + +#. **Asset Generation** + + During asset generation, the asset scripts of each component are executed. + + By using the resources files of its own and/or other components, + asset scripts are producing dynamic content assets for the later report, + like plots, images, tables and additional data (e.g. json) used for rendering the templates. + + Those assets are then registered with metadata, so that they can be searched/included by the templates. + + Each asset script may produce any amount of assets. + +#. **Project Build** + + Translates an existing Pharaoh project to a configured target format (e.g. HTML, Confluence, LaTeX), + while performing an additional templating step via |Jinja|. + + This is called **second-level templating** or **build-time templating** further on in the documentation. + + Templates may inherit and extend existing or user-defined base-templates (for re-use purposes). + + Templates may dynamically include other templates. + + Templates are rendered using a rendering context (variables that can be used in template), + that contains following data: + + - Project Settings + - Static/manually entered context data + - Dynamic context data, that is created by executing a Python script (potentially accessing generated assets) + + .. image:: _static/build_flow.svg diff --git a/_sources/plugins/index.rst.txt b/_sources/plugins/index.rst.txt new file mode 100644 index 0000000..506b667 --- /dev/null +++ b/_sources/plugins/index.rst.txt @@ -0,0 +1,10 @@ +======= +Plugins +======= + +.. seealso:: :ref:`plugins/plugin:Plugin Architecture` + +.. toctree:: + :maxdepth: 3 + + plugin diff --git a/_sources/plugins/plugin.rst.txt b/_sources/plugins/plugin.rst.txt new file mode 100644 index 0000000..c8a24a3 --- /dev/null +++ b/_sources/plugins/plugin.rst.txt @@ -0,0 +1,5 @@ +Plugin Architecture +=================== + + +.. image:: /_static/chirping_crickets.gif diff --git a/_sources/reference/api.rst.txt b/_sources/reference/api.rst.txt new file mode 100644 index 0000000..d7a4a20 --- /dev/null +++ b/_sources/reference/api.rst.txt @@ -0,0 +1,138 @@ +============= +API Reference +============= + + +Pharaoh API Module +================== + +Lists all items that can be imported from ``pharaoh.api``. + +.. automodule:: pharaoh.api + :members: get_project + +.. autoclass:: pharaoh.project.PharaohProject + :members: + :undoc-members: + :special-members: __init__ + + +Asset Generation API Module +=========================== + +Lists all items that can be imported from ``pharaoh.assetlib.api``. + +.. automodule:: pharaoh.assetlib.api + :members: register_asset,metadata_context,get_resource,find_components, + get_current_component,get_asset_finder,register_templating_context + +.. autoclass:: pharaoh.assetlib.finder.AssetFinder + :members: + :special-members: __init__ + +.. autoclass:: pharaoh.assetlib.finder.Asset + +.. autofunction:: pharaoh.assetlib.finder.asset_groupby + + +Resources +========= + +.. jinja:: default + + {% for resource in resources.values() %} + .. autoclass:: {{ resource.__module__ }}.{{ resource.__name__ }} + :show-inheritance: + :members: + + {% endfor %} + +**Bases:** + + .. autoclass:: pharaoh.assetlib.resource.Resource + :show-inheritance: + :members: + + .. autoclass:: pharaoh.assetlib.resource.LocalResource + :show-inheritance: + :members: + + .. autoclass:: pharaoh.assetlib.resource.TransformedResource + :show-inheritance: + :members: + + + +Matlab Integration API +====================== + +.. autoclass:: pharaoh.assetlib.api.Matlab + :members: + :special-members: __init__,__enter__,__exit__ + + + +Templating Builtins +=================== + +Following globals, filters and tests are available during build-time templating. + +Jinja2 Builtins +--------------- + +All Jinja2 builtins are available of course: + +- `List of builtin filters `_ +- `List of builtin tests `_ +- `List of global functions `_ + +Pharaoh Extension +----------------- + +Pharaoh extends the template environment by additional globals and filters, which are described in following chapters. + +Globals ++++++++ + +Following functions are available in the global templating scope, and can be used like variable, but all callable, e.g. +``{{ h1("Title") }}``. + +.. jinja:: default + + {% for func in env_globals.to_document.values()|set %} + .. autofunction:: {{ func.__module__ }}.{{ func.__name__ }} + {% endfor %} + +Filters ++++++++ + +Variables can be modified by **filters**. Filters are separated from the variable by a pipe symbol (``|``) +and may have optional arguments in parentheses. +Multiple filters can be chained. The output of one filter is applied to the next. + +For example, ``{{ name|trim|title }}`` will trim whitespace from the variable *name* and title-case the output. + +Filters that accept arguments have parentheses around the arguments, just like a function call. +For example: ``{{ listx|join(', ') }}`` will join a list with commas. + +.. jinja:: default + + {% for func in env_filters.to_document.values()|set %} + .. autofunction:: {{ func.__module__ }}.{{ func.__name__ }} + {% endfor %} + + +Ansible Filters +--------------- + +Jinja2 Ansible Filters is a port of the ansible filters provided by Ansible's templating engine. + +See `pypi project page `_ for a listing of all filters. + +The filters are useful, but unfortunately not documented. You may refer to the +`docstrings in the source code `_. + + +Plugin API +========== diff --git a/_sources/reference/assets.rst.txt b/_sources/reference/assets.rst.txt new file mode 100644 index 0000000..84293a8 --- /dev/null +++ b/_sources/reference/assets.rst.txt @@ -0,0 +1,677 @@ +Assets +====== + +What's an Asset? +---------------- + +Each Pharaoh components has a directory called ``asset_scripts``, that contain scripts Python scripts or +Jupyter Notebooks, that extract information from resources (local result files like HDF5, databases or other sources) +and transform this information into plot, tables and any other kind of types. + +The generated files are called **Assets** and may be directly referenced/embedded by a template +(like a picture, plot or table) or indirectly referenced by a template to provide context values for template rendering. + +The component's templates then *consume* these assets while Sphinx renders the template to HTML. +*Consume* means that some of the assets are directly included in the template via the +:ref:`Pharaoh asset directive ` and others are read by +:ref:`local context scripts ` to use their content during templating. + + + +Executing Asset Generation +-------------------------- + +Asset generation is executed before the actual Sphinx build via +:func:`generate_assets() `. + +.. automethod:: pharaoh.project.PharaohProject.generate_assets + :noindex: + +Besides calling the API function, also the CLI can be used: + +- From within PyCharm or wherever you have the **venv** activated that has Pharaoh installed, you can use the + :ref:`Pharaoh CLI `:: + + cd + pharaoh generate # for generating assets of all components + pharaoh generate -f "dummy_.*" # for generating assets of all components starting with "dummy_" + pharaoh generate -f dummy_1 -f dummy_2 # only for dummy_1 and dummy_2 + +- Via a terminal (PS or CMD) from within the project directory, where CMD scripts are generated for your convenience:: + + .\pharaoh-generate-assets.cmd # for generating assets of all components + .\pharaoh.cmd generate -f dummy_1 + +.. placeholder line, otherwise PyCharm does not show the next heading outline in the structure viewer :) + +Debugging Asset Scripts +----------------------- + +Asset scripts can be debugged in two different ways: + +- Via Pharaoh Asset Generation + + Generated Pharaoh project ship a debug script ``debug.py``. + Just open your generated project in an IDE with an interpreter/venv that has Pharaoh installed and start a debug + session on ``debug.py``. + + The script should be modified to your current needs, e.g. if you just want to debug asset generation, + comment out the ``proj.build_report()`` line. If you like to debug asset scripts of a single component, + pass the component name like this ``proj.generate_assets(component_filters=("dummy_1",))``. + + .. important:: If the setting ``asset_gen.worker_processes`` is set to a non-zero integer or ``"auto"``, + then asset scripts are executed as child processes. + + Make sure you have the PyCharm debugger option + ``File | Settings | Build, Execution, Deployment | Python Debugger -> + Attach to subprocess automatically while debugging`` enabled. + +- Directly debug your script + + You can execute your asset scripts directly via your IDE of choice. Any functions you import from + :ref:`pharaoh.api ` or + :ref:`pharaoh.assetlib.api ` are built in a way to support + being executed outside of a Pharaoh asset generation. + + The main difference is, that Pharaoh is not monkey-patching any toolkit (Plotly, Bokeh, Pandas) function, + so the functions are behaving according to their official documentation. + So statements like ```fig.write_image("myplot.png")`` are storing the plot in the current working directory + (this is the same directory your asset script is in), instead of ``report-project\.asset_build``. + + .. seealso:: :ref:`reference/assets:Patched Frameworks` + + +Implementing Asset Scripts +-------------------------- + +This section shows how asset scripts are developed. + +For simplicity we will show first how to generate assets without the use of resources. +An example on how to access resources can be found in the :ref:`Resources Reference `. + +Refresher: A components asset scripts are always located in the component's subdirectory ``asset_scripts``. + +The script names do not matter but certain names may be ignored +(see :func:`generate_assets() `). + +Let's assume we want to make a plot as our first asset in a script called ``my_plot.py``:: + + import plotly.express as px + + from pharaoh.assetlib.api import metadata_context + + fig = px.scatter( + data_frame=px.data.iris(), # Famous example IRIS data set + x="sepal_width", + y="sepal_length", + color="species", + symbol="species", + title="IRIS Example", + ) + + with metadata_context(label="my_plot"): + fig.write_html(file="iris_scatter.html") + +Now let's analyse what we got: + +#. ``import plotly.express as px`` + + Import a plotting framework of choice. Some frameworks are better supported by Pharaoh than others, we'll see this + later also in the :ref:`reference/assets:Patched Frameworks` section. + +#. ``from pharaoh.assetlib.api import metadata_context`` + + Any API function you will need can be imported from the ``pharaoh.assetlib.api`` module. + The purpose of ``metadata_context`` is explained later. + +#. ``fig = px.scatter(...)`` + + Create a Plotly figure with some example data. + Your own scripts would actually read the resources specified with the component and create plots, tables etc. + +#. ``with metadata_context(label="my_plot"):`` + + This context manager determines the metadata the asset will be stored with. + This metadata is then used to include assets in a template, in this case this would look like this: + + .. code-block:: none + + .. pharaoh-asset:: + :filter: label == "my_plot" + :template: iframe + :iframe-width: 500px + :iframe-height: 500px + + This so-called `Sphinx directive `_ + is a custom Sphinx directive (:ref:`click here for docs `) + Pharaoh adds via the plugin system of Sphinx. + + ``metadata_context`` is explained in more detail in the :ref:`next section `. + +#. ``fig.write_html(file="iris_scatter.html")`` + + This is where Pharaoh is doing it's *magic*. + + ``plotly.graph_objects.Figure.write_html`` is one of many function which is + `monkey-patched `_ + by Pharaoh. More info about the other patched frameworks you can find :ref:`here `. + + If the asset script is executed via Pharaoh, the patched ``write_html`` function will not store the plot to + ``"iris_scatter.html"`` in the current working directory, but rather in a predefined location for assets inside + your Pharaoh project ``report_project/.asset_build/`` with a unique suffix, for example + ``iris_scatter_9c30799b.html``. + + Next to the actual asset, an **asset-info** file ``iris_scatter_9c30799b.assetinfo`` + is stored that holds the metadata for the asset. + Here is a (shortened) content example: + + .. _example_asset_info: + + .. code-block:: json + + { + "asset": { + "script_name": "my_plot.py", + "script_path": "C:\\...\\project\\report-project\\components\\dummy_1\\asset_scripts\\my_plot.py", + "index": 1, + "component_name": "dummy_1", + "user_filepath": "iris_scatter.html", + "file": "C:\\...\\project\\report-project\\.asset_build\\dummy_1\\iris_scatter_9c30799b.html", + "name": "iris_scatter_9c30799b.html", + "stem": "iris_scatter_9c30799b", + "suffix": ".html", + "template": "raw_html" + }, + "label": "my_plot" + } + + This has following advantages: + + - Finding names for assets is completely unnecessary, since they get a unique name assigned and are included in + templating through their metadata rather than their file name. + You could basically name all your assets ``asset.``. + + So for example if you would create a single plot of a signal for 100 operating conditions like Vdd, + you would not add the value of Vdd to the file name, but rather put it in the metadata + (see :ref:`here `) and name the file always ``".png"``. + + .. note:: Though the assets name does not matter, its suffix does. + It is used internally so make sure you add it. + + - If the script is directly executed by the user, ``write_html`` is not patched and saves the plot to + ``"iris_scatter.html"`` in the current working directory. + +.. seealso:: :ref:`Asset Script Examples ` + +Metadata Stack +++++++++++++++ + +The metadata stack is a construct that helps assigning metadata to generated assets. + +Use the context manager returned by +:func:`metadata_context() ` in your asset scripts +to dynamically add/remove/update metadata that is added to your assets. + +Let's explained based on an example:: + + from pharaoh.assetlib.api import metadata_context, register_asset + + # Will have no metadata (except the default ones added by Pharaoh) + register_asset("") + + # Will have metadata: {"foo": "bar"} only + register_asset("", dict(foo="bar")) + + with metadata_context(a=1): + register_asset("") # Will have metadata: {"a": 1} only + + with metadata_context(a=2, b=3): + register_asset("") # Will have metadata: {"a": 2, "b": 3} + + with metadata_context(c=4): + register_asset("") # Will have metadata: {"a": 1, "c": 4} + + for i in range(10): + with metadata_context(e=i): + register_asset("") # Will have metadata: {"a": 1, "e": i} + + +.. note:: If you don't want to use the context manager, because the metadata should be set globally in the script anyway + or you're asset script is a Jupyter Notebook (no context manager over multiple cells possible), + you can also use the :func:`activate()` and :func:`deactivate()` method:: + + metadata_context(...).activate() + register_asset("") + + Or if you want to deactivate the context again:: + + mc = metadata_context(...).activate() + register_asset("") + mc.deactivate() + + +The function :func:`register_asset() ` is explained in the next section. + +Manually Registering Assets ++++++++++++++++++++++++++++ + +The function :func:`register_asset() ` may be used to manually register +assets of any type. + +This is mainly used if you like to create assets that are not generated via +:ref:`patched framework functions `, like txt, json, rst files or +generated via frameworks not yet supported, like *seaborn*. + +Here some examples:: + + register_asset("some-path.html", dict(label="my_html_snippet"), template="raw_html") + + data = b"""

This HTML text was generated by an asset script!

""" + register_asset("raw.html", dict(label="my_html_snippet"), template="raw_html", data=io.BytesIO(data)) + + data = b""".. important:: This reStructuredText text was generated by an asset script!""" + register_asset("raw.rst", dict(label="my_rst_snippet"), template="raw_rst", data=io.BytesIO(data)) + + data = b'This text was generated by an asset script and will be included via "literalinclude"' + register_asset("raw.txt", dict(label="my_txt_snippet"), template="raw_txt", data=io.BytesIO(data)) + + +Patched Frameworks +++++++++++++++++++ + +Pharaoh `monkey-patches `_ +plot/table export functions of popular tools like Pandas, Plotly etc. + +This has the advantage, that users can work with the official plotting APIs of the respective frameworks +without having to take care about :ref:`reference/assets:manually registering assets`. + +The following APIs are patched by Pharaoh: + +Pandas + - `pandas.DataFrame.to_html() `_ + + Automatically sets metadata ``template="datatable"`` (see :ref:`reference/directive:Asset Templates`) . + - but NOT `pandas.io.formats.style.Styler.to_html() + `_ + + Styler instances have to be exported manually:: + + html_file_name = "styled_table.html" + styler.to_html(buf=html_file_name) + register_asset(html_file_name, template="datatable") + + Examples:: + + import pandas as pd + + df = pd.DataFrame( + { + "blabla": ["a", "b", "c"], + "ints": [1, 2, 3], + "float": [1.5, 2.5, 3.5], + "hex": ["0x11", "0x12", "0x13"], + } + ) + df.to_html(buf="table.html") + +Plotly + - `plotly.io.show() `_ + + When running in Pharaoh asset generation, this function is patched to prevent showing the plot in the browser. + - `plotly.io.write_image() `_ + + - `plotly.io.write_html() `_ + + Supports :ref:`reference/assets:Force Static Exports`. + + Examples:: + + import plotly.express as px + + fig = px.scatter( + data_frame=px.data.iris(), x="sepal_width", y="sepal_length", + color="species", symbol="species", title=r"A title", + ) + + fig.write_image(file="iris_scatter1.svg") + fig.write_html(file="iris_scatter2.html") + fig.write_image(file="iris_scatter3.png", width=500, height=500) + +Bokeh + + - `bokeh.io.show() `_ + + When running in Pharaoh asset generation, this function is patched to prevent showing the plot in the browser. + - `bokeh.io.saving.save() `_ + + Supports :ref:`reference/assets:Force Static Exports`. + + - `bokeh.io.export_png() `_ + - `bokeh.io.export.export_png() + `_ + - `bokeh.io.export_svg() `_ + - `bokeh.io.export.export_svg() + `_ + + Examples:: + + from bokeh.io import save + from bokeh.plotting import figure + from bokeh.sampledata.iris import flowers + + colormap = {"setosa": "red", "versicolor": "green", "virginica": "blue"} + colors = [colormap[x] for x in flowers["species"]] + + p = figure(title=f"Iris Morphology", width=400, height=400) + p.xaxis.axis_label = "Petal Length" + p.yaxis.axis_label = "Petal Width" + p.scatter(flowers["petal_length"], flowers["petal_width"], color=colors, fill_alpha=0.2, size=10) + + save(p, filename="iris_scatter.html") + +Matplotlib + + - `matplotlib.pyplot.show() `_ + + When running in Pharaoh asset generation, this function is patched to prevent showing the plot in the browser. + - `matplotlib.figure.Figure.show() + `_ + + When running in Pharaoh asset generation, this function is patched to prevent showing the plot in the browser. + - `matplotlib.figure.Figure.savefig() + `_ + + Examples:: + + import matplotlib.pyplot as plt + import numpy as np + + fig, ax = plt.subplots() + ax.plot(np.arange(0.0, 2.0, 0.01), 1 + np.sin(2 * np.pi * t)) + ax.set(xlabel='time (s)', ylabel='voltage (mV)', title='About as simple as it gets, folks') + ax.grid() + + fig.savefig("test.png") + plt.show() + +Holoviews + - `holoviews.util.save() `_ + with backends Bokeh, Plotly and Matplotlib. + + Supports :ref:`reference/assets:Force Static Exports`. + + Examples:: + + import holoviews as hv + data = [(i, chr(97 + j), i * j) for i in range(5) for j in range(5) if i != j] + + with metadata_context(ext="plotly"): + hv.extension("plotly") + model = hv.HeatMap(data).opts(cmap="RdBu_r", width=400, height=400) + hv.save(model, "heatmap_holo_plotly.html") + hv.save(model, "heatmap_holo_plotly.svg") + hv.save(model, "heatmap_holo_plotly.png") + + with metadata_context(ext="bokeh"): + hv.extension("bokeh") + model = hv.HeatMap(data).opts(cmap="RdBu_r", width=400, height=400) + hv.save(model, "heatmap_holo_bokeh.html") + hv.save(model, "heatmap_holo_bokeh.png") + + with metadata_context(ext="matplotlib"): + hv.extension("matplotlib") + model = hv.HeatMap(data).opts(cmap="RdBu_r") + hv.save(model, "heatmap_holo_mpl.svg") + hv.save(model, "heatmap_holo_mpl.png") + + +``plotly.graph_objects.Figure.write_html`` is one of many function which is + +by Pharaoh. More info about the other patched frameworks you can find :ref:`here `. + +If the asset script is executed via Pharaoh, the patched ``write_html`` function will not store the plot to +``"iris_scatter.html"`` in the current working directory, but rather in a predefined location for assets inside +your Pharaoh project ``report_project/.asset_build/`` with a unique suffix, for example +``iris_scatter_9c30799b.html``. + + +Force Static Exports +++++++++++++++++++++ + +While for debugging and interactive review it's nice to have all plots rendered as dynamic elements in HTML, +other build targets might be used for export purposes like Confluence or LaTeX. + +Since those targets don't support embedding dynamic HTML elements you would have to tweak your asset generation scripts +to export different formats for different report output formats, which is suboptimal. + +Pharaoh therefore provides a setting ``asset_gen.force_static`` which can be used to signal asset scripts +to export static assets instead of HTML. + +For all patched plotting APIs, if ``asset_gen.force_static`` is set to ``true``, +Pharaoh takes care and exports static images instead of HTML when plotting APIs like +`plotly.io.write_html() `_ is used. + +Since not all frameworks are patched, you might have to add support by yourself inside asset scripts like this:: + + import altair as alt # altair not supported at the moment + + from pharaoh.api import get_project + from pharaoh.assetlib.api import register_asset + + pharaoh_project = get_project(__file__) + force_static = project.get_setting("asset_gen.force_static") + + chart = alt.Chart(...) + + if force_static: + filename = "chart.png" + else: + filename = "chart.html" + + chart.save(filename) + register_asset(filename, dict(plotting_framework="altair")) + + +.. seealso:: :ref:`reference/settings:Accessing Settings` + + +Matlab Integration +++++++++++++++++++ + +Pharaoh supports generating assets via Matlab scripts/function through the +Matlab API :class:`pharaoh.assetlib.api.Matlab`. + +The asset scripts still have to be Python scripts and any asset the Matlab scripts generate, have to be +manually registered using :func:`register_asset() `. + +Here an example:: + + from pharaoh.assetlib.api import Matlab, register_asset, FileResource, get_resource + + resource: FileResource = get_resource(alias="") + some_resource_path: Path = resource.locate() + + with Matlab() as matlab: + plot_path, out, err = matlab.execute_function("generate_plot", [str(some_resource_path)], nargout=1) + + register_asset(plot_path, dict(from_matlab=True, foo="bar")) + +To use the Matlab API you have to install an additional dependency, depending on your Matlab version: + + - R2020B: ``pip install matlabengine==9.9.*`` + - R2021A: ``pip install matlabengine==9.10.*`` + - R2021B: ``pip install matlabengine==9.11.*`` + - R2022A: ``pip install matlabengine==9.12.*`` + - R2022B: ``pip install matlabengine==9.13.*`` + - R2023A: ``pip install matlabengine==9.14.*`` + - R2023B: ``pip install matlabengine==9.15.*`` + + +Asset Lookup +------------ + +This section deals with how to access assets in :ref:`local context scripts +` and :ref:`build-time templates `. + +Asset Finder +++++++++++++ + +The :class:`AssetFinder ` is responsible for discovering and searching assets +based on filters using :func:`AssetFinder.search_assets() `. + +This function is available in various places: + +- In :ref:`local context scripts `. + + The following script searches all JSON files that have ``"context_name"`` set, + loads them and exports their content as context for :ref:`build-time templating `:: + + import json + from pharaoh.assetlib.api import get_asset_finder, get_current_component + + finder = get_asset_finder() + component_name = get_current_component() + + context = { + asset.context.context_name: json.loads(asset.read_text()) + for asset in finder.search_assets( + 'asset.suffix == ".json" and "context_name" in asset.context', + [component_name] + ) + } + context["component_name"] = component_name + return context + + In some use cases this is used to make measurement information available during templating + to render the information in a table. + +- In templates inside `Jinja statements + `_. + + The following snippet searches all image assets of the **current** component + (the component the template is rendered in) and stores the list of assets in a variable:: + + {% set image_assets = search_assets("asset.suffix in ['.png', '.svg']") %} + + The following snippet searches all image assets of the **all** components in the project. + This can be used to create components that summarize information from other components. + + :: + + {% set all_image_assets = search_assets_global("asset.suffix in ['.png', '.svg']") %} + + The used functions ``search_assets`` and ``search_assets_global`` are + `partial functions `_ where the ``component`` argument is preset. + + + +Manual Include +++++++++++++++ + +If you like to include some of your assets manually, the template environment provides +two functions for your convenience, that return the (relative) path of an asset to the including template: + +``asset_rel_path_from_project(asset)`` + Returns the relative path from the Sphinx source directory to the passed asset. + This path is needed for Sphinx directives that themselves take care to copy the asset into the build directory, + like directives ``literalinclude``, ``image`` and ``figure``. + + Here's an example: + + .. code-block:: none + + {% set matches = search_assets("") %} + + .. figure:: {{ asset_rel_path_from_project(matches[0]) }} + :scale: 50 % + + This is the caption of the figure (a simple paragraph). + + where ``{{ asset_rel_path_from_project(matches[0]) }}`` would render something like this: + ``/.asset_build/dummy/mytxt_f65223c4.txt`` + + +``asset_rel_path_from_build(asset)`` + Returns the relative path from the Sphinx build directory to the passed asset. + This path is needed for Sphinx directives that themselves do **NOT** take care to copy the asset into the + build directory, like when using the ``raw_html`` directive and including a picture or HTMl file using an iframe. + + In this case calling ``asset_rel_path_from_build`` will automatically copy the asset from the asset build folder + to the build directory and return it's relative path from the including template. + + Here's an example: + + .. code-block:: none + + {% set matches = search_assets("") %} + + .. raw:: html + +
+ + where ``{{ asset_rel_path_from_build(matches[0]) }}`` would render something like this: + ``../../pharaoh_assets/iris_scatter_3e7d7ab7.html`` + +.. note:: Another way to include assets with a custom template is to use the ``template`` + :ref:`option ` of the + :ref:`Pharaoh asset directive ` with a path to your custom template. + + +Grouping Assets By Metadata ++++++++++++++++++++++++++++ + +Imaging in your asset scripts you are creating a plot for a measured signal for many different operating conditions: + +.. code-block:: none + + from pharaoh.assetlib.api import metadata_context + + for vdd in (8.0, 10.0, 12.0): + for iout in (1.0, 2.0, 5.0): + with metadata_context(vdd=vdd, iout=iout, signal_name="idd"): + fig = ... + fig.write_image(file="idd_plot.png") + + +Now in your report you may like to have all those plots added separately each with it's own title containing the +values of either ``vdd`` or ``iout``. + +Defining the template like this is not really viable, since in the template you usually have no idea about what values +were iterated over in the asset script. But let's assume you know, then this would be the template: + +.. code-block:: none + + {% for vdd in (8.0, 10.0, 12.0) %} + {{ heading("Plots for Vdd:%.1fV, Iout:%.1fA" % vout, 2 }} + + {% for iout in (1.0, 2.0, 5.0) %} + {{ heading("Plots for Iout:%.1fA" % iout, 3 }} + + .. pharaoh-asset:: vdd == {{ vdd }} and iout == {{ iout }} and signal_name == 'idd' + + {% endfor %} + {% endfor %} + +But luckily there is a better option using :func:`asset_groupby ` to +group assets first by ``vdd`` and then by ``iout``: + +.. code-block:: none + + {% set idd_plots = search_assets("signal_name == 'idd'") %} + {% for vdd, assets_grby_vdd in agroupby(idd_plots, key="vdd").items() %} + {{ heading("Plots for Vdd:%.1fV" % vout, 2 }} + + {% for iout, assets_grby_iout in agroupby(assets_grby_vdd, key="iout").items() %} + {{ heading("Plots for Iout:%.1fA" % iout, 3 }} + + {% for asset in assets_grby_iout %} + .. pharaoh-asset:: {{ asset.id }} + + {% endfor %} + {% endfor %} + {% endfor %} + +.. note:: ``agroupby`` is an alias for ``asset_groupby``. Both are available as global function during templating. diff --git a/_sources/reference/building.rst.txt b/_sources/reference/building.rst.txt new file mode 100644 index 0000000..623576f --- /dev/null +++ b/_sources/reference/building.rst.txt @@ -0,0 +1,78 @@ + +Building +======== + +Configuration +------------- + +Following steps have to be done for configuring a Sphinx build: + +#. Choose a Sphinx build target via the ``report.target`` setting. + Currently supported are: + + - **html** and all other `Sphinx default builders + `_ like latex. + - `confluence, singleconfluence `_. + + .. note:: For other builders than ``html`` and ``*confluence``, you might need to install additional dependencies. + +#. Configure the selected builder + + - Via :ref:`settings ` sections like ``report.html`` and ``report.confluence`` + - Via the `Sphinx configuration `_ + ``report_project/conf.py``. + + This file loads a default Sphinx configuration variables (also partially from :ref:`settings + `) and assigns them to the local namespace. + + To modify just overwrite or alter them:: + + # The list "html_css_files" is already defined from the default config, + # but this is not the case for all variables. + # To be sure inspect the local namespace via the debugger or print it + # like this ``print(", ".join(locals().keys()))`` + html_css_files.append("css/mystyle.css") + + .. note:: The HTML builder already contains a reasonable default config, so no change needed in most cases. + + +Build API +--------- + +A Pharaoh report may be built using the method :func:`PharaohProject.build_report() +`. + +.. important:: + If your templates are accessing assets, make sure you call :func:`PharaohProject.generate_assets() + ` everytime your asset scripts or resources changed. + +You can call the API within a Python script, as for example in the included ``report-project/debug.py`` script. + + .. code-block:: + + from pharaoh.api import PharaohProject + + if __name__ == "__main__": # This guard is needed because Pharaoh is using multiprocessing + proj = PharaohProject(project_root="..") + proj.generate_assets() + proj.build_report() + +Or you just double-click the CLI scripts ``report-project/pharaoh-generate-assets.cmd`` and +``report-project/pharaoh-build.cmd``. + +Or with the Pharaoh venv activated, inside the project directory use the CLI directly: ``pharaoh build`` + +Log output should be displayed in the console, but are also written to ``log.txt`` and ``log_warnings.txt`` +(only warnings or errors). + + +Archiving +--------- + +After building a report, it may be archived using :func:`PharaohProject.archive_report() +`. + +.. automethod:: pharaoh.project.PharaohProject.archive_report + :noindex: + +Alternatively just double-click the CLI script ``report-project/pharaoh-archive.cmd``. diff --git a/_sources/reference/cli.rst.txt b/_sources/reference/cli.rst.txt new file mode 100644 index 0000000..e80f6e8 --- /dev/null +++ b/_sources/reference/cli.rst.txt @@ -0,0 +1,83 @@ +CLI +=== + +Preface +------- + + .. literalinclude:: /cli/default.txt + :language: none + +The CLI may be used in different ways: + +- Through the PIP entrypoint ``pharaoh`` + + When you install Pharaoh via PIP, it will install a ``pharaoh.exe`` + into your environment (``/Scripts``). + + Once your environment is activated you can use the CLI via the ``pharaoh`` command. + Some examples using CMD: + + .. code-block:: none + + cd "C:/projects/my_pharaoh_report" + + set PHARAOH.REPORT.TITLE="Some Title" + + pharaoh new + pharaoh add --name dummy1 -t pharaoh_testing.simple --context "{'test_name':'dummy1'}" + pharaoh add --name dummy2 -t pharaoh_testing.simple --context "{'test_name':'dummy2'}" + pharaoh generate + pharaoh build + + If you current working directory is not inside the Pharaoh project, add the absolute or relative (to CWD) path + to the project as first argument like this: + + .. code-block:: none + + cd "C:/projects" + + pharaoh -p "my_pharaoh_report" new + pharaoh -p "my_pharaoh_report" generate + cd "my_pharaoh_report" + pharaoh build + + +- Through the generated CMD scripts ``report-project/*.cmd`` + + The advantage of those scripts is, that they are working regardless of your Pharaoh Python environment being + activated or not, since they contain the absolute path to the Python interpreter used to generate them as a + fallback . + + Thus they are checking if ``pharaoh.exe`` is on the system path (e.g. when executing inside an + activated virtual env - like the terminal in PyCharm), but falling back to the absolute path. + + .. important:: If you rename or move the Python environment that was used to generate the Pharaoh project, + those CMD script won't work anymore (unless ``pharaoh.exe`` is on the system path). + + So you can just double click ``pharaoh-*.cmd`` to perform the corresponding action with default arguments. + + .. note:: + This way is just used to managed an already existing Pharaoh project, so commands like + ``pharaoh.cmd new`` does not really make sense. + + If you like to use them via the terminal, the syntax is similar to the above examples: + + - ``pharaoh.cmd generate`` or ``pharaoh-generate-assets.cmd`` + - ``pharaoh.cmd build`` or ``pharaoh-build.cmd`` + - ``pharaoh.cmd archive`` or ``pharaoh-archive.cmd`` + + + +Commands +-------- + +.. jinja:: default + + {% for command in cli_commands %} + {{ command|capitalize }} + +++++++++++++++++++++++++++++++++++++++++++++++ + + .. literalinclude:: /cli/{{ command }}.txt + :language: none + + {% endfor %} diff --git a/_sources/reference/components.rst.txt b/_sources/reference/components.rst.txt new file mode 100644 index 0000000..789e1fc --- /dev/null +++ b/_sources/reference/components.rst.txt @@ -0,0 +1,141 @@ +Components +========== + +What's a Component? +------------------- + +A Pharaoh project consists of any amount of components. + +The file ``report_project/index.rst`` determines how the components are included in the report. +Per default all components are added in sequence on the same level but this could be changed in the template. + +A component has following traits: + + Name + The name of the component; used for identification purposes. + + :ref:`reference/components:Templates` + A list of templates used to generate the component's files. + + Render Context + An arbitrarily-nested dictionary that is available during generation-time templating. + This context enables the re-use of the templates for different purposes. + + :ref:`reference/components:Resources` + A list of resource definitions that let asset scripts easily access external data by using an alias instead of + absolute resource specifiers (like a file path). + + Metadata + This metadata dictionary can be used to :ref:`lookup components ` + by their metadata. + + A potential use case is grouping components by their metadata and create a customized report layout + using the ``report_project/index.rst`` template. + +Resources +--------- + +Resources let asset scripts easily access external data by using an alias instead of absolute resource specifiers +(like a file path or an RDDL artifact id). + +The currently available resource types: + +.. jinja:: default + + {% for name, resource in resources.items() %} + - :class:`{{ name }} <{{ resource.__module__ }}.{{ resource.__name__ }}>` + + {% endfor %} + + + +Accessing resources in asset scripts can be done like this:: + + from pathlib import Path + from pharaoh.assetlib.api import get_resource, FileResource + + resource: FileResource = get_resource(alias="") + first_matching_file_path: Path = resource.locate() + ... + + +To get a resource from a component or update a component's resource, use theses functions: + + - :func:`PharaohProject.get_resource() ` + - :func:`PharaohProject.update_resource() ` + + +Templates +--------- + +Component templates are used to create the main content for components +(see :ref:`reference/components:Adding Components`) and can come in different forms: + +- **Template Directory** + + The simplest form of a template. A directory with some files. + + If |assets| shall be generated by the component, + the directory must contain an ``asset_scripts`` subdirectory containing Python scripts. + +- **Registered Templates** + + Pharaoh's :ref:`plugins/plugin:Plugin Architecture` allows the development of plugins that register templates. + + That means plugins may come with their own template directories and register their paths in Pharaoh, + so these templates are then selectable using a template identifier, + like ``my_awesome_plugin.template1``. + + An advantage of plugin templates is, that for those templates, dependencies to other templates can be specified. + For example the template ``my_awesome_plugin.template1`` may requires that the ``my_awesome_plugin.template2`` + template is used at least once in any component, otherwise cross-references won't work. + +- :ref:`Template Files/Single-file templates ` + + The smallest and most compact form of a template. + + Single-file templates are Python files with suffix ``.pharaoh.py`` whose Python code creates |assets| and + whose module-level docstring represents the rST content. + + This file will be internally converted to a template directory:: + + my_template.pharaoh.py -> index_my_template.rst + asset_scripts/my_template.py + + +Managing Components +------------------- + +Adding Components ++++++++++++++++++ + +Adding a component to a Pharaoh project can be done via the API function +:func:`PharaohProject.add_component() ` or the CLI +command :ref:`reference/cli:Add`: + +.. automethod:: pharaoh.project.PharaohProject.add_component + :noindex: + + +Updating Components ++++++++++++++++++++ + +Once a component is generated, the options to modify it are limited to: + + - :func:`PharaohProject.add_template_to_component() ` + - :func:`PharaohProject.update_resource() ` + +Removing Components ++++++++++++++++++++ + +A generated component can be removed via +:func:`PharaohProject.remove_component() `, +which results in deletion of ``report-project/components/`` and its entry in ``pharaoh.yaml``: + + +Finding Components +++++++++++++++++++ + +Components can be looked up via their metadata using the function +:func:`PharaohProject.get_component_names_by_metadata() +`. diff --git a/_sources/reference/directive.rst.txt b/_sources/reference/directive.rst.txt new file mode 100644 index 0000000..3beb4ca --- /dev/null +++ b/_sources/reference/directive.rst.txt @@ -0,0 +1,222 @@ + +Pharaoh Directive +================= + + +A `Sphinx Directive `_ +is a generic block of explicit markup. +Along with roles, directives are one of the extension mechanism, a way of adding support for new constructs +without adding new primary syntax (directives may support additional syntax locally). +And Sphinx makes heavy use of it. + +All `standard directives `_ are always available, +whereas any other directives are domain-specific +(see `Writing Sphinx Extensions `_) +and may require special action to make them available when processing the document, +like the **Pharaoh Asset** directive. + +The **Pharaoh Asset** directive ``pharaoh-asset`` is added via the Pharaoh Sphinx extension and allows users +to include assets in the document, based on one or multiple filter conditions. + +The matched assets are then rendered using :ref:`predefined templates ` +that can be specified in the asset metadata. + +The ``pharaoh-asset`` directive consists of a name, arguments, options and content, e.g: + + .. code-block:: none + + .. pharaoh-asset:: key1 == "A" or + key2 == "B" + :filter: key3 == "C" + :index: 0 + + key4 == "D"; + key5 == "E" + + - ``pharaoh-asset`` is the name + - ``key1 == "A" or key2 == "B"`` is an argument + - ``:filter: key3 == "C"`` and ``:index: 0`` are options + - ``key4 == "D";key5 == "E"`` is the content + + .. note:: + + The directive supports 3 ways to define an asset filter, all of them can be used in parallel and are **AND**-ed: + the argument, the ``:filter:`` option and the content. + + The filters are treated as multiline statements and are split only on unescaped ``;`` characters. + This means ``key4 == "D";key5 == "E"`` is effectively the same as ``key4 == "D" and key5 == "E"`` + + The directive internally uses the :func:`AssetFinder.search_assets() + ` function, so the same rules apply to the filter strings. + +If your filter matches multiple assets, all matches will be rendered one after another. +This can look quite messy very fast, so there is another option to add some more information to each rendered asset +by the combined use with the :func:`AssetFinder.search_assets() +` function, +where the directive simply gets the ID of the asset to include. + + Following template example will search for all images. If at least one exists a new section will be created and the + images are added, each under its own `rubric `_: + + .. code-block:: none + + {% set image_assets = search_assets("asset.suffix in ['.png', '.svg']") %} + + {% if image_assets|length %} + {{ h2("Images") }} + + {% for asset in image_assets %} + .. rubric:: {{ asset.context.caption }} + + .. pharaoh-asset:: {{ asset.id }} + + {% endfor %} + {% endif %} + + .. important:: In this case the asset's ID must be the one-and-only filter that is given! + + +Directive Options +----------------- + +Following options may be used for the ``pharaoh-asset`` directive: + +``:filter:`` + The filter (multiline string, ``;``-separated) to select assets by metadata. See example above. + +``:optional:`` + If false (default), the asset filter MUST match any asset, otherwise an exception is raised. + + Valid values: ``true/yes/1``, ``false/no/0`` + +``:index:`` + If the filters match multiple assets, only render the assets with specified index (0-based). + + Following style is possible: ``1,2,4-8,9`` (like selecting pages to print). Whitespace is ignored. + +``:components:`` + A comma-separated string of component names to look for assets (see :func:`AssetFinder.search_assets() + `). + + Special values: + + - ``_this_`` (default): Searches only the current component the template is rendered in + - ``_all_``: Searches all components + +``:template:`` + The template to use to embed the asset into the document. + It may be a template name as described in :ref:`reference/directive:Asset Templates` or + a file path to your own template. Please note that relative paths are resolved with the Pharaoh project directory + as root directory). + + If not specified as option to the directive, the asset metadata ``asset.template`` is looked up + (which maybe gets a default template automatically determined by the asset's file suffix). + + If the template is set neither via option nor asset metadata, an error will be raised. + + .. note:: Another way to include assets manually is described :ref:`here `. + + +``:image-(alt,height,width,scale,align):`` + Passed to the ``image`` directive, if the asset is rendered as image. + +``:iframe-width:`` + The width of the HTML `iframe `_. + + The default value is determined by setting ``asset_gen.default_iframe_width``. + +``:iframe-height:`` + The height of the HTML `iframe `_. + + The default value is determined by setting ``asset_gen.default_iframe_height``. + +``:ignore-title:`` + If asset metadata ``asset.title`` is set, the asset will be rendered under a unique + `rubric `_. + The rubric title is a unique cross-reference. + + If this option is true, this behavior is skipped. + + Valid values: ``true/yes/1``, ``false/no/0`` + +``:ignore-description:`` + If asset metadata ``asset.description`` is set, the description will be inserted directly before the asset + as raw string (so you could add reST as well). + + If this option is true, this behavior is skipped. + + +``:datatable-extended-search:`` + If true, assets rendered as interactive `datatable `_ will include an + `interactive search builder `_ + + The default value is determined by setting ``asset_gen.default_datatable_extended_search``. + + Valid values: ``false/no/0``, ``true/yes/1`` + + + +Asset Templates +--------------- + +This section describes the templates used to include assets into your documents. + +There are 2 options how to specify the used template: + + - Via the :func:`register_asset() ` function:: + + data = b'This text was generated by an asset script and will be included via "literalinclude"' + register_asset("raw.txt", dict(label="my_txt_snippet"), template="raw_txt", data=io.BytesIO(data)) + + As fallback, if ``template`` is not specified, a default template is chosen depending on the asset's file suffix: + + - ``".html"``: **iframe** + - ``".rst"``: **raw_rst** + - ``".txt"``: **raw_txt** + - ``".svg"``: **image** + - ``".png"``: **image** + - ``".jpg"``: **image** + - ``".jpeg"``: **image** + - ``".gif"``: **image** + - ``".md"``: **markdown** + + - Via the ``:template:`` option of the Pharaoh asset directive (has priority over setting in metadata). + +Following asset templates are currently supported: + + image + The asset file will be included via the `image `_ directive. Options on the ``pharaoh-asset`` directive starting with ``image-`` are + passed to the image directive. + + raw_html + The asset file will be included via the `raw:: html `_ directive. + The only modification is that the HTML content is pasted into a separate ``div``-tag with a unique, + autogenerated ID and a linebreak at the end. + + markdown + The asset file will be included via the `raw:: html `_ directive after converting the Markdown text to HTML. + + raw_rst + The content of the asset file will be pasted into the document without modification and no extra indentation. + + raw_txt + The asset file will be included via the `literalinclude `_ directive. Language highlighting is disabled. + + iframe + The asset file will be included via the `raw `_ directive, wrapped in an ``iframe``-tag. + Options on the ``pharaoh-asset`` directive starting with ``iframe-`` are added to the iframe options. + The iframe will be lazy-loaded by the browser. + + datatable + This template can only be used in combination with HTML tables produced by + `pandas.DataFrame.to_html() `_ or + `pandas.io.formats.style.Styler.to_html() `_. + + It transforms those tables into interactive JS-powered tables (see `DataTables `_). diff --git a/_sources/reference/index.rst.txt b/_sources/reference/index.rst.txt new file mode 100644 index 0000000..0aa7724 --- /dev/null +++ b/_sources/reference/index.rst.txt @@ -0,0 +1,16 @@ +========= +Reference +========= + +.. toctree:: + :maxdepth: 3 + + project_structure + settings + components + assets + directive + building + templating + API Reference + CLI Reference diff --git a/_sources/reference/project_structure.rst.txt b/_sources/reference/project_structure.rst.txt new file mode 100644 index 0000000..4fac88a --- /dev/null +++ b/_sources/reference/project_structure.rst.txt @@ -0,0 +1,51 @@ +Project Structure +================= + +A generated and built Pharaoh project has following directory structure: + +.. code-block:: none + + 📁 + ├── 📄 pharaoh.yaml + │ Contains all settings and component definitions. + ├── 📄 log.txt + │ Build logs for all logging levels. + ├── 📄 log_warnings.txt + │ Build logs for logging levels WARNING and higher. + ├── 📄 pharaoh_report_.zip + │ A ZIP archive of the built report. + ├── 📄 .gitignore + │ A file for GIT to ignore all transient files. + ├── 📁 report-build + │ Contains the build output, e.g. HTML pages or LaTeX input files. + │ For HTML output the main page is called index.html. + └── 📁 report-project + ├── 📁 .resource_cache + │ Contains temporary/cached resources + ├── 📁 _static + │ Contains the CSS overrides and the HTML logo for the report. + ├── 📁 _templates + │ Contains HTML templates that override the defaults of the selected Sphinx theme. + │ In the case layout.html and footer.html. Only change if you really need to. + ├── 📁 .asset_build + │ All registered assets from asset generation will be stored here for each component separately. + ├── 📁 components + │ This is where components get created when added via the project API. + ├── 📁 user_templates + │ Stores user-defined templates for the build-time templating step. + ├── 📄 conf.py + │ The Sphinx configuration file (https://www.sphinx-doc.org/en/master/usage/configuration.html) + │ It loads a default configuration from the Pharaoh project into local variables. + │ Used to overwrite or add additional configuration. + ├── 📄 debug.py + │ For debugging the asset/report generation. Modify for your needs. + │ Per default it executes asset generation and report build. + ├── 📄 index.rst + │ The default template to control how the title page looks like. + │ It includes all components into the TOC (table of content) tree. + ├── 📄 index.rst.rendered + │ *.rendered files are generated during build-time templating step and allow to see the + │ actual rST content Sphinx is using for generating the report. + │ This helps for debugging your templates since you see the result after the templating step. + └── 📄 \*.cmd + Scripts to execute asset generation, report build or upload to R&D Datalake with a single double-click. diff --git a/_sources/reference/settings.rst.txt b/_sources/reference/settings.rst.txt new file mode 100644 index 0000000..4bc4789 --- /dev/null +++ b/_sources/reference/settings.rst.txt @@ -0,0 +1,133 @@ +Settings +======== + +The Pharaoh settings file ``pharaoh.yaml`` is used for three main purposes: + +- Configuration of Pharaoh functionality like asset generation, report build, archiving, RDDL upload, notifications... +- Hold info about all added components +- User-defined settings (accessible from templates) + +The settings file is loaded using the `OmegaConf library `_. + + *OmegaConf is a YAML based hierarchical configuration system, with support for merging configurations* + *from multiple sources (files, CLI argument, environment variables) providing a consistent API regardless* + *of how the configuration was created. OmegaConf also offers runtime type safety via Structured Configs.* + + The library offers lots of features like + `Variable interpolation `_ and + `Resolvers `_. + So if you want to get the best out of your configuration file, have a look in the OmegaConf documentation on the + mentioned topics above. + + +Default Settings +---------------- + +A Pharaoh project is generated with following default settings, if not specified otherwise: + +.. literalinclude:: ../../src/pharaoh/plugins/core_plugin/default_settings.yaml + :language: yaml + + +Custom Settings +--------------- + +Since default settings are very likely to not fit everyone's needs, they can be modified by the user in several ways: + +.. important:: Make sure all keys in your settings are lowercase, otherwise putting/getting settings might + not work as expected since settings are all treated lowercase internally! + +- Pass a custom settings file on project creation via the keyword argument ``custom_settings``. + The file is merged with the default settings, so it's enough to overwrite only the divergent settings:: + + from pharaoh.api import PharaohProject + + proj = PharaohProject(project_root="some-path", custom_settings="mysettings.yaml") + + .. note:: If ``custom_settings`` is a relative path, then the current working directory is used as anchor + +- Put settings via the settings API + :func:`put_setting(key: str, value: Any) `:: + + from pharaoh.api import PharaohProject + + proj = PharaohProject(project_root="some-path") + proj.put_setting("report.title", "My own title") + proj.put_setting("toolkits.bokeh.export_png", dict(width=720, height=480)) + proj.save_settings() # Optional + + .. important:: Defining settings like this only changes the settings for the project instance in memory but does + not persist them to ``pharaoh.yaml``. + + To do so call :func:`save_settings() ` manually if + you don't plan to modify components/resources right after (those functions do an auto-safe). + +- Put settings via environment variables + + Same example as with settings API, but with environment variables:: + + import os + from pharaoh.api import PharaohProject + + # Env variables have to be prefixed with "pharaoh." and are always treated lowercase + os.environ["pharaoh.report.title"] = "My own title" + os.environ["pharaoh.toolkits.bokeh.export_png"] = "{'width': 720, 'height': 480}" + + proj = PharaohProject(project_root="some-path") + proj.save_settings(include_env=True) + + .. note:: Double-underscore ``__`` also acts as a valid separator for keys, since some shell environments don't + allow dots inside variable names. E.g. ``PHARAOH__a.B__c___d`` will resolve to ``pharaoh.a.b.c___d``. + + .. important:: Environment variables are only persisted to ``pharaoh.yaml`` when saving like this: + :func:`save_settings(include_env=True) ` + + .. important:: If Pharaoh environment variables are changed while the project is already instantiated, + you can reload settings using + :func:`load_settings(namespace="env") `:: + + import os + from pharaoh.api import PharaohProject + + proj = PharaohProject(project_root="some-path") + os.environ["pharaoh.report.title"] = "My Title" + proj.load_settings(namespace="env") + proj.save_settings(include_env=True) + +Accessing Settings +------------------ + +Setting values can be accessed in various places throughout the Pharaoh project using +:func:`PharaohProject.get_setting() `: + +- in Python scripts, e.g. |asset|- or local context-scripts:: + + from pharaoh.api import get_project + pharaoh_project = get_project(__file__) # Get a project instance location of this file in project directory + title = project.get_setting("report.title") + +- in templates:: + + {{ heading(get_setting("report.title"), 1) }} + {% set static_export = get_setting("asset_gen.force_static", False) -%} + + +Custom Resolvers +---------------- + +`OmegaConf `_ offers already some +`builtin resolvers `_ +like ``oc.env``: :code:`author: "${oc.env:USERNAME,unknown}"` +but Pharaoh adds additional ones for your convenience: + +- ``utcnow.strf``: Formats UTC time. + + Usage: :code:`archive_name: "pharaoh_report_${utcnow.strf:%Y%m%d_%H%M%S}.zip"` + +- ``now.strf``: Same as above, but with timestamp of local timezone. + +- ``pharaoh.project_dir``: Returns the Pharaoh project directory with ``/`` as path separator. + + Usage: :code:`key: "${pharaoh.project_dir:}/somepath"` (note the trailing ``:``, without, it would be a reference). + +If you need additional resolvers or wish to implement your own, please get in contact with us. diff --git a/_sources/reference/templating.rst.txt b/_sources/reference/templating.rst.txt new file mode 100644 index 0000000..f032ba1 --- /dev/null +++ b/_sources/reference/templating.rst.txt @@ -0,0 +1,479 @@ +Templating +========== + +As you may have noticed, templating is a dominating core functionality of Pharaoh. + +The foundation for all templating in Pharaoh is **reStructuredText** and **Jinja**. +You might make yourself familiar once you encounter related questions in the rest of the section. + + **reStructuredText** + + reStructuredText is an easy-to-read, what-you-see-is-what-you-get plaintext markup syntax and parser system. + It is useful for in-line program documentation (such as Python docstrings), for quickly creating simple web pages, + and for standalone documents. + + reStructuredText is designed for extensibility for specific application domains. + + The primary goal of reStructuredText is to define and implement a markup syntax for use in Python docstrings + and other documentation domains, that is readable and simple, yet powerful enough for non-trivial use. + + Refer to the `reStructuredText Reference `_ + for learning the basics. + + **Jinja** + + Jinja is a fast, expressive and extensible templating engine. + + A Jinja template is simply a text file. + Jinja can generate any text-based format (HTML, XML, CSV, LaTeX, etc.), in Pharaoh's case mostly rST files. + A Jinja template does not need to have a specific extension: .html, .xml, or any other extension is just fine. + + A template contains **variables** and/or **expressions**, which get replaced with values when a + template is *rendered*; and **tags**, which control the logic of the template. + The template syntax is heavily inspired by Python. + + Refer to the `Jinja Template Reference `_ + for learning the basics. + + See :ref:`Templating Builtins ` for a complete list of globals, filters and + tests you can use in your templates. + + +Like described in the :ref:`introduction `, Pharaoh's workflow distinguishes +two major stages where template rendering is performed. +Please refer to the corresponding sections by following the links: + + - :ref:`reference/templating:Generation-time Templating` + - :ref:`reference/templating:Build-time Templating` + + + +Generation-time Templating +-------------------------- + +Alias: **first-level templating** + +Generation-time templating is used while generating a new Pharaoh project or adding new components to an +existing project, and is inspired by `copier `_, +a |Jinja|-based library for rendering project templates. + +Basics +++++++ + +In Pharaoh, we need first-level templates for two different purposes: + + **Project Templates** + + These are the templates that may be specified when creating a new Pharaoh project. + The composition of all templates in this case must always generate a Sphinx project that contains at + least a ``conf.py`` and an index source file, like ``index.rst``. + + If you're interested in maintaining your own project template please contact us for support, + but for most use cases our default template ``pharaoh.default_project`` should be flexible enough. + + It is also possible to extend or overwrite parts of the default project, so please also get in touch + if you try to do so. + + **Component Templates** + + Component templates are used to render new components into a Pharaoh project. + + The minimal requirement is basically an ``index.rst`` file that can be included by the project + (at least what our default Pharaoh template concerns) or a + :ref:`Template File ` + +Like described :ref:`here `, templates can come in different styles. + +While for **Project Templates** only *registered templates* and *template directories* are allowed/useful, +**Component Templates** can, in addition to it, be generated using +:ref:`Template Files/Single-file templates `. + +Let's have a look on how a component template directory may look like: + + .. code-block:: none + + 📁 my_template + ├── 📄 index.rst.jinja # reST file with templated content + ├── 📄 test_context.py.jinja # Python file with templated content + ├── 📁 asset_scripts # folder copied as-is + │ └── 📄 default_plots.py # file copied as-is + └── 📁 [[foo]] # folder with a templated name + └── 📄 [[ bar ]]_script.py # file with a templated name + + - ``📁 my_template`` + + The top-level directory. The name is arbitrary, it will be replaced by the name of the component during coyping. + - ``📁 asset_scripts`` and ``📄 default_plots.py`` are copied as-is without modifications. + - ``📄 *.jinja`` + + The content of all files with suffix ``.jinja`` are rendered using Jinja. + - ``📁 [[foo]]`` and ``📄 [[ bar ]]_script.py`` + + File or directory names using ``[[ ]]`` are rendered using Jinja while copying. + +After rendering like this ``proj.add_component("dummy", [".../my_template"], render_context={"foo": "a", "bar": "b"}`` +a file structure like this is created: + + .. code-block:: none + + 📁 dummy + ├── 📄 index.rst + ├── 📄 test_context.py + ├── 📁 asset_scripts + │ └── 📄 default_plots.py + └── 📁 a + └── 📄 b_script.py + +You see the ``render_context`` passed to +:func:`PharaohProject.add_component() ` +or ``template_context`` passed to :func:`PharaohProject() ` are used to +render file- or folder name as well as file content. + +.. important:: + For Generation-time Templating, Jinja is configured to use brackets for blocks ``[% %]`` and statements ``[[ ]]`` + to not interfere with :ref:`reference/templating:Build-time Templating`, where Jinja will render the same + files (only reST) again before passing it to + Sphinx using curly-braces for blocks ``{% %}`` and statements ``{{ }}``. + + So imagine an extreme case of a file ``index.rst.jinja`` with following content:: + + {{ h1("[[heading_prefix]]%s"|format(ctx.project.component_name)) }} + + After :ref:`reference/templating:Generation-time Templating` + (component named ``Test_1`` with render context ``heading_prefix="PREFIX - "``) + it results in a file ``index.rst`` with content:: + + {{ h1("PREFIX - %s"|format(ctx.project.component_name)) }} + + After :ref:`reference/templating:Build-time Templating` it results in content:: + + PREFIX - Test_1 + ############### + + +Single-file Templates ++++++++++++++++++++++ + +Single-file templates, or also called template files, are smallest and most compact form of a template, but also +limited. + +They are mainly designed to deliver template code **and** asset script in a single file and contribute content to +an existing component. + +Template files are Python files with suffix ``.pharaoh.py`` those Python code creates |assets| and +those module level docstring represents the reST content. + +Here an example:: + + """ + {{ heading("My Plots", 2) }} + + .. pharaoh-asset:: label == "my_plot" + """ + from pharaoh.assetlib.api import metadata_context + + import plotly.express as px + + + df = px.data.iris() + fig = px.scatter( + df, + x="sepal_width", + y="sepal_length", + color="species", + symbol="species", + title=r"A title", + ) + + with metadata_context(label="my_plot"): + fig.write_html(file="iris_scatter.html") + + +This file will be internally converted to a template directory:: + + my_template.pharaoh.py -> index_my_template.rst + asset_scripts/my_template.py + + +In order to automatically include all ``index_*.rst`` files in your components index file ``index.rst``, you +must add following code: + + .. code-block:: + + {% for index_rst in fglob("index_*.rst") %} + .. include:: {{ index_rst }} + + {% endfor %} + + +Build-time Templating +--------------------- + +Build-time templating is the step where Pharaoh hooks into Sphinx's build process and renders each documentation +source file before it gets consumed by Sphinx. + +For example the source file ``index.rst`` will be read in, rendered, and finally passed to Sphinx for +further processing. Additionally for debugging purposes the output from rendering will be stored in the same +directory as the source file with a ``.rendered`` suffix (e.g. ``index.rst.rendered``), +in case the Sphinx build raises errors. + +.. dropdown:: Show Example + :animate: fade-in-slide-down + + .. tab-set:: + + .. tab-item:: Source File + + .. code-block:: + + {{ heading(ctx.local.test.test_name|req, 1) }} + + {{ h2("Some plots") }} + + + .. tab-item:: Rendered Source File + + .. code-block:: + + Dummy 1 + ####### + + Some plots + ********** + +Like mentionen in the :ref:`Introduction `, the main user groups of Pharaoh are +**Template Designers** and **End-Users**. + +**Template Designers** are responsible for creating templates for the **End-Users** of Pharaoh. +In order to make report generation as easy as possible for the end users, following template design guidelines +have to be considered: + +- **Tradeoff between flexibility and complexity for end-users** + + If the designer hides much of the template code (e.g. through :ref:`reference/templating:Template Inheritance`) + and leaves the end user with just template extensions and configurations, + the reports will gain a lot of maintainability (report can be just re-build with updated base templates). + + If the designer just provides component templates with less abstraction, a lot of template code will + reside in the user's report projects. This template code can only be updated by re-generating the + report project with updated component templates. + + So the general rule-of-thumb is to put all static template content or content that just needs configuration + in a base template and let the user just overwrite certain sections that are meant for it. + +- **Provide an abstraction library** + + Provide a small Python library to further standardize and reduce the amount of code users have to write in + their asset scripts. + +- Build smaller modular templates that can be composed together + + + +Template Inheritance +++++++++++++++++++++ + +Pharaoh templates support `Template Inheritance through Jinja +`_, +which is one of the most powerful and useful features of any template engine. +It means one template can inherit from another template. + +Generally, many report pages require the same or a similar layout and content for different pages, +so we use template inheritance to not repeat the same code in each template. + +A base template contains the basic layout which is common to all the other templates, +and it is from this base template we extend or derive the layout for other pages. + +In order to use inherit from base templates, those base templates must be discoverable via lookup paths. +Those lookup paths can be declared through: + + - :ref:`Pharaoh plugins ` + - a Sphinx configuration variable ``pharaoh_jinja_templates`` in ``report-project/conf.py``. + + This is a list of absolute or relative (to conf.py parent directory) lookup paths for base templates. + + Per default this is set to ``["user_templates"]``, which is an emtpy directory created by the default + Pharaoh Sphinx project template. + + Base template inside those lookup paths can be referenced via their relative path to the lookup directory, + so if we take this example: + + .. code-block:: none + + ... + 📁 user_templates + ├── 📄 baseA.rst + └── 📁 others + └── 📄 baseB.rst + + Then your templates could inherit from those templates like this: + + .. code-block:: none + + {% extends "baseA.rst" %} + {% block xyz %} + {# Insert block xyz content from baseA.rst #} + {{ super() }} + Some additional content + {% endblock %} + + or + + .. code-block:: none + + {% extends "others/baseB.rst" %} + {% block xyz %} + {# Overwrites block xyz content from others/baseB.rst #} + Some additional content + {% endblock %} + + + +.. dropdown:: Example + :open: + + .. tab-set:: + + .. tab-item:: Base Template ``base.rst`` + + The following base template defines an immutable page title and three sections with + immutable titles and a block declaration: + + .. code-block:: none + + {{ h1("Standardized Report Title") }} + + {{ h2("Prologue") }} + {% block prologue %} + This is a default content that may be overwritten by the child template + {% endblock %} + + {{ h2("Test Description") }} + {% block test_description %} + {% endblock %} + + {{ h2("Plots") }} + {% block plots %} + {% endblock %} + + .. tab-item:: Child Template ``index.rst`` + + The following child template inherits from ``base.rst`` and overwrites two of the the declared blocks with + custom content, and extends block ``prologue``: + + .. code-block:: none + + {% extends "base.rst" %} + + {% block prologue %} + {{ super() }} + + Additional content... + {% endblock %} + + {% block test_description %} + Some descriptive text... + {% endblock %} + + {% block plots %} + .. pharaoh-asset:: plot_name == "bla" + + {% endblock %} + + + +Rendering Context ++++++++++++++++++ + +During build-time templating you have access to a variety of context variables via Jinja variable ``ctx``. +``ctx`` is a nested dictionary that allows dotted-access (``{{ ctx.project.component_name }}`` or +``{{ ctx["project"]["component_name"] }}``) to all static or dynamic rendering context defined by Pharaoh or the user. + +.. code-block:: + + ctx # The root variable for accessing rendering context + .project # Project related context - set by Pharaoh + .instance # The Pharaoh project instance itself + .component_name # The name of the current component the template resides in + .config # The content of Sphinx's `conf.py`, e.g. `ctx.config.copyright` + .user # User defined context of dict variable `pharaoh_jinja_context` in `conf.py` + .local # Component's local static & dynamically generated context - see below + + +``ctx.local`` is a special local context that may be different for each component or even each source file +(but that's rarely a use-case) of a component. +It is composed by reading in so-called "local context files", that reside next to the file that is +currently rendered. +Those files can be: + + - YAML files with the naming scheme ``_context.yaml``. + So the content of a YAML file called ``default_context.yaml`` would be available via ``ctx.local.default``. + + .. note:: YAML files are loaded using the `OmegaConf library `_. + + - Python files with the naming scheme ``_context.py``. + + Those Python scripts are executed and must create a dict variable called ``context``. + + Since asset generation has already been executed, these scripts can also access the + :class:`AssetFinder ` instance to find and read assets + to extend the render context. + + .. dropdown:: Show Example ``test_context.py`` + :animate: fade-in-slide-down + + .. code-block:: + + """ + This script searches all JSON assets that have "context_name" metadata set, + loads them and exports their content as context for rendering via + variable `ctx.local.` + """ + import json + from pharaoh.assetlib.api import get_asset_finder, get_current_component + + finder = get_asset_finder() + component_name = get_current_component() + + # Find all assets of type JSON that have a "context_name" meta data set. + # Collect the content of those files in a dict using the "context_name" meta data as key. + context = { + asset.context.context_name: asset.read_json() + for asset in finder.search_assets( + 'asset.suffix == ".json" and "context_name" in asset.context', + [component_name] + ) + } + + - Data context that is registered via the :func:`pharaoh.assetlib.api.register_templating_context` function. + + +Extending Template Syntax ++++++++++++++++++++++++++ + +See :ref:`Templating Builtins ` for a complete list of builtin globals, filters and +tests you can use in your templates. + +If you like to add you own, Pharaoh provides some entrypoints in ``report-project/conf.py``: + +``pharaoh_jinja_filters`` + A dict that maps names to filter functions:: + + pharaoh_jinja_filters = { + "angry": lambda text: text + " 😠" # Usage: {{ "error"|angry }} + } + + +``pharaoh_jinja_globals`` + A dict that maps names to global functions:: + + pharaoh_jinja_globals = { + "angry": lambda text: text + " 😠" # Usage: {{ angry("error") }} + } + +``pharaoh_jinja_tests`` + A dict that maps names to tests:: + + pharaoh_jinja_tests = { + "angry_text": lambda text: "😠" in text # Usage: {% if "😠" is angry %}Someone is angry!{% endif %} + } diff --git a/_sources/template_designer_guide.rst.txt b/_sources/template_designer_guide.rst.txt new file mode 100644 index 0000000..ffa08f0 --- /dev/null +++ b/_sources/template_designer_guide.rst.txt @@ -0,0 +1,5 @@ +======================= +Template Designer Guide +======================= + +.. image:: /_static/chirping_crickets.gif diff --git a/_sources/user_guide.rst.txt b/_sources/user_guide.rst.txt new file mode 100644 index 0000000..33106d0 --- /dev/null +++ b/_sources/user_guide.rst.txt @@ -0,0 +1,231 @@ +========== +User Guide +========== + +Preface +======= + +This user guide targets **end-users**. + +If you're about to **create** templates for end users, +please refer to our :ref:`template_designer_guide:Template Designer Guide`. + +End-users usually will: + +- Generate Pharaoh projects based on the templates the template designers provided +- Update project settings +- Manage project components (update, add, remove) +- Modify/add asset scripts +- Overwrite certain pre-defined blocks inside provided templates + +.. important:: The following guide only shows the API provided by Pharaoh. Keep in mind that the template designers + of your project might provide an additional or different API to create Pharaoh projects. + + +Project Generation +================== + +The first thing to do is generating a Pharaoh project. + +.. note:: This step may be skipped if you already created a project and manage it via GIT for example. + + +.. tab-set:: + + .. tab-item:: API + + Create a Python script with following example content to create a new Pharaoh project: + + .. code-block:: + + from pharaoh.api import PharaohProject + + # Create a project with a default template optimized for HTML output inside current working directory + proj = PharaohProject(".") + + # For overwriting an existing project + proj = PharaohProject(project_root="path-to-existing-project", overwrite=True) + + # Passing a custom template to extend the default project template "pharaoh.default_project" + proj = PharaohProject( + ".", + templates=("pharaoh.default_project", r"C:\...\my-extension-template"), + template_context=dict(some_variable_in_extension_template="abc") + ) + + # Passing a custom settings YAML file + proj = PharaohProject(".", custom_settings=r"C:\...\my_custom_settings.yaml") + + + .. tab-item:: CLI + + In a terminal with activated virtual environment that has pharaoh installed, type following commands: + + .. code-block:: none + + # Create a project with a default template optimized for HTML output inside current working directory + pharaoh new + + # For overwriting an existing project + pharaoh -p "path/to/existing/project" new --force + + # Passing a custom template to extend the default project template "pharaoh.default_project" + pharaoh new -t pharaoh.default_project -t "C:\...\my-extension-template" + -c "{'some_variable_in_extension_template': 'abc'}" + + # Passing a custom settings YAML file + pharaoh -p . new --settings "C:\...\my_custom_settings.yaml" + + +Update Settings +=============== + +.. seealso:: :ref:`Settings Reference ` + +Single settings may be updated during project generation (API & CLI) or afterwards (API or manually). + +.. tab-set:: + + .. tab-item:: During Generation + + .. tab-set:: + + .. tab-item:: API + + This is done via the settings API function + :func:`put_setting(key: str, value: Any) ` + or via environment variables. Refer to the :ref:`reference/settings:Custom Settings` section + for examples. + + .. tab-item:: CLI + + Besides passing an entire settings YAML file to the project generation, + settings can be set using environment variables during project generation. + Those will be automatically persisted in the settings file. + + .. code-block:: none + + # CMD syntax: + set PHARAOH.LOGGING.LEVEL=INFO + set PHARAOH.FOO=bar + set PHARAOH.bla="{'blubb': 123}" + + # PowerShell syntax: + Set-Variable -Name PHARAOH.LOGGING.LEVEL -Value INFO + Set-Variable -Name PHARAOH.FOO -Value bar + Set-Variable -Name PHARAOH.bla -Value "{'blubb': 123}" + + # Bash syntax: + env "PHARAOH.LOGGING.LEVEL=INFO" bash + env "PHARAOH.FOO=bar" bash + env "PHARAOH.bla={'blubb': 123}" bash + + pharaoh new + + + .. tab-item:: After Generation + + .. tab-set:: + + .. tab-item:: API + + This is done via the settings API function + :func:`put_setting(key: str, value: Any) ` + or via environment variables. Refer to the :ref:`reference/settings:Custom Settings` section + for examples. + + .. code-block:: + + from pharaoh.api import PharaohProject + + proj = PharaohProject(project_root="some-path") + proj.put_setting("report.title", "My own title") + proj.put_setting("toolkits.bokeh.export_png", dict(width=720, height=480)) + + .. tab-item:: CLI + + Updating settings via CLI may be done via the :ref:`env command `: + + .. code-block:: none + + cd "path/to/project" + pharaoh env report.title "My own title" + + # or + pharaoh -p "path/to/project" env toolkits.bokeh.export_png "{'width':720, 'height':480}" + + .. tab-item:: Manually + + Just open the file ``pharaoh.yaml`` in your project root and modify its content according to + `YAML Coding Guidelines + `_ + and the `OmegaConf library specification `_. + + +Manage Components +================= + +The main content of a Pharaoh report is determined by components. + +.. seealso:: :ref:`What's a Component? ` in the + :ref:`Components Reference ` + +Components may be added or updated using following API/CLI: + +.. tab-set:: + + .. tab-item:: API + + This is done via the component API functions for managing components + :ref:`documented here `. + + Please refer to the component API functions for managing components + :ref:`here `. Relevant sections are: + + - :ref:`reference/components:adding components` + - :ref:`reference/components:updating components` + - :ref:`reference/components:removing components` + + .. tab-item:: CLI + + Please refer to the CLI reference :ref:`here `. Relevant commands are: + + - :ref:`reference/cli:add` + - :ref:`reference/cli:update-resource` + - :ref:`reference/cli:add-template` + - :ref:`reference/cli:remove` + + Here some examples: + + .. code-block:: none + + pharaoh add -n dummy1 -t pharaoh_testing.simple -c "{'test_name':'dummy'}" + pharaoh add -n dummy1 -t pharaoh_testing.simple -r "FileResource(alias='foo', pattern='.*')" + + pharaoh update-resource -n dummy1 -a foo -r "FileResource(alias='baz', pattern='.*')" + + pharaoh add-template -n dummy1 -t pharaoh_testing.simple -c "{'test_name':'dummy'}" + + pharaoh remove -f dummy.* + + +Manage Asset Scripts +==================== + +Once components are added, users may add or modify existing asset scripts. + +.. seealso:: :ref:`What's an Asset? ` in the + :ref:`Assets Reference ` and + :ref:`Example Asset Scripts `. + + + + +Modify Templates +================ + +If assets scripts are added or modified the templates might need to be updated to include potentially +modified or added assets. + +.. seealso:: :ref:`reference/directive:Pharaoh Directive` on how to include generated assets into your templates + and the :ref:`reference/templating:Build-time Templating` section. diff --git a/_sphinx_design_static/design-style.1e8bd061cd6da7fc9cf755528e8ffc24.min.css b/_sphinx_design_static/design-style.1e8bd061cd6da7fc9cf755528e8ffc24.min.css new file mode 100644 index 0000000..eb19f69 --- /dev/null +++ b/_sphinx_design_static/design-style.1e8bd061cd6da7fc9cf755528e8ffc24.min.css @@ -0,0 +1 @@ +.sd-bg-primary{background-color:var(--sd-color-primary) !important}.sd-bg-text-primary{color:var(--sd-color-primary-text) !important}button.sd-bg-primary:focus,button.sd-bg-primary:hover{background-color:var(--sd-color-primary-highlight) !important}a.sd-bg-primary:focus,a.sd-bg-primary:hover{background-color:var(--sd-color-primary-highlight) !important}.sd-bg-secondary{background-color:var(--sd-color-secondary) !important}.sd-bg-text-secondary{color:var(--sd-color-secondary-text) !important}button.sd-bg-secondary:focus,button.sd-bg-secondary:hover{background-color:var(--sd-color-secondary-highlight) !important}a.sd-bg-secondary:focus,a.sd-bg-secondary:hover{background-color:var(--sd-color-secondary-highlight) !important}.sd-bg-success{background-color:var(--sd-color-success) !important}.sd-bg-text-success{color:var(--sd-color-success-text) !important}button.sd-bg-success:focus,button.sd-bg-success:hover{background-color:var(--sd-color-success-highlight) !important}a.sd-bg-success:focus,a.sd-bg-success:hover{background-color:var(--sd-color-success-highlight) !important}.sd-bg-info{background-color:var(--sd-color-info) !important}.sd-bg-text-info{color:var(--sd-color-info-text) !important}button.sd-bg-info:focus,button.sd-bg-info:hover{background-color:var(--sd-color-info-highlight) !important}a.sd-bg-info:focus,a.sd-bg-info:hover{background-color:var(--sd-color-info-highlight) !important}.sd-bg-warning{background-color:var(--sd-color-warning) !important}.sd-bg-text-warning{color:var(--sd-color-warning-text) !important}button.sd-bg-warning:focus,button.sd-bg-warning:hover{background-color:var(--sd-color-warning-highlight) !important}a.sd-bg-warning:focus,a.sd-bg-warning:hover{background-color:var(--sd-color-warning-highlight) !important}.sd-bg-danger{background-color:var(--sd-color-danger) !important}.sd-bg-text-danger{color:var(--sd-color-danger-text) !important}button.sd-bg-danger:focus,button.sd-bg-danger:hover{background-color:var(--sd-color-danger-highlight) !important}a.sd-bg-danger:focus,a.sd-bg-danger:hover{background-color:var(--sd-color-danger-highlight) !important}.sd-bg-light{background-color:var(--sd-color-light) !important}.sd-bg-text-light{color:var(--sd-color-light-text) !important}button.sd-bg-light:focus,button.sd-bg-light:hover{background-color:var(--sd-color-light-highlight) !important}a.sd-bg-light:focus,a.sd-bg-light:hover{background-color:var(--sd-color-light-highlight) !important}.sd-bg-muted{background-color:var(--sd-color-muted) !important}.sd-bg-text-muted{color:var(--sd-color-muted-text) !important}button.sd-bg-muted:focus,button.sd-bg-muted:hover{background-color:var(--sd-color-muted-highlight) !important}a.sd-bg-muted:focus,a.sd-bg-muted:hover{background-color:var(--sd-color-muted-highlight) !important}.sd-bg-dark{background-color:var(--sd-color-dark) !important}.sd-bg-text-dark{color:var(--sd-color-dark-text) !important}button.sd-bg-dark:focus,button.sd-bg-dark:hover{background-color:var(--sd-color-dark-highlight) !important}a.sd-bg-dark:focus,a.sd-bg-dark:hover{background-color:var(--sd-color-dark-highlight) !important}.sd-bg-black{background-color:var(--sd-color-black) !important}.sd-bg-text-black{color:var(--sd-color-black-text) !important}button.sd-bg-black:focus,button.sd-bg-black:hover{background-color:var(--sd-color-black-highlight) !important}a.sd-bg-black:focus,a.sd-bg-black:hover{background-color:var(--sd-color-black-highlight) !important}.sd-bg-white{background-color:var(--sd-color-white) !important}.sd-bg-text-white{color:var(--sd-color-white-text) !important}button.sd-bg-white:focus,button.sd-bg-white:hover{background-color:var(--sd-color-white-highlight) !important}a.sd-bg-white:focus,a.sd-bg-white:hover{background-color:var(--sd-color-white-highlight) !important}.sd-text-primary,.sd-text-primary>p{color:var(--sd-color-primary) !important}a.sd-text-primary:focus,a.sd-text-primary:hover{color:var(--sd-color-primary-highlight) !important}.sd-text-secondary,.sd-text-secondary>p{color:var(--sd-color-secondary) !important}a.sd-text-secondary:focus,a.sd-text-secondary:hover{color:var(--sd-color-secondary-highlight) !important}.sd-text-success,.sd-text-success>p{color:var(--sd-color-success) !important}a.sd-text-success:focus,a.sd-text-success:hover{color:var(--sd-color-success-highlight) !important}.sd-text-info,.sd-text-info>p{color:var(--sd-color-info) !important}a.sd-text-info:focus,a.sd-text-info:hover{color:var(--sd-color-info-highlight) !important}.sd-text-warning,.sd-text-warning>p{color:var(--sd-color-warning) !important}a.sd-text-warning:focus,a.sd-text-warning:hover{color:var(--sd-color-warning-highlight) !important}.sd-text-danger,.sd-text-danger>p{color:var(--sd-color-danger) !important}a.sd-text-danger:focus,a.sd-text-danger:hover{color:var(--sd-color-danger-highlight) !important}.sd-text-light,.sd-text-light>p{color:var(--sd-color-light) !important}a.sd-text-light:focus,a.sd-text-light:hover{color:var(--sd-color-light-highlight) !important}.sd-text-muted,.sd-text-muted>p{color:var(--sd-color-muted) !important}a.sd-text-muted:focus,a.sd-text-muted:hover{color:var(--sd-color-muted-highlight) !important}.sd-text-dark,.sd-text-dark>p{color:var(--sd-color-dark) !important}a.sd-text-dark:focus,a.sd-text-dark:hover{color:var(--sd-color-dark-highlight) !important}.sd-text-black,.sd-text-black>p{color:var(--sd-color-black) !important}a.sd-text-black:focus,a.sd-text-black:hover{color:var(--sd-color-black-highlight) !important}.sd-text-white,.sd-text-white>p{color:var(--sd-color-white) !important}a.sd-text-white:focus,a.sd-text-white:hover{color:var(--sd-color-white-highlight) !important}.sd-outline-primary{border-color:var(--sd-color-primary) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-primary:focus,a.sd-outline-primary:hover{border-color:var(--sd-color-primary-highlight) !important}.sd-outline-secondary{border-color:var(--sd-color-secondary) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-secondary:focus,a.sd-outline-secondary:hover{border-color:var(--sd-color-secondary-highlight) !important}.sd-outline-success{border-color:var(--sd-color-success) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-success:focus,a.sd-outline-success:hover{border-color:var(--sd-color-success-highlight) !important}.sd-outline-info{border-color:var(--sd-color-info) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-info:focus,a.sd-outline-info:hover{border-color:var(--sd-color-info-highlight) !important}.sd-outline-warning{border-color:var(--sd-color-warning) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-warning:focus,a.sd-outline-warning:hover{border-color:var(--sd-color-warning-highlight) !important}.sd-outline-danger{border-color:var(--sd-color-danger) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-danger:focus,a.sd-outline-danger:hover{border-color:var(--sd-color-danger-highlight) !important}.sd-outline-light{border-color:var(--sd-color-light) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-light:focus,a.sd-outline-light:hover{border-color:var(--sd-color-light-highlight) !important}.sd-outline-muted{border-color:var(--sd-color-muted) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-muted:focus,a.sd-outline-muted:hover{border-color:var(--sd-color-muted-highlight) !important}.sd-outline-dark{border-color:var(--sd-color-dark) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-dark:focus,a.sd-outline-dark:hover{border-color:var(--sd-color-dark-highlight) !important}.sd-outline-black{border-color:var(--sd-color-black) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-black:focus,a.sd-outline-black:hover{border-color:var(--sd-color-black-highlight) !important}.sd-outline-white{border-color:var(--sd-color-white) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-white:focus,a.sd-outline-white:hover{border-color:var(--sd-color-white-highlight) !important}.sd-bg-transparent{background-color:transparent !important}.sd-outline-transparent{border-color:transparent !important}.sd-text-transparent{color:transparent !important}.sd-p-0{padding:0 !important}.sd-pt-0,.sd-py-0{padding-top:0 !important}.sd-pr-0,.sd-px-0{padding-right:0 !important}.sd-pb-0,.sd-py-0{padding-bottom:0 !important}.sd-pl-0,.sd-px-0{padding-left:0 !important}.sd-p-1{padding:.25rem !important}.sd-pt-1,.sd-py-1{padding-top:.25rem !important}.sd-pr-1,.sd-px-1{padding-right:.25rem !important}.sd-pb-1,.sd-py-1{padding-bottom:.25rem !important}.sd-pl-1,.sd-px-1{padding-left:.25rem !important}.sd-p-2{padding:.5rem !important}.sd-pt-2,.sd-py-2{padding-top:.5rem !important}.sd-pr-2,.sd-px-2{padding-right:.5rem !important}.sd-pb-2,.sd-py-2{padding-bottom:.5rem !important}.sd-pl-2,.sd-px-2{padding-left:.5rem !important}.sd-p-3{padding:1rem !important}.sd-pt-3,.sd-py-3{padding-top:1rem !important}.sd-pr-3,.sd-px-3{padding-right:1rem !important}.sd-pb-3,.sd-py-3{padding-bottom:1rem !important}.sd-pl-3,.sd-px-3{padding-left:1rem !important}.sd-p-4{padding:1.5rem !important}.sd-pt-4,.sd-py-4{padding-top:1.5rem !important}.sd-pr-4,.sd-px-4{padding-right:1.5rem !important}.sd-pb-4,.sd-py-4{padding-bottom:1.5rem !important}.sd-pl-4,.sd-px-4{padding-left:1.5rem !important}.sd-p-5{padding:3rem !important}.sd-pt-5,.sd-py-5{padding-top:3rem !important}.sd-pr-5,.sd-px-5{padding-right:3rem !important}.sd-pb-5,.sd-py-5{padding-bottom:3rem !important}.sd-pl-5,.sd-px-5{padding-left:3rem !important}.sd-m-auto{margin:auto !important}.sd-mt-auto,.sd-my-auto{margin-top:auto !important}.sd-mr-auto,.sd-mx-auto{margin-right:auto !important}.sd-mb-auto,.sd-my-auto{margin-bottom:auto !important}.sd-ml-auto,.sd-mx-auto{margin-left:auto !important}.sd-m-0{margin:0 !important}.sd-mt-0,.sd-my-0{margin-top:0 !important}.sd-mr-0,.sd-mx-0{margin-right:0 !important}.sd-mb-0,.sd-my-0{margin-bottom:0 !important}.sd-ml-0,.sd-mx-0{margin-left:0 !important}.sd-m-1{margin:.25rem !important}.sd-mt-1,.sd-my-1{margin-top:.25rem !important}.sd-mr-1,.sd-mx-1{margin-right:.25rem !important}.sd-mb-1,.sd-my-1{margin-bottom:.25rem !important}.sd-ml-1,.sd-mx-1{margin-left:.25rem !important}.sd-m-2{margin:.5rem !important}.sd-mt-2,.sd-my-2{margin-top:.5rem !important}.sd-mr-2,.sd-mx-2{margin-right:.5rem !important}.sd-mb-2,.sd-my-2{margin-bottom:.5rem !important}.sd-ml-2,.sd-mx-2{margin-left:.5rem !important}.sd-m-3{margin:1rem !important}.sd-mt-3,.sd-my-3{margin-top:1rem !important}.sd-mr-3,.sd-mx-3{margin-right:1rem !important}.sd-mb-3,.sd-my-3{margin-bottom:1rem !important}.sd-ml-3,.sd-mx-3{margin-left:1rem !important}.sd-m-4{margin:1.5rem !important}.sd-mt-4,.sd-my-4{margin-top:1.5rem !important}.sd-mr-4,.sd-mx-4{margin-right:1.5rem !important}.sd-mb-4,.sd-my-4{margin-bottom:1.5rem !important}.sd-ml-4,.sd-mx-4{margin-left:1.5rem !important}.sd-m-5{margin:3rem !important}.sd-mt-5,.sd-my-5{margin-top:3rem !important}.sd-mr-5,.sd-mx-5{margin-right:3rem !important}.sd-mb-5,.sd-my-5{margin-bottom:3rem !important}.sd-ml-5,.sd-mx-5{margin-left:3rem !important}.sd-w-25{width:25% !important}.sd-w-50{width:50% !important}.sd-w-75{width:75% !important}.sd-w-100{width:100% !important}.sd-w-auto{width:auto !important}.sd-h-25{height:25% !important}.sd-h-50{height:50% !important}.sd-h-75{height:75% !important}.sd-h-100{height:100% !important}.sd-h-auto{height:auto !important}.sd-d-none{display:none !important}.sd-d-inline{display:inline !important}.sd-d-inline-block{display:inline-block !important}.sd-d-block{display:block !important}.sd-d-grid{display:grid !important}.sd-d-flex-row{display:-ms-flexbox !important;display:flex !important;flex-direction:row !important}.sd-d-flex-column{display:-ms-flexbox !important;display:flex !important;flex-direction:column !important}.sd-d-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}@media(min-width: 576px){.sd-d-sm-none{display:none !important}.sd-d-sm-inline{display:inline !important}.sd-d-sm-inline-block{display:inline-block !important}.sd-d-sm-block{display:block !important}.sd-d-sm-grid{display:grid !important}.sd-d-sm-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-sm-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}@media(min-width: 768px){.sd-d-md-none{display:none !important}.sd-d-md-inline{display:inline !important}.sd-d-md-inline-block{display:inline-block !important}.sd-d-md-block{display:block !important}.sd-d-md-grid{display:grid !important}.sd-d-md-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-md-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}@media(min-width: 992px){.sd-d-lg-none{display:none !important}.sd-d-lg-inline{display:inline !important}.sd-d-lg-inline-block{display:inline-block !important}.sd-d-lg-block{display:block !important}.sd-d-lg-grid{display:grid !important}.sd-d-lg-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-lg-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}@media(min-width: 1200px){.sd-d-xl-none{display:none !important}.sd-d-xl-inline{display:inline !important}.sd-d-xl-inline-block{display:inline-block !important}.sd-d-xl-block{display:block !important}.sd-d-xl-grid{display:grid !important}.sd-d-xl-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-xl-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}.sd-align-major-start{justify-content:flex-start !important}.sd-align-major-end{justify-content:flex-end !important}.sd-align-major-center{justify-content:center !important}.sd-align-major-justify{justify-content:space-between !important}.sd-align-major-spaced{justify-content:space-evenly !important}.sd-align-minor-start{align-items:flex-start !important}.sd-align-minor-end{align-items:flex-end !important}.sd-align-minor-center{align-items:center !important}.sd-align-minor-stretch{align-items:stretch !important}.sd-text-justify{text-align:justify !important}.sd-text-left{text-align:left !important}.sd-text-right{text-align:right !important}.sd-text-center{text-align:center !important}.sd-font-weight-light{font-weight:300 !important}.sd-font-weight-lighter{font-weight:lighter !important}.sd-font-weight-normal{font-weight:400 !important}.sd-font-weight-bold{font-weight:700 !important}.sd-font-weight-bolder{font-weight:bolder !important}.sd-font-italic{font-style:italic !important}.sd-text-decoration-none{text-decoration:none !important}.sd-text-lowercase{text-transform:lowercase !important}.sd-text-uppercase{text-transform:uppercase !important}.sd-text-capitalize{text-transform:capitalize !important}.sd-text-wrap{white-space:normal !important}.sd-text-nowrap{white-space:nowrap !important}.sd-text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-fs-1,.sd-fs-1>p{font-size:calc(1.375rem + 1.5vw) !important;line-height:unset !important}.sd-fs-2,.sd-fs-2>p{font-size:calc(1.325rem + 0.9vw) !important;line-height:unset !important}.sd-fs-3,.sd-fs-3>p{font-size:calc(1.3rem + 0.6vw) !important;line-height:unset !important}.sd-fs-4,.sd-fs-4>p{font-size:calc(1.275rem + 0.3vw) !important;line-height:unset !important}.sd-fs-5,.sd-fs-5>p{font-size:1.25rem !important;line-height:unset !important}.sd-fs-6,.sd-fs-6>p{font-size:1rem !important;line-height:unset !important}.sd-border-0{border:0 solid !important}.sd-border-top-0{border-top:0 solid !important}.sd-border-bottom-0{border-bottom:0 solid !important}.sd-border-right-0{border-right:0 solid !important}.sd-border-left-0{border-left:0 solid !important}.sd-border-1{border:1px solid !important}.sd-border-top-1{border-top:1px solid !important}.sd-border-bottom-1{border-bottom:1px solid !important}.sd-border-right-1{border-right:1px solid !important}.sd-border-left-1{border-left:1px solid !important}.sd-border-2{border:2px solid !important}.sd-border-top-2{border-top:2px solid !important}.sd-border-bottom-2{border-bottom:2px solid !important}.sd-border-right-2{border-right:2px solid !important}.sd-border-left-2{border-left:2px solid !important}.sd-border-3{border:3px solid !important}.sd-border-top-3{border-top:3px solid !important}.sd-border-bottom-3{border-bottom:3px solid !important}.sd-border-right-3{border-right:3px solid !important}.sd-border-left-3{border-left:3px solid !important}.sd-border-4{border:4px solid !important}.sd-border-top-4{border-top:4px solid !important}.sd-border-bottom-4{border-bottom:4px solid !important}.sd-border-right-4{border-right:4px solid !important}.sd-border-left-4{border-left:4px solid !important}.sd-border-5{border:5px solid !important}.sd-border-top-5{border-top:5px solid !important}.sd-border-bottom-5{border-bottom:5px solid !important}.sd-border-right-5{border-right:5px solid !important}.sd-border-left-5{border-left:5px solid !important}.sd-rounded-0{border-radius:0 !important}.sd-rounded-1{border-radius:.2rem !important}.sd-rounded-2{border-radius:.3rem !important}.sd-rounded-3{border-radius:.5rem !important}.sd-rounded-pill{border-radius:50rem !important}.sd-rounded-circle{border-radius:50% !important}.shadow-none{box-shadow:none !important}.sd-shadow-sm{box-shadow:0 .125rem .25rem var(--sd-color-shadow) !important}.sd-shadow-md{box-shadow:0 .5rem 1rem var(--sd-color-shadow) !important}.sd-shadow-lg{box-shadow:0 1rem 3rem var(--sd-color-shadow) !important}@keyframes sd-slide-from-left{0%{transform:translateX(-100%)}100%{transform:translateX(0)}}@keyframes sd-slide-from-right{0%{transform:translateX(200%)}100%{transform:translateX(0)}}@keyframes sd-grow100{0%{transform:scale(0);opacity:.5}100%{transform:scale(1);opacity:1}}@keyframes sd-grow50{0%{transform:scale(0.5);opacity:.5}100%{transform:scale(1);opacity:1}}@keyframes sd-grow50-rot20{0%{transform:scale(0.5) rotateZ(-20deg);opacity:.5}75%{transform:scale(1) rotateZ(5deg);opacity:1}95%{transform:scale(1) rotateZ(-1deg);opacity:1}100%{transform:scale(1) rotateZ(0);opacity:1}}.sd-animate-slide-from-left{animation:1s ease-out 0s 1 normal none running sd-slide-from-left}.sd-animate-slide-from-right{animation:1s ease-out 0s 1 normal none running sd-slide-from-right}.sd-animate-grow100{animation:1s ease-out 0s 1 normal none running sd-grow100}.sd-animate-grow50{animation:1s ease-out 0s 1 normal none running sd-grow50}.sd-animate-grow50-rot20{animation:1s ease-out 0s 1 normal none running sd-grow50-rot20}.sd-badge{display:inline-block;padding:.35em .65em;font-size:.75em;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.sd-badge:empty{display:none}a.sd-badge{text-decoration:none}.sd-btn .sd-badge{position:relative;top:-1px}.sd-btn{background-color:transparent;border:1px solid transparent;border-radius:.25rem;cursor:pointer;display:inline-block;font-weight:400;font-size:1rem;line-height:1.5;padding:.375rem .75rem;text-align:center;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;vertical-align:middle;user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none}.sd-btn:hover{text-decoration:none}@media(prefers-reduced-motion: reduce){.sd-btn{transition:none}}.sd-btn-primary,.sd-btn-outline-primary:hover,.sd-btn-outline-primary:focus{color:var(--sd-color-primary-text) !important;background-color:var(--sd-color-primary) !important;border-color:var(--sd-color-primary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-primary:hover,.sd-btn-primary:focus{color:var(--sd-color-primary-text) !important;background-color:var(--sd-color-primary-highlight) !important;border-color:var(--sd-color-primary-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-primary{color:var(--sd-color-primary) !important;border-color:var(--sd-color-primary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-secondary,.sd-btn-outline-secondary:hover,.sd-btn-outline-secondary:focus{color:var(--sd-color-secondary-text) !important;background-color:var(--sd-color-secondary) !important;border-color:var(--sd-color-secondary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-secondary:hover,.sd-btn-secondary:focus{color:var(--sd-color-secondary-text) !important;background-color:var(--sd-color-secondary-highlight) !important;border-color:var(--sd-color-secondary-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-secondary{color:var(--sd-color-secondary) !important;border-color:var(--sd-color-secondary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-success,.sd-btn-outline-success:hover,.sd-btn-outline-success:focus{color:var(--sd-color-success-text) !important;background-color:var(--sd-color-success) !important;border-color:var(--sd-color-success) !important;border-width:1px !important;border-style:solid !important}.sd-btn-success:hover,.sd-btn-success:focus{color:var(--sd-color-success-text) !important;background-color:var(--sd-color-success-highlight) !important;border-color:var(--sd-color-success-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-success{color:var(--sd-color-success) !important;border-color:var(--sd-color-success) !important;border-width:1px !important;border-style:solid !important}.sd-btn-info,.sd-btn-outline-info:hover,.sd-btn-outline-info:focus{color:var(--sd-color-info-text) !important;background-color:var(--sd-color-info) !important;border-color:var(--sd-color-info) !important;border-width:1px !important;border-style:solid !important}.sd-btn-info:hover,.sd-btn-info:focus{color:var(--sd-color-info-text) !important;background-color:var(--sd-color-info-highlight) !important;border-color:var(--sd-color-info-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-info{color:var(--sd-color-info) !important;border-color:var(--sd-color-info) !important;border-width:1px !important;border-style:solid !important}.sd-btn-warning,.sd-btn-outline-warning:hover,.sd-btn-outline-warning:focus{color:var(--sd-color-warning-text) !important;background-color:var(--sd-color-warning) !important;border-color:var(--sd-color-warning) !important;border-width:1px !important;border-style:solid !important}.sd-btn-warning:hover,.sd-btn-warning:focus{color:var(--sd-color-warning-text) !important;background-color:var(--sd-color-warning-highlight) !important;border-color:var(--sd-color-warning-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-warning{color:var(--sd-color-warning) !important;border-color:var(--sd-color-warning) !important;border-width:1px !important;border-style:solid !important}.sd-btn-danger,.sd-btn-outline-danger:hover,.sd-btn-outline-danger:focus{color:var(--sd-color-danger-text) !important;background-color:var(--sd-color-danger) !important;border-color:var(--sd-color-danger) !important;border-width:1px !important;border-style:solid !important}.sd-btn-danger:hover,.sd-btn-danger:focus{color:var(--sd-color-danger-text) !important;background-color:var(--sd-color-danger-highlight) !important;border-color:var(--sd-color-danger-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-danger{color:var(--sd-color-danger) !important;border-color:var(--sd-color-danger) !important;border-width:1px !important;border-style:solid !important}.sd-btn-light,.sd-btn-outline-light:hover,.sd-btn-outline-light:focus{color:var(--sd-color-light-text) !important;background-color:var(--sd-color-light) !important;border-color:var(--sd-color-light) !important;border-width:1px !important;border-style:solid !important}.sd-btn-light:hover,.sd-btn-light:focus{color:var(--sd-color-light-text) !important;background-color:var(--sd-color-light-highlight) !important;border-color:var(--sd-color-light-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-light{color:var(--sd-color-light) !important;border-color:var(--sd-color-light) !important;border-width:1px !important;border-style:solid !important}.sd-btn-muted,.sd-btn-outline-muted:hover,.sd-btn-outline-muted:focus{color:var(--sd-color-muted-text) !important;background-color:var(--sd-color-muted) !important;border-color:var(--sd-color-muted) !important;border-width:1px !important;border-style:solid !important}.sd-btn-muted:hover,.sd-btn-muted:focus{color:var(--sd-color-muted-text) !important;background-color:var(--sd-color-muted-highlight) !important;border-color:var(--sd-color-muted-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-muted{color:var(--sd-color-muted) !important;border-color:var(--sd-color-muted) !important;border-width:1px !important;border-style:solid !important}.sd-btn-dark,.sd-btn-outline-dark:hover,.sd-btn-outline-dark:focus{color:var(--sd-color-dark-text) !important;background-color:var(--sd-color-dark) !important;border-color:var(--sd-color-dark) !important;border-width:1px !important;border-style:solid !important}.sd-btn-dark:hover,.sd-btn-dark:focus{color:var(--sd-color-dark-text) !important;background-color:var(--sd-color-dark-highlight) !important;border-color:var(--sd-color-dark-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-dark{color:var(--sd-color-dark) !important;border-color:var(--sd-color-dark) !important;border-width:1px !important;border-style:solid !important}.sd-btn-black,.sd-btn-outline-black:hover,.sd-btn-outline-black:focus{color:var(--sd-color-black-text) !important;background-color:var(--sd-color-black) !important;border-color:var(--sd-color-black) !important;border-width:1px !important;border-style:solid !important}.sd-btn-black:hover,.sd-btn-black:focus{color:var(--sd-color-black-text) !important;background-color:var(--sd-color-black-highlight) !important;border-color:var(--sd-color-black-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-black{color:var(--sd-color-black) !important;border-color:var(--sd-color-black) !important;border-width:1px !important;border-style:solid !important}.sd-btn-white,.sd-btn-outline-white:hover,.sd-btn-outline-white:focus{color:var(--sd-color-white-text) !important;background-color:var(--sd-color-white) !important;border-color:var(--sd-color-white) !important;border-width:1px !important;border-style:solid !important}.sd-btn-white:hover,.sd-btn-white:focus{color:var(--sd-color-white-text) !important;background-color:var(--sd-color-white-highlight) !important;border-color:var(--sd-color-white-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-white{color:var(--sd-color-white) !important;border-color:var(--sd-color-white) !important;border-width:1px !important;border-style:solid !important}.sd-stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.sd-hide-link-text{font-size:0}.sd-octicon,.sd-material-icon{display:inline-block;fill:currentColor;vertical-align:middle}.sd-avatar-xs{border-radius:50%;object-fit:cover;object-position:center;width:1rem;height:1rem}.sd-avatar-sm{border-radius:50%;object-fit:cover;object-position:center;width:3rem;height:3rem}.sd-avatar-md{border-radius:50%;object-fit:cover;object-position:center;width:5rem;height:5rem}.sd-avatar-lg{border-radius:50%;object-fit:cover;object-position:center;width:7rem;height:7rem}.sd-avatar-xl{border-radius:50%;object-fit:cover;object-position:center;width:10rem;height:10rem}.sd-avatar-inherit{border-radius:50%;object-fit:cover;object-position:center;width:inherit;height:inherit}.sd-avatar-initial{border-radius:50%;object-fit:cover;object-position:center;width:initial;height:initial}.sd-card{background-clip:border-box;background-color:var(--sd-color-card-background);border:1px solid var(--sd-color-card-border);border-radius:.25rem;color:var(--sd-color-card-text);display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;position:relative;word-wrap:break-word}.sd-card>hr{margin-left:0;margin-right:0}.sd-card-hover:hover{border-color:var(--sd-color-card-border-hover);transform:scale(1.01)}.sd-card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem 1rem}.sd-card-title{margin-bottom:.5rem}.sd-card-subtitle{margin-top:-0.25rem;margin-bottom:0}.sd-card-text:last-child{margin-bottom:0}.sd-card-link:hover{text-decoration:none}.sd-card-link+.card-link{margin-left:1rem}.sd-card-header{padding:.5rem 1rem;margin-bottom:0;background-color:var(--sd-color-card-header);border-bottom:1px solid var(--sd-color-card-border)}.sd-card-header:first-child{border-radius:calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0}.sd-card-footer{padding:.5rem 1rem;background-color:var(--sd-color-card-footer);border-top:1px solid var(--sd-color-card-border)}.sd-card-footer:last-child{border-radius:0 0 calc(0.25rem - 1px) calc(0.25rem - 1px)}.sd-card-header-tabs{margin-right:-0.5rem;margin-bottom:-0.5rem;margin-left:-0.5rem;border-bottom:0}.sd-card-header-pills{margin-right:-0.5rem;margin-left:-0.5rem}.sd-card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1rem;border-radius:calc(0.25rem - 1px)}.sd-card-img,.sd-card-img-bottom,.sd-card-img-top{width:100%}.sd-card-img,.sd-card-img-top{border-top-left-radius:calc(0.25rem - 1px);border-top-right-radius:calc(0.25rem - 1px)}.sd-card-img,.sd-card-img-bottom{border-bottom-left-radius:calc(0.25rem - 1px);border-bottom-right-radius:calc(0.25rem - 1px)}.sd-cards-carousel{width:100%;display:flex;flex-wrap:nowrap;-ms-flex-direction:row;flex-direction:row;overflow-x:hidden;scroll-snap-type:x mandatory}.sd-cards-carousel.sd-show-scrollbar{overflow-x:auto}.sd-cards-carousel:hover,.sd-cards-carousel:focus{overflow-x:auto}.sd-cards-carousel>.sd-card{flex-shrink:0;scroll-snap-align:start}.sd-cards-carousel>.sd-card:not(:last-child){margin-right:3px}.sd-card-cols-1>.sd-card{width:90%}.sd-card-cols-2>.sd-card{width:45%}.sd-card-cols-3>.sd-card{width:30%}.sd-card-cols-4>.sd-card{width:22.5%}.sd-card-cols-5>.sd-card{width:18%}.sd-card-cols-6>.sd-card{width:15%}.sd-card-cols-7>.sd-card{width:12.8571428571%}.sd-card-cols-8>.sd-card{width:11.25%}.sd-card-cols-9>.sd-card{width:10%}.sd-card-cols-10>.sd-card{width:9%}.sd-card-cols-11>.sd-card{width:8.1818181818%}.sd-card-cols-12>.sd-card{width:7.5%}.sd-container,.sd-container-fluid,.sd-container-lg,.sd-container-md,.sd-container-sm,.sd-container-xl{margin-left:auto;margin-right:auto;padding-left:var(--sd-gutter-x, 0.75rem);padding-right:var(--sd-gutter-x, 0.75rem);width:100%}@media(min-width: 576px){.sd-container-sm,.sd-container{max-width:540px}}@media(min-width: 768px){.sd-container-md,.sd-container-sm,.sd-container{max-width:720px}}@media(min-width: 992px){.sd-container-lg,.sd-container-md,.sd-container-sm,.sd-container{max-width:960px}}@media(min-width: 1200px){.sd-container-xl,.sd-container-lg,.sd-container-md,.sd-container-sm,.sd-container{max-width:1140px}}.sd-row{--sd-gutter-x: 1.5rem;--sd-gutter-y: 0;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-top:calc(var(--sd-gutter-y) * -1);margin-right:calc(var(--sd-gutter-x) * -0.5);margin-left:calc(var(--sd-gutter-x) * -0.5)}.sd-row>*{box-sizing:border-box;flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--sd-gutter-x) * 0.5);padding-left:calc(var(--sd-gutter-x) * 0.5);margin-top:var(--sd-gutter-y)}.sd-col{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-auto>*{flex:0 0 auto;width:auto}.sd-row-cols-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}@media(min-width: 576px){.sd-col-sm{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-sm-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-sm-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-sm-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-sm-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-sm-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-sm-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-sm-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-sm-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-sm-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-sm-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-sm-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-sm-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-sm-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}@media(min-width: 768px){.sd-col-md{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-md-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-md-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-md-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-md-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-md-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-md-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-md-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-md-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-md-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-md-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-md-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-md-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-md-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}@media(min-width: 992px){.sd-col-lg{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-lg-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-lg-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-lg-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-lg-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-lg-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-lg-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-lg-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-lg-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-lg-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-lg-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-lg-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-lg-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-lg-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}@media(min-width: 1200px){.sd-col-xl{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-xl-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-xl-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-xl-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-xl-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-xl-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-xl-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-xl-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-xl-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-xl-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-xl-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-xl-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-xl-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-xl-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}.sd-col-auto{flex:0 0 auto;-ms-flex:0 0 auto;width:auto}.sd-col-1{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}.sd-col-2{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-col-3{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-col-4{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-col-5{flex:0 0 auto;-ms-flex:0 0 auto;width:41.6666666667%}.sd-col-6{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-col-7{flex:0 0 auto;-ms-flex:0 0 auto;width:58.3333333333%}.sd-col-8{flex:0 0 auto;-ms-flex:0 0 auto;width:66.6666666667%}.sd-col-9{flex:0 0 auto;-ms-flex:0 0 auto;width:75%}.sd-col-10{flex:0 0 auto;-ms-flex:0 0 auto;width:83.3333333333%}.sd-col-11{flex:0 0 auto;-ms-flex:0 0 auto;width:91.6666666667%}.sd-col-12{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-g-0,.sd-gy-0{--sd-gutter-y: 0}.sd-g-0,.sd-gx-0{--sd-gutter-x: 0}.sd-g-1,.sd-gy-1{--sd-gutter-y: 0.25rem}.sd-g-1,.sd-gx-1{--sd-gutter-x: 0.25rem}.sd-g-2,.sd-gy-2{--sd-gutter-y: 0.5rem}.sd-g-2,.sd-gx-2{--sd-gutter-x: 0.5rem}.sd-g-3,.sd-gy-3{--sd-gutter-y: 1rem}.sd-g-3,.sd-gx-3{--sd-gutter-x: 1rem}.sd-g-4,.sd-gy-4{--sd-gutter-y: 1.5rem}.sd-g-4,.sd-gx-4{--sd-gutter-x: 1.5rem}.sd-g-5,.sd-gy-5{--sd-gutter-y: 3rem}.sd-g-5,.sd-gx-5{--sd-gutter-x: 3rem}@media(min-width: 576px){.sd-col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-sm-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-sm-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-sm-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-sm-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-sm-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-sm-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-sm-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-sm-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-sm-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-sm-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-sm-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-sm-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-sm-0,.sd-gy-sm-0{--sd-gutter-y: 0}.sd-g-sm-0,.sd-gx-sm-0{--sd-gutter-x: 0}.sd-g-sm-1,.sd-gy-sm-1{--sd-gutter-y: 0.25rem}.sd-g-sm-1,.sd-gx-sm-1{--sd-gutter-x: 0.25rem}.sd-g-sm-2,.sd-gy-sm-2{--sd-gutter-y: 0.5rem}.sd-g-sm-2,.sd-gx-sm-2{--sd-gutter-x: 0.5rem}.sd-g-sm-3,.sd-gy-sm-3{--sd-gutter-y: 1rem}.sd-g-sm-3,.sd-gx-sm-3{--sd-gutter-x: 1rem}.sd-g-sm-4,.sd-gy-sm-4{--sd-gutter-y: 1.5rem}.sd-g-sm-4,.sd-gx-sm-4{--sd-gutter-x: 1.5rem}.sd-g-sm-5,.sd-gy-sm-5{--sd-gutter-y: 3rem}.sd-g-sm-5,.sd-gx-sm-5{--sd-gutter-x: 3rem}}@media(min-width: 768px){.sd-col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-md-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-md-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-md-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-md-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-md-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-md-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-md-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-md-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-md-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-md-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-md-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-md-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-md-0,.sd-gy-md-0{--sd-gutter-y: 0}.sd-g-md-0,.sd-gx-md-0{--sd-gutter-x: 0}.sd-g-md-1,.sd-gy-md-1{--sd-gutter-y: 0.25rem}.sd-g-md-1,.sd-gx-md-1{--sd-gutter-x: 0.25rem}.sd-g-md-2,.sd-gy-md-2{--sd-gutter-y: 0.5rem}.sd-g-md-2,.sd-gx-md-2{--sd-gutter-x: 0.5rem}.sd-g-md-3,.sd-gy-md-3{--sd-gutter-y: 1rem}.sd-g-md-3,.sd-gx-md-3{--sd-gutter-x: 1rem}.sd-g-md-4,.sd-gy-md-4{--sd-gutter-y: 1.5rem}.sd-g-md-4,.sd-gx-md-4{--sd-gutter-x: 1.5rem}.sd-g-md-5,.sd-gy-md-5{--sd-gutter-y: 3rem}.sd-g-md-5,.sd-gx-md-5{--sd-gutter-x: 3rem}}@media(min-width: 992px){.sd-col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-lg-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-lg-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-lg-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-lg-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-lg-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-lg-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-lg-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-lg-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-lg-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-lg-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-lg-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-lg-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-lg-0,.sd-gy-lg-0{--sd-gutter-y: 0}.sd-g-lg-0,.sd-gx-lg-0{--sd-gutter-x: 0}.sd-g-lg-1,.sd-gy-lg-1{--sd-gutter-y: 0.25rem}.sd-g-lg-1,.sd-gx-lg-1{--sd-gutter-x: 0.25rem}.sd-g-lg-2,.sd-gy-lg-2{--sd-gutter-y: 0.5rem}.sd-g-lg-2,.sd-gx-lg-2{--sd-gutter-x: 0.5rem}.sd-g-lg-3,.sd-gy-lg-3{--sd-gutter-y: 1rem}.sd-g-lg-3,.sd-gx-lg-3{--sd-gutter-x: 1rem}.sd-g-lg-4,.sd-gy-lg-4{--sd-gutter-y: 1.5rem}.sd-g-lg-4,.sd-gx-lg-4{--sd-gutter-x: 1.5rem}.sd-g-lg-5,.sd-gy-lg-5{--sd-gutter-y: 3rem}.sd-g-lg-5,.sd-gx-lg-5{--sd-gutter-x: 3rem}}@media(min-width: 1200px){.sd-col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-xl-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-xl-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-xl-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-xl-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-xl-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-xl-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-xl-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-xl-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-xl-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-xl-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-xl-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-xl-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-xl-0,.sd-gy-xl-0{--sd-gutter-y: 0}.sd-g-xl-0,.sd-gx-xl-0{--sd-gutter-x: 0}.sd-g-xl-1,.sd-gy-xl-1{--sd-gutter-y: 0.25rem}.sd-g-xl-1,.sd-gx-xl-1{--sd-gutter-x: 0.25rem}.sd-g-xl-2,.sd-gy-xl-2{--sd-gutter-y: 0.5rem}.sd-g-xl-2,.sd-gx-xl-2{--sd-gutter-x: 0.5rem}.sd-g-xl-3,.sd-gy-xl-3{--sd-gutter-y: 1rem}.sd-g-xl-3,.sd-gx-xl-3{--sd-gutter-x: 1rem}.sd-g-xl-4,.sd-gy-xl-4{--sd-gutter-y: 1.5rem}.sd-g-xl-4,.sd-gx-xl-4{--sd-gutter-x: 1.5rem}.sd-g-xl-5,.sd-gy-xl-5{--sd-gutter-y: 3rem}.sd-g-xl-5,.sd-gx-xl-5{--sd-gutter-x: 3rem}}.sd-flex-row-reverse{flex-direction:row-reverse !important}details.sd-dropdown{position:relative}details.sd-dropdown .sd-summary-title{font-weight:700;padding-right:3em !important;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none}details.sd-dropdown:hover{cursor:pointer}details.sd-dropdown .sd-summary-content{cursor:default}details.sd-dropdown summary{list-style:none;padding:1em}details.sd-dropdown summary .sd-octicon.no-title{vertical-align:middle}details.sd-dropdown[open] summary .sd-octicon.no-title{visibility:hidden}details.sd-dropdown summary::-webkit-details-marker{display:none}details.sd-dropdown summary:focus{outline:none}details.sd-dropdown .sd-summary-icon{margin-right:.5em}details.sd-dropdown .sd-summary-icon svg{opacity:.8}details.sd-dropdown summary:hover .sd-summary-up svg,details.sd-dropdown summary:hover .sd-summary-down svg{opacity:1;transform:scale(1.1)}details.sd-dropdown .sd-summary-up svg,details.sd-dropdown .sd-summary-down svg{display:block;opacity:.6}details.sd-dropdown .sd-summary-up,details.sd-dropdown .sd-summary-down{pointer-events:none;position:absolute;right:1em;top:1em}details.sd-dropdown[open]>.sd-summary-title .sd-summary-down{visibility:hidden}details.sd-dropdown:not([open])>.sd-summary-title .sd-summary-up{visibility:hidden}details.sd-dropdown:not([open]).sd-card{border:none}details.sd-dropdown:not([open])>.sd-card-header{border:1px solid var(--sd-color-card-border);border-radius:.25rem}details.sd-dropdown.sd-fade-in[open] summary~*{-moz-animation:sd-fade-in .5s ease-in-out;-webkit-animation:sd-fade-in .5s ease-in-out;animation:sd-fade-in .5s ease-in-out}details.sd-dropdown.sd-fade-in-slide-down[open] summary~*{-moz-animation:sd-fade-in .5s ease-in-out,sd-slide-down .5s ease-in-out;-webkit-animation:sd-fade-in .5s ease-in-out,sd-slide-down .5s ease-in-out;animation:sd-fade-in .5s ease-in-out,sd-slide-down .5s ease-in-out}.sd-col>.sd-dropdown{width:100%}.sd-summary-content>.sd-tab-set:first-child{margin-top:0}@keyframes sd-fade-in{0%{opacity:0}100%{opacity:1}}@keyframes sd-slide-down{0%{transform:translate(0, -10px)}100%{transform:translate(0, 0)}}.sd-tab-set{border-radius:.125rem;display:flex;flex-wrap:wrap;margin:1em 0;position:relative}.sd-tab-set>input{opacity:0;position:absolute}.sd-tab-set>input:checked+label{border-color:var(--sd-color-tabs-underline-active);color:var(--sd-color-tabs-label-active)}.sd-tab-set>input:checked+label+.sd-tab-content{display:block}.sd-tab-set>input:not(:checked)+label:hover{color:var(--sd-color-tabs-label-hover);border-color:var(--sd-color-tabs-underline-hover)}.sd-tab-set>input:focus+label{outline-style:auto}.sd-tab-set>input:not(.focus-visible)+label{outline:none;-webkit-tap-highlight-color:transparent}.sd-tab-set>label{border-bottom:.125rem solid transparent;margin-bottom:0;color:var(--sd-color-tabs-label-inactive);border-color:var(--sd-color-tabs-underline-inactive);cursor:pointer;font-size:var(--sd-fontsize-tabs-label);font-weight:700;padding:1em 1.25em .5em;transition:color 250ms;width:auto;z-index:1}html .sd-tab-set>label:hover{color:var(--sd-color-tabs-label-active)}.sd-col>.sd-tab-set{width:100%}.sd-tab-content{box-shadow:0 -0.0625rem var(--sd-color-tabs-overline),0 .0625rem var(--sd-color-tabs-underline);display:none;order:99;padding-bottom:.75rem;padding-top:.75rem;width:100%}.sd-tab-content>:first-child{margin-top:0 !important}.sd-tab-content>:last-child{margin-bottom:0 !important}.sd-tab-content>.sd-tab-set{margin:0}.sd-sphinx-override,.sd-sphinx-override *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.sd-sphinx-override p{margin-top:0}:root{--sd-color-primary: #0071bc;--sd-color-secondary: #6c757d;--sd-color-success: #28a745;--sd-color-info: #17a2b8;--sd-color-warning: #f0b37e;--sd-color-danger: #dc3545;--sd-color-light: #f8f9fa;--sd-color-muted: #6c757d;--sd-color-dark: #212529;--sd-color-black: black;--sd-color-white: white;--sd-color-primary-highlight: #0060a0;--sd-color-secondary-highlight: #5c636a;--sd-color-success-highlight: #228e3b;--sd-color-info-highlight: #148a9c;--sd-color-warning-highlight: #cc986b;--sd-color-danger-highlight: #bb2d3b;--sd-color-light-highlight: #d3d4d5;--sd-color-muted-highlight: #5c636a;--sd-color-dark-highlight: #1c1f23;--sd-color-black-highlight: black;--sd-color-white-highlight: #d9d9d9;--sd-color-primary-text: #fff;--sd-color-secondary-text: #fff;--sd-color-success-text: #fff;--sd-color-info-text: #fff;--sd-color-warning-text: #212529;--sd-color-danger-text: #fff;--sd-color-light-text: #212529;--sd-color-muted-text: #fff;--sd-color-dark-text: #fff;--sd-color-black-text: #fff;--sd-color-white-text: #212529;--sd-color-shadow: rgba(0, 0, 0, 0.15);--sd-color-card-border: rgba(0, 0, 0, 0.125);--sd-color-card-border-hover: hsla(231, 99%, 66%, 1);--sd-color-card-background: transparent;--sd-color-card-text: inherit;--sd-color-card-header: transparent;--sd-color-card-footer: transparent;--sd-color-tabs-label-active: hsla(231, 99%, 66%, 1);--sd-color-tabs-label-hover: hsla(231, 99%, 66%, 1);--sd-color-tabs-label-inactive: hsl(0, 0%, 66%);--sd-color-tabs-underline-active: hsla(231, 99%, 66%, 1);--sd-color-tabs-underline-hover: rgba(178, 206, 245, 0.62);--sd-color-tabs-underline-inactive: transparent;--sd-color-tabs-overline: rgb(222, 222, 222);--sd-color-tabs-underline: rgb(222, 222, 222);--sd-fontsize-tabs-label: 1rem} diff --git a/_sphinx_design_static/design-tabs.js b/_sphinx_design_static/design-tabs.js new file mode 100644 index 0000000..36b38cf --- /dev/null +++ b/_sphinx_design_static/design-tabs.js @@ -0,0 +1,27 @@ +var sd_labels_by_text = {}; + +function ready() { + const li = document.getElementsByClassName("sd-tab-label"); + for (const label of li) { + syncId = label.getAttribute("data-sync-id"); + if (syncId) { + label.onclick = onLabelClick; + if (!sd_labels_by_text[syncId]) { + sd_labels_by_text[syncId] = []; + } + sd_labels_by_text[syncId].push(label); + } + } +} + +function onLabelClick() { + // Activate other inputs with the same sync id. + syncId = this.getAttribute("data-sync-id"); + for (label of sd_labels_by_text[syncId]) { + if (label === this) continue; + label.previousElementSibling.checked = true; + } + window.localStorage.setItem("sphinx-design-last-tab", syncId); +} + +document.addEventListener("DOMContentLoaded", ready, false); diff --git a/_static/_sphinx_javascript_frameworks_compat.js b/_static/_sphinx_javascript_frameworks_compat.js new file mode 100644 index 0000000..8141580 --- /dev/null +++ b/_static/_sphinx_javascript_frameworks_compat.js @@ -0,0 +1,123 @@ +/* Compatability shim for jQuery and underscores.js. + * + * Copyright Sphinx contributors + * Released under the two clause BSD licence + */ + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} diff --git a/_static/basic.css b/_static/basic.css new file mode 100644 index 0000000..30fee9d --- /dev/null +++ b/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/_static/build_flow.svg b/_static/build_flow.svg new file mode 100644 index 0000000..f3fd92e --- /dev/null +++ b/_static/build_flow.svg @@ -0,0 +1,16 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1daVfi2Lr+Xr/CVffLvedcdTAwMWM5e1x1MDAxZfrLXag4XHUwMDAwooI4cOssV4BcdTAwMDBRXGJTXHUwMDEwoVf/97s3KoRMhMlKVzfVbZVcdTAwMTkgJM/7vPO7f/92cPDdmfTM779cdTAwMWR8N99qRtuqXHUwMDBmjPH3f+ntr+ZgaHVttVx1MDAwYs1+XHUwMDFmdkeD2uzIluP0hr/9+9+LM1JWrZey7IZlm107Vet23t/CbJtcdTAwMWTTdobqpP9Tv1x1MDAxZlx1MDAxY/w+++n60IFZc1xmu9k2ZyfMdi0+V1x1MDAwMujdWujas2uAXHUwMDE0S8YgQ2x+hDU8UZ/nmHW1u2G0h+Zij970XHUwMDFkXHUwMDE3qseHPVx1MDAwYtR4r/H0fGJdWlx1MDAwNlx1MDAxMIuPbVjtdsmZtGeXNeyq77XYN3RcdTAwMDbdXHUwMDE3896qOy392Z7t87PqxrBluk5cdTAwMWJ0R82WbVx1MDAwZYdLJ3V7Rs1yJnpcdTAwMWJcdTAwMDDzre834beDxZY39ZvAi+83O0OKXHUwMDE09Xz8cbfdXHUwMDFk6I//L1wiKaB88flVo/bSVFx1MDAxN2HXXHUwMDE3xzSA/rM4ZvzxpTDF820t02q2XHUwMDFjtZFcdTAwMTGSXHUwMDAyrpfrm5uz+4xcdTAwMTHFXHUwMDEwSLC4Sv15vYv67JH/x3ujWsag93E/vlx1MDAwZvUvrmvVl5l5x8vH6Vx1MDAxZrvUTsRcdTAwMWGF3FNl+NLF+XapmpuKUsOen6tcdTAwMGWoo6pcdTAwMDOu0j3ZrzFBn4v1a5G5dFx1MDAxZpDOkvLg4fomd/VQKYGKNXrCReg+oPZ2k33swsar83A8rtDu02T4tPRcdTAwMGX8On9RXHUwMDAy9/h4NOg8PlXotdOu5b9/7P/P7O8//rVcdTAwMDHIXHUwMDA14WEgXHUwMDE3XHUwMDEyISpcdTAwMThDsTGORH3KbZyptthcdTAwMDXJZszK+Wv3eqdcdTAwMTj3nLVcdTAwMDOIM+aCtD6HYlx1MDAxY1x1MDAwMXIoXHUwMDE4MaqRIFx1MDAxZrcsx/RDXHUwMDFjXHUwMDExuYRmXHUwMDE3aueIVzfch3FBXHUwMDEwZlx1MDAxY4P9YnwlXHUwMDAwR09vo07nRZ5kjqdXV/XWc9ZsdNxcdTAwMDf0y0/nQ2c0vsiWb0sgc9hNT4pnq4Vge1xiM1x1MDAxYVxuYSSoQFx1MDAxMkJcdTAwMTJcdTAwMWLCj4/W09XJ+O753M61ipOXVlx1MDAxM1x1MDAxOIWE0zSEUnp5ei8sTVx0TUn3K4izuVx1MDAxN8GQU1x1MDAwMlx1MDAwMNozSa/kYPM1/XJcdTAwMDTI9LbaLL5cdTAwMGVcdTAwMWH5xlPu7HlcdTAwMDfwQyhcdTAwMTR+UCCEXHUwMDAwhyQ+hbbGTetcdTAwMTJcdTAwMGXB+cXVIbhLXHUwMDFi2d7RIdxcdTAwMDR/KFxmfzunUIhcdTAwMDT3cChi4VxmXG6qXFyJ5SZcZkpAXHUwMDAw4Fx1MDAxMGDLtIp9+INcdTAwMDBhqJTZnlx1MDAxMfh4Wm1b2ev64bhXPsn3ruWzeG65XHUwMDExmHtcdTAwMWXBQlx1MDAwNTxC6y1bt8XRuJ8hLFx1MDAxYYHvoCCD2/GNM35p0cp9fXqFMGKVo8VVRcBUP1x1MDAxZsK87IB9t1x1MDAxNivltCTZxH+nOYEh0u/Hhe+hz17rsc7PNVx1MDAxNzaFyFx1MDAwN+hcdTAwMTAgSnEj4Vx1MDAxMv1cdTAwMDVlYCy9W+eUwSRcdTAwMTFcdTAwMThQXHUwMDE2x7PYTqNvoI/f4Vir3j9cdTAwMTRMJ0/Ox+d3Jy+H6d7h3dRcdTAwMGZHx3xzPEhcdTAwMTRgXHSJXGL4kehcdTAwMTbTOfQw3S3WnIFhXHUwMDBme8ZA3bdcdTAwMTC8tYxaazQw/0SII5r7MFx1MDAxNoGAQ6GAY8o+klx1MDAxY3KxXHUwMDEx3uxRu7148PqBq+u7VtdndFtcdTAwMDfXg+6zoiTXXHUwMDFk7tpOyZq+W2ZLW0+NjtWeLN0k/W7pttXU1/+9bTacJeXrWMr5n+92um6SNoZm27Jnn0IjYNwwbk5uh/bL6232nrf7Lyc35/lBbFalXHUwMDAwKDZcdTAwMTSQXHUwMDAxwDFVd9CjXHUwMDAwgVx1MDAxZtlcdTAwMDKlMFFcdTAwMGVcdTAwMDdcdTAwMDPKx6A8QJlcdTAwMTHJU5BcdTAwMDPF0FpfcUTWwX0sy272+kU4VkJcdTAwMDQ5VtZlXHUwMDAw4ilcZqdYSDmh6v+NXHUwMDExXHUwMDFmXHUwMDAxqvurx9KEg/Z0XHUwMDA00lWrOL7s2vAtXHUwMDBlN1LOl1x1MDAxMcSRXHUwMDBmQW5HZmFcdTAwMDDtXHUwMDE4I79cIjcyXHUwMDAxMcMujnNRo8tU9UX5iGSYQLCZLlx1MDAwZeTG426n17XVMVx1MDAwNzCYXHUwMDE3wdLWPfFcIlx1MDAxNIFcdTAwMTCO5e9QXHUwMDE0fsOQkkag2Fx1MDAxMMS4YVx1MDAxZiT8anRe+lx1MDAxOXF3islF2nw8vVx1MDAwNeVcXFL4KdjfoWAhmfpcZi5oilxuxCVjQuFcZm1cdTAwMTkh9TB0qPZcYlximFx1MDAxMpKSkCurXG5DKFx1MDAxOKeIeFx1MDAwNUGJgfLEdVRkXHUwMDFmvlC0XHUwMDE3vcx5Llx1MDAwN5qFx9khXHUwMDA2jFx1MDAwMIjiO9Anhecsl7U3I+O08i+5w9Jr+ryYXHUwMDFjXCJcdTAwMGKBlMDLkJJ0u0B7IInP1Vxign7s+COPOtSp9Kske/GbXHUwMDE3p6/Nfss8WlxiYbiNXHQzQrv3ytZjOts/fKm0T/uF9FH65mTc8mv32TUsPWBGyPJcdTAwMDNmKEU4U1xcSpVcdTAwMDXJ/G5cdTAwMTDwPyEoUlx1MDAxNGHCiXJcdTAwMTAlZq5cdTAwMThfwn2ietdxNo14xkbc7Mv4XHUwMDE0P1x1MDAxNJBSSrhLI7mIh4faiJJLIVx1MDAwNY+lyFZo/l7Xcucg9Wvxr4PFM5v9Mv/3f/61+mhcdTAwMWYgXHUwMDE2Z3/zvMv3tjF0lMx0LP0orvUlea9z6Fx1MDAxOFx1MDAwM+fIsuuW3fTuM+16yJ7ZWenBoDtumUY94Dzvvlxi6SpiXHQ7ddB9vZF5YMLzK/iSLvilK8whw8tcdTAwMWOKXHUwMDAx90lcdTAwMTVy0cJCa8I1XHUwMDA0XHRVhYHNnyRIPzGcRVx1MDAwMOJcdTAwMTC5LGGXQch9IYd5/lx1MDAwNeo0OYpcdTAwMTX/XtvVXHUwMDFhyWrm+vKoWMlBxlx1MDAxZo+Iad91yrFcXC22XHUwMDFjhsKQuLTtXFxLsqBcdTAwMDTdOs7WX1x1MDAxNCtcbiZcdTAwMTKIJaPN5W5cdTAwMDGfXHUwMDEz9lx0XHUwMDE2KpUzz+LZejG9rWLp9uDUapvDg//+R2owdP5nXHUwMDE3PldNfaY5WGFE7NrrUtZ8qJHMlLJcIlx1MDAxOMdcblx1MDAxOb+LznR6e3V1n51mwah8nG5cdTAwMTRcdTAwMWXyXHUwMDE2SL6R7CV4V97XK3om43xcdTAwMTN7Z6EoeFhWaWGPXHUwMDAxtpxcdTAwMGZhXkkggFx1MDAwMySIK9K0d2fr/flmXHUwMDA3Ts146V+1XHUwMDFmJnd2plfOXGaO+1ZsTVxukfTEoqifXHUwMDFl103GxbBR105cZv7cbOiWNEk5XHUwMDA1XGJzXHUwMDFjXHUwMDE0lIL+tNG8KkdcdTAwMTBAKaabRS/3nK5cXOXqXHUwMDEz5DMgXHUwMDE2wTahK13oXHUwMDFhJXVV+vz0cEuG6d64fZkxW1x1MDAxM6uZrySexVxi8STLMYdcdTAwMTHO/rZMXHUwMDA2g1x1MDAwMkXcX2ZEXHUwMDE0nVwiXHT2XHUwMDEzXHUwMDE32lxc6+pcdTAwMGbId5V2PThWXHUwMDA3qVxy7/r8h61cdTAwMWNcdTAwMGaldX87+EdqYnTaP+z6xFbvMNvQm1x1MDAwNKpfXHUwMDE2XHUwMDEyXHUwMDEyiKXPI8hWjm6rR/eMpW+fM60puCs0qjggKFx1MDAxMOq2LEdcdTAwMDZcYlx1MDAwZsiJXHUwMDA2xv3XskVj1K39mrYox1hAXHUwMDFh5P5jXHUwMDEynlwiXHUwMDAymIqdsGxwXHUwMDE2PpJE31ElLttccvQysi5ANj16bVs3+c79Y7xE0nLRrjvpPidcdTAwMDW8bVwi6S+KJ4xcdTAwMDHnksiglCPiPpTN8SSVU1x1MDAwNFx1MDAxOaU7iCd9smJ6ODSdg1JtYPWcYfJySe8onuTGXHUwMDFkcV9B1ew5en6tnVxuzOrn8bmRLmda3KmnOZT5tvVcIn9RKCtLXHUwMDBiceEuNHJ76aHUiKVgTKdlNkZyXHUwMDA0XFzqtD/u39uFo3rZXHUwMDFhXHUwMDE2wElcdTAwMTHkXTXEXHUwMDExpMdcdTAwMDD2lXH7aY9cdTAwMDeYQn+zXlxmLUqBwIr1YJCvgpiPXGbnXHUwMDExXHUwMDFkJiQnYIdcdTAwMDGdXrvrXGaXXGa8PcZxOla93jbXIb1YoVx1MDAxYyxCQzlCcMwkXHUwMDE38X2gLMhb+exZXHUwMDE3Xtdey5OGfdyziiQ5wFx1MDAwYvGBvLzOwXYtXHUwMDE30Vx1MDAxZdBcblxy8cmHmEuE4H6aiTZzl7ErfuJcdTAwMTUtKIBkUsYp23tHXG5cdTAwMWZfXHUwMDBmKjWUXHUwMDFk1cvd51x1MDAxYbhrkSxcZoulJFx1MDAwNylcdTAwMTJ4eZ2zPWJFcD9U/JlxXHUwMDAxMZSAU7FcdTAwMTeobE5ts69nVJV/XHUwMDFj5lx1MDAwM4elxeNy4easR1AolqGyJyBQ+iU+mK23XHUwMDE351x0PZ2Tq5I9zt307oty+px4MNPlXCJRRuTPpz116zlBjMr9gHkz3qOh4c9ZXp7CNaKEdqWMjKer1mXhwbxcdTAwMDZ27tA5T5eSXHUwMDBlXHUwMDE1XHUwMDA2l5145TruXHUwMDExKiuiXHUwMDAxn0WUXGZcdTAwMTAs8H6SXHUwMDE525He//5cdTAwMTS+Wy7ziVx1MDAxN1iCXHUwMDE0QYH4XHUwMDFhXHUwMDFkifa4WyzeXlxcZIft24eqczko3YrejvG7TYVOXGKAXHRIMUaJMqgopHjZ3mOQpCSWVLmfQHmh1Jud2Vx1MDAxZLJcdTAwMDPKqFx1MDAxME5cdTAwMTFlPFHMlFdDXHUwMDE59HWVc0zV8yFyXHUwMDAz98lThFx1MDAxNFx09M3qbWLW8axTkTPft79KJd89X5z9zf33RpZcdTAwMDVHoVx1MDAwZaguyVVcdTAwMDYjjm9YvN2NLvH4cFJcdTAwMTHp64dC9qhYXHUwMDFiXHUwMDE5eJOM41x1MDAxN7awXHUwMDBij4UsttRcdTAwMTW7bWCHkmj7TrK9pJbmQEp4XHUwMDA3e5ipQ8OjJ1xiqJtGXHUwMDA0XFwjXHUwMDFhYHVvb8lpfZzrdVx1MDAwNtljo5S+zVx1MDAxNVtJt3WEy/Sc4Vfu08NTXG4plrGDOaWSJDFcdTAwMWQ6XHUwMDBi/Fx1MDAwZlx1MDAwZlx1MDAwZVx1MDAwZlx1MDAwMntJ9lhcdTAwMDNcdTAwMWSvUClialx1MDAwZUSIwKVs3CpAy9Jxn5G7fGN6z+32681cdTAwMTM0z8ImiiRcdTAwMDbQXHUwMDEyLFx1MDAxNyopLO3Tz1x1MDAwM2hlqZLf7UNQXHUwMDAyIFiSvD5cbsJL3HRcdTAwMGWXYUTjK/KTO3JcXLs54qV2XHUwMDExXHUwMDAx1EdccvuyMUo8cqDHUCZon8GuWExcYlx1MDAwNSWYUJ7I0lx1MDAxMJ1cdTAwMGaAqeFrc7fe35ZcdTAwMTRcYkPDXHUwMDE3kFxiJJTsrVx1MDAxMb9cdTAwMThNR6jTXHUwMDE5vTQzV4+1SteE7TNaTTyQvVx1MDAxNOhqcNq9RserptJcdTAwMDSEvVx1MDAwNKWEXGJJv7A2cyVcdTAwMDG6TFx1MDAxM1+IlHFFY/FaUT9wY/RbuCzG1Uc0ebXv6/JkZIVN10hcdTAwMGVusLc6jlx0uTfkQJetXHUwMDEyxYBcdTAwMDTp8jiQyEa4WbxcdTAwMWamWk6nnbCIP1x1MDAwYlx1MDAwZoMpXVx1MDAwZYlcXIdcdTAwMDXvnLFZsK4urcFZ8fjs8Y7A9HU58Wj2dlxus/1Fulx1MDAxMKR+LLvibnPek5hcdTAwMGLO9z0z8ZKlx81JdXpeb47RZbP0yC7br6ud7Y/9m/rSoZO4JEOAKVx1MDAxMzJ+XHUwMDFm8fSlbJZz+O3s6aFYLp3cd1Hp8jTxgMPQQ5+chY+C25o+SSz6VF40UUbPfuI/W7Jnx3SMuuFcdTAwMTip52HX/nr+XGbLIFBcdTAwMTLqXG5hLDmHOFZRzTuSz2tcdTAwMTJWKjdcdTAwMDa86PSfLnifX53JTOJcdTAwMTNcYlLCpVx1MDAwNMJy6pQjnlwiic0gqJ2CQkngXHUwMDA23vXfKYTgozdNIXyMLItcZlx1MDAwZc/l0dDfa7mykOuoN1HWt5BcXFx1MDAwN7GX87JU8Fx1MDAxNMdcbqBKYpFcdTAwMWXN4cOPQCnCMOZcdTAwMDQwJtW/XHUwMDAyguZRouCFc01cIlx1MDAwM1x1MDAxOZFw/rNcdTAwMGV5XG7p4Fx1MDAwN4pcdTAwMDAkXCKBg/QgpKFEyVx1MDAxOVx1MDAxN1KCXHJcdTAwMDfp7Vx1MDAwZtWhgPCc/c3zLuu38P8+f7PPUehcdTAwMTcxXHUwMDFhVmZKszbSX1x1MDAxN6RcdTAwMDCUglx0PctQ/VTWXHUwMDA1wa7DmkZPP4Ml8aBcXLKPI/5cYuaisKuKXHUwMDFlcum6qkN1WUxpQVx1MDAwNlx1MDAxOUBEciWDyH9VKEU1dDDAQlx1MDAwMm30+q5qLZb7oIZIbVx1MDAxZe1ccvnboVx1MDAxN91cdTAwMDFA/VnKkK3S6aetPH1hXHUwMDA1c9i+Nu/o+eRR3lqHSbdO1fPwVLXgfWZ6Nlx1MDAwYoxcdTAwMGJcdTAwMDR0pFx1MDAxOScpMI5DoSOp5JLwNVx1MDAxY2mDXHUwMDBlb1x1MDAwZTPVQfntaFgvXHUwMDE3MzawujT5yEHewHhUz+TWgfHoyVx1MDAwZvPeMiWylLurtZPj2OjAOEr17ERcdTAwMDXGqYyYe6tDQlSukeFcdTAwMTl1nvqgf31d6pnNxsnlbTvfLF7s1OrZw6h2XGaXXHUwMDFkXHUwMDE5ZVx1MDAwNURcdTAwMDA5xlx1MDAwNJGwYo1cdTAwMTWTbuaz2THRQ0w3KXZaJ1x1MDAxY1RcdTAwMWU0isXH/pA3T1x1MDAxZqcvxVxcTtz3S27lvrI447U/yV+8ZW+fj3rli5LVPnqeyIH7gMz4hlxyqm/XTXreemHTyjWr3OTcXHUwMDA3vEG7dPx4OH54y2fg+TSN317u09tFnHDE7HeijFx1MDAxMyyJiK/UX8+advtcdTAwMTiL+zFsXHIymcNyrj1cdKtUTUjtXHUwMDExxHi5eIO4Klx1MDAxYneH5lWzeFx1MDAxNmjGQmnL/TDyXHUwMDAySXbz/rRwNK5kr41cdTAwMGL6gFtcdTAwMGUr9421sLgh3H0+wUa6IWIqzs/XXHUwMDExzFUk71x1MDAxZi+KpETI1dm3spyvY1xcXHUwMDBlm/Q+Xa1I0K+ddsHwZJp0kYLLuVPEyc9UXHUwMDExTF3y8pySn25cdTAwMTBcdTAwMTNcdTAwMTC+4lx1MDAwYlx1MDAxNVxmXHUwMDAzXHUwMDAw40OkeNhsvfaPXHUwMDFjM4sv85m3e+uwbXeSXHUwMDBlXHUwMDEx4l21XGKJ8Fx1MDAxMOhcdTAwMTa8XHUwMDFitCxXXHUwMDAw7+oh6NTt4SaJdndHmrdmp9c2XHUwMDFjc/hFhPnR4mn1h8WiVWXTy4vL5oidVqadO3/s0t9cdTAwMTStYOKZNON6QvM1fVx1MDAwMsolXFzjYXZcdTAwMTKR3HhsU1x1MDAxMnuhXHUwMDExhMrhxjRwbpN/0MhiNTUsdet8XGZeitlcdTAwMGLdMSY/bMtumVx1MDAwM8v5YTdcdTAwMDZdN2tt3Fx1MDAxNT2YYSBcdTAwMWGsQehnUWMgou2hqPA7JFxipHS+i6nbLij1xFx1MDAwMbBcdTAwMWWUjFx0XHUwMDA3kDOiI6pcdTAwMDGTdFPKXHUwMDFllFhcYlxuXHThXFw57Fx1MDAwMT2euqk0+G3+wlx1MDAwMlx1MDAxMFx1MDAxNpBXt0mo+1x1MDAxODhcZlx1MDAwMIaWs0OImNJSYsOZOntMM4VcdTAwMDNEv1x1MDAwZX3YWLyhT7XsLEZcdTAwMWZcdTAwMWTcOFiK0Vx1MDAwM6qXs0CMM0pcdTAwMTTNXHUwMDAw4jrsI1x1MDAxYVx1MDAwZVKSUSSF4i0q9ajIjyPWjNFH20rLV4WIRFxch1x1MDAxNrDQIXhcdTAwMTJcdTAwMTSj913F18bkuVxmz1x1MDAxZSG9vCtep2CkNb3pt8vXRyOQu6g616hyc1x1MDAxM1anmZxwXHUwMDE0QN6CXHUwMDExsOU0ulBvY1x1MDAxN1x1MDAxMzU5oEwwslx1MDAxZmdkgc/gcNWntEdcdTAwMDIuNIhPQlx1MDAwN99CJZSIY0LiR/Gdy5Px6KxcdJhznjbLpjFcdTAwMWVcdTAwMDKU8GUzlF/rmYHvylx1MDAwNu1cdTAwMGVnMedcdTAwMWRcbkBcYlwicN8uy/CsW3iQ5YL1evVyPrVcbv00XHUwMDE5TnfkkSR8LGJ0iCvS5sN6nVEgiLL4JFx1MDAxMp6AOeEpSCRFkKqnp/5cdTAwMGJcdTAwMWGXXGLcp4OgKl9IdD1cdOVMsVxm5lx1MDAxY9FcdTAwMWRbfL9WXHUwMDExXHUwMDA24YQq2yFw7Fx1MDAxZFx093lcdTAwMTSxKbZeyi4kw+Q7XGaHiH75wfFcdTAwMTUmX3T4f8m4XCJEeUNYV75cdTAwMDG9XHUwMDAyhOviP0wr4ZFcdTAwMTG/pVx1MDAxNcvei+6GPliqyVD2vV7qXHUwMDE2XHUwMDAyRLXqxlx1MDAwMfbe/sy9d8qJbuBdQTk4pYDK9Vx1MDAxZFx1MDAwM8hcdTAwMTOBVVx1MDAxZUFcbqsjXHUwMDA04VRcYoFcdTAwMDMoR1ku6qFcdTAwMTCI1blQSFx1MDAxMqCEZjY4XHUwMDAwXGJcdTAwMDEsXHUwMDE4V6Kxzlx1MDAwMqp/OcpBQFCOMVxuXkUg3M1EjHKCY6VcYr6WcUJcdTAwMTEyO9+HjV0yTnhh6NaCr4MzXHUwMDE0U+XnqYvW5bo+qUcpjlx1MDAwNFx1MDAwNnpgXHUwMDE20KvVXGK/7/m1Xp+koVVcYkq+daPPXHUwMDFhxdVXoNmiXHUwMDE39vNwcGi+XFx3XHUwMDAwn9iX/aSIYKjT5+1cdTAwMTJQyv2nOX1EeHw+/6J1SlsjXHUwMDAw91Ros8DrylwihZU1XGLWoazfnOemb51cdTAwMTHkhNdcdTAwMWFOurOl38hcImJcdTAwMTRUz7ZcdTAwMDZrtNNcdTAwMTNhNqql7lxyf7iaVJuXx9lhhj0kXHUwMDFkrZ7Sfyq3WvYjzG1cZopcblx1MDAwNzVDK3KAXHUwMDEywu3zXHQ/zW8sKkI1XHUwMDA3iv8/fccvzWZlyOuwVcygXHUwMDE2mFbqw4czMjRcdTAwMDa230ZcdTAwMGLIZiHgiVx1MDAxZrhcIsmfj3HrNZt+YTsrOJ/FXHUwMDExVDYv9S/MMXOSI1x1MDAxYeqEMlIwjFOuXHUwMDE3M59l1Gr6SjdcdTAwMDf31jmsyFHmwVwiXHUwMDE5y7kgQKSE9tWUm6FMPE9W1j2EY05HKaRsKlx1MDAwZZVzKnWte0Bv5985qnDngTLlilxuXHUwMDA0gnxcdTAwMDdcdTAwMTSuUJVRXHUwMDBliHI7XHUwMDEy5zyEwmG292syUtEh54OlWFx1MDAwMFx1MDAwMFx1MDAxNEkkMdNjXHUwMDBiOFx1MDAwNYtO8LlbXHUwMDAwP1erXFy3TSTShDlYTkFxZThKXHQxU7dOXHUwMDE5S9x/XHUwMDE12HdcdTAwMTU7jklE24wraIOn9Ko8Qlx1MDAwMIKl9Kg/olxmZ8S55OruXG69JFRcdTAwMDCJ6M5cdTAwMDBB8GzGK1xmXFz0XHUwMDAzXCKh/DMlKZIqXHUwMDE3jbhcYv9vUvHafkRwQGVw4lx1MDAxYod37OqbXHUwMDBipeS70JU7pZVwfMz2epHxXHUwMDE1JLNGXGZcdTAwMTQyXHUwMDBlXHUwMDA0XHUwMDAxmDBMXHUwMDE0cVOfbMtl8fBHXHUwMDFmY/FNtIPvpT3OXHUwMDAwUGRDpSTK2aX+i6Ipuj/GWbnSOlxyn8HNXHUwMDA1XHUwMDE0XHUwMDE0c1x1MDAxY38wbdae1llcdTAwMTXhcTVrTMpyyDuyXHUwMDE1NpstKc4kgcu2l1x1MDAxMumIwMf6XHUwMDA0N5+oXHUwMDFjq25SzCxCvFx1MDAxZmdySy9xNFxmW/lic5t7I9CS8LVo9FxmO6ItovhNQ2mjzTk6KlZcdTAwMGI3942zUYXlrlx1MDAxZe9cdTAwMTNcdTAwMGVaJGnKXHUwMDBiW7FcdTAwMWRsfT7s3IGOXHUwMDA1XFw9xlFcdTAwMDC5p+mB21x1MDAwMdeya+1R/YvA+1x1MDAwZapGsXBXrJqEtfpdOaJP5Gr0NvTbeEExXHKX7M/KuF1zeeZmXFzQnL61Vrh6j71GXHUwMDAyYvWyRck110JcbnWV50FcdTAwMDRcIoFrlqPwWZNcdTAwMTRC5bLILdYs90HyetB9NmvOwdHIartuhlx1MDAxYt5cIlx1MDAwZby3XHUwMDBlcCBcdTAwMWFJwdHNm1wiauVuZY2JNeqXit1qaWydtTs3XHUwMDE1XHUwMDAyeieOQVxunbCh9lx0oWFcbkBKIKpcXFqp85LuUXz6XHKE7tIhXHUwMDEwS4FnVaLh7Vx1MDAxOLSmjMP6ZvSMXHUwMDA0SmFcIphcdTAwMWU1gpVvXHUwMDE4VOxEcEoq8Fx1MDAwM4whXHUwMDE0jFPkS6ggzKDUXHUwMDA23z7oe2HxXCLWKOSeKsOXLs63S9XcVJRcdTAwMWF2nLUvj69y1Vxcb1KtvHLSODqThaPyST5cdTAwMGWfMs/ANSHcrVMx556u5tNcdTAwMThP8NfjU4ZcdTAwMDTRa2RcdTAwMDX2PeBQx1x1MDAwMkFcdTAwMDaEoodcdTAwMWTSacl0XHUwMDFj5bTtJFS8h0Xg3jH8bFq33dZZ/86uT26GQ8rR6f2pXHUwMDFmw3XL6HTdwYaZWUCZJ+flSlkvpq95XHUwMDEyp0HRXHUwMDFkSlNcdTAwMDRcYiVcdTAwMTaQKsJwr1j1c9djTyC+sWZUjmhgjVx1MDAxYlx1MDAwZm1KXHUwMDE1QIFbqYTNYsZ7Xo/9o1j40Zne8O7FdHxcXFx1MDAxYo/HjUp3clx1MDAxZVCJXHUwMDE5YJ5SsVxcXGKlXHUwMDE0fVxuXHUwMDAxglx1MDAwMVx1MDAwNEqvuEezRo5UXauf7C9cdTAwMDY7xlx1MDAxNe6W5vnF6nOVXGYoS1x1MDAwM224XGbrSth9osrHvee3l/lcdTAwMWb2cddutEemupRcdTAwMWZ23rg1XHUwMDFmvoSKN2o9uyTPTSsz6lx1MDAxNWxYuK9cdTAwMWKs9Fx1MDAwMJtcdTAwMDGtZ0Hgx8LjdrvXY1xctMhcdTAwMDY0UEKQXHUwMDE0xHvN0Fx1MDAwNCBe+bNYXHUwMDAySPw1fN9n4y1DLVx0ZYBcYlx1MDAwNjdcZqO7IFx1MDAxZk2kPsh/dP0qi+OH/c+DXHUwMDFmdqnXsuy33TlzXHUwMDFi415u483J8FAwpYRj5c/Ed+eG6eOsXX1yXu9cdTAwMGadnCGPxH3zuFx1MDAxZVx1MDAwMtfEXGaV8q5KxiNH8VxihkxZW9/mX3PaXHUwMDAyXHUwMDAyUqr/3EX1e/HN9rCUkSu/6oVcdTAwMTRcdTAwMTNIMkjXmMrwWFx1MDAxZZdyzeJVo3Xfssd5LIfHhbC6yuQgintcdTAwMTBcdTAwMTUxXGZnWzxBXHUwMDEya/VcdTAwMGVcdFx1MDAwNNWLSidxRlnRXHUwMDFjdkeDmvneu/RFVWgr01xyKLxPXHUwMDBmzVx1MDAxYVr4OnNcdTAwMWGPSiev3UmVvbZbpcfbN3ZNblx1MDAxMlx1MDAxM1x1MDAxMFxiW07OM35cdTAwMDZG1Vx1MDAwNm9cdTAwMWFaXiwqXHUwMDE34C/4pzIqXHUwMDAywUDi/bBiMIxjKXH9XHUwMDAxs/W4XHUwMDBlzkzbXHUwMDFjKCshZIy4SzJ3mzbzXHUwMDE0jrhiMoiFOq2QMJ1cdTAwMTJfZ9pesMJINJI51ZFQwVxiZ1j9cHmrnyPEIdSRVCSVYVx1MDAwZsNcdTAwMTeo2SafxmVcblx1MDAxM8pcdTAwMTRcdTAwMGIjXGJcdTAwMDRiXHUwMDAxRlx1MDAwMEpcdTAwMDFGiICSXHUwMDAzLCCWLjX6WaMpXGKSiPNcctaq2WKceFhFRLSxd7DcXHUwMDE0puxcdTAwMTk9wFmxptJEKKBcdTAwMDLL84iA8Fx1MDAwZurd+fhgglx1MDAxOEdQ53iwjmbQgKHGJIVcdTAwMThBVFx1MDAxOS1cXP+Qvova8Vx1MDAxNHWvXHUwMDFj77TQJ1x1MDAxNIT65Yff4u1cIu3Bd15cdTAwMDDnjcdcdTAwMWVcdTAwMTiU2cnzkXy2TvKsa1x1MDAwN1x1MDAwNJeWVzp4e1x1MDAwN/WSPFxuV13/p/RcdTAwMTCJdC5cdTAwMDNyXGaUu1xiXFxcdTAwMDb7RsPTY2VegP7zp3O1w0rWsCZcdTAwMGX/ulJa8MJXS1FCq/xD6V6YM1x1MDAxOVx1MDAxNWuhePCc/c3zLrtsmdtupYRcYkni1/mLXHUwMDEyuMfHo0Hn8alCr512LSDrXHUwMDE1VClcbjzrXHUwMDA2ioCS8oBMId1x0utXXHUwMDEzXHUwMDFlwlx1MDAxNeBE4KSv8CmVglGoMFx1MDAxYWvu65dcdTAwMTaRLzWcXHUwMDFlUro4YZ/iXHUwMDEypq9xoXp82LNAjfdcdTAwMWFPzyfWpWVcdTAwMDBcdTAwMTFS6Vx09Fx1MDAxYVx1MDAwNJgzvWxcdTAwMDJ0LS148GWd5cGNfbFkXHUwMDEzueqFw/RcdTAwMWNCKaw9XHUwMDFjZZNcdTAwMTNBdC9tgJnIsLJJ1F1AurRcdTAwMWW5XHUwMDFhdv6WXb/sQuXGSiWKQZpcdTAwMGbB8KnNlFxuXHUwMDAwXHUwMDE4StyqIeFcYtGvQ1x1MDAxZjh+rnDHLppcdTAwMDYpZXZyZWR//vRXTKN9Vky/S7f5mn45XHUwMDAyZHpbbVx1MDAxNl9cdTAwMDeNfOMpd/ZcdTAwMWNXurlHuv1cdTAwMDV8XHUwMDAxiXmyzuyHv5706lhcdTAwMGYngFx1MDAwN+biw6NcdTAwMWGMYFxiMYy1XHUwMDFhxpearUuySn7ybIfHR+vp6mR893xu51rFyUurXHSMQrBwUqItXHUwMDE5ZdBwXTAmmV849yeaK1x1MDAwN6pJX2HnXHUwMDFjXGK6TYfpcYcxgPAxvW+cXHUwMDE52M+5yoPjnFFhoTuja50nPd8gPLOxKKF7XFxcdTAwMTUlMIPlzzhoLcQpTuRy4bNQ7fDg8ECJWK9rq/c9KLhvyMq8Q9ts7Cdai8NcdTAwMTcyU2SorFx1MDAxMvfk21VgXHUwMDBlTnBvoFx1MDAxYr6wyUFZNMqC4ZxCKjlmnvXDXHRjqZkrhvSMXHUwMDFjxfGhKN+8Z1x1MDAwN6WU1mFcdTAwMWNcdTAwMTFcdTAwMDIgV1pcdTAwMDRcdTAwMDXMXHUwMDEyXHUwMDEwXCKFpFJPXHUwMDE0carzar71dblcdTAwMTRSQpmMSG124NSMl/5V+2FyZ2d65czguG+FxESVbyWh4EjdYlx1MDAwNFx1MDAxOfRcdTAwMDdq6dJcdTAwMDNQutlvlsVSP9GFkUvqXHUwMDA3UCBcdTAwMTGUulx1MDAwN1EqSlx0iNPilF5cdTAwMDFcdTAwMGJJXHUwMDAwXHUwMDAw51x1MDAwNFNEfFx1MDAxN/VnXG7UhkNQv3zg89tcdTAwMTFcdTAwMWJpUoTDhzaTWWeou+d1XHUwMDE1+Ty8PZ5cdTAwMGVG03p2RJ+f0qLJJ81jknDyoVj6OqyiikE2J1x1MDAxOVx1MDAxMeTZ+/ur9Hg/IVx1MDAwMEpi2r75nuncV4fVt1x1MDAwZun4bvR6JUd/zieXfH+1zPFRePLt21x1MDAwN/Q1xkz9TX//49tcdTAwMWb/XHUwMDBm5n1cdTAwMWGRIn0= + + + + Pharaoh ProjectComponent 1Component NRST Files (*.rst)Local Context Filesstatic: *.yamldynamic: *.pyAsset Scriptsplots.pytables.py?.pyAssets - Component 1plot1.svgtable1.htmlmetadata.jsonplot2.pngRST Files (*.rst)RST TemplatesmayinheritfromLocal Context Filesstatic: *.yamldynamic: *.pyRendering ContextaccessuseincludeProject BuildSettingsHTMLConfluenceLaTeXTemplating+ Sphinx BuildResource FilesAsset GenerationAssets - Component Ngenerate diff --git a/_static/check-solid.svg b/_static/check-solid.svg new file mode 100644 index 0000000..92fad4b --- /dev/null +++ b/_static/check-solid.svg @@ -0,0 +1,4 @@ + + + + diff --git a/_static/chirping_crickets.gif b/_static/chirping_crickets.gif new file mode 100644 index 0000000..39fa040 Binary files /dev/null and b/_static/chirping_crickets.gif differ diff --git a/_static/clipboard.min.js b/_static/clipboard.min.js new file mode 100644 index 0000000..54b3c46 --- /dev/null +++ b/_static/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.8 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return o}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),c=n.n(e);function a(t){try{return document.execCommand(t)}catch(t){return}}var f=function(t){t=c()(t);return a("cut"),t};var l=function(t){var e,n,o,r=1 + + + + diff --git a/_static/copybutton.css b/_static/copybutton.css new file mode 100644 index 0000000..f1916ec --- /dev/null +++ b/_static/copybutton.css @@ -0,0 +1,94 @@ +/* Copy buttons */ +button.copybtn { + position: absolute; + display: flex; + top: .3em; + right: .3em; + width: 1.7em; + height: 1.7em; + opacity: 0; + transition: opacity 0.3s, border .3s, background-color .3s; + user-select: none; + padding: 0; + border: none; + outline: none; + border-radius: 0.4em; + /* The colors that GitHub uses */ + border: #1b1f2426 1px solid; + background-color: #f6f8fa; + color: #57606a; +} + +button.copybtn.success { + border-color: #22863a; + color: #22863a; +} + +button.copybtn svg { + stroke: currentColor; + width: 1.5em; + height: 1.5em; + padding: 0.1em; +} + +div.highlight { + position: relative; +} + +/* Show the copybutton */ +.highlight:hover button.copybtn, button.copybtn.success { + opacity: 1; +} + +.highlight button.copybtn:hover { + background-color: rgb(235, 235, 235); +} + +.highlight button.copybtn:active { + background-color: rgb(187, 187, 187); +} + +/** + * A minimal CSS-only tooltip copied from: + * https://codepen.io/mildrenben/pen/rVBrpK + * + * To use, write HTML like the following: + * + *

Short

+ */ + .o-tooltip--left { + position: relative; + } + + .o-tooltip--left:after { + opacity: 0; + visibility: hidden; + position: absolute; + content: attr(data-tooltip); + padding: .2em; + font-size: .8em; + left: -.2em; + background: grey; + color: white; + white-space: nowrap; + z-index: 2; + border-radius: 2px; + transform: translateX(-102%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); +} + +.o-tooltip--left:hover:after { + display: block; + opacity: 1; + visibility: visible; + transform: translateX(-100%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); + transition-delay: .5s; +} + +/* By default the copy button shouldn't show up when printing a page */ +@media print { + button.copybtn { + display: none; + } +} diff --git a/_static/copybutton.js b/_static/copybutton.js new file mode 100644 index 0000000..2ea7ff3 --- /dev/null +++ b/_static/copybutton.js @@ -0,0 +1,248 @@ +// Localization support +const messages = { + 'en': { + 'copy': 'Copy', + 'copy_to_clipboard': 'Copy to clipboard', + 'copy_success': 'Copied!', + 'copy_failure': 'Failed to copy', + }, + 'es' : { + 'copy': 'Copiar', + 'copy_to_clipboard': 'Copiar al portapapeles', + 'copy_success': '¡Copiado!', + 'copy_failure': 'Error al copiar', + }, + 'de' : { + 'copy': 'Kopieren', + 'copy_to_clipboard': 'In die Zwischenablage kopieren', + 'copy_success': 'Kopiert!', + 'copy_failure': 'Fehler beim Kopieren', + }, + 'fr' : { + 'copy': 'Copier', + 'copy_to_clipboard': 'Copier dans le presse-papier', + 'copy_success': 'Copié !', + 'copy_failure': 'Échec de la copie', + }, + 'ru': { + 'copy': 'Скопировать', + 'copy_to_clipboard': 'Скопировать в буфер', + 'copy_success': 'Скопировано!', + 'copy_failure': 'Не удалось скопировать', + }, + 'zh-CN': { + 'copy': '复制', + 'copy_to_clipboard': '复制到剪贴板', + 'copy_success': '复制成功!', + 'copy_failure': '复制失败', + }, + 'it' : { + 'copy': 'Copiare', + 'copy_to_clipboard': 'Copiato negli appunti', + 'copy_success': 'Copiato!', + 'copy_failure': 'Errore durante la copia', + } +} + +let locale = 'en' +if( document.documentElement.lang !== undefined + && messages[document.documentElement.lang] !== undefined ) { + locale = document.documentElement.lang +} + +let doc_url_root = DOCUMENTATION_OPTIONS.URL_ROOT; +if (doc_url_root == '#') { + doc_url_root = ''; +} + +/** + * SVG files for our copy buttons + */ +let iconCheck = ` + ${messages[locale]['copy_success']} + + +` + +// If the user specified their own SVG use that, otherwise use the default +let iconCopy = ``; +if (!iconCopy) { + iconCopy = ` + ${messages[locale]['copy_to_clipboard']} + + + +` +} + +/** + * Set up copy/paste for code blocks + */ + +const runWhenDOMLoaded = cb => { + if (document.readyState != 'loading') { + cb() + } else if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', cb) + } else { + document.attachEvent('onreadystatechange', function() { + if (document.readyState == 'complete') cb() + }) + } +} + +const codeCellId = index => `codecell${index}` + +// Clears selected text since ClipboardJS will select the text when copying +const clearSelection = () => { + if (window.getSelection) { + window.getSelection().removeAllRanges() + } else if (document.selection) { + document.selection.empty() + } +} + +// Changes tooltip text for a moment, then changes it back +// We want the timeout of our `success` class to be a bit shorter than the +// tooltip and icon change, so that we can hide the icon before changing back. +var timeoutIcon = 2000; +var timeoutSuccessClass = 1500; + +const temporarilyChangeTooltip = (el, oldText, newText) => { + el.setAttribute('data-tooltip', newText) + el.classList.add('success') + // Remove success a little bit sooner than we change the tooltip + // So that we can use CSS to hide the copybutton first + setTimeout(() => el.classList.remove('success'), timeoutSuccessClass) + setTimeout(() => el.setAttribute('data-tooltip', oldText), timeoutIcon) +} + +// Changes the copy button icon for two seconds, then changes it back +const temporarilyChangeIcon = (el) => { + el.innerHTML = iconCheck; + setTimeout(() => {el.innerHTML = iconCopy}, timeoutIcon) +} + +const addCopyButtonToCodeCells = () => { + // If ClipboardJS hasn't loaded, wait a bit and try again. This + // happens because we load ClipboardJS asynchronously. + if (window.ClipboardJS === undefined) { + setTimeout(addCopyButtonToCodeCells, 250) + return + } + + // Add copybuttons to all of our code cells + const COPYBUTTON_SELECTOR = 'div.highlight pre'; + const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR) + codeCells.forEach((codeCell, index) => { + const id = codeCellId(index) + codeCell.setAttribute('id', id) + + const clipboardButton = id => + `` + codeCell.insertAdjacentHTML('afterend', clipboardButton(id)) + }) + +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} + + +var copyTargetText = (trigger) => { + var target = document.querySelector(trigger.attributes['data-clipboard-target'].value); + + // get filtered text + let exclude = '.linenos'; + + let text = filterText(target, exclude); + return formatCopyText(text, '', false, true, true, true, '', '') +} + + // Initialize with a callback so we can modify the text before copy + const clipboard = new ClipboardJS('.copybtn', {text: copyTargetText}) + + // Update UI with error/success messages + clipboard.on('success', event => { + clearSelection() + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_success']) + temporarilyChangeIcon(event.trigger) + }) + + clipboard.on('error', event => { + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_failure']) + }) +} + +runWhenDOMLoaded(addCopyButtonToCodeCells) \ No newline at end of file diff --git a/_static/copybutton_funcs.js b/_static/copybutton_funcs.js new file mode 100644 index 0000000..dbe1aaa --- /dev/null +++ b/_static/copybutton_funcs.js @@ -0,0 +1,73 @@ +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +export function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +export function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} diff --git a/_static/css/badge_only.css b/_static/css/badge_only.css new file mode 100644 index 0000000..c718cee --- /dev/null +++ b/_static/css/badge_only.css @@ -0,0 +1 @@ +.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} \ No newline at end of file diff --git a/_static/css/fonts/Roboto-Slab-Bold.woff b/_static/css/fonts/Roboto-Slab-Bold.woff new file mode 100644 index 0000000..6cb6000 Binary files /dev/null and b/_static/css/fonts/Roboto-Slab-Bold.woff differ diff --git a/_static/css/fonts/Roboto-Slab-Bold.woff2 b/_static/css/fonts/Roboto-Slab-Bold.woff2 new file mode 100644 index 0000000..7059e23 Binary files /dev/null and b/_static/css/fonts/Roboto-Slab-Bold.woff2 differ diff --git a/_static/css/fonts/Roboto-Slab-Regular.woff b/_static/css/fonts/Roboto-Slab-Regular.woff new file mode 100644 index 0000000..f815f63 Binary files /dev/null and b/_static/css/fonts/Roboto-Slab-Regular.woff differ diff --git a/_static/css/fonts/Roboto-Slab-Regular.woff2 b/_static/css/fonts/Roboto-Slab-Regular.woff2 new file mode 100644 index 0000000..f2c76e5 Binary files /dev/null and b/_static/css/fonts/Roboto-Slab-Regular.woff2 differ diff --git a/_static/css/fonts/fontawesome-webfont.eot b/_static/css/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000..e9f60ca Binary files /dev/null and b/_static/css/fonts/fontawesome-webfont.eot differ diff --git a/_static/css/fonts/fontawesome-webfont.svg b/_static/css/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000..855c845 --- /dev/null +++ b/_static/css/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_static/css/fonts/fontawesome-webfont.ttf b/_static/css/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000..35acda2 Binary files /dev/null and b/_static/css/fonts/fontawesome-webfont.ttf differ diff --git a/_static/css/fonts/fontawesome-webfont.woff b/_static/css/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000..400014a Binary files /dev/null and b/_static/css/fonts/fontawesome-webfont.woff differ diff --git a/_static/css/fonts/fontawesome-webfont.woff2 b/_static/css/fonts/fontawesome-webfont.woff2 new file mode 100644 index 0000000..4d13fc6 Binary files /dev/null and b/_static/css/fonts/fontawesome-webfont.woff2 differ diff --git a/_static/css/fonts/lato-bold-italic.woff b/_static/css/fonts/lato-bold-italic.woff new file mode 100644 index 0000000..88ad05b Binary files /dev/null and b/_static/css/fonts/lato-bold-italic.woff differ diff --git a/_static/css/fonts/lato-bold-italic.woff2 b/_static/css/fonts/lato-bold-italic.woff2 new file mode 100644 index 0000000..c4e3d80 Binary files /dev/null and b/_static/css/fonts/lato-bold-italic.woff2 differ diff --git a/_static/css/fonts/lato-bold.woff b/_static/css/fonts/lato-bold.woff new file mode 100644 index 0000000..c6dff51 Binary files /dev/null and b/_static/css/fonts/lato-bold.woff differ diff --git a/_static/css/fonts/lato-bold.woff2 b/_static/css/fonts/lato-bold.woff2 new file mode 100644 index 0000000..bb19504 Binary files /dev/null and b/_static/css/fonts/lato-bold.woff2 differ diff --git a/_static/css/fonts/lato-normal-italic.woff b/_static/css/fonts/lato-normal-italic.woff new file mode 100644 index 0000000..76114bc Binary files /dev/null and b/_static/css/fonts/lato-normal-italic.woff differ diff --git a/_static/css/fonts/lato-normal-italic.woff2 b/_static/css/fonts/lato-normal-italic.woff2 new file mode 100644 index 0000000..3404f37 Binary files /dev/null and b/_static/css/fonts/lato-normal-italic.woff2 differ diff --git a/_static/css/fonts/lato-normal.woff b/_static/css/fonts/lato-normal.woff new file mode 100644 index 0000000..ae1307f Binary files /dev/null and b/_static/css/fonts/lato-normal.woff differ diff --git a/_static/css/fonts/lato-normal.woff2 b/_static/css/fonts/lato-normal.woff2 new file mode 100644 index 0000000..3bf9843 Binary files /dev/null and b/_static/css/fonts/lato-normal.woff2 differ diff --git a/_static/css/theme.css b/_static/css/theme.css new file mode 100644 index 0000000..19a446a --- /dev/null +++ b/_static/css/theme.css @@ -0,0 +1,4 @@ +html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search>a:hover{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .citation-reference>span.fn-bracket,.rst-content .footnote-reference>span.fn-bracket{display:none}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:auto minmax(80%,95%)}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{display:inline-grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{display:grid;grid-template-columns:auto auto minmax(.65rem,auto) minmax(40%,95%)}html.writer-html5 .rst-content aside.citation>span.label,html.writer-html5 .rst-content aside.footnote>span.label,html.writer-html5 .rst-content div.citation>span.label{grid-column-start:1;grid-column-end:2}html.writer-html5 .rst-content aside.citation>span.backrefs,html.writer-html5 .rst-content aside.footnote>span.backrefs,html.writer-html5 .rst-content div.citation>span.backrefs{grid-column-start:2;grid-column-end:3;grid-row-start:1;grid-row-end:3}html.writer-html5 .rst-content aside.citation>p,html.writer-html5 .rst-content aside.footnote>p,html.writer-html5 .rst-content div.citation>p{grid-column-start:4;grid-column-end:5}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{margin-bottom:24px}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a{word-break:keep-all}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{padding-left:1rem;padding-right:1rem;font-size:.9rem;line-height:1.2rem}html.writer-html5 .rst-content aside.citation p,html.writer-html5 .rst-content aside.footnote p,html.writer-html5 .rst-content div.citation p{font-size:.9rem;line-height:1.2rem;margin-bottom:12px}html.writer-html5 .rst-content aside.citation span.backrefs,html.writer-html5 .rst-content aside.footnote span.backrefs,html.writer-html5 .rst-content div.citation span.backrefs{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content aside.citation span.backrefs>a,html.writer-html5 .rst-content aside.footnote span.backrefs>a,html.writer-html5 .rst-content div.citation span.backrefs>a{word-break:keep-all}html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content aside.citation span.label,html.writer-html5 .rst-content aside.footnote span.label,html.writer-html5 .rst-content div.citation span.label{line-height:1.2rem}html.writer-html5 .rst-content aside.citation-list,html.writer-html5 .rst-content aside.footnote-list,html.writer-html5 .rst-content div.citation-list{margin-bottom:24px}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content aside.footnote-list aside.footnote,html.writer-html5 .rst-content div.citation-list>div.citation,html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content aside.footnote-list aside.footnote code,html.writer-html5 .rst-content aside.footnote-list aside.footnote tt,html.writer-html5 .rst-content aside.footnote code,html.writer-html5 .rst-content aside.footnote tt,html.writer-html5 .rst-content div.citation-list>div.citation code,html.writer-html5 .rst-content div.citation-list>div.citation tt,html.writer-html5 .rst-content dl.citation code,html.writer-html5 .rst-content dl.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel,.rst-content .menuselection{font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .guilabel,.rst-content .menuselection{border:1px solid #7fbbe3;background:#e7f2fa}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} \ No newline at end of file diff --git a/_static/design-style.1e8bd061cd6da7fc9cf755528e8ffc24.min.css b/_static/design-style.1e8bd061cd6da7fc9cf755528e8ffc24.min.css new file mode 100644 index 0000000..eb19f69 --- /dev/null +++ b/_static/design-style.1e8bd061cd6da7fc9cf755528e8ffc24.min.css @@ -0,0 +1 @@ +.sd-bg-primary{background-color:var(--sd-color-primary) !important}.sd-bg-text-primary{color:var(--sd-color-primary-text) !important}button.sd-bg-primary:focus,button.sd-bg-primary:hover{background-color:var(--sd-color-primary-highlight) !important}a.sd-bg-primary:focus,a.sd-bg-primary:hover{background-color:var(--sd-color-primary-highlight) !important}.sd-bg-secondary{background-color:var(--sd-color-secondary) !important}.sd-bg-text-secondary{color:var(--sd-color-secondary-text) !important}button.sd-bg-secondary:focus,button.sd-bg-secondary:hover{background-color:var(--sd-color-secondary-highlight) !important}a.sd-bg-secondary:focus,a.sd-bg-secondary:hover{background-color:var(--sd-color-secondary-highlight) !important}.sd-bg-success{background-color:var(--sd-color-success) !important}.sd-bg-text-success{color:var(--sd-color-success-text) !important}button.sd-bg-success:focus,button.sd-bg-success:hover{background-color:var(--sd-color-success-highlight) !important}a.sd-bg-success:focus,a.sd-bg-success:hover{background-color:var(--sd-color-success-highlight) !important}.sd-bg-info{background-color:var(--sd-color-info) !important}.sd-bg-text-info{color:var(--sd-color-info-text) !important}button.sd-bg-info:focus,button.sd-bg-info:hover{background-color:var(--sd-color-info-highlight) !important}a.sd-bg-info:focus,a.sd-bg-info:hover{background-color:var(--sd-color-info-highlight) !important}.sd-bg-warning{background-color:var(--sd-color-warning) !important}.sd-bg-text-warning{color:var(--sd-color-warning-text) !important}button.sd-bg-warning:focus,button.sd-bg-warning:hover{background-color:var(--sd-color-warning-highlight) !important}a.sd-bg-warning:focus,a.sd-bg-warning:hover{background-color:var(--sd-color-warning-highlight) !important}.sd-bg-danger{background-color:var(--sd-color-danger) !important}.sd-bg-text-danger{color:var(--sd-color-danger-text) !important}button.sd-bg-danger:focus,button.sd-bg-danger:hover{background-color:var(--sd-color-danger-highlight) !important}a.sd-bg-danger:focus,a.sd-bg-danger:hover{background-color:var(--sd-color-danger-highlight) !important}.sd-bg-light{background-color:var(--sd-color-light) !important}.sd-bg-text-light{color:var(--sd-color-light-text) !important}button.sd-bg-light:focus,button.sd-bg-light:hover{background-color:var(--sd-color-light-highlight) !important}a.sd-bg-light:focus,a.sd-bg-light:hover{background-color:var(--sd-color-light-highlight) !important}.sd-bg-muted{background-color:var(--sd-color-muted) !important}.sd-bg-text-muted{color:var(--sd-color-muted-text) !important}button.sd-bg-muted:focus,button.sd-bg-muted:hover{background-color:var(--sd-color-muted-highlight) !important}a.sd-bg-muted:focus,a.sd-bg-muted:hover{background-color:var(--sd-color-muted-highlight) !important}.sd-bg-dark{background-color:var(--sd-color-dark) !important}.sd-bg-text-dark{color:var(--sd-color-dark-text) !important}button.sd-bg-dark:focus,button.sd-bg-dark:hover{background-color:var(--sd-color-dark-highlight) !important}a.sd-bg-dark:focus,a.sd-bg-dark:hover{background-color:var(--sd-color-dark-highlight) !important}.sd-bg-black{background-color:var(--sd-color-black) !important}.sd-bg-text-black{color:var(--sd-color-black-text) !important}button.sd-bg-black:focus,button.sd-bg-black:hover{background-color:var(--sd-color-black-highlight) !important}a.sd-bg-black:focus,a.sd-bg-black:hover{background-color:var(--sd-color-black-highlight) !important}.sd-bg-white{background-color:var(--sd-color-white) !important}.sd-bg-text-white{color:var(--sd-color-white-text) !important}button.sd-bg-white:focus,button.sd-bg-white:hover{background-color:var(--sd-color-white-highlight) !important}a.sd-bg-white:focus,a.sd-bg-white:hover{background-color:var(--sd-color-white-highlight) !important}.sd-text-primary,.sd-text-primary>p{color:var(--sd-color-primary) !important}a.sd-text-primary:focus,a.sd-text-primary:hover{color:var(--sd-color-primary-highlight) !important}.sd-text-secondary,.sd-text-secondary>p{color:var(--sd-color-secondary) !important}a.sd-text-secondary:focus,a.sd-text-secondary:hover{color:var(--sd-color-secondary-highlight) !important}.sd-text-success,.sd-text-success>p{color:var(--sd-color-success) !important}a.sd-text-success:focus,a.sd-text-success:hover{color:var(--sd-color-success-highlight) !important}.sd-text-info,.sd-text-info>p{color:var(--sd-color-info) !important}a.sd-text-info:focus,a.sd-text-info:hover{color:var(--sd-color-info-highlight) !important}.sd-text-warning,.sd-text-warning>p{color:var(--sd-color-warning) !important}a.sd-text-warning:focus,a.sd-text-warning:hover{color:var(--sd-color-warning-highlight) !important}.sd-text-danger,.sd-text-danger>p{color:var(--sd-color-danger) !important}a.sd-text-danger:focus,a.sd-text-danger:hover{color:var(--sd-color-danger-highlight) !important}.sd-text-light,.sd-text-light>p{color:var(--sd-color-light) !important}a.sd-text-light:focus,a.sd-text-light:hover{color:var(--sd-color-light-highlight) !important}.sd-text-muted,.sd-text-muted>p{color:var(--sd-color-muted) !important}a.sd-text-muted:focus,a.sd-text-muted:hover{color:var(--sd-color-muted-highlight) !important}.sd-text-dark,.sd-text-dark>p{color:var(--sd-color-dark) !important}a.sd-text-dark:focus,a.sd-text-dark:hover{color:var(--sd-color-dark-highlight) !important}.sd-text-black,.sd-text-black>p{color:var(--sd-color-black) !important}a.sd-text-black:focus,a.sd-text-black:hover{color:var(--sd-color-black-highlight) !important}.sd-text-white,.sd-text-white>p{color:var(--sd-color-white) !important}a.sd-text-white:focus,a.sd-text-white:hover{color:var(--sd-color-white-highlight) !important}.sd-outline-primary{border-color:var(--sd-color-primary) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-primary:focus,a.sd-outline-primary:hover{border-color:var(--sd-color-primary-highlight) !important}.sd-outline-secondary{border-color:var(--sd-color-secondary) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-secondary:focus,a.sd-outline-secondary:hover{border-color:var(--sd-color-secondary-highlight) !important}.sd-outline-success{border-color:var(--sd-color-success) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-success:focus,a.sd-outline-success:hover{border-color:var(--sd-color-success-highlight) !important}.sd-outline-info{border-color:var(--sd-color-info) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-info:focus,a.sd-outline-info:hover{border-color:var(--sd-color-info-highlight) !important}.sd-outline-warning{border-color:var(--sd-color-warning) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-warning:focus,a.sd-outline-warning:hover{border-color:var(--sd-color-warning-highlight) !important}.sd-outline-danger{border-color:var(--sd-color-danger) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-danger:focus,a.sd-outline-danger:hover{border-color:var(--sd-color-danger-highlight) !important}.sd-outline-light{border-color:var(--sd-color-light) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-light:focus,a.sd-outline-light:hover{border-color:var(--sd-color-light-highlight) !important}.sd-outline-muted{border-color:var(--sd-color-muted) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-muted:focus,a.sd-outline-muted:hover{border-color:var(--sd-color-muted-highlight) !important}.sd-outline-dark{border-color:var(--sd-color-dark) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-dark:focus,a.sd-outline-dark:hover{border-color:var(--sd-color-dark-highlight) !important}.sd-outline-black{border-color:var(--sd-color-black) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-black:focus,a.sd-outline-black:hover{border-color:var(--sd-color-black-highlight) !important}.sd-outline-white{border-color:var(--sd-color-white) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-white:focus,a.sd-outline-white:hover{border-color:var(--sd-color-white-highlight) !important}.sd-bg-transparent{background-color:transparent !important}.sd-outline-transparent{border-color:transparent !important}.sd-text-transparent{color:transparent !important}.sd-p-0{padding:0 !important}.sd-pt-0,.sd-py-0{padding-top:0 !important}.sd-pr-0,.sd-px-0{padding-right:0 !important}.sd-pb-0,.sd-py-0{padding-bottom:0 !important}.sd-pl-0,.sd-px-0{padding-left:0 !important}.sd-p-1{padding:.25rem !important}.sd-pt-1,.sd-py-1{padding-top:.25rem !important}.sd-pr-1,.sd-px-1{padding-right:.25rem !important}.sd-pb-1,.sd-py-1{padding-bottom:.25rem !important}.sd-pl-1,.sd-px-1{padding-left:.25rem !important}.sd-p-2{padding:.5rem !important}.sd-pt-2,.sd-py-2{padding-top:.5rem !important}.sd-pr-2,.sd-px-2{padding-right:.5rem !important}.sd-pb-2,.sd-py-2{padding-bottom:.5rem !important}.sd-pl-2,.sd-px-2{padding-left:.5rem !important}.sd-p-3{padding:1rem !important}.sd-pt-3,.sd-py-3{padding-top:1rem !important}.sd-pr-3,.sd-px-3{padding-right:1rem !important}.sd-pb-3,.sd-py-3{padding-bottom:1rem !important}.sd-pl-3,.sd-px-3{padding-left:1rem !important}.sd-p-4{padding:1.5rem !important}.sd-pt-4,.sd-py-4{padding-top:1.5rem !important}.sd-pr-4,.sd-px-4{padding-right:1.5rem !important}.sd-pb-4,.sd-py-4{padding-bottom:1.5rem !important}.sd-pl-4,.sd-px-4{padding-left:1.5rem !important}.sd-p-5{padding:3rem !important}.sd-pt-5,.sd-py-5{padding-top:3rem !important}.sd-pr-5,.sd-px-5{padding-right:3rem !important}.sd-pb-5,.sd-py-5{padding-bottom:3rem !important}.sd-pl-5,.sd-px-5{padding-left:3rem !important}.sd-m-auto{margin:auto !important}.sd-mt-auto,.sd-my-auto{margin-top:auto !important}.sd-mr-auto,.sd-mx-auto{margin-right:auto !important}.sd-mb-auto,.sd-my-auto{margin-bottom:auto !important}.sd-ml-auto,.sd-mx-auto{margin-left:auto !important}.sd-m-0{margin:0 !important}.sd-mt-0,.sd-my-0{margin-top:0 !important}.sd-mr-0,.sd-mx-0{margin-right:0 !important}.sd-mb-0,.sd-my-0{margin-bottom:0 !important}.sd-ml-0,.sd-mx-0{margin-left:0 !important}.sd-m-1{margin:.25rem !important}.sd-mt-1,.sd-my-1{margin-top:.25rem !important}.sd-mr-1,.sd-mx-1{margin-right:.25rem !important}.sd-mb-1,.sd-my-1{margin-bottom:.25rem !important}.sd-ml-1,.sd-mx-1{margin-left:.25rem !important}.sd-m-2{margin:.5rem !important}.sd-mt-2,.sd-my-2{margin-top:.5rem !important}.sd-mr-2,.sd-mx-2{margin-right:.5rem !important}.sd-mb-2,.sd-my-2{margin-bottom:.5rem !important}.sd-ml-2,.sd-mx-2{margin-left:.5rem !important}.sd-m-3{margin:1rem !important}.sd-mt-3,.sd-my-3{margin-top:1rem !important}.sd-mr-3,.sd-mx-3{margin-right:1rem !important}.sd-mb-3,.sd-my-3{margin-bottom:1rem !important}.sd-ml-3,.sd-mx-3{margin-left:1rem !important}.sd-m-4{margin:1.5rem !important}.sd-mt-4,.sd-my-4{margin-top:1.5rem !important}.sd-mr-4,.sd-mx-4{margin-right:1.5rem !important}.sd-mb-4,.sd-my-4{margin-bottom:1.5rem !important}.sd-ml-4,.sd-mx-4{margin-left:1.5rem !important}.sd-m-5{margin:3rem !important}.sd-mt-5,.sd-my-5{margin-top:3rem !important}.sd-mr-5,.sd-mx-5{margin-right:3rem !important}.sd-mb-5,.sd-my-5{margin-bottom:3rem !important}.sd-ml-5,.sd-mx-5{margin-left:3rem !important}.sd-w-25{width:25% !important}.sd-w-50{width:50% !important}.sd-w-75{width:75% !important}.sd-w-100{width:100% !important}.sd-w-auto{width:auto !important}.sd-h-25{height:25% !important}.sd-h-50{height:50% !important}.sd-h-75{height:75% !important}.sd-h-100{height:100% !important}.sd-h-auto{height:auto !important}.sd-d-none{display:none !important}.sd-d-inline{display:inline !important}.sd-d-inline-block{display:inline-block !important}.sd-d-block{display:block !important}.sd-d-grid{display:grid !important}.sd-d-flex-row{display:-ms-flexbox !important;display:flex !important;flex-direction:row !important}.sd-d-flex-column{display:-ms-flexbox !important;display:flex !important;flex-direction:column !important}.sd-d-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}@media(min-width: 576px){.sd-d-sm-none{display:none !important}.sd-d-sm-inline{display:inline !important}.sd-d-sm-inline-block{display:inline-block !important}.sd-d-sm-block{display:block !important}.sd-d-sm-grid{display:grid !important}.sd-d-sm-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-sm-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}@media(min-width: 768px){.sd-d-md-none{display:none !important}.sd-d-md-inline{display:inline !important}.sd-d-md-inline-block{display:inline-block !important}.sd-d-md-block{display:block !important}.sd-d-md-grid{display:grid !important}.sd-d-md-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-md-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}@media(min-width: 992px){.sd-d-lg-none{display:none !important}.sd-d-lg-inline{display:inline !important}.sd-d-lg-inline-block{display:inline-block !important}.sd-d-lg-block{display:block !important}.sd-d-lg-grid{display:grid !important}.sd-d-lg-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-lg-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}@media(min-width: 1200px){.sd-d-xl-none{display:none !important}.sd-d-xl-inline{display:inline !important}.sd-d-xl-inline-block{display:inline-block !important}.sd-d-xl-block{display:block !important}.sd-d-xl-grid{display:grid !important}.sd-d-xl-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-xl-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}.sd-align-major-start{justify-content:flex-start !important}.sd-align-major-end{justify-content:flex-end !important}.sd-align-major-center{justify-content:center !important}.sd-align-major-justify{justify-content:space-between !important}.sd-align-major-spaced{justify-content:space-evenly !important}.sd-align-minor-start{align-items:flex-start !important}.sd-align-minor-end{align-items:flex-end !important}.sd-align-minor-center{align-items:center !important}.sd-align-minor-stretch{align-items:stretch !important}.sd-text-justify{text-align:justify !important}.sd-text-left{text-align:left !important}.sd-text-right{text-align:right !important}.sd-text-center{text-align:center !important}.sd-font-weight-light{font-weight:300 !important}.sd-font-weight-lighter{font-weight:lighter !important}.sd-font-weight-normal{font-weight:400 !important}.sd-font-weight-bold{font-weight:700 !important}.sd-font-weight-bolder{font-weight:bolder !important}.sd-font-italic{font-style:italic !important}.sd-text-decoration-none{text-decoration:none !important}.sd-text-lowercase{text-transform:lowercase !important}.sd-text-uppercase{text-transform:uppercase !important}.sd-text-capitalize{text-transform:capitalize !important}.sd-text-wrap{white-space:normal !important}.sd-text-nowrap{white-space:nowrap !important}.sd-text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-fs-1,.sd-fs-1>p{font-size:calc(1.375rem + 1.5vw) !important;line-height:unset !important}.sd-fs-2,.sd-fs-2>p{font-size:calc(1.325rem + 0.9vw) !important;line-height:unset !important}.sd-fs-3,.sd-fs-3>p{font-size:calc(1.3rem + 0.6vw) !important;line-height:unset !important}.sd-fs-4,.sd-fs-4>p{font-size:calc(1.275rem + 0.3vw) !important;line-height:unset !important}.sd-fs-5,.sd-fs-5>p{font-size:1.25rem !important;line-height:unset !important}.sd-fs-6,.sd-fs-6>p{font-size:1rem !important;line-height:unset !important}.sd-border-0{border:0 solid !important}.sd-border-top-0{border-top:0 solid !important}.sd-border-bottom-0{border-bottom:0 solid !important}.sd-border-right-0{border-right:0 solid !important}.sd-border-left-0{border-left:0 solid !important}.sd-border-1{border:1px solid !important}.sd-border-top-1{border-top:1px solid !important}.sd-border-bottom-1{border-bottom:1px solid !important}.sd-border-right-1{border-right:1px solid !important}.sd-border-left-1{border-left:1px solid !important}.sd-border-2{border:2px solid !important}.sd-border-top-2{border-top:2px solid !important}.sd-border-bottom-2{border-bottom:2px solid !important}.sd-border-right-2{border-right:2px solid !important}.sd-border-left-2{border-left:2px solid !important}.sd-border-3{border:3px solid !important}.sd-border-top-3{border-top:3px solid !important}.sd-border-bottom-3{border-bottom:3px solid !important}.sd-border-right-3{border-right:3px solid !important}.sd-border-left-3{border-left:3px solid !important}.sd-border-4{border:4px solid !important}.sd-border-top-4{border-top:4px solid !important}.sd-border-bottom-4{border-bottom:4px solid !important}.sd-border-right-4{border-right:4px solid !important}.sd-border-left-4{border-left:4px solid !important}.sd-border-5{border:5px solid !important}.sd-border-top-5{border-top:5px solid !important}.sd-border-bottom-5{border-bottom:5px solid !important}.sd-border-right-5{border-right:5px solid !important}.sd-border-left-5{border-left:5px solid !important}.sd-rounded-0{border-radius:0 !important}.sd-rounded-1{border-radius:.2rem !important}.sd-rounded-2{border-radius:.3rem !important}.sd-rounded-3{border-radius:.5rem !important}.sd-rounded-pill{border-radius:50rem !important}.sd-rounded-circle{border-radius:50% !important}.shadow-none{box-shadow:none !important}.sd-shadow-sm{box-shadow:0 .125rem .25rem var(--sd-color-shadow) !important}.sd-shadow-md{box-shadow:0 .5rem 1rem var(--sd-color-shadow) !important}.sd-shadow-lg{box-shadow:0 1rem 3rem var(--sd-color-shadow) !important}@keyframes sd-slide-from-left{0%{transform:translateX(-100%)}100%{transform:translateX(0)}}@keyframes sd-slide-from-right{0%{transform:translateX(200%)}100%{transform:translateX(0)}}@keyframes sd-grow100{0%{transform:scale(0);opacity:.5}100%{transform:scale(1);opacity:1}}@keyframes sd-grow50{0%{transform:scale(0.5);opacity:.5}100%{transform:scale(1);opacity:1}}@keyframes sd-grow50-rot20{0%{transform:scale(0.5) rotateZ(-20deg);opacity:.5}75%{transform:scale(1) rotateZ(5deg);opacity:1}95%{transform:scale(1) rotateZ(-1deg);opacity:1}100%{transform:scale(1) rotateZ(0);opacity:1}}.sd-animate-slide-from-left{animation:1s ease-out 0s 1 normal none running sd-slide-from-left}.sd-animate-slide-from-right{animation:1s ease-out 0s 1 normal none running sd-slide-from-right}.sd-animate-grow100{animation:1s ease-out 0s 1 normal none running sd-grow100}.sd-animate-grow50{animation:1s ease-out 0s 1 normal none running sd-grow50}.sd-animate-grow50-rot20{animation:1s ease-out 0s 1 normal none running sd-grow50-rot20}.sd-badge{display:inline-block;padding:.35em .65em;font-size:.75em;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.sd-badge:empty{display:none}a.sd-badge{text-decoration:none}.sd-btn .sd-badge{position:relative;top:-1px}.sd-btn{background-color:transparent;border:1px solid transparent;border-radius:.25rem;cursor:pointer;display:inline-block;font-weight:400;font-size:1rem;line-height:1.5;padding:.375rem .75rem;text-align:center;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;vertical-align:middle;user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none}.sd-btn:hover{text-decoration:none}@media(prefers-reduced-motion: reduce){.sd-btn{transition:none}}.sd-btn-primary,.sd-btn-outline-primary:hover,.sd-btn-outline-primary:focus{color:var(--sd-color-primary-text) !important;background-color:var(--sd-color-primary) !important;border-color:var(--sd-color-primary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-primary:hover,.sd-btn-primary:focus{color:var(--sd-color-primary-text) !important;background-color:var(--sd-color-primary-highlight) !important;border-color:var(--sd-color-primary-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-primary{color:var(--sd-color-primary) !important;border-color:var(--sd-color-primary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-secondary,.sd-btn-outline-secondary:hover,.sd-btn-outline-secondary:focus{color:var(--sd-color-secondary-text) !important;background-color:var(--sd-color-secondary) !important;border-color:var(--sd-color-secondary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-secondary:hover,.sd-btn-secondary:focus{color:var(--sd-color-secondary-text) !important;background-color:var(--sd-color-secondary-highlight) !important;border-color:var(--sd-color-secondary-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-secondary{color:var(--sd-color-secondary) !important;border-color:var(--sd-color-secondary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-success,.sd-btn-outline-success:hover,.sd-btn-outline-success:focus{color:var(--sd-color-success-text) !important;background-color:var(--sd-color-success) !important;border-color:var(--sd-color-success) !important;border-width:1px !important;border-style:solid !important}.sd-btn-success:hover,.sd-btn-success:focus{color:var(--sd-color-success-text) !important;background-color:var(--sd-color-success-highlight) !important;border-color:var(--sd-color-success-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-success{color:var(--sd-color-success) !important;border-color:var(--sd-color-success) !important;border-width:1px !important;border-style:solid !important}.sd-btn-info,.sd-btn-outline-info:hover,.sd-btn-outline-info:focus{color:var(--sd-color-info-text) !important;background-color:var(--sd-color-info) !important;border-color:var(--sd-color-info) !important;border-width:1px !important;border-style:solid !important}.sd-btn-info:hover,.sd-btn-info:focus{color:var(--sd-color-info-text) !important;background-color:var(--sd-color-info-highlight) !important;border-color:var(--sd-color-info-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-info{color:var(--sd-color-info) !important;border-color:var(--sd-color-info) !important;border-width:1px !important;border-style:solid !important}.sd-btn-warning,.sd-btn-outline-warning:hover,.sd-btn-outline-warning:focus{color:var(--sd-color-warning-text) !important;background-color:var(--sd-color-warning) !important;border-color:var(--sd-color-warning) !important;border-width:1px !important;border-style:solid !important}.sd-btn-warning:hover,.sd-btn-warning:focus{color:var(--sd-color-warning-text) !important;background-color:var(--sd-color-warning-highlight) !important;border-color:var(--sd-color-warning-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-warning{color:var(--sd-color-warning) !important;border-color:var(--sd-color-warning) !important;border-width:1px !important;border-style:solid !important}.sd-btn-danger,.sd-btn-outline-danger:hover,.sd-btn-outline-danger:focus{color:var(--sd-color-danger-text) !important;background-color:var(--sd-color-danger) !important;border-color:var(--sd-color-danger) !important;border-width:1px !important;border-style:solid !important}.sd-btn-danger:hover,.sd-btn-danger:focus{color:var(--sd-color-danger-text) !important;background-color:var(--sd-color-danger-highlight) !important;border-color:var(--sd-color-danger-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-danger{color:var(--sd-color-danger) !important;border-color:var(--sd-color-danger) !important;border-width:1px !important;border-style:solid !important}.sd-btn-light,.sd-btn-outline-light:hover,.sd-btn-outline-light:focus{color:var(--sd-color-light-text) !important;background-color:var(--sd-color-light) !important;border-color:var(--sd-color-light) !important;border-width:1px !important;border-style:solid !important}.sd-btn-light:hover,.sd-btn-light:focus{color:var(--sd-color-light-text) !important;background-color:var(--sd-color-light-highlight) !important;border-color:var(--sd-color-light-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-light{color:var(--sd-color-light) !important;border-color:var(--sd-color-light) !important;border-width:1px !important;border-style:solid !important}.sd-btn-muted,.sd-btn-outline-muted:hover,.sd-btn-outline-muted:focus{color:var(--sd-color-muted-text) !important;background-color:var(--sd-color-muted) !important;border-color:var(--sd-color-muted) !important;border-width:1px !important;border-style:solid !important}.sd-btn-muted:hover,.sd-btn-muted:focus{color:var(--sd-color-muted-text) !important;background-color:var(--sd-color-muted-highlight) !important;border-color:var(--sd-color-muted-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-muted{color:var(--sd-color-muted) !important;border-color:var(--sd-color-muted) !important;border-width:1px !important;border-style:solid !important}.sd-btn-dark,.sd-btn-outline-dark:hover,.sd-btn-outline-dark:focus{color:var(--sd-color-dark-text) !important;background-color:var(--sd-color-dark) !important;border-color:var(--sd-color-dark) !important;border-width:1px !important;border-style:solid !important}.sd-btn-dark:hover,.sd-btn-dark:focus{color:var(--sd-color-dark-text) !important;background-color:var(--sd-color-dark-highlight) !important;border-color:var(--sd-color-dark-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-dark{color:var(--sd-color-dark) !important;border-color:var(--sd-color-dark) !important;border-width:1px !important;border-style:solid !important}.sd-btn-black,.sd-btn-outline-black:hover,.sd-btn-outline-black:focus{color:var(--sd-color-black-text) !important;background-color:var(--sd-color-black) !important;border-color:var(--sd-color-black) !important;border-width:1px !important;border-style:solid !important}.sd-btn-black:hover,.sd-btn-black:focus{color:var(--sd-color-black-text) !important;background-color:var(--sd-color-black-highlight) !important;border-color:var(--sd-color-black-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-black{color:var(--sd-color-black) !important;border-color:var(--sd-color-black) !important;border-width:1px !important;border-style:solid !important}.sd-btn-white,.sd-btn-outline-white:hover,.sd-btn-outline-white:focus{color:var(--sd-color-white-text) !important;background-color:var(--sd-color-white) !important;border-color:var(--sd-color-white) !important;border-width:1px !important;border-style:solid !important}.sd-btn-white:hover,.sd-btn-white:focus{color:var(--sd-color-white-text) !important;background-color:var(--sd-color-white-highlight) !important;border-color:var(--sd-color-white-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-white{color:var(--sd-color-white) !important;border-color:var(--sd-color-white) !important;border-width:1px !important;border-style:solid !important}.sd-stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.sd-hide-link-text{font-size:0}.sd-octicon,.sd-material-icon{display:inline-block;fill:currentColor;vertical-align:middle}.sd-avatar-xs{border-radius:50%;object-fit:cover;object-position:center;width:1rem;height:1rem}.sd-avatar-sm{border-radius:50%;object-fit:cover;object-position:center;width:3rem;height:3rem}.sd-avatar-md{border-radius:50%;object-fit:cover;object-position:center;width:5rem;height:5rem}.sd-avatar-lg{border-radius:50%;object-fit:cover;object-position:center;width:7rem;height:7rem}.sd-avatar-xl{border-radius:50%;object-fit:cover;object-position:center;width:10rem;height:10rem}.sd-avatar-inherit{border-radius:50%;object-fit:cover;object-position:center;width:inherit;height:inherit}.sd-avatar-initial{border-radius:50%;object-fit:cover;object-position:center;width:initial;height:initial}.sd-card{background-clip:border-box;background-color:var(--sd-color-card-background);border:1px solid var(--sd-color-card-border);border-radius:.25rem;color:var(--sd-color-card-text);display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;position:relative;word-wrap:break-word}.sd-card>hr{margin-left:0;margin-right:0}.sd-card-hover:hover{border-color:var(--sd-color-card-border-hover);transform:scale(1.01)}.sd-card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem 1rem}.sd-card-title{margin-bottom:.5rem}.sd-card-subtitle{margin-top:-0.25rem;margin-bottom:0}.sd-card-text:last-child{margin-bottom:0}.sd-card-link:hover{text-decoration:none}.sd-card-link+.card-link{margin-left:1rem}.sd-card-header{padding:.5rem 1rem;margin-bottom:0;background-color:var(--sd-color-card-header);border-bottom:1px solid var(--sd-color-card-border)}.sd-card-header:first-child{border-radius:calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0}.sd-card-footer{padding:.5rem 1rem;background-color:var(--sd-color-card-footer);border-top:1px solid var(--sd-color-card-border)}.sd-card-footer:last-child{border-radius:0 0 calc(0.25rem - 1px) calc(0.25rem - 1px)}.sd-card-header-tabs{margin-right:-0.5rem;margin-bottom:-0.5rem;margin-left:-0.5rem;border-bottom:0}.sd-card-header-pills{margin-right:-0.5rem;margin-left:-0.5rem}.sd-card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1rem;border-radius:calc(0.25rem - 1px)}.sd-card-img,.sd-card-img-bottom,.sd-card-img-top{width:100%}.sd-card-img,.sd-card-img-top{border-top-left-radius:calc(0.25rem - 1px);border-top-right-radius:calc(0.25rem - 1px)}.sd-card-img,.sd-card-img-bottom{border-bottom-left-radius:calc(0.25rem - 1px);border-bottom-right-radius:calc(0.25rem - 1px)}.sd-cards-carousel{width:100%;display:flex;flex-wrap:nowrap;-ms-flex-direction:row;flex-direction:row;overflow-x:hidden;scroll-snap-type:x mandatory}.sd-cards-carousel.sd-show-scrollbar{overflow-x:auto}.sd-cards-carousel:hover,.sd-cards-carousel:focus{overflow-x:auto}.sd-cards-carousel>.sd-card{flex-shrink:0;scroll-snap-align:start}.sd-cards-carousel>.sd-card:not(:last-child){margin-right:3px}.sd-card-cols-1>.sd-card{width:90%}.sd-card-cols-2>.sd-card{width:45%}.sd-card-cols-3>.sd-card{width:30%}.sd-card-cols-4>.sd-card{width:22.5%}.sd-card-cols-5>.sd-card{width:18%}.sd-card-cols-6>.sd-card{width:15%}.sd-card-cols-7>.sd-card{width:12.8571428571%}.sd-card-cols-8>.sd-card{width:11.25%}.sd-card-cols-9>.sd-card{width:10%}.sd-card-cols-10>.sd-card{width:9%}.sd-card-cols-11>.sd-card{width:8.1818181818%}.sd-card-cols-12>.sd-card{width:7.5%}.sd-container,.sd-container-fluid,.sd-container-lg,.sd-container-md,.sd-container-sm,.sd-container-xl{margin-left:auto;margin-right:auto;padding-left:var(--sd-gutter-x, 0.75rem);padding-right:var(--sd-gutter-x, 0.75rem);width:100%}@media(min-width: 576px){.sd-container-sm,.sd-container{max-width:540px}}@media(min-width: 768px){.sd-container-md,.sd-container-sm,.sd-container{max-width:720px}}@media(min-width: 992px){.sd-container-lg,.sd-container-md,.sd-container-sm,.sd-container{max-width:960px}}@media(min-width: 1200px){.sd-container-xl,.sd-container-lg,.sd-container-md,.sd-container-sm,.sd-container{max-width:1140px}}.sd-row{--sd-gutter-x: 1.5rem;--sd-gutter-y: 0;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-top:calc(var(--sd-gutter-y) * -1);margin-right:calc(var(--sd-gutter-x) * -0.5);margin-left:calc(var(--sd-gutter-x) * -0.5)}.sd-row>*{box-sizing:border-box;flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--sd-gutter-x) * 0.5);padding-left:calc(var(--sd-gutter-x) * 0.5);margin-top:var(--sd-gutter-y)}.sd-col{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-auto>*{flex:0 0 auto;width:auto}.sd-row-cols-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}@media(min-width: 576px){.sd-col-sm{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-sm-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-sm-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-sm-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-sm-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-sm-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-sm-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-sm-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-sm-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-sm-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-sm-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-sm-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-sm-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-sm-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}@media(min-width: 768px){.sd-col-md{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-md-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-md-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-md-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-md-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-md-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-md-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-md-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-md-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-md-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-md-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-md-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-md-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-md-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}@media(min-width: 992px){.sd-col-lg{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-lg-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-lg-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-lg-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-lg-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-lg-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-lg-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-lg-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-lg-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-lg-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-lg-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-lg-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-lg-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-lg-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}@media(min-width: 1200px){.sd-col-xl{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-xl-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-xl-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-xl-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-xl-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-xl-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-xl-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-xl-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-xl-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-xl-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-xl-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-xl-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-xl-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-xl-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}.sd-col-auto{flex:0 0 auto;-ms-flex:0 0 auto;width:auto}.sd-col-1{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}.sd-col-2{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-col-3{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-col-4{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-col-5{flex:0 0 auto;-ms-flex:0 0 auto;width:41.6666666667%}.sd-col-6{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-col-7{flex:0 0 auto;-ms-flex:0 0 auto;width:58.3333333333%}.sd-col-8{flex:0 0 auto;-ms-flex:0 0 auto;width:66.6666666667%}.sd-col-9{flex:0 0 auto;-ms-flex:0 0 auto;width:75%}.sd-col-10{flex:0 0 auto;-ms-flex:0 0 auto;width:83.3333333333%}.sd-col-11{flex:0 0 auto;-ms-flex:0 0 auto;width:91.6666666667%}.sd-col-12{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-g-0,.sd-gy-0{--sd-gutter-y: 0}.sd-g-0,.sd-gx-0{--sd-gutter-x: 0}.sd-g-1,.sd-gy-1{--sd-gutter-y: 0.25rem}.sd-g-1,.sd-gx-1{--sd-gutter-x: 0.25rem}.sd-g-2,.sd-gy-2{--sd-gutter-y: 0.5rem}.sd-g-2,.sd-gx-2{--sd-gutter-x: 0.5rem}.sd-g-3,.sd-gy-3{--sd-gutter-y: 1rem}.sd-g-3,.sd-gx-3{--sd-gutter-x: 1rem}.sd-g-4,.sd-gy-4{--sd-gutter-y: 1.5rem}.sd-g-4,.sd-gx-4{--sd-gutter-x: 1.5rem}.sd-g-5,.sd-gy-5{--sd-gutter-y: 3rem}.sd-g-5,.sd-gx-5{--sd-gutter-x: 3rem}@media(min-width: 576px){.sd-col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-sm-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-sm-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-sm-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-sm-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-sm-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-sm-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-sm-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-sm-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-sm-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-sm-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-sm-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-sm-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-sm-0,.sd-gy-sm-0{--sd-gutter-y: 0}.sd-g-sm-0,.sd-gx-sm-0{--sd-gutter-x: 0}.sd-g-sm-1,.sd-gy-sm-1{--sd-gutter-y: 0.25rem}.sd-g-sm-1,.sd-gx-sm-1{--sd-gutter-x: 0.25rem}.sd-g-sm-2,.sd-gy-sm-2{--sd-gutter-y: 0.5rem}.sd-g-sm-2,.sd-gx-sm-2{--sd-gutter-x: 0.5rem}.sd-g-sm-3,.sd-gy-sm-3{--sd-gutter-y: 1rem}.sd-g-sm-3,.sd-gx-sm-3{--sd-gutter-x: 1rem}.sd-g-sm-4,.sd-gy-sm-4{--sd-gutter-y: 1.5rem}.sd-g-sm-4,.sd-gx-sm-4{--sd-gutter-x: 1.5rem}.sd-g-sm-5,.sd-gy-sm-5{--sd-gutter-y: 3rem}.sd-g-sm-5,.sd-gx-sm-5{--sd-gutter-x: 3rem}}@media(min-width: 768px){.sd-col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-md-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-md-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-md-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-md-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-md-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-md-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-md-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-md-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-md-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-md-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-md-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-md-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-md-0,.sd-gy-md-0{--sd-gutter-y: 0}.sd-g-md-0,.sd-gx-md-0{--sd-gutter-x: 0}.sd-g-md-1,.sd-gy-md-1{--sd-gutter-y: 0.25rem}.sd-g-md-1,.sd-gx-md-1{--sd-gutter-x: 0.25rem}.sd-g-md-2,.sd-gy-md-2{--sd-gutter-y: 0.5rem}.sd-g-md-2,.sd-gx-md-2{--sd-gutter-x: 0.5rem}.sd-g-md-3,.sd-gy-md-3{--sd-gutter-y: 1rem}.sd-g-md-3,.sd-gx-md-3{--sd-gutter-x: 1rem}.sd-g-md-4,.sd-gy-md-4{--sd-gutter-y: 1.5rem}.sd-g-md-4,.sd-gx-md-4{--sd-gutter-x: 1.5rem}.sd-g-md-5,.sd-gy-md-5{--sd-gutter-y: 3rem}.sd-g-md-5,.sd-gx-md-5{--sd-gutter-x: 3rem}}@media(min-width: 992px){.sd-col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-lg-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-lg-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-lg-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-lg-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-lg-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-lg-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-lg-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-lg-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-lg-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-lg-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-lg-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-lg-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-lg-0,.sd-gy-lg-0{--sd-gutter-y: 0}.sd-g-lg-0,.sd-gx-lg-0{--sd-gutter-x: 0}.sd-g-lg-1,.sd-gy-lg-1{--sd-gutter-y: 0.25rem}.sd-g-lg-1,.sd-gx-lg-1{--sd-gutter-x: 0.25rem}.sd-g-lg-2,.sd-gy-lg-2{--sd-gutter-y: 0.5rem}.sd-g-lg-2,.sd-gx-lg-2{--sd-gutter-x: 0.5rem}.sd-g-lg-3,.sd-gy-lg-3{--sd-gutter-y: 1rem}.sd-g-lg-3,.sd-gx-lg-3{--sd-gutter-x: 1rem}.sd-g-lg-4,.sd-gy-lg-4{--sd-gutter-y: 1.5rem}.sd-g-lg-4,.sd-gx-lg-4{--sd-gutter-x: 1.5rem}.sd-g-lg-5,.sd-gy-lg-5{--sd-gutter-y: 3rem}.sd-g-lg-5,.sd-gx-lg-5{--sd-gutter-x: 3rem}}@media(min-width: 1200px){.sd-col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-xl-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-xl-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-xl-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-xl-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-xl-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-xl-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-xl-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-xl-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-xl-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-xl-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-xl-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-xl-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-xl-0,.sd-gy-xl-0{--sd-gutter-y: 0}.sd-g-xl-0,.sd-gx-xl-0{--sd-gutter-x: 0}.sd-g-xl-1,.sd-gy-xl-1{--sd-gutter-y: 0.25rem}.sd-g-xl-1,.sd-gx-xl-1{--sd-gutter-x: 0.25rem}.sd-g-xl-2,.sd-gy-xl-2{--sd-gutter-y: 0.5rem}.sd-g-xl-2,.sd-gx-xl-2{--sd-gutter-x: 0.5rem}.sd-g-xl-3,.sd-gy-xl-3{--sd-gutter-y: 1rem}.sd-g-xl-3,.sd-gx-xl-3{--sd-gutter-x: 1rem}.sd-g-xl-4,.sd-gy-xl-4{--sd-gutter-y: 1.5rem}.sd-g-xl-4,.sd-gx-xl-4{--sd-gutter-x: 1.5rem}.sd-g-xl-5,.sd-gy-xl-5{--sd-gutter-y: 3rem}.sd-g-xl-5,.sd-gx-xl-5{--sd-gutter-x: 3rem}}.sd-flex-row-reverse{flex-direction:row-reverse !important}details.sd-dropdown{position:relative}details.sd-dropdown .sd-summary-title{font-weight:700;padding-right:3em !important;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none}details.sd-dropdown:hover{cursor:pointer}details.sd-dropdown .sd-summary-content{cursor:default}details.sd-dropdown summary{list-style:none;padding:1em}details.sd-dropdown summary .sd-octicon.no-title{vertical-align:middle}details.sd-dropdown[open] summary .sd-octicon.no-title{visibility:hidden}details.sd-dropdown summary::-webkit-details-marker{display:none}details.sd-dropdown summary:focus{outline:none}details.sd-dropdown .sd-summary-icon{margin-right:.5em}details.sd-dropdown .sd-summary-icon svg{opacity:.8}details.sd-dropdown summary:hover .sd-summary-up svg,details.sd-dropdown summary:hover .sd-summary-down svg{opacity:1;transform:scale(1.1)}details.sd-dropdown .sd-summary-up svg,details.sd-dropdown .sd-summary-down svg{display:block;opacity:.6}details.sd-dropdown .sd-summary-up,details.sd-dropdown .sd-summary-down{pointer-events:none;position:absolute;right:1em;top:1em}details.sd-dropdown[open]>.sd-summary-title .sd-summary-down{visibility:hidden}details.sd-dropdown:not([open])>.sd-summary-title .sd-summary-up{visibility:hidden}details.sd-dropdown:not([open]).sd-card{border:none}details.sd-dropdown:not([open])>.sd-card-header{border:1px solid var(--sd-color-card-border);border-radius:.25rem}details.sd-dropdown.sd-fade-in[open] summary~*{-moz-animation:sd-fade-in .5s ease-in-out;-webkit-animation:sd-fade-in .5s ease-in-out;animation:sd-fade-in .5s ease-in-out}details.sd-dropdown.sd-fade-in-slide-down[open] summary~*{-moz-animation:sd-fade-in .5s ease-in-out,sd-slide-down .5s ease-in-out;-webkit-animation:sd-fade-in .5s ease-in-out,sd-slide-down .5s ease-in-out;animation:sd-fade-in .5s ease-in-out,sd-slide-down .5s ease-in-out}.sd-col>.sd-dropdown{width:100%}.sd-summary-content>.sd-tab-set:first-child{margin-top:0}@keyframes sd-fade-in{0%{opacity:0}100%{opacity:1}}@keyframes sd-slide-down{0%{transform:translate(0, -10px)}100%{transform:translate(0, 0)}}.sd-tab-set{border-radius:.125rem;display:flex;flex-wrap:wrap;margin:1em 0;position:relative}.sd-tab-set>input{opacity:0;position:absolute}.sd-tab-set>input:checked+label{border-color:var(--sd-color-tabs-underline-active);color:var(--sd-color-tabs-label-active)}.sd-tab-set>input:checked+label+.sd-tab-content{display:block}.sd-tab-set>input:not(:checked)+label:hover{color:var(--sd-color-tabs-label-hover);border-color:var(--sd-color-tabs-underline-hover)}.sd-tab-set>input:focus+label{outline-style:auto}.sd-tab-set>input:not(.focus-visible)+label{outline:none;-webkit-tap-highlight-color:transparent}.sd-tab-set>label{border-bottom:.125rem solid transparent;margin-bottom:0;color:var(--sd-color-tabs-label-inactive);border-color:var(--sd-color-tabs-underline-inactive);cursor:pointer;font-size:var(--sd-fontsize-tabs-label);font-weight:700;padding:1em 1.25em .5em;transition:color 250ms;width:auto;z-index:1}html .sd-tab-set>label:hover{color:var(--sd-color-tabs-label-active)}.sd-col>.sd-tab-set{width:100%}.sd-tab-content{box-shadow:0 -0.0625rem var(--sd-color-tabs-overline),0 .0625rem var(--sd-color-tabs-underline);display:none;order:99;padding-bottom:.75rem;padding-top:.75rem;width:100%}.sd-tab-content>:first-child{margin-top:0 !important}.sd-tab-content>:last-child{margin-bottom:0 !important}.sd-tab-content>.sd-tab-set{margin:0}.sd-sphinx-override,.sd-sphinx-override *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.sd-sphinx-override p{margin-top:0}:root{--sd-color-primary: #0071bc;--sd-color-secondary: #6c757d;--sd-color-success: #28a745;--sd-color-info: #17a2b8;--sd-color-warning: #f0b37e;--sd-color-danger: #dc3545;--sd-color-light: #f8f9fa;--sd-color-muted: #6c757d;--sd-color-dark: #212529;--sd-color-black: black;--sd-color-white: white;--sd-color-primary-highlight: #0060a0;--sd-color-secondary-highlight: #5c636a;--sd-color-success-highlight: #228e3b;--sd-color-info-highlight: #148a9c;--sd-color-warning-highlight: #cc986b;--sd-color-danger-highlight: #bb2d3b;--sd-color-light-highlight: #d3d4d5;--sd-color-muted-highlight: #5c636a;--sd-color-dark-highlight: #1c1f23;--sd-color-black-highlight: black;--sd-color-white-highlight: #d9d9d9;--sd-color-primary-text: #fff;--sd-color-secondary-text: #fff;--sd-color-success-text: #fff;--sd-color-info-text: #fff;--sd-color-warning-text: #212529;--sd-color-danger-text: #fff;--sd-color-light-text: #212529;--sd-color-muted-text: #fff;--sd-color-dark-text: #fff;--sd-color-black-text: #fff;--sd-color-white-text: #212529;--sd-color-shadow: rgba(0, 0, 0, 0.15);--sd-color-card-border: rgba(0, 0, 0, 0.125);--sd-color-card-border-hover: hsla(231, 99%, 66%, 1);--sd-color-card-background: transparent;--sd-color-card-text: inherit;--sd-color-card-header: transparent;--sd-color-card-footer: transparent;--sd-color-tabs-label-active: hsla(231, 99%, 66%, 1);--sd-color-tabs-label-hover: hsla(231, 99%, 66%, 1);--sd-color-tabs-label-inactive: hsl(0, 0%, 66%);--sd-color-tabs-underline-active: hsla(231, 99%, 66%, 1);--sd-color-tabs-underline-hover: rgba(178, 206, 245, 0.62);--sd-color-tabs-underline-inactive: transparent;--sd-color-tabs-overline: rgb(222, 222, 222);--sd-color-tabs-underline: rgb(222, 222, 222);--sd-fontsize-tabs-label: 1rem} diff --git a/_static/design-tabs.js b/_static/design-tabs.js new file mode 100644 index 0000000..36b38cf --- /dev/null +++ b/_static/design-tabs.js @@ -0,0 +1,27 @@ +var sd_labels_by_text = {}; + +function ready() { + const li = document.getElementsByClassName("sd-tab-label"); + for (const label of li) { + syncId = label.getAttribute("data-sync-id"); + if (syncId) { + label.onclick = onLabelClick; + if (!sd_labels_by_text[syncId]) { + sd_labels_by_text[syncId] = []; + } + sd_labels_by_text[syncId].push(label); + } + } +} + +function onLabelClick() { + // Activate other inputs with the same sync id. + syncId = this.getAttribute("data-sync-id"); + for (label of sd_labels_by_text[syncId]) { + if (label === this) continue; + label.previousElementSibling.checked = true; + } + window.localStorage.setItem("sphinx-design-last-tab", syncId); +} + +document.addEventListener("DOMContentLoaded", ready, false); diff --git a/_static/doctools.js b/_static/doctools.js new file mode 100644 index 0000000..d06a71d --- /dev/null +++ b/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/_static/documentation_options.js b/_static/documentation_options.js new file mode 100644 index 0000000..f45177f --- /dev/null +++ b/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '0.4.1.dev15+g326e192', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/_static/file.png b/_static/file.png new file mode 100644 index 0000000..a858a41 Binary files /dev/null and b/_static/file.png differ diff --git a/_static/generation_time_templating.svg b/_static/generation_time_templating.svg new file mode 100644 index 0000000..250435a --- /dev/null +++ b/_static/generation_time_templating.svg @@ -0,0 +1,16 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO2dW3PayLbH3/MpXFzeL2fvXG5M3y/zclxu44DBXHUwMDA0MFx1MDAxOIw5maIwXGJcdTAwMTBcdTAwMDaBJWEuU/PdT1x1MDAwYjtGgCSEMVjZM56pJEa3Rvr1v9davXrpzy9nZ+f2fKyd/352rs1azYHeNpvT86/O58+aaekjQ21Cy9+t0cRsLffs2fbY+v2331ZHxPXWOK5cdTAwMWJcdTAwMWTd0EZGvDVcdTAwMWG+nEJcdTAwMWJoQ82wLXXQ/6nfz87+XFz+6bqoqbXsptFcdTAwMWRoy1x1MDAwM5abVtfFQmx+mlx1MDAxZlx1MDAxOcs2QMFcdTAwMDXChFD+toduXarr2Vpbbe40XHUwMDA3lrba4nx0zvRZtfd0Mcg93LZcdTAwMTfVXHUwMDFiXHUwMDAzd6ed8eqyXHUwMDFkfTAo2/PBslnWSH2v1TbLNkeP2p3etnvOtTc+9zvKXHUwMDFjTbo9Q7OstWNG42ZLt+fOZ1x1MDAwMLx9+nJcdTAwMGZ+P1t9MnP2wFx1MDAxMsSB64e+bXZOXHUwMDEwY1DEpftno2XJ0WBkOi37V1tcdTAwMTJcdTAwMDE6q7Y9NFuPXdVAo73ap1x1MDAwM5z/VvtMX78vJnL9KuRtl56md3u22odcdTAwMTPo11x1MDAxMm35RFx1MDAwNFx1MDAwMJhJurpcdTAwMGbO1ceZ9pKNPzbvaK9pjl/v3Lnl/OJqudPoby9gvVx1MDAxZf66SW1szW6y9yPYebZryWmdjlx1MDAxYXOr8f3tWLVDXCJLKmateHNdqNXLoK5PXHUwMDFhuFx1MDAwNN076NqzXHUwMDE4XFwsXG65cnqeuPpeqVx1MDAxNq9l6vx1+1x1MDAxZsu///rqzbKtzWxPjDn1xVx1MDAxOFxiSVx1MDAxOcWuXHUwMDA3u1x1MDAwYuOpc7nJXUxcdTAwMTe5ziyLrfucOS75YNxrtnpcdTAwMTNTi1x1MDAwMMhcdTAwMDTAQJBBWJBfXHUwMDBlXHUwMDBmXHUwMDA02TabhjVumlxukG2YXHUwMDExYtv0YrqJq2ow5UIyeVxmXleHd0aGXdZcdTAwMTfOXHJDYu3TVHOoXHUwMDBm5ms3fMmXukBRXaA56p1cdTAwMTXNUV/Jp/teWNpASbBzNrp2WGKgd1x1MDAxZFx1MDAxNM9cdTAwMDdaZ51RW1dcdTAwMWH+ttlcdTAwMWWNz1x1MDAwM1x1MDAxOVx1MDAwZtRrRbJcdTAwMWboWFx1MDAxMFx1MDAwMVx0XHUwMDEx4eU6V0zYMHdDulx1MDAxYbzqli/7iUmhlI24XFxcdTAwMTPkqKCAXGZcdTAwMDCOqeRcdTAwMWKUU+orklx1MDAxYpRcdTAwMTNJXHUwMDE1fzvkevnjQbhAcUxcdTAwMDRjXGZQjCmX27xcdTAwMTPJ45BcdTAwMDMoMZRcYnFEtvmXglEmKONH6Vx1MDAwMO/SUVx1MDAwMqFcdTAwMWZekEtcdTAwMDIwICi8jrZNdHNp2rFBI/F4Pcv0zGHZYtHXUSmCdJRi+XGEXHUwMDA16iiEXHUwMDFlVlx1MDAwMNriXGJcdTAwMDGGJcNcdTAwMTTi01x0KVj7NEBIk6PheGSo051BT1x1MDAxMYVi7ZBcdTAwMTOJKPelXHUwMDFjqT5LqUThRbSu5crNeVx1MDAwMiTsPEyNnoynx06yXHUwMDFheVx1MDAxMcVBiCNcdTAwMTYnkCOAMYSCKbvzNFwiirdpJyQuXS2hiGzSXHUwMDBmMceCXHUwMDEyNVx1MDAxNERHRTHgvnxcdTAwMDE1RqtvxHFowMziYpJ/1tL1+/5D+TpcdTAwMDU6lVmrXHUwMDEzeVx1MDAxNaWAXHUwMDA0IVx1MDAwNlx1MDAxNWIuXHJcdTAwMTWCXHUwMDFkwliwjFwiuFxy1raMXG5cdTAwMDFcdTAwMTFSQ9xR3KdcdTAwMGZT0fxJVXR5ei/Aka9cdTAwMTWKXHUwMDAwXHUwMDA3XHUwMDFjYlxmwlx1MDAwM35cdTAwMGJcdTAwMTZ67ebCmuZ6qfG8OqTZzmjxwYC3R7bTgFx1MDAwZiWc0SDClUdPXHUwMDExJlxcWeRYYqZ07mhcdTAwMGVcdTAwMTfY5lx1MDAxYoq1q0uxiTthXHUwMDAyKyNavMP4XFy2LCTult007Vx1MDAwYt1o60ZXbTEmg8HbNs1o+2xcdTAwMTk0LVsxP9Sdh1ZcdTAwMWPphr25x/K8XHTTXHUwMDFjTXtas+1xZt9tY+d06zGN1b/OVvdy+cvbv//4unvvrXu+OvqL++932SxcZvpcdTAwMDbqOKeEXCJJ9+hx6HrwcGlcdTAwMTVcdTAwMWKpYeNhfFx1MDAxNatPe/OHyFx1MDAwZimEXHUwMDA2XHUwMDBlKc6IXHUwMDEzzjBHXHUwMDBmoom19/c35HpcdTAwMTYrU1x1MDAwNW52Mci4eirOXHUwMDFmXHUwMDExMk0o8cNcYlwiZ6SVXGaF54hMa4hqxUy2Mlx1MDAxYVx1MDAwZjKN2sAoXHUwMDE0zehzJFx1MDAwMlx1MDAwM2WO505Owlx1MDAxMWRcdTAwMWWBsm3LXHUwMDA0YijVrvBIgYLDTJNS+fYspVx1MDAwZjTr7H/+XHUwMDEzNy3733tcdTAwMTkoLdVcdTAwMGXNPMREXHRcdTAwMTRNjnxcdTAwMWQ9QTFSns1cdTAwMWVR4e/z+1SlVm5AZjUoxcXrJ5CYR1x1MDAxZvZg0XQ2h1x1MDAxM02NcX5QVFx1MDAxOPK1hlx1MDAwMFx1MDAwZndcdTAwMGZcdTAwMDK23lx1MDAxYbbZXHUwMDE3kCCqL3B2XHUwMDFjK/1dkko48pVUXHUwMDAxlGvKXHRcdTAwMGIvqaX7b1x1MDAwNdon9Ulvjp+um5Pcola5iD5lylx1MDAxNqZcdTAwMDGUXHUwMDExXHUwMDEwUlJcdTAwMGalXGZ6RVx1MDAxMfiWpHJl+1xuQfBcdOdcdTAwMWVCK2pupFx1MDAwNPAsqXZSXHUwMDFmvGjrXHUwMDBmQ9m7Slx1MDAxOH8/+0983lx1MDAxY1x1MDAwZX5cdTAwMTjtuaHOsPxgPPeUXFz2OZKLXX12ayZOXHUwMDAyzDHGe8xQTMZGp0duuVXpJWlcbl9cXHaqiej3XHUwMDA2XHUwMDFh6Fx1MDAxOWJcdTAwMWN2Jlx1MDAwZVxuRppcdTAwMGaHaK5XXHUwMDA02cPCXHUwMDEwkFEnivwpc8ev299p0EJf3jgmTN1cdTAwMDFcdTAwMTl+iM9b+eebx2KrW85cXGXMQmlaKGWG0cdN8mDcwk6JXHUwMDFkilx1MDAxYsShJiwgXHUwMDA2jKlH43ow0VHfhGVp9lm5Zepj24rOlFx1MDAwNab+XHRcdTAwMGXqZkLhXHUwMDA0XHUwMDFjwssqn1RoLZVt1qjWrqpbX++3cfQ558GzXHUwMDE2gp2Kc1x1MDAxZS7BgVwiQZ1nc1x1MDAxY87fKZfAlyPIXGKCXHUwMDAw7WGsXlx1MDAxNyaxXHUwMDE2eWjPqiOhXHUwMDE3J0YmWXxcdTAwMWVFniOKSJCxisSp9JJ72Kpe87tcXFA1NFx1MDAxZiex60C5XHUwMDFjXHUwMDBmRrblZ4K+2+tcdTAwMWbq7bZ6cu+XS6g8XHUwMDEyP85cdTAwMTHEUnImYXi9nFx1MDAxNe8rmZRRuH3KN24748t7ym8n0Z7jZSDQXHUwMDA2pVx1MDAxMMSpL9ghUlx1MDAxOac93dY8LE7hMVx1MDAxOeFcdTAwMTFcdTAwMWHFXHUwMDE0OzFr6O2ArSzGdFxmldrFp8uGTnHMvGo+5e6B9WYxXHUwMDFlU0F38SX9+KKMXHUwMDEzXHUwMDA09lx0xyfwfYxcdTAwMThXIFx1MDAwNlh5cFVJ5VxmNixEXHUwMDFjL1x1MDAxOGhzOjk0n4lcdTAwMTfnXHUwMDAyK8akd0rML4BcdTAwMTfwNfdcYlHqxdX/ofHidJhq9jrZmZRsciXgnIuCXHUwMDE1cbyQjFx1MDAwYsQpYEJ9W0TwXHUwMDA2XkLEgXAmK5yBXHUwMDExuyyWk6MmJOCEUVx1MDAxZlx1MDAxYu80pK2uYpXqs7Z1fVxyrJZZMe9pt7CIkcNcdTAwMWNsXHUwMDAxfMObUj1cdTAwMWXCWHj/Okvv8pOCNIGc8FZnjNCMV/zsxaiQSHAwiWpcdTAwMWP9KFx1MDAxMoNjOdSDx21rUVx1MDAxOfhcdTAwMDJcdTAwMTDEP3VgPcymLI97ujH7mXt9dqtccseDpq3t541cdTAwMWY5zEmwf6IsVGO/klpXkuGufvHt+mZOXHUwMDEzoEbvLJjJXHUwMDE2q/ogNbyNvFx1MDAxZrXLXHUwMDFmJzBk0P9U/jhcdTAwMTRYXHUwMDAwXGZYlPxxfztcdTAwMTJRKCRcdTAwMDdcIjxGxUo+WSqn20/124vLOlxcWNVMMvrT8Vx1MDAxNMJAd1x1MDAxY9NcdTAwMTNh5MqFXG5cdTAwMTBYpfSYXHUwMDAxKaKYbW03XHUwMDFmXHUwMDA2Wlx1MDAwNN1xwvwp55Bcbuhex7HTXHUwMDE5XHUwMDFmX8pKt5Z6bMhi9lu/YD2bi2TkKSdcdTAwMWNcdTAwMDXnw6JoiSWTXGJjRMhxgk7v00rpv1ZcdTAwMTVJ1Vrl9O2RXHUwMDAzx4flWmLa5jPZXHUwMDFhplLmXHUwMDFkm5Wij1x1MDAxMcWBfjek7ERcdTAwMTiFm+ohWHDn0URxpud/P0UnfVOqpa/Hz5XJXCIpx+Elspdq1PLfL265+b2bLXat+lxi8MdcdTAwMGZm+yhcdTAwMTnVMK7cSWdcdTAwMTaCKtJdy0hf4HbmOZlybpxcdTAwMTlPzFx0P1x1MDAxYdxenlx1MDAxNo5cdTAwMTMnnVx1MDAwNzNcboRq6HbMyYn7cSboP1x1MDAxOdVcdTAwMWaWUb1101dHf3H//b6Mauw/peqskFx1MDAxMpLsMZyAST89a3Sy6fHzMEPHhadMttWPdmhjuZZWIMpcdTAwMThT/Vx0u5NFnFx1MDAxMyiLhFx1MDAxMqhcdTAwMDZdTFxiYO67tdndaEtcdTAwMTLQXHUwMDBl7G6HrVx1MDAwMsO7V4FcdOVyXHUwMDEz99zlUVx1MDAxMlhcdTAwMTDr5K9cdTAwMWJ163GEc4Pyw/VClDvGYfE1yH1tYyeSgJT2y/AzVVx1MDAxONyVb3KpmMVzuKTJ6kLaLb/0wahgSFx1MDAwM1cjKrvZ7Vx1MDAxZMLNhu2FYbD/XHUwMDE3KsDGoVCYsSNcdTAwMTnGh5k0Zc22lfh/cKTsYPePXHUwMDEx/6RcdTAwMDMmhCCK8vCIQ9sulkTxkqJcdTAwMTLrWZXH5+9cXEORN9xcdFxyXGaWKYtnfUGkv+UuXHUwMDE40mTrkKTAcKtXXHUwMDA04JDzY5eT8a5cdTAwMTZzkKJiV2xmXHUwMDBiNzWKIChh+KBcdTAwMWG1S+MszN9b6KqTqVx1MDAxN8r565b+XHUwMDBiJGTLQNyUJS1PQ1x1MDAxYiThpi1cYpdcdTAwMDJgXHUwMDEyRUexpL3U5XrJxY7SLFx1MDAwNFx1MDAwNNjXeFCGXHUwMDEyXHUwMDE3ksrwpMfscdU2eY3XUT1V5ZOp2Vx1MDAxOVxyom07MLBuwW5iTuOSXG61XHUwMDEzo1RiLJAv5lx1MDAxMTBhKSdAXCK+M2chl75J4sWkIVPJqdXqJ1x1MDAxNncjnP/wmeRcdTAwMDMs3UBkJfOdTkaUYudcdTAwMGXsUVx1MDAxYWFRa8pWvvJkXFzPNP5sdjLZSj3ixEJcdTAwMTLgc8VcdTAwMTRKv1x1MDAwZbGYQVx1MDAwZYDfYqz/XHUwMDE2YoV/2Fx1MDAxOTFcdLiEe2hsYp7NPj4z1lx1MDAxYubzpJ3JPsJa5irixFwiXHUwMDE2RCxcdTAwMDe/XHUwMDBlsVx1MDAwMjBcYlx0lLuSIz5cdTAwMTPYr6tcdTAwMWTsTL9dS6FmV5s+TvvV+UU7Xbs4zDYmwj/agFx1MDAwNcROuCe8/qbzg4fYxUPmJsvG057eT9yxZNRcdTAwMTNcdTAwMTd54EpFdrzwXHUwMDAydM3kXHUwMDA3TZlwXHUwMDA1N0PEO4x8XHUwMDFhRj8mXGZcdTAwMTHNzFx1MDAxZIWI/3pdgpdJyXtcYrpcdTAwMWR7zJbUc8+2XHUwMDEzsNHQW8wmzyDiXVx1MDAwMK2bIFx1MDAxYtMs2Im4uVLagH/g96DyX1xm7KqhXGIlW6uhuJ3RXHUwMDAzOFx1MDAwNJRSsstmZp3n2V15mCx9n+RjZauZNG5q95+f51x1MDAxYmBcZiu/XHUwMDFiXHUwMDExykX4ST87fbtoxExcdTAwMWFrI61RmV7X+5V0O+IkYlx1MDAxYUhcIlx1MDAwM1x1MDAxMSFcdTAwMTFcdTAwMDFcdTAwMTBMXCKiXHUwMDE4SYXiL1xuovCfhGCqj0GK6Fx1MDAxZbPP/Vxmm1xikCmUv1/cplx1MDAxZud1Xlx1MDAxOdpcdTAwMTFcdTAwMDeRiEBcdTAwMTCdYl5cdTAwMTFcdTAwMDFcdTAwMTHuXHUwMDAwXHUwMDExYOjUn5K7nLJjkri6yjCt+jhupurJauF+dq1cciGY9Vx1MDAwZjNhmX9cdCNcdKmUhJM9kslcbmKgXHUwMDBmOzN82/t2l67z51x1MDAxOe1cdTAwMWI+rEYmust4YE66MuI/kNZcdTAwMWRzXHShortcdTAwMTBB5SZcdTAwMDOJd1x1MDAxObVHlcfDjNpVXHLG95m1R11cdTAwMWUu/KvDQUIgXHUwMDA1XHUwMDFj7VHWy0pfzsxRXHUwMDFhfZPtRFx1MDAwN9o3ODZO3ke/U6DATlx1MDAwMUJbtScqXHUwMDBmh1x1MDAxOCOOhu9cbkZ8nsngp8DUX4FcdTAwMTFaTiftUeJlYSV6i8vEVZ1OvsHkfHKfvW9Evrwtk4Hr05Cka+vTjlxiW8hcdTAwMWFyRCkwXHUwMDExQHwqbIdcdHCkK81J4lx1MDAxZlSQypRzwo7hrZKc3nnq9M1ywvqGqiA9rF59m1ZcIt8nglx1MDAwNVx1MDAxONHQXHUwMDAyfIpSc4RcdTAwMDe/TIdyoIybX1CcXHUwMDE596/wyZUhJoTcI6iQv81cdTAwMTbGvafU86zYT99cdTAwMTVcdTAwMWVamadZ5CtcIjFB4jiARCzCqvNRytF52MdcdTAwMTRzSSFGn+m0fXzZuihcdHRQt1x1MDAwMKpTQFx1MDAwMUB4oyV5qd0mclrMmuRq48eYPnvijafId4tggXairSFcdTAwMDX68Lp0bKdAbyx0XHUwMDAx26VAVVx1MDAxZpZCfK5cdTAwMDG9uspcdTAwMTFq21Hin6GuvjnhTrgqNLNgQvBCXHUwMDBlrupX15XSXW5opm4qfi8qiVxms3yZLuHPrFxiXHUwMDFk6jhVcTuKmETCr+jXLyHlp6+B90JouaVcckE3q08vLL39YN3Pntv95up0P7tLW29cdTAwMGVH7vUyy4xHgFx1MDAwM1/bhDn005M3VfKSIfWh35tcdTAwMTm3id1kXHUwMDBlPHAkaCBzXHUwMDFi1UfCXHUwMDA3xzfzLD6iXHUwMDBmvjfn8u2NfkqUhFx1MDAwNHDr9TdLXHUwMDBm1D/zQEBEXHUwMDE5hTBMiCpQhL0zJr6udvCOR7t28K6g4trhXHUwMDFkb698obu1mFx1MDAxN1x1MDAxZpO9pymc5y+4pY/NYsKD7vXB4Fx1MDAwNW0ug3JcdTAwMTYwkLvQhtAjSsCDxPfvjjKQXHUwMDE4cOyuvuB+26i/s49cdTAwMDHEiuZQdeW3UV5bV7fxMspcdTAwMWZGopj5LZnLuG7xvu+23HvRh5f4M1x1MDAxMED5bWJcZrSLtJXnrFXNXHUwMDBlrqr4O+2Fo1x1MDAxY+PAXHUwMDFh4my9aqOXgCNcdTAwMGZcdTAwMDH/h3J/ytXgxp1cdTAwMDW3wItyKnzfYlx1MDAwNlx1MDAxMVJcdTAwMGZAulx1MDAxN4dcdTAwMWNM+Y/ztGZoZtNW14nZ+lD7YbxOtOhG98f5SaB37JO9iVx1MDAwZlx1MDAxZXPeiG86y2fXkZdcdTAwMWJu0Na7TD103GNqXHUwMDAxXHUwMDAx9H5cdTAwMTMlxFxm4K9J/Maa6p/EcyUzwv22QPfLT/14p0hcdTAwMDJJRKiKTjtw/9DF0i5cdTAwMWOcn9g2XHSrM2x5XHUwMDE2IZeEr1x1MDAxNpT/+Xayn2+dz4TIaV72zdbE+cogLrFQgqOcdiAkpMy1T7c5djraWlx1MDAxNlx1MDAwNuT8dYe/3tq0tsTdr0XBjsR6izgj0Mn5o4BcdTAwMGJXjYW3JjmFUiUhwClSwzhAaKtJe62cf9WBXHUwMDAwPVx0NlGD9SSw5ivixE8nXHUwMDAy7cTVoPqPnGxccqBcZikswPb7XHUwMDEwz9dKpGzNyUvBICahojenXHUwMDE1XHUwMDE05t5cdTAwMWafQj6C08VcXJ01XHUwMDA24khgSSFVRpqyQNzZkW/ddS2hUIm2PLqCXHUwMDAwXHUwMDAwlcvLXHUwMDEw4k6pXG5cYlxchf/eWsXjgHMpqZBSOOXDXGI7topcdTAwMDT7sYEqgkBgaV9Gd6uI17udXFwrQ/7RkVxyXHUwMDFkIVQxXHLk9uvKXHUwMDFjdvwrTVwizJ21LqHecHJaXHUwMDE5we791aM/hZBcdTAwMDTXOV5cdTAwMTdcdTAwMTIl2lx1MDAwMuGXTosg8Vx1MDAxY/YhR0RiZ71cdTAwMDBnkGyP+1x1MDAxZiskTqMoV9RTXCJcdTAwMThcdTAwMTaUejWKxCFcItzJKFx1MDAxMFx1MDAxOGF6dFx1MDAxNfFcdTAwMGV2hVFcdTAwMTEnolx1MDAxN/yOcblTRrzc+X28+b+ZiEBAlV2tbNit8lx1MDAwN057/aOvlEIukFxiNf15Ulx1MDAxNUHYZ//jacg+3Vx1MDAxNVx1MDAwMKdkgVx1MDAxMm3KXHUwMDA0Q8pcdTAwMWEh21x1MDAxZFx1MDAxNsYxVlx1MDAwM796KJIwJdU/XHUwMDFksj1VhOmzau/pYpB7uG0vqjdcdTAwMDbuTjtjXHUwMDFmc4RKJJ01qc57XHUwMDA3XHUwMDE5Xlx1MDAxNVZxNepcdTAwMDNl48vrYzhvjsdlu2lrb9/i/FnXplx1MDAxN/7p719eXHUwMDFi4HQ6zWn7n399+ev/XHUwMDAxj2E9SyJ9 + + + + Pharaoh ProjectComponent 1Component NRST Files (*.rst)Local Context Filesstatic: *.yamldynamic: *.pyAsset Scriptsplots.pySphinx Project Templatestables.py?.pySettingsResource FilesSetting TemplatesComponent TemplatesRST Files (*.rst)Local Context FilesAsset ScriptsPharaohAPI/CLI"Generation-timeTemplating" diff --git a/_static/html_logo.png b/_static/html_logo.png new file mode 100644 index 0000000..6744b5e Binary files /dev/null and b/_static/html_logo.png differ diff --git a/_static/icon.pdn b/_static/icon.pdn new file mode 100644 index 0000000..b71e0ff Binary files /dev/null and b/_static/icon.pdn differ diff --git a/_static/icon.png b/_static/icon.png new file mode 100644 index 0000000..4f1f3ef Binary files /dev/null and b/_static/icon.png differ diff --git a/_static/jquery.js b/_static/jquery.js new file mode 100644 index 0000000..c4c6022 --- /dev/null +++ b/_static/jquery.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/_static/js/html5shiv.min.js b/_static/js/html5shiv.min.js new file mode 100644 index 0000000..cd1c674 --- /dev/null +++ b/_static/js/html5shiv.min.js @@ -0,0 +1,4 @@ +/** +* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed +*/ +!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/_static/js/theme.js b/_static/js/theme.js new file mode 100644 index 0000000..1fddb6e --- /dev/null +++ b/_static/js/theme.js @@ -0,0 +1 @@ +!function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/_static/minus.png b/_static/minus.png new file mode 100644 index 0000000..d96755f Binary files /dev/null and b/_static/minus.png differ diff --git a/_static/overview.png b/_static/overview.png new file mode 100644 index 0000000..cbd1fa4 Binary files /dev/null and b/_static/overview.png differ diff --git a/_static/plus.png b/_static/plus.png new file mode 100644 index 0000000..7107cec Binary files /dev/null and b/_static/plus.png differ diff --git a/_static/pptx.png b/_static/pptx.png new file mode 100644 index 0000000..f0dbb2d Binary files /dev/null and b/_static/pptx.png differ diff --git a/_static/pygments.css b/_static/pygments.css new file mode 100644 index 0000000..84ab303 --- /dev/null +++ b/_static/pygments.css @@ -0,0 +1,75 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #008000; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #9C6500 } /* Comment.Preproc */ +.highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +.highlight .gr { color: #E40000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #008400 } /* Generic.Inserted */ +.highlight .go { color: #717171 } /* Generic.Output */ +.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0044DD } /* Generic.Traceback */ +.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #008000 } /* Keyword.Pseudo */ +.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #B00040 } /* Keyword.Type */ +.highlight .m { color: #666666 } /* Literal.Number */ +.highlight .s { color: #BA2121 } /* Literal.String */ +.highlight .na { color: #687822 } /* Name.Attribute */ +.highlight .nb { color: #008000 } /* Name.Builtin */ +.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +.highlight .no { color: #880000 } /* Name.Constant */ +.highlight .nd { color: #AA22FF } /* Name.Decorator */ +.highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #0000FF } /* Name.Function */ +.highlight .nl { color: #767600 } /* Name.Label */ +.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #19177C } /* Name.Variable */ +.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mb { color: #666666 } /* Literal.Number.Bin */ +.highlight .mf { color: #666666 } /* Literal.Number.Float */ +.highlight .mh { color: #666666 } /* Literal.Number.Hex */ +.highlight .mi { color: #666666 } /* Literal.Number.Integer */ +.highlight .mo { color: #666666 } /* Literal.Number.Oct */ +.highlight .sa { color: #BA2121 } /* Literal.String.Affix */ +.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ +.highlight .sc { color: #BA2121 } /* Literal.String.Char */ +.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ +.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ +.highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ +.highlight .sx { color: #008000 } /* Literal.String.Other */ +.highlight .sr { color: #A45A77 } /* Literal.String.Regex */ +.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ +.highlight .ss { color: #19177C } /* Literal.String.Symbol */ +.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #0000FF } /* Name.Function.Magic */ +.highlight .vc { color: #19177C } /* Name.Variable.Class */ +.highlight .vg { color: #19177C } /* Name.Variable.Global */ +.highlight .vi { color: #19177C } /* Name.Variable.Instance */ +.highlight .vm { color: #19177C } /* Name.Variable.Magic */ +.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/_static/searchtools.js b/_static/searchtools.js new file mode 100644 index 0000000..7918c3f --- /dev/null +++ b/_static/searchtools.js @@ -0,0 +1,574 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename] = item; + + let listItem = document.createElement("li"); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + } + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms) + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = _( + `Search finished, found ${resultCount} page(s) matching the search query.` + ); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() }); + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent !== undefined) return docContent.textContent; + console.warn( + "Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + /** + * execute search (requires search index to be loaded) + */ + query: (query) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + // array of [docname, title, anchor, descr, score, filename] + let results = []; + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + let score = Math.round(100 * queryLower.length / title.length) + results.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id] of foundEntries) { + let score = Math.round(100 * queryLower.length / entry.length) + results.push([ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // lookup as object + objectTerms.forEach((term) => + results.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + results.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item))); + + // now sort the results by score (in opposite order of appearance, since the + // display function below uses pop() to retrieve items) and then + // alphabetically + results.sort((a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; + }); + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + results = results.reverse(); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + const arr = [ + { files: terms[word], score: Scorer.term }, + { files: titleTerms[word], score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord) && !terms[word]) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord) && !titleTerms[word]) + arr.push({ files: titleTerms[word], score: Scorer.partialTitle }); + }); + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, {}); + scoreMap.get(file)[word] = record.score; + }); + }); + + // create the mapping + files.forEach((file) => { + if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1) + fileMap.get(file).push(word); + else fileMap.set(file, [word]); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords) => { + const text = Search.htmlToText(htmlText); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/_static/showcase_slides.pptx b/_static/showcase_slides.pptx new file mode 100644 index 0000000..6e46c12 Binary files /dev/null and b/_static/showcase_slides.pptx differ diff --git a/_static/sphinx_highlight.js b/_static/sphinx_highlight.js new file mode 100644 index 0000000..8a96c69 --- /dev/null +++ b/_static/sphinx_highlight.js @@ -0,0 +1,154 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore( + span, + parent.insertBefore( + rest, + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/_static/sphinx_rtd_theme_overrides.css b/_static/sphinx_rtd_theme_overrides.css new file mode 100644 index 0000000..34f124f --- /dev/null +++ b/_static/sphinx_rtd_theme_overrides.css @@ -0,0 +1,18 @@ +/* This line is theme specific - it includes the base theme CSS */ +@import 'css/theme.css'; /* for the Read the Docs theme */ + +/* Disable page width limit */ +.wy-nav-content { + max-width: None +} + +/* Make the version text darker */ +.wy-side-nav-search>div.version { + color:hsla(0, 0%, 0%, 0.7) +} + +/*Fix for Sphinx 5.0+ prints double colons for roles: https://github.com/sphinx-doc/sphinx/issues/10594*/ +/*See also: https://stackoverflow.com/questions/70426337/sphinx-documentation-showing-double-colon-in-fields*/ +html.writer-html5 .rst-content dl.field-list>dt:after { + content: "" +} diff --git a/changelog.html b/changelog.html new file mode 100644 index 0000000..ca30c0d --- /dev/null +++ b/changelog.html @@ -0,0 +1,381 @@ + + + + + + + Release History — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Release History

+
+

v0.5.0

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/contact.html b/contact.html new file mode 100644 index 0000000..0b28a0c --- /dev/null +++ b/contact.html @@ -0,0 +1,401 @@ + + + + + + + Contact — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Contact

+
+

Help!

+

todo

+
+
+

Bug reports

+

If you think you found a bug, please provide following information when contacting us:

+
+
    +
  • Your operating system name and version

  • +
  • Infos about your Pharaoh installation:

    +

    Open a terminal with your Pharaoh env loaded (e.g. in PyCharm) and type +pharaoh info or python -m pharaoh info.

    +

    That should return something like Pharaoh vX.Y.Z [Python 3.9.13, Windows-10-10.0.19045-SP0].

    +
  • +
  • Any details about your local setup that might be helpful in troubleshooting

  • +
  • The complete error message and traceback (not just a screenshot of parts of the error)

  • +
  • Log files

  • +
  • Detailed steps to reproduce the bug

  • +
  • Maybe also a list of packages in your Python env: pip freeze > pharaoh_env_packages.txt

  • +
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/dev.html b/dev.html new file mode 100644 index 0000000..39ca5d1 --- /dev/null +++ b/dev.html @@ -0,0 +1,377 @@ + + + + + + + Development — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Development

+

todo

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/examples/asset_scripts.html b/examples/asset_scripts.html new file mode 100644 index 0000000..235c812 --- /dev/null +++ b/examples/asset_scripts.html @@ -0,0 +1,380 @@ + + + + + + + Asset Scripts — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Asset Scripts

+../_images/chirping_crickets.gif +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/examples/demo_reports.html b/examples/demo_reports.html new file mode 100644 index 0000000..9e206d0 --- /dev/null +++ b/examples/demo_reports.html @@ -0,0 +1,387 @@ + + + + + + + Demo Reports — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Demo Reports

+
+

HTML

+../_images/chirping_crickets.gif +
+
+

Confluence

+../_images/chirping_crickets.gif +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/examples/index.html b/examples/index.html new file mode 100644 index 0000000..fb6b2f4 --- /dev/null +++ b/examples/index.html @@ -0,0 +1,388 @@ + + + + + + + Examples — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Examples

+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/genindex.html b/genindex.html new file mode 100644 index 0000000..851bd63 --- /dev/null +++ b/genindex.html @@ -0,0 +1,723 @@ + + + + + + Index — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + +

Index

+ +
+ _ + | A + | B + | C + | D + | E + | F + | G + | H + | I + | L + | M + | O + | P + | R + | S + | T + | U + +
+

_

+ + + +
+ +

A

+ + + +
+ +

B

+ + +
+ +

C

+ + + +
+ +

D

+ + +
+ +

E

+ + + +
+ +

F

+ + + +
+ +

G

+ + + +
+ +

H

+ + + +
+ +

I

+ + + +
+ +

L

+ + + +
+ +

M

+ + + +
+ +

O

+ + + +
+ +

P

+ + + +
    +
  • + pharaoh.api + +
  • +
  • + pharaoh.assetlib.api + +
  • +
+ +

R

+ + + +
+ +

S

+ + + +
+ +

T

+ + + +
+ +

U

+ + +
+ + + +
+
+
+ +
+ +
+

© Copyright . + Last updated on Nov 28, 2023. +

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..08c1a1b --- /dev/null +++ b/index.html @@ -0,0 +1,498 @@ + + + + + + + Pharaoh — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Pharaoh

+

Pharaoh is a Sphinx-based Python framework for +generating reports in various format (HTML, Confluence, PDF) by combining the power of configurable +Jinja templates and Python scripts for asset (tables, plots, pictures, etc…) generation.

+_images/overview.png +
+
+

Table of Contents

+ +
+

Indices and tables

+ +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/installation.html b/installation.html new file mode 100644 index 0000000..04d4537 --- /dev/null +++ b/installation.html @@ -0,0 +1,459 @@ + + + + + + + Installation — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Installation

+
+

Requirements

+

Any of the following Python versions are compatible. +If you don’t have any installed, please download and execute a version by clicking the links below.

+
+
+
+
+
+

Installation via Pip

+
+

Create a virtual environment

+

A virtual environment has its own Python binary (which matches the version of the binary that was +used to create this environment) and can have its own independent set of installed Python packages +in its site directories.

+

The easiest way to create a virtual environment, is to let the IDE PyCharm (recommended) create one for you. +Configure a virtual environment shows how to do that.

+

The other option is to create it via command line like follows and then configure PyCharm to use this as +project interpreter.

+ +
    +
  1. Open a shell (e.g. an Inicio shell or PowerShell)

  2. +
  3. Change into your workspace: cd C:\temp

  4. +
  5. Create a virtual environment: C:\Python311\python.exe -m venv pharaoh_venv

  6. +
  7. Activate it:

    +
      +
    • Powershell .\pharaoh_venv\Scripts\Activate.ps1

    • +
    • Bash: source ./pharaoh_venv/Scripts/activate

    • +
    • Cmd/Bat: .\pharaoh_venv\Scripts\activate.bat

    • +
    +
  8. +
  9. You should be getting a prefix to your command line like this: (pharaoh_venv) PS C:\temp>

  10. +
  11. Ensure you use binary packages where possible by installing wheel first pip install wheel

  12. +
+

You can find more information about virtual environments here.

+
+
+

Install

+

After the virtual environment has been created, activate it and install ifx-pharaoh via pip:

+
pip install pharaoh-report[<extras>]
+
+
+
+

Note

+

Refer to section Extras to install additional dependencies.

+
+
+

Note

+

For updating Pharaoh to the latest version, append the -U flag to the pip command

+
+

Then you would be able to run:

+
pip install pharaoh-report[<extras>]
+
+
+
+
+

Extras

+

The extras field is a comma-separated list of additional dependency groups.

+

Following extra dependencies may be installed, depending on your use case:

+
    +
  • bokeh: Installs the Bokeh plotting framework and exporters

  • +
  • holoview: Installs the Holoviews plotting framework

  • +
  • plotly: Installs the Plotly plotting framework and exporters

  • +
  • matplotlib: Installs the Matplotlib plotting framework

  • +
  • jupyter: Installs Jupyter for asset script implementation

  • +
  • pandas: Installs Pandas Data Analysis Library

  • +
  • pdf: Installs an SVG to PDF converter for the LaTeX builder

  • +
  • all-plotting: Installs all supported plotting frameworks

  • +
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/intro.html b/intro.html new file mode 100644 index 0000000..6a2adbb --- /dev/null +++ b/intro.html @@ -0,0 +1,437 @@ + + + + + + + Introduction — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Introduction

+

Pharaoh is a Sphinx-based Python framework for +generating reports in various formats (HTML, Confluence, PDF) by combining the power of configurable +Jinja templates and Python scripts for asset (tables, plots, pictures, etc…) generation.

+_images/overview.png +

Click the PowerPoint icon to download the latest presentation slides:

+
+
_images/pptx.png +
+
+

See also

+

Demo Reports (might not reflect latest development version)

+
+

Pharaoh mainly serves two user groups:

+
+
Template Designers

They are creating pre-defined configurable templates & data post-processing methods and for your project and +therefore provide a simplification layer for the end user.

+
+
End-Users

They will build reports based on the templates the template designers created. +They provide the final content (assets like plots, table, etc…) and configuration +(context to fill template variables) for the templates.

+
+
+
+

Working Principle

+

The standard Pharaoh workflow consists of following major steps:

+
    +
  1. Project Generation

    +

    Generates a Pharaoh project with one or multiple components based on predefined templates. +Its core is a fully-preconfigured Sphinx documentation project.

    +

    This generation is called first-level templating or generation-time templating +further on in the documentation.

    +

    It may be checked into GIT for incremental modifications or generated each time, depending on your project setup.

    +

    Now the templates and asset scripts in the generated components as well as the project settings +can be modified by the user.

    +_images/generation_time_templating.svg
  2. +
  3. Asset Generation

    +

    During asset generation, the asset scripts of each component are executed.

    +

    By using the resources files of its own and/or other components, +asset scripts are producing dynamic content assets for the later report, +like plots, images, tables and additional data (e.g. json) used for rendering the templates.

    +

    Those assets are then registered with metadata, so that they can be searched/included by the templates.

    +

    Each asset script may produce any amount of assets.

    +
  4. +
  5. Project Build

    +

    Translates an existing Pharaoh project to a configured target format (e.g. HTML, Confluence, LaTeX), +while performing an additional templating step via Jinja.

    +

    This is called second-level templating or build-time templating further on in the documentation.

    +

    Templates may inherit and extend existing or user-defined base-templates (for re-use purposes).

    +

    Templates may dynamically include other templates.

    +

    Templates are rendered using a rendering context (variables that can be used in template), +that contains following data:

    +
      +
    • Project Settings

    • +
    • Static/manually entered context data

    • +
    • Dynamic context data, that is created by executing a Python script (potentially accessing generated assets)

    • +
    +_images/build_flow.svg
  6. +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/objects.inv b/objects.inv new file mode 100644 index 0000000..105f13d Binary files /dev/null and b/objects.inv differ diff --git a/plugins/index.html b/plugins/index.html new file mode 100644 index 0000000..e3418ab --- /dev/null +++ b/plugins/index.html @@ -0,0 +1,387 @@ + + + + + + + Plugins — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Plugins

+
+

See also

+

Plugin Architecture

+
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/plugins/plugin.html b/plugins/plugin.html new file mode 100644 index 0000000..4a63c3f --- /dev/null +++ b/plugins/plugin.html @@ -0,0 +1,380 @@ + + + + + + + Plugin Architecture — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Plugin Architecture

+../_images/chirping_crickets.gif +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/py-modindex.html b/py-modindex.html new file mode 100644 index 0000000..57ff471 --- /dev/null +++ b/py-modindex.html @@ -0,0 +1,399 @@ + + + + + + Python Module Index — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + +

Python Module Index

+ +
+ p +
+ + + + + + + + + + + + + +
 
+ p
+ pharaoh +
    + pharaoh.api +
    + pharaoh.assetlib.api +
+ + +
+
+
+ +
+ +
+

© Copyright . + Last updated on Nov 28, 2023. +

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/reference/api.html b/reference/api.html new file mode 100644 index 0000000..5f6939e --- /dev/null +++ b/reference/api.html @@ -0,0 +1,1444 @@ + + + + + + + API Reference — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

API Reference

+
+

Pharaoh API Module

+

Lists all items that can be imported from pharaoh.api.

+

This module contains Pharaoh project-related API functions.

+
+
+get_project(lookup_path: str | Path | None = None) PharaohProject[source]
+

Returns an instance of PharaohProject.

+
+
Parameters:
+

lookup_path – If None, the function tries to return the Pharaoh singleton instance that is already loaded in +memory and raises and exception if there is none. +If given a string or Path instance, searches the given path and all its parent folders +for a pharaoh.yaml project file and returns a PharaohProject instance.

+
+
+
+ +
+
+class PharaohProject(*args, **kwargs)[source]
+
+
+PROJECT_SETTINGS_YAML = 'pharaoh.yaml'
+
+ +
+
+__init__(project_root: PathLike, overwrite: bool = False, templates: str | Iterable[str] = ('pharaoh.default_project',), template_context: dict[str, Any] | None = None, **kwargs)[source]
+

Instantiates a Pharaoh project instance using either an existing project root +(directory containing pharaoh.yaml) or creates a new project.

+
+
Parameters:
+
    +
  • project_root – A directory containing a pharaoh.yaml file

  • +
  • overwrite – If the project directory already contains files, overwrite them.

  • +
  • templates – The project template(s) to use for creating the Sphinx project files. +Maybe be any number of template names or paths to directories.

  • +
  • template_context – The Jinja rendering context for the selected project templates.

  • +
  • custom_settings – A path to a YAML file containing settings to overwrite the default project settings.

  • +
+
+
+
+ +
+
+add_component(component_name: str, templates: str | Path | Iterable[str] | Iterable[Path] = ('pharaoh.empty',), render_context: dict | None = None, resources: list[resource.Resource] | None = None, metadata: dict[str, Any] | None = None, index: int = -1, overwrite: bool = False)[source]
+

Adds a new component to the Pharaoh project

+

Example:

+
from pharaoh.api import FileResource, PharaohProject
+
+proj = PharaohProject(".")
+proj.add_component(
+    component_name="component_ABC",
+    templates=[
+        "plugin_abc.template_xyz",              # plugin template
+        "path/to/template/directory",           # template directory
+        "path/to/template/file/tmpl.pharaoh.py" # template file
+    ],
+    render_context={"foo": "bar"},
+    resources=[FileResource(alias="dlh5_result", pattern="C:/temp/**/*.dlh5")],
+    metadata={"some tag": "some value"},
+    index=-1,  # append
+    overwrite=False
+)
+
+
+
+
Parameters:
+
    +
  • component_name – The name of the component. Must be a valid Python identifier.

  • +
  • templates

    A list of component templates to use for creating the components project files. +Those may be the template identifier (e.g. plugin_abc.template_xyz) of a registered plugin template, +a path to a template directory or a path to a template file (single-file template).

    +

    Since multiple template may be specified, their order matters in cases where different templates +create equally-named files, thus templates might overwrite files of the previous templates.

    +

    This enables template-composition, where template designers can chunk their bigger templates into +smaller re-usable building blocks.

    +

    If omitted, an empty default template is used.

    +

  • +
  • render_context – The Jinja rendering context for the selected template. +The actual template rendering context will have component_name, resources and +metadata available under the respective keys.

  • +
  • resources – A list of Resource instances, defining the component’s resources used in asset scripts

  • +
  • metadata – A dictionary of metadata that may be used to find the component via +PharaohProject.find_component() method.

  • +
  • overwrite – If True, an already existing component will be overwritten

  • +
+
+
+
+ +
+
+add_template_to_component(component_name: str, templates: str | Path | Iterable[str], render_context: dict | None = None)[source]
+

Adds additional templates to an existing component, that may overwrite existing files during rendering.

+
+
Parameters:
+
    +
  • component_name – The name of the component. Must be a valid Python identifier.

  • +
  • templates – The component template(s) to use for creating the components project files. +Maybe be the a single or multiple template names or paths to a directory.

  • +
  • render_context – The Jinja rendering context for the selected template. +The actual template rendering context will have component_name, resources and metadata +available under the respective keys.

  • +
+
+
+
+ +
+
+archive_report(dest: str | Path | None = None) Path[source]
+

Create an archive from the build folder.

+
+
Parameters:
+

dest – A destination path to create the archive. Relative paths are relative to the project root. +If omitted, the filename will be taken from the report.archive_name setting.

+
+
Returns:
+

The path to the archive

+
+
+
+ +
+
+property asset_build_dir
+
+ +
+
+property asset_finder: AssetFinder
+
+ +
+
+build_report(catch_errors=True) int[source]
+

Builds the Sphinx project and returns the status code.

+
+
Parameters:
+

catch_errors – If True, Sphinx build errors will not raise an exception but return a -1 instead.

+
+
+
+ +
+
+find_components(expression: str = '') list[DictConfig][source]
+

Find components by their metadata using an evaluated expression.

+

The expression must be a valid Python expression and following local variables may be used:

+
    +
  • name: The name of the component

  • +
  • templates: A list of templates used to render the component

  • +
  • metadata: A dict of metadata specified

  • +
  • render_context: The Jinja rendering context specified

  • +
  • resources: A list of resource definitions

  • +
+
+
Parameters:
+

expression

A Python expression that will be evaluated. If it evaluates to a truthy result, +the component name is included in the returned list.

+

Example: name == "dummy" and metadata.foo in (1,2,3).

+

A failing evaluation will be treated as False. An empty expression will always match.

+

+
+
+
+ +
+
+generate_assets(component_filters: Iterable[str] = ('.*',)) list[Path][source]
+

Generate all assets by executing the asset scripts of a selected or all components.

+

All asset scripts are executed in separate parallel child processes +(number of workers determined by asset_gen.worker_processes setting; +setting 0 executes all asset scripts sequentially in the current process).

+

Setting asset_gen.script_ignore_pattern determines if a script is ignored.

+

Putting the comment # pharaoh: ignore at the start of a script will also ignore the file.

+
+
Parameters:
+

component_filters – A list of regular expressions that are matched against each component name. +If a component name matches any of the regular expressions, the component’s +assets are regenerated (containing directory will be cleared)

+
+
+
+ +
+
+get_default_sphinx_configuration(confdir: str | Path)[source]
+

Provides Sphinx project configurations, that will be dynamically included by the generated conf.py.

+
+ +
+
+get_resource(alias: str, component: str) Resource[source]
+

Finds a Resource from a project component by its alias.

+
+
Parameters:
+
    +
  • component – The component’s name

  • +
  • alias – The resource’s alias

  • +
+
+
+
+ +
+
+get_setting(key: str, default=<object object>, to_container: bool = False, resolve: bool = True)[source]
+

Gets a setting by its dot-separated name, e.g. “core.debug”.

+

The settings are preferably taken from environment variables (e.g. PHARAOH.CORE.DEBUG), whereas the values +must be TOML compatible values (e.g. “true” for boolean).

+

If no environment variable is found, the user defined settings in the pharaoh.yaml file in the +project root will be used. If the setting is not specified in there or the file is not existing, +the Pharaoh default settings are queried.

+

If the setting is not found, a LookupError is returned unless “default” is set.

+

Examples:

+
proj.get_setting("report.title")
+proj.get_setting("cloud.metadata", to_container=True, resolve=False) == {
+    "author": "${report.author}",
+    "title": "${report.title}",
+}
+proj.get_setting("cloud.metadata", to_container=True, resolve=True) == {
+    "author": "loibljoh",
+    "title": "Pharaoh Report",
+}
+proj.get_setting("cloud.metadata.title") == "Pharaoh Report"
+proj.get_setting("cloud.metadata") == omegaconf.dictconfig.DictConfig(...)
+
+
+
+
Parameters:
+
    +
  • key – Dot-separated name of the setting value to return

  • +
  • default – The returned default value if the key does not exist.

  • +
  • to_container – If set, recursively converts an OmegaConf config to a primitive Python container type +(dict or list).

  • +
  • resolve – True to resolve all values if “to_container” is set to True

  • +
+
+
+
+ +
+
+get_settings() DictConfig[source]
+

Returns merged settings from all namespaces.

+
+ +
+
+iter_components() Iterator[omegaconf.DictConfig][source]
+

Returns an iterator over all components from a project.

+
+ +
+
+iter_resources(component: str) Iterator[omegaconf.DictConfig][source]
+

Returns an iterator over all resources from a project component.

+
+
Parameters:
+

component – The component’s name

+
+
+
+ +
+
+load_settings(namespace: str = 'all')[source]
+

Loads settings from various namespaces.

+
+
default

The default setting in the Pharaoh library

+
+
project

The project settings that may be modified by the user

+
+
env

The settings defined by environment variables starting with PHARAO

+
+
all

Loads all of the above

+
+
+
+
Parameters:
+

namespace – The namespace to load settings from. all, default, project or env.

+
+
+
+ +
+
+open_report()[source]
+

Opens the generated report (if possible, e.g. for local HTML reports).

+
+ +
+
+property project_root
+
+ +
+
+put_setting(key: str, value: Any)[source]
+

Sets a setting by its dot-separated name, e.g. “core.debug”.

+

This has no effect, if there is an environment variable defining the same setting!

+
+ +
+
+remove_component(filter: str, regex: bool = False) list[str][source]
+

Removes one or multiple existing components.

+
+
Parameters:
+
    +
  • filter – A case-insensitive component filter. Either a full-match or regular expression, depending on +regex argument.

  • +
  • regex – If True, the filter argument will be treated as regular expression. Components +that partially match the regular expression are removed.

  • +
+
+
Returns:
+

A list of component names that got removed

+
+
+
+ +
+
+save_settings(include_env: bool = False)[source]
+

Saves back the project settings to the project YAML file.

+
+
Parameters:
+

include_env – If True, Pharaoh settings that are set via environment variables will be persisted +to the project settings YAML file.

+
+
+
+ +
+
+property settings_file
+
+ +
+
+property sphinx_report_build
+
+ +
+
+property sphinx_report_project
+
+ +
+
+property sphinx_report_project_components
+
+ +
+
+update_resource(alias: str, component: str, resource: Resource)[source]
+

Updates a Resource from a project component by its alias.

+
+
Parameters:
+
    +
  • component – The component’s name

  • +
  • alias – The resource’s alias

  • +
  • resource – The resource’s alias

  • +
+
+
+
+ +
+ +
+
+

Asset Generation API Module

+

Lists all items that can be imported from pharaoh.assetlib.api.

+

This module contains API functions related to asset generation. +When asset scripts are accessing those API functions, the functions can access the project information by themselves.

+
+
+find_components(expression: str = '')[source]
+

Find components by their metadata using an evaluated expression.

+

The expression must be a valid Python expression and following local variables may be used:

+
    +
  • name: The name of the component

  • +
  • templates: A list of templates used to render the component

  • +
  • metadata: A dict of metadata specified

  • +
  • render_context: The Jinja rendering context specified

  • +
  • resources: A list of resource definitions

  • +
+
+
Parameters:
+

expression

A Python expression that will be evaluated. If it evaluates to a truthy result, +the component name is included in the returned list.

+

Example: name == "dummy" and metadata.foo in (1,2,3).

+

A failing evaluation will be treated as False. An empty expression will always match.

+

+
+
+
+ +
+
+get_asset_finder() AssetFinder[source]
+

Returns the AssetFinder instance for the current project

+
+ +
+
+get_current_component() str[source]
+

If executed from within a script that is placed inside a Pharaoh component, the function returns the components +name by analyzing the call stack.

+
+ +
+
+get_resource(alias: str, component: str | None = None)[source]
+

Get a Resource instance by its component name and resource alias.

+
+ +
+
+metadata_context(context_name='', **context) MetadataContext
+

Returns a context manager that adds a new stack entry with custom metadata, +effectively updating the metadata context assets are exported with.

+

When the context manager is left, the stack entry is removed again.

+

See Metadata Stack docs.

+
+
Parameters:
+
    +
  • context_name – An optional name for the context in case it must be looked up.

  • +
  • context – Keywords arguments to specify custom metadata.

  • +
+
+
+
+ +
+
+register_asset(file: str | Path, metadata: dict | None = None, template: str | None = None, data: BytesIO | None = None, copy2build: bool = False, **kwargs) Asset[source]
+

Register an asset manually. The file will be copied (if data is None and ‘file’ is a real file) or +written (if data is given) to the asset build folder of the current Pharaoh project.

+
+
Parameters:
+
    +
  • file – The filename. Must exist even if data is set, to have a filename to store the asset and to +automatically determine the template (if not set via template argument).

  • +
  • metadata – Additional metadata to store on the asset.

  • +
  • template

    The template used to render the asset. If omitted, it is inferred by the file extension.

    +
    +

    See also

    +

    Asset Templates

    +
    +

  • +
  • data – An io.BytesIO instance. Used if the asset is generated in memory and should be stored to disk by +this function.

  • +
  • copy2build

    If True, the asset will be copied to the asset build directory, +even if not referenced in the template.

    +

    Background: Pharaoh stores all assets in the project directory and copies them to the build directory only if +copy2build is set to True or on-demand by Pharaoh. For example if an HTML file is rendered using an iframe, +the HTML file has to be copied to the build folder where the iframe can later include it.

    +

  • +
+
+
Returns:
+

The file path where the asset will be actually stored

+
+
+
+ +
+
+register_templating_context(name: str, context: str | Path | dict | list, metadata: dict | None = None, **kwargs)[source]
+

Register a data context for the build-time templating stage. +The data may be given directly as dict/list or via a json or yaml file.

+

This function is designed to be used within asset scripts, to easily register data you extract from resources +for the templating process.

+

Example:

+
from pharaoh.assetlib.api import register_templating_context
+
+register_templating_context(name="foo", context={"bar": "baz"})
+# will be accessed like this: {{ ctx.local.foo.bar.baz }}
+
+
+
+
Parameters:
+
    +
  • name

    The name under which the data context is available inside Jinja templates. +Access like this (name: mycontext):

    +
    {% set mycontext = ctx.local.mycontext %}
    +
    +
    +

  • +
  • context – Either a str or Path instance pointing to a json or yaml +file, or a dict or list. All data must contain only json-compatible types, otherwise the data cannot be stored.

  • +
  • metadata – The given context will be internally registered as an asset with following metadata: +dict(pharaoh_templating_context=name, **metadata)

  • +
  • kwargs – Keyword arguments that are mostly (except component) passed to json.dumps(...), in case +context is a dict or list.

  • +
+
+
+
+ +
+
+class AssetFinder(lookup_path: Path)[source]
+
+
+__init__(lookup_path: Path)[source]
+

A class for discovering and searching generated assets.

+

An instance of this class will be created by the Pharaoh project, where lookup_path will be set to +report_project/.asset_build.

+
+
Parameters:
+

lookup_path – The root directory to look for assets. It will be searched recursively for assets.

+
+
+
+ +
+
+discover_assets(components: list[str] | None = None) dict[str, list[Asset]][source]
+

Discovers all assets by recursively searching for *.assetinfo files and stores +the collection as instance variable (_assets).

+
+
Parameters:
+

components – A list of components to search for assets. +If None (the default), all components will be searched.

+
+
Returns:
+

A dictionary that maps component names to a list of Asset instances.

+
+
+
+ +
+
+get_asset_by_id(id: str) Asset | None[source]
+

Returns the corresponding Asset instance for a certain ID.

+
+
Parameters:
+

id – The ID of the asset to return

+
+
Returns:
+

An Asset instance if found, None otherwise.

+
+
+
+ +
+
+iter_assets(components: str | Iterable[str] | None = None) Iterator[Asset][source]
+

Iterates over all discovered assets.

+
+
Parameters:
+

components – A list of component names to search. If None (the default), all components will be searched.

+
+
Returns:
+

An iterator over all discovered assets.

+
+
+
+ +
+
+search_assets(condition: str, components: str | Iterable[str] | None = None) list[Asset][source]
+

Searches already discovered assets (see discover_assets()) that match a condition.

+
+
Parameters:
+
    +
  • condition

    A Python expression that is evaluated using the content of the *.assetinfo JSON file +as namespace. If the evaluation returns a truthy result, the asset is returned.

    +

    Refer to this example assetinfo file to see the available default namespace.

    +

    Example:

    +
    # All HTML file where the "label" metadata ends with "_plot"
    +finder.search_assets('asset.suffix == ".html" and label.endswith("_plot")')
    +
    +
    +

  • +
  • components – A list of component names to search. If None (the default), all components will be searched.

  • +
+
+
Returns:
+

A list of assets whose metadata match the condition.

+
+
+
+ +
+ +
+
+class Asset(info_file: Path)[source]
+

Holds information about a generated asset.

+
+
Variables:
+
    +
  • id – An MD5 hash of the asset’s filename, prefixed with “__ID__”. +Since a unique suffix is included in the filename, this ID hash is also unique. +Can be used to quickly find this Asset instance.

  • +
  • infofile (Path) – Absolute path to the *.assetinfo file

  • +
  • assetfile (Path) – Absolute path to the actual asset file

  • +
  • context (omegaconf.DictConfig) – The content of infofile parsed into a OmegaConf dict.

  • +
+
+
+
+ +
+
+asset_groupby(seq: Iterable[Asset], key: str, sort_reverse: bool = False, default: str | None = None) dict[str, list[Asset]][source]
+

Groups an iterable of Assets by a certain metadata key.

+

During build-time rendering this function will be available as Jinja global function +asset_groupby and alias agroupby.

+

Example:

+
We have following 4 assets (simplified notation of specified metadata):
+Asset[a="1", b="3"]
+Asset[a="1", c="4"]
+Asset[a="2", b="3"]
+Asset[a="2", c="4"]
+
+Grouping by "a":
+    asset_groupby(assets, "a")
+will yield
+    {
+        "1": [Asset[a="1", b="3"], Asset[a="1", c="4"]],
+        "2": [Asset[a="2", b="3"], Asset[a="2", c="4"]],
+    }
+
+
+Grouping by "b" and default "default":
+    asset_groupby(assets, "b", default="default")
+will yield
+    {
+        "3":       [Asset[a="1", b="3"], Asset[a="2", b="3"]],
+        "default": [Asset[a="1", c="4"], Asset[a="2", c="4"]],
+    }
+
+
+
+
Parameters:
+
    +
  • seq – The iterable of assets to group

  • +
  • key – The nested attribute to use for grouping, e.g. “A.B.C”

  • +
  • sort_reverse – Reverse-sort the keys in the returned dictionary

  • +
  • default – Sort each item, where “key” is not an existing attribute, into this default group

  • +
+
+
Returns:
+

A dictionary that maps the group names (values of A.B.C) to a list of items out of the input iterable

+
+
+
+ +
+
+

Resources

+
+
+class FileResource(alias: str, pattern, sort: str = 'descending', *, cachedir: str = '')[source]
+

Bases: LocalResource

+

A resource that matches files/directories using a wildcard pattern.

+

Example:

+
FileResource(alias="mycsvfile", pattern="C:/temp/*.csv", sort="ascending")
+
+
+
+
Variables:
+
    +
  • pattern – A pathname pattern. +The pattern may contain simple shell-style wildcards. +Filenames starting with a dot are special cases that are not matched by ‘*’ and ‘?’ patterns.

  • +
  • sort

    descending (default) or ascending. +Sorting is done using the natsort library to sort filesystem paths naturally. Example:

    +
    0.txt, 01.txt, 1.txt, 10.txt, 2.txt  # default sorting (ascending)
    +0.txt, 01.txt, 1.txt, 2.txt, 10.txt  # natural sorting (ascending)
    +
    +
    +

  • +
+
+
+
+
+first_match() Path[source]
+

Returns the first match for the file pattern, depending on the chosen sort order.

+
+ +
+
+get_files(recursive=True) list[Path][source]
+

Returns all matches for the file pattern, depending on the chosen sort order.

+
+
Parameters:
+

recursive – If recursive is true, the pattern ‘**’ will match any files and +zero or more directories and subdirectories.

+
+
+
+ +
+
+get_match(index) Path[source]
+

Returns the n-th match for the file pattern, depending on the chosen sort order.

+
+ +
+
+last_match() Path[source]
+

Returns the last match for the file pattern, depending on the chosen sort order.

+
+ +
+
+locate() Path[source]
+

Returns the first match for the file pattern, depending on the chosen sort order.

+
+ +
+ +
+
+class CustomResource(alias: str, traits: dict, *, cachedir: str = '')[source]
+

Bases: Resource

+

A resource that can hold arbitrary information. It may be used as a temporary replacement for +more specific resources that are not yet implemented. +For example, it could hold database connection properties or other custom data.

+

Example:

+
CustomResource(alias="foo", traits=dict(a=1, b=[2, 3], c=dict(d=[4, 5])))
+
+
+
+
Variables:
+

traits – A dict with arbitrary data.

+
+
+
+ +

Bases:

+
+
+
+class Resource(alias: str, *, cachedir: str = '')[source]
+

Bases: object

+

The base class for all resources. Provides serialization/deserialization methods.

+
+ +
+
+class LocalResource(alias: str, *, cachedir: str = '')[source]
+

Bases: ABC, Resource

+

Represents a resource that is located on the user’s hard-drive.

+
+
+abstract locate() Path[source]
+

Returns the path to the resource file.

+
+ +
+ +
+
+class TransformedResource(alias: str, sources: list[str], *, cachedir: str = '')[source]
+

Bases: LocalResource, ABC

+

A resource that is depending on another resource and maybe transforms it dynamically into another resource.

+
+
+abstract transform(resources: dict[str, Resource])[source]
+

Executed by Pharaoh before asset generation. +Transforms the linked resource into a new one. +The resulting resource may be cached or recreated each time.

+
+ +
+ +
+
+
+

Matlab Integration API

+
+
+class Matlab(start_options='-nodesktop')[source]
+

Matlab engine for Pharaoh asset generation.

+

Usage:

+
from pharaoh.assetlib.api import Matlab
+
+eng = Matlab()
+with eng:
+    out, err = eng.execute_script("myscript.m")
+    result, out, err = eng.execute_function("myfunc", [800.0], nargout=1)
+
+
+
+
+__enter__()[source]
+

Context manager enter. Connects to Matlab.

+
+ +
+
+__exit__(exc_type, exc_val, exc_tb)[source]
+

Context manager exit. Disconnects from Matlab.

+
+ +
+
+__init__(start_options='-nodesktop')[source]
+

Matlab engine for Pharaoh asset generation.

+
+
Parameters:
+

start_options – See options at https://de.mathworks.com/help/matlab/ref/matlabwindows.html

+
+
+
+ +
+
+connect()[source]
+

Connects to a Matlab engine. If an engine is running it will be connected, otherwise a new Matlab instance +will be started and connected.

+
+ +
+
+property eng: me.MatlabEngine
+

Returns the matlab engine instance

+
+ +
+
+execute_function(function_name: str, args: list[Any] | None = None, nargout: int = 0, workdir: str | Path | None = None) tuple[Any, str, str][source]
+

Executes a Matlab function

+

Returns a tuple of result, stdout stream and stderr stream of the function execution.

+

The shape/type of result depends on the argument ‘nargout’. See below.

+
+
Parameters:
+
    +
  • function_name

    The name of the function to execute.

    +

    The function must be in the Matlab path to execute it.

    +

    Alternatively the workdir argument can be set to the parent directory of the function.

    +

  • +
  • args – A list of positional input arguments to the function

  • +
  • nargout

    The number of output arguments.

    +

    If 0, the result return value of this function will be None.

    +

    If 1, the result return value will be a single value.

    +

    If greater than 1, the result return value will be a tuple containing nargout values.

    +

  • +
  • workdir – The Matlab working directory to be changed to during function execution. Skipped if None.

  • +
+
+
+
+ +
+
+execute_script(script_path: str | Path) tuple[str, str][source]
+

Executes a Matlab script.

+

Returns a tuple of strings containing the stdout and stderr streams of the script execution.

+
+ +
+
+show_gui()[source]
+

Makes the Matlab GUI visible.

+
+ +
+ +
+
+

Templating Builtins

+

Following globals, filters and tests are available during build-time templating.

+
+

Jinja2 Builtins

+

All Jinja2 builtins are available of course:

+ +
+
+

Pharaoh Extension

+

Pharaoh extends the template environment by additional globals and filters, which are described in following chapters.

+
+

Globals

+

Following functions are available in the global templating scope, and can be used like variable, but all callable, e.g. +{{ h1("Title") }}.

+
+
+assert_true(statement: bool, message: str = '')[source]
+

Wrapper for Python’s assert builtin.

+
+
Parameters:
+
    +
  • statement – A boolean statement

  • +
  • message – The message for the AssertionError, if statement is False.

  • +
+
+
+
+ +
+
+read_text(file) str[source]
+

Reads a file content using utf-8 encoding.

+
+ +
+
+heading(text: str, level: int) str[source]
+

Renders a heading for a certain level.

+

Shortcuts for this function are automatically generated with names h1…h7.

+

Examples:

+
{{ heading("Page Title", 1) }}
+{{ h2("Sub-Title") }}
+
+
+
+
Parameters:
+
    +
  • text – The heading

  • +
  • level – The heading level. From 1 (top-level) to 7.

  • +
+
+
+
+ +
+
+raise_helper(msg)[source]
+

Raises an Exception with a certain message. The actual name of the Jinja global function is raise.

+
+ +
+
+hrule()[source]
+

Renders a 1px gray horizontal rule using a raw html directive.

+
+ +
+
+fglob(pattern: str, root: str = '.') list[Path][source]
+

Returns a list of relative paths of all files relative to a root directory that match a certain pattern.

+

Examples:

+
{% set index_files1 = fglob("index_*.rst") %}
+{% set index_files2 = fglob("subdir/index_*.rst") %}
+
+
+
+
Parameters:
+
    +
  • pattern – A glob pattern to match files.

  • +
  • root – The root directory for discovering files and anchor for relative paths. +Default is the current working directory (the parent directory of the currently rendered file).

  • +
+
+
+
+ +
+
+rand_id(chars: int | None = None) str[source]
+

Returns a unique id for usage in HTML as it is made sure it starts with no number

+
+ +
+
+

Filters

+

Variables can be modified by filters. Filters are separated from the variable by a pipe symbol (|) +and may have optional arguments in parentheses. +Multiple filters can be chained. The output of one filter is applied to the next.

+

For example, {{ name|trim|title }} will trim whitespace from the variable name and title-case the output.

+

Filters that accept arguments have parentheses around the arguments, just like a function call. +For example: {{ listx|join(', ') }} will join a list with commas.

+
+
+or_default(value, default)[source]
+

Returns the result of value or default.

+

Alias: or_d

+
+ +
+
+to_path(path: str) Path[source]
+

Returns path as a pathlib.Path instance.

+
+ +
+
+oc_resolve(value: DictConfig)[source]
+

Recursively converts an OmegaConf config to a primitive container (dict or list) and returns it.

+
+ +
+
+required(value)[source]
+

Raises an UndefinedError if the context variable is undefined. +The actual name of the Jinja global function is req.

+

Example:

+
{{ h1(ctx.local.test.test_name|req) }}
+
+
+
+ +
+
+hasattr_(obj, name)[source]
+
+ +
+
+oc_get(cfg: ~omegaconf.dictconfig.DictConfig, key, default=<object object>)[source]
+

Returns an object inside an omegaconf.DictConfig container. +If the object does not exist, the default is returned.

+

Example:

+
{% set plot_title = asset.context|oc_get("plot.title", "") %}
+
+
+
+
Parameters:
+
    +
  • cfg – The omegaconf.DictConfig container to search

  • +
  • key – The name of the object to extract, e.g. plot.title.

  • +
  • default – The default to return if key is not in cfg

  • +
+
+
+
+ +
+
+rep(value) str[source]
+

Returns repr(value). The actual name of the Jinja global function is repr.

+
+ +
+
+md2html(text)[source]
+

Converts the Markdown text (CommonMark syntax) to HTML.

+
+ +
+
+exists(path: str) bool[source]
+

Returns if path exists.

+
+ +
+
+
+

Ansible Filters

+

Jinja2 Ansible Filters is a port of the ansible filters provided by Ansible’s templating engine.

+

See pypi project page for a listing of all filters.

+

The filters are useful, but unfortunately not documented. You may refer to the +docstrings in the source code.

+
+
+
+

Plugin API

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/reference/assets.html b/reference/assets.html new file mode 100644 index 0000000..076e613 --- /dev/null +++ b/reference/assets.html @@ -0,0 +1,1012 @@ + + + + + + + Assets — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Assets

+
+

What’s an Asset?

+

Each Pharaoh components has a directory called asset_scripts, that contain scripts Python scripts or +Jupyter Notebooks, that extract information from resources (local result files like HDF5, databases or other sources) +and transform this information into plot, tables and any other kind of types.

+

The generated files are called Assets and may be directly referenced/embedded by a template +(like a picture, plot or table) or indirectly referenced by a template to provide context values for template rendering.

+

The component’s templates then consume these assets while Sphinx renders the template to HTML. +Consume means that some of the assets are directly included in the template via the +Pharaoh asset directive and others are read by +local context scripts to use their content during templating.

+
+
+

Executing Asset Generation

+

Asset generation is executed before the actual Sphinx build via +generate_assets().

+
+
+PharaohProject.generate_assets(component_filters: Iterable[str] = ('.*',)) list[Path][source]
+

Generate all assets by executing the asset scripts of a selected or all components.

+

All asset scripts are executed in separate parallel child processes +(number of workers determined by asset_gen.worker_processes setting; +setting 0 executes all asset scripts sequentially in the current process).

+

Setting asset_gen.script_ignore_pattern determines if a script is ignored.

+

Putting the comment # pharaoh: ignore at the start of a script will also ignore the file.

+
+
Parameters:
+

component_filters – A list of regular expressions that are matched against each component name. +If a component name matches any of the regular expressions, the component’s +assets are regenerated (containing directory will be cleared)

+
+
+
+ +

Besides calling the API function, also the CLI can be used:

+
    +
  • From within PyCharm or wherever you have the venv activated that has Pharaoh installed, you can use the +Pharaoh CLI:

    +
    cd <your Pharaoh project directory>
    +pharaoh generate  # for generating assets of all components
    +pharaoh generate -f "dummy_.*"  # for generating assets of all components starting with "dummy_"
    +pharaoh generate -f dummy_1  -f dummy_2  # only for dummy_1 and dummy_2
    +
    +
    +
  • +
  • Via a terminal (PS or CMD) from within the project directory, where CMD scripts are generated for your convenience:

    +
    .\pharaoh-generate-assets.cmd  # for generating assets of all components
    +.\pharaoh.cmd generate -f dummy_1
    +
    +
    +
  • +
+
+
+

Debugging Asset Scripts

+

Asset scripts can be debugged in two different ways:

+
    +
  • Via Pharaoh Asset Generation

    +

    Generated Pharaoh project ship a debug script debug.py. +Just open your generated project in an IDE with an interpreter/venv that has Pharaoh installed and start a debug +session on debug.py.

    +

    The script should be modified to your current needs, e.g. if you just want to debug asset generation, +comment out the proj.build_report() line. If you like to debug asset scripts of a single component, +pass the component name like this proj.generate_assets(component_filters=("dummy_1",)).

    +
    +

    Important

    +

    If the setting asset_gen.worker_processes is set to a non-zero integer or "auto", +then asset scripts are executed as child processes.

    +

    Make sure you have the PyCharm debugger option +File | Settings | Build, Execution, Deployment | Python Debugger -> +Attach to subprocess automatically while debugging enabled.

    +
    +
  • +
  • Directly debug your script

    +

    You can execute your asset scripts directly via your IDE of choice. Any functions you import from +pharaoh.api or +pharaoh.assetlib.api are built in a way to support +being executed outside of a Pharaoh asset generation.

    +

    The main difference is, that Pharaoh is not monkey-patching any toolkit (Plotly, Bokeh, Pandas) function, +so the functions are behaving according to their official documentation. +So statements like `fig.write_image("myplot.png") are storing the plot in the current working directory +(this is the same directory your asset script is in), instead of report-project\.asset_build.

    +
    +

    See also

    +

    Patched Frameworks

    +
    +
  • +
+
+
+

Implementing Asset Scripts

+

This section shows how asset scripts are developed.

+

For simplicity we will show first how to generate assets without the use of resources. +An example on how to access resources can be found in the Resources Reference.

+

Refresher: A components asset scripts are always located in the component’s subdirectory asset_scripts.

+

The script names do not matter but certain names may be ignored +(see generate_assets()).

+

Let’s assume we want to make a plot as our first asset in a script called my_plot.py:

+
import plotly.express as px
+
+from pharaoh.assetlib.api import metadata_context
+
+fig = px.scatter(
+    data_frame=px.data.iris(),  # Famous example IRIS data set
+    x="sepal_width",
+    y="sepal_length",
+    color="species",
+    symbol="species",
+    title="IRIS Example",
+)
+
+with metadata_context(label="my_plot"):
+    fig.write_html(file="iris_scatter.html")
+
+
+

Now let’s analyse what we got:

+
    +
  1. import plotly.express as px

    +

    Import a plotting framework of choice. Some frameworks are better supported by Pharaoh than others, we’ll see this +later also in the Patched Frameworks section.

    +
  2. +
  3. from pharaoh.assetlib.api import metadata_context

    +

    Any API function you will need can be imported from the pharaoh.assetlib.api module. +The purpose of metadata_context is explained later.

    +
  4. +
  5. fig = px.scatter(...)

    +

    Create a Plotly figure with some example data. +Your own scripts would actually read the resources specified with the component and create plots, tables etc.

    +
  6. +
  7. with metadata_context(label="my_plot"):

    +

    This context manager determines the metadata the asset will be stored with. +This metadata is then used to include assets in a template, in this case this would look like this:

    +
    .. pharaoh-asset::
    +    :filter: label == "my_plot"
    +    :template: iframe
    +    :iframe-width: 500px
    +    :iframe-height: 500px
    +
    +
    +

    This so-called Sphinx directive +is a custom Sphinx directive (click here for docs) +Pharaoh adds via the plugin system of Sphinx.

    +

    metadata_context is explained in more detail in the next section.

    +
  8. +
  9. fig.write_html(file="iris_scatter.html")

    +

    This is where Pharaoh is doing it’s magic.

    +

    plotly.graph_objects.Figure.write_html is one of many function which is +monkey-patched +by Pharaoh. More info about the other patched frameworks you can find here.

    +

    If the asset script is executed via Pharaoh, the patched write_html function will not store the plot to +"iris_scatter.html" in the current working directory, but rather in a predefined location for assets inside +your Pharaoh project report_project/.asset_build/<component-name> with a unique suffix, for example +iris_scatter_9c30799b.html.

    +

    Next to the actual asset, an asset-info file iris_scatter_9c30799b.assetinfo +is stored that holds the metadata for the asset. +Here is a (shortened) content example:

    +
    {
    +    "asset": {
    +        "script_name": "my_plot.py",
    +        "script_path": "C:\\...\\project\\report-project\\components\\dummy_1\\asset_scripts\\my_plot.py",
    +        "index": 1,
    +        "component_name": "dummy_1",
    +        "user_filepath": "iris_scatter.html",
    +        "file": "C:\\...\\project\\report-project\\.asset_build\\dummy_1\\iris_scatter_9c30799b.html",
    +        "name": "iris_scatter_9c30799b.html",
    +        "stem": "iris_scatter_9c30799b",
    +        "suffix": ".html",
    +        "template": "raw_html"
    +    },
    +    "label": "my_plot"
    +}
    +
    +
    +

    This has following advantages:

    +
      +
    • Finding names for assets is completely unnecessary, since they get a unique name assigned and are included in +templating through their metadata rather than their file name. +You could basically name all your assets asset.<suffix>.

      +

      So for example if you would create a single plot of a signal for 100 operating conditions like Vdd, +you would not add the value of Vdd to the file name, but rather put it in the metadata +(see here) and name the file always "<signal-name>.png".

      +
      +

      Note

      +

      Though the assets name does not matter, its suffix does. +It is used internally so make sure you add it.

      +
      +
    • +
    • If the script is directly executed by the user, write_html is not patched and saves the plot to +"iris_scatter.html" in the current working directory.

    • +
    +
  10. +
+
+

See also

+

Asset Script Examples

+
+
+

Metadata Stack

+

The metadata stack is a construct that helps assigning metadata to generated assets.

+

Use the context manager returned by +metadata_context() in your asset scripts +to dynamically add/remove/update metadata that is added to your assets.

+

Let’s explained based on an example:

+
from pharaoh.assetlib.api import metadata_context, register_asset
+
+# Will have no metadata (except the default ones added by Pharaoh)
+register_asset("<some-file>")
+
+# Will have metadata: {"foo": "bar"} only
+register_asset("<some-file>", dict(foo="bar"))
+
+with metadata_context(a=1):
+    register_asset("<some-file>")  # Will have metadata: {"a": 1} only
+
+    with metadata_context(a=2, b=3):
+        register_asset("<some-file>")  # Will have metadata: {"a": 2, "b": 3}
+
+    with metadata_context(c=4):
+        register_asset("<some-file>")  # Will have metadata: {"a": 1, "c": 4}
+
+    for i in range(10):
+        with metadata_context(e=i):
+            register_asset("<some-file>")  # Will have metadata: {"a": 1, "e": i}
+
+
+
+

Note

+

If you don’t want to use the context manager, because the metadata should be set globally in the script anyway +or you’re asset script is a Jupyter Notebook (no context manager over multiple cells possible), +you can also use the activate() and deactivate() method:

+
metadata_context(...).activate()
+register_asset("<some-file>")
+
+
+

Or if you want to deactivate the context again:

+
mc = metadata_context(...).activate()
+register_asset("<some-file>")
+mc.deactivate()
+
+
+
+

The function register_asset() is explained in the next section.

+
+
+

Manually Registering Assets

+

The function register_asset() may be used to manually register +assets of any type.

+

This is mainly used if you like to create assets that are not generated via +patched framework functions, like txt, json, rst files or +generated via frameworks not yet supported, like seaborn.

+

Here some examples:

+
register_asset("some-path.html", dict(label="my_html_snippet"), template="raw_html")
+
+data = b"""<div><p>This HTML text was generated by an asset script!</p></div>"""
+register_asset("raw.html", dict(label="my_html_snippet"), template="raw_html", data=io.BytesIO(data))
+
+data = b""".. important:: This reStructuredText text was generated by an asset script!"""
+register_asset("raw.rst", dict(label="my_rst_snippet"), template="raw_rst", data=io.BytesIO(data))
+
+data = b'This text was generated by an asset script and will be included via "literalinclude"'
+register_asset("raw.txt", dict(label="my_txt_snippet"), template="raw_txt", data=io.BytesIO(data))
+
+
+
+
+

Patched Frameworks

+

Pharaoh monkey-patches +plot/table export functions of popular tools like Pandas, Plotly etc.

+

This has the advantage, that users can work with the official plotting APIs of the respective frameworks +without having to take care about Manually Registering Assets.

+

The following APIs are patched by Pharaoh:

+
+
Pandas
+

Examples:

+
import pandas as pd
+
+df = pd.DataFrame(
+    {
+        "blabla": ["a", "b", "c"],
+        "ints": [1, 2, 3],
+        "float": [1.5, 2.5, 3.5],
+        "hex": ["0x11", "0x12", "0x13"],
+    }
+)
+df.to_html(buf="table.html")
+
+
+
+
Plotly
+

Examples:

+
import plotly.express as px
+
+fig = px.scatter(
+    data_frame=px.data.iris(), x="sepal_width", y="sepal_length",
+    color="species", symbol="species", title=r"A title",
+)
+
+fig.write_image(file="iris_scatter1.svg")
+fig.write_html(file="iris_scatter2.html")
+fig.write_image(file="iris_scatter3.png", width=500, height=500)
+
+
+
+
+

Bokeh

+
+
+

Examples:

+
from bokeh.io import save
+from bokeh.plotting import figure
+from bokeh.sampledata.iris import flowers
+
+colormap = {"setosa": "red", "versicolor": "green", "virginica": "blue"}
+colors = [colormap[x] for x in flowers["species"]]
+
+p = figure(title=f"Iris Morphology", width=400, height=400)
+p.xaxis.axis_label = "Petal Length"
+p.yaxis.axis_label = "Petal Width"
+p.scatter(flowers["petal_length"], flowers["petal_width"], color=colors, fill_alpha=0.2, size=10)
+
+save(p, filename="iris_scatter.html")
+
+
+
+

Matplotlib

+
+
+

Examples:

+
import matplotlib.pyplot as plt
+import numpy as np
+
+fig, ax = plt.subplots()
+ax.plot(np.arange(0.0, 2.0, 0.01), 1 + np.sin(2 * np.pi * t))
+ax.set(xlabel='time (s)', ylabel='voltage (mV)', title='About as simple as it gets, folks')
+ax.grid()
+
+fig.savefig("test.png")
+plt.show()
+
+
+
+
+
Holoviews
+

Examples:

+
import holoviews as hv
+data = [(i, chr(97 + j), i * j) for i in range(5) for j in range(5) if i != j]
+
+with metadata_context(ext="plotly"):
+    hv.extension("plotly")
+    model = hv.HeatMap(data).opts(cmap="RdBu_r", width=400, height=400)
+    hv.save(model, "heatmap_holo_plotly.html")
+    hv.save(model, "heatmap_holo_plotly.svg")
+    hv.save(model, "heatmap_holo_plotly.png")
+
+with metadata_context(ext="bokeh"):
+    hv.extension("bokeh")
+    model = hv.HeatMap(data).opts(cmap="RdBu_r", width=400, height=400)
+    hv.save(model, "heatmap_holo_bokeh.html")
+    hv.save(model, "heatmap_holo_bokeh.png")
+
+with metadata_context(ext="matplotlib"):
+    hv.extension("matplotlib")
+    model = hv.HeatMap(data).opts(cmap="RdBu_r")
+    hv.save(model, "heatmap_holo_mpl.svg")
+    hv.save(model, "heatmap_holo_mpl.png")
+
+
+
+
+

plotly.graph_objects.Figure.write_html is one of many function which is

+

by Pharaoh. More info about the other patched frameworks you can find here.

+

If the asset script is executed via Pharaoh, the patched write_html function will not store the plot to +"iris_scatter.html" in the current working directory, but rather in a predefined location for assets inside +your Pharaoh project report_project/.asset_build/<component-name> with a unique suffix, for example +iris_scatter_9c30799b.html.

+
+
+

Force Static Exports

+

While for debugging and interactive review it’s nice to have all plots rendered as dynamic elements in HTML, +other build targets might be used for export purposes like Confluence or LaTeX.

+

Since those targets don’t support embedding dynamic HTML elements you would have to tweak your asset generation scripts +to export different formats for different report output formats, which is suboptimal.

+

Pharaoh therefore provides a setting asset_gen.force_static which can be used to signal asset scripts +to export static assets instead of HTML.

+

For all patched plotting APIs, if asset_gen.force_static is set to true, +Pharaoh takes care and exports static images instead of HTML when plotting APIs like +plotly.io.write_html() is used.

+

Since not all frameworks are patched, you might have to add support by yourself inside asset scripts like this:

+
import altair as alt  # altair not supported at the moment
+
+from pharaoh.api import get_project
+from pharaoh.assetlib.api import register_asset
+
+pharaoh_project = get_project(__file__)
+force_static = project.get_setting("asset_gen.force_static")
+
+chart = alt.Chart(...)
+
+if force_static:
+    filename = "chart.png"
+else:
+    filename = "chart.html"
+
+chart.save(filename)
+register_asset(filename, dict(plotting_framework="altair"))
+
+
+
+

See also

+

Accessing Settings

+
+
+
+

Matlab Integration

+

Pharaoh supports generating assets via Matlab scripts/function through the +Matlab API pharaoh.assetlib.api.Matlab.

+

The asset scripts still have to be Python scripts and any asset the Matlab scripts generate, have to be +manually registered using register_asset().

+

Here an example:

+
from pharaoh.assetlib.api import Matlab, register_asset, FileResource, get_resource
+
+resource: FileResource = get_resource(alias="<resource_alias>")
+some_resource_path: Path = resource.locate()
+
+with Matlab() as matlab:
+    plot_path, out, err = matlab.execute_function("generate_plot", [str(some_resource_path)], nargout=1)
+
+register_asset(plot_path, dict(from_matlab=True, foo="bar"))
+
+
+

To use the Matlab API you have to install an additional dependency, depending on your Matlab version:

+
+
    +
  • R2020B: pip install matlabengine==9.9.*

  • +
  • R2021A: pip install matlabengine==9.10.*

  • +
  • R2021B: pip install matlabengine==9.11.*

  • +
  • R2022A: pip install matlabengine==9.12.*

  • +
  • R2022B: pip install matlabengine==9.13.*

  • +
  • R2023A: pip install matlabengine==9.14.*

  • +
  • R2023B: pip install matlabengine==9.15.*

  • +
+
+
+
+
+

Asset Lookup

+

This section deals with how to access assets in local context scripts and build-time templates.

+
+

Asset Finder

+

The AssetFinder is responsible for discovering and searching assets +based on filters using AssetFinder.search_assets().

+

This function is available in various places:

+
    +
  • In local context scripts.

    +

    The following script searches all JSON files that have "context_name" set, +loads them and exports their content as context for build-time templating:

    +
    import json
    +from pharaoh.assetlib.api import get_asset_finder, get_current_component
    +
    +finder = get_asset_finder()
    +component_name = get_current_component()
    +
    +context = {
    +    asset.context.context_name: json.loads(asset.read_text())
    +    for asset in finder.search_assets(
    +        'asset.suffix == ".json" and "context_name" in asset.context',
    +        [component_name]
    +    )
    +}
    +context["component_name"] = component_name
    +return context
    +
    +
    +

    In some use cases this is used to make measurement information available during templating +to render the information in a table.

    +
  • +
  • In templates inside Jinja statements.

    +

    The following snippet searches all image assets of the current component +(the component the template is rendered in) and stores the list of assets in a variable:

    +
    {% set image_assets = search_assets("asset.suffix in ['.png', '.svg']") %}
    +
    +
    +

    The following snippet searches all image assets of the all components in the project. +This can be used to create components that summarize information from other components.

    +
    {% set all_image_assets = search_assets_global("asset.suffix in ['.png', '.svg']") %}
    +
    +
    +

    The used functions search_assets and search_assets_global are +partial functions where the component argument is preset.

    +
  • +
+
+
+

Manual Include

+

If you like to include some of your assets manually, the template environment provides +two functions for your convenience, that return the (relative) path of an asset to the including template:

+
+
asset_rel_path_from_project(asset)

Returns the relative path from the Sphinx source directory to the passed asset. +This path is needed for Sphinx directives that themselves take care to copy the asset into the build directory, +like directives literalinclude, image and figure.

+

Here’s an example:

+
{% set matches = search_assets("<some condition>") %}
+
+.. figure:: {{ asset_rel_path_from_project(matches[0]) }}
+   :scale: 50 %
+
+   This is the caption of the figure (a simple paragraph).
+
+
+

where {{ asset_rel_path_from_project(matches[0]) }} would render something like this: +/.asset_build/dummy/mytxt_f65223c4.txt

+
+
asset_rel_path_from_build(asset)

Returns the relative path from the Sphinx build directory to the passed asset. +This path is needed for Sphinx directives that themselves do NOT take care to copy the asset into the +build directory, like when using the raw_html directive and including a picture or HTMl file using an iframe.

+

In this case calling asset_rel_path_from_build will automatically copy the asset from the asset build folder +to the build directory and return it’s relative path from the including template.

+

Here’s an example:

+
{% set matches = search_assets("<some condition>") %}
+
+.. raw:: html
+
+    <iframe src="{{ asset_rel_path_from_build(matches[0]) }}"
+        loading="lazy"
+    ></iframe><br>
+
+
+

where {{ asset_rel_path_from_build(matches[0]) }} would render something like this: +../../pharaoh_assets/iris_scatter_3e7d7ab7.html

+
+
+
+

Note

+

Another way to include assets with a custom template is to use the template +option of the +Pharaoh asset directive with a path to your custom template.

+
+
+
+

Grouping Assets By Metadata

+

Imaging in your asset scripts you are creating a plot for a measured signal for many different operating conditions:

+
from pharaoh.assetlib.api import metadata_context
+
+for vdd in (8.0, 10.0, 12.0):
+    for iout in (1.0, 2.0, 5.0):
+        with metadata_context(vdd=vdd, iout=iout, signal_name="idd"):
+            fig = ...
+            fig.write_image(file="idd_plot.png")
+
+
+

Now in your report you may like to have all those plots added separately each with it’s own title containing the +values of either vdd or iout.

+

Defining the template like this is not really viable, since in the template you usually have no idea about what values +were iterated over in the asset script. But let’s assume you know, then this would be the template:

+
{% for vdd in (8.0, 10.0, 12.0) %}
+{{ heading("Plots for Vdd:%.1fV, Iout:%.1fA" % vout, 2 }}
+
+{% for iout in (1.0, 2.0, 5.0) %}
+{{ heading("Plots for Iout:%.1fA" % iout, 3 }}
+
+.. pharaoh-asset:: vdd == {{ vdd }} and iout == {{ iout }} and signal_name == 'idd'
+
+{% endfor %}
+{% endfor %}
+
+
+

But luckily there is a better option using asset_groupby to +group assets first by vdd and then by iout:

+
{% set idd_plots = search_assets("signal_name == 'idd'") %}
+{% for vdd, assets_grby_vdd in agroupby(idd_plots, key="vdd").items() %}
+{{ heading("Plots for Vdd:%.1fV" % vout, 2 }}
+
+    {% for iout, assets_grby_iout in agroupby(assets_grby_vdd, key="iout").items() %}
+{{ heading("Plots for Iout:%.1fA" % iout, 3 }}
+
+        {% for asset in assets_grby_iout %}
+.. pharaoh-asset:: {{ asset.id }}
+
+        {% endfor %}
+    {% endfor %}
+{% endfor %}
+
+
+
+

Note

+

agroupby is an alias for asset_groupby. Both are available as global function during templating.

+
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/reference/building.html b/reference/building.html new file mode 100644 index 0000000..48b78d0 --- /dev/null +++ b/reference/building.html @@ -0,0 +1,461 @@ + + + + + + + Building — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Building

+
+

Configuration

+

Following steps have to be done for configuring a Sphinx build:

+
    +
  1. Choose a Sphinx build target via the report.target setting. +Currently supported are:

    + +
    +

    Note

    +

    For other builders than html and *confluence, you might need to install additional dependencies.

    +
    +
  2. +
  3. Configure the selected builder

    +
      +
    • Via settings sections like report.html and report.confluence

    • +
    • Via the Sphinx configuration +report_project/conf.py.

      +

      This file loads a default Sphinx configuration variables (also partially from settings) and assigns them to the local namespace.

      +

      To modify just overwrite or alter them:

      +
      # The list "html_css_files" is already defined from the default config,
      +# but this is not the case for all variables.
      +# To be sure inspect the local namespace via the debugger or print it
      +# like this ``print(", ".join(locals().keys()))``
      +html_css_files.append("css/mystyle.css")
      +
      +
      +
    • +
    +
    +

    Note

    +

    The HTML builder already contains a reasonable default config, so no change needed in most cases.

    +
    +
  4. +
+
+
+

Build API

+

A Pharaoh report may be built using the method PharaohProject.build_report().

+
+

Important

+

If your templates are accessing assets, make sure you call PharaohProject.generate_assets() everytime your asset scripts or resources changed.

+
+

You can call the API within a Python script, as for example in the included report-project/debug.py script.

+
+
from pharaoh.api import PharaohProject
+
+if __name__ == "__main__":  # This guard is needed because Pharaoh is using multiprocessing
+    proj = PharaohProject(project_root="..")
+    proj.generate_assets()
+    proj.build_report()
+
+
+
+

Or you just double-click the CLI scripts report-project/pharaoh-generate-assets.cmd and +report-project/pharaoh-build.cmd.

+

Or with the Pharaoh venv activated, inside the project directory use the CLI directly: pharaoh build

+

Log output should be displayed in the console, but are also written to log.txt and log_warnings.txt +(only warnings or errors).

+
+
+

Archiving

+

After building a report, it may be archived using PharaohProject.archive_report().

+
+
+PharaohProject.archive_report(dest: str | Path | None = None) Path[source]
+

Create an archive from the build folder.

+
+
Parameters:
+

dest – A destination path to create the archive. Relative paths are relative to the project root. +If omitted, the filename will be taken from the report.archive_name setting.

+
+
Returns:
+

The path to the archive

+
+
+
+ +

Alternatively just double-click the CLI script report-project/pharaoh-archive.cmd.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/reference/cli.html b/reference/cli.html new file mode 100644 index 0000000..0dd2d51 --- /dev/null +++ b/reference/cli.html @@ -0,0 +1,728 @@ + + + + + + + CLI — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

CLI

+
+

Preface

+
+
Usage: pharaoh [OPTIONS] COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...
+
+  Pharaoh Commandline Interface.
+
+  Usage examples:
+
+      pharaoh new -t pharaoh_testing.simple -c "{'a': 1}" --settings ../my_settings.yaml
+      pharaoh add -n dummy1 -t pharaoh_testing.simple -c "{'test_name':'dummy'}"
+      pharaoh generate
+      pharaoh build
+      pharaoh archive
+
+  Multi-command chaining is also possible (all on one line):
+
+      pharaoh new add --name dummy1 -t pharaoh_testing.simple -c "{'test_name':'dummy1'}"
+       add --name dummy2 -t pharaoh_testing.simple -c "{'test_name':'dummy2'}"
+       generate build archive -d "archives/myarchive.zip"
+
+Options:
+  -p, --path PATH  The path to the Pharaoh report directory (contains
+                   pharaoh.yaml) or the path to pharaoh.yaml itself. If omitted,
+                   the current working directory is used.
+  --help           Show this message and exit.
+
+Commands:
+  add              Adds a new component to the Pharaoh project.
+  add-template     Adds additional templates to an existing component, that...
+  archive          Archives the report to a ZIP file.
+  build            Executes report generation for the current project.
+  env              Updates the projects settings.
+  generate         Generates assets.
+  info             Prints Pharaoh version information, e.g.
+  new              Creates a new Pharaoh report skeleton.
+  print-plugins    Prints all available plugins and their installation paths
+  remove           Removes one or multiple existing components.
+  show-report      Opens the generated report (if possible, e.g.
+  update-resource  Updates a Resource from a project component by its alias.
+
+
+
+

The CLI may be used in different ways:

+
    +
  • Through the PIP entrypoint pharaoh

    +

    When you install Pharaoh via PIP, it will install a pharaoh.exe +into your environment (<installdir>/Scripts).

    +

    Once your environment is activated you can use the CLI via the pharaoh command. +Some examples using CMD:

    +
    cd "C:/projects/my_pharaoh_report"
    +
    +set PHARAOH.REPORT.TITLE="Some Title"
    +
    +pharaoh new
    +pharaoh add --name dummy1 -t pharaoh_testing.simple --context "{'test_name':'dummy1'}"
    +pharaoh add --name dummy2 -t pharaoh_testing.simple --context "{'test_name':'dummy2'}"
    +pharaoh generate
    +pharaoh build
    +
    +
    +

    If you current working directory is not inside the Pharaoh project, add the absolute or relative (to CWD) path +to the project as first argument like this:

    +
    cd "C:/projects"
    +
    +pharaoh -p "my_pharaoh_report" new
    +pharaoh -p "my_pharaoh_report" generate
    +cd "my_pharaoh_report"
    +pharaoh build
    +
    +
    +
  • +
  • Through the generated CMD scripts report-project/*.cmd

    +

    The advantage of those scripts is, that they are working regardless of your Pharaoh Python environment being +activated or not, since they contain the absolute path to the Python interpreter used to generate them as a +fallback .

    +

    Thus they are checking if pharaoh.exe is on the system path (e.g. when executing inside an +activated virtual env - like the terminal in PyCharm), but falling back to the absolute path.

    +
    +

    Important

    +

    If you rename or move the Python environment that was used to generate the Pharaoh project, +those CMD script won’t work anymore (unless pharaoh.exe is on the system path).

    +
    +

    So you can just double click pharaoh-*.cmd to perform the corresponding action with default arguments.

    +
    +

    Note

    +

    This way is just used to managed an already existing Pharaoh project, so commands like +pharaoh.cmd new does not really make sense.

    +
    +

    If you like to use them via the terminal, the syntax is similar to the above examples:

    +
    +
      +
    • pharaoh.cmd generate or pharaoh-generate-assets.cmd

    • +
    • pharaoh.cmd build or pharaoh-build.cmd

    • +
    • pharaoh.cmd archive or pharaoh-archive.cmd

    • +
    +
    +
  • +
+
+
+

Commands

+
+

New

+
+
Usage: pharaoh new [OPTIONS]
+
+  Creates a new Pharaoh report skeleton.
+
+  Example:
+
+      pharaoh new -t pharaoh_testing.simple -c "{'a': 1}" --settings ../my_settings.yaml
+
+Options:
+  -f, --force          Force project overwrite if already exists
+  -t, --template TEXT  The project templates to use for creating the project
+                       files.
+  -c, --context TEXT   The Jinja rendering context for the selected template.
+  -s, --settings FILE  A path to a YAML file containing settings to overwrite
+                       the default project settings.
+  --help               Show this message and exit.
+
+
+
+
+
+

Add

+
+
Usage: pharaoh add [OPTIONS]
+
+  Adds a new component to the Pharaoh project.
+
+  Example:
+
+      pharaoh add -n dummy1 -t pharaoh_testing.simple -c "{'test_name':'dummy'}"
+      pharaoh add -n dummy1 -t pharaoh_testing.simple -r "FileResource(alias='foo', pattern='.*')"
+
+Options:
+  -n, --name TEXT      The name of the component. Must be a valid Python
+                       identifier.   [required]
+  -t, --template TEXT  The component templates to use for creating the
+                       components project files.
+  -c, --context TEXT   The Jinja rendering context for the selected templates.
+  -m, --metadata TEXT  A dictionary of metadata that may be used to find the
+                       components.Enter a valid json string
+  -r, --resource TEXT  A Python expression that evaluates to a valid object of
+                       type Resource.
+  -i, --index INTEGER  The index to add the component. Default is -1 with means
+                       at the end.
+  --help               Show this message and exit.
+
+
+
+
+
+

Update-resource

+
+
Usage: pharaoh update-resource [OPTIONS]
+
+  Updates a Resource from a project component by its alias.
+
+  Examples:
+
+      pharaoh update-resource -n dummy1 -a foo -r "FileResource(alias='baz', pattern='*')"
+
+Options:
+  -n, --name TEXT      The name of the component. Must be a valid Python
+                       identifier.   [required]
+  -a, --alias TEXT     The resource alias.  [required]
+  -r, --resource TEXT  A Python expression that evaluates to a valid object of
+                       type Resource.
+  --help               Show this message and exit.
+
+
+
+
+
+

Add-template

+
+
Usage: pharaoh add-template [OPTIONS]
+
+  Adds additional templates to an existing component, that may overwrite
+  existing files during rendering.
+
+  Example:
+
+      pharaoh add-template -n dummy1 -t pharaoh_testing.simple -c "{'test_name':'dummy'}"
+
+Options:
+  -n, --name TEXT      The name of the component. Must be a valid Python
+                       identifier.   [required]
+  -t, --template TEXT  The component templates to use for creating the
+                       components project files.
+  -c, --context TEXT   The Jinja rendering context for the selected templates.
+  --help               Show this message and exit.
+
+
+
+
+
+

Remove

+
+
Usage: pharaoh remove [OPTIONS]
+
+  Removes one or multiple existing components.
+
+  If the regular expression (case-insensitive) partially matches the component
+  name. To do a full match surround the pattern by ^ and &, e.g. ^my_component&.
+
+  Example:
+
+      pharaoh remove -f dummy.*
+
+Options:
+  -f, --filter TEXT  A case-insensitive component filter. Either a full- or
+                     regular expression match, depending on regex option.
+                     [required]
+  -r, --regex        If True, the filter option will be treated as regular
+                     expression. Components that partially match the regular
+                     expression are removed.
+  --help             Show this message and exit.
+
+
+
+
+
+

Env

+
+
Usage: pharaoh env [OPTIONS] KEY [VALUE]
+
+  Updates the projects settings.
+
+  Example:
+
+      pharaoh env foo_A 123               -> foo_a: 123
+      pharaoh env foo_B bar               -> foo_b: "bar"
+      pharaoh env foo_C "{'baz': 123}"    -> foo_c: {'baz': 123}
+      pharaoh env foo_D class             -> foo_d: "class"
+      pharaoh env foo_E                   -> foo_e: None
+
+Options:
+  --help  Show this message and exit.
+
+
+
+
+
+

Generate

+
+
Usage: pharaoh generate [OPTIONS]
+
+  Generates assets.  Either of the entire project or just a selected subset of
+  components.
+
+  Examples:
+
+      pharaoh generate
+      pharaoh generate -f dummy[12]
+      pharaoh generate -f dummy1 -f dummy2
+
+Options:
+  -f, --filter TEXT  A list of regular expressions that are matched against each
+                     component name. If a component name matches any of the
+                     regular expressions, the component's assets are regenerated
+                     (containing directory will be cleared)
+  --help             Show this message and exit.
+
+
+
+
+
+

Build

+
+
Usage: pharaoh build [OPTIONS]
+
+  Executes report generation for the current project.
+
+  Examples:
+
+      pharaoh build
+      pharaoh -p "path/to/my/project" build
+
+Options:
+  --help  Show this message and exit.
+
+
+
+
+
+

Show-report

+
+
Usage: pharaoh show-report [OPTIONS]
+
+  Opens the generated report (if possible, e.g. for local HTML reports).
+
+  Examples:
+
+      pharaoh show-report
+      pharaoh -p "path/to/my/project" show-report
+
+Options:
+  --help  Show this message and exit.
+
+
+
+
+
+

Archive

+
+
Usage: pharaoh archive [OPTIONS]
+
+  Archives the report to a ZIP file.
+
+Options:
+  -d, --dest PATH  Path to the archive. Either a directory or filename with
+                   extension zip.
+  --help           Show this message and exit.
+
+
+
+
+
+

Info

+
+
Usage: pharaoh info [OPTIONS]
+
+  Prints Pharaoh version information, e.g. ``Pharaoh v1.0.0 [Python 3.9.13,
+  Windows-10-10.0.19045-SP0]``
+
+Options:
+  --help  Show this message and exit.
+
+
+
+
+ +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/reference/components.html b/reference/components.html new file mode 100644 index 0000000..238766d --- /dev/null +++ b/reference/components.html @@ -0,0 +1,548 @@ + + + + + + + Components — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Components

+
+

What’s a Component?

+

A Pharaoh project consists of any amount of components.

+

The file report_project/index.rst determines how the components are included in the report. +Per default all components are added in sequence on the same level but this could be changed in the template.

+

A component has following traits:

+
+
+
Name

The name of the component; used for identification purposes.

+
+
Templates

A list of templates used to generate the component’s files.

+
+
Render Context

An arbitrarily-nested dictionary that is available during generation-time templating. +This context enables the re-use of the templates for different purposes.

+
+
Resources

A list of resource definitions that let asset scripts easily access external data by using an alias instead of +absolute resource specifiers (like a file path).

+
+
Metadata

This metadata dictionary can be used to lookup components +by their metadata.

+

A potential use case is grouping components by their metadata and create a customized report layout +using the report_project/index.rst template.

+
+
+
+
+
+

Resources

+

Resources let asset scripts easily access external data by using an alias instead of absolute resource specifiers +(like a file path or an RDDL artifact id).

+

The currently available resource types:

+
+
+

Accessing resources in asset scripts can be done like this:

+
from pathlib import Path
+from pharaoh.assetlib.api import get_resource, FileResource
+
+resource: FileResource = get_resource(alias="<resource_alias>")
+first_matching_file_path: Path = resource.locate()
+...
+
+
+

To get a resource from a component or update a component’s resource, use theses functions:

+
+
+
+
+

Templates

+

Component templates are used to create the main content for components +(see Adding Components) and can come in different forms:

+
    +
  • Template Directory

    +
    +

    The simplest form of a template. A directory with some files.

    +

    If assets shall be generated by the component, +the directory must contain an asset_scripts subdirectory containing Python scripts.

    +
    +
  • +
  • Registered Templates

    +
    +

    Pharaoh’s Plugin Architecture allows the development of plugins that register templates.

    +

    That means plugins may come with their own template directories and register their paths in Pharaoh, +so these templates are then selectable using a template identifier, +like my_awesome_plugin.template1.

    +

    An advantage of plugin templates is, that for those templates, dependencies to other templates can be specified. +For example the template my_awesome_plugin.template1 may requires that the my_awesome_plugin.template2 +template is used at least once in any component, otherwise cross-references won’t work.

    +
    +
  • +
  • Template Files/Single-file templates

    +
    +

    The smallest and most compact form of a template.

    +

    Single-file templates are Python files with suffix .pharaoh.py whose Python code creates assets and +whose module-level docstring represents the rST content.

    +

    This file will be internally converted to a template directory:

    +
    my_template.pharaoh.py -> index_my_template.rst
    +                          asset_scripts/my_template.py
    +
    +
    +
    +
  • +
+
+
+

Managing Components

+
+

Adding Components

+

Adding a component to a Pharaoh project can be done via the API function +PharaohProject.add_component() or the CLI +command Add:

+
+
+PharaohProject.add_component(component_name: str, templates: str | Path | Iterable[str] | Iterable[Path] = ('pharaoh.empty',), render_context: dict | None = None, resources: list[resource.Resource] | None = None, metadata: dict[str, Any] | None = None, index: int = -1, overwrite: bool = False)[source]
+

Adds a new component to the Pharaoh project

+

Example:

+
from pharaoh.api import FileResource, PharaohProject
+
+proj = PharaohProject(".")
+proj.add_component(
+    component_name="component_ABC",
+    templates=[
+        "plugin_abc.template_xyz",              # plugin template
+        "path/to/template/directory",           # template directory
+        "path/to/template/file/tmpl.pharaoh.py" # template file
+    ],
+    render_context={"foo": "bar"},
+    resources=[FileResource(alias="dlh5_result", pattern="C:/temp/**/*.dlh5")],
+    metadata={"some tag": "some value"},
+    index=-1,  # append
+    overwrite=False
+)
+
+
+
+
Parameters:
+
    +
  • component_name – The name of the component. Must be a valid Python identifier.

  • +
  • templates

    A list of component templates to use for creating the components project files. +Those may be the template identifier (e.g. plugin_abc.template_xyz) of a registered plugin template, +a path to a template directory or a path to a template file (single-file template).

    +

    Since multiple template may be specified, their order matters in cases where different templates +create equally-named files, thus templates might overwrite files of the previous templates.

    +

    This enables template-composition, where template designers can chunk their bigger templates into +smaller re-usable building blocks.

    +

    If omitted, an empty default template is used.

    +

  • +
  • render_context – The Jinja rendering context for the selected template. +The actual template rendering context will have component_name, resources and +metadata available under the respective keys.

  • +
  • resources – A list of Resource instances, defining the component’s resources used in asset scripts

  • +
  • metadata – A dictionary of metadata that may be used to find the component via +PharaohProject.find_component() method.

  • +
  • overwrite – If True, an already existing component will be overwritten

  • +
+
+
+
+ +
+
+

Updating Components

+

Once a component is generated, the options to modify it are limited to:

+
+
+
+
+

Removing Components

+

A generated component can be removed via +PharaohProject.remove_component(), +which results in deletion of report-project/components/<component-name> and its entry in pharaoh.yaml:

+
+
+

Finding Components

+

Components can be looked up via their metadata using the function +PharaohProject.get_component_names_by_metadata().

+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/reference/directive.html b/reference/directive.html new file mode 100644 index 0000000..fe36411 --- /dev/null +++ b/reference/directive.html @@ -0,0 +1,556 @@ + + + + + + + Pharaoh Directive — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Pharaoh Directive

+

A Sphinx Directive +is a generic block of explicit markup. +Along with roles, directives are one of the extension mechanism, a way of adding support for new constructs +without adding new primary syntax (directives may support additional syntax locally). +And Sphinx makes heavy use of it.

+

All standard directives are always available, +whereas any other directives are domain-specific +(see Writing Sphinx Extensions) +and may require special action to make them available when processing the document, +like the Pharaoh Asset directive.

+

The Pharaoh Asset directive pharaoh-asset is added via the Pharaoh Sphinx extension and allows users +to include assets in the document, based on one or multiple filter conditions.

+

The matched assets are then rendered using predefined templates +that can be specified in the asset metadata.

+

The pharaoh-asset directive consists of a name, arguments, options and content, e.g:

+
+
.. pharaoh-asset:: key1 == "A" or
+                   key2 == "B"
+    :filter: key3 == "C"
+    :index: 0
+
+    key4 == "D";
+    key5 == "E"
+
+
+
    +
  • pharaoh-asset is the name

  • +
  • key1 == "A" or key2 == "B" is an argument

  • +
  • :filter: key3 == "C" and :index: 0 are options

  • +
  • key4 == "D";key5 == "E" is the content

  • +
+
+

Note

+

The directive supports 3 ways to define an asset filter, all of them can be used in parallel and are AND-ed: +the argument, the :filter: option and the content.

+

The filters are treated as multiline statements and are split only on unescaped ; characters. +This means key4 == "D";key5 == "E" is effectively the same as key4 == "D" and key5 == "E"

+

The directive internally uses the AssetFinder.search_assets() function, so the same rules apply to the filter strings.

+
+
+

If your filter matches multiple assets, all matches will be rendered one after another. +This can look quite messy very fast, so there is another option to add some more information to each rendered asset +by the combined use with the AssetFinder.search_assets() function, +where the directive simply gets the ID of the asset to include.

+
+

Following template example will search for all images. If at least one exists a new section will be created and the +images are added, each under its own rubric:

+
{% set image_assets = search_assets("asset.suffix in ['.png', '.svg']") %}
+
+{% if image_assets|length %}
+{{ h2("Images") }}
+
+ {% for asset in image_assets %}
+.. rubric:: {{ asset.context.caption }}
+
+.. pharaoh-asset:: {{ asset.id }}
+
+ {% endfor %}
+{% endif %}
+
+
+
+

Important

+

In this case the asset’s ID must be the one-and-only filter that is given!

+
+
+
+

Directive Options

+

Following options may be used for the pharaoh-asset directive:

+
+
:filter:

The filter (multiline string, ;-separated) to select assets by metadata. See example above.

+
+
:optional:

If false (default), the asset filter MUST match any asset, otherwise an exception is raised.

+

Valid values: true/yes/1, false/no/0

+
+
:index:

If the filters match multiple assets, only render the assets with specified index (0-based).

+

Following style is possible: 1,2,4-8,9 (like selecting pages to print). Whitespace is ignored.

+
+
:components:

A comma-separated string of component names to look for assets (see AssetFinder.search_assets()).

+

Special values:

+
    +
  • _this_ (default): Searches only the current component the template is rendered in

  • +
  • _all_: Searches all components

  • +
+
+
:template:

The template to use to embed the asset into the document. +It may be a template name as described in Asset Templates or +a file path to your own template. Please note that relative paths are resolved with the Pharaoh project directory +as root directory).

+

If not specified as option to the directive, the asset metadata asset.template is looked up +(which maybe gets a default template automatically determined by the asset’s file suffix).

+

If the template is set neither via option nor asset metadata, an error will be raised.

+
+

Note

+

Another way to include assets manually is described here.

+
+
+
:image-(alt,height,width,scale,align):

Passed to the image directive, if the asset is rendered as image.

+
+
:iframe-width:

The width of the HTML iframe.

+

The default value is determined by setting asset_gen.default_iframe_width.

+
+
:iframe-height:

The height of the HTML iframe.

+

The default value is determined by setting asset_gen.default_iframe_height.

+
+
:ignore-title:

If asset metadata asset.title is set, the asset will be rendered under a unique +rubric. +The rubric title is a unique cross-reference.

+

If this option is true, this behavior is skipped.

+

Valid values: true/yes/1, false/no/0

+
+
:ignore-description:

If asset metadata asset.description is set, the description will be inserted directly before the asset +as raw string (so you could add reST as well).

+

If this option is true, this behavior is skipped.

+
+
:datatable-extended-search:

If true, assets rendered as interactive datatable will include an +interactive search builder

+

The default value is determined by setting asset_gen.default_datatable_extended_search.

+

Valid values: false/no/0, true/yes/1

+
+
+
+
+

Asset Templates

+

This section describes the templates used to include assets into your documents.

+

There are 2 options how to specify the used template:

+
+
    +
  • Via the register_asset() function:

    +
    data = b'This text was generated by an asset script and will be included via "literalinclude"'
    +register_asset("raw.txt", dict(label="my_txt_snippet"), template="raw_txt", data=io.BytesIO(data))
    +
    +
    +

    As fallback, if template is not specified, a default template is chosen depending on the asset’s file suffix:

    +
      +
    • ".html": iframe

    • +
    • ".rst": raw_rst

    • +
    • ".txt": raw_txt

    • +
    • ".svg": image

    • +
    • ".png": image

    • +
    • ".jpg": image

    • +
    • ".jpeg": image

    • +
    • ".gif": image

    • +
    • ".md": markdown

    • +
    +
  • +
  • Via the :template: option of the Pharaoh asset directive (has priority over setting in metadata).

  • +
+
+

Following asset templates are currently supported:

+
+
+
image

The asset file will be included via the image directive. Options on the pharaoh-asset directive starting with image- are +passed to the image directive.

+
+
raw_html

The asset file will be included via the raw:: html directive. +The only modification is that the HTML content is pasted into a separate div-tag with a unique, +autogenerated ID and a linebreak at the end.

+
+
markdown

The asset file will be included via the raw:: html directive after converting the Markdown text to HTML.

+
+
raw_rst

The content of the asset file will be pasted into the document without modification and no extra indentation.

+
+
raw_txt

The asset file will be included via the literalinclude directive. Language highlighting is disabled.

+
+
iframe

The asset file will be included via the raw directive, wrapped in an iframe-tag. +Options on the pharaoh-asset directive starting with iframe- are added to the iframe options. +The iframe will be lazy-loaded by the browser.

+
+
datatable

This template can only be used in combination with HTML tables produced by +pandas.DataFrame.to_html() or +pandas.io.formats.style.Styler.to_html().

+

It transforms those tables into interactive JS-powered tables (see DataTables).

+
+
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/reference/index.html b/reference/index.html new file mode 100644 index 0000000..a9f32e7 --- /dev/null +++ b/reference/index.html @@ -0,0 +1,507 @@ + + + + + + + Reference — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Reference

+
+ +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/reference/project_structure.html b/reference/project_structure.html new file mode 100644 index 0000000..41f88cd --- /dev/null +++ b/reference/project_structure.html @@ -0,0 +1,426 @@ + + + + + + + Project Structure — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Project Structure

+

A generated and built Pharaoh project has following directory structure:

+
📁 <project-name>
+├── 📄 pharaoh.yaml
+│       Contains all settings and component definitions.
+├── 📄 log.txt
+│       Build logs for all logging levels.
+├── 📄 log_warnings.txt
+│       Build logs for logging levels WARNING and higher.
+├── 📄 pharaoh_report_<timestamp>.zip
+│       A ZIP archive of the built report.
+├── 📄 .gitignore
+│       A file for GIT to ignore all transient files.
+├── 📁 report-build
+│       Contains the build output, e.g. HTML pages or LaTeX input files.
+│       For HTML output the main page is called index.html.
+└── 📁 report-project
+    ├── 📁 .resource_cache
+    │       Contains temporary/cached resources
+    ├── 📁 _static
+    │       Contains the CSS overrides and the HTML logo for the report.
+    ├── 📁 _templates
+    │       Contains HTML templates that override the defaults of the selected Sphinx theme.
+    │       In the case layout.html and footer.html. Only change if you really need to.
+    ├── 📁 .asset_build
+    │       All registered assets from asset generation will be stored here for each component separately.
+    ├── 📁 components
+    │       This is where components get created when added via the project API.
+    ├── 📁 user_templates
+    │       Stores user-defined templates for the build-time templating step.
+    ├── 📄 conf.py
+    │       The Sphinx configuration file (https://www.sphinx-doc.org/en/master/usage/configuration.html)
+    │       It loads a default configuration from the Pharaoh project into local variables.
+    │       Used to overwrite or add additional configuration.
+    ├── 📄 debug.py
+    │       For debugging the asset/report generation. Modify for your needs.
+    │       Per default it executes asset generation and report build.
+    ├── 📄 index.rst
+    │       The default template to control how the title page looks like.
+    │       It includes all components into the TOC (table of content) tree.
+    ├── 📄 index.rst.rendered
+    │       *.rendered files are generated during build-time templating step and allow to see the
+    │       actual rST content Sphinx is using for generating the report.
+    │       This helps for debugging your templates since you see the result after the templating step.
+    └── 📄 \*.cmd
+            Scripts to execute asset generation, report build or upload to R&D Datalake with a single double-click.
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/reference/settings.html b/reference/settings.html new file mode 100644 index 0000000..dc3d50a --- /dev/null +++ b/reference/settings.html @@ -0,0 +1,592 @@ + + + + + + + Settings — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Settings

+

The Pharaoh settings file pharaoh.yaml is used for three main purposes:

+
    +
  • Configuration of Pharaoh functionality like asset generation, report build, archiving, RDDL upload, notifications…

  • +
  • Hold info about all added components

  • +
  • User-defined settings (accessible from templates)

  • +
+

The settings file is loaded using the OmegaConf library.

+
+

OmegaConf is a YAML based hierarchical configuration system, with support for merging configurations +from multiple sources (files, CLI argument, environment variables) providing a consistent API regardless +of how the configuration was created. OmegaConf also offers runtime type safety via Structured Configs.

+

The library offers lots of features like +Variable interpolation and +Resolvers. +So if you want to get the best out of your configuration file, have a look in the OmegaConf documentation on the +mentioned topics above.

+
+
+

Default Settings

+

A Pharaoh project is generated with following default settings, if not specified otherwise:

+
logging:
+  # The logging level for the Pharaoh logger, one of DEBUG, INFO, WARNING, ERROR
+  level: "INFO"
+
+report:
+  # The report output format. Currently supported are:
+  # - html and all other Sphinx default builders; see https://www.sphinx-doc.org/en/master/usage/builders/index.html
+  # - confluence, singleconfluence; see https://sphinxcontrib-confluencebuilder.readthedocs.io/en/stable/builders/
+  builder: "html"
+  # Verbosity of the Sphinx build. 0: INFO, 1: VERBOSE, 2: DEBUG
+  # VERBOSE: Will enable debug output of .. pharaoh-asset:: directive
+  verbosity: 0
+  # The report title displayed on the index page
+  title: "Pharaoh Report"
+  # The report author
+  author: "${user:}"
+  # Name of the ZIP archive. For available resolvers consult documentation
+  archive_name: "pharaoh_report_${utcnow.strf:%Y%m%d_%H%M%S}.zip"
+  # Options for HTML builder
+  html:
+    # Show Pharaoh logo on HTML report. Kindly let this enabled for branding, unless it's for external customers ;)
+    show_pharaoh_logo: true
+
+asset_gen:
+  # Create static (png, svg) assets instead of dynamic (html) ones where possible.
+  # This only has effect on the patched functions of frameworks like pandas, bokeh, plotly, ... .
+  # For more info refer to the documentation: todo
+  force_static: false
+  # How many worker processes (CPUs) to use for parallel asset generation. "auto" or some integer value.
+  # Use 0 to execute all asset scripts sequentially in the current process.
+  # Using multiprocessing only outperforms single-process if there are a large amount of asset scripts.
+  # The proof is in the pudding ;)
+  worker_processes: 0
+  # Regular expression (case-insensitive). If matches on file name with extension, the script is not executed during
+  # asset generation
+  script_ignore_pattern: "_.*"  # ignore scripts that start with underscore
+  # The default width of embedded iframes. (html attribute)
+  default_iframe_width: "100%"
+  # The default height of embedded iframes. (html attribute)
+  default_iframe_height: "500px"
+  # Show all datatables per default with an interactive search function
+  default_datatable_extended_search: false
+
+# Options for toolkit patches
+toolkits:
+  bokeh:
+    export_png:
+      width: 720
+      height: 480
+  matplotlib:
+    savefig:
+      dpi: 150
+      facecolor: "auto"
+      edgecolor: "auto"
+  plotly:
+    write_image:
+      width: 1000
+      height: 600
+      scale: 1
+      validate: true
+    write_html:
+      default_width: "100%"
+      default_height: "100%"
+  holoviews:
+    save:
+      dpi: 300
+  pandas:
+    to_html:
+      na_rep: ""
+
+
+
+
+

Custom Settings

+

Since default settings are very likely to not fit everyone’s needs, they can be modified by the user in several ways:

+
+

Important

+

Make sure all keys in your settings are lowercase, otherwise putting/getting settings might +not work as expected since settings are all treated lowercase internally!

+
+
    +
  • Pass a custom settings file on project creation via the keyword argument custom_settings. +The file is merged with the default settings, so it’s enough to overwrite only the divergent settings:

    +
    from pharaoh.api import PharaohProject
    +
    +proj = PharaohProject(project_root="some-path", custom_settings="mysettings.yaml")
    +
    +
    +
    +

    Note

    +

    If custom_settings is a relative path, then the current working directory is used as anchor

    +
    +
  • +
  • Put settings via the settings API +put_setting(key: str, value: Any):

    +
    from pharaoh.api import PharaohProject
    +
    +proj = PharaohProject(project_root="some-path")
    +proj.put_setting("report.title", "My own title")
    +proj.put_setting("toolkits.bokeh.export_png", dict(width=720, height=480))
    +proj.save_settings()  # Optional
    +
    +
    +
    +

    Important

    +

    Defining settings like this only changes the settings for the project instance in memory but does +not persist them to pharaoh.yaml.

    +

    To do so call save_settings() manually if +you don’t plan to modify components/resources right after (those functions do an auto-safe).

    +
    +
  • +
  • Put settings via environment variables

    +

    Same example as with settings API, but with environment variables:

    +
    import os
    +from pharaoh.api import PharaohProject
    +
    +# Env variables have to be prefixed with "pharaoh." and are always treated lowercase
    +os.environ["pharaoh.report.title"] = "My own title"
    +os.environ["pharaoh.toolkits.bokeh.export_png"] = "{'width': 720, 'height': 480}"
    +
    +proj = PharaohProject(project_root="some-path")
    +proj.save_settings(include_env=True)
    +
    +
    +
    +

    Note

    +

    Double-underscore __ also acts as a valid separator for keys, since some shell environments don’t +allow dots inside variable names. E.g. PHARAOH__a.B__c___d will resolve to pharaoh.a.b.c___d.

    +
    +
    +

    Important

    +

    Environment variables are only persisted to pharaoh.yaml when saving like this: +save_settings(include_env=True)

    +
    +
    +

    Important

    +

    If Pharaoh environment variables are changed while the project is already instantiated, +you can reload settings using +load_settings(namespace="env"):

    +
    import os
    +from pharaoh.api import PharaohProject
    +
    +proj = PharaohProject(project_root="some-path")
    +os.environ["pharaoh.report.title"] = "My Title"
    +proj.load_settings(namespace="env")
    +proj.save_settings(include_env=True)
    +
    +
    +
    +
  • +
+
+
+

Accessing Settings

+

Setting values can be accessed in various places throughout the Pharaoh project using +PharaohProject.get_setting():

+
    +
  • in Python scripts, e.g. asset- or local context-scripts:

    +
    from pharaoh.api import get_project
    +pharaoh_project = get_project(__file__)  # Get a project instance location of this file in project directory
    +title = project.get_setting("report.title")
    +
    +
    +
  • +
  • in templates:

    +
    {{ heading(get_setting("report.title"), 1) }}
    +{% set static_export = get_setting("asset_gen.force_static", False) -%}
    +
    +
    +
  • +
+
+
+

Custom Resolvers

+

OmegaConf offers already some +builtin resolvers +like oc.env: author: "${oc.env:USERNAME,unknown}" +but Pharaoh adds additional ones for your convenience:

+
    +
  • utcnow.strf: Formats UTC time.

    +
    +

    Usage: archive_name: "pharaoh_report_${utcnow.strf:%Y%m%d_%H%M%S}.zip"

    +
    +
  • +
  • now.strf: Same as above, but with timestamp of local timezone.

  • +
  • pharaoh.project_dir: Returns the Pharaoh project directory with / as path separator.

    +
    +

    Usage: key: "${pharaoh.project_dir:}/somepath" (note the trailing :, without, it would be a reference).

    +
    +
  • +
+

If you need additional resolvers or wish to implement your own, please get in contact with us.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/reference/templating.html b/reference/templating.html new file mode 100644 index 0000000..129f536 --- /dev/null +++ b/reference/templating.html @@ -0,0 +1,830 @@ + + + + + + + Templating — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Templating

+

As you may have noticed, templating is a dominating core functionality of Pharaoh.

+

The foundation for all templating in Pharaoh is reStructuredText and Jinja. +You might make yourself familiar once you encounter related questions in the rest of the section.

+
+

reStructuredText

+
+

reStructuredText is an easy-to-read, what-you-see-is-what-you-get plaintext markup syntax and parser system. +It is useful for in-line program documentation (such as Python docstrings), for quickly creating simple web pages, +and for standalone documents.

+

reStructuredText is designed for extensibility for specific application domains.

+

The primary goal of reStructuredText is to define and implement a markup syntax for use in Python docstrings +and other documentation domains, that is readable and simple, yet powerful enough for non-trivial use.

+

Refer to the reStructuredText Reference +for learning the basics.

+
+

Jinja

+
+

Jinja is a fast, expressive and extensible templating engine.

+

A Jinja template is simply a text file. +Jinja can generate any text-based format (HTML, XML, CSV, LaTeX, etc.), in Pharaoh’s case mostly rST files. +A Jinja template does not need to have a specific extension: .html, .xml, or any other extension is just fine.

+

A template contains variables and/or expressions, which get replaced with values when a +template is rendered; and tags, which control the logic of the template. +The template syntax is heavily inspired by Python.

+

Refer to the Jinja Template Reference +for learning the basics.

+

See Templating Builtins for a complete list of globals, filters and +tests you can use in your templates.

+
+
+

Like described in the introduction, Pharaoh’s workflow distinguishes +two major stages where template rendering is performed. +Please refer to the corresponding sections by following the links:

+
+
+
+

Generation-time Templating

+

Alias: first-level templating

+

Generation-time templating is used while generating a new Pharaoh project or adding new components to an +existing project, and is inspired by copier, +a Jinja-based library for rendering project templates.

+
+

Basics

+

In Pharaoh, we need first-level templates for two different purposes:

+
+

Project Templates

+
+

These are the templates that may be specified when creating a new Pharaoh project. +The composition of all templates in this case must always generate a Sphinx project that contains at +least a conf.py and an index source file, like index.rst.

+

If you’re interested in maintaining your own project template please contact us for support, +but for most use cases our default template pharaoh.default_project should be flexible enough.

+

It is also possible to extend or overwrite parts of the default project, so please also get in touch +if you try to do so.

+
+

Component Templates

+
+

Component templates are used to render new components into a Pharaoh project.

+

The minimal requirement is basically an index.rst file that can be included by the project +(at least what our default Pharaoh template concerns) or a +Template File

+
+
+

Like described here, templates can come in different styles.

+

While for Project Templates only registered templates and template directories are allowed/useful, +Component Templates can, in addition to it, be generated using +Template Files/Single-file templates.

+

Let’s have a look on how a component template directory may look like:

+
+
📁 my_template
+├── 📄 index.rst.jinja             # reST file with templated content
+├── 📄 test_context.py.jinja       # Python file with templated content
+├── 📁 asset_scripts               # folder copied as-is
+│   └── 📄 default_plots.py        # file copied as-is
+└── 📁 [[foo]]                     # folder with a templated name
+    └── 📄 [[ bar ]]_script.py     # file with a templated name
+
+
+
    +
  • 📁 my_template

    +

    The top-level directory. The name is arbitrary, it will be replaced by the name of the component during coyping.

    +
  • +
  • 📁 asset_scripts and 📄 default_plots.py are copied as-is without modifications.

  • +
  • 📄 *.jinja

    +

    The content of all files with suffix .jinja are rendered using Jinja.

    +
  • +
  • 📁 [[foo]] and 📄 [[ bar ]]_script.py

    +

    File or directory names using [[ <context-variable> ]] are rendered using Jinja while copying.

    +
  • +
+
+

After rendering like this proj.add_component("dummy", [".../my_template"], render_context={"foo": "a", "bar": "b"} +a file structure like this is created:

+
+
📁 dummy
+├── 📄 index.rst
+├── 📄 test_context.py
+├── 📁 asset_scripts
+│   └── 📄 default_plots.py
+└── 📁 a
+    └── 📄 b_script.py
+
+
+
+

You see the render_context passed to +PharaohProject.add_component() +or template_context passed to PharaohProject() are used to +render file- or folder name as well as file content.

+
+

Important

+

For Generation-time Templating, Jinja is configured to use brackets for blocks [% %] and statements [[ ]] +to not interfere with Build-time Templating, where Jinja will render the same +files (only reST) again before passing it to +Sphinx using curly-braces for blocks {% %} and statements {{ }}.

+

So imagine an extreme case of a file index.rst.jinja with following content:

+
{{ h1("[[heading_prefix]]%s"|format(ctx.project.component_name)) }}
+
+
+

After Generation-time Templating +(component named Test_1 with render context heading_prefix="PREFIX - ") +it results in a file index.rst with content:

+
{{ h1("PREFIX - %s"|format(ctx.project.component_name)) }}
+
+
+

After Build-time Templating it results in content:

+
PREFIX - Test_1
+###############
+
+
+
+
+
+

Single-file Templates

+

Single-file templates, or also called template files, are smallest and most compact form of a template, but also +limited.

+

They are mainly designed to deliver template code and asset script in a single file and contribute content to +an existing component.

+

Template files are Python files with suffix .pharaoh.py those Python code creates assets and +those module level docstring represents the reST content.

+

Here an example:

+
"""
+{{ heading("My Plots", 2) }}
+
+.. pharaoh-asset:: label == "my_plot"
+"""
+from pharaoh.assetlib.api import metadata_context
+
+import plotly.express as px
+
+
+df = px.data.iris()
+fig = px.scatter(
+    df,
+    x="sepal_width",
+    y="sepal_length",
+    color="species",
+    symbol="species",
+    title=r"A title",
+)
+
+with metadata_context(label="my_plot"):
+    fig.write_html(file="iris_scatter.html")
+
+
+

This file will be internally converted to a template directory:

+
my_template.pharaoh.py -> index_my_template.rst
+                          asset_scripts/my_template.py
+
+
+

In order to automatically include all index_*.rst files in your components index file index.rst, you +must add following code:

+
+
{% for index_rst in fglob("index_*.rst") %}
+.. include:: {{ index_rst }}
+
+{% endfor %}
+
+
+
+
+
+
+

Build-time Templating

+

Build-time templating is the step where Pharaoh hooks into Sphinx’s build process and renders each documentation +source file before it gets consumed by Sphinx.

+

For example the source file index.rst will be read in, rendered, and finally passed to Sphinx for +further processing. Additionally for debugging purposes the output from rendering will be stored in the same +directory as the source file with a .rendered suffix (e.g. index.rst.rendered), +in case the Sphinx build raises errors.

+
+ +Show Example
+
+
+
+
+
+ +
+
{{ heading(ctx.local.test.test_name|req, 1) }}
+
+{{ h2("Some plots") }}
+
+
+
+ +
+
Dummy 1
+#######
+
+Some plots
+**********
+
+
+
+
+
+

Like mentionen in the Introduction, the main user groups of Pharaoh are +Template Designers and End-Users.

+

Template Designers are responsible for creating templates for the End-Users of Pharaoh. +In order to make report generation as easy as possible for the end users, following template design guidelines +have to be considered:

+
    +
  • Tradeoff between flexibility and complexity for end-users

    +

    If the designer hides much of the template code (e.g. through Template Inheritance) +and leaves the end user with just template extensions and configurations, +the reports will gain a lot of maintainability (report can be just re-build with updated base templates).

    +

    If the designer just provides component templates with less abstraction, a lot of template code will +reside in the user’s report projects. This template code can only be updated by re-generating the +report project with updated component templates.

    +

    So the general rule-of-thumb is to put all static template content or content that just needs configuration +in a base template and let the user just overwrite certain sections that are meant for it.

    +
  • +
  • Provide an abstraction library

    +

    Provide a small Python library to further standardize and reduce the amount of code users have to write in +their asset scripts.

    +
  • +
  • Build smaller modular templates that can be composed together

  • +
+
+

Template Inheritance

+

Pharaoh templates support Template Inheritance through Jinja, +which is one of the most powerful and useful features of any template engine. +It means one template can inherit from another template.

+

Generally, many report pages require the same or a similar layout and content for different pages, +so we use template inheritance to not repeat the same code in each template.

+

A base template contains the basic layout which is common to all the other templates, +and it is from this base template we extend or derive the layout for other pages.

+

In order to use inherit from base templates, those base templates must be discoverable via lookup paths. +Those lookup paths can be declared through:

+
+
    +
  • Pharaoh plugins

  • +
  • a Sphinx configuration variable pharaoh_jinja_templates in report-project/conf.py.

    +

    This is a list of absolute or relative (to conf.py parent directory) lookup paths for base templates.

    +

    Per default this is set to ["user_templates"], which is an emtpy directory created by the default +Pharaoh Sphinx project template.

    +

    Base template inside those lookup paths can be referenced via their relative path to the lookup directory, +so if we take this example:

    +
    +
    ...
    +📁 user_templates
    +├── 📄 baseA.rst
    +└── 📁 others
    +    └── 📄 baseB.rst
    +
    +
    +
    +

    Then your templates could inherit from those templates like this:

    +
    +
    {% extends "baseA.rst" %}
    +{% block xyz %}
    +{# Insert block xyz content from baseA.rst #}
    +{{ super() }}
    +Some additional content
    +{% endblock %}
    +
    +
    +

    or

    +
    {% extends "others/baseB.rst" %}
    +{% block xyz %}
    +{# Overwrites block xyz content from others/baseB.rst #}
    +Some additional content
    +{% endblock %}
    +
    +
    +
    +
  • +
+
+
+ +Example
+
+
+
+
+
+ +
+

The following base template defines an immutable page title and three sections with +immutable titles and a block declaration:

+
{{ h1("Standardized Report Title") }}
+
+{{ h2("Prologue") }}
+{% block prologue %}
+This is a default content that may be overwritten by the child template
+{% endblock %}
+
+{{ h2("Test Description") }}
+{% block test_description %}
+{% endblock %}
+
+{{ h2("Plots") }}
+{% block plots %}
+{% endblock %}
+
+
+
+ +
+

The following child template inherits from base.rst and overwrites two of the the declared blocks with +custom content, and extends block prologue:

+
{% extends "base.rst" %}
+
+{% block prologue %}
+{{ super() }}
+
+Additional content...
+{% endblock %}
+
+{% block test_description %}
+Some descriptive text...
+{% endblock %}
+
+{% block plots %}
+.. pharaoh-asset:: plot_name == "bla"
+
+{% endblock %}
+
+
+
+
+
+
+
+

Rendering Context

+

During build-time templating you have access to a variety of context variables via Jinja variable ctx. +ctx is a nested dictionary that allows dotted-access ({{ ctx.project.component_name }} or +{{ ctx["project"]["component_name"] }}) to all static or dynamic rendering context defined by Pharaoh or the user.

+
ctx                             # The root variable for accessing rendering context
+   .project                     # Project related context - set by Pharaoh
+           .instance            # The Pharaoh project instance itself
+           .component_name      # The name of the current component the template resides in
+   .config                      # The content of Sphinx's `conf.py`, e.g. `ctx.config.copyright`
+   .user                        # User defined context of dict variable `pharaoh_jinja_context` in `conf.py`
+   .local                       # Component's local static & dynamically generated context - see below
+
+
+

ctx.local is a special local context that may be different for each component or even each source file +(but that’s rarely a use-case) of a component. +It is composed by reading in so-called “local context files”, that reside next to the file that is +currently rendered. +Those files can be:

+
+
    +
  • YAML files with the naming scheme <contextname>_context.yaml. +So the content of a YAML file called default_context.yaml would be available via ctx.local.default.

    +
    +

    Note

    +

    YAML files are loaded using the OmegaConf library.

    +
    +
  • +
  • Python files with the naming scheme <contextname>_context.py.

    +

    Those Python scripts are executed and must create a dict variable called context.

    +

    Since asset generation has already been executed, these scripts can also access the +AssetFinder instance to find and read assets +to extend the render context.

    +
    + +Show Example test_context.py
    +
    +
    +
    +
    +
    """
    +This script searches all JSON assets that have "context_name" metadata set,
    +loads them and exports their content as context for rendering via
    +variable `ctx.local.<context_name>`
    +"""
    +import json
    +from pharaoh.assetlib.api import get_asset_finder, get_current_component
    +
    +finder = get_asset_finder()
    +component_name = get_current_component()
    +
    +# Find all assets of type JSON that have a "context_name" meta data set.
    +# Collect the content of those files in a dict using the "context_name" meta data as key.
    +context = {
    +    asset.context.context_name: asset.read_json()
    +    for asset in finder.search_assets(
    +        'asset.suffix == ".json" and "context_name" in asset.context',
    +        [component_name]
    +    )
    +}
    +
    +
    +
    +
  • +
  • Data context that is registered via the pharaoh.assetlib.api.register_templating_context() function.

  • +
+
+
+
+

Extending Template Syntax

+

See Templating Builtins for a complete list of builtin globals, filters and +tests you can use in your templates.

+

If you like to add you own, Pharaoh provides some entrypoints in report-project/conf.py:

+
+
pharaoh_jinja_filters

A dict that maps names to filter functions:

+
pharaoh_jinja_filters = {
+    "angry": lambda text: text + " 😠"    # Usage: {{ "error"|angry }}
+}
+
+
+
+
pharaoh_jinja_globals

A dict that maps names to global functions:

+
pharaoh_jinja_globals = {
+    "angry": lambda text: text + " 😠"    # Usage: {{ angry("error") }}
+}
+
+
+
+
pharaoh_jinja_tests

A dict that maps names to tests:

+
pharaoh_jinja_tests = {
+    "angry_text": lambda text: "😠" in text    # Usage: {% if "😠" is angry %}Someone is angry!{% endif %}
+}
+
+
+
+
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/search.html b/search.html new file mode 100644 index 0000000..ce3da04 --- /dev/null +++ b/search.html @@ -0,0 +1,389 @@ + + + + + + Search — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + + + +
+ +
+ +
+
+
+ +
+ +
+

© Copyright . + Last updated on Nov 28, 2023. +

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/searchindex.js b/searchindex.js new file mode 100644 index 0000000..c0f6265 --- /dev/null +++ b/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"docnames": ["changelog", "contact", "dev", "examples/asset_scripts", "examples/demo_reports", "examples/index", "index", "installation", "intro", "plugins/index", "plugins/plugin", "reference/api", "reference/assets", "reference/building", "reference/cli", "reference/components", "reference/directive", "reference/index", "reference/project_structure", "reference/settings", "reference/templating", "template_designer_guide", "user_guide"], "filenames": ["changelog.rst", "contact.rst", "dev.rst", "examples/asset_scripts.rst", "examples/demo_reports.rst", "examples/index.rst", "index.rst", "installation.rst", "intro.rst", "plugins/index.rst", "plugins/plugin.rst", "reference/api.rst", "reference/assets.rst", "reference/building.rst", "reference/cli.rst", "reference/components.rst", "reference/directive.rst", "reference/index.rst", "reference/project_structure.rst", "reference/settings.rst", "reference/templating.rst", "template_designer_guide.rst", "user_guide.rst"], "titles": ["Release History", "Contact", "Development", "Asset Scripts", "Demo Reports", "Examples", "Pharaoh", "Installation", "Introduction", "Plugins", "Plugin Architecture", "API Reference", "Assets", "Building", "CLI", "Components", "Pharaoh Directive", "Reference", "Project Structure", "Settings", "Templating", "Template Designer Guide", "User Guide"], "terms": {"sphinx": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22], "jinja": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22], "asset": [0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 13, 14, 15, 17, 18, 19, 20, 21], "todo": [1, 2, 19], "If": [1, 7, 11, 12, 13, 14, 15, 16, 19, 20, 22], "you": [1, 7, 11, 12, 13, 14, 16, 18, 19, 20, 22], "think": 1, "found": [1, 11, 12], "pleas": [1, 7, 16, 19, 20, 22], "provid": [1, 8, 11, 12, 19, 20, 22], "follow": [1, 7, 8, 11, 12, 13, 15, 16, 18, 19, 20, 22], "inform": [1, 7, 11, 12, 14, 16], "when": [1, 11, 12, 14, 16, 18, 19, 20], "u": [1, 7, 19, 20], "your": [1, 7, 8, 12, 13, 14, 16, 18, 19, 20, 22], "oper": [1, 12], "system": [1, 12, 14, 19, 20], "name": [1, 11, 12, 14, 15, 16, 18, 19, 20, 22], "version": [1, 7, 8, 12, 14], "info": [1, 12, 17, 19, 22], "about": [1, 7, 11, 12, 19, 22], "pharaoh": [1, 7, 8, 12, 13, 14, 15, 17, 18, 19, 20, 22], "instal": [1, 6, 12, 13, 14, 22], "open": [1, 7, 11, 12, 14, 22], "termin": [1, 12, 14, 22], "env": [1, 11, 17, 19, 22], "load": [1, 11, 12, 13, 16, 18, 19, 20], "e": [1, 7, 8, 11, 12, 14, 15, 16, 18, 19, 20], "g": [1, 7, 8, 11, 12, 14, 15, 16, 18, 19, 20], "pycharm": [1, 7, 12, 14], "type": [1, 11, 12, 14, 15, 19, 20, 22], "python": [1, 6, 7, 8, 11, 12, 13, 14, 15, 19, 20, 22], "m": [1, 7, 11, 14, 19], "That": [1, 15], "should": [1, 7, 11, 12, 13, 20], "return": [1, 11, 12, 13, 19], "someth": [1, 12], "like": [1, 7, 8, 11, 12, 13, 14, 15, 16, 18, 19, 20], "vx": 1, "y": [1, 12, 19, 20], "z": 1, "3": [1, 7, 11, 12, 14, 16], "9": [1, 7, 12, 14, 16], "13": [1, 7, 12, 14], "window": [1, 14], "10": [1, 7, 11, 12, 14], "0": [1, 6, 11, 12, 14, 16, 19], "19045": [1, 14], "sp0": [1, 14], "ani": [1, 7, 8, 11, 12, 14, 15, 16, 19, 20, 22], "detail": [1, 12], "local": [1, 11, 12, 13, 14, 16, 18, 19, 20], "setup": [1, 8], "might": [1, 8, 11, 12, 13, 15, 19, 20, 22], "troubleshoot": 1, "The": [1, 7, 8, 11, 12, 13, 14, 15, 16, 18, 19, 20, 22], "complet": [1, 12, 20], "error": [1, 11, 13, 16, 19, 20], "messag": [1, 11, 14], "traceback": 1, "just": [1, 11, 12, 13, 14, 20, 22], "screenshot": 1, "part": [1, 20], "log": [1, 13, 18, 19, 22], "file": [1, 8, 11, 12, 13, 14, 15, 16, 17, 18, 19, 22], "step": [1, 8, 13, 18, 20, 22], "reproduc": 1, "mayb": [1, 11, 16], "also": [1, 11, 12, 13, 14, 19, 20], "list": [1, 7, 11, 12, 13, 14, 15, 20], "packag": [1, 7], "pip": [1, 6, 12, 14], "freez": 1, "pharaoh_env_packag": 1, "txt": [1, 11, 12, 13, 16, 18], "demo": [5, 6, 8], "report": [5, 6, 7, 8, 11, 12, 13, 15, 17, 18, 19, 20, 22], "html": [5, 6, 8, 11, 12, 13, 14, 16, 18, 19, 20, 22], "confluenc": [5, 6, 8, 12, 13, 19], "script": [5, 6, 7, 8, 11, 13, 14, 15, 16, 17, 18, 19, 20], "i": [6, 7, 8, 11, 12, 13, 14, 15, 16, 18, 19, 20, 22], "base": [6, 8, 11, 12, 16, 19, 20, 22], "framework": [6, 7, 8, 17, 19], "gener": [6, 8, 13, 15, 16, 17, 18, 19], "variou": [6, 8, 11, 12, 19], "format": [6, 8, 12, 16, 19, 20], "pdf": [6, 7, 8], "combin": [6, 8, 16], "power": [6, 8, 16, 20], "configur": [6, 7, 8, 11, 17, 18, 19, 20], "templat": [6, 8, 12, 13, 17, 18, 19], "plot": [6, 7, 8, 11, 12, 20], "pictur": [6, 8, 12], "etc": [6, 8, 12, 20], "introduct": [6, 20], "work": [6, 11, 12, 14, 15, 19, 22], "principl": 6, "requir": [6, 11, 14, 15, 16, 20], "via": [6, 8, 11, 12, 13, 14, 15, 16, 18, 19, 20, 22], "creat": [6, 8, 11, 12, 13, 14, 15, 16, 18, 19, 20, 22], "virtual": [6, 14, 22], "environ": [6, 11, 12, 14, 19, 22], "extra": [6, 16], "user": [6, 8, 11, 12, 16, 18, 19, 20], "guid": 6, "prefac": [6, 17], "project": [6, 7, 8, 11, 12, 13, 14, 15, 16, 17, 19, 20], "updat": [6, 7, 11, 12, 17, 20], "set": [6, 7, 8, 11, 12, 13, 14, 16, 17, 18, 20], "manag": [6, 11, 12, 14, 17], "compon": [6, 8, 11, 12, 14, 16, 17, 18, 19, 20], "modifi": [6, 8, 11, 12, 13, 15, 18, 19], "design": [6, 8, 11, 15, 20, 22], "refer": [6, 7, 12, 15, 16, 19, 20, 22], "structur": [6, 17, 19, 20], "default": [6, 11, 12, 13, 14, 15, 16, 17, 18, 20, 22], "custom": [6, 11, 12, 15, 17, 20, 22], "access": [6, 8, 11, 12, 13, 15, 17, 20], "resolv": [6, 11, 16, 17], "what": [6, 17, 20, 22], "": [6, 11, 14, 16, 17, 19, 20, 22], "resourc": [6, 8, 12, 13, 17, 18, 19, 22], "an": [6, 7, 8, 11, 13, 14, 15, 16, 17, 19, 20, 22], "execut": [6, 7, 8, 11, 14, 17, 18, 19, 20], "debug": [6, 11, 13, 17, 18, 19, 20], "implement": [6, 7, 11, 17, 19, 20], "lookup": [6, 15, 17, 20], "direct": [6, 11, 12, 17, 19, 22], "option": [6, 7, 11, 12, 14, 15, 17, 19], "build": [6, 8, 11, 12, 15, 17, 18, 19, 22], "api": [6, 12, 15, 17, 18, 19, 20, 22], "archiv": [6, 11, 17, 18, 19], "time": [6, 8, 11, 12, 15, 17, 18, 19, 22], "modul": [6, 12, 15, 17, 20], "matlab": [6, 17], "integr": [6, 17], "builtin": [6, 17, 19, 20], "plugin": [6, 12, 15, 17, 20], "cli": [6, 12, 13, 15, 17, 19, 22], "command": [6, 7, 15, 17, 22], "architectur": [6, 9, 15], "exampl": [6, 11, 12, 13, 14, 15, 16, 19, 20, 22], "contact": [6, 19, 20], "help": [6, 11, 12, 14, 18], "bug": 6, "releas": 6, "histori": 6, "v0": 6, "5": [6, 11, 12], "develop": [6, 8, 12, 15], "index": [6, 11, 12, 14, 15, 16, 18, 19, 20], "search": [6, 8, 11, 12, 16, 19, 20], "page": [6, 11, 16, 18, 19, 20], "ar": [7, 8, 11, 12, 13, 14, 15, 16, 18, 19, 20, 22], "compat": [7, 11], "don": [7, 12, 19], "t": [7, 12, 14, 15, 19, 22], "have": [7, 11, 12, 13, 15, 19, 20], "download": [7, 8], "click": [7, 8, 12, 13, 14, 18], "link": [7, 11, 20], "below": [7, 11, 20], "11": [7, 12], "recommend": 7, "4": [7, 11, 12, 16], "min": 7, "2": [7, 11, 12, 16, 19, 20], "A": [7, 11, 12, 13, 14, 15, 16, 18, 19, 20], "ha": [7, 11, 12, 15, 16, 18, 19, 20, 22], "its": [7, 8, 11, 12, 14, 15, 16, 22], "own": [7, 8, 12, 15, 16, 19, 20, 22], "binari": 7, "which": [7, 11, 12, 15, 16, 20], "match": [7, 11, 12, 14, 16, 19], "wa": [7, 12, 14, 16, 19], "us": [7, 8, 11, 12, 13, 14, 15, 16, 18, 19, 20, 22], "thi": [7, 8, 11, 12, 13, 14, 15, 16, 18, 19, 20, 22], "can": [7, 8, 11, 12, 13, 14, 15, 16, 19, 20, 22], "independ": 7, "site": 7, "directori": [7, 11, 12, 13, 14, 15, 16, 18, 19, 20, 22], "easiest": 7, "wai": [7, 12, 14, 16, 19], "let": [7, 12, 15, 19, 20], "id": [7, 11, 12, 15, 16], "one": [7, 8, 11, 12, 14, 16, 19, 20], "show": [7, 12, 17, 19, 20, 22], "how": [7, 12, 15, 16, 18, 19, 20, 22], "do": [7, 12, 14, 19, 20, 22], "other": [7, 8, 11, 12, 13, 15, 16, 19, 20], "line": [7, 12, 14, 20], "interpret": [7, 12, 14], "venv": [7, 12, 13], "creation": [7, 19], "shell": [7, 11, 19], "inicio": 7, "powershel": [7, 22], "chang": [7, 11, 13, 15, 18, 19], "workspac": 7, "cd": [7, 12, 14, 22], "c": [7, 11, 12, 14, 15, 16, 22], "temp": [7, 11, 15], "python311": 7, "ex": [7, 14], "pharaoh_venv": 7, "activ": [7, 12, 13, 14, 22], "ps1": 7, "bash": [7, 22], "sourc": [7, 11, 12, 13, 15, 19, 20], "cmd": [7, 12, 13, 14, 18, 22], "bat": 7, "get": [7, 11, 12, 15, 16, 18, 19, 20], "prefix": [7, 11, 19, 20], "p": [7, 12, 14, 22], "ensur": 7, "where": [7, 11, 12, 15, 16, 18, 19, 20], "possibl": [7, 11, 12, 14, 16, 19, 20], "wheel": 7, "first": [7, 8, 11, 12, 14, 20, 22], "find": [7, 11, 12, 14, 17, 20], "more": [7, 11, 12, 16, 19], "here": [7, 12, 16, 18, 20, 22], "after": [7, 13, 16, 18, 19, 20, 22], "been": [7, 20], "ifx": 7, "section": [7, 12, 13, 16, 20, 22], "addit": [7, 8, 11, 12, 13, 14, 16, 18, 19, 20, 22], "depend": [7, 8, 11, 12, 13, 14, 15, 16], "For": [7, 11, 12, 13, 15, 18, 19, 20, 22], "latest": [7, 8], "append": [7, 11, 13, 15], "flag": 7, "Then": [7, 20], "would": [7, 12, 19, 20], "abl": 7, "run": [7, 11, 12], "field": 7, "comma": [7, 11, 16], "separ": [7, 11, 12, 16, 18, 19], "group": [7, 8, 11, 15, 17, 20], "mai": [7, 8, 11, 12, 13, 14, 15, 16, 20, 22], "case": [7, 11, 12, 13, 14, 15, 16, 18, 19, 20], "bokeh": [7, 12, 19, 22], "export": [7, 11, 17, 20], "holoview": [7, 12, 19], "plotli": [7, 12, 19, 20], "matplotlib": [7, 12, 19], "jupyt": [7, 12], "panda": [7, 12, 16, 19], "data": [7, 8, 11, 12, 15, 16, 20], "analysi": 7, "librari": [7, 11, 19, 20, 22], "svg": [7, 12, 16, 19], "convert": [7, 11, 15, 16, 20], "latex": [7, 8, 12, 13, 18, 20], "builder": [7, 13, 16, 19], "all": [7, 11, 12, 13, 14, 15, 16, 18, 19, 20], "support": [7, 12, 13, 16, 19, 20], "tabl": [8, 12, 16, 18], "powerpoint": 8, "icon": 8, "present": 8, "slide": 8, "reflect": 8, "mainli": [8, 12, 20], "serv": 8, "two": [8, 12, 20], "thei": [8, 12, 14, 19, 20], "pre": [8, 22], "defin": [8, 11, 12, 13, 15, 16, 18, 19, 20, 22], "post": 8, "process": [8, 11, 12, 16, 19, 20], "method": [8, 11, 12, 13, 15], "therefor": [8, 12], "simplif": 8, "layer": 8, "end": [8, 11, 14, 16, 20, 22], "final": [8, 20], "content": [8, 11, 12, 15, 16, 18, 20, 22], "context": [8, 11, 12, 14, 15, 16, 17, 19], "fill": 8, "variabl": [8, 11, 12, 13, 18, 19, 20, 22], "standard": [8, 16, 20], "workflow": [8, 20], "consist": [8, 15, 16, 19], "major": [8, 20], "multipl": [8, 11, 12, 14, 15, 16, 19], "predefin": [8, 12, 16], "Its": 8, "core": [8, 11, 20], "fulli": 8, "preconfigur": 8, "document": [8, 11, 12, 16, 19, 20, 22], "call": [8, 11, 12, 13, 18, 19, 20], "level": [8, 11, 15, 18, 19, 20, 22], "further": [8, 20], "It": [8, 11, 12, 16, 18, 20], "check": [8, 14], "git": [8, 18, 22], "increment": 8, "modif": [8, 16, 20], "each": [8, 11, 12, 14, 16, 18, 20], "now": [8, 12, 19], "well": [8, 16, 20], "dure": [8, 11, 12, 14, 15, 18, 19, 20, 22], "By": [8, 17], "produc": [8, 16], "dynam": [8, 11, 12, 19, 20], "later": [8, 11, 12], "imag": [8, 12, 16], "json": [8, 11, 12, 14, 20], "render": [8, 11, 12, 14, 15, 16, 17, 18], "those": [8, 11, 12, 14, 15, 16, 19, 20, 22], "regist": [8, 11, 15, 17, 18, 20], "metadata": [8, 11, 14, 15, 16, 17, 20], "so": [8, 12, 13, 14, 15, 16, 19, 20], "includ": [8, 11, 13, 15, 16, 17, 18, 20, 22], "amount": [8, 15, 19, 20], "translat": 8, "exist": [8, 11, 14, 15, 16, 20, 22], "target": [8, 12, 13, 22], "while": [8, 12, 19, 20], "perform": [8, 14, 20], "second": 8, "inherit": [8, 17], "extend": [8, 11, 16, 17, 22], "re": [8, 11, 12, 15, 20, 22], "purpos": [8, 12, 15, 19, 20], "contain": [8, 11, 12, 13, 14, 15, 18, 20], "static": [8, 17, 19, 20], "manual": [8, 11, 16, 17, 19, 22], "enter": [8, 11, 14], "potenti": [8, 15, 22], "item": [11, 12], "import": [11, 12, 13, 15, 19, 20, 22], "from": [11, 12, 13, 14, 15, 18, 19, 20, 22], "relat": [11, 20], "function": [11, 12, 15, 16, 19, 20, 22], "get_project": [11, 12, 17, 19], "lookup_path": 11, "str": [11, 12, 13, 15, 19, 22], "path": [11, 12, 13, 14, 15, 16, 19, 20, 22], "none": [11, 13, 14, 15], "pharaohproject": [11, 12, 13, 15, 17, 19, 20, 22], "instanc": [11, 12, 15, 19, 20], "paramet": [11, 12, 13, 15], "tri": 11, "singleton": 11, "alreadi": [11, 13, 14, 15, 19, 20, 22], "memori": [11, 19], "rais": [11, 16, 20], "except": [11, 12, 16], "given": [11, 16], "string": [11, 14, 16], "parent": [11, 20], "folder": [11, 12, 13, 20], "yaml": [11, 14, 15, 18, 19, 20, 22], "class": [11, 14], "arg": [11, 14], "kwarg": 11, "project_settings_yaml": 11, "__init__": 11, "project_root": [11, 13, 19, 22], "pathlik": 11, "overwrit": [11, 13, 14, 15, 18, 19, 20, 22], "bool": [11, 15], "fals": [11, 15, 16, 19], "iter": [11, 12, 15], "default_project": [11, 20, 22], "template_context": [11, 20, 22], "dict": [11, 12, 15, 16, 19, 20, 22], "instanti": [11, 19], "either": [11, 12, 14], "root": [11, 13, 16, 20, 22], "new": [11, 15, 16, 17, 20, 22], "them": [11, 12, 13, 14, 16, 19, 20], "number": [11, 12], "select": [11, 12, 13, 14, 15, 16, 18], "custom_set": [11, 19, 22], "add_compon": [11, 15, 20], "component_nam": [11, 12, 15, 20], "empti": [11, 15], "render_context": [11, 15, 20], "int": [11, 12, 15], "1": [11, 12, 14, 15, 16, 19, 20], "add": [11, 12, 15, 16, 17, 18, 19, 20, 22], "fileresourc": [11, 12, 14, 15, 17, 22], "proj": [11, 12, 13, 15, 19, 20, 22], "component_abc": [11, 15], "plugin_abc": [11, 15], "template_xyz": [11, 15], "tmpl": [11, 15], "py": [11, 12, 13, 15, 18, 20], "foo": [11, 12, 14, 15, 20, 22], "bar": [11, 12, 14, 15, 20, 22], "alia": [11, 12, 14, 15, 20, 22], "dlh5_result": [11, 15], "pattern": [11, 14, 15, 22], "dlh5": [11, 15], "some": [11, 12, 14, 15, 16, 19, 20, 22], "tag": [11, 15, 16, 20], "valu": [11, 12, 14, 15, 16, 19, 20, 22], "must": [11, 14, 15, 16, 20], "valid": [11, 14, 15, 16, 19], "identifi": [11, 14, 15], "singl": [11, 12, 15, 17, 18, 19, 22], "sinc": [11, 12, 14, 15, 18, 19, 20], "specifi": [11, 12, 15, 16, 19, 20], "order": [11, 15, 20], "matter": [11, 12, 15], "differ": [11, 12, 14, 15, 20, 22], "equal": [11, 15], "thu": [11, 14, 15], "previou": [11, 15], "enabl": [11, 12, 15, 19], "composit": [11, 15, 20], "chunk": [11, 15], "bigger": [11, 15], "smaller": [11, 15, 20], "usabl": [11, 15], "block": [11, 15, 16, 20, 22], "omit": [11, 13, 14, 15], "actual": [11, 12, 15, 18], "avail": [11, 12, 14, 15, 16, 19, 20], "under": [11, 15, 16], "respect": [11, 12, 15], "kei": [11, 12, 13, 14, 15, 19, 20, 22], "dictionari": [11, 14, 15, 20], "find_compon": [11, 15, 17], "true": [11, 12, 14, 15, 16, 19, 22], "overwritten": [11, 15, 20], "add_template_to_compon": [11, 15], "archive_report": [11, 13], "dest": [11, 13, 14], "destin": [11, 13], "rel": [11, 12, 13, 14, 16, 19, 20], "filenam": [11, 12, 13, 14], "taken": [11, 13], "archive_nam": [11, 13, 19], "properti": 11, "asset_build_dir": 11, "asset_find": 11, "assetfind": [11, 12, 16, 17, 20], "build_report": [11, 12, 13], "catch_error": 11, "statu": 11, "code": [11, 15, 20, 22], "instead": [11, 12, 15, 19], "express": [11, 12, 14, 19, 20], "dictconfig": 11, "evalu": [11, 14], "definit": [11, 15, 18], "truthi": 11, "result": [11, 12, 15, 18, 20], "dummi": [11, 12, 14, 20, 22], "fail": 11, "treat": [11, 14, 16, 19], "alwai": [11, 12, 16, 19, 20], "generate_asset": [11, 12, 13], "component_filt": [11, 12], "parallel": [11, 12, 16, 19], "child": [11, 12, 20], "worker": [11, 12, 19], "determin": [11, 12, 15, 16, 22], "asset_gen": [11, 12, 16, 19], "worker_process": [11, 12, 19], "sequenti": [11, 12, 19], "current": [11, 12, 13, 14, 15, 16, 19, 20, 22], "script_ignore_pattern": [11, 12, 19], "ignor": [11, 12, 16, 18, 19], "put": [11, 12, 19, 20], "comment": [11, 12], "start": [11, 12, 16, 19], "regular": [11, 12, 14, 19], "against": [11, 12, 14], "regener": [11, 12, 14], "clear": [11, 12, 14], "get_default_sphinx_configur": 11, "confdir": 11, "conf": [11, 13, 18, 20], "get_resourc": [11, 12, 15, 17], "get_set": [11, 12, 19], "object": [11, 14], "to_contain": 11, "dot": [11, 19, 20], "prefer": 11, "wherea": [11, 16], "toml": 11, "boolean": 11, "queri": 11, "lookuperror": 11, "unless": [11, 14, 19], "titl": [11, 12, 14, 16, 18, 19, 20, 22], "cloud": 11, "author": [11, 19], "loibljoh": 11, "omegaconf": [11, 19, 20, 22], "doe": [11, 12, 14, 19, 20], "recurs": 11, "config": [11, 13, 19, 20], "primit": 11, "merg": [11, 19], "namespac": [11, 13, 19], "iter_compon": 11, "over": [11, 12, 16], "iter_resourc": 11, "load_set": [11, 19], "pharao": 11, "abov": [11, 14, 16, 19], "open_report": 11, "put_set": [11, 19, 22], "effect": [11, 16, 19], "same": [11, 12, 15, 16, 19, 20], "remove_compon": [11, 15], "regex": [11, 14], "remov": [11, 12, 17, 22], "insensit": [11, 14, 19], "full": [11, 14], "argument": [11, 12, 14, 16, 19], "partial": [11, 12, 13, 14], "got": [11, 12], "save_set": [11, 19], "include_env": [11, 19], "save": [11, 12, 19], "back": [11, 14], "persist": [11, 19, 22], "settings_fil": 11, "sphinx_report_build": 11, "sphinx_report_project": 11, "sphinx_report_project_compon": 11, "update_resourc": [11, 15], "assetlib": [11, 12, 15, 20], "themselv": [11, 12], "get_asset_find": [11, 12, 17, 20], "get_current_compon": [11, 12, 17, 20], "within": [11, 12, 13], "place": [11, 12, 19], "insid": [11, 12, 13, 14, 19, 20, 22], "analyz": 11, "stack": [11, 17], "metadata_context": [11, 12, 17, 20], "context_nam": [11, 12, 20], "metadatacontext": 11, "entri": [11, 15], "left": 11, "again": [11, 12, 20], "see": [11, 12, 15, 16, 18, 19, 20], "doc": [11, 12, 18, 19], "look": [11, 12, 15, 16, 18, 19, 20], "up": [11, 15, 16], "keyword": [11, 19], "register_asset": [11, 12, 16, 17], "bytesio": [11, 12, 16], "copy2build": 11, "copi": [11, 12, 20], "real": 11, "written": [11, 13], "even": [11, 20], "store": [11, 12, 18, 20], "automat": [11, 12, 16, 20, 22], "infer": 11, "io": [11, 12, 16, 19], "disk": 11, "referenc": [11, 12, 20], "background": 11, "onli": [11, 12, 13, 16, 18, 19, 20, 22], "demand": 11, "ifram": [11, 12, 16, 19], "register_templating_context": [11, 17, 20], "stage": [11, 20], "directli": [11, 12, 13, 16], "easili": [11, 15], "extract": [11, 12], "baz": [11, 14, 22], "ctx": [11, 20], "mycontext": 11, "point": 11, "otherwis": [11, 15, 16, 19], "cannot": 11, "intern": [11, 12, 15, 16, 19, 20], "pharaoh_templating_context": 11, "mostli": [11, 20], "pass": [11, 12, 16, 19, 20, 22], "dump": 11, "discov": [11, 12], "report_project": [11, 12, 13, 15], "asset_build": [11, 12, 18], "discover_asset": 11, "assetinfo": [11, 12], "collect": [11, 20], "_asset": 11, "map": [11, 20], "get_asset_by_id": 11, "correspond": [11, 14, 20], "certain": [11, 12, 20, 22], "iter_asset": 11, "search_asset": [11, 12, 16, 20], "condit": [11, 12, 16], "label": [11, 12, 16, 20], "_plot": 11, "finder": [11, 17, 20], "suffix": [11, 12, 15, 16, 20], "endswith": 11, "whose": [11, 15], "info_fil": 11, "hold": [11, 12, 19], "md5": 11, "hash": 11, "__id__": 11, "uniqu": [11, 12, 16], "quickli": [11, 20], "infofil": 11, "absolut": [11, 14, 15, 20], "assetfil": 11, "pars": 11, "asset_groupbi": [11, 12, 17], "seq": 11, "sort_revers": 11, "agroupbi": [11, 12], "we": [11, 12, 20], "simplifi": 11, "notat": 11, "b": [11, 12, 16, 19, 20], "yield": 11, "nest": [11, 15, 20], "attribut": [11, 19], "revers": 11, "sort": 11, "out": [11, 12, 19], "input": [11, 18], "descend": 11, "cachedir": 11, "localresourc": [11, 17], "wildcard": 11, "mycsvfil": 11, "csv": [11, 20], "ascend": 11, "pathnam": 11, "simpl": [11, 12, 14, 20, 22], "style": [11, 12, 16, 20], "special": [11, 16, 20], "done": [11, 13, 15, 22], "natsort": 11, "filesystem": 11, "natur": 11, "01": [11, 12], "first_match": 11, "chosen": [11, 16], "get_fil": 11, "zero": [11, 12], "subdirectori": [11, 12, 15], "get_match": 11, "n": [11, 14, 22], "th": 11, "last_match": 11, "last": 11, "locat": [11, 12, 15, 19], "customresourc": [11, 15, 17], "trait": [11, 15], "arbitrari": [11, 20], "temporari": [11, 18], "replac": [11, 20], "specif": [11, 16, 20, 22], "yet": [11, 12, 20], "could": [11, 12, 15, 16, 20], "databas": [11, 12], "connect": 11, "d": [11, 14, 16, 18], "serial": 11, "deseri": 11, "abc": [11, 22], "repres": [11, 15, 20], "hard": 11, "drive": 11, "abstract": [11, 20], "transformedresourc": [11, 17], "anoth": [11, 12, 16, 20], "transform": [11, 12, 16], "befor": [11, 12, 16, 20], "cach": [11, 18], "recreat": 11, "start_opt": 11, "nodesktop": 11, "engin": [11, 20], "usag": [11, 14, 18, 19, 20], "eng": 11, "err": [11, 12], "execute_script": 11, "myscript": 11, "execute_funct": [11, 12], "myfunc": 11, "800": 11, "nargout": [11, 12], "__enter__": 11, "__exit__": 11, "exc_typ": 11, "exc_val": 11, "exc_tb": 11, "exit": [11, 14], "disconnect": 11, "http": [11, 18, 19], "de": 11, "mathwork": 11, "com": 11, "ref": 11, "matlabwindow": 11, "me": 11, "matlabengin": [11, 12], "function_nam": 11, "workdir": 11, "tupl": 11, "stdout": 11, "stream": 11, "stderr": 11, "shape": 11, "altern": [11, 13], "posit": 11, "output": [11, 12, 13, 18, 19, 20, 22], "greater": 11, "than": [11, 12, 13], "skip": [11, 16, 22], "script_path": [11, 12], "show_gui": 11, "make": [11, 12, 13, 14, 16, 19, 20], "gui": 11, "visibl": 11, "test": [11, 12, 20], "cours": 11, "describ": [11, 16, 20], "chapter": 11, "scope": 11, "callabl": 11, "h1": [11, 20], "assert_tru": 11, "statement": [11, 12, 16, 20], "wrapper": 11, "assert": 11, "assertionerror": 11, "read_text": [11, 12], "read": [11, 12, 20], "utf": 11, "8": [11, 12, 16], "encod": 11, "head": [11, 12, 19, 20], "text": [11, 12, 14, 16, 20], "shortcut": 11, "h7": 11, "h2": [11, 16, 20], "sub": 11, "top": [11, 20], "7": 11, "raise_help": 11, "msg": 11, "hrule": 11, "1px": 11, "grai": 11, "horizont": 11, "rule": [11, 16, 20], "raw": [11, 12, 16], "fglob": [11, 20], "index_files1": 11, "index_": [11, 20], "rst": [11, 12, 15, 16, 18, 20], "index_files2": 11, "subdir": 11, "glob": 11, "anchor": [11, 19], "rand_id": 11, "char": 11, "made": 11, "sure": [11, 12, 13, 19], "pipe": 11, "symbol": [11, 12, 20], "parenthes": 11, "chain": [11, 14], "appli": [11, 16], "next": [11, 12, 20], "trim": 11, "whitespac": [11, 16], "accept": 11, "around": 11, "listx": 11, "join": [11, 13], "or_default": 11, "or_d": 11, "to_path": 11, "pathlib": [11, 15], "oc_resolv": 11, "undefinederror": 11, "undefin": 11, "req": [11, 20], "test_nam": [11, 14, 20, 22], "hasattr_": 11, "obj": 11, "oc_get": 11, "cfg": 11, "plot_titl": 11, "rep": 11, "repr": 11, "md2html": 11, "markdown": [11, 16], "commonmark": 11, "syntax": [11, 14, 16, 17, 22], "port": 11, "pypi": 11, "unfortun": 11, "docstr": [11, 15, 20], "asset_script": [12, 15, 20], "notebook": 12, "hdf5": 12, "kind": 12, "embed": [12, 19], "indirectli": 12, "consum": [12, 20], "mean": [12, 14, 15, 16, 20], "besid": [12, 22], "wherev": 12, "f": [12, 14, 22], "dummy_": 12, "dummy_1": 12, "dummy_2": 12, "conveni": [12, 19], "ship": 12, "session": 12, "need": [12, 13, 18, 19, 20, 22], "want": [12, 19], "non": [12, 20], "integ": [12, 14, 19], "auto": [12, 19], "debugg": [12, 13], "deploy": 12, "attach": 12, "subprocess": 12, "choic": 12, "built": [12, 13, 18], "being": [12, 14], "outsid": 12, "main": [12, 15, 18, 19, 20, 22], "monkei": 12, "toolkit": [12, 19, 22], "behav": 12, "accord": [12, 22], "offici": 12, "fig": [12, 20], "write_imag": [12, 19], "myplot": 12, "png": [12, 16, 19], "simplic": 12, "without": [12, 16, 19, 20], "refresh": 12, "assum": 12, "our": [12, 20, 22], "my_plot": [12, 20], "px": [12, 20], "scatter": [12, 20], "data_fram": 12, "iri": [12, 20], "famou": 12, "x": [12, 20], "sepal_width": [12, 20], "sepal_length": [12, 20], "color": [12, 20], "speci": [12, 20], "write_html": [12, 19, 20], "iris_scatt": [12, 20], "analys": 12, "better": 12, "ll": 12, "explain": 12, "figur": 12, "filter": [12, 14, 16, 17, 20], "width": [12, 16, 19, 22], "500px": [12, 19], "height": [12, 16, 19, 22], "magic": 12, "graph_object": 12, "mani": [12, 19, 20], "rather": 12, "iris_scatter_9c30799b": 12, "shorten": 12, "script_nam": 12, "user_filepath": 12, "stem": 12, "raw_html": [12, 16], "advantag": [12, 14, 15], "unnecessari": 12, "assign": [12, 13], "through": [12, 14, 20], "basic": [12, 17], "signal": 12, "100": [12, 19], "vdd": 12, "though": 12, "construct": [12, 16], "ad": [12, 16, 17, 18, 19, 20, 22], "Will": [12, 19], "ones": [12, 19], "rang": 12, "becaus": [12, 13], "global": [12, 20], "anywai": 12, "cell": 12, "deactiv": 12, "Or": [12, 13], "mc": 12, "seaborn": 12, "my_html_snippet": 12, "div": [12, 16], "restructuredtext": [12, 20], "my_rst_snippet": 12, "raw_rst": [12, 16], "literalinclud": [12, 16], "my_txt_snippet": [12, 16], "raw_txt": [12, 16], "popular": 12, "tool": 12, "take": [12, 20], "care": 12, "datafram": [12, 16], "to_html": [12, 16, 19], "datat": [12, 16, 19], "NOT": 12, "styler": [12, 16], "html_file_nam": 12, "styled_t": 12, "buf": 12, "pd": 12, "df": [12, 20], "blabla": 12, "float": 12, "hex": 12, "0x11": 12, "0x12": 12, "0x13": 12, "prevent": 12, "browser": [12, 16], "r": [12, 14, 18, 20, 22], "iris_scatter1": 12, "iris_scatter2": 12, "iris_scatter3": 12, "500": 12, "export_png": [12, 19, 22], "export_svg": 12, "sampledata": 12, "flower": 12, "colormap": 12, "setosa": 12, "red": 12, "versicolor": 12, "green": 12, "virginica": 12, "blue": 12, "morphologi": 12, "400": 12, "xaxi": 12, "axis_label": 12, "petal": 12, "length": [12, 16], "yaxi": 12, "petal_length": 12, "petal_width": 12, "fill_alpha": 12, "size": 12, "pyplot": 12, "savefig": [12, 19], "plt": 12, "numpi": 12, "np": 12, "ax": 12, "subplot": 12, "arang": 12, "sin": 12, "pi": 12, "xlabel": 12, "ylabel": 12, "voltag": 12, "mv": 12, "folk": 12, "grid": 12, "util": 12, "backend": 12, "hv": 12, "chr": 12, "97": 12, "j": [12, 16], "ext": 12, "extens": [12, 14, 16, 17, 19, 20, 22], "model": 12, "heatmap": 12, "opt": 12, "cmap": 12, "rdbu_r": 12, "heatmap_holo_plotli": 12, "heatmap_holo_bokeh": 12, "heatmap_holo_mpl": 12, "interact": [12, 16, 19], "review": 12, "nice": 12, "element": 12, "tweak": 12, "suboptim": 12, "force_stat": [12, 19], "yourself": [12, 20], "altair": 12, "alt": [12, 16], "moment": 12, "pharaoh_project": [12, 19], "__file__": [12, 19], "chart": 12, "els": 12, "plotting_framework": 12, "still": 12, "resource_alia": [12, 15], "some_resource_path": 12, "plot_path": 12, "generate_plot": 12, "from_matlab": 12, "To": [12, 13, 14, 15, 19], "r2020b": 12, "r2021a": 12, "r2021b": 12, "r2022a": 12, "12": [12, 14], "r2022b": 12, "r2023a": 12, "14": 12, "r2023b": 12, "15": 12, "deal": 12, "respons": [12, 20], "In": [12, 16, 18, 20, 22], "measur": 12, "snippet": 12, "image_asset": [12, 16], "summar": 12, "all_image_asset": 12, "search_assets_glob": 12, "preset": 12, "asset_rel_path_from_project": 12, "scale": [12, 16, 19], "50": 12, "caption": [12, 16], "paragraph": 12, "mytxt_f65223c4": 12, "asset_rel_path_from_build": 12, "src": 12, "lazi": [12, 16], "br": 12, "pharaoh_asset": 12, "iris_scatter_3e7d7ab7": 12, "iout": 12, "signal_nam": 12, "idd": 12, "idd_plot": 12, "realli": [12, 14, 18], "viabl": 12, "usual": [12, 22], "idea": 12, "were": 12, "But": 12, "know": 12, "1fv": 12, "1fa": 12, "vout": 12, "endfor": [12, 16, 20], "luckili": 12, "assets_grby_vdd": 12, "assets_grby_iout": 12, "both": 12, "choos": 13, "singleconflu": [13, 19], "alter": 13, "html_css_file": 13, "inspect": 13, "print": [13, 16, 17], "css": [13, 18], "mystyl": 13, "reason": 13, "most": [13, 15, 20], "everytim": 13, "__name__": 13, "__main__": 13, "guard": 13, "multiprocess": [13, 19], "doubl": [13, 14, 18, 19], "displai": [13, 19], "consol": 13, "log_warn": [13, 18], "warn": [13, 18, 19], "command1": 14, "command2": 14, "commandlin": 14, "interfac": 14, "pharaoh_test": [14, 22], "my_set": 14, "dummy1": [14, 22], "multi": 14, "dummy2": 14, "myarchiv": 14, "zip": [14, 18, 19], "itself": [14, 20], "skeleton": 14, "entrypoint": [14, 20], "installdir": 14, "onc": [14, 15, 20, 22], "my_pharaoh_report": 14, "cwd": 14, "regardless": [14, 19], "fallback": [14, 16], "fall": 14, "renam": 14, "move": 14, "won": [14, 15], "anymor": 14, "action": [14, 16], "sens": 14, "similar": [14, 20], "forc": [14, 17, 22], "surround": 14, "my_compon": 14, "foo_a": 14, "123": [14, 22], "foo_b": 14, "foo_c": 14, "foo_d": 14, "foo_": 14, "entir": [14, 22], "subset": 14, "my": [14, 19, 20, 22], "v1": 14, "per": [15, 18, 19, 20], "sequenc": 15, "identif": 15, "arbitrarili": 15, "extern": [15, 19], "layout": [15, 18, 20], "rddl": [15, 19], "artifact": 15, "first_matching_file_path": 15, "theses": 15, "come": [15, 20], "form": [15, 20], "simplest": 15, "shall": 15, "allow": [15, 16, 18, 19, 20], "my_awesome_plugin": 15, "template1": 15, "template2": 15, "least": [15, 16, 20], "cross": [15, 16], "smallest": [15, 20], "compact": [15, 20], "my_templ": [15, 20], "index_my_templ": [15, 20], "limit": [15, 20], "delet": 15, "get_component_names_by_metadata": 15, "explicit": 16, "markup": [16, 20], "along": 16, "role": 16, "mechan": 16, "primari": [16, 20], "And": 16, "heavi": 16, "domain": [16, 20], "write": [16, 20], "key1": 16, "key2": 16, "key3": 16, "key4": 16, "key5": 16, "AND": 16, "ed": 16, "multilin": 16, "split": 16, "unescap": 16, "charact": 16, "quit": 16, "messi": 16, "veri": [16, 19], "fast": [16, 20], "simpli": [16, 20], "rubric": 16, "endif": [16, 20], "ye": 16, "_this_": 16, "_all_": 16, "emb": 16, "note": [16, 19], "neither": 16, "nor": 16, "align": 16, "default_iframe_width": [16, 19], "default_iframe_height": [16, 19], "behavior": 16, "descript": [16, 20], "insert": [16, 20], "rest": [16, 20], "default_datatable_extended_search": [16, 19], "There": 16, "As": [16, 20], "jpg": 16, "jpeg": 16, "gif": 16, "md": 16, "prioriti": 16, "past": 16, "autogener": 16, "linebreak": 16, "indent": 16, "languag": 16, "highlight": 16, "disabl": 16, "wrap": 16, "patch": [17, 19], "jinja2": 17, "ansibl": 17, "higher": 18, "pharaoh_report_": [18, 19], "timestamp": [18, 19], "gitignor": 18, "transient": 18, "resource_cach": 18, "_static": 18, "overrid": 18, "logo": [18, 19], "_templat": 18, "theme": 18, "footer": 18, "user_templ": [18, 20], "www": [18, 19], "org": [18, 19], "en": [18, 19], "master": [18, 19], "control": [18, 20], "toc": 18, "tree": 18, "upload": [18, 19], "datalak": 18, "three": [19, 20], "notif": 19, "hierarch": 19, "offer": 19, "runtim": 19, "safeti": 19, "lot": [19, 20], "featur": [19, 20], "interpol": 19, "best": 19, "mention": 19, "topic": 19, "logger": 19, "sphinxcontrib": 19, "confluencebuild": 19, "readthedoc": 19, "stabl": 19, "verbos": 19, "consult": 19, "utcnow": 19, "strf": 19, "d_": 19, "h": 19, "kindli": 19, "brand": 19, "show_pharaoh_logo": 19, "cpu": 19, "outperform": 19, "larg": 19, "proof": 19, "pud": 19, "_": 19, "underscor": 19, "720": [19, 22], "480": [19, 22], "dpi": 19, "150": 19, "facecolor": 19, "edgecolor": 19, "1000": 19, "600": 19, "default_width": 19, "default_height": 19, "300": 19, "na_rep": 19, "fit": 19, "everyon": 19, "sever": 19, "lowercas": 19, "expect": 19, "enough": [19, 20], "diverg": 19, "myset": 19, "plan": 19, "right": 19, "safe": 19, "o": 19, "__": 19, "act": 19, "pharaoh__a": 19, "b__c___d": 19, "c___d": 19, "reload": 19, "throughout": 19, "static_export": 19, "oc": 19, "usernam": 19, "unknown": 19, "utc": 19, "timezon": 19, "project_dir": 19, "somepath": 19, "trail": 19, "wish": 19, "notic": 20, "domin": 20, "foundat": 20, "familiar": 20, "encount": 20, "question": 20, "easi": 20, "plaintext": 20, "parser": 20, "program": 20, "web": 20, "standalon": 20, "applic": 20, "goal": 20, "readabl": 20, "trivial": 20, "learn": 20, "xml": 20, "fine": 20, "logic": 20, "heavili": 20, "inspir": 20, "distinguish": 20, "copier": 20, "These": 20, "interest": 20, "maintain": 20, "flexibl": 20, "touch": 20, "try": 20, "minim": 20, "concern": 20, "test_context": 20, "default_plot": 20, "_script": 20, "coyp": 20, "b_script": 20, "bracket": 20, "interfer": 20, "curli": 20, "brace": 20, "imagin": 20, "extrem": 20, "heading_prefix": 20, "test_1": 20, "deliv": 20, "contribut": 20, "index_rst": 20, "hook": 20, "addition": 20, "mentionen": 20, "guidelin": [20, 22], "consid": 20, "tradeoff": 20, "between": 20, "complex": 20, "hide": 20, "much": 20, "leav": 20, "gain": 20, "less": 20, "resid": 20, "thumb": 20, "meant": 20, "small": 20, "reduc": 20, "modular": 20, "compos": 20, "togeth": 20, "repeat": 20, "common": 20, "deriv": 20, "discover": 20, "declar": 20, "pharaoh_jinja_templ": 20, "emtpi": 20, "basea": 20, "baseb": 20, "xyz": 20, "super": 20, "endblock": 20, "immut": 20, "prologu": 20, "test_descript": 20, "plot_nam": 20, "bla": [20, 22], "varieti": 20, "copyright": 20, "pharaoh_jinja_context": 20, "rare": 20, "scheme": 20, "contextnam": 20, "_context": 20, "default_context": 20, "meta": 20, "read_json": 20, "pharaoh_jinja_filt": 20, "angri": 20, "lambda": 20, "pharaoh_jinja_glob": 20, "pharaoh_jinja_test": 20, "angry_text": 20, "someon": 20, "keep": 22, "mind": 22, "thing": 22, "optim": 22, "some_variable_in_extension_templ": 22, "my_custom_set": 22, "afterward": 22, "blubb": 22, "relev": 22}, "objects": {"pharaoh": [[11, 0, 0, "-", "api"]], "pharaoh.api": [[11, 1, 1, "", "get_project"]], "pharaoh.assetlib": [[11, 0, 0, "-", "api"]], "pharaoh.assetlib.api": [[11, 2, 1, "", "Matlab"], [11, 1, 1, "", "find_components"], [11, 1, 1, "", "get_asset_finder"], [11, 1, 1, "", "get_current_component"], [11, 1, 1, "", "get_resource"], [11, 1, 1, "", "metadata_context"], [11, 1, 1, "", "register_asset"], [11, 1, 1, "", "register_templating_context"]], "pharaoh.assetlib.api.Matlab": [[11, 3, 1, "", "__enter__"], [11, 3, 1, "", "__exit__"], [11, 3, 1, "", "__init__"], [11, 3, 1, "", "connect"], [11, 4, 1, "", "eng"], [11, 3, 1, "", "execute_function"], [11, 3, 1, "", "execute_script"], [11, 3, 1, "", "show_gui"]], "pharaoh.assetlib.finder": [[11, 2, 1, "", "Asset"], [11, 2, 1, "", "AssetFinder"], [11, 1, 1, "", "asset_groupby"]], "pharaoh.assetlib.finder.AssetFinder": [[11, 3, 1, "", "__init__"], [11, 3, 1, "", "discover_assets"], [11, 3, 1, "", "get_asset_by_id"], [11, 3, 1, "", "iter_assets"], [11, 3, 1, "", "search_assets"]], "pharaoh.assetlib.resource": [[11, 2, 1, "", "CustomResource"], [11, 2, 1, "", "FileResource"], [11, 2, 1, "", "LocalResource"], [11, 2, 1, "", "Resource"], [11, 2, 1, "", "TransformedResource"]], "pharaoh.assetlib.resource.FileResource": [[11, 3, 1, "", "first_match"], [11, 3, 1, "", "get_files"], [11, 3, 1, "", "get_match"], [11, 3, 1, "", "last_match"], [11, 3, 1, "", "locate"]], "pharaoh.assetlib.resource.LocalResource": [[11, 3, 1, "", "locate"]], "pharaoh.assetlib.resource.TransformedResource": [[11, 3, 1, "", "transform"]], "pharaoh.project": [[11, 2, 1, "", "PharaohProject"]], "pharaoh.project.PharaohProject": [[11, 5, 1, "", "PROJECT_SETTINGS_YAML"], [11, 3, 1, "", "__init__"], [11, 3, 1, "", "add_component"], [11, 3, 1, "", "add_template_to_component"], [11, 3, 1, "", "archive_report"], [11, 4, 1, "", "asset_build_dir"], [11, 4, 1, "", "asset_finder"], [11, 3, 1, "", "build_report"], [11, 3, 1, "", "find_components"], [11, 3, 1, "", "generate_assets"], [11, 3, 1, "", "get_default_sphinx_configuration"], [11, 3, 1, "", "get_resource"], [11, 3, 1, "", "get_setting"], [11, 3, 1, "", "get_settings"], [11, 3, 1, "", "iter_components"], [11, 3, 1, "", "iter_resources"], [11, 3, 1, "", "load_settings"], [11, 3, 1, "", "open_report"], [11, 4, 1, "", "project_root"], [11, 3, 1, "", "put_setting"], [11, 3, 1, "", "remove_component"], [11, 3, 1, "", "save_settings"], [11, 4, 1, "", "settings_file"], [11, 4, 1, "", "sphinx_report_build"], [11, 4, 1, "", "sphinx_report_project"], [11, 4, 1, "", "sphinx_report_project_components"], [11, 3, 1, "", "update_resource"]], "pharaoh.templating.second_level.env_filters": [[11, 1, 1, "", "exists"], [11, 1, 1, "", "hasattr_"], [11, 1, 1, "", "md2html"], [11, 1, 1, "", "oc_get"], [11, 1, 1, "", "oc_resolve"], [11, 1, 1, "", "or_default"], [11, 1, 1, "", "rep"], [11, 1, 1, "", "required"], [11, 1, 1, "", "to_path"]], "pharaoh.templating.second_level.env_globals": [[11, 1, 1, "", "assert_true"], [11, 1, 1, "", "fglob"], [11, 1, 1, "", "heading"], [11, 1, 1, "", "hrule"], [11, 1, 1, "", "raise_helper"], [11, 1, 1, "", "rand_id"], [11, 1, 1, "", "read_text"]]}, "objtypes": {"0": "py:module", "1": "py:function", "2": "py:class", "3": "py:method", "4": "py:property", "5": "py:attribute"}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "function", "Python function"], "2": ["py", "class", "Python class"], "3": ["py", "method", "Python method"], "4": ["py", "property", "Python property"], "5": ["py", "attribute", "Python attribute"]}, "titleterms": {"releas": 0, "histori": 0, "v0": 0, "5": 0, "0": 0, "contact": 1, "help": 1, "bug": 1, "report": [1, 4, 14], "develop": 2, "asset": [3, 11, 12, 16, 22], "script": [3, 12, 22], "demo": 4, "html": 4, "confluenc": 4, "exampl": 5, "pharaoh": [6, 11, 16], "tabl": 6, "content": 6, "indic": 6, "instal": 7, "requir": 7, "via": 7, "pip": 7, "creat": 7, "virtual": 7, "environ": 7, "extra": 7, "introduct": 8, "work": 8, "principl": 8, "plugin": [9, 10, 11, 14], "architectur": 10, "api": [11, 13], "refer": [11, 17], "modul": 11, "gener": [11, 12, 14, 20, 22], "resourc": [11, 14, 15], "matlab": [11, 12], "integr": [11, 12], "templat": [11, 14, 15, 16, 20, 21, 22], "builtin": 11, "jinja2": 11, "extens": 11, "global": 11, "filter": 11, "ansibl": 11, "what": [12, 15], "": [12, 15], "an": 12, "execut": 12, "debug": 12, "implement": 12, "metadata": 12, "stack": 12, "manual": 12, "regist": 12, "patch": 12, "framework": 12, "forc": 12, "static": 12, "export": 12, "lookup": 12, "finder": 12, "includ": 12, "group": 12, "By": 12, "build": [13, 14, 20], "configur": 13, "archiv": [13, 14], "cli": 14, "prefac": [14, 22], "command": 14, "new": 14, "add": 14, "updat": [14, 15, 22], "remov": [14, 15], "env": 14, "show": 14, "info": 14, "print": 14, "compon": [15, 22], "manag": [15, 22], "ad": 15, "find": 15, "direct": 16, "option": 16, "project": [18, 22], "structur": 18, "set": [19, 22], "default": 19, "custom": 19, "access": 19, "resolv": 19, "time": 20, "basic": 20, "singl": 20, "file": 20, "inherit": 20, "render": 20, "context": 20, "extend": 20, "syntax": 20, "design": 21, "guid": [21, 22], "user": 22, "modifi": 22}, "envversion": {"sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx.ext.viewcode": 1, "sphinx.ext.intersphinx": 1, "sphinx": 60}, "alltitles": {"Release History": [[0, "release-history"]], "v0.5.0": [[0, "v0-5-0"]], "Contact": [[1, "contact"]], "Help!": [[1, "help"]], "Bug reports": [[1, "bug-reports"]], "Development": [[2, "development"]], "Asset Scripts": [[3, "asset-scripts"]], "Demo Reports": [[4, "demo-reports"]], "HTML": [[4, "html"]], "Confluence": [[4, "confluence"]], "Examples": [[5, "examples"]], "Pharaoh": [[6, "pharaoh"]], "Table of Contents": [[6, "table-of-contents"]], "Indices and tables": [[6, "indices-and-tables"]], "Installation": [[7, "installation"]], "Requirements": [[7, "requirements"]], "Installation via Pip": [[7, "installation-via-pip"]], "Create a virtual environment": [[7, "create-a-virtual-environment"]], "Install": [[7, "install"]], "Extras": [[7, "extras"]], "Introduction": [[8, "introduction"]], "Working Principle": [[8, "working-principle"]], "Plugins": [[9, "plugins"]], "Plugin Architecture": [[10, "plugin-architecture"]], "API Reference": [[11, "api-reference"]], "Pharaoh API Module": [[11, "pharaoh-api-module"]], "Asset Generation API Module": [[11, "asset-generation-api-module"]], "Resources": [[11, "resources"], [15, "resources"]], "Matlab Integration API": [[11, "matlab-integration-api"]], "Templating Builtins": [[11, "templating-builtins"]], "Jinja2 Builtins": [[11, "jinja2-builtins"]], "Pharaoh Extension": [[11, "pharaoh-extension"]], "Globals": [[11, "globals"]], "Filters": [[11, "filters"]], "Ansible Filters": [[11, "ansible-filters"]], "Plugin API": [[11, "plugin-api"]], "Assets": [[12, "assets"]], "What\u2019s an Asset?": [[12, "what-s-an-asset"]], "Executing Asset Generation": [[12, "executing-asset-generation"]], "Debugging Asset Scripts": [[12, "debugging-asset-scripts"]], "Implementing Asset Scripts": [[12, "implementing-asset-scripts"]], "Metadata Stack": [[12, "metadata-stack"]], "Manually Registering Assets": [[12, "manually-registering-assets"]], "Patched Frameworks": [[12, "patched-frameworks"]], "Force Static Exports": [[12, "force-static-exports"]], "Matlab Integration": [[12, "matlab-integration"]], "Asset Lookup": [[12, "asset-lookup"]], "Asset Finder": [[12, "asset-finder"]], "Manual Include": [[12, "manual-include"]], "Grouping Assets By Metadata": [[12, "grouping-assets-by-metadata"]], "Building": [[13, "building"]], "Configuration": [[13, "configuration"]], "Build API": [[13, "build-api"]], "Archiving": [[13, "archiving"]], "CLI": [[14, "cli"]], "Preface": [[14, "preface"], [22, "preface"]], "Commands": [[14, "commands"]], "New": [[14, "new"]], "Add": [[14, "add"]], "Update-resource": [[14, "update-resource"]], "Add-template": [[14, "add-template"]], "Remove": [[14, "remove"]], "Env": [[14, "env"]], "Generate": [[14, "generate"]], "Build": [[14, "build"]], "Show-report": [[14, "show-report"]], "Archive": [[14, "archive"]], "Info": [[14, "info"]], "Print-plugins": [[14, "print-plugins"]], "Components": [[15, "components"]], "What\u2019s a Component?": [[15, "what-s-a-component"]], "Templates": [[15, "templates"]], "Managing Components": [[15, "managing-components"]], "Adding Components": [[15, "adding-components"]], "Updating Components": [[15, "updating-components"]], "Removing Components": [[15, "removing-components"]], "Finding Components": [[15, "finding-components"]], "Pharaoh Directive": [[16, "pharaoh-directive"]], "Directive Options": [[16, "directive-options"]], "Asset Templates": [[16, "asset-templates"]], "Reference": [[17, "reference"]], "Project Structure": [[18, "project-structure"]], "Settings": [[19, "settings"]], "Default Settings": [[19, "default-settings"]], "Custom Settings": [[19, "custom-settings"]], "Accessing Settings": [[19, "accessing-settings"]], "Custom Resolvers": [[19, "custom-resolvers"]], "Templating": [[20, "templating"]], "Generation-time Templating": [[20, "generation-time-templating"]], "Basics": [[20, "basics"]], "Single-file Templates": [[20, "single-file-templates"]], "Build-time Templating": [[20, "build-time-templating"]], "Template Inheritance": [[20, "template-inheritance"]], "Rendering Context": [[20, "rendering-context"]], "Extending Template Syntax": [[20, "extending-template-syntax"]], "Template Designer Guide": [[21, "template-designer-guide"]], "User Guide": [[22, "user-guide"]], "Project Generation": [[22, "project-generation"]], "Update Settings": [[22, "update-settings"]], "Manage Components": [[22, "manage-components"]], "Manage Asset Scripts": [[22, "manage-asset-scripts"]], "Modify Templates": [[22, "modify-templates"]]}, "indexentries": {"asset (class in pharaoh.assetlib.finder)": [[11, "pharaoh.assetlib.finder.Asset"]], "assetfinder (class in pharaoh.assetlib.finder)": [[11, "pharaoh.assetlib.finder.AssetFinder"]], "customresource (class in pharaoh.assetlib.resource)": [[11, "pharaoh.assetlib.resource.CustomResource"]], "fileresource (class in pharaoh.assetlib.resource)": [[11, "pharaoh.assetlib.resource.FileResource"]], "localresource (class in pharaoh.assetlib.resource)": [[11, "pharaoh.assetlib.resource.LocalResource"]], "matlab (class in pharaoh.assetlib.api)": [[11, "pharaoh.assetlib.api.Matlab"]], "project_settings_yaml (pharaohproject attribute)": [[11, "pharaoh.project.PharaohProject.PROJECT_SETTINGS_YAML"]], "pharaohproject (class in pharaoh.project)": [[11, "pharaoh.project.PharaohProject"]], "resource (class in pharaoh.assetlib.resource)": [[11, "pharaoh.assetlib.resource.Resource"]], "transformedresource (class in pharaoh.assetlib.resource)": [[11, "pharaoh.assetlib.resource.TransformedResource"]], "__enter__() (matlab method)": [[11, "pharaoh.assetlib.api.Matlab.__enter__"]], "__exit__() (matlab method)": [[11, "pharaoh.assetlib.api.Matlab.__exit__"]], "__init__() (assetfinder method)": [[11, "pharaoh.assetlib.finder.AssetFinder.__init__"]], "__init__() (matlab method)": [[11, "pharaoh.assetlib.api.Matlab.__init__"]], "__init__() (pharaohproject method)": [[11, "pharaoh.project.PharaohProject.__init__"]], "add_component() (pharaohproject method)": [[11, "pharaoh.project.PharaohProject.add_component"]], "add_template_to_component() (pharaohproject method)": [[11, "pharaoh.project.PharaohProject.add_template_to_component"]], "archive_report() (pharaohproject method)": [[11, "pharaoh.project.PharaohProject.archive_report"]], "assert_true() (in module pharaoh.templating.second_level.env_globals)": [[11, "pharaoh.templating.second_level.env_globals.assert_true"]], "asset_build_dir (pharaohproject property)": [[11, "pharaoh.project.PharaohProject.asset_build_dir"]], "asset_finder (pharaohproject property)": [[11, "pharaoh.project.PharaohProject.asset_finder"]], "asset_groupby() (in module pharaoh.assetlib.finder)": [[11, "pharaoh.assetlib.finder.asset_groupby"]], "build_report() (pharaohproject method)": [[11, "pharaoh.project.PharaohProject.build_report"]], "connect() (matlab method)": [[11, "pharaoh.assetlib.api.Matlab.connect"]], "discover_assets() (assetfinder method)": [[11, "pharaoh.assetlib.finder.AssetFinder.discover_assets"]], "eng (matlab property)": [[11, "pharaoh.assetlib.api.Matlab.eng"]], "execute_function() (matlab method)": [[11, "pharaoh.assetlib.api.Matlab.execute_function"]], "execute_script() (matlab method)": [[11, "pharaoh.assetlib.api.Matlab.execute_script"]], "exists() (in module pharaoh.templating.second_level.env_filters)": [[11, "pharaoh.templating.second_level.env_filters.exists"]], "fglob() (in module pharaoh.templating.second_level.env_globals)": [[11, "pharaoh.templating.second_level.env_globals.fglob"]], "find_components() (pharaohproject method)": [[11, "pharaoh.project.PharaohProject.find_components"]], "find_components() (in module pharaoh.assetlib.api)": [[11, "pharaoh.assetlib.api.find_components"]], "first_match() (fileresource method)": [[11, "pharaoh.assetlib.resource.FileResource.first_match"]], "generate_assets() (pharaohproject method)": [[11, "pharaoh.project.PharaohProject.generate_assets"]], "get_asset_by_id() (assetfinder method)": [[11, "pharaoh.assetlib.finder.AssetFinder.get_asset_by_id"]], "get_asset_finder() (in module pharaoh.assetlib.api)": [[11, "pharaoh.assetlib.api.get_asset_finder"]], "get_current_component() (in module pharaoh.assetlib.api)": [[11, "pharaoh.assetlib.api.get_current_component"]], "get_default_sphinx_configuration() (pharaohproject method)": [[11, "pharaoh.project.PharaohProject.get_default_sphinx_configuration"]], "get_files() (fileresource method)": [[11, "pharaoh.assetlib.resource.FileResource.get_files"]], "get_match() (fileresource method)": [[11, "pharaoh.assetlib.resource.FileResource.get_match"]], "get_project() (in module pharaoh.api)": [[11, "pharaoh.api.get_project"]], "get_resource() (pharaohproject method)": [[11, "pharaoh.project.PharaohProject.get_resource"]], "get_resource() (in module pharaoh.assetlib.api)": [[11, "pharaoh.assetlib.api.get_resource"]], "get_setting() (pharaohproject method)": [[11, "pharaoh.project.PharaohProject.get_setting"]], "get_settings() (pharaohproject method)": [[11, "pharaoh.project.PharaohProject.get_settings"]], "hasattr_() (in module pharaoh.templating.second_level.env_filters)": [[11, "pharaoh.templating.second_level.env_filters.hasattr_"]], "heading() (in module pharaoh.templating.second_level.env_globals)": [[11, "pharaoh.templating.second_level.env_globals.heading"]], "hrule() (in module pharaoh.templating.second_level.env_globals)": [[11, "pharaoh.templating.second_level.env_globals.hrule"]], "iter_assets() (assetfinder method)": [[11, "pharaoh.assetlib.finder.AssetFinder.iter_assets"]], "iter_components() (pharaohproject method)": [[11, "pharaoh.project.PharaohProject.iter_components"]], "iter_resources() (pharaohproject method)": [[11, "pharaoh.project.PharaohProject.iter_resources"]], "last_match() (fileresource method)": [[11, "pharaoh.assetlib.resource.FileResource.last_match"]], "load_settings() (pharaohproject method)": [[11, "pharaoh.project.PharaohProject.load_settings"]], "locate() (fileresource method)": [[11, "pharaoh.assetlib.resource.FileResource.locate"]], "locate() (localresource method)": [[11, "pharaoh.assetlib.resource.LocalResource.locate"]], "md2html() (in module pharaoh.templating.second_level.env_filters)": [[11, "pharaoh.templating.second_level.env_filters.md2html"]], "metadata_context() (in module pharaoh.assetlib.api)": [[11, "pharaoh.assetlib.api.metadata_context"]], "module": [[11, "module-pharaoh.api"], [11, "module-pharaoh.assetlib.api"]], "oc_get() (in module pharaoh.templating.second_level.env_filters)": [[11, "pharaoh.templating.second_level.env_filters.oc_get"]], "oc_resolve() (in module pharaoh.templating.second_level.env_filters)": [[11, "pharaoh.templating.second_level.env_filters.oc_resolve"]], "open_report() (pharaohproject method)": [[11, "pharaoh.project.PharaohProject.open_report"]], "or_default() (in module pharaoh.templating.second_level.env_filters)": [[11, "pharaoh.templating.second_level.env_filters.or_default"]], "pharaoh.api": [[11, "module-pharaoh.api"]], "pharaoh.assetlib.api": [[11, "module-pharaoh.assetlib.api"]], "project_root (pharaohproject property)": [[11, "pharaoh.project.PharaohProject.project_root"]], "put_setting() (pharaohproject method)": [[11, "pharaoh.project.PharaohProject.put_setting"]], "raise_helper() (in module pharaoh.templating.second_level.env_globals)": [[11, "pharaoh.templating.second_level.env_globals.raise_helper"]], "rand_id() (in module pharaoh.templating.second_level.env_globals)": [[11, "pharaoh.templating.second_level.env_globals.rand_id"]], "read_text() (in module pharaoh.templating.second_level.env_globals)": [[11, "pharaoh.templating.second_level.env_globals.read_text"]], "register_asset() (in module pharaoh.assetlib.api)": [[11, "pharaoh.assetlib.api.register_asset"]], "register_templating_context() (in module pharaoh.assetlib.api)": [[11, "pharaoh.assetlib.api.register_templating_context"]], "remove_component() (pharaohproject method)": [[11, "pharaoh.project.PharaohProject.remove_component"]], "rep() (in module pharaoh.templating.second_level.env_filters)": [[11, "pharaoh.templating.second_level.env_filters.rep"]], "required() (in module pharaoh.templating.second_level.env_filters)": [[11, "pharaoh.templating.second_level.env_filters.required"]], "save_settings() (pharaohproject method)": [[11, "pharaoh.project.PharaohProject.save_settings"]], "search_assets() (assetfinder method)": [[11, "pharaoh.assetlib.finder.AssetFinder.search_assets"]], "settings_file (pharaohproject property)": [[11, "pharaoh.project.PharaohProject.settings_file"]], "show_gui() (matlab method)": [[11, "pharaoh.assetlib.api.Matlab.show_gui"]], "sphinx_report_build (pharaohproject property)": [[11, "pharaoh.project.PharaohProject.sphinx_report_build"]], "sphinx_report_project (pharaohproject property)": [[11, "pharaoh.project.PharaohProject.sphinx_report_project"]], "sphinx_report_project_components (pharaohproject property)": [[11, "pharaoh.project.PharaohProject.sphinx_report_project_components"]], "to_path() (in module pharaoh.templating.second_level.env_filters)": [[11, "pharaoh.templating.second_level.env_filters.to_path"]], "transform() (transformedresource method)": [[11, "pharaoh.assetlib.resource.TransformedResource.transform"]], "update_resource() (pharaohproject method)": [[11, "pharaoh.project.PharaohProject.update_resource"]]}}) \ No newline at end of file diff --git a/template_designer_guide.html b/template_designer_guide.html new file mode 100644 index 0000000..676d7f0 --- /dev/null +++ b/template_designer_guide.html @@ -0,0 +1,379 @@ + + + + + + + Template Designer Guide — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Template Designer Guide

+_images/chirping_crickets.gif +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/user_guide.html b/user_guide.html new file mode 100644 index 0000000..2fc21aa --- /dev/null +++ b/user_guide.html @@ -0,0 +1,606 @@ + + + + + + + User Guide — Pharaoh 0.4.1.dev15+g326e192 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

User Guide

+
+

Preface

+

This user guide targets end-users.

+

If you’re about to create templates for end users, +please refer to our Template Designer Guide.

+

End-users usually will:

+
    +
  • Generate Pharaoh projects based on the templates the template designers provided

  • +
  • Update project settings

  • +
  • Manage project components (update, add, remove)

  • +
  • Modify/add asset scripts

  • +
  • Overwrite certain pre-defined blocks inside provided templates

  • +
+
+

Important

+

The following guide only shows the API provided by Pharaoh. Keep in mind that the template designers +of your project might provide an additional or different API to create Pharaoh projects.

+
+
+
+

Project Generation

+

The first thing to do is generating a Pharaoh project.

+
+

Note

+

This step may be skipped if you already created a project and manage it via GIT for example.

+
+
+ +
+

Create a Python script with following example content to create a new Pharaoh project:

+
from pharaoh.api import PharaohProject
+
+# Create a project with a default template optimized for HTML output inside current working directory
+proj = PharaohProject(".")
+
+# For overwriting an existing project
+proj = PharaohProject(project_root="path-to-existing-project", overwrite=True)
+
+# Passing a custom template to extend the default project template "pharaoh.default_project"
+proj = PharaohProject(
+    ".",
+    templates=("pharaoh.default_project", r"C:\...\my-extension-template"),
+    template_context=dict(some_variable_in_extension_template="abc")
+)
+
+# Passing a custom settings YAML file
+proj = PharaohProject(".", custom_settings=r"C:\...\my_custom_settings.yaml")
+
+
+
+ +
+

In a terminal with activated virtual environment that has pharaoh installed, type following commands:

+
# Create a project with a default template optimized for HTML output inside current working directory
+pharaoh new
+
+# For overwriting an existing project
+pharaoh -p "path/to/existing/project" new --force
+
+# Passing a custom template to extend the default project template "pharaoh.default_project"
+pharaoh new -t pharaoh.default_project -t "C:\...\my-extension-template"
+    -c "{'some_variable_in_extension_template': 'abc'}"
+
+# Passing a custom settings YAML file
+pharaoh -p . new --settings "C:\...\my_custom_settings.yaml"
+
+
+
+
+
+
+

Update Settings

+
+

See also

+

Settings Reference

+
+

Single settings may be updated during project generation (API & CLI) or afterwards (API or manually).

+
+ +
+
+ +
+

This is done via the settings API function +put_setting(key: str, value: Any) +or via environment variables. Refer to the Custom Settings section +for examples.

+
+ +
+

Besides passing an entire settings YAML file to the project generation, +settings can be set using environment variables during project generation. +Those will be automatically persisted in the settings file.

+
# CMD syntax:
+set PHARAOH.LOGGING.LEVEL=INFO
+set PHARAOH.FOO=bar
+set PHARAOH.bla="{'blubb': 123}"
+
+# PowerShell syntax:
+Set-Variable -Name PHARAOH.LOGGING.LEVEL -Value INFO
+Set-Variable -Name PHARAOH.FOO -Value bar
+Set-Variable -Name PHARAOH.bla -Value "{'blubb': 123}"
+
+# Bash syntax:
+env "PHARAOH.LOGGING.LEVEL=INFO" bash
+env "PHARAOH.FOO=bar" bash
+env "PHARAOH.bla={'blubb': 123}" bash
+
+pharaoh new
+
+
+
+
+
+ +
+
+ +
+

This is done via the settings API function +put_setting(key: str, value: Any) +or via environment variables. Refer to the Custom Settings section +for examples.

+
from pharaoh.api import PharaohProject
+
+proj = PharaohProject(project_root="some-path")
+proj.put_setting("report.title", "My own title")
+proj.put_setting("toolkits.bokeh.export_png", dict(width=720, height=480))
+
+
+
+ +
+

Updating settings via CLI may be done via the env command:

+
cd "path/to/project"
+pharaoh env report.title "My own title"
+
+# or
+pharaoh -p "path/to/project" env toolkits.bokeh.export_png "{'width':720, 'height':480}"
+
+
+
+ +
+

Just open the file pharaoh.yaml in your project root and modify its content according to +YAML Coding Guidelines +and the OmegaConf library specification.

+
+
+
+
+
+
+

Manage Components

+

The main content of a Pharaoh report is determined by components.

+ +

Components may be added or updated using following API/CLI:

+
+ +
+

This is done via the component API functions for managing components +documented here.

+

Please refer to the component API functions for managing components +here. Relevant sections are:

+ +
+ +
+

Please refer to the CLI reference here. Relevant commands are:

+ +

Here some examples:

+
pharaoh add -n dummy1 -t pharaoh_testing.simple -c "{'test_name':'dummy'}"
+pharaoh add -n dummy1 -t pharaoh_testing.simple -r "FileResource(alias='foo', pattern='.*')"
+
+pharaoh update-resource -n dummy1 -a foo -r "FileResource(alias='baz', pattern='.*')"
+
+pharaoh add-template -n dummy1 -t pharaoh_testing.simple -c "{'test_name':'dummy'}"
+
+pharaoh remove -f dummy.*
+
+
+
+
+
+
+

Manage Asset Scripts

+

Once components are added, users may add or modify existing asset scripts.

+ +
+
+

Modify Templates

+

If assets scripts are added or modified the templates might need to be updated to include potentially +modified or added assets.

+
+

See also

+

Pharaoh Directive on how to include generated assets into your templates +and the Build-time Templating section.

+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file