From 2526c2277c07b2954738d06a1db933789223e610 Mon Sep 17 00:00:00 2001 From: Brady Endres Date: Thu, 25 Jul 2024 17:33:31 -0700 Subject: [PATCH 1/3] add sphinx docs. format all methods to produce nice looking docs --- build/lib/syncsketch/__init__.py | 4 +- build/lib/syncsketch/syncsketch.py | 83 +- dist/syncsketch-1.0.10.5-py3-none-any.whl | Bin 0 -> 16206 bytes dist/syncsketch-1.0.10.5.tar.gz | Bin 0 -> 18386 bytes docs/Makefile | 20 + docs/_build/doctrees/environment.pickle | Bin 0 -> 60255 bytes docs/_build/doctrees/index.doctree | Bin 0 -> 330166 bytes docs/_build/html/.buildinfo | 4 + docs/_build/html/_sources/index.rst.txt | 46 + docs/_build/html/_static/alabaster.css | 708 ++++++++ docs/_build/html/_static/basic.css | 925 +++++++++++ docs/_build/html/_static/custom.css | 1 + docs/_build/html/_static/doctools.js | 156 ++ .../html/_static/documentation_options.js | 13 + docs/_build/html/_static/file.png | Bin 0 -> 286 bytes docs/_build/html/_static/language_data.js | 199 +++ docs/_build/html/_static/minus.png | Bin 0 -> 90 bytes docs/_build/html/_static/plus.png | Bin 0 -> 90 bytes docs/_build/html/_static/pygments.css | 84 + docs/_build/html/_static/searchtools.js | 620 +++++++ docs/_build/html/_static/sphinx_highlight.js | 154 ++ docs/_build/html/genindex.html | 307 ++++ docs/_build/html/index.html | 1438 +++++++++++++++++ docs/_build/html/objects.inv | Bin 0 -> 718 bytes docs/_build/html/py-modindex.html | 117 ++ docs/_build/html/search.html | 116 ++ docs/_build/html/searchindex.js | 1 + docs/conf.py | 31 + docs/index.rst | 46 + docs/make.bat | 35 + syncsketch.egg-info/PKG-INFO | 6 +- syncsketch/syncsketch.py | 565 ++++--- 32 files changed, 5416 insertions(+), 263 deletions(-) create mode 100644 dist/syncsketch-1.0.10.5-py3-none-any.whl create mode 100644 dist/syncsketch-1.0.10.5.tar.gz create mode 100644 docs/Makefile create mode 100644 docs/_build/doctrees/environment.pickle create mode 100644 docs/_build/doctrees/index.doctree create mode 100644 docs/_build/html/.buildinfo create mode 100644 docs/_build/html/_sources/index.rst.txt create mode 100644 docs/_build/html/_static/alabaster.css create mode 100644 docs/_build/html/_static/basic.css create mode 100644 docs/_build/html/_static/custom.css create mode 100644 docs/_build/html/_static/doctools.js create mode 100644 docs/_build/html/_static/documentation_options.js create mode 100644 docs/_build/html/_static/file.png create mode 100644 docs/_build/html/_static/language_data.js create mode 100644 docs/_build/html/_static/minus.png create mode 100644 docs/_build/html/_static/plus.png create mode 100644 docs/_build/html/_static/pygments.css create mode 100644 docs/_build/html/_static/searchtools.js create mode 100644 docs/_build/html/_static/sphinx_highlight.js create mode 100644 docs/_build/html/genindex.html create mode 100644 docs/_build/html/index.html create mode 100644 docs/_build/html/objects.inv create mode 100644 docs/_build/html/py-modindex.html create mode 100644 docs/_build/html/search.html create mode 100644 docs/_build/html/searchindex.js create mode 100644 docs/conf.py create mode 100644 docs/index.rst create mode 100644 docs/make.bat diff --git a/build/lib/syncsketch/__init__.py b/build/lib/syncsketch/__init__.py index abd58fe..2313cc7 100644 --- a/build/lib/syncsketch/__init__.py +++ b/build/lib/syncsketch/__init__.py @@ -2,12 +2,12 @@ # @Author: yafes # @Date: 2018-11-20 17:39:54 # @Last Modified by: Brady Endres -# @Last Modified time: 2023-04-04 +# @Last Modified time: 2024-07-25 from __future__ import absolute_import from .syncsketch import SyncSketchAPI -__version__ = "1.0.10.4" +__version__ = "1.0.10.5" __author__ = "SyncSketch Dev Team" __credits__ = "Philip Floetotto, Yafes Sahin, Brady Endres, Eric Palakovich Carr" diff --git a/build/lib/syncsketch/syncsketch.py b/build/lib/syncsketch/syncsketch.py index daad3a1..fc2f4d1 100644 --- a/build/lib/syncsketch/syncsketch.py +++ b/build/lib/syncsketch/syncsketch.py @@ -133,7 +133,7 @@ def _get_json_response( method = method or "get" if postData or method == "post": method = "post" - r = requests.post(url, params=params, data=json.dumps(postData), headers=headers) + r = requests.post(url, params=params, data=json.dumps(postData) if postData else None, headers=headers) elif patchData or method == "patch": method = "patch" r = requests.patch(url, params=params, json=patchData, headers=headers) @@ -1146,7 +1146,7 @@ def remove_users_from_project(self, project_id, users): Shotgrid API """ - def shotgun_get_projects(self, syncsketch_project_id): + def shotgrid_get_projects(self, syncsketch_project_id): """ Returns list of Shotgrid projects connected to your account @@ -1157,7 +1157,7 @@ def shotgun_get_projects(self, syncsketch_project_id): raise DeprecationWarning("DEPRECATED! Please use Shotgrid's API.") - def shotgun_create_config(self, syncsketch_account_id, syncsketch_project_id=None, data=None): + def shotgrid_create_config(self, syncsketch_account_id, syncsketch_project_id=None, data=None): """ Create a new Shotgrid configuration for a SyncSketch workspace and optionally a project :param int syncsketch_account_id: @@ -1184,16 +1184,16 @@ def shotgun_create_config(self, syncsketch_account_id, syncsketch_project_id=Non else: raise Exception("Shotgrid configuration test failed. Please check your Shotgrid config settings.") - def shotgun_get_playlists(self, syncsketch_account_id, syncsketch_project_id, shotgun_project_id=None): + def shotgrid_get_playlists(self, syncsketch_account_id, syncsketch_project_id, shotgun_project_id=None): """ Returns list of Shotgrid playlists modified in the last 120 days + If the syncsketch project is directly linked to a shotgrid by the workspace admin, the + param shotgun_project_id will be ignored and can be omitted during the function call :param int syncsketch_account_id: SyncSketch account id :param int syncsketch_project_id: SyncSketch project id :param int shotgun_project_id: (optional) Shotgrid project id - - If the syncsketch project is directly linked to a shotgun by the workspace admin, the - param shotgun_project_id will be ignored and can be omitted during the function call + :return: list of Shotgrid playlists """ url = "/api/v2/shotgun/playlists/{}/".format(syncsketch_account_id) @@ -1203,26 +1203,45 @@ def shotgun_get_playlists(self, syncsketch_account_id, syncsketch_project_id, sh data = {"shotgun_project_id": shotgun_project_id} return self._get_json_response(url, method="get", getData=data) - def shotgun_sync_review_notes(self, review_id): + def shotgrid_sync_review_notes(self, review_id): """ - Sync notes from SyncSketch review to the original shotgun playlist + Sync notes from SyncSketch review to the original shotgrid playlist Returns task id to use in get_shotgun_sync_review_notes_progress to get progress - :param review_id: + :param int review_id: SyncSketch review id :returns message= "Shotgrid review notes sync started" status= processing/done/failed progress_url= Full url to call for progress/results task_id= task_ids *pass this value to the get_shotgun_sync_review_items_progress function percent_complete= 0-100 value of percent complete - total_items= number of items being synced from shotgun - remaining_items= number of items not yet pulled from shotgun + total_items= number of items being synced from shotgrid + remaining_items= number of items not yet pulled from shotgrid """ url = "/api/v2/shotgun/sync-review-notes/review/{}/".format(review_id) return self._get_json_response(url, method="post") - def get_shotgun_sync_review_notes_progress(self, task_id): + def shotgrid_sync_new_item_notes(self, project_id, review_id, item_id): + """ + Sync new notes from SyncSketch review item to the original shotgrid playlist + Returns dict with information about the REST API call + + :param int project_id: SyncSketch project id + :param int review_id: SyncSketch review id + :param int item_id: SyncSketch item id + :returns + sketch_upload_error= "True in case of error" + sketches= "Number of sketches synced" + comments= "Number of comments synced" + attachments= "Number of attachments synced" + item_name= "Name of item that was synced" + """ + url = "/api/v2/shotgun/sync-notes/project/{}/review/{}/{}/".format(project_id, review_id, item_id) + + return self._get_json_response(url, method="post") + + def get_shotgrid_sync_review_notes_progress(self, task_id): """ Returns status of review notes sync for the task id provided in shotgun_sync_review_notes @@ -1233,33 +1252,33 @@ def get_shotgun_sync_review_notes_progress(self, task_id): progress_url= Full url to call for progress/results task_id= task_ids *pass this value to the get_shotgun_sync_review_items_progress function percent_complete= 0-100 value of percent complete - total_items= number of items being synced from shotgun - remaining_items= number of items not yet pulled from shotgun + total_items= number of items being synced from shotgrid + remaining_items= number of items not yet pulled from shotgrid """ url = "/api/v2/shotgun/sync-review-notes/{}/".format(task_id) return self._get_json_response(url, method="get") - def shotgun_sync_review_items(self, syncsketch_project_id, playlist_code, playlist_id, review_id=None): + def shotgrid_sync_review_items(self, syncsketch_project_id, playlist_code, playlist_id, review_id=None): """ - Create or update SyncSketch review with shotgun playlist items + Create or update SyncSketch review with shotgrid playlist items Returns task id to use in get_shotgun_sync_review_items_progress to get progress - :param syncsketch_project_id - :param playlist_code - :param playlist_id - :param review_id (optional) + :param int syncsketch_project_id: + :param str playlist_code: + :param int playlist_id: + :param int review_id: (optional) :returns message= "Shotgrid review item sync started", status= processing/done/failed, progress_url= Full url to call for progress/results, task_id= task_ids *pass this value to the get_shotgun_sync_review_items_progress function, percent_complete= 0-100 value of percent complete, - total_items= number of items being synced from shotgun, - remaining_items= number of items not yet pulled from shotgun, + total_items= number of items being synced from shotgrid, + remaining_items= number of items not yet pulled from shotgrid, data= review_id= review.id, - review_link= url link to the syncsketch player with the review pulled from shotgun, + review_link= url link to the syncsketch player with the review pulled from shotgrid, ) """ url = "/api/v2/shotgun/sync-items/project/{}/".format(syncsketch_project_id) @@ -1294,7 +1313,7 @@ def shotgun_sync_review_items(self, syncsketch_project_id, playlist_code, playli print(item_data) return result - def get_shotgun_sync_review_items_progress(self, task_id): + def get_shotgrid_sync_review_items_progress(self, task_id): """ Returns status of review items sync for the task id provided in shotgun_sync_review_items @@ -1305,11 +1324,11 @@ def get_shotgun_sync_review_items_progress(self, task_id): progress_url= Full url to call for progress/results, task_id= task_ids *pass this value to the get_shotgun_sync_review_items_progress function, percent_complete= 0-100 value of percent complete, - total_items= number of items being synced from shotgun, - remaining_items= number of items not yet pulled from shotgun, + total_items= number of items being synced from shotgrid, + remaining_items= number of items not yet pulled from shotgrid, ) """ - print("Deprecated. Response is printed in the shotgun_sync_review_items() function") + print("Deprecated. Response is printed in the shotgrid_sync_review_items() function") # alias methods to _v1 if they have a v2 add_media_v1 = add_media @@ -1344,3 +1363,11 @@ def get_shotgun_sync_review_items_progress(self, task_id): getGreasePencilOverlays = get_grease_pencil_overlays getTree = get_tree getAnnotations = get_annotations + get_shotgun_sync_review_items_progress = get_shotgrid_sync_review_items_progress + shotgun_sync_review_items = shotgrid_sync_review_items + get_shotgun_sync_review_notes_progress = get_shotgrid_sync_review_notes_progress + shotgun_sync_new_item_notes = shotgrid_sync_new_item_notes + shotgun_sync_review_notes = shotgrid_sync_review_notes + shotgun_get_playlists = shotgrid_get_playlists + shotgun_create_config = shotgrid_create_config + shotgun_get_projects = shotgrid_get_projects diff --git a/dist/syncsketch-1.0.10.5-py3-none-any.whl b/dist/syncsketch-1.0.10.5-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..3368f78ab04ac352e0b134fc1d82aa32bfa1377d GIT binary patch literal 16206 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 On`ucF`vdYg0Dl9Wv9`+q literal 0 HcmV?d00001 diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_build/doctrees/environment.pickle b/docs/_build/doctrees/environment.pickle new file mode 100644 index 0000000000000000000000000000000000000000..37ee72f173e57705690269897fcf9ca1e05d6d84 GIT binary patch literal 60255 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

+u{hCz=XJdO3?raYE z;r)D>IAv2pZPR2b*Tu$ zpZPddH+Hq+YsPip=f&j!H~T|^{8;xOKimz-=^6zwOt@fDlA@0xq^1k@HYoIU!A@{Y zBTP77aJt3&GUf?6UdGL*nj9)VmjE10sJC-!gVFn#$gX3zx)hYVp$ zM7d<+a)3E~w%WFcW@Tam36^q5XZ)z~X+9(o2FV(1vf42qTKbm5E@xrFDzE&$YN_5TT1D!*NsMB`U+ymq;S2SOmL-FRjoBI*Xt?J zv1+X0EVsH|F=aH@%Ong;$HbPBrOr_nPRm(qHb_^FA_RVZ26@mO;1A86Lsi`m&7FZu zLr}m_{W{EW!yf^iSfLMa#Ft+3vI(biRq4Rksu-p769MKTc`pGBgw-FM!(z3sOp%nE zA++9M4woePYmMIAkPRIMW3P%{O^~HmRfRIDK;@h+MD#kSn@cET+Gs*)lEx;KmO96# zv00MIY?UpU&&{AwLNc9Qiic!2<>-}f@uBL@=@FewRr?ZicnrEyr88YfkN`Xv@mtu) zwaO^Qz)EITltd6#HRi0}Bolw(0~~w&}TkJ8nvI|^}o+_ux4`6z@1KT9Oy$k-VeVsE6* zR4AeoBkvuGxEGqbl!pdh|$65pP=8wez0ZUP*h?~)?}4unFJD`h--*X=5BBz0>SqL z66KPm|I9lU`r2t+z@jubdy^H&@M%egOGSXDbIj%YixZP@eCsxn*V}^0Vx!1Cc97yl z;X;Hv1)yMkBZbzqE@gaxaE$40RFeO zQd+w?*f4fqQ8$etR+1w-r(1-f6M6>J!|lRfJPG@p^a8F>J~ zIfHZNG{@3u+?fxaF>vs{&a!f4cex7xv_^}K-OcGcr%`cq7FCP2-EGK{nC>i?oPsB@ zYj}Pz`W@0RCgpsclv3VST??;k*C9OAZcV~l+98v^%|bZRK1=Sd1o+ceP1CA^IVZTg zR0ApcAAmQO(I=i7)aU>lhOL_I@$s!Fb)XozzNOcL%RsB^s1`M=M}Wv10AdJ zAv6T6LCcsjT71YP44Pc#1&WqB?x3aFAZzJZ6T0V)Yx`UR-vUR0^*SAvC za*Oj6h30PKm{8Uzt)k4-pV5k^LbR z!QUIbxgq0n_)dZ>y{amQ(OpnEZ=CWyP&bzx#XG zOJ((Z;6}R2bjKQ>7q{(9&U~!V#LN$Ne)-T}r} zK~tBGHv)_g5p>L?}L_M-*aseK+gH981I#b{%NM~GMq<5v{ z4hh)72fZ87$c)e(xix;~m`!9M_sI{Em!IZc3ytP97AQtHJ3G;=F9o6h;#E;&pmo zPPr1!^a5z)(hjQi;^j;)fsR!t24}0)nT{!=IWZ<-+=c=knD{Hs*)<@=E2I4}Aobq*XF%%xIt@s@ zs#r*u8O2g~{}{NTs!o08yA8g|jY)SUIU_#DhZLGlpAnXgs96$zpw4Gv>ddfV`7Ixs2*dIqHp;o^_R+BXr5CK+ zu>2`BvKW?FtZK(z49g?V)cbPEl`t#^ppi>!bZuBJhmKXl5@)g1u#73A8I~qt+y(?1 zmOmy6yOHLGB??YXo97{_+84v}5- zVTq7?5!uONVOR={B$TJK5gC?3!`oXFqO}00%do`n%&?roUu0O)uUR!LH7x+ot^Exb2}hhH2n68!^c@#t3_x zl&^=TuGwywl&{TEJz-KxenhW9NKKRSJ}C4xDUHL71``fOpMpLU7NsF=w!@5CM~Xcu z0EboR1&BkYJ-KQd&EEJhqjC9p+GjZfKF@~{N==^uHXHH~9BtIdLzn&oLH;Es$PAm1 zkNc2Bn2?WPqnyj@8O+>=1Jm-%;tlLhY`8_WRSy{cq`i!c3&U?r+dlY>7Yf|6{3SH9 zSe96<>d0O!%N0-1`*KQ~uq>BBBbV~%+Ok{;9jlfl&StA+8B<2HEKS1L@-8fOR@zuC zC#2cJZI__E`ZMC2o0QyMMJdf`N_~^6_QhVk+KcPlUcCw%<(p6}QLudMozsQ#YDi)| z0sc7&{y7EyS#vABErE1;T811cCMOQ=MJFehgvlv1n9#M(Mr3k|MZn&o5S;;Vx=c>| z&P>h&_=`+V`ZcR2=Lkz*rR>#wJK?9L?u08{^I) zBc!Hr{30myHI7%OP8z)D<3FLtglVkJ3^I%j3tN#q#Vi&egA#uULZ4|CFT07~@lm4$ zdNDE0VkvPy=G_WKtIy5kYBD$*(-6@AL5%(k8^H^PT*}HIh%kZ=$40I-+dozQRIW&* zrEIzL0pnAKp&V-!9=nSFmONkMg(5eSPl84kBN>ZT-QJ6le6G=#Qx=7hd=|l#UbSl@ z`F!YDHIi{&TaDzHGMbTW62@jETk5!z4H?gvKR}SBS5<{Fx*96y z&6s~K)XgQ7F>N%VG)ZFFiQu&>x+nSMC#{($4AY%%Q4% z@d-Z(U8&NUE+j|*?h}3t8@bjOC4+tiGbKvwG^Bd3hvkc06u#mE7pa#2AfmY8=cGpX z`%G$&ocf>ppe9KE6Ks@|vZ1pWzYvdJWEXqhd-%6&8sOgy$j)q^i2v zIepymgq?BQPBioJ1c{9$m~eU=4R_JV7GyAj;kvhOd<6BQlt?1hVnTR_8X{=wQW!?4 zVHZ_5LJcSsmKcfiDf{Pj5Aw5n2l;T2U<<@?F%X)5OA^1jdw5^bJG{${$!3AwS_SY( zluMlczI&Yhwi}$%b-Xp)Bd2#1BBW-xX9pDehI>}oRUF28{sX#8#CoL4qCn4a_U1~I zM?eZ;j_VQV%qY)8B~qL9tc}#_P3O2v;svTDYku?t2{$ zZY)=tL9<+MS4;R_VY?ZEl~{x43!7muRc{CTig29*wkR>{vB@rN4EMtg1E8~~f^xmt z3QGfQRCsQBve?8YsIe1_PKb)5?M9UVi3?g~xZ|N#tW@a}41v|9fNsP4BI)`-cq@?v z6{PV0b1dbY`UYpZuu_~RHJD<9hkWS*$$T)DO#S|jw;NpC60mDoV%CE!Y!1q;*5u}q zk#jZ;Z#sMUg7b&Z**LOqay`gtE3CC(TLcfC87z~C2FM7F1CiQNT?<14fT|H0Zx8hqKR%h>&!|cSLQiSIW=k;iq zH!&FTp_XVv>7w<46w%sODYeR*H*VN;HVH^M#EWt^pSNiP{aI|Z-~xor!P+M_ilwRG z@>&V9nAg&$QoB(^^Kqc7lk}OiQ%M#CWw=|3YBeiQ4>t$+ zZ}CtO72{)la2d@6K4A2WU~i=q)(2ui``?a6EOyI)$ulW-M`*I3h}}gyuDkK_&9p=n ztNV&m&7c;-(gn*1EqLrr(54gzyohXD(-~^vp5Dfgzc(?)lw5tYKE<$0II=~J+QaZgcTlDYZG+qzJ2?K z6;BTz87@v%M)qzT!JUMW=6E@rD6*G^3ow7T^fah4^@k05z@22^a9D058}0&90Mxgc z#>n5 ztmVH`CH+{rS9L>pd%MzrIU4)8G!2ctD#bD7yH!ewyVbux(u zb&j%dT5`^8knYVfvtU)UJA*t(NxolIqN?uws*^F!AfMXafq!K@kKhbJyw{*oIv){G zE{g9aKog>f#p+H#wkW2*v;D6|Uv9X#DE>3Sm0nd9#pvZwIqxpj=b>&cQH&|0iK0mu znbPbBT6L9Zj@N;@CvnQfG9g z#YK|a96fuc%*mj&=S(?EdVRD>V7w!Oy9Y2u=rs{EY{B~9Rks-zi6AC7F%snx_nls% z#s~RkM2dRF@_lP;^>Io(u@%!7^^+etQ63>g@?zznK4n*i%#OIxj zDDOaQ50e;c(1^P9sc?auZWgexypL~ZG)=JBDIJpT%9rciT z&`jx4O+DCEsfLrqRv9u6C@MUP?%Qi{jz!uOz+ke)#C0gR}`yF$XGz@ z0xsBw4T|k0R+h|HSR9i?*sn^7RB{&q7gb|G7kYQb!ufIA&YnY)vA{WFNm=-VIGn>M z*hkuO6HYr!p+tkwcvX=ps7cm!_`z>&vRHLB#_Y45zxPZx|Rh}a`to& z{_ftvhf4-=Get}k&Liz!$0bS?F$c6ViOb#HK+nRdRX6_a-9{iG{yx2X;)1ix^ znZ`c>g%G;sqdZd`<*>m)F%15c6wKdV0O9>v8fK1x1TeZf6W z7p!8#_r$7|mPNtB$RbCk?bre^i3o?HpODaOtMj+QX-Q~u_f=BE`UW{fdw*;Y&*W@TZI&_ zcB?)C85QHjDkO)oq_lxzEg;Vy6VZyTR&l({^UyHmi`kFE0dD9A>x0TJwyEC;DYXn! z$siA{5!Cn9SZZ6m*A2O96z~R|srMLC97bdT;0STLkUIxW%*6kz=u4ebcFs@3Qljj$ zL+*xvFozt<8GqmXP9JiJpv}{Tfmr z(tTw^9!048O$1kZRW+XFwX6O&(6JioMm=raRgWp7g}P0`*h1ZwI__O{vqAdOCPHX0 z&L9t(?|fJNkEyDASN#&<)ykc9j0DhbCF)>WG3qtQ=xy!^Afq=JX-!OyOw{*Q!r^`4 z7=PnXe^N<0zkHuY7D!$7C;+F*SvtcJBQCSwB#eOadL{@Kt9@n5Z2I>5uZ+IjaPjS# zUl3gBRaKddJ_42VZomHq>gJN!m@=BonuM{*tfkI+KM{~jHT%jI=dWi_&QqLkR6-b1 zqH)WWvPXEn2&*LXKM0aH|$)3kqZPdX?URMERza{@7P#t}z{a zY6itB9L9o!29Skb48|JueN9N{!dz}}j45}!-T)ujHWiE*9-j_Isie@SyhM*riH{MH z5>o<;y{5!GB!_UC*&hCzDAIxSM7RI9E6T@O^TQ{+N3Z^W1AF~I+yoH zOe9gwF|sGbM=~htIU)W`jaayURd1qwq`Ia6ZxQPsX=m({(bmuHt% zv5_l^=AT`5zy}V>Ho7wh!#HUX-aeDoBfIQXKB$R6tjLjFhOs1; zUG^wo#Inoi*Q{ojogHsyjgC#>H39jO%+_TmneqO5$TfRf%FdmDR7s1X2oQpw%sJB> zjI@lhBTX_0qdWOQu1miIKB^XoF81#HvR}n*JB>q_UludPl4f>6*YxCeVc)aJjyc;! z;K;QxzXF~)%7g0#j#KketH_2@excbr9_Iz`tC7U*EmJ3$IX;TU zxs2?jm$^slf`1GMPyV^9ClXww+K-kZ0%n5C&tO^bO>i-vpBCxjv93QDy&J|#5>YJb&OZ*?a~l#iVkeOdFN2Dtx=yGtM4BQc9!d{-Hi%yvb;`Ob(Eg_j6!DQZ8NhtZ95iV}57r&0%_O6WnQ) zo!cZB*N2|u>isXigpEFjE!b5)644pHk>XoEPzsmyAU1Njq`!x`Gr#jflt&ML1&u7x zLo8Np_v}1`N{wwNfT?E$4OgC{_oe#*#{3hzQ%6E0mw~E!>t4At$3Vww&=7UMHFqYa zj21LB31bTyTI#rSXUqoatELDL{DRo#rXgE#Jm@=BknuM{5tfkJn`~o1C1hb!PN#2%0H6<3EN0NUZ z(`Nn@P*f!|T|^LBF6A#Jx+J6=i`Ble6?3|jzs2aw4HuX4HxgXwRlAn*w?W6bq&%jK zCgmn!Y*KEi)5o+Kv-fP-e^CZ4J!SuyS}d>M$pUnDYN;N=J$H4u+lAkC2RF9DeT8^R zH7wZ{0wZxCE1lq5ULwh-$~TCNiK&9cUQ@-HPVh^kxAYl+T;r4FX9QV#)vhPYub|^x zlO?8&Hd##4*d~jm&hEaX6XXyixuBR(WKXIGGiaWeRL(BN=H`!c7+ib@4to)jE!7wj^czW z7i9>Ojt;%hWf~HW9X@c-X4low!e#0syqvTMZ=Xr)vAMa&2Q@+BGB(OdV&Bcp1Gxd} zR?%2CH&Y#^XApCoa!350%C0B)jl}cfw|g%0%}o({uy0iw4k>MJiuj$qMS)rKj?GQ9 z_Sxp6-D>4ptnweXKXsKgeuZiCSF^NH!*C|l10YS>Wx7+;2*ThCA31m5P($^_i( z-=Mu^u}Kc0|5mvU`@duD77zEc^mw)T;K@rYP zw&9uxbZ>*=SRL@Et#W$gh`mH4w!@!#cK@mO@PP;Z57194`j0vlh3S*wCjD$!&@i7q z!~vc0J0HI6LlF^e_#!rPMH~G0tC@UqB_doH%lm~r( z2#qWo*I2B&XW9EB=^NLJ&e!|WT|Gl4MaXwPG;+BfY9z*M<9Yx(RztqHVpun>W6EeD zUz0GlkguiAN*k*s>&zDE3oS(PDEjj~;+vb4eCPVxRMow6eW{29$c<~SBmY*pmF^sm zbjkvW`lVjd!KL;hk`AD~zITnqeo~vht9O&pmz&glqv8gFE4`{Jwb4mXIq%-}%}_U& z)W($2q}C*iO=>N5hCO9Ka@FiCTc9tMbf*5mozn6%UkasLr`^whR>GJ+sqc1mHT;5+zaHUu6THap= z9p{qwm@=BYn}o5+yQR*P`Y0iCE|>#l&xPk^kkxZ8{E-&E?e~VZbuxJFSAW?{`1qvw z0#PY3DX`dUQaJakzi;%GF6+n?J}tgOkfm4cdRqJtI?gpMV%lia!X%AtT3G5_fgW4W zwahxU19aP?z5dea5EFsI8D5`b?< zk6)q$B)jgj!zh(hVOD8i4Dw0%We4bVSku_WGq#RTx)lm7uUb***APU&}YR8MT&Nd83MMo7(#o6kd`@5YUB zLAJt_gV9l&ExR^~)X|oWaY1(6fwEsCfQ9(-7ZC0<%7;7mYdV+Sh!;D;|BJ!yuogCo zRoFwp=@B3P8xEdUZ_@?||G;)^(6(s?V^d;^^xlyajuU_y>f+{9?XUz49{NcH1U`;K z2?UlW?7li!DGd$`Vvq&?1A9iTb`yRS;o|YVSVGr`*BgVIgRR$GGkWv(8?PF@;qq&? z-njj`9dKUqqV?->a|mt&hnIQq!dZCyy%y{Vr-IwtpsT{>z#t2vH@okEU1VMwXEQX_3(k0qapji0Q^8)`A>!~|AXPo z|KNDTUn!VBeBh-SsKZbk`5t@W6o6hh4WMVD0+b8|I1Exk{IRxDY4<-xd4i zOkd%R)?tfvS0A1xQ?I|VPW*>k`1qt#MWe|dOb1-pCGx7+G^fC)*$>aEHpd&4F}TbX z?(sukc3T7};(O~f)l1!Am!+AcMKOjm94)%GVs z8?<511$;n85YG%77ImT!t0ie-b=h|IKEXbGiWj!rk&#oN(Sx;Yno*>9EnRyJvtwcs z-bsMx3xa+1b~S#KKxqFf)A|CRSLqF1Ul31t#vrXfNvWH1QwnIKBD^dO;gh-g7-cuY zZV7g5z4r3p#_NKemv6n{va19xS=A(nK0&Sf7E8u|D8aG{C$Bfyb4yFW|!HC=$P`{v^V;cZ={*X$WtKdBRbMnRD9pH(d9m z%P+ezg-iYoB^uA{7LB9R(AWYxuQ`cFnc91+@ z;|9U89($1pzOY*aSEV6%eIsnPtF30RC1|26b!}Lx6bp9W8dKBL3sX)Sq_LT zSSuUBu;5k@{BbAqxcwY%vkB5al)Uoy;_xEH;W0?6{bblVke!e9PA?mL&hY7czW{>vPb^ zlAVLas)v}l?<-G4S;C@^8@;6)WoU-+Ev`ohvh=EI1l21)=iAV+8b8I@fHglSrj546 zWs(N)oex^^rGa89Tk0I4GhDLGtm}-3QatuE8I)1tu_&tZ~Z38y~zhQBKHWiaK*zEaw86XCb!4l&vibi z39?^aZbm&Fb5TM=)!({T?jMPU_n4Sv>z7-JLg~0D0*}(;CGgJC#eR>7{MmPJZ2LVT7;A4) zV6x=c??G8(`#tXfjM#n;{hHPNp3NA7CLa{*P!=Ky{T9pP8oalpTm-bm|^-nt|qI<9C<8j;0{+U1QnTDz5 z*r$?%O-CF1esJ@fG3MTzL=Qnz*P>`_5`85{^~5HTWK8sBgw)(5`Y;swZW5iAM^y2g z&)kbFJ4R`Ocn`j>dPbhEmAxc^6F58{Mf5WFl2%_Cw&1~-Di2Fhu!Bu1Vf#30$hk5A zn5j#hQ_*AKZ?pVM3jVjdGe$C&pS(J){9o8}19XZzDDuZlIwA zKpQG0lBs8V0nVMj4baHq{9&;wAeqh|-6P2TQ{zs-?M7ctVH2+36$Dp$Rn?gCa{aD` zj#X0%C%M)2iz%bIekNgn841_VQfKiEpK2usv>Jqbr!epL8Dc=kaUk(!tKj} zndBPx}P=CjMKJ=&~41Z&&RSXzJmrU6`YK z!c~)8h&CgnrmJ=;6#BYqMmnBG`h(Fp^q9DZL6a|X%1%g^+;!8S(aQHiCWinSJdM*4 z`bOjIA&lIEbH51(_o9=NOTvs4dQ0e6XCpc-B9y+pMIrhS zz}az_k@%gNk;h*w%t-n*t7hamW9{mmQDxa|<})VGOf@DG9rj>SuG1IhMAyQwY$h$s z6^Y8izO;OiQ~oUAP5S(BTbUzq+fH-ltxOAk7F%->w>4Szh;D2e-!yAe{|3rb7D!?} zDMr}a-n;^udf1y!%uzjIZ%TedTM<&z-aHcueeKOtG9(gr1tRD-VRc%hj%?30867Hn z0RjoopF@aGrUiN;w?HQ#X|RH+!zc`RUPQG--IXM>n|w&2|D4L3b_8Z^5&(fFV*<^v zU3#w%S%h8sTx^tYLG5f3GdL(vW(C}EbnChR&Zvm=7W@uoD%|AevQy>!<;I{akUV~s z7c$&ZeE=F+ELAL4HEE`$%F;79bY^^|WMHxTS4Tik`wr-YQ|EPeA3IvD)bCP`7)+f{n>? z0p_Yf&YyOJAtNlTdzzDhrK(KZoD55y;~i5~PCm1t+ayIR`egu)iw%mmff^^L75!|g z+81x*)Jt@FrB6l0+X%2xKINFx+t>&_=A;u2?nNgjmxQ+=RwJ=?IvbI43YvZo&Wi%q)mCHpMY!hJHAir++! zmxm@DjRDz8s2{aR&hOh%uEAy#EY&B2+qIa8+jgom?^>uBS$vA)(uhO&7j!4%2(2>J z(63*&Q~O;WbH7i=K&)nqE^G3Vpq%v?8=Qs74Yc~W zq?J5F|Ci);(0$3xGMzSs$H9tlwvpx3j#nIt7Ctq+%AF`QHJo6pSzX z3P$uLY{8DFOLj&7bB1!b`n#tFm-8dNG4BRWB!Hq3G(8l+vq{!4HPw~ z562?YI3fu@fK8gCt@|FlMnjonRnftqywib0H3Pfo!t z>@|$ZN52C>f;~NHSKB=I6YA!Y?lFQui||GN4F0|d zeyLX!z7*=J1WSionZSI2tf->%IPdGa8Td7XSvrhe)Aseyao4o{xk6htu=w6?P#hx^ zL9jK|grzE*Y^upp=Ry%!>}E#G8EW>Nt+Pe}UY9GK&{@td#XY!7bM(ry0?+`+8K?Ut zRqabW^Jf5{%BFNlMFQ|M%m0OqT(*k5xh!Wq^YE?N)aP`5DsAZh@my8Q)pFp$6e@p89K}-SfsOD3ksY?|bQOz5ux*pY>hY~?Z z=>bhy4p6f{B*=~KL9X=MT`W$a$^8UCdx~u~=cB#Ywj)q2-_!btcmPTd_z!BeCZ}jDqNfF;Vli*6Ps(Ptj z@vRZ)SoKnI&RgSKF=aF-(j<({iL}%?-ob6jL9=1H!UPUdTx&%JnG_#!8C7-1wT?$4 zMZThSnGi`~x)dT!D4n)T2;-?Dl+H^<9E$`b5LFLJVX@j*wm7E0e)V*tFE?B~AhnO+ zO0TMlWAsF*oHro#45*t+9AnC8;%E}aCXSXmOEu0)0W_;-3*aAR5XMsg&ttm)xRX|Z zBut8^-oOs+<0YebMI`7;e7PoDWh(Y4SDH$R#Q7b7fht?+OjhLdt%P<$PO(_+D_c&} z-_rcJ(U%)8E~g(QxYDb3EvKJ^j&sRrOc_m1O~TmZ)KcfPT)daOG`q-_-#29t%u{}M zkW(RL_X_m-gIarH3|?WzJ4#_oi#@MpUHPHmuAn$MS*?s0$EqO@7U)Vh!*BVmm)P)~ zfd3{cB*Ynu`z6jtU8eVyE~7|tF3!uLk<0JyTAYuDj&q4~Oc_m_O~Tm3*;40{Ud2I5 zy4icSr2pJk(w$w3aPpEIz49?LVvn5@u!v$r$r;qcagw zNR&$j^Lm^DU1rk0Hg4OwedaT16S!I8&1c5(=1l5w@4JtI=l8|r@D4n`2b#JTKqJof zE~>5vo{uy!<_K!R&;eOV$c9=Z#o`OyEi5{p=7~Uz|0Qwy zk$bc*!lMj`bQPO}ZOf4(kcU~}v)slt#h-C`IE%NrrgC%v#HovBcxVU;Ax<|Rn!;Uw zd3biLdlVPX_$WR+JKa567d&G?c=F6$JrUBDiZOa83Y{6!ekBz8hO|#|E*qHqeFe}e z5!OzqehO_HM{f<)s)V-%_#h*%=F`U+-hS%9+&OcugljX|MYxdAjPE({V0V+=%Hc_} z!vR^z&7fR_tk)v#q2h7}=V^m|(gmJ~a8L#w6#4z4jnvH7IcK?{ zvuHbGmh26f2G?*{XZ&i^k`GbDmPQd9xjY*GEsgUMTN*G!d5l9o2S86cVcbqkN+$b8 zBst#BNZ;OgrWe{guyF@8vTSc)v1;7*5~rIs`pOm9crn41UR4cjcx`XI3_4Z=8>suO z+Z!=uw7`Z*7+YY&Qs<1EoR>4(>?D051<#naI`(IfOo`OBsj7RcPgDa0I7O-xrkX``D2h{*n_n$+RtzcvaY5685HABnj|YfKe5}bXh>exHx~35KF}A zuvqP@7jgck(U%)89;f>{!IfTB73b)UP&x1R$hV+wE^&@2qlvRg7@Ig->I{3zf#j;$ zS+-bzEQ55OV*TXUZID6e8!o@>x@)h!e8;xSw*~7#v|DhX3{5K;1Y!0UKI}FQhSfNU zKO7E2i^r<2b2=W!U9QVzx_~G$el#?)sBA3mmyEAB`f|g?W&BKnE4^yhGCl$w=aTW5 zGMbE=gt5uErOw4YN`;hpv-513Uy(t|gv>j;6rt!wj+w+mtY{7846*(ZRqabCy5S{u zJQQ8WMlQomhN3&p!vc5vj7}@=fgEh&1Vt#l2Csc=D`vlO(e@obZ+CA()>K*pu(k2dP)%y903>=lLEaryolf-f9 z6}l0oTSLxB@*-%OGm?C~dlXkBnUCTVNiMiY>mriOfJk5AjOf2j6jyK?5NMlv8g8R@Q`*bJ29j$G@{0>m z;=O|qtOQ7?DmG~^03Rc7R3U_quOK(i3g9jiW+DV(ZM$ZjEem^yx0Ox-?5o@r7kt;>{G&jbD)t$00BG{rXAD`cDB^+oEgq<= z@~y7ddSI4Aymwa?76shzdFrlutLm+KyC;+H^ULp-_t#TzRlQaJdaCMq{!bm6OU7o> zFrl@*l7L(gB`0hIJ(tJ{w1U2a1%spHcM4XVtMc>@S+bImn_DcgXE6!hNJ{38npuJe zA>$wk7T9PKY(is`U`w3C!vsQtX6BJD-)mA3iyU8pdeJR4MY^f7tE`TJBjgFqW$Lgt z2~S#jOumyZBpi1~$qrOExhLi$0rLWL9H2rS=s0)>iP{xU%nMNx#ym04 zVIrUIuXtik=Z6MKV>8lLr z>A~5Wz3i>d%f6eXc{H!hbQXb&xcFPj^INmmpiSWnS?Pa||0_OPpM6E6^`~Gb=ar&= z$Qpgj|E&z9J}rWK?y-uQKYD`Hmt#ZaQmlYU>YoOIv7~-JbU;N`|2y!31gq~G6A7(< zWauXpUZ2+wsM|AGTLFFJIp=2SnXrtqS}#z-9p*1-^x!Gv=M}~Zxz~_+j!tueagR?Z z#ps8uXj~^qQ+yWe0Ao5J`p*(kd20N3VgbyZHCiJl$|Ir@j~34^RY!XJ@z)UkTDJ7)<9b)GySNwoW-n>{UfM$!UqCd`#T$UD zT0GPmUIPCqX_W^%_W)lxmE*~tGZ3nrutZs`aLW!-BdE}hP!uUxFB!18$F%meMrmNQ zQ68_n0%_1>J01{*@dc4jG1pSZnxrnJv6$k>+CgghN~!Yk5=RfK5Xo;m{M|=;vtyGM=R_&F2$8m^<$Lb zDUitG(81s+%FuJd*ajmncTYgsXLR_5q+IT(Dq$GqQoIB*RtZDY#jGwxfs95Nn!wly zLrWa1OVNzyR%1Eeb7BfA2p~Hs|2PtL`??g{@J&@}+|7ptWfJ&CQc%EAcgbL|npY>g zX5Me)gs7vvFh#Mq<0vSyLO<-&iXo+KSDVnj;@l`f&~ zWAA=%EnjROh zZmacHTsS}sO-hkct0adPs|ofXTVg2&t72+LTqmbN3Ll?|D6j@Y6rvi;FEPw@!i`5x zdUz8N*9m8E>?!he67slCaFU{~lPc7Rx=zTiNp+nZhwg&WW~l*NN5=YUa3WvWJpxC1 zJtkU)9XLu3Pm*q0)_E*&(g!jpQ7LX+S>8x7zSS$nevik^B5iv+Tsv>6pXFeuDTofK zx$X&2!Zi|IhQpje|B&F^Eh-r8ym%KR^>ucGRUQwBo?w+F{*}A1q$aC;0|dsh%9aCP zH0eV!`3sP5f=sqZ8R3u*NfYOVcp2W%8-Z2em4IpJby%O&CE@3;!_zc@P$0*oZ!E)R zxLgxBEI1PePm>{6Dzd-f^2>p8quC&`jXhfL203_!Vh3KaxyoL!nfawznU!6X?|NZ)ktGJM}>Le=z(%{WWlR(Zbr0@vx{_QX$wsH-y@05%U^K;Xa&vIG)LvcL7m@{1 zN$itB{rX18mW+5itCu5UXv% zEsIcO*nvfOgk6OeS?5WKJtm=u$p>wrtjjlYbbA&WM(v@xeELnor$pNAslCctP#Z`; z;b5v%?{%9{eW4kJ$_rFGXnG0IUqmIN&B8xJQr{G3n1wF{L{FH7vJEQFV@XZ3@CgX) zl3Az#zpZl6xjwT{M)jG6B9^kEh*tu%v29qNRI_l-Yv5c@R8n#q$8xgqp(!nIWV0@S z^1J2UP~f(=fhK_AH)b8iN;~2EVo!~Mg)2KJVWL1~XFh2nX`!t~YaBf>eiJRFlU^8A zY-XgL2Z=038V0M9(TU$>z{pDaDmkBTe%*R1zTeYo5k6Ie*xM0G!KwF(%wHykH@`K2wqfs zdpk0>N%+KOw=j$WlGr3%E3l`?Q$#&B2_jBylD*G^U&JOMzb4ftIYo1Lj5O+%Qhy6J zaD-VVOT&CsGTRR9lcVkS3GuGU59H;~q^a_1M`ih7=6o;ve=oG1dx-U$Ek}v8?Xm5% zcSkr?Br4X8J3Z?A=~s{I7EI!;X2zIrVT@9PBy?wq>PH(iXFyWluwocAD*~b?3>w)w zmE~Ac)1Wy50%HvtpO;mId|PDza!+`>obpJF8_Ri1miAUm99}I@W%5|VR1;@EG;s=W zOh3sNPdzB)Sh0DR@kNnMUuU1=W2bkbCq~aCcMyQ6Dj=F*-_+}4z~L&z&6p@qrI;;6 zjULZT8qZi{ruJE;0;xf=E#DngTxRmT9THhg9t>7TQ;W(|S-gv~EZ?&n_W>g>uZ(e% z=cA-t?x?EH6lL=KJ!Gt!JlMajCXYZyGkHv4ygCihsQxCUCW>)_vJ6TuGrX9tl(g0C-^jCCEXo!7`{C?M zG(F+$0D9=#qVJpr`{#t#iiV&giku;C_jYQ(t#_+PyS?`G zI;vyyRh`~3>P&;w4sw{zawYGKgRIq8Fno%r672f~VA0m$-H##>xcqd*zAXRQ*Jr4BlUoO0P{KoP8&{TStd0yFaQ|G)BBQD7D zX*S@(?VUmmVi!o^dF4_;qu^6i{A5D zEe*7mED^hPw(EDFyy-)V|8lKYh)sV4uG`*D$3H1>D_SRCA8k2G! z4oYcp*@|Xj_Y}eXqOBCD(Bftr;jbl8{TtzrlUk}2*wd+{6(_T*zRT%hnYl8MeFKW6 zN}Idcu`Wztj?$3?9{N-Xhk$*zU<&LUKZsmgzIgqBR`~uB9SH3R;oEd#q`sCVaIZFhJd@Jnzr#aY1A+Y zPQH-U-}3@p_S{0C$nk7QWEp`mI7*Ja$8aw+a`TsrN%9e-WbUY$CHW}GI7pHOHku@x z(AXr|5@%nx0LUgZ)1^!B?kOl`m0)g%StMAzV!Ezhf_dx^OYfQ}IbkF5RRn>=NQ}YV zli*P!H@8?K;7o!Wq-5@>nI(7&WE>>H0vk<&O=xTqY>6Wzm^Y~$!lVS7>Cz?m@)VR3 z670>Ap8`<}7}=TU3w7>5N5B;%YFC_#pO2C#w({{an8;@yC?@GVzAa7B?|CP*zLSmk zt(dYAN8>jF8j-<`b?7qO5st=R#Z--3WzS%uK-e=n8ea)6SJqL(vPI2lef){E5sVAk z7Bvh_8B!sMxkYW>g?g&)kB97&K_jB9nd)dXYvvtdh`_%<5OUWAM=d7sC_PaE@BKP= zG;$up?lv^+TH~JQMMBs0 zTL%tB$3i+*O2)|~7ktjrtHTFuGgyw*{od~7clq^+wC$1Yx4e@>OZa1bOPb)7(A?4e zUHU7{H>ACS5lH+kQMyjlG}>3U8Inf$>KXyj6V$qFh|0BCQj=Q00s>>H^#hU~Z6bA~ zo8WOJ?}1Dcbh;^MghD^amz6@B^RfeNeic?Db-CtjLYs>{Y3v3yXu{+R*_QXStobG(%=HA9j)?TAa#M1w#964cD41SalCRz+WUg^c-$;N z@S@t=+mX3hz-JsjIeHTjvw+Xk_7r)FsK+co#Hm?u2h@m~1?1PHngu5pibZyoVXNM^ zrQX=o94Yh%9%$I>G!N{#c3>u)p{rK*{PUvxWU(Pm#mz;zTD?_TzF;T-llNX)M8s4-~*9hlh-Yd-P8mmg1$sB`5qKkJy1gW zc>znb0rD^;_01v13aw9)=z3MA5|OdgL3sg%XMf33e=mEfAMd=>)0u>`#?frPDfo`O zfO6A6WU+sfz1Y8`=!LBNd(rY`XE(;d$`ruDln`!HPy|I#EwHW=pdup&s zls|gHq?O}9G-=-hfw3m-VIhMhnzjo7vV>{t8uy5aYn&xyA6FDpm)8l5yt|nk z_*aPSeR{E&yBJ4qTz7LRpOAQyyIxB%Sn3r6lkh{ccXG#HKxQ#3GOV@w?<15wamSJ2 zX!(`b(<4sipFF=+YPL?p$C6@c{-XJCWA$*k*@TS)@UBoCF4yMY2xkNWDFnSeV`_>d zlxiiEs<~00(^UA{Q(+ZUe`);o_N*aTcvr>6cYTG%U=tSqVb7?{Spd>CZDg!9RIiak z3;zO@gL7I}V0GzzL`uz{g~JYwg@frvQO^a11{}}X0&(HhR)eHeTpA-4TyNiiiF|r{ z{3^4B>SQTb?v&y7vVk0)K`!2t`WRa|^;wg(vmPj`+=!|&V-aeQ$igCEu&U{FvR>yl zBQLjvq1DHH#IGmiaz|B-nR(>AmW({PJnp$$m|MI!hd)q6Yo#r0&-mipNF9bAxP~bw zhoNSU4MU4^^M?zy!eFT}-zLZ@`qck|BC6u%Zg;E~6WvEhy`TZ~rDhnc=9MbC^i)jN zxy&Onzi;H`FBucw?~;z}TIQ7>LfjzX71(IPYeHiaUQ3)+!NTz5O?Q)7 z>B*bkw+MR0-Ap9lPepGtzmP8MpH0CtQrJ&I9a)|qmgl#6EX)nVDszxwviKYa=W=Z` z+&qRG^^|krj4|4)anIbK3!_^m6v3Vji7aC>21g0@tK7G_7vCuUsK zx^kdVAC4s~k9883o~%1yEm!DN3e#?(fNg+dtkovLAfb+YkKQZO5Sa6X2_;!zrh{s`nsvJg^}P;w%l|DPiRLZ81xX`#%deB@-Qg{t&0gxKvXt6A z+!4ZHWZF(U-Hx+gZYqrWdsn2KRUX5Vn%vkYATX92TNvCWkszCXv4tS>_AtVd9Tof$ zg(~A!go;1N+Qm!8u8>o!4M#=@6y2a_72K&!dl8;!jHYKp(O*G5>OxL( zWSI?^sd}kfLB#0_3Ai0#5CPD4XXQ3oxFvoT?#4yA>*tq?^G`l{*)hi+C+WA?WE4Os z$H7pJ3E|xi!U`^2J4LVS#PV(@#0UfD-HydXKBaGm=G`uesxl)l)mMdZe|Mid_{+@ zy(?=|@WI)P@os06s2O=TIp#oP~Pofh#Mrl0vkZ3{2t`HV~f~-^t zb_)^$zzB;MFp+QWiX|+1E{P}wAuOgt3ZFrNrDeckX`_Jkw=5$pxF%(%A8dpLS9t9y z^0ae$2n+1Il(0AkDo+Uu@@rBFixV~XINJsqcK-HaCsSd|vwIYUEqCSQ?M2l2<5^U- zr!5E0P1s+`q3K+&touocvqjqW=7}dI?6tJ85&Ov_dQI?bq=b4hZ<3NQ-@?S921y|M z1sKtEL?0yejUL9Lmn%v1?$8m}XD{`Z&P$zn8P#3ci~SagUdS@4_xr!%TSmpc61I%$ z6aMdHSVl!Z@18lV66KGcSVkp#f8`3SfJqgtg1}g+Xui(k*t`6;%0EHYiA7ZQk&ci< zzSUV`ju4j`^V)#XzM8d+pB38kbaesM1@ztrF#ua%%5mdg*eCcr>{WRPcQct|EnxCP ziOFev^-z0H+sXt1f%T|8~mMW&E_Zp zvLa_>t;nuWkrka)#676Dr)?aqR!gn%_RfpRTEVFxZm?F6^(_=&rEh7gB#-y=v&Je( z3Xrl)GHY^*D3t59fXRgLwXE(vdfg|MuiYFY9o*(^Vj`c-n>rZMlOp9v(mq_O&9+sN z>@Dd4#`+soA!Y-q8YDZy+oGz<$gVd-A`4H8!RpwSN_Kg8+7B6ddFNox)80?Y<&LUS zT~R#khaqE?>OyVD%F_yDG^)!4#zu8n;>?ljBY9KHhBM>4M-`aU2qSVw3OdT%cN>YS z&2|alTtL}avO1F9vTa`@X%o+sm5)!r=T$*+wPA01VZ!w1Zn_UZw-)E;oYDs>nW& zykK4XCfh{GzG|NRvVWEj?*oZ^Dp6)^4}^>}WBY=Ft$K9KF|G7YJlDAvJ-q38^Jc#zm-RPU+%%Sqg3m@%3iOeLAlX=oHNLfJ$Sa>tTdM z?TSz5b5SzJd^!(fBAeC^=Ce^32oI2dqMW{?e4Rh=)b2@gM8F%2_ zIa#MLou5d$c#?}x_jRVGJj9%~Cx(>$-7eQ>41MVK`*?O0Y1`LNypP9IUyGw>jw*_EMjarPS^* z%DUWCN>^n+EUD>3cohW3`Vfq(Tsbo7kL+&atobJc!9B{Fc(=N_(Y+P2cSvINp{S}d`|$4}k%bt=;5hrRTx)TCf{>)L3BGCMe zaULY{X=lCN1n22DV0<3b{8+ zPIdfkK(5Rp$7vwYBEKsjS7wp(nUAYY-cl2boNIgb6nQE`9*Z1hj9TP_C_#@*fd~0D zsTTRk&7pd0uu(4d^*2g|Rtc|v9Vicmnd9yw?CiOAV2f|mZE<`4c}WlS$y=Bf<&*7aJ>xi@(SF2A*YgGjqQj#zTl)MBcis-PJSvdyaB%bFpH zsT=s7h%kye-pbZ^r$98??7s_=`X&>@?0*Z1uABY)DYZ0&kb$BdJ*U=2M(3jEcq{Ar ziR^X#C@p-*s>(0=zv5d}$-bhks+_W}UK7U;&_862zUBW`hLx4H2=0MDpiyUt(Zkk; zQ}8WsGWv(C&(r?;oCKSA3x@SxSwwgCZVPXrKl;DoYa#ZP@D|$XN?qyGb%rcQ2+|@% zwva!1V(qIOXDfFiSSD|^1p;GvD`SzWYjE9GIRqd}kXEjFg)mme5rX!tm3`5?PGIbh zF0#@V+zc6-X3^tsJITaIimxtEMduPgYh&AfuVKCNS=im@_ErQ);3xC>M~Zeg;JqDycMu36+d*6DsAc z6sminNU9*XTSQ?@-b?C{U`#Mr%`0`enMbG|GV=15i*NTO<#I<=g{ty;h#bn7JPdJz zgi0Wz36%*}4cM53nqe#9Ad%<&`smOw4PN|#Kx zfFJ;DxzNd&$fs+_og&cjxJARzu zgddN>$=^_nYvjtEojEj%{EVWi+f(G}`0L>saeSs+V*_eLxkmD92jLp={<)EAVGI|Q zHoL|(YPoj6HExoI&FL4cSt2xdE&nL45hzGBq(*CfQjtg{9ktJKr9>Ks`fJ(i-h>^< zxl_zfI^HJIZdcM#uL?E6^*j4kTg?Rrv+G2gfM{I6;V>O=RO>4H_yGZJH2ZiDB=rqo z22u4M5?yB>)mlmsBzU-MX+(tC(~*r7zRzc`<-?uV^3{%B#@byYSm!3=@;+vJC9C+o z>{Wc6woC}My3PL;A92UN5>DLx!T+rc)G954dvdf&tUr2!xRYa5<=t2TleoJD0=q@r z83WX|${tsHi90>Mm$=iSD#RVH6KDgkXYJ!B?taG(5f5pEvG*>K)HG9_3OBCc#%elK;xtS}L8>RjhFnZN;Z4=}G%Ml86Rs-iD zF4@xVU&xW|*^4l+X9RDVuuOotE#+!8w-F8oAFS0IkY%A(%=N=*ix5;FF1K0`R2*%T zYl9^3fzet&IXt`{h(+F2|GFwI2cdmH{@jzisZwsIds@hdimqf$ra8sA7c26FQ=E%q zu;FIMk(kJ5cEm3>Q_f3PywP$)uGBJfjL~$QJEzXN8Mx20qwrwd=b4bm!hK?}DtD=5 zgNLTN+Q`dq9x|%o3Q{h2RF!Io;y(KzW0h*a5#Gvu3S=~@!34%eHCW>8Wfub3(akjO zNfOLhvMTNL6oeGAVFiim=TucGmI|R&irMP`DV7=Wfn1ew<*fwb?NC-#^4ubT6=YKU zHc~-o3w?PS2CI2>BE=su^75C9N%7y3a=D|bQe0_3(xv#$6oewBm@aeD>~AT;dG4=VDE*yEH7rXhZ59IK!ECQ25dR9*QYFtV z0*Vy>oM4fVVhmRE>O_iPF!J)3i%Icwq+IT(nWgwe$T&!f1u~ixo50wl*b?WU6giNs zXeLe<=bxlNl@ModmYe}TB_LPEY@&V>$ZRekQMvU#UumV z-j8ofPZ~RXX7NRlO?qxjNy$9B22%J~8LSZlA8Ql^zQ6Sut;`i_J4tNX07xryL(-li zPiGnrt&B4frIkmaMwC`2zb2JdULoubv|Rh>F;0DWEma`3e2lbsmq>j~qka=PcR(*6 z6Z(xjt4g_;+)uinyV64NJyE3K-{}>6KV5u>NZY<);&+sKs$-*!*-2k;jR^rn=R?hx z8Z~IVgy};Gk;r{e)IOTgeLp1ijU5I*_P&7V34TmAPUUVasmYJs3W2fwSUyyA@%oRi z!3PqH+kA3++xXDa?IS}!p>SoqelWz{hE+}7lR1tbA$OZMbL7Gg`B69%9#1(#ehV3` zdZRp8hNWzh>f&r#59ixjh2|!_7X(tkP93DykpCdk3JPb_9Fx!mGzgb%kt=#3CX#IA zZ{BZr%)&{zf1E+gXA$qDPL|hA{@?(T@hvGCa?9!QjZ$m0QA2vOzXgP3YY5s8RyPjQ zb&iFN_0blQgNs(L-;jeT0SL-!^<>^uS(8gJe;x~Fg<=^qk^yH`G_1xfg%Jls7i!1+_wc>c7 zEDX)Fys=)aUKfwZH+b=Fwh(Op>qQ3+*^d@=)D7la~lv39YiFI$4M9`_`?$G6S87G1F$}(n^+$Y_aOI|YAx7+Har4nnU zQyaxYgz;Kw5(-{3^pv5x0e&;0)NqB_#He3l)LFg~sZv&?W&%=$ZXyNS7lsSv8eri? z3#Pl=7%E?QaIS$@;%MMAI?t194Bf~|!hS^)g@;dFjpvR52g1%G zW@-!0-iq3iJar!4U+KO9Rm-ipWcm$0_mp(!a|p9a?+Hr-zKHll≶ zh^)hdQFUN`kB^h4O)$n7tm+<}FvgD=d3mh>zH{L#q+IT(Dq|ewz551atTM)Eds@AB z0vU}lHi5A*#+EpHTH9K-oteblWuT)FQt=}x04by*%4r~}_zn^^y<>?ryi6d)-?BCgl)v zt+5fY@l^|xRa+8MH4gkym?&^8liLC;!vb^&bEm+n-~y?=y7E&%G0>aIJ{i=nS3+r0 z+L5&Psp;`JYmeYXwYRr>R~T#0CqK@fcoUI>Cpe{LPm!mHdRTizoU-c2^(ZNF}NllnS_WaStv7dr&Dy^HH<4gxl!0xJ>8 z_2fxl2~x%qZ}k*NoZ0jh&*obg>eL_!>W2g{(R}|0AgOP3GWh;`Nc8S|EWVVz2A}V| z2Gh|_748_#%qb#EcwCkXpy13$M`DpE zp>0~Th##xST0B5B-&G`l)Ex#2*n$Lq<3i$e1qmM4h6I1d^3jo4l@$pjwN`c$iKT^B ztI$8>07H4{!s9ZD$FWLu8zjnWD=JrHMFkyJmvj@AUZOzM3d7(Vpiazn@S8K>;E*YR z3%AhZm?UW2C2{HbY_ZHthJDNPVO^-M%?clSvxYl^&#l8)DOzQOQ-=15rD)$40}rP) z?!-hsS|fEnO594dP?{B1Dwn->tZi2f1~wq8L2|DDSX6ZxoBLrR1)vt|tJpDEox&`% zjed)gXD`O1MqXYWgtNI{BIR;NRe7H%Huo!#vC8_OW@}}01u`0&YXSqvC60Zt#7SMn zZl-Z}FHYxvFa;q+QTPCf>fej89o!9nf?}vbrPg4c7@GJtU(r|t(nIan^ zpNEo}5|+Z{;T&>utDiJ23G?L`K83l@#!m6K95`1;wLg=~(@Fa$RFgCfg#F=YpW!Z9Of^?}|kpfR*)bwV_H%r_bAnVN2 zgZf1v^ZzCiwJWZ*d!s~&xz_H+L_SqWE?NsbF6ZL`=rf$~<54*I8;W^IxbDgM5N{&l zA>jm+Jw=`l?H&&a4*k?a@_ncg^^lNXljz%97o_;S?xJ@zXnKjinI{92@K-#+mklwd3fC9OMC zrZb2MP`bZCf2FhaYATo`L{tgilztz472oN`J3&&P(q{NdUM6FZdoH!I^|FO3FJWVu zZ2i*^7|Yflmr2NRqr_oQ;{?H%c(F=P42TA6XGXvi$mPA~eBU2n-m#REs z3=lsmLCl(?MCapK(ZQV^f7eZPI@@DlVDx2)QD^xgQg5HS)6P5LKRiKp@5)!RLWbKt z{-K+Y{Z(yZ_-R(yaqq~FI)mNKEm%EdY&yefT+v@9);@gtMIoRK@T_ba@&}wvN39RJ}H+ws!Huf`NS4L#wxXo zMx)gyCXmsnT@x4^wQGrULRL|bFq*l#yBxqs)@aX2!4TnQ3?#~VAOrb4iP{yWwl7LD z7*l%{CJOA5_I(bl3)x>vz(-u9 zAU+UWAhqLHPKIQGzDCO?2xr&72eM5uElGR7H$5KjU zw?R4beo>Aeo&vkPizVgHa&Zx(4{^Ia-UFCq{q0A`uqsh%#}`G8B7Xo0P?ZFIw)S)T zzY%FC?Z_imV+);sddH|U6>fpL<6VL3TJElirloe42v92{RMzIe?6ujq^V-Z5wc*EKX&#)!Yp979ChKur_Ie!M zc|B%{9X@1GVB)FOMcFmYlm{h5*5kbF^;kpdQ3*M=|4RQ?e8={)uY@1lU-o}1!?FFe z2<{o&+Kv3t6B}jZ09tt&$5PYv_6!8Zy52&LC1a!Jw#ppdJr&AS(e3n`tq z#TFS}KVS=AmGz4hg61<>D~|4{ zPeEdz%AmO6{$6~CA(wE&9TsqeSOSZ0ODw|D%O-zZlql2@$C@17O#j%;nx6zNeR6tX z>>*+aIG&PlbdagU=$Wh-p?m0GyNS^?j9@j75nJ4)gTxY0{#imf@!yt+znm3ubUD4) zO~g;gc9>WK$TeQTu9{RMC9$z{JKqWaXjHY?KBX@bX#fsR z-{6VCs=ko!%!X}OybqAgo>E$WXym1H-iDRTo!S3P%H@u#`m~~)**}JiRcAJGGFE4{ zKt^+Bo4~lc19vHZKBXp#wf7kk)xQyQo}gFJk3kns5a5l~4+cBTEMFs3Hqx{~y}{kc zV|khI>;j1_DNagg{3WHj+HfdS+aXA)TA^e8PX z=Ls`nx-8z70!u;`y;<@twGRahjBKG6YIcF^NbVz1yRuO0S5YFw7HU0%iF`_^gaKEnw=-esJ$wN!j2Q4gM#f;DGI@4oJSgaX6xnuUqN@M4RZun-ny7W#yF$m|ip z1j>z9*?iDLhz#6+ut$S7HV$g*;h*$+CEud#T}IP6nUzk zdDd5<0!P#NAGNnKy%-a@VWJtu4nwf>n7R$hf}q1Hc`Wec^Q12-q#+fj}9 zaxiItAgH>4KA8BIS=B_^N!@R0)Qv7y*iKuhZx;~Qe0<*JP`5%--#BHgdSV@i zY@y0cSW}QgIEIOlc!kwGL`VS*7uat5! z;t!-;?x?EL8AX6T2N|o@B#vKJ0#qQQS(7F(Hfz!nC&O+YDhjf*o4I<`QKAh0AO%CT zB$)AoavrD*KT4u@#YjA=tV@}@xfP=)kBI`id1}E0QagQR8YBxOt}T|Pv)fl7 z+mv=B?LC+tk9RHvFRH!0U7PRZ;S(pH{Jn{YrO9<8dx|_o)MIHP;?&Z7KmH;+dC0Fx zwKSI&ip9R+Qn6g<+kAAGIeEBrs2eA8sd;r^Gj3iFn{d{X`%&=NEj3qpwfLgk09+lU;PSHy)h6uV!TI5WLIY0iUQ?uD z$&EX^sbA(JpB^a3yw5|Us&l?R8vCurheg`$sc*b8EY5F|==uhnT{%jmdU^%)cTj#AAdCO&?8Sek z^Wx9aI}d9kZKujiB?|8bT9~ZM&J|xT@EZTGP;5hXtL)+bif^|H`--+(WtRmDc(w4S ztj&S`?_}7iLO<`GYy#wMHR^DI-3t;KfN;_+59LS@Ts^4HbZa)aS5H|GC}ujSZRw3eGn zCSG1Q_Lr>FZT_!iXg^wrj@oZ-QcJKk-)?F&UPC=lR`fo9MSYETcX;DH;QxxR@z_^l z8t)7KuVrXFT8PNT^G8oG!BXq0d;_OolL>wh0%Mur{oJZ6o|yS_04hQE>KY~CbPsT6 zqY$^e)W8FN3M-IG+`d2*;aa%_cWvkRb?&%KmfmiZD-ewj0}5y}& zt@VcG$c@!U zL02#3hU(209^?pr!$sg*YB^$n;I{e_+@#bq3j4@#buK8XBzI9#{0zCn9**I}2&Jv9 zQlkdiOZnQgJpg9*3 zS;%b+Rt?>Ba@)Pq?kFQK-Doz;#a7zoNx9rnRh}z~++GYBtK>G0)>d*`Afu7nCNMT~ z+Y)E4rKM$$H50h!IG{)gwYEnJJV>RoyD@enQTtu|~`x(?@Cq$m=W8 zF<8wjU1r@5nXN`%{&F#y9VO**M^$CEavDSqfH~C|rh==nbGuAbZFU52TTZzOtoIYvl^fX1%`*8c&h$9V$1e(n2&Q zn~ZA&317XYd|aU!J^@u!rPD1Yill#(;F6GZ3|8~%MAE-%erpNZG$i5JPUs$0$p37Z61}H(c0M$~8wp zV+WIfDBh@b_tzWX6>L#k0X6Pgvs|y)`hdbaaqUDDL6QHLqU4N?^#4V$NytA2cT4_f zSM|KyLX8c<tCNu>yBq9X7<hY|qVaFp-ZulY5lfGYI*Mr*ZJPNC}Y*_LrD)5QgNXfCgjGVtKm^ZNy@; z{fG6z#@*{t#u&u=V4^^XV;7q(4K7gDVaT%B%t^y7g3|a@Z2*O|_rmmee6bmV7uDYW zzRb2YaYEa^bj+YR$zn6^&#|Y-Q;Nv5*bFHoy4b8AYD5>CkzbR#*z6o}=bv$Z8tMW~ zahhY;GPIS>WoThv>0p7{a@{=__C5EWrX#{K;G_rza@Nk2?(Osm%Rm&`i3ZDz0aEhq9^>=vV|&l zU`b6sb`t_){n*E5k#O{Ie+oX9@NkDp>}}&iPq&ZH@@d5@&M^Q-;wZwA>J^{B%|^^H znZPedDg6SgET1y;PHs*zJLYz#&xr<`V9)P&W5D8?)#I2bP_vrs?M%Onsvxs@pM*pf zk1qzR(?=&hg_n%Hyi&?J)fY**+)-6!Gs@%p3S_L>yf}JUJ-z}N&E_?MvDv(qI9=Gz zWajEsLx~dnixdpef?!4u%6Xs?{4El-EB54ZHC@Ww&8-+a%P>)3m$dC!Vgt18l^j{m ziYXZZdljVcP5TPiSc5Ku9bsYi$5f4vgV$i9K+t0?%&ox%QagQRHY5vlnOiJOXSZ)b zwkhpM+WXq{c)W8Vcv0=`?b>`h6Q4Nw6oHZZNp|GV!b`l*06+GPC^B=wDXhROQRB)V>H8wW+=B&+X>vP1yg$q1D~ z_FVSb{9EU>Nt|e?*Cp{9YNCb7dd#f*I*WIA)O1MNVXJiSPNGAz*W+MPkD#qXNBh6x z+rz-VqU~Xrg158rv5o#ATV|#ITN!pR&?30!VQV+?M^7+6asaJ-54(uT{5%4IvCPkc zz&Q!Yo@)TL1li-6HVD77Fz^EkrNb)+l+G8~P~bnIX2-kthZ!0>UcqD^D0};WjX>HV zE+iUY#RiEuZQ;%1cqRJOcJkgK)&R*D5`e9-093YM3VI=tEJo`3|Ms%sQnOhYEU^RY z=dT~Cw+0)qG=i@6CkrQ1E+l#rez{v061_QV#qnhJ+aa-UW&B7MbTwju#T$*Ug%DaCZ=q10nmB&q+8s$W(TVN3vps7ZQD@n;1>} zr9cLh|0JQD_-{+Zzn2wpypZVeZX%xf_8a4ZI3yDteW-a;oIsgAcBCgF^ zZ&y;be_WNd?eIdP%Q|a2x4~__kcjE~sP-!Q{={xlz99x4?xGpPL_QZys*y6;7ZSZQ zs@lwfeHW1iAmH@vpBSu?)aedv_m<_48F}f1w=th`2ln5Qa=D|bzN{z*_5+Zy>cB=u z#_GTp$Y>616Bu`Q04t7oy55>n6UEwlGl}ZoT|7Y-68#K5tv0wDc`Ppzo*$F)CWHrr z)x6S$$Gvy>Ka9NmAInKz-UoIv#%SgH0Q8SCp36OD+*a&1au`z*3 z7n^-kY7!|n6Luj{F{+u^sB;aeTw>J0;BJY}n30!TCJ;#`KATCo+)*=&&-IXTkoX8> zH1RQk0pt=F5?SKdFC;P}rpw~hDX=7D(VHdT>~lxJz{nP7p=KAjeq=L=+LgswUy2eT zwiD$MOypBSC1D?UAS4kP#K3P}A9hCMId8(j! z)>WYbN7q&LHsBXoS4DnJ>bk0x+G-)#pxcLws`_g6R;d}bvg%m5hRHZJ+LLt$Ypd3H z+{{M#M;i4C9B9#pM;?pQw&)Sj5SBa_dVg<;DI4jHBYCT{p^epPUhfkA}7vb zma@CYacr!_ni<(^DwkkM%{4PCAux8$jBiPc*;BSvJ^^1yteWwT7i8VcQQ-sH!f~8H z-#D~DR1sNjtZnLMs9W$Wh&9PaMphgPZ#BDw04A| zNU{DbgOAVa`bceR0=T_uM;Mf$-9IhbJ(crqFKd!p7y9T?9DSumquyA4>KW&sf9{Ih z{0)s!IKQI?Z3O4D!XX~y?R?r=a`nNt@pEipiYK=xZ@Sx>-;+1JPjL-A9t(QqF-$>M zYdW-?2y(BI@pDmV|$*^yEKCBDXky+uxHBLu# z2A^AjvHBpD5l$J}C)NjfP7FNU2l*OI{OYLJ}kOHtKjL~emd0qDfK?-7I5j%lH5jJp8cet_$Yyu3OHCvvxta=D|bd{30` z@dn6PC2~=(wfY_fG8&O<0t3h;h+IpYz3j~`8_!JR?p~Z_dTk0qilT5OiR$-hE)X1o zRYv(5U#o~J)*zEmB}CTolTZv*sNAiKWoH8V04aMyKrvX&s}lkJx{;T^T+Gw(RZ=c@ zR8>GLZ-L06{>^Vf+#mrJ$Y=s;0%H?UOPoCw0hMhn+s;gqE}tJw0Vq;F4<$1ttTxZX zNJCD&R7yBA4@8dS3o=F}a)# ziF{g7X1Ux8G7geUfs7`XCNMU+w8WWfX=T}RW`cC7{I6K4^k&IDVV4Eu$~-)%Uj#D# zXOgI0@q}F#B}&W_b}=UMsY217uz?ie#Qw!_!jDJclH*E6=?WFA-XB8&&v3?GjqOsvj*?sT87e(!DT#(nl z@i|E9o6`*c#-{_KCm43wIF*O6q$b1uJ_w9u*aLQ@vIc=mwjlomzwQyq&|RstV@PhD zR}L6}d$FFW-1<0nz8uNM#jC@a%E*TYQ0lD$+_wTs9HhYevVfO$ogzp+C=OS|1Q0U^ zrt)9|wjpxyzAm+1&SA~Pc0}FP5kpU#*DJdC(BX1wyD78i#wMf6n$-Jqu zCYNA79}8yXKQRU6)n%1Z37piOuCy~Wv0pS>a65{HKHqf|zRVm7`o39U_|_XcXOsEq zJYNrH=A3kFG&-v|lE>2^v2XBH?66mf?=a*N4uwOZzO7Lp(bsRdXa)Ahe&~#F%|LGv zjw$FJfUE0Dlp&C+VdY>6MNo3t;D?Cv*rp6my^$V1u$%QhkqmZ!UN4MNqtzB0s6;2K952Dho~K0U#HvB*^)7|R5)C_&7cqeN#QD>}Hlbz?WtIki=9!R?wv zk{B>G;r1k7*UMD1-br`?ULB<$l^VaKhpZ|w|rH_c#mmMOEhqP9$|v+SW5 zc(}9dK};0J5Vg01gW;P|bzlyLuaTxr5V;tv>Rg==xlb5*d948M0sAQ_mpiITm@c|OGE2Qf_ zn{^R#wN*c#2jikLdq{riIJ%Sh2-rnxb z&xhrcA7@Xzi3nTIDJ^@7JVn&Q)+6GSt-lj$MA>@sYf{Iwg{rg4Q_SWsVp~0xAh0$NFfSMg^f}yeFVJ~aWB&NP`z!MSF z2FF|3EMF4P+V~%@%i!~n)HhcdE`x_jblvHp7EcObN-d3uMlF@MvX(#0Ud!)wUdvbW zo+Haf`WS0>?beyO$+#pM)YA4!R`CzntN0t*GBx$ioSm2R&%!VHm6!cr@g2R&z7l@) zYR?wmh*8OK^eQcady2D4tUr3f$tB0C%BK-5)5&!Y1jahK4qXou%zCl{UvAOxak9z#INVHyqIEh!}Vf$pbP{B zc&gCFRV_f&qn9ns6$@j{o;3p`P>zPkh~q?s_l6eBc;9q&EQe<*K@LEC7UX7=jbkL~ zmU?4TbEME;f=fw<%e6%q*fUaS6ozS3xN%JOOW2D7Eb~Tin+?|L4al-kD+2Re!=U= zQ(Bl5f1J0Kz8wIA)x0{9;>V1<+@guVvsJBMA?0#MRi(IcCqxdUO)B4jxIt1ZkkO>r z1jZ)CmN=>uD?%U}&rFjp#UDvQC{l_~POAEHAkNpt1?y60;+l_DmTl$%TOMJ1C4qPa zs-;SvTLiFzOp5%vc!?1o~LIT(8r zVglVHk;jh;M5D>$FG5n^NM%rQpCi$A@_0W5HmROFnbi(;{eQ_`*B{Wrhw!V<`oH4i zSJ_v>`PJwB-^#$R(jvGgihxF)A;u0S*cv-N1>f?)g8m`LhFN1iebMX6=z*lb0fn^C z0sgP}T8MomyoHYRe=9=^(IP~)kUx5Y0hi-!<+CVaCIkL{2<#FAZj7zlDr*5THv_Im zzYQ;ycQD{uOoajG6$0+?e%5{b4EUw1hynn-G@SP&>2InJj5Z2{J%*EyAcCBXa|&k- z{4+NNf#@!9`kQxx3Cmyhs|{W$G<7=MV6i=&v|3A%^X8y!E@VZXa9+0@gAM0(2QZP( zn26_f_gCgZNykz;feoa4?y}QP;uJ4m_QQB}$yioSg>WUNvKI5=79TY-#58JNJhhhZ@5i1~M8N=+1k>N*nDPqV5* zB^9hNp_1`!LZ!TwLiJ53k}3%97EySpuaSBrcqj~3^Xi0$dcw%dUoOT&{gjl;9aR;o z$^#HNlug|Raf5_PAfpMD2~4_BeL1BjkwP^eRk&Op5B?)ZOspc7F`o(Xj&S8*okn9L-G}$qs zv2mD|IP+{xEW5p#B3&YnBmWLR=0;$r2+ji;!VPi4h8iT1>ABuFWEs;OrxvDTqOj?{y`!w6IXJ4K zj7-=c5?Qo&3|7baPVBy8jl8^~$yd=FP0Hnts_O1hWWw=~v8ub{kYXhh1TvcLZUSS| z-7RraSJ9Yhyc!%KDfUT02(@>nKcX}T>W@@4$S>@fR8OM?R|BQ{*da;NxwN z{)q3X{&*e!B3HMNUmfd@>1E$GMeh-MiaeFUsr|6(x;b7_N0rLwAZ6t?QS>*8qLW`) ziaxX`iD3IEssAk=8EXyIYl{mb<;B(V#zvtrwzydDAH{>T2~$2!=4ULWnk+|?vdo;)I8mA0ydm3DjFf!U2UpLgdu03P0 zR4aiiq27R;(1BKhBV>k0#@~MFjGf58b?uq-%YC>|9wPOyKjCN<_-pr8y}xf`p??#; zg%V7KqBh3H+j9r%^%fqSqr6BB1?`iPK0E#QkR0ZJVS*sW0-sW)1E)zaotb$snE?Vh1RRs1=%J*Tv_4DgYY zad@Pu?Wx7m#?e8@Z`PJVgPex|`IZX95HbVWp}&Nc8h>MZR=EaX^_6PNMWoy;`UZjur-SSxFK8-lbFU?Oot(*Y@tk(czIkA&USk34r5Z zOAWZl1DkFS5<;XLn_mWvG)tq!df(Pz6sg&ztt0hjsZUKwqy%nL+FWjysjTkNS1dOR z8>@VM3?!V+`v~;6-Jmi}=oPf(x(8b(}(Bv~4 zg)QPw5Yai<(Li!)gCL02GHm4-KX3Me_NBcHJw*lj z$VQj(+uO5YP;C{q_6?N>hpO-&brInZ|DM`=8?bYpMBLjoU%lr zJ+OZW6jiVQ8nnLw$^=44y=im=J~0bQ*DALFe>x7_4$@Qa$7@{Vz%UnwistA>BVKtJ zg|}KS(B3$Oj7lIgaB&7)Bh?y%A`F#EE%?Y@{Sa$YAD(wo?JE?EcrYC)_8iO^o4i@7 z4$xuk_V%kvwaw#jpjLZVI-EfN1yL;5=sux7I7bYq5+XQrpim%MEf{Dak78+{3FA2I zuv0^8pr(^x$ZbIUF%7#=k%UjjK>EA#Hk=*rgh#VI1A|**BP20-aeH%UUzjGy`=LdZ zCvakY3LoE|29NLJznAfyc@CIA}gR4#mg7q43y*j|UEe$EWa7Jpvxr;^XE#Jl=qhTNlFP4t(5x zBs}iK$6Jqr$J_C-ZZSO8@$rS#@OTs-r=1Ot)%dvK9C+M}k0;i` z<0*U`aUML5!pBYP;BhNHuDB2$efW6KMew*6AD`F&kB9KF>JoUIjgMDd4v$^&vE>SQ z+<=c;u7byH__+18@VEmXZ@2~?Z^FmZ8{zS5d^}Tx$8Yda9)d>=9}kw{@i0D`!|>RO zk4I|o_y>I8ZB!M!vZ{ioW>)ZM%?fTUtl<8`3NEpz;5v*7k{T6cK1SQStK$U`;NAPn zRIkD@iiY5{z~j-LqK43TFz~|oGQ6=~6+;k-x~CB+V$aZWoWZ|J63x}}CujYkN&#e( z_5qyx8!e(?!K6iYwd3qOv%e098 .section { + text-align: left; +} + +div.footer { + width: 940px; + margin: 20px auto 30px auto; + font-size: 14px; + color: #888; + text-align: right; +} + +div.footer a { + color: #888; +} + +p.caption { + font-family: inherit; + font-size: inherit; +} + + +div.relations { + display: none; +} + + +div.sphinxsidebar { + max-height: 100%; + overflow-y: auto; +} + +div.sphinxsidebar a { + color: #444; + text-decoration: none; + border-bottom: 1px dotted #999; +} + +div.sphinxsidebar a:hover { + border-bottom: 1px solid #999; +} + +div.sphinxsidebarwrapper { + padding: 18px 10px; +} + +div.sphinxsidebarwrapper p.logo { + padding: 0; + margin: -10px 0 0 0px; + text-align: center; +} + +div.sphinxsidebarwrapper h1.logo { + margin-top: -10px; + text-align: center; + margin-bottom: 5px; + text-align: left; +} + +div.sphinxsidebarwrapper h1.logo-name { + margin-top: 0px; +} + +div.sphinxsidebarwrapper p.blurb { + margin-top: 0; + font-style: normal; +} + +div.sphinxsidebar h3, +div.sphinxsidebar h4 { + font-family: Georgia, serif; + color: #444; + font-size: 24px; + font-weight: normal; + margin: 0 0 5px 0; + padding: 0; +} + +div.sphinxsidebar h4 { + font-size: 20px; +} + +div.sphinxsidebar h3 a { + color: #444; +} + +div.sphinxsidebar p.logo a, +div.sphinxsidebar h3 a, +div.sphinxsidebar p.logo a:hover, +div.sphinxsidebar h3 a:hover { + border: none; +} + +div.sphinxsidebar p { + color: #555; + margin: 10px 0; +} + +div.sphinxsidebar ul { + margin: 10px 0; + padding: 0; + color: #000; +} + +div.sphinxsidebar ul li.toctree-l1 > a { + font-size: 120%; +} + +div.sphinxsidebar ul li.toctree-l2 > a { + font-size: 110%; +} + +div.sphinxsidebar input { + border: 1px solid #CCC; + font-family: Georgia, serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox input[type="text"] { + width: 160px; +} + +div.sphinxsidebar .search > div { + display: table-cell; +} + +div.sphinxsidebar hr { + border: none; + height: 1px; + color: #AAA; + background: #AAA; + + text-align: left; + margin-left: 0; + width: 50%; +} + +div.sphinxsidebar .badge { + border-bottom: none; +} + +div.sphinxsidebar .badge:hover { + border-bottom: none; +} + +/* To address an issue with donation coming after search */ +div.sphinxsidebar h3.donation { + margin-top: 10px; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #004B6B; + text-decoration: underline; +} + +a:hover { + color: #6D4100; + text-decoration: underline; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: Georgia, serif; + font-weight: normal; + margin: 30px 0px 10px 0px; + padding: 0; +} + +div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } +div.body h2 { font-size: 180%; } +div.body h3 { font-size: 150%; } +div.body h4 { font-size: 130%; } +div.body h5 { font-size: 100%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #DDD; + padding: 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + color: #444; + background: #EAEAEA; +} + +div.body p, div.body dd, div.body li { + line-height: 1.4em; +} + +div.admonition { + margin: 20px 0px; + padding: 10px 30px; + background-color: #EEE; + border: 1px solid #CCC; +} + +div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fafafa; +} + +div.admonition p.admonition-title { + font-family: Georgia, serif; + font-weight: normal; + font-size: 24px; + margin: 0 0 10px 0; + padding: 0; + line-height: 1; +} + +div.admonition p.last { + margin-bottom: 0; +} + +div.highlight { + background-color: #fff; +} + +dt:target, .highlight { + background: #FAF3E8; +} + +div.warning { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.danger { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.error { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.caution { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.attention { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.important { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.note { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.tip { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.hint { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.seealso { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.topic { + background-color: #EEE; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre, tt, code { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; + font-size: 0.9em; +} + +.hll { + background-color: #FFC; + margin: 0 -12px; + padding: 0 12px; + display: block; +} + +img.screenshot { +} + +tt.descname, tt.descclassname, code.descname, code.descclassname { + font-size: 0.95em; +} + +tt.descname, code.descname { + padding-right: 0.08em; +} + +img.screenshot { + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils { + border: 1px solid #888; + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils td, table.docutils th { + border: 1px solid #888; + padding: 0.25em 0.7em; +} + +table.field-list, table.footnote { + border: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +table.footnote { + margin: 15px 0; + width: 100%; + border: 1px solid #EEE; + background: #FDFDFD; + font-size: 0.9em; +} + +table.footnote + table.footnote { + margin-top: -15px; + border-top: none; +} + +table.field-list th { + padding: 0 0.8em 0 0; +} + +table.field-list td { + padding: 0; +} + +table.field-list p { + margin-bottom: 0.8em; +} + +/* Cloned from + * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 + */ +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +table.footnote td.label { + width: .1px; + padding: 0.3em 0 0.3em 0.5em; +} + +table.footnote td { + padding: 0.3em 0.5em; +} + +dl { + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0; +} + +dl dd { + margin-left: 30px; +} + +blockquote { + margin: 0 0 0 30px; + padding: 0; +} + +ul, ol { + /* Matches the 30px from the narrow-screen "li > ul" selector below */ + margin: 10px 0 10px 30px; + padding: 0; +} + +pre { + background: #EEE; + padding: 7px 30px; + margin: 15px 0px; + line-height: 1.3em; +} + +div.viewcode-block:target { + background: #ffd; +} + +dl pre, blockquote pre, li pre { + margin-left: 0; + padding-left: 30px; +} + +tt, code { + background-color: #ecf0f3; + color: #222; + /* padding: 1px 2px; */ +} + +tt.xref, code.xref, a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fff; +} + +a.reference { + text-decoration: none; + border-bottom: 1px dotted #004B6B; +} + +/* Don't put an underline on images */ +a.image-reference, a.image-reference:hover { + border-bottom: none; +} + +a.reference:hover { + border-bottom: 1px solid #6D4100; +} + +a.footnote-reference { + text-decoration: none; + font-size: 0.7em; + vertical-align: top; + border-bottom: 1px dotted #004B6B; +} + +a.footnote-reference:hover { + border-bottom: 1px solid #6D4100; +} + +a:hover tt, a:hover code { + background: #EEE; +} + + +@media screen and (max-width: 870px) { + + div.sphinxsidebar { + display: none; + } + + div.document { + width: 100%; + + } + + div.documentwrapper { + margin-left: 0; + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + } + + div.bodywrapper { + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + margin-left: 0; + } + + ul { + margin-left: 0; + } + + li > ul { + /* Matches the 30px from the "ul, ol" selector above */ + margin-left: 30px; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .bodywrapper { + margin: 0; + } + + .footer { + width: auto; + } + + .github { + display: none; + } + + + +} + + + +@media screen and (max-width: 875px) { + + body { + margin: 0; + padding: 20px 30px; + } + + div.documentwrapper { + float: none; + background: #fff; + } + + div.sphinxsidebar { + display: block; + float: none; + width: 102.5%; + margin: 50px -30px -20px -30px; + padding: 10px 20px; + background: #333; + color: #FFF; + } + + div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, + div.sphinxsidebar h3 a { + color: #fff; + } + + div.sphinxsidebar a { + color: #AAA; + } + + div.sphinxsidebar p.logo { + display: none; + } + + div.document { + width: 100%; + margin: 0; + } + + div.footer { + display: none; + } + + div.bodywrapper { + margin: 0; + } + + div.body { + min-height: 0; + padding: 0; + } + + .rtd_doc_footer { + display: none; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .footer { + width: auto; + } + + .github { + display: none; + } +} + + +/* misc. */ + +.revsys-inline { + display: none!important; +} + +/* Hide ugly table cell borders in ..bibliography:: directive output */ +table.docutils.citation, table.docutils.citation td, table.docutils.citation th { + border: none; + /* Below needed in some edge cases; if not applied, bottom shadows appear */ + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + + +/* relbar */ + +.related { + line-height: 30px; + width: 100%; + font-size: 0.9rem; +} + +.related.top { + border-bottom: 1px solid #EEE; + margin-bottom: 20px; +} + +.related.bottom { + border-top: 1px solid #EEE; +} + +.related ul { + padding: 0; + margin: 0; + list-style: none; +} + +.related li { + display: inline; +} + +nav#rellinks { + float: right; +} + +nav#rellinks li+li:before { + content: "|"; +} + +nav#breadcrumbs li+li:before { + content: "\00BB"; +} + +/* Hide certain items when printing */ +@media print { + div.related { + display: none; + } +} \ No newline at end of file diff --git a/docs/_build/html/_static/basic.css b/docs/_build/html/_static/basic.css new file mode 100644 index 0000000..e5179b7 --- /dev/null +++ b/docs/_build/html/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2024 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: inherit; + 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/docs/_build/html/_static/custom.css b/docs/_build/html/_static/custom.css new file mode 100644 index 0000000..2a924f1 --- /dev/null +++ b/docs/_build/html/_static/custom.css @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/docs/_build/html/_static/doctools.js b/docs/_build/html/_static/doctools.js new file mode 100644 index 0000000..4d67807 --- /dev/null +++ b/docs/_build/html/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2024 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/docs/_build/html/_static/documentation_options.js b/docs/_build/html/_static/documentation_options.js new file mode 100644 index 0000000..da6f32e --- /dev/null +++ b/docs/_build/html/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '1.0.10.5', + 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/docs/_build/html/_static/file.png b/docs/_build/html/_static/file.png new file mode 100644 index 0000000000000000000000000000000000000000..a858a410e4faa62ce324d814e4b816fff83a6fb3 GIT binary patch literal 286 zcmV+(0pb3MP)s`hMrGg#P~ix$^RISR_I47Y|r1 z_CyJOe}D1){SET-^Amu_i71Lt6eYfZjRyw@I6OQAIXXHDfiX^GbOlHe=Ae4>0m)d(f|Me07*qoM6N<$f}vM^LjV8( literal 0 HcmV?d00001 diff --git a/docs/_build/html/_static/language_data.js b/docs/_build/html/_static/language_data.js new file mode 100644 index 0000000..367b8ed --- /dev/null +++ b/docs/_build/html/_static/language_data.js @@ -0,0 +1,199 @@ +/* + * language_data.js + * ~~~~~~~~~~~~~~~~ + * + * This script contains the language-specific data used by searchtools.js, + * namely the list of stopwords, stemmer, scorer and splitter. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; + + +/* Non-minified version is copied as a separate JS file, if available */ + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + 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/docs/_build/html/_static/minus.png b/docs/_build/html/_static/minus.png new file mode 100644 index 0000000000000000000000000000000000000000..d96755fdaf8bb2214971e0db9c1fd3077d7c419d GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu=nj kDsEF_5m^0CR;1wuP-*O&G^0G}KYk!hp00i_>zopr08q^qX#fBK literal 0 HcmV?d00001 diff --git a/docs/_build/html/_static/plus.png b/docs/_build/html/_static/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..7107cec93a979b9a5f64843235a16651d563ce2d GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu>-2 m3q%Vub%g%s<8sJhVPMczOq}xhg9DJoz~JfX=d#Wzp$Pyb1r*Kz literal 0 HcmV?d00001 diff --git a/docs/_build/html/_static/pygments.css b/docs/_build/html/_static/pygments.css new file mode 100644 index 0000000..04a4174 --- /dev/null +++ b/docs/_build/html/_static/pygments.css @@ -0,0 +1,84 @@ +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: #8f5902; font-style: italic } /* Comment */ +.highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ +.highlight .g { color: #000000 } /* Generic */ +.highlight .k { color: #004461; font-weight: bold } /* Keyword */ +.highlight .l { color: #000000 } /* Literal */ +.highlight .n { color: #000000 } /* Name */ +.highlight .o { color: #582800 } /* Operator */ +.highlight .x { color: #000000 } /* Other */ +.highlight .p { color: #000000; font-weight: bold } /* Punctuation */ +.highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #8f5902 } /* Comment.Preproc */ +.highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #a40000 } /* Generic.Deleted */ +.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ +.highlight .ges { color: #000000 } /* Generic.EmphStrong */ +.highlight .gr { color: #ef2929 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #888888 } /* Generic.Output */ +.highlight .gp { color: #745334 } /* Generic.Prompt */ +.highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ +.highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */ +.highlight .ld { color: #000000 } /* Literal.Date */ +.highlight .m { color: #990000 } /* Literal.Number */ +.highlight .s { color: #4e9a06 } /* Literal.String */ +.highlight .na { color: #c4a000 } /* Name.Attribute */ +.highlight .nb { color: #004461 } /* Name.Builtin */ +.highlight .nc { color: #000000 } /* Name.Class */ +.highlight .no { color: #000000 } /* Name.Constant */ +.highlight .nd { color: #888888 } /* Name.Decorator */ +.highlight .ni { color: #ce5c00 } /* Name.Entity */ +.highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #000000 } /* Name.Function */ +.highlight .nl { color: #f57900 } /* Name.Label */ +.highlight .nn { color: #000000 } /* Name.Namespace */ +.highlight .nx { color: #000000 } /* Name.Other */ +.highlight .py { color: #000000 } /* Name.Property */ +.highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #000000 } /* Name.Variable */ +.highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */ +.highlight .pm { color: #000000; font-weight: bold } /* Punctuation.Marker */ +.highlight .w { color: #f8f8f8 } /* Text.Whitespace */ +.highlight .mb { color: #990000 } /* Literal.Number.Bin */ +.highlight .mf { color: #990000 } /* Literal.Number.Float */ +.highlight .mh { color: #990000 } /* Literal.Number.Hex */ +.highlight .mi { color: #990000 } /* Literal.Number.Integer */ +.highlight .mo { color: #990000 } /* Literal.Number.Oct */ +.highlight .sa { color: #4e9a06 } /* Literal.String.Affix */ +.highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */ +.highlight .sc { color: #4e9a06 } /* Literal.String.Char */ +.highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */ +.highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #4e9a06 } /* Literal.String.Double */ +.highlight .se { color: #4e9a06 } /* Literal.String.Escape */ +.highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */ +.highlight .si { color: #4e9a06 } /* Literal.String.Interpol */ +.highlight .sx { color: #4e9a06 } /* Literal.String.Other */ +.highlight .sr { color: #4e9a06 } /* Literal.String.Regex */ +.highlight .s1 { color: #4e9a06 } /* Literal.String.Single */ +.highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */ +.highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #000000 } /* Name.Function.Magic */ +.highlight .vc { color: #000000 } /* Name.Variable.Class */ +.highlight .vg { color: #000000 } /* Name.Variable.Global */ +.highlight .vi { color: #000000 } /* Name.Variable.Instance */ +.highlight .vm { color: #000000 } /* Name.Variable.Magic */ +.highlight .il { color: #990000 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs/_build/html/_static/searchtools.js b/docs/_build/html/_static/searchtools.js new file mode 100644 index 0000000..b08d58c --- /dev/null +++ b/docs/_build/html/_static/searchtools.js @@ -0,0 +1,620 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2024 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, anchor) + ); + // 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." + ).replace('${resultCount}', resultCount); +}; +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); +}; +// Helper function used by query() to order search results. +// Each input is an array of [docname, title, anchor, descr, score, filename]. +// Order the results by score (in opposite order of appearance, since the +// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically. +const _orderResultsByScoreThenName = (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; +}; + +/** + * 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, anchor) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + for (const removalQuery of [".headerlink", "script", "style"]) { + htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() }); + } + if (anchor) { + const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`); + if (anchorContent) return anchorContent.textContent; + + console.warn( + `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.` + ); + } + + // if anchor not specified or not found, fall back to main content + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent) return docContent.textContent; + + console.warn( + "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. 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); + }, + + _parseQuery: (query) => { + // 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]); + + return [query, searchTerms, excludedTerms, highlightTerms, objectTerms]; + }, + + /** + * execute search (requires search index to be loaded) + */ + _performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => { + 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; + + // Collect multiple result groups to be sorted separately and then ordered. + // Each is an array of [docname, title, anchor, descr, score, filename]. + const normalResults = []; + const nonMainIndexResults = []; + + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase().trim(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + const score = Math.round(Scorer.title * queryLower.length / title.length); + const boost = titles[file] === title ? 1 : 0; // add a boost for document titles + normalResults.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score + boost, + 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, isMain] of foundEntries) { + const score = Math.round(100 * queryLower.length / entry.length); + const result = [ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + ]; + if (isMain) { + normalResults.push(result); + } else { + nonMainIndexResults.push(result); + } + } + } + } + + // lookup as object + objectTerms.forEach((term) => + normalResults.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) { + normalResults.forEach((item) => (item[4] = Scorer.score(item))); + nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item))); + } + + // Sort each group of results by score and then alphabetically by name. + normalResults.sort(_orderResultsByScoreThenName); + nonMainIndexResults.sort(_orderResultsByScoreThenName); + + // Combine the result groups in (reverse) order. + // Non-main index entries are typically arbitrary cross-references, + // so display them after other results. + let results = [...nonMainIndexResults, ...normalResults]; + + // 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; + }, []); + + return results.reverse(); + }, + + query: (query) => { + const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query); + const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms); + + // 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); + if (!terms.hasOwnProperty(word)) { + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + } + if (!titleTerms.hasOwnProperty(word)) { + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: titleTerms[term], 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.set(file, [word]); + else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(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, anchor) => { + const text = Search.htmlToText(htmlText, anchor); + 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/docs/_build/html/_static/sphinx_highlight.js b/docs/_build/html/_static/sphinx_highlight.js new file mode 100644 index 0000000..8a96c69 --- /dev/null +++ b/docs/_build/html/_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/docs/_build/html/genindex.html b/docs/_build/html/genindex.html new file mode 100644 index 0000000..c65496e --- /dev/null +++ b/docs/_build/html/genindex.html @@ -0,0 +1,307 @@ + + + + + + + Index — SyncSketch Python API Library 1.0.10.5 documentation + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + +

Index

+ +
+ A + | B + | C + | D + | G + | I + | M + | R + | S + | U + +
+

A

+ + + +
+ +

B

+ + +
+ +

C

+ + +
+ +

D

+ + + +
+ +

G

+ + + +
+ +

I

+ + +
+ +

M

+ + +
+ +

R

+ + + +
+ +

S

+ + + +
+ +

U

+ + + +
+ + + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/_build/html/index.html b/docs/_build/html/index.html new file mode 100644 index 0000000..3108077 --- /dev/null +++ b/docs/_build/html/index.html @@ -0,0 +1,1438 @@ + + + + + + + + SyncSketch Python API Library documentation — SyncSketch Python API Library 1.0.10.5 documentation + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

SyncSketch Python API Library documentation

+

autodoc_member_order = ‘bysource’

+
+
+
+
+class syncsketch.SyncSketchAPI
+

SyncSketchAPI is a class that provides a set of methods to interact with SyncSketch API.

+
+
+__init__(
+
+auth,
+
+api_key,
+
+host="https://www.syncsketch.com",
+
+useExpiringToken=False,
+
+debug=False,
+
+api_version="v1",
+
+use_header_auth=False
+
+)
+

Constructor for SyncSketchAPI class.

+
+
Parameters:
+
    +
  • auth (str) – The username of the user.

  • +
  • api_key (str) – The api key of the user.

  • +
  • host (str) – The host of the SyncSketch API.

  • +
  • useExpiringToken (bool) – If True, the token will expire after 1 hour.

  • +
  • debug (bool) – If True, the debug mode will be enabled.

  • +
  • api_version (str) – The version of the SyncSketch API.

  • +
  • use_header_auth (bool) – If True, the authentication will be done using headers.

  • +
+
+
Returns:
+

SyncSketchAPI object.

+
+
Return type:
+

obj

+
+
+
+ +
+ +
+
+class syncsketch.SyncSketchAPI(auth, api_key, host='https://www.syncsketch.com', useExpiringToken=False, debug=False, api_version='v1', use_header_auth=False)
+

Bases: object

+

Convenience API to communicate with the SyncSketch Service for collaborative online reviews

+
+
+is_connected()
+

Convenience function to check if the API is connected to SyncSketch +Will check against Status Code 200 and return False if not which most likely would be +and authorization error

+
+
Returns:
+

Connection success

+
+
Return type:
+

bool

+
+
+
+ +
+
+get_tree(withItems=False)
+

Get nested tree of account, projects, reviews and optionally items for the current user

+
+
Parameters:
+

withItems (bool) – Include items in the response

+
+
Returns:
+

Tree data

+
+
Return type:
+

dict

+
+
+
+ +
+
+get_accounts()
+

Summary

+
+
Returns:
+

List of workspaces the user has access to

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+update_account(account_id, data)
+

Update a workspace / account

+
+
Parameters:
+
    +
  • account_id (int) – the id of the item

  • +
  • data (dict) – normal dict with data for item

  • +
+
+
Returns:
+

Workspace / Account data

+
+
Return type:
+

dict

+
+
+
+ +
+
+create_project(account_id, name, description='', data=None)
+

Add a project to your account. Please make sure to pass the accountId which you can query using the getAccounts command.

+
+
Parameters:
+
    +
  • account_id (int) – id of the account to connect with

  • +
  • name (str) – Name of the project

  • +
  • description (str) – Description of the project

  • +
  • data (dict) – additional information e.g is_public. Find out more about available fields at /api/v1/project/schema/.

  • +
+
+
Returns:
+

Project data

+
+
Return type:
+

dict

+
+
+
+ +
+
+get_projects(include_deleted=False, include_archived=False, include_tags=False, include_connections=False, limit=100, offset=0)
+

Get a list of currently active projects

+
+
Parameters:
+
    +
  • include_deleted (bool) – if true, include deleted projects

  • +
  • include_archived (bool) – if true, include archived projects

  • +
  • include_tags (bool) – if true, include tag list on the project object

  • +
  • include_connections (bool) – if true, include full user connections on the project object

  • +
  • limit (int) – limit the number of results

  • +
  • offset (int) – offset the results

  • +
+
+
Returns:
+

Dict with meta information and an array of found projects

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+get_projects_by_name(name)
+

Get a project by name regardless of status

+
+
Parameters:
+

name (str) – Name to search for

+
+
Returns:
+

List of projects

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+get_project_by_id(project_id)
+

Get single project by id

+
+
Parameters:
+

project_id (int) – Project id

+
+
Returns:
+

Project data

+
+
Return type:
+

dict

+
+
+
+ +
+
+get_project_storage(project_id)
+

Get project storage usage in bytes

+
+
Parameters:
+

project_id (int) – Project id

+
+
Returns:
+

Storage usage in bytes

+
+
+
+ +
+
+update_project(project_id, data)
+

Update a project

+
+
Parameters:
+
    +
  • project_id (int) – the id of the item

  • +
  • data (dict) – dict with new data for item

  • +
+
+
Returns:
+

updated project data

+
+
Return type:
+

dict

+
+
+
+ +
+
+delete_project(project_id)
+

Delete a project by id.

+
+
Parameters:
+

project_id (int) – Project ID to delete

+
+
Returns:
+

+
+
+
+ +
+
+duplicate_project(project_id, name=None, copy_reviews=False, copy_users=False, copy_settings=False)
+

Create a new project from an existing project

+
+
Parameters:
+
    +
  • project_id (int) – Source project id

  • +
  • name (str) – New project name

  • +
  • copy_reviews (bool) – Whether to copy reviews (without items)

  • +
  • copy_users (bool) – Whether to copy users

  • +
  • copy_settings (bool) – Whether to copy settings

  • +
+
+
Returns:
+

New project data

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+archive_project(project_id)
+

Archive a project

+
+
Parameters:
+

project_id (int)

+
+
Returns:
+

+
+
+
+ +
+
+restore_project(project_id)
+

Restore (unarchive) a project

+
+
Parameters:
+

project_id (int)

+
+
Returns:
+

+
+
+
+ +
+
+get_reviews_by_project_id(project_id, limit=100, offset=0)
+

Get list of reviews by project id.

+
+
Parameters:
+

project_id (int) – SyncSketch project id

+
+
Returns:
+

Dict with meta information and an array of found projects

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+get_review_by_name(name)
+

Get reviews by name using a case insensitive startswith query

+
+
Parameters:
+

name – String - Name of the review

+
+
Returns:
+

Dict with meta information and an array of found projects

+
+
+
+ +
+
+get_review_by_id(review_id)
+

Get single review by id.

+
+
Parameters:
+

review_id – Number

+
+
Returns:
+

Review Dict

+
+
+
+ +
+
+get_review_by_uuid(uuid)
+

Get single review by uuid. +UUID can be found in the review URL e.g. syncsketch.com/sketch/<uuid>/

+
+
Parameters:
+

uuid (str) – UUID of the review.

+
+
Returns:
+

Review dict

+
+
Return type:
+

dict

+
+
+
+ +
+
+get_review_storage(review_id)
+

Get review storage usage in bytes

+
+
Parameters:
+

review_id (int) – Review ID

+
+
Returns:
+

Storage usage in bytes

+
+
Return type:
+

int

+
+
+
+ +
+
+update_review(review_id, data)
+

Update a review

+
+
Parameters:
+
    +
  • review_id (int) – the id of the item

  • +
  • data (dict) – dict with data for item

  • +
+
+
Returns:
+

updated review data

+
+
Return type:
+

dict

+
+
+
+ +
+
+sort_review_items(review_id, items)
+

Update a review

+

Example items param

+
items = [{
+    "id": 1, # item id
+    "sortorder": 0, # sortorder, starting at 0
+}]
+
+
+

Method output example:

+
# number of successful items sort updated
+{ "updated_items": int }
+
+
+
+
Parameters:
+
    +
  • review_id (int) – the id of the item

  • +
  • items (list) – payload

  • +
+
+
Returns:
+

response

+
+
Return type:
+

dict

+
+
+
+ +
+
+archive_review(review_id)
+

Archive a review

+
+
Parameters:
+

review_id (int)

+
+
Returns:
+

empty response

+
+
+
+ +
+
+restore_review(review_id)
+

Restore (unarchive) a review

+
+
Parameters:
+

review_id (int)

+
+
Returns:
+

empty response

+
+
+
+ +
+
+delete_review(review_id)
+

Delete a review by id.

+
+
Parameters:
+

review_id (int) – Review ID to delete

+
+
Returns:
+

+
+
+
+ +
+
+update_item(item_id, data)
+

Update an item

+
+
Parameters:
+
    +
  • item_id (int) – the id of the item

  • +
  • data (dict) – dict with data for item

  • +
+
+
Returns:
+

updated item data

+
+
Return type:
+

dict

+
+
+
+ +
+
+add_item(review_id, name, fps, additional_data)
+

create a media item record and connect it to a review. This should be used in case you want to add items with externaly hosted +media by passing in the external_url and external_thumbnail_url to the additionalData dict e.g

+
additionalData = {
+    external_url: http://52.24.98.51/wp-content/uploads/2017/03/rain.jpg
+    external_thumbnail_url: http://52.24.98.51/wp-content/uploads/2017/03/rain.jpg
+}
+
+
+

or

+
additionalData = {
+    width:1024
+    height:720
+    artist: "Brady Endres"
+    duration:3 (in seconds)
+    description: the description here
+    size: size in byte
+    type: image | video
+}
+
+
+

NOTE: you always need to pass in FPS for SyncSketch to work!

+

For a complete list of available fields to set, please +visit https://www.syncsketch.com/api/v1/item/schema/

+
+
Parameters:
+
    +
  • review_id (int) – Required review_id

  • +
  • name (str) – Name of the item

  • +
  • fps (float) – The frame per second is very important for syncsketch to determine the correct number of frames

  • +
  • additional_data (dict) – dictionary with item info

  • +
+
+
Returns:
+

Item data

+
+
Return type:
+

dict

+
+
+
+ +
+
+add_media(review_id, filepath, artist_name='', file_name='', noConvertFlag=False, itemParentId=False)
+

Convenience function to upload a file to a review. It will automatically create +an Item and attach it to the review. NOTE - if you are hosting your own media, please +use the addItem function and pass in the external_url and external_thumbnail_url

+
+
Parameters:
+
    +
  • review_id (int) – Required review_id

  • +
  • filepath (str) – path for the file on disk e.g /tmp/movie.webm

  • +
  • artist_name (str) – The name of the artist you want associated with this media file

  • +
  • file_name (str) – The name of the file. Please make sure to pass the correct file extension

  • +
  • noConvertFlag (bool) – the video you are uploading is already in a browser compatible format

  • +
  • itemParentId (int) – (Optional) set when you want to add a new version of an item. itemParentId is the id of the item you want to upload a new version for

  • +
+
+
Returns:
+

Item data

+
+
Return type:
+

dict

+
+
+
+ +
+
+add_media_by_url(review_id, media_url, artist_name='', noConvertFlag=False)
+

Convenience function to upload a mediaURl to a review. Please use this function when you already have your files in the cloud, e.g +AWS, Dropbox, Shotgrid, etc…

+

We will automatically create an Item and attach it to the review.

+
+
Parameters:
+
    +
  • review_id (int) – Required review_id

  • +
  • media_url (str) – url to the media you are trying to upload

  • +
  • artist_name (str) – The name of the artist you want associated with this media file

  • +
  • noConvertFlag (bool) – the video you are uploading is already in a browser compatible format and does not need to be converted

  • +
+
+
Returns:
+

Item data

+
+
Return type:
+

dict

+
+
+
+ +
+
+add_media_v2(review_id, filepath, file_name='', item_uuid=None, noConvertFlag=False)
+

Similar to add_media method, but uploads the media file directly to SyncSketche’s internal S3 instead of to +the SyncSketch server. In some cases, using this method over add_media can improve upload performance and +stability. Unlike add_media this method does not return as much data about the created item.

+
+
Parameters:
+
    +
  • review_id (int) – Required review_id.

  • +
  • filepath (str) – path for the file on disk e.g /tmp/movie.webm.

  • +
  • file_name (str) – The name of the file. Please make sure to pass the correct file extension.

  • +
  • noConvertFlag (bool) – the video you are uploading is already in a browser compatible format.

  • +
+
+
Returns:
+

A dict, containing “item_id” and “uuid” or None on failure.

+
+
Return type:
+

Optional[dict]

+
+
+
+ +
+
+get_media(searchCriteria)
+

This is a general search function. You can search media items by

+

‘id’ +‘name’ +‘status’ +‘active’ +‘creator’: ALL_WITH_RELATIONS, <– these are foreign key queries +‘reviews’: ALL_WITH_RELATIONS, <– these are foreign key queries +‘created’ using ‘exact’, ‘range’, ‘gt’, ‘gte’, ‘lt’, ‘lte’

+

To query items by foreign keys please use the foreign key syntax described in the Django search definition: +https://docs.djangoproject.com/en/1.11/topics/db/queries/

+

If you want to query by “review name” for example you would pass in

+

reviews__name = NAME TO SEARCH

+

Using the “__” syntax you can even search for items by project like

+

reviews__project__name = $PROJECT NAME TO SEARCH

+

To speed up a query you can also pass in a limit e.g limit:10

+

results = s.getMedia({‘reviews__project__name’:’test’, ‘limit’: 1, ‘active’: 1})

+

NOTE: Please make sure to include the active:1 query if you only want active media. Deleted files are currently +only deactivated and kept for a certain period of time before they are “purged” from the system.

+
+
Parameters:
+

searchCriteria (dict) – Search params

+
+
Returns:
+

List of media items

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+get_items_by_review_id(review_id)
+

Get all items in a review

+
+
Parameters:
+

review_id (int) – Review ID

+
+
Returns:
+

List of media items

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+delete_item(item_id)
+

Delete a item by id.

+
+
Parameters:
+

item_id (int) – Item ID to delete

+
+
Returns:
+

+
+
+
+ +
+
+bulk_delete_items(item_ids)
+

Delete multiple items by id.

+
+
Parameters:
+

item_ids (list[int]) – List of item IDs to delete

+
+
Returns:
+

+
+
+
+ +
+
+move_items(new_review_id, item_data)
+

Move items from one review to another

+

item_data should be a list of dictionaries with the old review id and the item id. +The items in the list will be moved to the new review for the param new_review_id

+
# Example item_data
+# review_id is the current review an item is in
+# it will be moved to the new_review_id
+items_to_move = [
+    {"review_id": 1, "item_id": 1},
+    {"review_id": 1, "item_id": 2},
+    {"review_id": 1, "item_id": 3},
+]
+
+
+
+
Parameters:
+
    +
  • new_review_id (int) – The review id to move the items to

  • +
  • item_data (list[dict]) – List of dictionaries with the old review id and the item id

  • +
+
+
Returns:
+

+
+
+
+ +
+
+add_comment(item_id, text, review_id, frame=0)
+

Add a comment to an item

+
+
Parameters:
+
    +
  • item_id (int) – Item to add the comment to

  • +
  • text (str) – Comment text

  • +
  • review_id (int) – Review you are adding the comment to

  • +
  • frame (int) – Frame number of the video to add the comment to (if applicable)

  • +
+
+
Returns:
+

+
+
+
+ +
+
+get_annotations(item_id, revisionId=False, review_id=False)
+

Get sketches and comments for an item. Frames have a revision id which signifies a “set of notes”. +When querying an item you’ll get the available revisions for this item. If you wish to get only the latest +revision, please get the revisionId for the latest revision.

+
+
Parameters:
+
    +
  • item_id (int) – id of the media item you are querying.

  • +
  • revisionId (int) – Optional revisionId to narrow down the results

  • +
  • review_id (int) – RECOMMENDED - retrieve annotations for a specific review only.

  • +
+
+
Returns:
+

dict

+
+
+
+ +
+
+get_flattened_annotations(review_id, item_id, with_tracing_paper=False, return_as_base64=False)
+

Returns a list of sketches either as signed urls from s3 or base64 encoded strings. +The sketches are composited over the background frame of the item.

+
+
Parameters:
+
    +
  • review_id (int) – Review ID

  • +
  • item_id (int) – Item ID

  • +
  • with_tracing_paper (bool) – Include tracing paper in the response

  • +
  • return_as_base64 (bool) – Return sketches as base64 encoded strings

  • +
+
+
Returns:
+

List of sketches as signed urls from s3 or base64 encoded strings

+
+
+
+ +
+
+get_grease_pencil_overlays(review_id, item_id, homedir=None)
+

Download overlay sketches for Maya Greasepencil.

+

Download overlay sketches for Maya Greasepencil. Function will download +a zip file which contains an XML and the sketches as png files. Maya +can load the zip file to overlay the sketches over the 3D model!

+

For more information visit: +https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2015/ENU/Maya/files/Grease-Pencil-Tool-htm.html

+

PLEASE make sure that /tmp is writable

+
+
Parameters:
+
    +
  • review_id (int) – Review ID

  • +
  • item_id (int) – Item ID

  • +
  • homedir (str) – Optional path to download the zip file to

  • +
+
+
Returns:
+

filePath to the zip file with the greasePencil data.

+
+
+
+ +
+
+get_users_by_name(name)
+

Name is a combined search and will search in first_name, last_name and email

+
+
Parameters:
+

name (str) – Name to search for

+
+
Returns:
+

List of users

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+get_user_by_email(email)
+

Get user by email

+
+
Parameters:
+

email (str) – Email to search for

+
+
Returns:
+

User data

+
+
Return type:
+

dict

+
+
+
+ +
+
+get_users_by_project_id(project_id)
+

Get all users in a project

+
+
Parameters:
+

project_id (int)

+
+
Returns:
+

List of users

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+get_connections_by_user_id(user_id, account_id, include_inactive=None, include_archived=None)
+

Get all project and account connections for a user. Good for checking access for a user that might have left…

+
+
Parameters:
+
    +
  • user_id (int) – User ID to get connections for

  • +
  • account_id (int) – Account ID to get connections for

  • +
  • include_inactive (bool) – Include inactive projects

  • +
  • include_archived (bool) – Include archived projects

  • +
+
+
Returns:
+

List of connections

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+get_user_by_id(user_id)
+

Get a user by ID

+
+
Parameters:
+

user_id (int)

+
+
Returns:
+

User data

+
+
Return type:
+

dict

+
+
+
+ +
+
+add_users_to_workspace(workspace_id, users, note='')
+

Add Users to Workspace

+
users=[{"email":"test@test.de","permission":"admin"}]
+
+
+
+
Parameters:
+
    +
  • workspace_id (int) – id of the workspace

  • +
  • users (list) – list of new users - possible permissions “admin”, “manager”

  • +
  • note (str) – (Optional) message for the invitation email

  • +
+
+
Returns:
+

response

+
+
+
+ +
+
+remove_users_from_workspace(workspace_id, users)
+

Remove a list of users from a workspace +Can remove by id or email

+
users=[{"email":"test@test.de"}, {"id":12345}]
+
+
+
+
Parameters:
+
    +
  • workspace_id (int) – id of the workspace

  • +
  • users (list) – list of users to remove - either remove by user email or id

  • +
+
+
Returns:
+

response

+
+
+
+ +
+
+add_users_to_project(project_id, users, note='')
+

Add Users to Project

+

possible permissions

+
    +
  • admin

  • +
  • member

  • +
  • viewer

  • +
  • reviewer

  • +
+
users=[{"email":"test@test.de","permission":"viewer"}]
+
+
+
+
Parameters:
+
    +
  • project_id (int) – id of the project

  • +
  • users (list[dict]) – list of new users

  • +
  • note (str) – (Optional) message for the invitation email

  • +
+
+
Returns:
+

response

+
+
+
+ +
+
+remove_users_from_project(project_id, users)
+

Remove a list of users from a project

+

remove by user email or id

+
users=[{"email":"test@test.de"}, {"id":12345}]
+
+
+
+
Parameters:
+
    +
  • project_id (int) – id of the project

  • +
  • users (list) – list of users to remove - either remove by user email or id

  • +
+
+
+
+ +
+
+shotgrid_create_config(syncsketch_account_id, syncsketch_project_id=None, data=None)
+

Create a new Shotgrid configuration for a SyncSketch workspace and optionally a project

+
+
Parameters:
+
    +
  • syncsketch_account_id (int)

  • +
  • syncsketch_project_id (int)

  • +
  • data (dict) – Configuration data.

  • +
+
+
Returns:
+

+
+
+
+ +
+
+shotgrid_get_playlists(syncsketch_account_id, syncsketch_project_id, shotgun_project_id=None)
+

Returns list of Shotgrid playlists modified in the last 120 days +If the syncsketch project is directly linked to a shotgrid by the workspace admin, the +param shotgun_project_id will be ignored and can be omitted during the function call

+
+
Parameters:
+
    +
  • syncsketch_account_id (int) – SyncSketch account id

  • +
  • syncsketch_project_id (int) – SyncSketch project id

  • +
  • shotgun_project_id (int) – (optional) Shotgrid project id

  • +
+
+
Returns:
+

list of Shotgrid playlists

+
+
+
+ +
+
+shotgrid_sync_review_notes(review_id)
+

Sync notes from SyncSketch review to the original shotgrid playlist +Returns task id to use in get_shotgun_sync_review_notes_progress to get progress

+

returns dict with information about the REST API call:

+
    +
  • message=<STR> “Shotgrid review notes sync started”

  • +
  • status=<STR> processing/done/failed

  • +
  • progress_url=<STR> Full url to call for progress/results

  • +
  • task_id=<STR> task_ids pass this value to the get_shotgun_sync_review_items_progress function

  • +
  • percent_complete=<INT> 0-100 value of percent complete

  • +
  • total_items=<INT> number of items being synced from shotgrid

  • +
  • remaining_items=<INT> number of items not yet pulled from shotgrid

  • +
+
+
Parameters:
+

review_id (int) – SyncSketch review id

+
+
Returns:
+

Progress information

+
+
Return type:
+

dict

+
+
+
+ +
+
+shotgrid_sync_new_item_notes(project_id, review_id, item_id)
+

Sync new notes from SyncSketch review item to the original shotgrid playlist +Returns dict with information about the REST API call

+
    +
  • sketch_upload_error=<BOOL> “True in case of error”

  • +
  • sketches=<INT> “Number of sketches synced”

  • +
  • comments=<INT> “Number of comments synced”

  • +
  • attachments=<INT> “Number of attachments synced”

  • +
  • item_name=<STR> “Name of item that was synced”

  • +
+
+
Parameters:
+
    +
  • project_id (int) – SyncSketch project id

  • +
  • review_id (int) – SyncSketch review id

  • +
  • item_id (int) – SyncSketch item id

  • +
+
+
Returns:
+

+
+
+
+ +
+
+get_shotgrid_sync_review_notes_progress(task_id)
+

Returns status of review notes sync for the task id provided in shotgun_sync_review_notes

+

Returns a dict with the following keys:

+
    +
  • message=<STR> “Shotgrid review notes sync started”

  • +
  • status=<STR> processing/done/failed

  • +
  • progress_url=<STR> Full url to call for progress/results

  • +
  • task_id=<STR> task_ids pass this value to the get_shotgun_sync_review_items_progress function

  • +
  • percent_complete=<INT> 0-100 value of percent complete

  • +
  • total_items=<INT> number of items being synced from shotgrid

  • +
  • remaining_items=<INT> number of items not yet pulled from shotgrid

  • +
+
+
Parameters:
+

task_id (str) – UUID of the task returned by shotgrid_sync_review_notes

+
+
Returns:
+

Progress information

+
+
Return type:
+

dict

+
+
+
+ +
+
+shotgrid_sync_review_items(syncsketch_project_id, playlist_code, playlist_id, review_id=None)
+

Create or update SyncSketch review with shotgrid playlist items +Returns task id to use in get_shotgun_sync_review_items_progress to get progress

+

Response format:

+
    +
  • message=<STR> “Shotgrid review item sync started”,

  • +
  • status=<STR> processing/done/failed,

  • +
  • progress_url=<STR> Full url to call for progress/results,

  • +
  • task_id=<STR> task_ids - pass this value to the get_shotgun_sync_review_items_progress function,

  • +
  • percent_complete=<INT> 0-100 value of percent complete,

  • +
  • total_items=<INT> number of items being synced from shotgrid,

  • +
  • remaining_items=<INT> number of items not yet pulled from shotgrid,

  • +
  • data=<dict>

  • +
  • review_id=<INT> review.id,

  • +
  • review_link=<STR> url link to the syncsketch player with the review pulled from shotgrid,

  • +
+
+
Parameters:
+
    +
  • syncsketch_project_id (int)

  • +
  • playlist_code (str)

  • +
  • playlist_id (int)

  • +
  • review_id (int) – (optional)

  • +
+
+
Returns:
+

+
+
Return type:
+

dict

+
+
+
+ +
+
+get_shotgrid_sync_review_items_progress(task_id)
+

Returns status of review items sync for the task id provided in shotgun_sync_review_items

+
+
Parameters:
+

task_id (str) – UUID of the task returned by shotgrid_sync_review_items

+
+
Returns:
+

DeprecationWarning

+
+
Return type:
+

dict

+
+
+
+ +
+
+add_media_v1(review_id, filepath, artist_name='', file_name='', noConvertFlag=False, itemParentId=False)
+

Convenience function to upload a file to a review. It will automatically create +an Item and attach it to the review. NOTE - if you are hosting your own media, please +use the addItem function and pass in the external_url and external_thumbnail_url

+
+
Parameters:
+
    +
  • review_id (int) – Required review_id

  • +
  • filepath (str) – path for the file on disk e.g /tmp/movie.webm

  • +
  • artist_name (str) – The name of the artist you want associated with this media file

  • +
  • file_name (str) – The name of the file. Please make sure to pass the correct file extension

  • +
  • noConvertFlag (bool) – the video you are uploading is already in a browser compatible format

  • +
  • itemParentId (int) – (Optional) set when you want to add a new version of an item. itemParentId is the id of the item you want to upload a new version for

  • +
+
+
Returns:
+

Item data

+
+
Return type:
+

dict

+
+
+
+ +
+
+get_shotgun_sync_review_items_progress(task_id)
+

Returns status of review items sync for the task id provided in shotgun_sync_review_items

+
+
Parameters:
+

task_id (str) – UUID of the task returned by shotgrid_sync_review_items

+
+
Returns:
+

DeprecationWarning

+
+
Return type:
+

dict

+
+
+
+ +
+
+shotgun_sync_review_items(syncsketch_project_id, playlist_code, playlist_id, review_id=None)
+

Create or update SyncSketch review with shotgrid playlist items +Returns task id to use in get_shotgun_sync_review_items_progress to get progress

+

Response format:

+
    +
  • message=<STR> “Shotgrid review item sync started”,

  • +
  • status=<STR> processing/done/failed,

  • +
  • progress_url=<STR> Full url to call for progress/results,

  • +
  • task_id=<STR> task_ids - pass this value to the get_shotgun_sync_review_items_progress function,

  • +
  • percent_complete=<INT> 0-100 value of percent complete,

  • +
  • total_items=<INT> number of items being synced from shotgrid,

  • +
  • remaining_items=<INT> number of items not yet pulled from shotgrid,

  • +
  • data=<dict>

  • +
  • review_id=<INT> review.id,

  • +
  • review_link=<STR> url link to the syncsketch player with the review pulled from shotgrid,

  • +
+
+
Parameters:
+
    +
  • syncsketch_project_id (int)

  • +
  • playlist_code (str)

  • +
  • playlist_id (int)

  • +
  • review_id (int) – (optional)

  • +
+
+
Returns:
+

+
+
Return type:
+

dict

+
+
+
+ +
+
+shotgun_sync_new_item_notes(project_id, review_id, item_id)
+

Sync new notes from SyncSketch review item to the original shotgrid playlist +Returns dict with information about the REST API call

+
    +
  • sketch_upload_error=<BOOL> “True in case of error”

  • +
  • sketches=<INT> “Number of sketches synced”

  • +
  • comments=<INT> “Number of comments synced”

  • +
  • attachments=<INT> “Number of attachments synced”

  • +
  • item_name=<STR> “Name of item that was synced”

  • +
+
+
Parameters:
+
    +
  • project_id (int) – SyncSketch project id

  • +
  • review_id (int) – SyncSketch review id

  • +
  • item_id (int) – SyncSketch item id

  • +
+
+
Returns:
+

+
+
+
+ +
+
+shotgun_sync_review_notes(review_id)
+

Sync notes from SyncSketch review to the original shotgrid playlist +Returns task id to use in get_shotgun_sync_review_notes_progress to get progress

+

returns dict with information about the REST API call:

+
    +
  • message=<STR> “Shotgrid review notes sync started”

  • +
  • status=<STR> processing/done/failed

  • +
  • progress_url=<STR> Full url to call for progress/results

  • +
  • task_id=<STR> task_ids pass this value to the get_shotgun_sync_review_items_progress function

  • +
  • percent_complete=<INT> 0-100 value of percent complete

  • +
  • total_items=<INT> number of items being synced from shotgrid

  • +
  • remaining_items=<INT> number of items not yet pulled from shotgrid

  • +
+
+
Parameters:
+

review_id (int) – SyncSketch review id

+
+
Returns:
+

Progress information

+
+
Return type:
+

dict

+
+
+
+ +
+
+shotgun_get_playlists(syncsketch_account_id, syncsketch_project_id, shotgun_project_id=None)
+

Returns list of Shotgrid playlists modified in the last 120 days +If the syncsketch project is directly linked to a shotgrid by the workspace admin, the +param shotgun_project_id will be ignored and can be omitted during the function call

+
+
Parameters:
+
    +
  • syncsketch_account_id (int) – SyncSketch account id

  • +
  • syncsketch_project_id (int) – SyncSketch project id

  • +
  • shotgun_project_id (int) – (optional) Shotgrid project id

  • +
+
+
Returns:
+

list of Shotgrid playlists

+
+
+
+ +
+
+shotgun_create_config(syncsketch_account_id, syncsketch_project_id=None, data=None)
+

Create a new Shotgrid configuration for a SyncSketch workspace and optionally a project

+
+
Parameters:
+
    +
  • syncsketch_account_id (int)

  • +
  • syncsketch_project_id (int)

  • +
  • data (dict) – Configuration data.

  • +
+
+
Returns:
+

+
+
+
+ +
+
+shotgun_get_projects(syncsketch_project_id)
+

Returns list of Shotgrid projects connected to your account

+
+
Parameters:
+

syncsketch_project_id (int) – SyncSketch project id

+
+
+
+ +
+ +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/_build/html/objects.inv b/docs/_build/html/objects.inv new file mode 100644 index 0000000000000000000000000000000000000000..62892ee28cc0cce8bd2b6ad174002af709dd0a5e GIT binary patch literal 718 zcmV;<0x|s~AX9K?X>NERX>N99Zgg*Qc_4OWa&u{KZXhxWBOp+6Z)#;@bUGkYd2VA< zYh`p}XdqB|bZBpGAVE+`AWUgua$$0L3L_v^WpZ8b#rNMXCQiPX<{x4c-pm?OK%$?5XbNS z6iDsWmYid5IYw$FOZfl@!+4jP$BKv3{q_Nt^+Qb+m7zDpGC%$>!!WF$4fkgVeB8aI z=38mL9A3#ZKXb*rCwn4|fZw|@X!Y$o*>3z_Om<8N%B|L5LN?bBMl1f&UIx(3#u|iV zIBR;GX>&|{+9NJKhkMaUIIhEnyBH_IWag6Fr z1tT6bk_ih%VxW6mvdpG#D-wwHI)T&F#1n{CSm&`!VZm&$rpSMoyN3(6CZ` zu#OFY<#;q8ug5UmuB7?}w=FkFCoM7J)nI+JV9E}!ZbyjnqYZxAGaKVQDm4Xa4>DTK8+5kGGd_>9Opa7QT zoSfCDEBj9xt~yNNnvwz_H7yK8J##&;wZKarJIMiuDwjgKs+IJO*UDoSG4-4;d87mI zK6muPA=RbC*2xEn)X4=a2~#3GWbIzA{OlI!Mnh<`F5orAN#ro74+IRN`K?v$G0CYS zzRpbpn9@Uh5YLJoL6PmAti5F%dCkquZJ4k;54sz1o17fxG?5|sXU-3Kbj-~pjgR^F zOnH%Pv=yJsIL+Jj!-D*2MO0AdzWe%xS)KmD+CzTHKN>E5RsHh)kc!032N(dpr=y)s A@Bjb+ literal 0 HcmV?d00001 diff --git a/docs/_build/html/py-modindex.html b/docs/_build/html/py-modindex.html new file mode 100644 index 0000000..328f756 --- /dev/null +++ b/docs/_build/html/py-modindex.html @@ -0,0 +1,117 @@ + + + + + + + Python Module Index — SyncSketch Python API Library 1.0.10.5 documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + +

Python Module Index

+ +
+ s +
+ + + + + + + +
 
+ s
+ syncsketch +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/_build/html/search.html b/docs/_build/html/search.html new file mode 100644 index 0000000..7744bca --- /dev/null +++ b/docs/_build/html/search.html @@ -0,0 +1,116 @@ + + + + + + + Search — SyncSketch Python API Library 1.0.10.5 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Search

+ + + + +

+ Searching for multiple words only shows matches that contain + all words. +

+ + +
+ + + +
+ + +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/_build/html/searchindex.js b/docs/_build/html/searchindex.js new file mode 100644 index 0000000..f1038f7 --- /dev/null +++ b/docs/_build/html/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"alltitles": {"SyncSketch Python API Library documentation": [[0, null]]}, "docnames": ["index"], "envversion": {"sphinx": 62, "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}, "filenames": ["index.rst"], "indexentries": {"add_comment() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.add_comment", false]], "add_item() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.add_item", false]], "add_media() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.add_media", false]], "add_media_by_url() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.add_media_by_url", false]], "add_media_v1() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.add_media_v1", false]], "add_media_v2() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.add_media_v2", false]], "add_users_to_project() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.add_users_to_project", false]], "add_users_to_workspace() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.add_users_to_workspace", false]], "archive_project() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.archive_project", false]], "archive_review() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.archive_review", false]], "bulk_delete_items() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.bulk_delete_items", false]], "create_project() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.create_project", false]], "delete_item() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.delete_item", false]], "delete_project() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.delete_project", false]], "delete_review() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.delete_review", false]], "duplicate_project() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.duplicate_project", false]], "get_accounts() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_accounts", false]], "get_annotations() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_annotations", false]], "get_connections_by_user_id() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_connections_by_user_id", false]], "get_flattened_annotations() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_flattened_annotations", false]], "get_grease_pencil_overlays() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_grease_pencil_overlays", false]], "get_items_by_review_id() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_items_by_review_id", false]], "get_media() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_media", false]], "get_project_by_id() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_project_by_id", false]], "get_project_storage() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_project_storage", false]], "get_projects() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_projects", false]], "get_projects_by_name() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_projects_by_name", false]], "get_review_by_id() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_review_by_id", false]], "get_review_by_name() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_review_by_name", false]], "get_review_by_uuid() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_review_by_uuid", false]], "get_review_storage() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_review_storage", false]], "get_reviews_by_project_id() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_reviews_by_project_id", false]], "get_shotgrid_sync_review_items_progress() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_shotgrid_sync_review_items_progress", false]], "get_shotgrid_sync_review_notes_progress() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_shotgrid_sync_review_notes_progress", false]], "get_shotgun_sync_review_items_progress() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_shotgun_sync_review_items_progress", false]], "get_tree() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_tree", false]], "get_user_by_email() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_user_by_email", false]], "get_user_by_id() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_user_by_id", false]], "get_users_by_name() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_users_by_name", false]], "get_users_by_project_id() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_users_by_project_id", false]], "is_connected() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.is_connected", false]], "move_items() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.move_items", false]], "remove_users_from_project() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.remove_users_from_project", false]], "remove_users_from_workspace() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.remove_users_from_workspace", false]], "restore_project() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.restore_project", false]], "restore_review() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.restore_review", false]], "shotgrid_create_config() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgrid_create_config", false]], "shotgrid_get_playlists() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgrid_get_playlists", false]], "shotgrid_sync_new_item_notes() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgrid_sync_new_item_notes", false]], "shotgrid_sync_review_items() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgrid_sync_review_items", false]], "shotgrid_sync_review_notes() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgrid_sync_review_notes", false]], "shotgun_create_config() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgun_create_config", false]], "shotgun_get_playlists() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgun_get_playlists", false]], "shotgun_get_projects() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgun_get_projects", false]], "shotgun_sync_new_item_notes() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgun_sync_new_item_notes", false]], "shotgun_sync_review_items() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgun_sync_review_items", false]], "shotgun_sync_review_notes() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgun_sync_review_notes", false]], "sort_review_items() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.sort_review_items", false]], "syncsketchapi (class in syncsketch)": [[0, "syncsketch.SyncSketchAPI", false]], "update_account() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.update_account", false]], "update_item() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.update_item", false]], "update_project() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.update_project", false]], "update_review() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.update_review", false]]}, "objects": {"syncsketch": [[0, 0, 1, "", "SyncSketchAPI"]], "syncsketch.SyncSketchAPI": [[0, 1, 1, "", "add_comment"], [0, 1, 1, "", "add_item"], [0, 1, 1, "", "add_media"], [0, 1, 1, "", "add_media_by_url"], [0, 1, 1, "", "add_media_v1"], [0, 1, 1, "", "add_media_v2"], [0, 1, 1, "", "add_users_to_project"], [0, 1, 1, "", "add_users_to_workspace"], [0, 1, 1, "", "archive_project"], [0, 1, 1, "", "archive_review"], [0, 1, 1, "", "bulk_delete_items"], [0, 1, 1, "", "create_project"], [0, 1, 1, "", "delete_item"], [0, 1, 1, "", "delete_project"], [0, 1, 1, "", "delete_review"], [0, 1, 1, "", "duplicate_project"], [0, 1, 1, "", "get_accounts"], [0, 1, 1, "", "get_annotations"], [0, 1, 1, "", "get_connections_by_user_id"], [0, 1, 1, "", "get_flattened_annotations"], [0, 1, 1, "", "get_grease_pencil_overlays"], [0, 1, 1, "", "get_items_by_review_id"], [0, 1, 1, "", "get_media"], [0, 1, 1, "", "get_project_by_id"], [0, 1, 1, "", "get_project_storage"], [0, 1, 1, "", "get_projects"], [0, 1, 1, "", "get_projects_by_name"], [0, 1, 1, "", "get_review_by_id"], [0, 1, 1, "", "get_review_by_name"], [0, 1, 1, "", "get_review_by_uuid"], [0, 1, 1, "", "get_review_storage"], [0, 1, 1, "", "get_reviews_by_project_id"], [0, 1, 1, "", "get_shotgrid_sync_review_items_progress"], [0, 1, 1, "", "get_shotgrid_sync_review_notes_progress"], [0, 1, 1, "", "get_shotgun_sync_review_items_progress"], [0, 1, 1, "", "get_tree"], [0, 1, 1, "", "get_user_by_email"], [0, 1, 1, "", "get_user_by_id"], [0, 1, 1, "", "get_users_by_name"], [0, 1, 1, "", "get_users_by_project_id"], [0, 1, 1, "", "is_connected"], [0, 1, 1, "", "move_items"], [0, 1, 1, "", "remove_users_from_project"], [0, 1, 1, "", "remove_users_from_workspace"], [0, 1, 1, "", "restore_project"], [0, 1, 1, "", "restore_review"], [0, 1, 1, "", "shotgrid_create_config"], [0, 1, 1, "", "shotgrid_get_playlists"], [0, 1, 1, "", "shotgrid_sync_new_item_notes"], [0, 1, 1, "", "shotgrid_sync_review_items"], [0, 1, 1, "", "shotgrid_sync_review_notes"], [0, 1, 1, "", "shotgun_create_config"], [0, 1, 1, "", "shotgun_get_playlists"], [0, 1, 1, "", "shotgun_get_projects"], [0, 1, 1, "", "shotgun_sync_new_item_notes"], [0, 1, 1, "", "shotgun_sync_review_items"], [0, 1, 1, "", "shotgun_sync_review_notes"], [0, 1, 1, "", "sort_review_items"], [0, 1, 1, "", "update_account"], [0, 1, 1, "", "update_item"], [0, 1, 1, "", "update_project"], [0, 1, 1, "", "update_review"]]}, "objnames": {"0": ["py", "class", "Python class"], "1": ["py", "method", "Python method"]}, "objtypes": {"0": "py:class", "1": "py:method"}, "terms": {"": 0, "0": 0, "03": 0, "1": 0, "10": 0, "100": 0, "1024": 0, "11": 0, "120": 0, "123": [], "12345": 0, "2": 0, "200": 0, "2015": 0, "2017": 0, "24": 0, "3": 0, "3d": 0, "51": 0, "52": 0, "720": 0, "98": 0, "A": 0, "For": 0, "If": 0, "In": 0, "It": 0, "No": [], "TO": 0, "The": 0, "To": 0, "Will": 0, "__": 0, "__init__": 0, "abc": [], "about": 0, "access": 0, "account": 0, "account_id": 0, "accountid": 0, "activ": 0, "ad": 0, "add": 0, "add_com": 0, "add_item": 0, "add_media": 0, "add_media_by_url": 0, "add_media_v1": 0, "add_media_v2": 0, "add_us": [], "add_users_to_project": 0, "add_users_to_workspac": 0, "addcom": [], "addit": 0, "additem": 0, "additional_data": 0, "additionaldata": 0, "addmedia": [], "addmediabyurl": [], "addproject": [], "addreview": [], "addus": [], "admin": 0, "after": 0, "against": 0, "all": 0, "all_with_rel": 0, "alreadi": 0, "also": 0, "alwai": 0, "an": 0, "ani": 0, "annot": 0, "anoth": 0, "api_kei": 0, "api_secret": [], "api_url": [], "api_vers": 0, "appen": [], "append": [], "applic": 0, "ar": 0, "archiv": 0, "archive_project": 0, "archive_review": 0, "arg": [], "arrai": 0, "artist": 0, "artist_nam": 0, "associ": 0, "attach": 0, "auth": 0, "authent": 0, "author": 0, "autodesk": 0, "autodoc_member_ord": 0, "automat": 0, "avail": 0, "aw": 0, "background": 0, "base": 0, "base64": 0, "befor": 0, "behind": [], "being": 0, "bool": 0, "boolean": [], "bradi": 0, "browser": 0, "bulk_delete_item": 0, "bysourc": 0, "byte": 0, "caa": 0, "call": 0, "can": 0, "case": 0, "certain": 0, "check": 0, "class": 0, "cloud": 0, "cloudhelp": 0, "code": 0, "collabor": 0, "com": 0, "combin": 0, "command": 0, "comment": 0, "commun": 0, "compat": 0, "complet": 0, "composit": 0, "configur": 0, "connect": 0, "connect_item_to_review": [], "connectitemtoreview": [], "constructor": 0, "contain": 0, "content": 0, "conveni": 0, "convert": 0, "copi": 0, "copy_review": 0, "copy_set": 0, "copy_us": 0, "correct": 0, "creat": 0, "create_project": 0, "create_review": [], "creator": 0, "current": 0, "dai": 0, "data": 0, "db": 0, "de": 0, "deactiv": 0, "debug": 0, "definit": 0, "delet": 0, "delete_item": 0, "delete_project": 0, "delete_review": 0, "deleteitem": [], "deleteproject": [], "deletereview": [], "delimit": [], "deprec": [], "deprecationwarn": 0, "describ": 0, "descript": 0, "detail": [], "determin": 0, "dict": 0, "dictionari": 0, "directli": 0, "disk": 0, "django": 0, "djangoproject": 0, "doc": 0, "doe": 0, "done": 0, "down": 0, "download": 0, "dropbox": 0, "duplicate_project": 0, "durat": 0, "dure": 0, "e": 0, "either": 0, "email": 0, "empti": 0, "en": 0, "enabl": 0, "encod": 0, "endr": 0, "enu": 0, "error": 0, "etc": 0, "even": 0, "exact": 0, "exampl": 0, "exist": 0, "expir": 0, "explor": 0, "extens": 0, "external_thumbnail_url": 0, "external_url": 0, "externali": 0, "fail": 0, "failur": 0, "fals": 0, "field": 0, "file": 0, "file_nam": 0, "filepath": 0, "find": 0, "first_nam": 0, "float": 0, "follow": 0, "foreign": 0, "format": 0, "found": 0, "fp": 0, "frame": 0, "from": 0, "full": 0, "function": 0, "g": 0, "gener": 0, "get": 0, "get_account": 0, "get_annot": 0, "get_api_base_url": [], "get_connections_by_user_id": 0, "get_current_us": [], "get_flattened_annot": 0, "get_grease_pencil_overlai": 0, "get_item": [], "get_items_by_review_id": 0, "get_media": 0, "get_media_by_review_id": [], "get_project": 0, "get_project_by_id": 0, "get_project_storag": 0, "get_projects_by_nam": 0, "get_review_by_id": 0, "get_review_by_nam": 0, "get_review_by_uuid": 0, "get_review_storag": 0, "get_reviews_by_project_id": 0, "get_shotgrid_sync_review_items_progress": 0, "get_shotgrid_sync_review_notes_progress": 0, "get_shotgun_sync_review_items_progress": 0, "get_shotgun_sync_review_notes_progress": 0, "get_tre": 0, "get_user_by_email": 0, "get_user_by_id": 0, "get_users_by_nam": 0, "get_users_by_project_id": 0, "getaccount": 0, "getannot": [], "getcurrentus": [], "getgreasepenciloverlai": [], "getitem": [], "getmedia": 0, "getmediabyreviewid": [], "getproject": [], "getprojectbyid": [], "getprojectsbynam": [], "getreviewbyid": [], "getreviewbynam": [], "getreviewsbyprojectid": [], "gettoken": [], "gettre": [], "getuserbyid": [], "getusersbynam": [], "getusersbyprojectid": [], "good": 0, "greas": 0, "greasepencil": 0, "gt": 0, "gte": 0, "ha": 0, "handl": [], "have": 0, "header": 0, "height": 0, "here": 0, "homedir": 0, "host": 0, "hour": 0, "htm": 0, "html": 0, "http": 0, "i": 0, "id": 0, "ignor": 0, "imag": 0, "import": 0, "improv": 0, "inact": 0, "includ": 0, "include_archiv": 0, "include_connect": 0, "include_delet": 0, "include_inact": 0, "include_tag": 0, "index": [], "info": 0, "inform": 0, "insensit": 0, "instal": [], "instead": 0, "int": 0, "interact": 0, "intern": 0, "invit": 0, "is_connect": 0, "is_publ": 0, "isconnect": [], "item": 0, "item_data": 0, "item_id": 0, "item_nam": 0, "item_uuid": 0, "itemparentid": 0, "items_to_mov": 0, "join_url_path": [], "jpg": 0, "json": [], "kei": 0, "kept": 0, "knowledg": 0, "last": 0, "last_nam": 0, "latest": 0, "learn": 0, "left": 0, "like": 0, "limit": 0, "link": 0, "list": 0, "ll": 0, "load": 0, "local": [], "lt": 0, "lte": 0, "make": 0, "manag": 0, "maya": 0, "media": 0, "media_url": 0, "mediaurl": 0, "member": 0, "messag": 0, "meta": 0, "method": 0, "might": 0, "mode": 0, "model": 0, "modifi": 0, "more": 0, "most": 0, "move": 0, "move_item": 0, "movi": 0, "much": 0, "multipl": 0, "name": 0, "narrow": 0, "need": 0, "nest": 0, "new": 0, "new_review_id": 0, "noconvertflag": 0, "none": 0, "normal": 0, "note": 0, "number": 0, "obj": 0, "object": 0, "offset": 0, "old": 0, "omit": 0, "one": 0, "onli": 0, "onlin": 0, "option": 0, "origin": 0, "out": 0, "output": 0, "over": 0, "overlai": 0, "own": 0, "paper": 0, "param": 0, "paramet": 0, "pass": 0, "path": 0, "path_seg": [], "payload": 0, "pencil": 0, "per": 0, "percent": 0, "percent_complet": 0, "perform": 0, "period": 0, "permiss": 0, "player": 0, "playlist": 0, "playlist_cod": 0, "playlist_id": 0, "pleas": 0, "png": 0, "possibl": 0, "prefix": [], "print": [], "process": 0, "progress": 0, "progress_url": 0, "project": 0, "project_id": 0, "properli": [], "provid": 0, "pull": 0, "purg": 0, "queri": 0, "rain": 0, "rang": 0, "recommend": 0, "record": 0, "regardless": 0, "remaining_item": 0, "remov": 0, "remove_users_from_project": 0, "remove_users_from_workspac": 0, "requir": 0, "respons": 0, "rest": 0, "restor": 0, "restore_project": 0, "restore_review": 0, "restructuredtext": [], "result": 0, "retriev": 0, "return": 0, "return_as_base64": 0, "review": 0, "review_id": 0, "review_link": 0, "reviews__nam": 0, "reviews__project__nam": 0, "revis": 0, "revisionid": 0, "rtype": [], "s3": 0, "schema": 0, "search": 0, "searchcriteria": 0, "second": 0, "secret": [], "see": [], "self": [], "server": 0, "servic": 0, "set": 0, "setup": [], "shotgrid": 0, "shotgrid_create_config": 0, "shotgrid_get_playlist": 0, "shotgrid_get_project": [], "shotgrid_sync_new_item_not": 0, "shotgrid_sync_review_item": 0, "shotgrid_sync_review_not": 0, "shotgun_create_config": 0, "shotgun_get_playlist": 0, "shotgun_get_project": 0, "shotgun_project_id": 0, "shotgun_sync_new_item_not": 0, "shotgun_sync_review_item": 0, "shotgun_sync_review_not": 0, "should": 0, "sign": 0, "signifi": 0, "similar": 0, "singl": 0, "size": 0, "sketch": 0, "sketch_upload_error": 0, "some": 0, "sort": 0, "sort_review_item": 0, "sortord": 0, "sourc": 0, "specif": 0, "speed": 0, "stabil": 0, "start": 0, "startswith": 0, "static": [], "statu": 0, "storag": 0, "str": 0, "string": 0, "success": 0, "summari": 0, "support": 0, "sure": 0, "sync": 0, "syncsketch_account_id": 0, "syncsketch_item_id": [], "syncsketch_project_id": 0, "syncsketch_review_id": [], "syncsketchapi": 0, "syntax": 0, "system": 0, "tab": [], "tag": 0, "take": [], "task": 0, "task_id": 0, "termin": [], "test": 0, "text": 0, "thei": 0, "thi": 0, "time": 0, "tmp": 0, "token": 0, "tool": 0, "topic": 0, "total_item": 0, "trace": 0, "tree": 0, "true": 0, "try": 0, "type": 0, "unarch": 0, "unlik": 0, "up": 0, "updat": 0, "update_account": 0, "update_item": 0, "update_project": 0, "update_review": 0, "updated_item": 0, "updateitem": [], "upload": 0, "url": 0, "us": 0, "usag": 0, "use_header_auth": 0, "useexpiringtoken": 0, "user": 0, "user_auth": [], "user_id": 0, "userid": [], "usernam": 0, "uuid": 0, "v1": 0, "v2": [], "valu": 0, "veri": 0, "version": 0, "video": 0, "viewer": 0, "visit": 0, "wa": 0, "want": 0, "we": 0, "webm": 0, "when": 0, "whether": 0, "which": 0, "width": 0, "wish": 0, "with_tracing_pap": 0, "withitem": 0, "without": 0, "work": 0, "workspac": 0, "workspace_id": 0, "would": 0, "wp": 0, "writabl": 0, "www": 0, "xml": 0, "xyz": [], "yet": 0, "you": 0, "your": 0, "zip": 0}, "titles": ["SyncSketch Python API Library documentation"], "titleterms": {"api": 0, "document": 0, "librari": 0, "python": 0, "syncsketch": 0}}) \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..97a4cf5 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,31 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +import os, sys + +sys.path.insert(0, os.path.abspath('../syncsketch')) + +project = 'SyncSketch Python API Library' +copyright = '2024, Brady Endres, Phil Floetotto, Nicholas Kegler dos Santos, Tyler Nickerson' +author = 'Brady Endres, Phil Floetotto, Nicholas Kegler dos Santos, Tyler Nickerson' +release = '1.0.10.5' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = ['sphinx.ext.autodoc'] + +templates_path = ['_templates'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'alabaster' +html_static_path = ['_static'] diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..10ef73f --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,46 @@ +.. SyncSketch Python API Library documentation master file, created by + sphinx-quickstart on Thu Jul 25 15:46:05 2024. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +SyncSketch Python API Library documentation +=========================================== + +autodoc_member_order = 'bysource' + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + +.. class:: syncsketch.SyncSketchAPI + :no-index: + + SyncSketchAPI is a class that provides a set of methods to interact with SyncSketch API. + + .. method:: __init__( + auth, + api_key, + host="https://www.syncsketch.com", + useExpiringToken=False, + debug=False, + api_version="v1", + use_header_auth=False + ) + + Constructor for SyncSketchAPI class. + + :param str auth: The username of the user. + :param str api_key: The api key of the user. + :param str host: The host of the SyncSketch API. + :param bool useExpiringToken: If True, the token will expire after 1 hour. + :param bool debug: If True, the debug mode will be enabled. + :param str api_version: The version of the SyncSketch API. + :param bool use_header_auth: If True, the authentication will be done using headers. + :return: SyncSketchAPI object. + :rtype: obj + +.. autoclass:: syncsketch.SyncSketchAPI + :show-inheritance: + :members: + :exclude-members: isConnected,get_api_base_url,get_media_by_review_id,join_url_path,addComment,addItem,addMedia,addMediaByURL,addProject,addReview,addUsers,getProjectById,getProjects,getProjectsByName,get_shotgun_sync_review_notes_progress,updateItem,connectItemToReview,deleteItem,deleteProject,deleteReview,getAccounts,getAnnotations,getCurrentUser,getGreasePencilOverlays,getItem,getMedia,add_users,shotgrid_get_projects,getMediaByReviewId,getReviewById,getReviewByName,getReviewsByProjectId,getTree,getUserById,getUsersByName,getUsersByProjectId + :member-order: bysource diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..38b21b1 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=.. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/syncsketch.egg-info/PKG-INFO b/syncsketch.egg-info/PKG-INFO index 3dd28ff..66b1eab 100644 --- a/syncsketch.egg-info/PKG-INFO +++ b/syncsketch.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: syncsketch -Version: 1.0.10.4 +Version: 1.0.10.5 Summary: SyncSketch Python API Home-page: https://github.com/syncsketch/python-api Author: Philip Floetotto @@ -193,14 +193,14 @@ review_data = s.get_review_by_uuid(review_uuid) ##### Adding a user to the project ```python - addedUsers = s.add_users_to_project( + added_users = s.add_users_to_project( project_id, [ {'email': 'test@syncsketch.com', 'permission':'viewer'} ], "This is a note to include with the welcome email" ) - print(addedUsers) + print(added_users) ``` diff --git a/syncsketch/syncsketch.py b/syncsketch/syncsketch.py index fc2f4d1..5c21af6 100644 --- a/syncsketch/syncsketch.py +++ b/syncsketch/syncsketch.py @@ -1,10 +1,8 @@ # -*- coding: utf-8 -*- -"""Summary""" # @Author: floepi # @Date: 2015-06-04 17:42:44 # @Last Modified by: Brady Endres # @Last Modified time: 2024-04-04 -#!/usr/local/bin/python from __future__ import absolute_import, division, print_function @@ -43,15 +41,18 @@ def __init__( api_version="v1", use_header_auth=False, ): - """Summary + """ + Setup the SyncSketch API class. - Args: - user_auth (str): Your email or username - api_key (str): Your SyncSketch API Key, found in the settings tab - host (str): Used for testing or local installs - useExpiringToken (bool, optional): When using the expiring tokens for authentication. - Expiring tokens are generated behind a authenticated URL like https://syncsketch.com/users/getToken/ - which returns JSON when the authentication is successful + :param str auth: Your email or username + :param str api_key:: Your SyncSketch API Key, found in the settings tab + :param str host: Used for testing or local installs + :param bool useExpiringToken: (Optional) When using the expiring tokens for authentication. Expiring tokens are generated behind a authenticated URL like https://syncsketch.com/users/getToken/ which returns JSON when the authentication is successful + :param bool debug: (Optional) Print debug information + :param str api_version: (Optional) The version of the API to use + :param bool use_header_auth: (Optional) Use header authentication instead of query parameters + :return: SyncSketchAPI + :rtype: SyncSketchAPI """ # set initial values self.user_auth = auth @@ -97,7 +98,6 @@ def join_url_path(base, *path_segments): :param str base: The "base" path to append to. :param path_segments: Additional strings to be appened to the path. :type path_segments: List[str] - :returns: A "/" terminated string containing base and path_segments delimited by "/". """ # remove preceeding "/" from entries to avoid absolute path behavior with os.path.join @@ -168,7 +168,9 @@ def is_connected(self): Convenience function to check if the API is connected to SyncSketch Will check against Status Code 200 and return False if not which most likely would be and authorization error - :return: + + :return: Connection success + :rtype: bool """ url = "/api/v1/person/connected/" params = self.api_params.copy() @@ -181,36 +183,36 @@ def is_connected(self): def get_tree(self, withItems=False): """ - get nested tree of account, projects, reviews and optionally items for the current user - :param withItems: - :return: + Get nested tree of account, projects, reviews and optionally items for the current user + + :param bool withItems: Include items in the response + :return: Tree data + :rtype: dict """ get_params = {"fetchItems": 1} if withItems else {} return self._get_json_response("/api/v1/person/tree/", getData=get_params) """ - Accounts + Workspace / Account """ def get_accounts(self): """Summary - Returns: - TYPE: Account + :return: List of workspaces the user has access to + :rtype: list[dict] """ get_params = {"active": 1} return self._get_json_response("/api/v1/account/", getData=get_params) def update_account(self, account_id, data): """ - Update a project - - Args: - account_id (TYPE): the id of the item - data (dict): normal dict with data for item + Update a workspace / account - Returns: - TYPE: item + :param int account_id: the id of the item + :param dict data: normal dict with data for item + :return: Workspace / Account data + :rtype: dict """ if not isinstance(data, dict): print("Please make sure you pass a dict as data") @@ -228,9 +230,10 @@ def create_project(self, account_id, name, description="", data=None): :param int account_id: id of the account to connect with :param str name: Name of the project - :param description: String + :param str description: Description of the project :param dict data: additional information e.g is_public. Find out more about available fields at /api/v1/project/schema/. - :return: + :return: Project data + :rtype: dict """ if data is None: data = {} @@ -257,12 +260,14 @@ def get_projects( """ Get a list of currently active projects - :param include_deleted: boolean: if true, include deleted projects - :param include_archived: boolean: if true, include archived projects - :param include_tags: boolean: if true, include tag list on the project object - - Returns: - TYPE: Dict with meta information and an array of found projects + :param bool include_deleted: if true, include deleted projects + :param bool include_archived: if true, include archived projects + :param bool include_tags: if true, include tag list on the project object + :param bool include_connections: if true, include full user connections on the project object + :param int limit: limit the number of results + :param int offset: offset the results + :return: Dict with meta information and an array of found projects + :rtype: list[dict] """ get_params = { "active": 1, @@ -291,8 +296,9 @@ def get_projects_by_name(self, name): """ Get a project by name regardless of status - Returns: - TYPE: Dict with meta information and an array of found projects + :param str name: Name to search for + :return: List of projects + :rtype: list[dict] """ get_params = {"name__istartswith": name} return self._get_json_response("/api/v1/project/", getData=get_params) @@ -300,16 +306,19 @@ def get_projects_by_name(self, name): def get_project_by_id(self, project_id): """ Get single project by id - :param project_id: Number - :return: + + :param int project_id: Project id + :return: Project data + :rtype: dict """ return self._get_json_response("/api/v1/project/%s/" % project_id) def get_project_storage(self, project_id): """ Get project storage usage in bytes - :param project_id: Number - :return: + + :param int project_id: Project id + :return: Storage usage in bytes """ return self._get_json_response("/api/v2/project/%s/storage/" % project_id) @@ -317,12 +326,10 @@ def update_project(self, project_id, data): """ Update a project - Args: - project_id (TYPE): the id of the item - data (dict): normal dict with data for item - - Returns: - TYPE: item + :param int project_id: the id of the item + :param dict data: dict with new data for item + :return: updated project data + :rtype: dict """ if not isinstance(data, dict): print("Please make sure you pass a dict as data") @@ -332,8 +339,9 @@ def update_project(self, project_id, data): def delete_project(self, project_id): """ - Get single project by id. - :param project_id: Number + Delete a project by id. + + :param int project_id: Project ID to delete :return: """ return self._get_json_response("/api/v1/project/%s/" % project_id, patchData=dict(active=False)) @@ -349,12 +357,13 @@ def duplicate_project( """ Create a new project from an existing project - :param project_id: Int - :param name: Str - :param copy_reviews: Bool - :param copy_users: Bool - :param copy_settings: Bool + :param int project_id: Source project id + :param str name: New project name + :param bool copy_reviews: Whether to copy reviews (without items) + :param bool copy_users: Whether to copy users + :param bool copy_settings: Whether to copy settings :return: New project data + :rtype: dict[str, Any] """ config = dict( @@ -408,8 +417,10 @@ def create_review(self, project_id, name, description="", data=None): def get_reviews_by_project_id(self, project_id, limit=100, offset=0): """ Get list of reviews by project id. - :param project_id: Number + + :param int project_id: SyncSketch project id :return: Dict with meta information and an array of found projects + :rtype: list[dict] """ get_params = { "project__id": project_id, @@ -423,6 +434,7 @@ def get_reviews_by_project_id(self, project_id, limit=100, offset=0): def get_review_by_name(self, name): """ Get reviews by name using a case insensitive startswith query + :param name: String - Name of the review :return: Dict with meta information and an array of found projects """ @@ -432,6 +444,7 @@ def get_review_by_name(self, name): def get_review_by_id(self, review_id): """ Get single review by id. + :param review_id: Number :return: Review Dict """ @@ -440,8 +453,11 @@ def get_review_by_id(self, review_id): def get_review_by_uuid(self, uuid): """ Get single review by uuid. - :param uuid: + UUID can be found in the review URL e.g. syncsketch.com/sketch// + + :param str uuid: UUID of the review. :return: Review dict + :rtype: dict """ data = self._get_json_response("/api/v1/review/", getData={"uuid": uuid}) if "objects" in data and len(data["objects"]) > 0: @@ -451,8 +467,10 @@ def get_review_by_uuid(self, uuid): def get_review_storage(self, review_id): """ Get review storage usage in bytes - :param review_id: Number - :return: + + :param int review_id: Review ID + :return: Storage usage in bytes + :rtype: int """ return self._get_json_response("/api/v2/review/%s/storage/" % review_id) @@ -460,12 +478,10 @@ def update_review(self, review_id, data): """ Update a review - Args: - review_id (TYPE): the id of the item - data (dict): normal dict with data for item - - Returns: - TYPE: item + :param int review_id: the id of the item + :param dict data: dict with data for item + :return: updated review data + :rtype: dict """ if not isinstance(data, dict): print("Please make sure you pass a dict as data") @@ -477,20 +493,26 @@ def sort_review_items(self, review_id, items): """ Update a review - Args: - review_id (TYPE): the id of the item - items (list): payload - e.g. - [ - { - "id": 1, # item id - "sortorder": 0, # sortorder, starting at 0 - } - ] - - Returns: - TYPE: dict - { "updated_items": int } # number of successful items sort updated + Example `items` param + + .. code:: python + + items = [{ + "id": 1, # item id + "sortorder": 0, # sortorder, starting at 0 + }] + + Method output example: + + .. code:: python + + # number of successful items sort updated + { "updated_items": int } + + :param int review_id: the id of the item + :param list items: payload + :return: response + :rtype: dict """ if not isinstance(items, list): print("Please make sure you pass a list as data") @@ -520,8 +542,9 @@ def restore_review(self, review_id): def delete_review(self, review_id): """ - Get single review by id. - :param review_id: Int + Delete a review by id. + + :param int review_id: Review ID to delete :return: """ return self._get_json_response("/api/v1/review/%s/" % review_id, patchData=dict(active=False)) @@ -534,14 +557,13 @@ def get_item(self, item_id, data=None): return self._get_json_response("/api/v1/item/{}/".format(item_id), getData=data) def update_item(self, item_id, data): - """Summary - - Args: - item_id (TYPE): the id of the item - data (dict): normal dict with data for item + """ + Update an item - Returns: - TYPE: item + :param int item_id: the id of the item + :param dict data: dict with data for item + :return: updated item data + :rtype: dict """ if not isinstance(data, dict): print("Please make sure you pass a dict as data") @@ -554,21 +576,18 @@ def add_item(self, review_id, name, fps, additional_data): create a media item record and connect it to a review. This should be used in case you want to add items with externaly hosted media by passing in the external_url and external_thumbnail_url to the additionalData dict e.g - additionalData = { - external_url: http://52.24.98.51/wp-content/uploads/2017/03/rain.jpg - external_thumbnail_url: http://52.24.98.51/wp-content/uploads/2017/03/rain.jpg - } + .. code:: python - NOTE: you always need to pass in FPS for SyncSketch to work! + additionalData = { + external_url: http://52.24.98.51/wp-content/uploads/2017/03/rain.jpg + external_thumbnail_url: http://52.24.98.51/wp-content/uploads/2017/03/rain.jpg + } - For a complete list of available fields to set, please - visit https://www.syncsketch.com/api/v1/item/schema/ + or + + .. code:: python - Args: - review_id (TYPE): Required review_id - name (TYPE): Name of the item - fps (TYPE): The frame per second is very important for syncsketch to determine the correct number of frames - additional_data (TYPE): dictionary with item info like { + additionalData = { width:1024 height:720 artist: "Brady Endres" @@ -578,8 +597,17 @@ def add_item(self, review_id, name, fps, additional_data): type: image | video } - Returns: - TYPE: Item + NOTE: you always need to pass in FPS for SyncSketch to work! + + For a complete list of available fields to set, please + visit https://www.syncsketch.com/api/v1/item/schema/ + + :param int review_id: Required review_id + :param str name: Name of the item + :param float fps: The frame per second is very important for syncsketch to determine the correct number of frames + :param dict additional_data: dictionary with item info + :return: Item data + :rtype: dict """ postData = { @@ -603,21 +631,18 @@ def add_media( itemParentId=False, ): """ - Convenience function to upload a file to a review. It will automatically create - an Item and attach it to the review. NOTE - if you are hosting your own media, please - use the addItem function and pass in the external_url and external_thumbnail_url - - Args: - review_id (int): Required review_id - filepath (string): path for the file on disk e.g /tmp/movie.webm - artist_name (string): The name of the artist you want associated with this media file - file_name (string): The name of the file. Please make sure to pass the correct file extension - noConvertFlag (bool): the video you are uploading is already in a browser compatible format - itemParentId (int): set when you want to add a new version of an item. - itemParentId is the id of the item you want to upload a new version for + Convenience function to upload a file to a review. It will automatically create + an Item and attach it to the review. NOTE - if you are hosting your own media, please + use the addItem function and pass in the external_url and external_thumbnail_url - Returns: - TYPE: Description + :param int review_id: Required review_id + :param str filepath: path for the file on disk e.g /tmp/movie.webm + :param str artist_name: The name of the artist you want associated with this media file + :param str file_name: The name of the file. Please make sure to pass the correct file extension + :param bool noConvertFlag: the video you are uploading is already in a browser compatible format + :param int itemParentId: (Optional) set when you want to add a new version of an item. itemParentId is the id of the item you want to upload a new version for + :return: Item data + :rtype: dict """ get_params = self.api_params.copy() @@ -648,18 +673,17 @@ def add_media( def add_media_by_url(self, review_id, media_url, artist_name="", noConvertFlag=False): """ - Convenience function to upload a mediaURl to a review. Please use this function when you already have your files in the cloud, e.g - AWS, Dropbox, Shotgrid, etc... + Convenience function to upload a mediaURl to a review. Please use this function when you already have your files in the cloud, e.g + AWS, Dropbox, Shotgrid, etc... - We will automatically create an Item and attach it to the review. + We will automatically create an Item and attach it to the review. - Args: - review_id (int): Required review_id - media_url (string): url to the media you are trying to upload - noConvertFlag (bool): the video you are uploading is already in a browser compatible format and does not need to be converted - - Returns: - TYPE: Description + :param int review_id: Required review_id + :param str media_url: url to the media you are trying to upload + :param str artist_name: The name of the artist you want associated with this media file + :param bool noConvertFlag: the video you are uploading is already in a browser compatible format and does not need to be converted + :return: Item data + :rtype: dict """ get_params = self.api_params.copy() @@ -687,7 +711,8 @@ def add_media_by_url(self, review_id, media_url, artist_name="", noConvertFlag=F print(r.text) def add_media_v2(self, review_id, filepath, file_name="", item_uuid=None, noConvertFlag=False): - """Similar to add_media method, but uploads the media file directly to SyncSketche's internal S3 instead of to + """ + Similar to add_media method, but uploads the media file directly to SyncSketche's internal S3 instead of to the SyncSketch server. In some cases, using this method over add_media can improve upload performance and stability. Unlike add_media this method does not return as much data about the created item. @@ -695,7 +720,6 @@ def add_media_v2(self, review_id, filepath, file_name="", item_uuid=None, noConv :param str filepath: path for the file on disk e.g /tmp/movie.webm. :param str file_name: The name of the file. Please make sure to pass the correct file extension. :param bool noConvertFlag: the video you are uploading is already in a browser compatible format. - :return: A dict, containing "item_id" and "uuid" or None on failure. :rtype: Optional[dict] """ @@ -808,31 +832,29 @@ def get_media(self, searchCriteria): NOTE: Please make sure to include the active:1 query if you only want active media. Deleted files are currently only deactivated and kept for a certain period of time before they are "purged" from the system. - Args: - searchCriteria (dict): dictionary - - Returns: - dict: search results + :param dict searchCriteria: Search params + :return: List of media items + :rtype: list[dict] """ return self._get_json_response("/api/v1/item/", getData=searchCriteria) - def get_media_by_review_id(self, review_id): - """Summary - - Args: - review_id (TYPE): Description + def get_items_by_review_id(self, review_id): + """ + Get all items in a review - Returns: - TYPE: Description + :param int review_id: Review ID + :return: List of media items + :rtype: list[dict] """ get_params = {"reviews__id": review_id, "active": 1} return self._get_json_response("/api/v1/item/", getData=get_params) def delete_item(self, item_id): """ - Get single item by id. - :param item_id: Int + Delete a item by id. + + :param int item_id: Item ID to delete :return: """ return self._get_json_response("/api/v1/item/%s/" % item_id, patchData=dict(active=False)) @@ -840,7 +862,9 @@ def delete_item(self, item_id): def bulk_delete_items(self, item_ids): """ Delete multiple items by id. - :param item_ids: Array[Int} + + :param list[int] item_ids: List of item IDs to delete + :return: """ return self._get_json_response( "/api/v2/bulk-delete-items/", @@ -861,8 +885,19 @@ def move_items(self, new_review_id, item_data): item_data should be a list of dictionaries with the old review id and the item id. The items in the list will be moved to the new review for the param new_review_id - :param new_review_id: int - :param item_data: list [ dict { review_id: int, item_id: int} ] + .. code:: python + + # Example item_data + # review_id is the current review an item is in + # it will be moved to the new_review_id + items_to_move = [ + {"review_id": 1, "item_id": 1}, + {"review_id": 1, "item_id": 2}, + {"review_id": 1, "item_id": 3}, + ] + + :param int new_review_id: The review id to move the items to + :param list[dict] item_data: List of dictionaries with the old review id and the item id :return: """ @@ -878,6 +913,15 @@ def move_items(self, new_review_id, item_data): """ def add_comment(self, item_id, text, review_id, frame=0): + """ + Add a comment to an item + + :param int item_id: Item to add the comment to + :param str text: Comment text + :param int review_id: Review you are adding the comment to + :param int frame: Frame number of the video to add the comment to (if applicable) + :return: + """ item = self.get_item(item_id, data={"review_id": review_id}) # Ugly method of getting revision id from item data, should fix this with api v2 @@ -900,9 +944,10 @@ def get_annotations(self, item_id, revisionId=False, review_id=False): Get sketches and comments for an item. Frames have a revision id which signifies a "set of notes". When querying an item you'll get the available revisions for this item. If you wish to get only the latest revision, please get the revisionId for the latest revision. - :param item_id: id of the media item you are querying. - :param (number) revisionId: Optional revisionId to narrow down the results - :param (number) review_id: RECOMMENDED - retrieve annotations for a specific review only. + + :param int item_id: id of the media item you are querying. + :param int revisionId: Optional revisionId to narrow down the results + :param int review_id: RECOMMENDED - retrieve annotations for a specific review only. :return: dict """ get_params = {"item__id": item_id, "active": 1} @@ -920,10 +965,11 @@ def get_flattened_annotations(self, review_id, item_id, with_tracing_paper=False Returns a list of sketches either as signed urls from s3 or base64 encoded strings. The sketches are composited over the background frame of the item. - :param syncsketch_review_id: - :param syncsketch_item_id: - :param with_tracing_paper: - :param return_as_base64: + :param int review_id: Review ID + :param int item_id: Item ID + :param bool with_tracing_paper: Include tracing paper in the response + :param bool return_as_base64: Return sketches as base64 encoded strings + :return: List of sketches as signed urls from s3 or base64 encoded strings """ getData = { "include_data": 1, @@ -937,19 +983,22 @@ def get_flattened_annotations(self, review_id, item_id, with_tracing_paper=False return self._get_json_response(url, method="post", getData=getData) def get_grease_pencil_overlays(self, review_id, item_id, homedir=None): - """Download overlay sketches for Maya Greasepencil. - - Download overlay sketches for Maya Greasepencil. Function will download - a zip file which contains an XML and the sketches as png files. Maya - can load the zip file to overlay the sketches over the 3D model! + """ + Download overlay sketches for Maya Greasepencil. - For more information visit: - https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2015/ENU/Maya/files/Grease-Pencil-Tool-htm.html + Download overlay sketches for Maya Greasepencil. Function will download + a zip file which contains an XML and the sketches as png files. Maya + can load the zip file to overlay the sketches over the 3D model! - :return: filePath to the zip file with the greasePencil data. + For more information visit: + https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2015/ENU/Maya/files/Grease-Pencil-Tool-htm.html PLEASE make sure that /tmp is writable + :param int review_id: Review ID + :param int item_id: Item ID + :param str homedir: Optional path to download the zip file to + :return: filePath to the zip file with the greasePencil data. """ url = "%s/api/v2/downloads/greasePencil/%s/%s/" % ( self.HOST, @@ -1012,12 +1061,20 @@ def add_users(self, project_id, users): def get_users_by_name(self, name): """ Name is a combined search and will search in first_name, last_name and email + + :param str name: Name to search for + :return: List of users + :rtype: list[dict] """ return self._get_json_response("/api/v1/simpleperson/", getData={"name": name}) def get_user_by_email(self, email): """ Get user by email + + :param str email: Email to search for + :return: User data + :rtype: dict """ response = self._get_json_response( "/api/v1/simpleperson/", getData={"email__iexact": email}, raw_response=True @@ -1030,11 +1087,25 @@ def get_user_by_email(self, email): return None def get_users_by_project_id(self, project_id): + """ + Get all users in a project + + :param int project_id: + :return: List of users + :rtype: list[dict] + """ return self._get_json_response("/api/v2/all-project-users/{}/".format(project_id)) def get_connections_by_user_id(self, user_id, account_id, include_inactive=None, include_archived=None): """ Get all project and account connections for a user. Good for checking access for a user that might have left... + + :param int user_id: User ID to get connections for + :param int account_id: Account ID to get connections for + :param bool include_inactive: Include inactive projects + :param bool include_archived: Include archived projects + :return: List of connections + :rtype: list[dict] """ data = {} if include_inactive is not None: @@ -1046,22 +1117,31 @@ def get_connections_by_user_id(self, user_id, account_id, include_inactive=None, getData=data, ) - def get_user_by_id(self, userId): - return self._get_json_response("/api/v1/simpleperson/%s/" % userId) + def get_user_by_id(self, user_id): + """ + Get a user by ID + + :param int user_id: + :return: User data + :rtype: dict + """ + return self._get_json_response("/api/v1/simpleperson/%s/" % user_id) def get_current_user(self): return self._get_json_response("/api/v1/simpleperson/currentUser/") def add_users_to_workspace(self, workspace_id, users, note=""): - """Add Users to Workspace + """ + Add Users to Workspace - Args: - workspace_id (Number): id of the workspace - users (List): list with dicts e.g users=[{"email":"test@test.de","permission":"admin"}] - possible permissions "admin" - note (String): Optional message for the invitation email + .. code:: python - Returns: - TYPE: Description + users=[{"email":"test@test.de","permission":"admin"}] + + :param int workspace_id: id of the workspace + :param list users: list of new users - possible permissions "admin", "manager" + :param str note: (Optional) message for the invitation email + :return: response """ if not isinstance(users, list): print("Please add users by list with user items e.g users=[{'email':'test@test.de','permission':'admin'}]") @@ -1077,12 +1157,17 @@ def add_users_to_workspace(self, workspace_id, users, note=""): return self._get_json_response("/api/v2/add-users/", postData=post_data) def remove_users_from_workspace(self, workspace_id, users): - """Remove a list of users from a workspace + """ + Remove a list of users from a workspace + Can remove by id or email - Args: - workspace_id (Number): id of the workspace - users (List): list with dicts e.g users=[{"email":"test@test.de"}, {"id":12345}] - either remove by user email or id + .. code:: python + users=[{"email":"test@test.de"}, {"id":12345}] + + :param int workspace_id: id of the workspace + :param list users: list of users to remove - either remove by user email or id + :return: response """ if not isinstance(users, list): print("Please add users by list with user items e.g users=[{'email':'test@test.de'}]") @@ -1097,15 +1182,24 @@ def remove_users_from_workspace(self, workspace_id, users): return self._get_json_response("/api/v2/remove-users/", postData=post_data) def add_users_to_project(self, project_id, users, note=""): - """Add Users to Project + """ + Add Users to Project + + possible permissions - Args: - project_id (Number): id of the project - users (List): list with dicts e.g users=[{"email":"test@test.de","permission":"viewer"}] - possible permissions "admin, member, viewer or reviewer" - note (String): Optional message for the invitation email + - admin + - member + - viewer + - reviewer - Returns: - TYPE: Description + .. code:: python + + users=[{"email":"test@test.de","permission":"viewer"}] + + :param int project_id: id of the project + :param list[dict] users: list of new users + :param str note: (Optional) message for the invitation email + :return: response """ if not isinstance(users, list): print( @@ -1123,12 +1217,17 @@ def add_users_to_project(self, project_id, users, note=""): return self._get_json_response("/api/v2/add-users/", postData=post_data) def remove_users_from_project(self, project_id, users): - """Remove a list of users from a project + """ + Remove a list of users from a project - Args: - project_id (Number): id of the project - users (List): list with dicts e.g users=[{"email":"test@test.de"}, {"id":12345}] - either remove by user email or id + remove by user email or id + .. code:: python + + users=[{"email":"test@test.de"}, {"id":12345}] + + :param int project_id: id of the project + :param list users: list of users to remove - either remove by user email or id """ if not isinstance(users, list): print("Please add users by list with user items e.g users=[{'email':'test@test.de']") @@ -1150,7 +1249,7 @@ def shotgrid_get_projects(self, syncsketch_project_id): """ Returns list of Shotgrid projects connected to your account - :param syncsketch_project_id: + :param int syncsketch_project_id: SyncSketch project id """ print("DEPRECATED! Please use Shotgrid's API") print("https://github.com/shotgunsoftware/python-api") @@ -1160,6 +1259,7 @@ def shotgrid_get_projects(self, syncsketch_project_id): def shotgrid_create_config(self, syncsketch_account_id, syncsketch_project_id=None, data=None): """ Create a new Shotgrid configuration for a SyncSketch workspace and optionally a project + :param int syncsketch_account_id: :param int syncsketch_project_id: :param dict data: Configuration data. @@ -1194,7 +1294,6 @@ def shotgrid_get_playlists(self, syncsketch_account_id, syncsketch_project_id, s :param int syncsketch_project_id: SyncSketch project id :param int shotgun_project_id: (optional) Shotgrid project id :return: list of Shotgrid playlists - """ url = "/api/v2/shotgun/playlists/{}/".format(syncsketch_account_id) if syncsketch_project_id: @@ -1208,15 +1307,19 @@ def shotgrid_sync_review_notes(self, review_id): Sync notes from SyncSketch review to the original shotgrid playlist Returns task id to use in get_shotgun_sync_review_notes_progress to get progress + returns dict with information about the REST API call: + + - message= "Shotgrid review notes sync started" + - status= processing/done/failed + - progress_url= Full url to call for progress/results + - task_id= task_ids pass this value to the get_shotgun_sync_review_items_progress function + - percent_complete= 0-100 value of percent complete + - total_items= number of items being synced from shotgrid + - remaining_items= number of items not yet pulled from shotgrid + :param int review_id: SyncSketch review id - :returns - message= "Shotgrid review notes sync started" - status= processing/done/failed - progress_url= Full url to call for progress/results - task_id= task_ids *pass this value to the get_shotgun_sync_review_items_progress function - percent_complete= 0-100 value of percent complete - total_items= number of items being synced from shotgrid - remaining_items= number of items not yet pulled from shotgrid + :return: Progress information + :rtype: dict """ url = "/api/v2/shotgun/sync-review-notes/review/{}/".format(review_id) @@ -1227,15 +1330,16 @@ def shotgrid_sync_new_item_notes(self, project_id, review_id, item_id): Sync new notes from SyncSketch review item to the original shotgrid playlist Returns dict with information about the REST API call + - sketch_upload_error= "True in case of error" + - sketches= "Number of sketches synced" + - comments= "Number of comments synced" + - attachments= "Number of attachments synced" + - item_name= "Name of item that was synced" + :param int project_id: SyncSketch project id :param int review_id: SyncSketch review id :param int item_id: SyncSketch item id - :returns - sketch_upload_error= "True in case of error" - sketches= "Number of sketches synced" - comments= "Number of comments synced" - attachments= "Number of attachments synced" - item_name= "Name of item that was synced" + :return: """ url = "/api/v2/shotgun/sync-notes/project/{}/review/{}/{}/".format(project_id, review_id, item_id) @@ -1245,15 +1349,19 @@ def get_shotgrid_sync_review_notes_progress(self, task_id): """ Returns status of review notes sync for the task id provided in shotgun_sync_review_notes - :param task_id: - :returns - message= "Shotgrid review notes sync started" - status= processing/done/failed - progress_url= Full url to call for progress/results - task_id= task_ids *pass this value to the get_shotgun_sync_review_items_progress function - percent_complete= 0-100 value of percent complete - total_items= number of items being synced from shotgrid - remaining_items= number of items not yet pulled from shotgrid + Returns a dict with the following keys: + + - message= "Shotgrid review notes sync started" + - status= processing/done/failed + - progress_url= Full url to call for progress/results + - task_id= task_ids pass this value to the get_shotgun_sync_review_items_progress function + - percent_complete= 0-100 value of percent complete + - total_items= number of items being synced from shotgrid + - remaining_items= number of items not yet pulled from shotgrid + + :param str task_id: UUID of the task returned by shotgrid_sync_review_notes + :return: Progress information + :rtype: dict """ url = "/api/v2/shotgun/sync-review-notes/{}/".format(task_id) @@ -1264,22 +1372,25 @@ def shotgrid_sync_review_items(self, syncsketch_project_id, playlist_code, playl Create or update SyncSketch review with shotgrid playlist items Returns task id to use in get_shotgun_sync_review_items_progress to get progress + Response format: + + - message= "Shotgrid review item sync started", + - status= processing/done/failed, + - progress_url= Full url to call for progress/results, + - task_id= task_ids - pass this value to the get_shotgun_sync_review_items_progress function, + - percent_complete= 0-100 value of percent complete, + - total_items= number of items being synced from shotgrid, + - remaining_items= number of items not yet pulled from shotgrid, + - data= + - review_id= review.id, + - review_link= url link to the syncsketch player with the review pulled from shotgrid, + :param int syncsketch_project_id: :param str playlist_code: :param int playlist_id: :param int review_id: (optional) - :returns - message= "Shotgrid review item sync started", - status= processing/done/failed, - progress_url= Full url to call for progress/results, - task_id= task_ids *pass this value to the get_shotgun_sync_review_items_progress function, - percent_complete= 0-100 value of percent complete, - total_items= number of items being synced from shotgrid, - remaining_items= number of items not yet pulled from shotgrid, - data= - review_id= review.id, - review_link= url link to the syncsketch player with the review pulled from shotgrid, - ) + :return: + :rtype: dict """ url = "/api/v2/shotgun/sync-items/project/{}/".format(syncsketch_project_id) if review_id: @@ -1317,18 +1428,11 @@ def get_shotgrid_sync_review_items_progress(self, task_id): """ Returns status of review items sync for the task id provided in shotgun_sync_review_items - :param task_id: - :returns - message= "Shotgrid review item sync started", - status= processing/done/failed, - progress_url= Full url to call for progress/results, - task_id= task_ids *pass this value to the get_shotgun_sync_review_items_progress function, - percent_complete= 0-100 value of percent complete, - total_items= number of items being synced from shotgrid, - remaining_items= number of items not yet pulled from shotgrid, - ) + :param str task_id: UUID of the task returned by shotgrid_sync_review_items + :returns: DeprecationWarning + :rtype: dict """ - print("Deprecated. Response is printed in the shotgrid_sync_review_items() function") + raise DeprecationWarning("DEPRECATED! Response is printed in the shotgrid_sync_review_items() function") # alias methods to _v1 if they have a v2 add_media_v1 = add_media @@ -1349,7 +1453,8 @@ def get_shotgrid_sync_review_items_progress(self, task_id): updateItem = update_item addMedia = add_media addMediaByURL = add_media_by_url - getMediaByReviewId = get_media_by_review_id + getMediaByReviewId = get_items_by_review_id + get_media_by_review_id = get_items_by_review_id getMedia = get_media connectItemToReview = connect_item_to_review deleteReview = delete_review From dba651f638c3596c1b3d618ae0a18683e48c1382 Mon Sep 17 00:00:00 2001 From: Brady Endres Date: Thu, 25 Jul 2024 17:41:07 -0700 Subject: [PATCH 2/3] add github workflow to build docs --- .github/workflows/documentation.yml | 27 + _build/.buildinfo | 4 + _build/.doctrees/environment.pickle | Bin 0 -> 60140 bytes _build/.doctrees/index.doctree | Bin 0 -> 330166 bytes _build/_sources/index.rst.txt | 46 + _build/_static/alabaster.css | 708 ++++++++ _build/_static/basic.css | 925 +++++++++++ _build/_static/custom.css | 1 + _build/_static/doctools.js | 156 ++ _build/_static/documentation_options.js | 13 + _build/_static/file.png | Bin 0 -> 286 bytes _build/_static/language_data.js | 199 +++ _build/_static/minus.png | Bin 0 -> 90 bytes _build/_static/plus.png | Bin 0 -> 90 bytes _build/_static/pygments.css | 84 + _build/_static/searchtools.js | 620 +++++++ _build/_static/sphinx_highlight.js | 154 ++ _build/genindex.html | 307 ++++ _build/index.html | 1438 ++++++++++++++++ _build/objects.inv | Bin 0 -> 718 bytes _build/search.html | 116 ++ _build/searchindex.js | 1 + docs/_build/man/syncsketchpythonapilibrary.1 | 1567 ++++++++++++++++++ 23 files changed, 6366 insertions(+) create mode 100644 .github/workflows/documentation.yml create mode 100644 _build/.buildinfo create mode 100644 _build/.doctrees/environment.pickle create mode 100644 _build/.doctrees/index.doctree create mode 100644 _build/_sources/index.rst.txt create mode 100644 _build/_static/alabaster.css create mode 100644 _build/_static/basic.css create mode 100644 _build/_static/custom.css create mode 100644 _build/_static/doctools.js create mode 100644 _build/_static/documentation_options.js create mode 100644 _build/_static/file.png create mode 100644 _build/_static/language_data.js create mode 100644 _build/_static/minus.png create mode 100644 _build/_static/plus.png create mode 100644 _build/_static/pygments.css create mode 100644 _build/_static/searchtools.js create mode 100644 _build/_static/sphinx_highlight.js create mode 100644 _build/genindex.html create mode 100644 _build/index.html create mode 100644 _build/objects.inv create mode 100644 _build/search.html create mode 100644 _build/searchindex.js create mode 100644 docs/_build/man/syncsketchpythonapilibrary.1 diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml new file mode 100644 index 0000000..6ace7a6 --- /dev/null +++ b/.github/workflows/documentation.yml @@ -0,0 +1,27 @@ +name: documentation + +on: [push, pull_request, workflow_dispatch] + +permissions: + contents: write + +jobs: + docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + - name: Install dependencies + run: | + pip install sphinx sphinx_rtd_theme myst_parser + - name: Sphinx build + run: | + sphinx-build doc _build + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v3 + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + with: + publish_branch: gh-pages + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: _build/ + force_orphan: true \ No newline at end of file diff --git a/_build/.buildinfo b/_build/.buildinfo new file mode 100644 index 0000000..3ef5a91 --- /dev/null +++ b/_build/.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: 92f8d6ac56ccab42ac73d860c02e5024 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/_build/.doctrees/environment.pickle b/_build/.doctrees/environment.pickle new file mode 100644 index 0000000000000000000000000000000000000000..06c5eab1973655b496dbe5bae9d78eb15f975294 GIT binary patch literal 60140 zcmdsgeY_ni4aF)m87O14&&FOAn@x{sWmojjawV7P1c09Ia-IC#0_=NU9cD9Xd;nHTyJ<4 zFG!oRJ}|=$Ym9j&G~K}V+#o&~cU5vGtQeip19oggqfNtI>==uXbdBkv4Nb3ZG|ei6 zVNZ?l>j1ZI3A~P9H>;+Lmf0?38S8`#2W~qMkX)Nws4b{+>7dGyw(mJ+9csV&#IjpI zv1EpIYw9>t#?;Z{4@^C1LtXwd+74+f4+?+aUee#xF{Z%%Q^zg4IrZwMXNF!Fdi$s5 z?YiYPjbLidTx^>DRKp9VP8e?J1^cHSS*9G^v4o!R(2lK1_0_u9UiR%p3xYg+=-{El z`=>5qEn{7Ds}hZzLgi3*9Ic9@5#MZ@MgYBz&Q=aqW)D`5fGJSBu^d~o(HKir7g`~7 zwG%zt@y)8}i|T^iG+m>GhcAD#?|I?b%9};m**BwG&&JWzq8WxrR*h!UJ6&DqJpJ@? zHS`TPXc{5w^OfkT%yktCSp+eIY8^TZz|fUwyk!KT=~syg(R$l$m}iJ!yqkm0!h(GU zqa#{Z@dFw@csleu^$?;qsOmU~Ce1VTW~X7&@Bk8H+O)ML8hga*v}!@6fdMmCx$gwV zjOUxN8BN-5;elw)jW>c{n;PbV(P@TN-)owr6{FEWyWBc82Nn1j#Fj~tpNl4`tmQR2 zpeiYf^0tLuz3SD7bl_;{+bs8G#oTIOL1P>c19d~gYZ*3r?P@&JsxCm^t{0kB-&`oI^)^OFt3}F1ThZ54F}k{Hw(X|3h+5bch{Lb8G5wI?0uibUO#>C5gA$-M z3}mo~t8q;mgL;yigNzA6LpQYyw+&kpR6BkX@T0CBwlQXxNta2wYA?D(709wf&O(PQ zf}kka;C5PM3|FGc-<#bgCG5s*0uIgYCszSM#>3|iWOTwo*;4vE-naiq?sA26>_o0zHvPH>g4w%Bm zgvntO!5YV)>4ME)G~1n|Srh#U^T2F@2~BWLSHWh>2p$Yk5v>WsU#*f+V)Tjl_ zO5T6ZXfj)mvwRG0vUuz00?h5^RWoB{3(K|}Xr{ejo0v65I_{F|orWMASy@nLIM(yg zD7)_1jTjfaj=G21<9*51BFao$ZDu!O5HQ2bn#=2Hdqi{?W0tqs3R}%;9VRr*;!+AD zm}XBfKv*6Si(Ax2>pqX|O<>e|6jKrfzEJ#9CL-K!FL z+0e#7A`;s!aSo=0i3loGxiyESWFQSQsQWfEIiw7p2^Rt*x&~hWj>q%SR(9uU-zIL8 zaRuHuCT9C-+-EfDg4&;S)0ojTYHa$5qb=Qn)Pilx|((< zWkWDrPq1qzqwPtS6NH%H>S;wIFus&Qc9NCD=J-Zhd>PBblowVz?FNi2<`g#Ra`*rY zs2N}<%oD#AGYd4}xkeKh)DbAz&Oo?R+Ofwu9Z-&8o7-L~FkA zN!UbI7POmom~_udl1FYtmCw5=2J2p)N8rf6oIlT|XUeK3Q#2;%5VLV2C3QYHzc}xKun|&W(t(y{gmWq zSjVdn($_J+fbp|Jq2%y3k};O3nzsS`Hfg59tyd*Nf-N)&Fca^3V&bRec55cN_nw<) z%;3ljw0WS_Y96SQB)A#FPJm@vx>@8U7JB<%LO*ef(Ih6Lne47+9s}P^^q)u&P7+Ix!rLW z8a{HF&Ambtvk@%(Sz0aZdiA3ZJ~%ah^r2%@uRQk9(FY#9=jeU+z5dvV6L(=Gcc0>` z8@N+O3%q&o==}YU9=#urJow-}O}paxi@1FwIbUalW!r#Rt;6gc&rZzKB)`H!5#7rS z06dZ1Jd?&8PqpB{V#qf;Ef-DR#WO>%-84^`O>$F6Ikb41k9P3FqE+Gf8S}oz^)R|K z*<_gDJ1`%VXJfI!JW(2Nm-DQQAxkHwt-OdiIEm9r3>d9>-V!oFDlBQ5p6`371JEI< zlbQVNES%_q=QAop7n3x0F!^G1bbYwp-Apk{!h!5?S(yUUhuA^}0}-nW+lQi<<=xzU zp8Rg|{&sb7#Y%rtV%lh5d3gdV-OOGq+?KXEciYvXXtSsNU9hBRveb-XK`Rbdbn=nOo79pj#1XmYJv~C_F1jW~o zJ?$8$4DOVk7j_ro4t6`Uz^WPbCB!)b3qaTM09%!8aL0WLmYWs^4Yt@#VgoDD^~p`= zYrvl5zGQl&v4gP%ht2fi4~q5kc8rgvX_?J7rZKkxzX}r^40<=aVV@%~`4?OXf@+zS+X^F3G>5doN$v3)6*3s9V&sUCe}j ztGhV8yj#eggQvw^lPxoyz@Mqv?u=zq1T>B^RnTgfmb=NlThn_*0pe3#U?`Io(lkC0 zA|gFv0I&rPxiUTA#v<1EsTw4~xmb!%Fg!WyyLMp8F}x^x5p} z)U}no4;BI=hXw&sjI8zS8V^w!rl)GlWQ%nSV|Z=RCZmZ3ZOz2QPHw>!;x3MoF8hJg zvd_Rgckd7pt&D@VTRe?=h}KwYbrGGVK=Gc4l^%+$0;35V39KMq}M4qwB?k zX*mXBy6CHNmnue}8v%%JEp#(t5;h!NHr?(lq9pN&XgwPX@GitHNmL2ze7FlftmT!W z#C9!2DME@X@+Okm-6cvXN@e<|5M$=?KIX-+wWqYS9?kUHw6)K=FEmy-ep=l5c$)cy zby7U@WGZDfO}#0V&9G|X>FIfE7OJXS4ffYe?_OA88H<+1{@Rwq{w}4pHRo0fke20& zXn)h@n!kewPh)2$*nQu*R{FyZMOj`Oj>#vDN-fsP@NJgTXBvL~wK^~C5 zvb&qXH#4x8A+|DDTYf%`Ygx}h2k)|;V}IXmy@&oLk*ClO5n;4$WLZ3Jh2cTFAl81C z#{)#^($?C;a+5$+r-oHBhM2gSXLr}^R)y}ie!+U4{Y7wx{e7?XylCGq?h~nBN>jgV zy-#HRiuJ2W(s>30%6S|9V-UY)eL&pvH|N%htlj~f6w~+BKdLaA0$Z;nPN(f4tIN~$9~h4*p0#d z5nqS!^{RkMBO1)7rd}Eh(E6=(pnW1u{lhf%e(P_D_WZW>Ns;_V)~7`BpIDz3$sU=S1@J=S1oYY3hsC?}^Nhq?!M8MdW=c&HHC*>i4ZLi{gI{gPB;=f00)H zl{EDS=`;T_O?@>@{VVHh;^D7b|5_x!k(U3V^>0MxH&e)eYyFYP{FXJ#2GqYxpZND( zalUPRSlss?(oy{%({_Ew`eRZ2C)S^ebGHF7B~@ zaL#!TpJV=8n)>02$oq?QB>Z>lM}qW!Y5fmT{(|*ClVr3hS-87!{Xw$Pb^|*qSehW( zgaNXJmkN%u)r56NbWMQ0)PU`!a!(c8%Gi~rjnoP~d*02~i@3*Xy%@#@>!JV6&Hewe ze#|caFEjF@2{g#Ds%&6^$x8o<-~5wvbmgof^Rk-E3tg>2g5Bk;rTldwe?47o5LY8~ zHJV&m|I2!jJ-d-|H;H>D=xTFv6>Uzn$vwTZOv%m`x@l58vX!p3iL32&wL@H8Mpu`M zt1IYgr?}dM#N28o!g~UFVx)!N)@y~MhAcsG5 zok&Gvw0P$+X{;^5hAYe25J!8}%pjJj)_!Adb<&s4^{9?_!OKM|+8)47#exTZ7M#^2 zV4j*Fwm4^DgFIXN$%nabzw`K-RjZShzOAtE;c zPE0V`IeQ_Xa|8Zk8k?r8ed6jyB)I-=qWt|L{{Rxad6o2z8M@;j{l~pZXO_~3G8s1` zEk?F;mDx0IcToD3nT%sd3mfLVnhNgEl)i@24`ec4OX;~x z#)C+w&p(6<-oW`Z^>Frv$A6=$dp8vbr*BV()Ik#P&Af41h?6Fgn_>W z9gd4fap62h|0NkeMd>$WGTw;vd{UZSSYsg!S~PG9A&#!=GCc2!9T2m8ENb}a450IP z_TmX#tccrAW=f*WIzK=Rmxpz*T;M|E5JDOTU@;jubZOa^ZW;IWqCH({d$}dA!NOJu z8}05sEmqVBG0>hT!cwVPG69A3F1{;&QXn}`qCHNP{!29dCQ2Kbj2hCS`Ozc-7i@{G z=J3+d26jJs7>66gYQFpM)#Vln#)4bnIs_2YtJBC_L|c+Yd>5~u_npkuj+udQ7I5J# z(tq#7f1sX4X*-kQP$vBI2TEknj7e9jwF`he}S~S7;Y1pnPj3t8K+oc>HriALF4cL>Q zIKsSzE%4-6S!#1fjwk20#KAIayLCC%w0FG&;R9@d(>W2oJ1OEb)?LvkI+Y*Cc@DwP zYxpjs5>8KSR$cq((4a(lYapJkVI?;|Kd`KZA^f2GG zMnH6%l#g9ioTfntBWVe35RF1Fw8>8)pJ@ahX+I8e?&K6uvKp5BHXZ_Yu zC=Q!E6d6N6oz6R92@3tLM3>cZ*Ak9>AeLW6xSdWRA)ItB+SJAwB^taeNJ$&Xe76(X zL4~V;cHnS?ax>kTiTER~=P%SV&+Zk^Vhi8EHXt4JtJr3^zyLt28L~cvM9)b`n{8Lx zc(!hcHsb+imaAtdyhTc*{SZ)0&fO|@NdmLEKzqJRxOLnza7c}9=+So3$1^))t+gIni6+DXA03F}7SfMZ`p6R_yFS1BnhgP+n4`@|Y^l>(w`e1_?wV)@q@b%LyoSBX5Jy${(H-$y$ZW**0&%m_f2q84Y7)DzKUW5#nEG2gM%PH{9l^C%2# zFoRHsIAT9THgkqgBXqoe27@%1$#@m4%T5qr&6&lY-uk|^q{Mk;Spl;gBgv*s|-z=^PQ(tQ(jJ zKhf5YDiQivglvhJ6;6siMGlSD%KOip6ygyxT7vx42 z39(OKC`Cf-)kc1)5u%O-GEQnGv9%l_o*0}EZ`9V1s(t7~2)R*3LOiA~lp-NcXd@4u z5Iv@ggbn%WVr6hTJgcoARXUt!x)4n&lHu+8A}NyL8ExdDli_-Kx}b?xXs%jYW~Uo- zHsX_mlj0NFdQv4trF_!p)wCixKCUmCA~`;$jXZR6Tw6*G;)b1HZ~p1v%=nJBl2n;- zlM*v{yNa~A|VcIBfr!L zfwxM{($$AEI3X6bHFOaOAvdZ>h=#sUii9wyXyN#240Qq1H`3Sjr0j= z71~v##a?~66lrmtHuBJEk=qLq!JnRH;*EoI;xTOvshWvCdm(b8ii9|!FO(u7j%y>o z)CeI$KRpTY?BIlWyS9cd0wLr^6$$Z-zEFyUIIE33bVB6(h-4qBCm%jBI3GT)tsqrC z^zkF4HWlgcF@2d7>F`l)r)UpG^ujY!66lEqssI$XSEi2WP>p+R9P22G^BagS0(G8XVS_N0A1z+Q>tvK`v;Ws*Pg) z_>^*v4U2=bp`ooKRW|eqTIZWpB!!_bmLe&h)J7gUDRPJSyV^T-C`X8Ra6-ICTR*CV z=yQla+oU2H-lZ>+A{l;88+qtt$W0WqZQN_3`265Z_^h^aRGH9cqDb3Qq`{~4_QbhtavIs$2oaCWZ)W{qa87(*TT`l>I9TpH5GRU_D-z^;`obv^fm(vinfYW>CoqBey&wTN_<&g zDn&|sNgH|Sl<08|n?4wiU$DW47R$-;`UPD4@?+kJ>>D3=`LP!emuC5KxZJxaZ(xxq*XawW zNR%mU#{#|G!e32h~*+L1o1kwUwQv^cIWmm)15(nfx% z(txsgh&1|D4jJbw!GNR9`wpihM*HdFT}B;dvxqNu>{xvz=4SFGZGodvKO~OIus2 zEIE(o(Yt{~qI^?dKt-Z_LmPSMM9Ce}6*Dw3r10X;_{M?V(h)#h8n?91AzisqMMA98 z7fO*3tF@7bPKewl8ik3t`Ni*2&<}G5^johv$K%$)iE&t4Q>s>@&n8;$#uW)Nt1p}) zK`PqFLnlb)*BK~8PQQGNAUWsAg739TS<)DsC5E=TR9SMQ+ym*;!Xj0k)R$0^DvxU; z51lHxk1I;Ut@oEr-ZMBy-leT4RgU!exMHuS70K~)`l2b4mPaebRU$7T*{>b+S z=g9Z8HKodtKK@AW#uW+jU47vc3Gye}$S+xfkk{WvaGicuDIZ|pxoO~)%w>SMG%Fbu zf=G=k5@egcaEb)kqK!Osg5+W$=^zX6^UdNtWo+k~d6FC(oFw;ZYfIIN^ofO(H?T;Q zyY&TBB+4tbk%vx{{c>DY)}w2lW+#EFy{O_3P8CmEU#e8OrF?1AuZcynH1$PPB#W($ zJan?$DwBl_vaHSI#4)1_eVPqBzd_!_d|+_iykA>&s=PT`&Kt$n7Af>TeQ6ab^o!ca zL#L3+#MJ9B>Q@G5&6l;crOF!JiHS9^NR%(>3#dqxFKQzXohYidhUv1**1R}4YhKV+ zohoZC#Ma2IEmG)5`qC;==!e?KL#I&r`*O3n#q-{Yf!9XY0pilEjnv+kn=O)IN?#;J zGF+{VJajVLQsTRG+-i1FBoDA0O->9>ljGW&Q#B{|D7Y?t8(SpML;Auh66m$s$U`Sk zE_TZt-(LF6apsx9IdfK9S*o1r6T2;KUy&wH>&vG|lhfMBL#Ih@t58y7-}Usz1}Dl# zwbi9cls;R9eOg$g%187iRHVvpXd@4uDhd{*SK#GagA?VO+R9QTil#+j?JLsc8~XAo z(&TH}$U~>e%Vp0+j5jG9o||+=HV^F2t^>rS@n>hs9hIJKE7D`NzHEy0_^bbK=+SSl z@bKXDnAPtB#UF`Rpoi49B0VblvMJKzCT--QTaVm%6lsL{o@3Sv8->Q;?08aJPpb1v zpYtfanpPyoT z`Y*tLo!SLJ984ijTqs0WibEF)(Usx=gD@^k7oA!VZ3(@4)$O#Zc!Als%^*G-JD*## zYSr95LA0650<(_d8s0fV5zoT8icHod?h1Vqufx0bW~b4GI2i}gpvDox#$YYNM@pEysmE++Z$!zX;!)qC~+b}`{k4;Cb>+H`)-}A7ivfQe9%{bZ_ zv@P2`Q{ndpm3k#D+Hiv3^L)JTZS*AT`}t^7VAqiLfSMj{lhT7+&Eq3sFGkecUw6%(#=V~^5)1`yb>~1u8hmvCyG?gHpTu~KuFUJO-A=XWdKSpY3pJC;WkvX z8DG`G(TdPK4z;xc#S(kszeR|zM<5T=(`C8~^1LM;vr z>_j8h9Z5qSgJm+to{XobqYVK%wrR$Z{8Ow`)9A<`n6lj|iem*!W>~lO5qZ+9isV4X z*6e%;^?Vn?MF;Hw@A$herU4V537VQNdupm>hL+a=@XO0?sbsOFTK1E#N&`u)hHFb} z5I7S0LCQ7ib+6;nTsBx6vRG0rhmudTCKajGb3RHOKn zj_l|aoJ^FfrqlCsGC`ACcPRNz&_$|AMw4nfQuq~3wPuy~TLDn2?`H8id#$FG+Q~`| zPOeLQDg7eVm4lP&x|8_G&tnvRC{Klzhx)RzC$LoiE#3O{#{5kR6$?bE#=~-r^eUH1 zL-#4xhu_r7j=fy__-{t3)Ti1$_G3+DHEJ0cr5bzLms#l-Vbs*Qw^W@Hn54@ig`XKw ztE+s>7XYRD4v9g^X{=URx!sh}+I1>5+%1yqNsxn~rl**q1N%)7;Y%){qYt1Uvl-r>^o>bex zZYg~RSf!?N4?c?})pCGr>qXI73MAp*PT73#9XjJ2W-sHav}dNYgLQlo zskoe@)FcV8cYS0elP|kmocdBgslGcTaS{hy*~A>|tKPJgU0A7Z*1Nqw)u2YN{=M59 zS(4z8Xgw)M)iW;lcw{uGrrT*abA`xn&#QE9xh3z7D%E^<@pZ$@%lj(TA1>|XKG2d3 zw@GqpE%VA_9lbE6TJJ21r&V>i^(qCH>c5?Bz4UlPQAO$Hecl_jq-LUI=GC{0RI`LC z)qGbf9g#-3EXjEdmD>`2RNbg*%aJiZ0Hu4RN)_Kpe52&(2&9saFnl(0R>m4MxD)w)>I zeKr}R%W@m9?PcsAx^cAeVYUT)414m+@$-<2w&b1vOq-GG*-mD>62@;+-ez=uYs;!t z3CN!j$OgVqnV^)1hKO*qB^hTC{=A4#Zt@60Y&%LQKP;k@yVALhz^pVs(!t4X^JQ^f zDB|>QRpLAbQmbApqLfpT4d;a3wB%sN1+p^zxQNz&C7H;?*DsP_KPf`#ZK-ADS_3-e zICS2=OGJ}|TCanX#Ep94j23aq^;F#H$~J9d5vn{`B4XOTWZJ9)Btq7`fVLKa$|n?} z6!$E1`&p9ScN8I&wm*7t^ok-@d62d%UJr_1SwvEmifsdByYcD^#1b1kS*)odR{5TQ zynmC0xvmIPPSWDmOcrWy5vqLr36gSJNIcl5gCjWF1808`r(8qwu1U68Gewy4fM{li zMQYQbB1rlCMnMB|{w0JXMFf@IjciM9D?*eH3KD|t8qk=)PKYGT9Yv(_X{n2J>X3wT zR}rPWNi;~AJQBpwB1Cymf*|;!3QbB@y4V`ds_4EVUU_F{_YATi_ZLCR$5e4UAPe={ zB9v0VKa25D5u?0K)LF^qfTWn?MUe6th!mZP{!2h7ia`A*j@}XYYy%%FLY7BpNVbxg zdKUAIMa=R(B+N9BT=18kxDY5Bbu6?lDX&_D(hqNEn^`LY_E%j=z&eX*7BS1`0in7c zGp|*|DIeR!+hXWiQr=P#uY46=jGm?eX$jOVf|k41l90W;s;7!*<%6RrVlT+B2wAR0 zwj3=4gQW(az92m6<}}1B;XPf%D`#LTAH@%Z3Fo1SECbILF)uJ&nniu4h&p)8Gh4yi zixp_9g3WG{dY>&~UVzZKP-IriEOIAEBS^0^{@`M?rEnJmeQn*D_$ zWVtz!AmwW|KwYqR~au z#k(Konb`TqldD!)v(ZEnENgmogO2!btv=!#Zh+Sf0CU{;&QPcfZ+bXiL$E9w!SkkX z;0=-W4=|W_#A4spPS?4pTMm+9(78lW`hgcYJDhse7Flj{vnVD2Q zp4xbV-7$6CSTti^t5lWq2fRJokF#jsRIZXvG)^ytbXCFHpD5wk*!cpUcD~3Ua79ui zqTq^<6ZmK24to`3eQ5f469X@ObfOJ*3o-?WFD9z&gfn378y5b+BaXT*;DU*|O!cB_BI7t6fRHWy`j(dk;nV@)st0wE;afj|NYH-wvzgh04#5XMKiA)MjL|K6*ruA{4~yJlv!f`N+u-2M>>GZm1 zP2V|P=qzp&_ciP7#&|g0nFBS(%av-W5!R+}g^$ayeydV#4)YEcj@Ku&_T~AVhj&iZ z#&_-sTjS;6`l(jAUJJHfzdg97GS(^nZ-m8GSPI6b z1{kR8Z*Nz|_cU9@Mk|2IHcM$b2guS?&b~W1e-Q&I(vBFY4vt6UaSSh zQgN~sw91txbr}G7lhv>lR;PkiJ(vQ#f@)+NbOD7J!X2(S!{ z*K4g}r3QV3L8D%81-G^8t}4#ogFqx;#)`RX)74cvQ&GH2k$d zNb1Iso1jH=1VpSf71lstn8t0r z$AFUyo5S5#q4KHo1P>W!!m`4z4I_2|aNbxxqkMRIa7TIF zj`mowd@NA8yc(qM1o-D9_~#V(XU(nC?egj65vV@Is+SApbyqJW)D{E7in|-d$+C*U znqs?E2a=3VgcD<7W3=8V!93g&4314Pq7DkUPfw}&@PU_$x(^?CU|K@FH9y3clm`jY z#R77Ico9z9db?Iq0A}1M&jE>G^D+4?jWp#|o%zIf5KEBUMuiFE(n@W-+Af8tdq@Ts zO%(T+!pT;7`s%|#fI*X>Ky(&g1}XvCH8+FI?5@_wrtj-4C|61)P>T0;j^Mq-1&eBJ zBxqQgzIsk)fF!3`uT`g@?f~dk1pxe0j8fi(47`z(V+oMs$tJfu%Q@!j_W?5}!^U*c z6oBgl8CgFdFhDSlyU_4Luu~;TB@zO)S9Z#T5c-@4%g1W7Sk50<8%WZy@p5Whtkvo*S{{@UI-72}=+qXd+s(ls>6XEZhk}d5V56iu zHV1K)VDRFLVPen(UeTFPEff)bkt(7p)T|?Q+_{062=Ge^`76{O^l90dyD!O_mje}F z)E)~{$zDFSd>WGbkD=o8l}Wsun7EWt23BrVhLV?^B??gmpjw#(`5oyX7oeZY?)9K} zssGLqI2;j;{ti_^6M+7W70|^5s0rl}Nq(1Cbc|97=|^3VE<#8dn2T_*bO=A3gixnH ztXI(4bY}p&tdB*o225+Xb{0(3OKs5oJA@2D-B^lYsoK@9Rv9em(%l&tg_%1FYPbb9 zm)72(!1F-cP+jV>{0)?cd$~?n4#+$g>7vL38Y{RKOL-8B)}_px19R-D8tDVDjEVtq z7EBzldL|q7y%iuMlr}@KvUUX%A=qrCCK^l?=)O@LZw31*t#TkcLW7PQK`Ul(04WgP zNf!uJG?89nNjVDW^*E>3K0yt>ilnU+Hq}Rci1oMp9KEMP`94tqoExpwDy`Ac0-J{) z5nrN3V1SdwxI(QIV;2qJ_taIsQ2t!`Uo@!{GZ5rES`fFTh4g51)hLwzqx>bk^}%)J zN6HIS**Y3JI-GX|;w5pK4{LvU$i(Lqi4U(Jaaj|?W3yP?xSSNXoe5kPfe|>mC!9*4 zbFM^3){z*UA4BDj(b;j^nZRgmx!!DTSzB(kCYzf_M)vL7H!PLYFc?v56S!V0am~|O zQsKH76MrgP9k-ncDjeHxhL`W3tTe!_yRp6}tZliXSZ#(0)Jqce)Fu+&T`|o5_>RSG zX98a^1;*ODQ;@tvBB|GtnB5se=a1P`+;%1~TY)olFZgj_cW+s{cVkj~Un0@7RF*hS z$8h@N_@cP&OyGD7Nb6`hEP}T+iprJo(bTYSlz65zkhs1%hSwk0zmD6^1g>+}nRtCj z;sqZi4j+l(@W%Cl&}E}o9fc3chiC#N>YF4`ja zgL(SoIz@vFpqP-F%1O`>!UVy?2VSZ;`NRT^)+;)z)1^ex&FowEIt{vk$FeF~nn5(M z)}sN2&O)jhErI{&TBW7>01RM@Y)3~cy>%Ay)?4pjRXs$=6MVCt| z;R;u-rZUJ-(8Y5e))%tL{*d_n9a7j=FyBEaCIl0U)xLTW%ugGA`MlC}CxgORydH6GfA0lO{Hqw$vH&5(Y^$ zv#V?Y|5bkl{CtWZq)9dV2qsm?M0+d@U{7GI8kV>jtV{HKsxJhD|0R*V)=NUT>~1Fs zNysi1_fK}mjNaU{ip%aOL6%;%YuPP9$8On;iK5A_NfVpwTIv)u*>$m8veN7(TW+t& zpq8g%J`b%@9v#x{2T_>cgjk~VC4^TilXySiB`92k?Pwj@<$rlSC6>lOi?&w$xdR21N$jB@4|CvPJhL8Fca#-6zAkuLk|n zlrP2*U_Y-i&XS|Zwg3fOs@E_Q0Umz9B8N?@E?#boA_O~8E-CY3H( zCEQNi;#Wp*=`yQ`_b&*t^r~Hp_ivzMw|K`y(Zt)NiA}sMbnyw>ge;U= z9J7r7mV~-wo-VfO(u3pkfaOKlC=OUga{&Pz4kZ?IjC5%+K+Mt%;s|ldR|((sKJZa2 z=~`&vvak^kPIAPf&m>>MNFJ~V9_C4|=xAjUwFw{81nDDel#}#_1F=a9T0D0Ei#|(m zo}C+ zXl((@KQZ%gc)*y|SqOY3`HKDyP=hpPNCQ&;uGQDGgg*!-*XrWuGEux9lkhMhZz=yM z{$2q!cuq|-uC%;X7iFRz_Jrd>#!L%V<>~uiN|o=X#SfECc(P_DL0CSIy?TL##(CzN zmcSAH10WrJ7XS5+_^*G)f4w*U^*;D@Z}fip_XptL_eCG1e}9Pn{bBm|NATY&&_e=> zayPj$1<1q&IzXOO=Tu$W6@`4?$=^}!b%sX@>-@H&8wt!3K z5Qai2;e$d?TL#nfBnF3YqsNv+5Rh}kH`}R0u|GYycOz@28g{HA+x9G!|3s__eNA4B z9tY;BhF1B3cpQ7OV)R3!g`r(upGYKyY(*Nzr8Ptr5+p1}---w56mz6vOIa9^KZjxW z26MC_K^F{N@a2?G8h585wRtY=jJ-WZTT6K4nO0hvRPyuY>%`MZQ7FH!{C;h+2`nMo zQNc1g9J^s)#Ccw3J|hRj$nle)9F+lzEA2}XTm@xL=O{|Q#Efs+tTlFkJ9o|Q#GtiU zW(mh&s@svuM(P}vhl<(6qvnRRQf&p_e3g_Pc2c%8x0cAyjfSwvs;B_}aoJ8>9HKK= z^|e$z8ifDE!Wi$CA^)TGsh_|AiK|M`uf&d1_b;Lg;Ilbf1?-o>w;o|%gF$+yrerMC z(QT|KNc>;n#NR3GFN3D-v8V?BiDzDlAiTjn z%G2R%82?|~IJuP4 zXP{|LK0V|f#lmIEOJ~1GA;?t2zTzU&ym^NlGnC{_?$Ns7A_Kyci|*>3)f2GzRv^o;7UJn0W>T}HLt1D9 zR3RyDf$Tc^Iygr~Jg;dvi}5w>JW%;TNO$^rbTsyRFxm%1qTkP(Ew zh~v}cQ7w{pKeUtChw+IoOM2qIVgnCFCZr(vwa{b=e}X3hl67fpV%-AS&wImKC9I8ybgUmviQydp?HX=c zVa@<#)M9pw^w@))aF)9Q)iGImyk4yq$Le6i!#R35(~om2XoPz!;l4OXKcizvz4{K0 zpNM?vGN71H|)#<0u zr5W++=&ZR1)~$}4;W)K$MH5hi-uWD7g?mMNhpLEnwQF>H1D;tOFNfoMf(lFD#|<|) z@fuG8tRT@ExEYi8ScBqj$VZ37)SYnhyxj~g!vv^J8#V;RS_$+MZXyQc_hHX)JU!S~ zuE6~!6PU{iH;Ui|Bm3wgk+E<9n_!%+-l#mCZRCfI281m5>i548ayiF-%b6pldbZM> zjp+nZ^s88kqQ{jTVSb*cFFVIaEo2L1E?S7~%jMW8=l11MAmgaK@uHk&dwe`>#%rYQ zljPE=K9JFFVgM~%CKDPhe9ss$$S@_uHvLknnoVqNf0$1btxTeJsSj#`=v%N+PNK`K z6wTb_XKBD8rHoh!&`r4kQf*D2gD-qYW!spPFC8WjZh<)K@?@fT)~s9=?Z~bt_>IK# z;xk_5ywPm~EKiceY`AAO4~ZQ~HG) zruWf?>3vb&4O3?G@QvQ?_Dq?LHUvkTM)7{BeD>LzRQpWy(}Qg`D3?aG%|wp{H0rm* zS-Pgay(!JKMWU)$<79!HD1QM|Pv6;an`f_%+jgRvH_s$CWJl?y*>Zy#gtEQ^WUXQZ zf)>Z!2%0G+l4$=BBW^no>Wq;89W>1u!25)I6j$(vkKz;j`J#KYE`mP{h;$WVY<0YY zD3h)vv3fKK280^rks=tq9T6~1><6IG*Ti0BpKxei{~WqYnAdVzk!gLLy*c;}!mJjM zfk(0Y7OCCF?dBzb4YmXWF6P(o~&XO zOkg~43pek9pN}Dc@pc1milZ~MLda*Ve>r9Tha)Q1iF7^-zy46G6yU~hVs3P2p5{J} zee@!i)-|FW!d_Z|jdI#c8)HSLW;L^`)wn>2>lDN0B%GaxbG;N7*N&Vj%8%QBN{Qs{ zIxlQ^zVhkN2%JgtAT<`N>ygFW(~q4>%MkY^3^VXzqc^A2iFEu639|I6-CB&%rO>fz zFQPWF+KVx5w6tZDG=Oj7{HdkRN!y)lmuxiKrfY6CMOQ^@Gbp9_Cn&r*?bT&eHQPUl z(}uZh#(ztc_Cr^yG^H~YLFH-0ZETcto-Mu+)p4oVDhgeylt?)4^MQk0zvmGV-0*VO z58>@IX*D;`?fU(d4{CzMufs+;Nj$Gq8D|HJExG{bJbjn6es6Ao)QKGJgl0KUkn@Hna}-856`Q8_22>;(OR`vTXzok5k#5}dRwuWVjo0GG`dcb%n#<)9MJ+>Cot%q$sv=B?% zR&11WX_@E|ORL4d+Su{`u7Ttm-2EVvC#R$P_O6tTtx#3cc z5Wn~{4XGL-s9mi7#NF_Le66{+C@^BFY8{ds48aB{m`s?^C|~sw>mI$ z+cJ35(*dGTIxdPQgVN(A@XpbN`6bku-Iy}&LakNqzB4;p}GqFr>A@sRfES2^T zfGO(i)Me9cn0+U1+nJpCz`Ti>B|LwmNg%?%pa5~i{_UC3BISr54DT`A_WfE{$_<9vd+&E*qsB3%>|rF|BR zh`_s;!R|_u$5%LcY$|v(tY>HH0(CCqpn`07m8Fq841S9bO@#IKRBYt3-l7GGgZ~Bh z#f)c2%@Y*hdVRW93=Q#0NnFcFx0a%-U|f5r>LBh(s6~?hd%bYwLGlJPvINPoSX~<| zmYW_VKOB$}0!zOoTrV*C($^V7E=7p^UVCTVQ4X{mE^2Dc@(%*NSr`o0WOCFIoErMO(C zlA~9?b%DA(r!RFARm~0_v_u!ubXh?H@Li$fv5{-pQS$MRyII6@z+TwOHtMxa zqVx)qox?s*Qq<-N(86WDA~u}xh)tgf?-8}R+6OhkfUB@kP6kM=Vo^XAMRQwjfV$Nl zmOZ1?we1gyIZml0em^a{p5QkU&x_ygnay3K-uu=D@hI+TO6w(8?T&hO~H+i>xA}c&@^X~?z!$!T-@QKXxy2H+E(y~{UJHD z%{^8Z9AY4Na>!jhk)kV=cl3`qVa({^>!7g5=%J~x4@M!7Dj7XA>e-@)dKD#lC}4;9 z*z5VccSjHRZ!N)zGm)N)XWZzeYrb*}2iN1%)*-yLy(ffoL~v0()R=_lnP~@@*V=uPB!Mq$Zss=XzMn6ILUh&LHTk zy-??&rdL2COAr)`RfE#v>E#ZBzSHPSH%<*P6+zIq6I|(4Raem~2>N%>u^MPXWo8Y6 z#+1ft=Mp&7hVNYnrC2?wIHrv@7L>^E>b_NTflX z$h|O|pfYQei>PUueiw#S1uC7h2rC!P#|c&-u^tD-VzsYq;Y@$;Yt9jRUv9W~6!f?KX`OA>~6!(9!d zlkG8h2yi&K0g^WXv4Br2jp5JY-eLu=kEz04GWftHWG%LW5xDtmWbeiiu8T*S zaG%3Oam4yUg42t;+e@Ojq(75Ll~Br9>?P?kj0}CTKO_fVZuFKez>2KjPmrZo?ON7f z0UhU(^_VuAted2<$-1S^u0td>lIkW9+4JTZ86;248)uhdlk3Gf)(^hdf~I@U*!!JS zwJ)1o4|$0v-{kr#Hgb6x$|l#+>)F9mZDUI>lx+Kf4>V*iem4i(IDrxBJ`>nuGi%8* zU8|%|Zj>}m=tbBlC!wVlvRIK8#T4WQs9SAg+007yQBDMCT!CgF3h{e&c0I9~g~apX zw|hqO%`D;n+qb3+ACNY)L>$Q8qQJ~*$7U9q-E1>!Ct$=jv*_2XZf4N|D86p@nybQP6CCXdH-TdZ^AuxDY7d*}Z zR#5z@TA8S{wrt$6VF)bvUGTKTmJP|xFgn2_020w#y-=#cD)3shLUeuM-UutlZ4cG4 z8;O-At@n08xI;#J?$m23x5rqa;5D_qo#jSN!Ky^LWQCy7VqxdlL3+~anU?c;gCq^D6xFa>JjfqRuYJ!1!p=93o-H5uF_wD@5 z`?Y%%7xVZiKFnLZ+_erwT`-RU(G&Al3$7%VLK_cPT51bcTE9t-u61MY;^@iHG^bwO z;2y=rQ9gW8~t}&iq^o-J`g;#7EJ% zbU0XR0wek@@oBn8>ViWIfSx$CNEntj1}$J81wP_WiBiXnl8Z(6=4a6>+@rWy#7EIs zG&g1t{F1o5!97YB>|uj@vd3LLk>DVW^62Tff|v;o<51|E;9$8&Kw9DlqfY^$66p=P z!$HXn#wjbjM?iF=q&5hsVG}&$OQ1WoVem@0kE{q+?6TAA{7NynYODy?qQVX0Vhka* zx$8?&_`FjTOmF-U-a{a913m-A{)GcN<7oqr`%py0nI6GLt~ir_+Q5Zk(dSN|a={qh z@7!oZA}Ox|38mzd>jGh%ph`GjeCsPwE`{T;BVDGGAsk|-_IE%E%k~!*tM0fZ#-^tc zI4>MK)(bMe0d_Pra)rp$Fr8N-!D{GO-4sBzVNE26X`^iln54073Rvo_$rc02O0#{s z?iGQ;UlSkosENsPez@wFRMnkGutkWo@H<`d9M_RrAbBJ(>4kEMPJWPdxXw#(_&(Rw zBsjnpJ^28O`zO*Rqc=C@xkwiYvh=E|NJm3ZIdA&GE~uMJq+{A>B5jh!CeoHVYZD@! zlmy93vx97zzAA%U${tl7nTB0j7SKc&+$GQOov-3GHFbtRht2I#E!9i+(79mig_00| z256~*m@W^9AQ$RCC0Zne8jIDwdehTyH+pkJ#)bN=1X+63u7&z_4Q6oR zw|4J^S;d9`>39|I6UC)8vLC3k~KujBL4w$5| z%>hfDixP9dM^Yr0&91X2#K8|_7+fa?$&rn)2~Vf7*D4Gn zJR=Tt)(Tzl()q6_@)2lcF}ShVONbqp7hGlZ<%Wxk@Z|(odeyE)_({-lE)k9?qlvId z7@G)N>YU(WwUkh^WwwM4Wzfk}Lf5iLmK0I`H_E6(GDXCTYg_TBMC_SfGQbt=9fVs# z!D4a0WOLf+D_!6e_4;ChE4^yhviUOTIG1e3l+k3+LI`!mRs z5J_j3BA4rDIXp_9YJ~BWoT(?@WX|XJEbkosSSjn|xUk?WUZTWPjQ#@~xk5imF4qOy z;=4rPsg|Oa0ZI>N)q-N9QJlh6WEVU;P|_nhAPqhAAO+$(K6p~r(BnA-g0l|k(P!3q zWQi?2O4lsuQygWOvv3|Z%E`hmFRDB-H(cGSp3c0)MU|;>sN(<{G8wdsDzP*}D}w}@ z`MH~nR_PS)9y7WdGnZPGn5h?4nwUA&6$$(?M4@zCP?qZ`<#hbao9%|Ctq+)LPG?6pr zBODx|BAxmj)`7uUog6^F561MH;VUA)=>{T%7sm3m)JArtYw-w|Bt zRaM)`Yj5&!fJU{PaOzw4CS%HI4yj2Pn?q`;bCiX@avGWqoCXn@V~=K#M={4xD07-) z|3+2&at!D~FQ#+9<9uwCbN(E84bKA6wnTv)$q$my*Z4q1gx(GCkP`(ZkXVZD(=xrj1tll$9T5 z2}dsnW~!rSVwSC0_7R2BaZv;wrN>L)oudm!Pb~4mCUG_*M^6}4_7(+3afhRavc?>} z&)_e5k$`^9s-t&~;v}G-S1IN5^3KfT<*^QXaPgjyY5{XtxmpIWlV0A61Zu*JwS1A< z`w`%dI!$#Ub^CV*PWPTwz} zscY>soW7srsGe~8BwwQcLP$-g?`u%#>-3$HA)Dy&op7|poqI2-EfbA;_y{UVoIb}kW4-eut}d8W^fGsxLKP7K$tZg+8Iw>y}*Yo z!X`Qw8|Ac#PL?xR?k`@?&!=-18av+VEaiKPYLVpW4lm%jJNG1LWO3)PSe1{=Bqqn+ z_qfrQu7wR*6Ykt?1Xp@h)%Npp=R)XMb?0ziTiv;sGMYPQ62|7vS?UZZJeC|YE4nQg zeE#Xm46-Q37K&v~W9uxc+7~zORnV0xIO!ZjP`MlT3T%{f2I&_!h`c^QUc4(tiEzBl z2O4tU-b|!$L(R7>5b8b?*u#DMj1Ovp(4WLcISK81o5R7}0I72~+7Hcg`)vvDCUy2c z2;I6&5s*IO_gAv(iESz*o)^EJ(^+^gVr{egB8GM(??spi_7(+3QHS?}6ldPc%43B0 zLceC!dpS=v@tduBqqsZgF4a&j|Alwig9o!d-KZ1r%GEvT!>qI*C`>l%H@Qv>0iV*9 zs@sn##BDq6L+i)H6rU|m=CT-dZ%^hDXzEfQh9`4jj_L_dMsg&ZAA zkC+_?qjBgtv3q5eLGor!%@+K1)2LAIEf2=1fU$BqVwLI7tVMr@PaN2zqKOX~&@lZW zy9c(iS|LjW1xqVWRXJ;_e0ZTJor^jw27|LXL4aJ9m|QcwRrM?%!U)shE^L(3beKsm z=Ve~Nb1&y5(8%KDV6m!Kd+~DKZ1m-nHsR&Gf#6E7s#;23Ud~&gW7W&S8E^G+V#;V< zj!77smt(18_j1gNPP2%NvOmcni{j;=Smrd!?x3oD@p2x8u2jKE=b+-{JdBNU&Y)F0 zor*B+vs8XR_koaz{u3gP8x!vNASPZ!cd}eq!b1NEeeG;Smatf0>@5n>hXBrw!xF~t z%o09jm9T{A*Q{E?;$>mJCguy`XTB`Vd+WjewZAMZpydC}@v?9X37kj;#Uh?c6~$79 z&j+TY&oZ~!dv@IRP`t#$pK=u_FjFpCqdwPiq)8yM{mtigWF;2h?=FOYU5vc9;d?bS z^)P&|r0V)UguEu#cy5P+GdyC$DoI}N?jGdvZa_{~E@;{(Y;`G4(G>`(X{(Pwp|7oO z+<9Tj(81^)=rm!bo706Xbo_+{Z(w%M#revrI;5${^4 zW#^hghWDX+K{!SOI0#gDVN8V?wz*&LLla?h{{=R3Sq%P{Vw@p0jNE|0lTGqbg|xR> z)m;bYM#P7_aOEcY`=F7)$Ff`Iq7OkEG)0ft|vC;ka%AF zcF%Br&AKoRg%#^;MBxZwQrlY;m=bn`BTz}RaKuA^5lbtiU$YyIV0WX5`}ledN9euv z5RNd;jOqOrq?w!CiG#;g@wPXCk=TSYi>0>uIj}=r?RDvO2O|C}ZhI&K5hiA{we@zy zaW0BEAN*YmO+6wI^K(>Bxbu=J(L99IbmzYhOzY8|H#u=IS`Q;7-Fc&)&7Ie)DDJ#~ z9UR#oaxnv2^|5u&n!awMW5IFZbg~UUdXK_$xJZuf}Zs8|f_Eeg@|0Z!Mx7=CAl=p*=x&bQF7Sv5q@DYYlz zZNH+l-16C?>E~M#9rj?3-mWLfB)V2q_z)fC-GVQu55beglT)LOaBn5t7r*60Wq1jo zUILH$v>@-F7bfl66^TKGLbZI6s^ho7ABZhI&W2w3p57`WF<+rK}Q=8LNK z3XHj^>RVmrS5We5k)-|6t6j8r+Q~;kQx7|NDOJ~Z>K5aHaFRogqPE@WJUc$rJ>Uff zNuF0~HoHf0-R93n(Qfmfhf_|>3HFC%#Z~UHy13Jyfsn4QjU+-FhmK0B2OF!Fs?`L7 zdZ9$L=tk7_5W}tcSyy+D;$j^iMPpsYOHp^aN9%%l42YhXcciJuZLAxRl?3C|BFVOw zxlwen?Iroy_LuHaTx{c`_^|D7-J^BEHU>mbY+Fej(HC1A=Z?_o3FaB4l7F9Z}EACNT{Ntmf@$de5bKo{W_#b*KR}`yF^(v9a-NSalNCu53Bi+>#VIXO}L`x6> zGYs@oux@8a4M8e&3yVGS|Gl+i*=CShzLCQF?aNmk3LYt~QKv|1;$mxg6RV6Xn1C{gUg1jE;=90viGMXfsgt1AYrOuH%OzuiS zG^=L|;yoF}@f5_1$?W418|5j7o68~O(KRp@k2T=(J&NfBg?*J)x!!J3(o&O0ZFE7^ zJvE+Nrx!{Bd==19g)N=yis1eS(I6qXSgiJyDY(uzrG9Ai=7x-m?)M0?^r~Hp?vJ74 zT%sG(MiX6=G&a$-)EUex29lv>FWD0P?-?}nl;|@_qAA|!kZ1bN{UA-vs_v@D3za=8 zr}57?L6^34rYn-W78+R=e=P2w=7x+*?)d~+deyEacMEi!OLAk{Xp(D^#wNLz zI>(rj%NQ=@)NGS2r>A64$Wu-?kW>lB+##x>icMM#|C|e~Q7Vyccu58~6KaHaLfc|- z|77+aqqlTHL^5-keGWmEUbSnPy$?FhC9^SYG?_I?W0P4+o#RZIjX5tlXg13h+dUaX zN{FqqOOY{pb`GV@_Yu*z%ejvjP}RQdw|>@3p!k04r?8RB4OOyFG_y`U43zvJ+4qnS zRFozXCj9xRZDRHp(aFIg@gql)-VuF2upT=;Y**unUD= z5jxP>h>~)JO18HsM5h3puB2T2&g{b5@fRiK(y!UI3*kjnxT+{VOx3GhXmr;}{lgk31P61^NDHSNM@L1B+}VS*J0qYp!`NxRUjXtN8AI*MH=zz1{aZp0+h zEJ z*ozNhqkIc#r@!{%Prbn9_TrDAk;PubVpUgW?m9azmtTCc-j`FhguS=`8oAU**Y@HP z=vcKEaW-4+#h5ahy=W4~ZB*dlr0)}j-AHqL5oIT*9rP8d+82BALNESsd+~g1luyia z+KbyWIIh@>IJg&`oLmz2qR=Zs2Ra*(y(m<*y+t898Q^r;i};<{i<9_^>_z%DtM=j+ zyak)5#^6gg(qGMIIc`dgD5iGYgAI9=9>+-?La`=?f@)==(%Q0d!-gSvb#oW&NN(AX z3_Y$+?MoP&jv8`ly$@Kcs#0AOxXsSz#%()y%zQY~fv+Vnd5V+^D?xeeV&#qo~DP=hIgqA^a~X4e#k5^mj2>@*K+gxO)`W5d%Jo zcEsRtm?;7y`YrMKf_tPcP8Bc!(v@!x4lNRs+Qy&-?4!U({3%iTz8fXiDUfgHXVI_S zqqtbaN6}a`H)at0lDI5B#ie|rF4)5c_hgT|dcv=hYA^azB$Mga9e_e#ziu$+I)Rb@ zGl6Oe56_|O$=5SBxl)y>;`IsSz)E~J7YDG#9hZa9?fO;zNUF$Qh5JFcDpv0a$x~Iz z$yrW0vH5?qz8(S5HgKqC{P@RJKI9Re;N{pTpNZUACl^!YT*p*xeCM991#d@726L6x z-Il_wUU+a9@oCV=;v!b-}jycG8KkRfOkC;BL~1VyjZG!ST5g9-%8X8pSDa1K`%u zHk9z07C&gxLl2U5@AtuzSoe=aWH&u{)Bx$xXV!US$bHoZHNnC!VWXTZ?DCE3ALfQj z)f3S@&@AW48_OHjshZ-u&@JguEjtcNRq?l|P*5Ib-rxK{ZZ#W6iUZM(N!otUIOnNU4(yxF<}qp7;6xPe}v6uZ&6^f>=QDhFmANUcY_V0xg1^4Cfq`tSNjz+b5~K zZ*tBzcL3+faobMoPzG=$oh^|YdFWCPY$)YgNJEyRHlm}Pk~-BGL+@Bj8Jc>;VoEux zCtOL%fM^^cHC@STpwQQq46-B;eaH@UobVxSf=15c+N>@WuTkIu4$uxnDRZxI%a!na zo8m1Z2jTfPyf&a1j2D~uid!?RH7hMlG;6ksjaCy+LUdVEW?g5==mB z?xl17f6xnR?oz%N8j(4aaE$PaY7&9QIl%?46f3i(bI~Uo`#l7OQ~X5cI+8i3$?=y| zwJ+|`cMM{zslktVN*} zgc5T$B5P6TYI}Iv-*5c{-i#7}C*Q{ELXX{o~rIgQBT$j^U#Jfxu>}X9F zF{H5sS#PUjsQ}o@!(g|SUl21ORR#)4^oUT2EM4eOBYYK zp}0A2+i4tXLlKE=F%uWZ^daPRThaWe8H~n9(BVpnq}NR`&fcculcA}H>3D69>Iu_P zvLw0&AvI0MbD+@IbTnQ8OG??ns14mF49BF{k=f|9L~$<0Z~h7lU<#dw$YdIg$Dz@P zHeywn2Jq0Yy1|fML2~#mK9taT`iwArYD_DeE#U{c+!51dh7G-!`cOp}dN0C8IhXa7 z@yrdt+S}2d7z-OV!%mr%d^uN2Bp2V{1ur-8{t_D5O+5Ufs!31}3pk8&OtX@EeZ@Sh z*V2Ga2@}>F(lVz>@ItEE7i;d{4ZPi;a%=8iu~AO8L(~@Uvk5TfE!HcoOp^BzA6UrZ z`X&*=4Kug65N0p(I+-ghE-^iYE^#&@i%Tdvdy8y~>xi|&;#!8k$l{`3vube}@ws+8 zug#S=K8HQ_V0Ag;a|A8^D`|2Wu@MFZd&$!FNy_pdFh|uVx{SKbu2bW-hdw^XFtiw6 z^J3g}n@cIz%q>}t@i}TNsdI6Rp|_E>8Jc<+S?A`co-nc`1EO;fQq#yf847)kET>h( zXnQdFBj`9`X4wQSJ3fb9DyEje1J89=5AVyaU#zMY9S*a=E zn&74zt_i~7-NS+6P>t6oMw*m2Fme(0a`A{*4GzIbQu20lna?T)OHPbdY zca5|%N!}m(z(SVv4~P(Mn7Jj5F!!0ffJUp`fHh=lGJa=4E0d@#JGBckFTqAR$?W?~ z=Bc>>Qm1hAUZ7&`oQ_21L+T_x1)y=6M;Nli>2zXtJ>i%l@x1u$oXo;=5-Xcnh@Fkd za}suoy+wgh)Df#jinGjyrvOIGbE03rH=Yxoh%WB#XJ!NMu?NrT38|aF0@^sP<9457 zP?GMG_#|g}9XOP(Ox^C&p1AEHcAo?qvl6#{UW~Q3OZ9AM>e3m;;fA|%R8P26k}1)h z2&w5(?SjJoxl}PH4n}_k9XnkrQ8&Y-;?)$FO5g$7w2UZax>Re>rDC%M5*lc#L&gJ4 zct~}CICd+H^E-3Ylyl{8_~1e>Iu~(J{)4kPEr3>UiD@;%F3msr&_%d3AI3&GErk6w?nqMEF zs(tZh3a9CElg>cJn>igD<(xdLb~-g++Gk1h3w$6XoA+F3;acYuq9Z1KCc5U&NcT

+u{hCz=XJdO3?raYE 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≶ zh^)hdQFUN`kB^h4O)$n7tm+<}FvgD=d3mh>zH{L#q+IT(Dq|ewz551atTM)Eds@AB z0vU}lHi5A*#+EpHTH9K-oteblWuT)FQt=}x04by*%4r~}_zn^^y<>?ryi6d)-?BCgl)v zt+5fY@l^|xRa+8MH4gkym?&^8liLC;!vb^&bEm+n-~y?=y7E&%G0>aIJ{i=nS3+r0 z+L5&Psp;`JYmeYXwYRr>R~T#0CqK@fcoUI>Cpe{LPm!mHdRTizoU-c2^(ZNF}NllnS_WaStv7dr&Dy^HH<4gxl!0xJ>8 z_2fxl2~x%qZ}k*NoZ0jh&*obg>eL_!>W2g{(R}|0AgOP3GWh;`Nc8S|EWVVz2A}V| z2Gh|_748_#%qb#EcwCkXpy13$M`DpE zp>0~Th##xST0B5B-&G`l)Ex#2*n$Lq<3i$e1qmM4h6I1d^3jo4l@$pjwN`c$iKT^B ztI$8>07H4{!s9ZD$FWLu8zjnWD=JrHMFkyJmvj@AUZOzM3d7(Vpiazn@S8K>;E*YR z3%AhZm?UW2C2{HbY_ZHthJDNPVO^-M%?clSvxYl^&#l8)DOzQOQ-=15rD)$40}rP) z?!-hsS|fEnO594dP?{B1Dwn->tZi2f1~wq8L2|DDSX6ZxoBLrR1)vt|tJpDEox&`% zjed)gXD`O1MqXYWgtNI{BIR;NRe7H%Huo!#vC8_OW@}}01u`0&YXSqvC60Zt#7SMn zZl-Z}FHYxvFa;q+QTPCf>fej89o!9nf?}vbrPg4c7@GJtU(r|t(nIan^ zpNEo}5|+Z{;T&>utDiJ23G?L`K83l@#!m6K95`1;wLg=~(@Fa$RFgCfg#F=YpW!Z9Of^?}|kpfR*)bwV_H%r_bAnVN2 zgZf1v^ZzCiwJWZ*d!s~&xz_H+L_SqWE?NsbF6ZL`=rf$~<54*I8;W^IxbDgM5N{&l zA>jm+Jw=`l?H&&a4*k?a@_ncg^^lNXljz%97o_;S?xJ@zXnKjinI{92@K-#+mklwd3fC9OMC zrZb2MP`bZCf2FhaYATo`L{tgilztz472oN`J3&&P(q{NdUM6FZdoH!I^|FO3FJWVu zZ2i*^7|Yflmr2NRqr_oQ;{?H%c(F=P42TA6XGXvi$mPA~eBU2n-m#REs z3=lsmLCl(?MCapK(ZQV^f7eZPI@@DlVDx2)QD^xgQg5HS)6P5LKRiKp@5)!RLWbKt z{-K+Y{Z(yZ_-R(yaqq~FI)mNKEm%EdY&yefT+v@9);@gtMIoRK@T_ba@&}wvN39RJ}H+ws!Huf`NS4L#wxXo zMx)gyCXmsnT@x4^wQGrULRL|bFq*l#yBxqs)@aX2!4TnQ3?#~VAOrb4iP{yWwl7LD z7*l%{CJOA5_I(bl3)x>vz(-u9 zAU+UWAhqLHPKIQGzDCO?2xr&72eM5uElGR7H$5KjU zw?R4beo>Aeo&vkPizVgHa&Zx(4{^Ia-UFCq{q0A`uqsh%#}`G8B7Xo0P?ZFIw)S)T zzY%FC?Z_imV+);sddH|U6>fpL<6VL3TJElirloe42v92{RMzIe?6ujq^V-Z5wc*EKX&#)!Yp979ChKur_Ie!M zc|B%{9X@1GVB)FOMcFmYlm{h5*5kbF^;kpdQ3*M=|4RQ?e8={)uY@1lU-o}1!?FFe z2<{o&+Kv3t6B}jZ09tt&$5PYv_6!8Zy52&LC1a!Jw#ppdJr&AS(e3n`tq z#TFS}KVS=AmGz4hg61<>D~|4{ zPeEdz%AmO6{$6~CA(wE&9TsqeSOSZ0ODw|D%O-zZlql2@$C@17O#j%;nx6zNeR6tX z>>*+aIG&PlbdagU=$Wh-p?m0GyNS^?j9@j75nJ4)gTxY0{#imf@!yt+znm3ubUD4) zO~g;gc9>WK$TeQTu9{RMC9$z{JKqWaXjHY?KBX@bX#fsR z-{6VCs=ko!%!X}OybqAgo>E$WXym1H-iDRTo!S3P%H@u#`m~~)**}JiRcAJGGFE4{ zKt^+Bo4~lc19vHZKBXp#wf7kk)xQyQo}gFJk3kns5a5l~4+cBTEMFs3Hqx{~y}{kc zV|khI>;j1_DNagg{3WHj+HfdS+aXA)TA^e8PX z=Ls`nx-8z70!u;`y;<@twGRahjBKG6YIcF^NbVz1yRuO0S5YFw7HU0%iF`_^gaKEnw=-esJ$wN!j2Q4gM#f;DGI@4oJSgaX6xnuUqN@M4RZun-ny7W#yF$m|ip z1j>z9*?iDLhz#6+ut$S7HV$g*;h*$+CEud#T}IP6nUzk zdDd5<0!P#NAGNnKy%-a@VWJtu4nwf>n7R$hf}q1Hc`Wec^Q12-q#+fj}9 zaxiItAgH>4KA8BIS=B_^N!@R0)Qv7y*iKuhZx;~Qe0<*JP`5%--#BHgdSV@i zY@y0cSW}QgIEIOlc!kwGL`VS*7uat5! z;t!-;?x?EL8AX6T2N|o@B#vKJ0#qQQS(7F(Hfz!nC&O+YDhjf*o4I<`QKAh0AO%CT zB$)AoavrD*KT4u@#YjA=tV@}@xfP=)kBI`id1}E0QagQR8YBxOt}T|Pv)fl7 z+mv=B?LC+tk9RHvFRH!0U7PRZ;S(pH{Jn{YrO9<8dx|_o)MIHP;?&Z7KmH;+dC0Fx zwKSI&ip9R+Qn6g<+kAAGIeEBrs2eA8sd;r^Gj3iFn{d{X`%&=NEj3qpwfLgk09+lU;PSHy)h6uV!TI5WLIY0iUQ?uD z$&EX^sbA(JpB^a3yw5|Us&l?R8vCurheg`$sc*b8EY5F|==uhnT{%jmdU^%)cTj#AAdCO&?8Sek z^Wx9aI}d9kZKujiB?|8bT9~ZM&J|xT@EZTGP;5hXtL)+bif^|H`--+(WtRmDc(w4S ztj&S`?_}7iLO<`GYy#wMHR^DI-3t;KfN;_+59LS@Ts^4HbZa)aS5H|GC}ujSZRw3eGn zCSG1Q_Lr>FZT_!iXg^wrj@oZ-QcJKk-)?F&UPC=lR`fo9MSYETcX;DH;QxxR@z_^l z8t)7KuVrXFT8PNT^G8oG!BXq0d;_OolL>wh0%Mur{oJZ6o|yS_04hQE>KY~CbPsT6 zqY$^e)W8FN3M-IG+`d2*;aa%_cWvkRb?&%KmfmiZD-ewj0}5y}& zt@VcG$c@!U zL02#3hU(209^?pr!$sg*YB^$n;I{e_+@#bq3j4@#buK8XBzI9#{0zCn9**I}2&Jv9 zQlkdiOZnQgJpg9*3 zS;%b+Rt?>Ba@)Pq?kFQK-Doz;#a7zoNx9rnRh}z~++GYBtK>G0)>d*`Afu7nCNMT~ z+Y)E4rKM$$H50h!IG{)gwYEnJJV>RoyD@enQTtu|~`x(?@Cq$m=W8 zF<8wjU1r@5nXN`%{&F#y9VO**M^$CEavDSqfH~C|rh==nbGuAbZFU52TTZzOtoIYvl^fX1%`*8c&h$9V$1e(n2&Q zn~ZA&317XYd|aU!J^@u!rPD1Yill#(;F6GZ3|8~%MAE-%erpNZG$i5JPUs$0$p37Z61}H(c0M$~8wp zV+WIfDBh@b_tzWX6>L#k0X6Pgvs|y)`hdbaaqUDDL6QHLqU4N?^#4V$NytA2cT4_f zSM|KyLX8c<tCNu>yBq9X7<hY|qVaFp-ZulY5lfGYI*Mr*ZJPNC}Y*_LrD)5QgNXfCgjGVtKm^ZNy@; z{fG6z#@*{t#u&u=V4^^XV;7q(4K7gDVaT%B%t^y7g3|a@Z2*O|_rmmee6bmV7uDYW zzRb2YaYEa^bj+YR$zn6^&#|Y-Q;Nv5*bFHoy4b8AYD5>CkzbR#*z6o}=bv$Z8tMW~ zahhY;GPIS>WoThv>0p7{a@{=__C5EWrX#{K;G_rza@Nk2?(Osm%Rm&`i3ZDz0aEhq9^>=vV|&l zU`b6sb`t_){n*E5k#O{Ie+oX9@NkDp>}}&iPq&ZH@@d5@&M^Q-;wZwA>J^{B%|^^H znZPedDg6SgET1y;PHs*zJLYz#&xr<`V9)P&W5D8?)#I2bP_vrs?M%Onsvxs@pM*pf zk1qzR(?=&hg_n%Hyi&?J)fY**+)-6!Gs@%p3S_L>yf}JUJ-z}N&E_?MvDv(qI9=Gz zWajEsLx~dnixdpef?!4u%6Xs?{4El-EB54ZHC@Ww&8-+a%P>)3m$dC!Vgt18l^j{m ziYXZZdljVcP5TPiSc5Ku9bsYi$5f4vgV$i9K+t0?%&ox%QagQRHY5vlnOiJOXSZ)b zwkhpM+WXq{c)W8Vcv0=`?b>`h6Q4Nw6oHZZNp|GV!b`l*06+GPC^B=wDXhROQRB)V>H8wW+=B&+X>vP1yg$q1D~ z_FVSb{9EU>Nt|e?*Cp{9YNCb7dd#f*I*WIA)O1MNVXJiSPNGAz*W+MPkD#qXNBh6x z+rz-VqU~Xrg158rv5o#ATV|#ITN!pR&?30!VQV+?M^7+6asaJ-54(uT{5%4IvCPkc zz&Q!Yo@)TL1li-6HVD77Fz^EkrNb)+l+G8~P~bnIX2-kthZ!0>UcqD^D0};WjX>HV zE+iUY#RiEuZQ;%1cqRJOcJkgK)&R*D5`e9-093YM3VI=tEJo`3|Ms%sQnOhYEU^RY z=dT~Cw+0)qG=i@6CkrQ1E+l#rez{v061_QV#qnhJ+aa-UW&B7MbTwju#T$*Ug%DaCZ=q10nmB&q+8s$W(TVN3vps7ZQD@n;1>} zr9cLh|0JQD_-{+Zzn2wpypZVeZX%xf_8a4ZI3yDteW-a;oIsgAcBCgF^ zZ&y;be_WNd?eIdP%Q|a2x4~__kcjE~sP-!Q{={xlz99x4?xGpPL_QZys*y6;7ZSZQ zs@lwfeHW1iAmH@vpBSu?)aedv_m<_48F}f1w=th`2ln5Qa=D|bzN{z*_5+Zy>cB=u z#_GTp$Y>616Bu`Q04t7oy55>n6UEwlGl}ZoT|7Y-68#K5tv0wDc`Ppzo*$F)CWHrr z)x6S$$Gvy>Ka9NmAInKz-UoIv#%SgH0Q8SCp36OD+*a&1au`z*3 z7n^-kY7!|n6Luj{F{+u^sB;aeTw>J0;BJY}n30!TCJ;#`KATCo+)*=&&-IXTkoX8> zH1RQk0pt=F5?SKdFC;P}rpw~hDX=7D(VHdT>~lxJz{nP7p=KAjeq=L=+LgswUy2eT zwiD$MOypBSC1D?UAS4kP#K3P}A9hCMId8(j! z)>WYbN7q&LHsBXoS4DnJ>bk0x+G-)#pxcLws`_g6R;d}bvg%m5hRHZJ+LLt$Ypd3H z+{{M#M;i4C9B9#pM;?pQw&)Sj5SBa_dVg<;DI4jHBYCT{p^epPUhfkA}7vb zma@CYacr!_ni<(^DwkkM%{4PCAux8$jBiPc*;BSvJ^^1yteWwT7i8VcQQ-sH!f~8H z-#D~DR1sNjtZnLMs9W$Wh&9PaMphgPZ#BDw04A| zNU{DbgOAVa`bceR0=T_uM;Mf$-9IhbJ(crqFKd!p7y9T?9DSumquyA4>KW&sf9{Ih z{0)s!IKQI?Z3O4D!XX~y?R?r=a`nNt@pEipiYK=xZ@Sx>-;+1JPjL-A9t(QqF-$>M zYdW-?2y(BI@pDmV|$*^yEKCBDXky+uxHBLu# z2A^AjvHBpD5l$J}C)NjfP7FNU2l*OI{OYLJ}kOHtKjL~emd0qDfK?-7I5j%lH5jJp8cet_$Yyu3OHCvvxta=D|bd{30` z@dn6PC2~=(wfY_fG8&O<0t3h;h+IpYz3j~`8_!JR?p~Z_dTk0qilT5OiR$-hE)X1o zRYv(5U#o~J)*zEmB}CTolTZv*sNAiKWoH8V04aMyKrvX&s}lkJx{;T^T+Gw(RZ=c@ zR8>GLZ-L06{>^Vf+#mrJ$Y=s;0%H?UOPoCw0hMhn+s;gqE}tJw0Vq;F4<$1ttTxZX zNJCD&R7yBA4@8dS3o=F}a)# ziF{g7X1Ux8G7geUfs7`XCNMU+w8WWfX=T}RW`cC7{I6K4^k&IDVV4Eu$~-)%Uj#D# zXOgI0@q}F#B}&W_b}=UMsY217uz?ie#Qw!_!jDJclH*E6=?WFA-XB8&&v3?GjqOsvj*?sT87e(!DT#(nl z@i|E9o6`*c#-{_KCm43wIF*O6q$b1uJ_w9u*aLQ@vIc=mwjlomzwQyq&|RstV@PhD zR}L6}d$FFW-1<0nz8uNM#jC@a%E*TYQ0lD$+_wTs9HhYevVfO$ogzp+C=OS|1Q0U^ zrt)9|wjpxyzAm+1&SA~Pc0}FP5kpU#*DJdC(BX1wyD78i#wMf6n$-Jqu zCYNA79}8yXKQRU6)n%1Z37piOuCy~Wv0pS>a65{HKHqf|zRVm7`o39U_|_XcXOsEq zJYNrH=A3kFG&-v|lE>2^v2XBH?66mf?=a*N4uwOZzO7Lp(bsRdXa)Ahe&~#F%|LGv zjw$FJfUE0Dlp&C+VdY>6MNo3t;D?Cv*rp6my^$V1u$%QhkqmZ!UN4MNqtzB0s6;2K952Dho~K0U#HvB*^)7|R5)C_&7cqeN#QD>}Hlbz?WtIki=9!R?wv zk{B>G;r1k7*UMD1-br`?ULB<$l^VaKhpZ|w|rH_c#mmMOEhqP9$|v+SW5 zc(}9dK};0J5Vg01gW;P|bzlyLuaTxr5V;tv>Rg==xlb5*d948M0sAQ_mpiITm@c|OGE2Qf_ zn{^R#wN*c#2jikLdq{riIJ%Sh2-rnxb z&xhrcA7@Xzi3nTIDJ^@7JVn&Q)+6GSt-lj$MA>@sYf{Iwg{rg4Q_SWsVp~0xAh0$NFfSMg^f}yeFVJ~aWB&NP`z!MSF z2FF|3EMF4P+V~%@%i!~n)HhcdE`x_jblvHp7EcObN-d3uMlF@MvX(#0Ud!)wUdvbW zo+Haf`WS0>?beyO$+#pM)YA4!R`CzntN0t*GBx$ioSm2R&%!VHm6!cr@g2R&z7l@) zYR?wmh*8OK^eQcady2D4tUr3f$tB0C%BK-5)5&!Y1jahK4qXou%zCl{UvAOxak9z#INVHyqIEh!}Vf$pbP{B zc&gCFRV_f&qn9ns6$@j{o;3p`P>zPkh~q?s_l6eBc;9q&EQe<*K@LEC7UX7=jbkL~ zmU?4TbEME;f=fw<%e6%q*fUaS6ozS3xN%JOOW2D7Eb~Tin+?|L4al-kD+2Re!=U= zQ(Bl5f1J0Kz8wIA)x0{9;>V1<+@guVvsJBMA?0#MRi(IcCqxdUO)B4jxIt1ZkkO>r z1jZ)CmN=>uD?%U}&rFjp#UDvQC{l_~POAEHAkNpt1?y60;+l_DmTl$%TOMJ1C4qPa zs-;SvTLiFzOp5%vc!?1o~LIT(8r zVglVHk;jh;M5D>$FG5n^NM%rQpCi$A@_0W5HmROFnbi(;{eQ_`*B{Wrhw!V<`oH4i zSJ_v>`PJwB-^#$R(jvGgihxF)A;u0S*cv-N1>f?)g8m`LhFN1iebMX6=z*lb0fn^C z0sgP}T8MomyoHYRe=9=^(IP~)kUx5Y0hi-!<+CVaCIkL{2<#FAZj7zlDr*5THv_Im zzYQ;ycQD{uOoajG6$0+?e%5{b4EUw1hynn-G@SP&>2InJj5Z2{J%*EyAcCBXa|&k- z{4+NNf#@!9`kQxx3Cmyhs|{W$G<7=MV6i=&v|3A%^X8y!E@VZXa9+0@gAM0(2QZP( zn26_f_gCgZNykz;feoa4?y}QP;uJ4m_QQB}$yioSg>WUNvKI5=79TY-#58JNJhhhZ@5i1~M8N=+1k>N*nDPqV5* zB^9hNp_1`!LZ!TwLiJ53k}3%97EySpuaSBrcqj~3^Xi0$dcw%dUoOT&{gjl;9aR;o z$^#HNlug|Raf5_PAfpMD2~4_BeL1BjkwP^eRk&Op5B?)ZOspc7F`o(Xj&S8*okn9L-G}$qs zv2mD|IP+{xEW5p#B3&YnBmWLR=0;$r2+ji;!VPi4h8iT1>ABuFWEs;OrxvDTqOj?{y`!w6IXJ4K zj7-=c5?Qo&3|7baPVBy8jl8^~$yd=FP0Hnts_O1hWWw=~v8ub{kYXhh1TvcLZUSS| z-7RraSJ9Yhyc!%KDfUT02(@>nKcX}T>W@@4$S>@fR8OM?R|BQ{*da;NxwN z{)q3X{&*e!B3HMNUmfd@>1E$GMeh-MiaeFUsr|6(x;b7_N0rLwAZ6t?QS>*8qLW`) ziaxX`iD3IEssAk=8EXyIYl{mb<;B(V#zvtrwzydDAH{>T2~$2!=4ULWnk+|?vdo;)I8mA0ydm3DjFf!U2UpLgdu03P0 zR4aiiq27R;(1BKhBV>k0#@~MFjGf58b?uq-%YC>|9wPOyKjCN<_-pr8y}xf`p??#; zg%V7KqBh3H+j9r%^%fqSqr6BB1?`iPK0E#QkR0ZJVS*sW0-sW)1E)zaotb$snE?Vh1RRs1=%J*Tv_4DgYY zad@Pu?Wx7m#?e8@Z`PJVgPex|`IZX95HbVWp}&Nc8h>MZR=EaX^_6PNMWoy;`UZjur-SSxFK8-lbFU?Oot(*Y@tk(czIkA&USk34r5Z zOAWZl1DkFS5<;XLn_mWvG)tq!df(Pz6sg&ztt0hjsZUKwqy%nL+FWjysjTkNS1dOR z8>@VM3?!V+`v~;6-Jmi}=oPf(x(8b(}(Bv~4 zg)QPw5Yai<(Li!)gCL02GHm4-KX3Me_NBcHJw*lj z$VQj(+uO5YP;C{q_6?N>hpO-&brInZ|DM`=8?bYpMBLjoU%lr zJ+OZW6jiVQ8nnLw$^=44y=im=J~0bQ*DALFe>x7_4$@Qa$7@{Vz%UnwistA>BVKtJ zg|}KS(B3$Oj7lIgaB&7)Bh?y%A`F#EE%?Y@{Sa$YAD(wo?JE?EcrYC)_8iO^o4i@7 z4$xuk_V%kvwaw#jpjLZVI-EfN1yL;5=sux7I7bYq5+XQrpim%MEf{Dak78+{3FA2I zuv0^8pr(^x$ZbIUF%7#=k%UjjK>EA#Hk=*rgh#VI1A|**BP20-aeH%UUzjGy`=LdZ zCvakY3LoE|29NLJznAfyc@CIA}gR4#mg7q43y*j|UEe$EWa7Jpvxr;^XE#Jl=qhTNlFP4t(5x zBs}iK$6Jqr$J_C-ZZSO8@$rS#@OTs-r=1Ot)%dvK9C+M}k0;i` z<0*U`aUML5!pBYP;BhNHuDB2$efW6KMew*6AD`F&kB9KF>JoUIjgMDd4v$^&vE>SQ z+<=c;u7byH__+18@VEmXZ@2~?Z^FmZ8{zS5d^}Tx$8Yda9)d>=9}kw{@i0D`!|>RO zk4I|o_y>I8ZB!M!vZ{ioW>)ZM%?fTUtl<8`3NEpz;5v*7k{T6cK1SQStK$U`;NAPn zRIkD@iiY5{z~j-LqK43TFz~|oGQ6=~6+;k-x~CB+V$aZWoWZ|J63x}}CujYkN&#e( z_5qyx8!e(?!K6iYwd3qOv%e098 .section { + text-align: left; +} + +div.footer { + width: 940px; + margin: 20px auto 30px auto; + font-size: 14px; + color: #888; + text-align: right; +} + +div.footer a { + color: #888; +} + +p.caption { + font-family: inherit; + font-size: inherit; +} + + +div.relations { + display: none; +} + + +div.sphinxsidebar { + max-height: 100%; + overflow-y: auto; +} + +div.sphinxsidebar a { + color: #444; + text-decoration: none; + border-bottom: 1px dotted #999; +} + +div.sphinxsidebar a:hover { + border-bottom: 1px solid #999; +} + +div.sphinxsidebarwrapper { + padding: 18px 10px; +} + +div.sphinxsidebarwrapper p.logo { + padding: 0; + margin: -10px 0 0 0px; + text-align: center; +} + +div.sphinxsidebarwrapper h1.logo { + margin-top: -10px; + text-align: center; + margin-bottom: 5px; + text-align: left; +} + +div.sphinxsidebarwrapper h1.logo-name { + margin-top: 0px; +} + +div.sphinxsidebarwrapper p.blurb { + margin-top: 0; + font-style: normal; +} + +div.sphinxsidebar h3, +div.sphinxsidebar h4 { + font-family: Georgia, serif; + color: #444; + font-size: 24px; + font-weight: normal; + margin: 0 0 5px 0; + padding: 0; +} + +div.sphinxsidebar h4 { + font-size: 20px; +} + +div.sphinxsidebar h3 a { + color: #444; +} + +div.sphinxsidebar p.logo a, +div.sphinxsidebar h3 a, +div.sphinxsidebar p.logo a:hover, +div.sphinxsidebar h3 a:hover { + border: none; +} + +div.sphinxsidebar p { + color: #555; + margin: 10px 0; +} + +div.sphinxsidebar ul { + margin: 10px 0; + padding: 0; + color: #000; +} + +div.sphinxsidebar ul li.toctree-l1 > a { + font-size: 120%; +} + +div.sphinxsidebar ul li.toctree-l2 > a { + font-size: 110%; +} + +div.sphinxsidebar input { + border: 1px solid #CCC; + font-family: Georgia, serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox input[type="text"] { + width: 160px; +} + +div.sphinxsidebar .search > div { + display: table-cell; +} + +div.sphinxsidebar hr { + border: none; + height: 1px; + color: #AAA; + background: #AAA; + + text-align: left; + margin-left: 0; + width: 50%; +} + +div.sphinxsidebar .badge { + border-bottom: none; +} + +div.sphinxsidebar .badge:hover { + border-bottom: none; +} + +/* To address an issue with donation coming after search */ +div.sphinxsidebar h3.donation { + margin-top: 10px; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #004B6B; + text-decoration: underline; +} + +a:hover { + color: #6D4100; + text-decoration: underline; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: Georgia, serif; + font-weight: normal; + margin: 30px 0px 10px 0px; + padding: 0; +} + +div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } +div.body h2 { font-size: 180%; } +div.body h3 { font-size: 150%; } +div.body h4 { font-size: 130%; } +div.body h5 { font-size: 100%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #DDD; + padding: 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + color: #444; + background: #EAEAEA; +} + +div.body p, div.body dd, div.body li { + line-height: 1.4em; +} + +div.admonition { + margin: 20px 0px; + padding: 10px 30px; + background-color: #EEE; + border: 1px solid #CCC; +} + +div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fafafa; +} + +div.admonition p.admonition-title { + font-family: Georgia, serif; + font-weight: normal; + font-size: 24px; + margin: 0 0 10px 0; + padding: 0; + line-height: 1; +} + +div.admonition p.last { + margin-bottom: 0; +} + +div.highlight { + background-color: #fff; +} + +dt:target, .highlight { + background: #FAF3E8; +} + +div.warning { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.danger { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.error { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.caution { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.attention { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.important { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.note { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.tip { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.hint { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.seealso { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.topic { + background-color: #EEE; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre, tt, code { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; + font-size: 0.9em; +} + +.hll { + background-color: #FFC; + margin: 0 -12px; + padding: 0 12px; + display: block; +} + +img.screenshot { +} + +tt.descname, tt.descclassname, code.descname, code.descclassname { + font-size: 0.95em; +} + +tt.descname, code.descname { + padding-right: 0.08em; +} + +img.screenshot { + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils { + border: 1px solid #888; + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils td, table.docutils th { + border: 1px solid #888; + padding: 0.25em 0.7em; +} + +table.field-list, table.footnote { + border: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +table.footnote { + margin: 15px 0; + width: 100%; + border: 1px solid #EEE; + background: #FDFDFD; + font-size: 0.9em; +} + +table.footnote + table.footnote { + margin-top: -15px; + border-top: none; +} + +table.field-list th { + padding: 0 0.8em 0 0; +} + +table.field-list td { + padding: 0; +} + +table.field-list p { + margin-bottom: 0.8em; +} + +/* Cloned from + * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 + */ +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +table.footnote td.label { + width: .1px; + padding: 0.3em 0 0.3em 0.5em; +} + +table.footnote td { + padding: 0.3em 0.5em; +} + +dl { + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0; +} + +dl dd { + margin-left: 30px; +} + +blockquote { + margin: 0 0 0 30px; + padding: 0; +} + +ul, ol { + /* Matches the 30px from the narrow-screen "li > ul" selector below */ + margin: 10px 0 10px 30px; + padding: 0; +} + +pre { + background: #EEE; + padding: 7px 30px; + margin: 15px 0px; + line-height: 1.3em; +} + +div.viewcode-block:target { + background: #ffd; +} + +dl pre, blockquote pre, li pre { + margin-left: 0; + padding-left: 30px; +} + +tt, code { + background-color: #ecf0f3; + color: #222; + /* padding: 1px 2px; */ +} + +tt.xref, code.xref, a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fff; +} + +a.reference { + text-decoration: none; + border-bottom: 1px dotted #004B6B; +} + +/* Don't put an underline on images */ +a.image-reference, a.image-reference:hover { + border-bottom: none; +} + +a.reference:hover { + border-bottom: 1px solid #6D4100; +} + +a.footnote-reference { + text-decoration: none; + font-size: 0.7em; + vertical-align: top; + border-bottom: 1px dotted #004B6B; +} + +a.footnote-reference:hover { + border-bottom: 1px solid #6D4100; +} + +a:hover tt, a:hover code { + background: #EEE; +} + + +@media screen and (max-width: 870px) { + + div.sphinxsidebar { + display: none; + } + + div.document { + width: 100%; + + } + + div.documentwrapper { + margin-left: 0; + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + } + + div.bodywrapper { + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + margin-left: 0; + } + + ul { + margin-left: 0; + } + + li > ul { + /* Matches the 30px from the "ul, ol" selector above */ + margin-left: 30px; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .bodywrapper { + margin: 0; + } + + .footer { + width: auto; + } + + .github { + display: none; + } + + + +} + + + +@media screen and (max-width: 875px) { + + body { + margin: 0; + padding: 20px 30px; + } + + div.documentwrapper { + float: none; + background: #fff; + } + + div.sphinxsidebar { + display: block; + float: none; + width: 102.5%; + margin: 50px -30px -20px -30px; + padding: 10px 20px; + background: #333; + color: #FFF; + } + + div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, + div.sphinxsidebar h3 a { + color: #fff; + } + + div.sphinxsidebar a { + color: #AAA; + } + + div.sphinxsidebar p.logo { + display: none; + } + + div.document { + width: 100%; + margin: 0; + } + + div.footer { + display: none; + } + + div.bodywrapper { + margin: 0; + } + + div.body { + min-height: 0; + padding: 0; + } + + .rtd_doc_footer { + display: none; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .footer { + width: auto; + } + + .github { + display: none; + } +} + + +/* misc. */ + +.revsys-inline { + display: none!important; +} + +/* Hide ugly table cell borders in ..bibliography:: directive output */ +table.docutils.citation, table.docutils.citation td, table.docutils.citation th { + border: none; + /* Below needed in some edge cases; if not applied, bottom shadows appear */ + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + + +/* relbar */ + +.related { + line-height: 30px; + width: 100%; + font-size: 0.9rem; +} + +.related.top { + border-bottom: 1px solid #EEE; + margin-bottom: 20px; +} + +.related.bottom { + border-top: 1px solid #EEE; +} + +.related ul { + padding: 0; + margin: 0; + list-style: none; +} + +.related li { + display: inline; +} + +nav#rellinks { + float: right; +} + +nav#rellinks li+li:before { + content: "|"; +} + +nav#breadcrumbs li+li:before { + content: "\00BB"; +} + +/* Hide certain items when printing */ +@media print { + div.related { + display: none; + } +} \ No newline at end of file diff --git a/_build/_static/basic.css b/_build/_static/basic.css new file mode 100644 index 0000000..e5179b7 --- /dev/null +++ b/_build/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2024 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: inherit; + 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/_build/_static/custom.css b/_build/_static/custom.css new file mode 100644 index 0000000..2a924f1 --- /dev/null +++ b/_build/_static/custom.css @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/_build/_static/doctools.js b/_build/_static/doctools.js new file mode 100644 index 0000000..4d67807 --- /dev/null +++ b/_build/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2024 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/_build/_static/documentation_options.js b/_build/_static/documentation_options.js new file mode 100644 index 0000000..da6f32e --- /dev/null +++ b/_build/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '1.0.10.5', + 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/_build/_static/file.png b/_build/_static/file.png new file mode 100644 index 0000000000000000000000000000000000000000..a858a410e4faa62ce324d814e4b816fff83a6fb3 GIT binary patch literal 286 zcmV+(0pb3MP)s`hMrGg#P~ix$^RISR_I47Y|r1 z_CyJOe}D1){SET-^Amu_i71Lt6eYfZjRyw@I6OQAIXXHDfiX^GbOlHe=Ae4>0m)d(f|Me07*qoM6N<$f}vM^LjV8( literal 0 HcmV?d00001 diff --git a/_build/_static/language_data.js b/_build/_static/language_data.js new file mode 100644 index 0000000..367b8ed --- /dev/null +++ b/_build/_static/language_data.js @@ -0,0 +1,199 @@ +/* + * language_data.js + * ~~~~~~~~~~~~~~~~ + * + * This script contains the language-specific data used by searchtools.js, + * namely the list of stopwords, stemmer, scorer and splitter. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; + + +/* Non-minified version is copied as a separate JS file, if available */ + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + 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/_build/_static/minus.png b/_build/_static/minus.png new file mode 100644 index 0000000000000000000000000000000000000000..d96755fdaf8bb2214971e0db9c1fd3077d7c419d GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu=nj kDsEF_5m^0CR;1wuP-*O&G^0G}KYk!hp00i_>zopr08q^qX#fBK literal 0 HcmV?d00001 diff --git a/_build/_static/plus.png b/_build/_static/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..7107cec93a979b9a5f64843235a16651d563ce2d GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu>-2 m3q%Vub%g%s<8sJhVPMczOq}xhg9DJoz~JfX=d#Wzp$Pyb1r*Kz literal 0 HcmV?d00001 diff --git a/_build/_static/pygments.css b/_build/_static/pygments.css new file mode 100644 index 0000000..04a4174 --- /dev/null +++ b/_build/_static/pygments.css @@ -0,0 +1,84 @@ +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: #8f5902; font-style: italic } /* Comment */ +.highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ +.highlight .g { color: #000000 } /* Generic */ +.highlight .k { color: #004461; font-weight: bold } /* Keyword */ +.highlight .l { color: #000000 } /* Literal */ +.highlight .n { color: #000000 } /* Name */ +.highlight .o { color: #582800 } /* Operator */ +.highlight .x { color: #000000 } /* Other */ +.highlight .p { color: #000000; font-weight: bold } /* Punctuation */ +.highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #8f5902 } /* Comment.Preproc */ +.highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #a40000 } /* Generic.Deleted */ +.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ +.highlight .ges { color: #000000 } /* Generic.EmphStrong */ +.highlight .gr { color: #ef2929 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #888888 } /* Generic.Output */ +.highlight .gp { color: #745334 } /* Generic.Prompt */ +.highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ +.highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */ +.highlight .ld { color: #000000 } /* Literal.Date */ +.highlight .m { color: #990000 } /* Literal.Number */ +.highlight .s { color: #4e9a06 } /* Literal.String */ +.highlight .na { color: #c4a000 } /* Name.Attribute */ +.highlight .nb { color: #004461 } /* Name.Builtin */ +.highlight .nc { color: #000000 } /* Name.Class */ +.highlight .no { color: #000000 } /* Name.Constant */ +.highlight .nd { color: #888888 } /* Name.Decorator */ +.highlight .ni { color: #ce5c00 } /* Name.Entity */ +.highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #000000 } /* Name.Function */ +.highlight .nl { color: #f57900 } /* Name.Label */ +.highlight .nn { color: #000000 } /* Name.Namespace */ +.highlight .nx { color: #000000 } /* Name.Other */ +.highlight .py { color: #000000 } /* Name.Property */ +.highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #000000 } /* Name.Variable */ +.highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */ +.highlight .pm { color: #000000; font-weight: bold } /* Punctuation.Marker */ +.highlight .w { color: #f8f8f8 } /* Text.Whitespace */ +.highlight .mb { color: #990000 } /* Literal.Number.Bin */ +.highlight .mf { color: #990000 } /* Literal.Number.Float */ +.highlight .mh { color: #990000 } /* Literal.Number.Hex */ +.highlight .mi { color: #990000 } /* Literal.Number.Integer */ +.highlight .mo { color: #990000 } /* Literal.Number.Oct */ +.highlight .sa { color: #4e9a06 } /* Literal.String.Affix */ +.highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */ +.highlight .sc { color: #4e9a06 } /* Literal.String.Char */ +.highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */ +.highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #4e9a06 } /* Literal.String.Double */ +.highlight .se { color: #4e9a06 } /* Literal.String.Escape */ +.highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */ +.highlight .si { color: #4e9a06 } /* Literal.String.Interpol */ +.highlight .sx { color: #4e9a06 } /* Literal.String.Other */ +.highlight .sr { color: #4e9a06 } /* Literal.String.Regex */ +.highlight .s1 { color: #4e9a06 } /* Literal.String.Single */ +.highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */ +.highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #000000 } /* Name.Function.Magic */ +.highlight .vc { color: #000000 } /* Name.Variable.Class */ +.highlight .vg { color: #000000 } /* Name.Variable.Global */ +.highlight .vi { color: #000000 } /* Name.Variable.Instance */ +.highlight .vm { color: #000000 } /* Name.Variable.Magic */ +.highlight .il { color: #990000 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/_build/_static/searchtools.js b/_build/_static/searchtools.js new file mode 100644 index 0000000..b08d58c --- /dev/null +++ b/_build/_static/searchtools.js @@ -0,0 +1,620 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2024 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, anchor) + ); + // 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." + ).replace('${resultCount}', resultCount); +}; +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); +}; +// Helper function used by query() to order search results. +// Each input is an array of [docname, title, anchor, descr, score, filename]. +// Order the results by score (in opposite order of appearance, since the +// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically. +const _orderResultsByScoreThenName = (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; +}; + +/** + * 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, anchor) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + for (const removalQuery of [".headerlink", "script", "style"]) { + htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() }); + } + if (anchor) { + const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`); + if (anchorContent) return anchorContent.textContent; + + console.warn( + `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.` + ); + } + + // if anchor not specified or not found, fall back to main content + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent) return docContent.textContent; + + console.warn( + "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. 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); + }, + + _parseQuery: (query) => { + // 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]); + + return [query, searchTerms, excludedTerms, highlightTerms, objectTerms]; + }, + + /** + * execute search (requires search index to be loaded) + */ + _performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => { + 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; + + // Collect multiple result groups to be sorted separately and then ordered. + // Each is an array of [docname, title, anchor, descr, score, filename]. + const normalResults = []; + const nonMainIndexResults = []; + + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase().trim(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + const score = Math.round(Scorer.title * queryLower.length / title.length); + const boost = titles[file] === title ? 1 : 0; // add a boost for document titles + normalResults.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score + boost, + 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, isMain] of foundEntries) { + const score = Math.round(100 * queryLower.length / entry.length); + const result = [ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + ]; + if (isMain) { + normalResults.push(result); + } else { + nonMainIndexResults.push(result); + } + } + } + } + + // lookup as object + objectTerms.forEach((term) => + normalResults.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) { + normalResults.forEach((item) => (item[4] = Scorer.score(item))); + nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item))); + } + + // Sort each group of results by score and then alphabetically by name. + normalResults.sort(_orderResultsByScoreThenName); + nonMainIndexResults.sort(_orderResultsByScoreThenName); + + // Combine the result groups in (reverse) order. + // Non-main index entries are typically arbitrary cross-references, + // so display them after other results. + let results = [...nonMainIndexResults, ...normalResults]; + + // 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; + }, []); + + return results.reverse(); + }, + + query: (query) => { + const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query); + const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms); + + // 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); + if (!terms.hasOwnProperty(word)) { + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + } + if (!titleTerms.hasOwnProperty(word)) { + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: titleTerms[term], 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.set(file, [word]); + else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(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, anchor) => { + const text = Search.htmlToText(htmlText, anchor); + 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/_build/_static/sphinx_highlight.js b/_build/_static/sphinx_highlight.js new file mode 100644 index 0000000..8a96c69 --- /dev/null +++ b/_build/_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/_build/genindex.html b/_build/genindex.html new file mode 100644 index 0000000..c65496e --- /dev/null +++ b/_build/genindex.html @@ -0,0 +1,307 @@ + + + + + + + Index — SyncSketch Python API Library 1.0.10.5 documentation + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + +

Index

+ +
+ A + | B + | C + | D + | G + | I + | M + | R + | S + | U + +
+

A

+ + + +
+ +

B

+ + +
+ +

C

+ + +
+ +

D

+ + + +
+ +

G

+ + + +
+ +

I

+ + +
+ +

M

+ + +
+ +

R

+ + + +
+ +

S

+ + + +
+ +

U

+ + + +
+ + + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/_build/index.html b/_build/index.html new file mode 100644 index 0000000..3108077 --- /dev/null +++ b/_build/index.html @@ -0,0 +1,1438 @@ + + + + + + + + SyncSketch Python API Library documentation — SyncSketch Python API Library 1.0.10.5 documentation + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

SyncSketch Python API Library documentation

+

autodoc_member_order = ‘bysource’

+
+
+
+
+class syncsketch.SyncSketchAPI
+

SyncSketchAPI is a class that provides a set of methods to interact with SyncSketch API.

+
+
+__init__(
+
+auth,
+
+api_key,
+
+host="https://www.syncsketch.com",
+
+useExpiringToken=False,
+
+debug=False,
+
+api_version="v1",
+
+use_header_auth=False
+
+)
+

Constructor for SyncSketchAPI class.

+
+
Parameters:
+
    +
  • auth (str) – The username of the user.

  • +
  • api_key (str) – The api key of the user.

  • +
  • host (str) – The host of the SyncSketch API.

  • +
  • useExpiringToken (bool) – If True, the token will expire after 1 hour.

  • +
  • debug (bool) – If True, the debug mode will be enabled.

  • +
  • api_version (str) – The version of the SyncSketch API.

  • +
  • use_header_auth (bool) – If True, the authentication will be done using headers.

  • +
+
+
Returns:
+

SyncSketchAPI object.

+
+
Return type:
+

obj

+
+
+
+ +
+ +
+
+class syncsketch.SyncSketchAPI(auth, api_key, host='https://www.syncsketch.com', useExpiringToken=False, debug=False, api_version='v1', use_header_auth=False)
+

Bases: object

+

Convenience API to communicate with the SyncSketch Service for collaborative online reviews

+
+
+is_connected()
+

Convenience function to check if the API is connected to SyncSketch +Will check against Status Code 200 and return False if not which most likely would be +and authorization error

+
+
Returns:
+

Connection success

+
+
Return type:
+

bool

+
+
+
+ +
+
+get_tree(withItems=False)
+

Get nested tree of account, projects, reviews and optionally items for the current user

+
+
Parameters:
+

withItems (bool) – Include items in the response

+
+
Returns:
+

Tree data

+
+
Return type:
+

dict

+
+
+
+ +
+
+get_accounts()
+

Summary

+
+
Returns:
+

List of workspaces the user has access to

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+update_account(account_id, data)
+

Update a workspace / account

+
+
Parameters:
+
    +
  • account_id (int) – the id of the item

  • +
  • data (dict) – normal dict with data for item

  • +
+
+
Returns:
+

Workspace / Account data

+
+
Return type:
+

dict

+
+
+
+ +
+
+create_project(account_id, name, description='', data=None)
+

Add a project to your account. Please make sure to pass the accountId which you can query using the getAccounts command.

+
+
Parameters:
+
    +
  • account_id (int) – id of the account to connect with

  • +
  • name (str) – Name of the project

  • +
  • description (str) – Description of the project

  • +
  • data (dict) – additional information e.g is_public. Find out more about available fields at /api/v1/project/schema/.

  • +
+
+
Returns:
+

Project data

+
+
Return type:
+

dict

+
+
+
+ +
+
+get_projects(include_deleted=False, include_archived=False, include_tags=False, include_connections=False, limit=100, offset=0)
+

Get a list of currently active projects

+
+
Parameters:
+
    +
  • include_deleted (bool) – if true, include deleted projects

  • +
  • include_archived (bool) – if true, include archived projects

  • +
  • include_tags (bool) – if true, include tag list on the project object

  • +
  • include_connections (bool) – if true, include full user connections on the project object

  • +
  • limit (int) – limit the number of results

  • +
  • offset (int) – offset the results

  • +
+
+
Returns:
+

Dict with meta information and an array of found projects

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+get_projects_by_name(name)
+

Get a project by name regardless of status

+
+
Parameters:
+

name (str) – Name to search for

+
+
Returns:
+

List of projects

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+get_project_by_id(project_id)
+

Get single project by id

+
+
Parameters:
+

project_id (int) – Project id

+
+
Returns:
+

Project data

+
+
Return type:
+

dict

+
+
+
+ +
+
+get_project_storage(project_id)
+

Get project storage usage in bytes

+
+
Parameters:
+

project_id (int) – Project id

+
+
Returns:
+

Storage usage in bytes

+
+
+
+ +
+
+update_project(project_id, data)
+

Update a project

+
+
Parameters:
+
    +
  • project_id (int) – the id of the item

  • +
  • data (dict) – dict with new data for item

  • +
+
+
Returns:
+

updated project data

+
+
Return type:
+

dict

+
+
+
+ +
+
+delete_project(project_id)
+

Delete a project by id.

+
+
Parameters:
+

project_id (int) – Project ID to delete

+
+
Returns:
+

+
+
+
+ +
+
+duplicate_project(project_id, name=None, copy_reviews=False, copy_users=False, copy_settings=False)
+

Create a new project from an existing project

+
+
Parameters:
+
    +
  • project_id (int) – Source project id

  • +
  • name (str) – New project name

  • +
  • copy_reviews (bool) – Whether to copy reviews (without items)

  • +
  • copy_users (bool) – Whether to copy users

  • +
  • copy_settings (bool) – Whether to copy settings

  • +
+
+
Returns:
+

New project data

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+archive_project(project_id)
+

Archive a project

+
+
Parameters:
+

project_id (int)

+
+
Returns:
+

+
+
+
+ +
+
+restore_project(project_id)
+

Restore (unarchive) a project

+
+
Parameters:
+

project_id (int)

+
+
Returns:
+

+
+
+
+ +
+
+get_reviews_by_project_id(project_id, limit=100, offset=0)
+

Get list of reviews by project id.

+
+
Parameters:
+

project_id (int) – SyncSketch project id

+
+
Returns:
+

Dict with meta information and an array of found projects

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+get_review_by_name(name)
+

Get reviews by name using a case insensitive startswith query

+
+
Parameters:
+

name – String - Name of the review

+
+
Returns:
+

Dict with meta information and an array of found projects

+
+
+
+ +
+
+get_review_by_id(review_id)
+

Get single review by id.

+
+
Parameters:
+

review_id – Number

+
+
Returns:
+

Review Dict

+
+
+
+ +
+
+get_review_by_uuid(uuid)
+

Get single review by uuid. +UUID can be found in the review URL e.g. syncsketch.com/sketch/<uuid>/

+
+
Parameters:
+

uuid (str) – UUID of the review.

+
+
Returns:
+

Review dict

+
+
Return type:
+

dict

+
+
+
+ +
+
+get_review_storage(review_id)
+

Get review storage usage in bytes

+
+
Parameters:
+

review_id (int) – Review ID

+
+
Returns:
+

Storage usage in bytes

+
+
Return type:
+

int

+
+
+
+ +
+
+update_review(review_id, data)
+

Update a review

+
+
Parameters:
+
    +
  • review_id (int) – the id of the item

  • +
  • data (dict) – dict with data for item

  • +
+
+
Returns:
+

updated review data

+
+
Return type:
+

dict

+
+
+
+ +
+
+sort_review_items(review_id, items)
+

Update a review

+

Example items param

+
items = [{
+    "id": 1, # item id
+    "sortorder": 0, # sortorder, starting at 0
+}]
+
+
+

Method output example:

+
# number of successful items sort updated
+{ "updated_items": int }
+
+
+
+
Parameters:
+
    +
  • review_id (int) – the id of the item

  • +
  • items (list) – payload

  • +
+
+
Returns:
+

response

+
+
Return type:
+

dict

+
+
+
+ +
+
+archive_review(review_id)
+

Archive a review

+
+
Parameters:
+

review_id (int)

+
+
Returns:
+

empty response

+
+
+
+ +
+
+restore_review(review_id)
+

Restore (unarchive) a review

+
+
Parameters:
+

review_id (int)

+
+
Returns:
+

empty response

+
+
+
+ +
+
+delete_review(review_id)
+

Delete a review by id.

+
+
Parameters:
+

review_id (int) – Review ID to delete

+
+
Returns:
+

+
+
+
+ +
+
+update_item(item_id, data)
+

Update an item

+
+
Parameters:
+
    +
  • item_id (int) – the id of the item

  • +
  • data (dict) – dict with data for item

  • +
+
+
Returns:
+

updated item data

+
+
Return type:
+

dict

+
+
+
+ +
+
+add_item(review_id, name, fps, additional_data)
+

create a media item record and connect it to a review. This should be used in case you want to add items with externaly hosted +media by passing in the external_url and external_thumbnail_url to the additionalData dict e.g

+
additionalData = {
+    external_url: http://52.24.98.51/wp-content/uploads/2017/03/rain.jpg
+    external_thumbnail_url: http://52.24.98.51/wp-content/uploads/2017/03/rain.jpg
+}
+
+
+

or

+
additionalData = {
+    width:1024
+    height:720
+    artist: "Brady Endres"
+    duration:3 (in seconds)
+    description: the description here
+    size: size in byte
+    type: image | video
+}
+
+
+

NOTE: you always need to pass in FPS for SyncSketch to work!

+

For a complete list of available fields to set, please +visit https://www.syncsketch.com/api/v1/item/schema/

+
+
Parameters:
+
    +
  • review_id (int) – Required review_id

  • +
  • name (str) – Name of the item

  • +
  • fps (float) – The frame per second is very important for syncsketch to determine the correct number of frames

  • +
  • additional_data (dict) – dictionary with item info

  • +
+
+
Returns:
+

Item data

+
+
Return type:
+

dict

+
+
+
+ +
+
+add_media(review_id, filepath, artist_name='', file_name='', noConvertFlag=False, itemParentId=False)
+

Convenience function to upload a file to a review. It will automatically create +an Item and attach it to the review. NOTE - if you are hosting your own media, please +use the addItem function and pass in the external_url and external_thumbnail_url

+
+
Parameters:
+
    +
  • review_id (int) – Required review_id

  • +
  • filepath (str) – path for the file on disk e.g /tmp/movie.webm

  • +
  • artist_name (str) – The name of the artist you want associated with this media file

  • +
  • file_name (str) – The name of the file. Please make sure to pass the correct file extension

  • +
  • noConvertFlag (bool) – the video you are uploading is already in a browser compatible format

  • +
  • itemParentId (int) – (Optional) set when you want to add a new version of an item. itemParentId is the id of the item you want to upload a new version for

  • +
+
+
Returns:
+

Item data

+
+
Return type:
+

dict

+
+
+
+ +
+
+add_media_by_url(review_id, media_url, artist_name='', noConvertFlag=False)
+

Convenience function to upload a mediaURl to a review. Please use this function when you already have your files in the cloud, e.g +AWS, Dropbox, Shotgrid, etc…

+

We will automatically create an Item and attach it to the review.

+
+
Parameters:
+
    +
  • review_id (int) – Required review_id

  • +
  • media_url (str) – url to the media you are trying to upload

  • +
  • artist_name (str) – The name of the artist you want associated with this media file

  • +
  • noConvertFlag (bool) – the video you are uploading is already in a browser compatible format and does not need to be converted

  • +
+
+
Returns:
+

Item data

+
+
Return type:
+

dict

+
+
+
+ +
+
+add_media_v2(review_id, filepath, file_name='', item_uuid=None, noConvertFlag=False)
+

Similar to add_media method, but uploads the media file directly to SyncSketche’s internal S3 instead of to +the SyncSketch server. In some cases, using this method over add_media can improve upload performance and +stability. Unlike add_media this method does not return as much data about the created item.

+
+
Parameters:
+
    +
  • review_id (int) – Required review_id.

  • +
  • filepath (str) – path for the file on disk e.g /tmp/movie.webm.

  • +
  • file_name (str) – The name of the file. Please make sure to pass the correct file extension.

  • +
  • noConvertFlag (bool) – the video you are uploading is already in a browser compatible format.

  • +
+
+
Returns:
+

A dict, containing “item_id” and “uuid” or None on failure.

+
+
Return type:
+

Optional[dict]

+
+
+
+ +
+
+get_media(searchCriteria)
+

This is a general search function. You can search media items by

+

‘id’ +‘name’ +‘status’ +‘active’ +‘creator’: ALL_WITH_RELATIONS, <– these are foreign key queries +‘reviews’: ALL_WITH_RELATIONS, <– these are foreign key queries +‘created’ using ‘exact’, ‘range’, ‘gt’, ‘gte’, ‘lt’, ‘lte’

+

To query items by foreign keys please use the foreign key syntax described in the Django search definition: +https://docs.djangoproject.com/en/1.11/topics/db/queries/

+

If you want to query by “review name” for example you would pass in

+

reviews__name = NAME TO SEARCH

+

Using the “__” syntax you can even search for items by project like

+

reviews__project__name = $PROJECT NAME TO SEARCH

+

To speed up a query you can also pass in a limit e.g limit:10

+

results = s.getMedia({‘reviews__project__name’:’test’, ‘limit’: 1, ‘active’: 1})

+

NOTE: Please make sure to include the active:1 query if you only want active media. Deleted files are currently +only deactivated and kept for a certain period of time before they are “purged” from the system.

+
+
Parameters:
+

searchCriteria (dict) – Search params

+
+
Returns:
+

List of media items

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+get_items_by_review_id(review_id)
+

Get all items in a review

+
+
Parameters:
+

review_id (int) – Review ID

+
+
Returns:
+

List of media items

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+delete_item(item_id)
+

Delete a item by id.

+
+
Parameters:
+

item_id (int) – Item ID to delete

+
+
Returns:
+

+
+
+
+ +
+
+bulk_delete_items(item_ids)
+

Delete multiple items by id.

+
+
Parameters:
+

item_ids (list[int]) – List of item IDs to delete

+
+
Returns:
+

+
+
+
+ +
+
+move_items(new_review_id, item_data)
+

Move items from one review to another

+

item_data should be a list of dictionaries with the old review id and the item id. +The items in the list will be moved to the new review for the param new_review_id

+
# Example item_data
+# review_id is the current review an item is in
+# it will be moved to the new_review_id
+items_to_move = [
+    {"review_id": 1, "item_id": 1},
+    {"review_id": 1, "item_id": 2},
+    {"review_id": 1, "item_id": 3},
+]
+
+
+
+
Parameters:
+
    +
  • new_review_id (int) – The review id to move the items to

  • +
  • item_data (list[dict]) – List of dictionaries with the old review id and the item id

  • +
+
+
Returns:
+

+
+
+
+ +
+
+add_comment(item_id, text, review_id, frame=0)
+

Add a comment to an item

+
+
Parameters:
+
    +
  • item_id (int) – Item to add the comment to

  • +
  • text (str) – Comment text

  • +
  • review_id (int) – Review you are adding the comment to

  • +
  • frame (int) – Frame number of the video to add the comment to (if applicable)

  • +
+
+
Returns:
+

+
+
+
+ +
+
+get_annotations(item_id, revisionId=False, review_id=False)
+

Get sketches and comments for an item. Frames have a revision id which signifies a “set of notes”. +When querying an item you’ll get the available revisions for this item. If you wish to get only the latest +revision, please get the revisionId for the latest revision.

+
+
Parameters:
+
    +
  • item_id (int) – id of the media item you are querying.

  • +
  • revisionId (int) – Optional revisionId to narrow down the results

  • +
  • review_id (int) – RECOMMENDED - retrieve annotations for a specific review only.

  • +
+
+
Returns:
+

dict

+
+
+
+ +
+
+get_flattened_annotations(review_id, item_id, with_tracing_paper=False, return_as_base64=False)
+

Returns a list of sketches either as signed urls from s3 or base64 encoded strings. +The sketches are composited over the background frame of the item.

+
+
Parameters:
+
    +
  • review_id (int) – Review ID

  • +
  • item_id (int) – Item ID

  • +
  • with_tracing_paper (bool) – Include tracing paper in the response

  • +
  • return_as_base64 (bool) – Return sketches as base64 encoded strings

  • +
+
+
Returns:
+

List of sketches as signed urls from s3 or base64 encoded strings

+
+
+
+ +
+
+get_grease_pencil_overlays(review_id, item_id, homedir=None)
+

Download overlay sketches for Maya Greasepencil.

+

Download overlay sketches for Maya Greasepencil. Function will download +a zip file which contains an XML and the sketches as png files. Maya +can load the zip file to overlay the sketches over the 3D model!

+

For more information visit: +https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2015/ENU/Maya/files/Grease-Pencil-Tool-htm.html

+

PLEASE make sure that /tmp is writable

+
+
Parameters:
+
    +
  • review_id (int) – Review ID

  • +
  • item_id (int) – Item ID

  • +
  • homedir (str) – Optional path to download the zip file to

  • +
+
+
Returns:
+

filePath to the zip file with the greasePencil data.

+
+
+
+ +
+
+get_users_by_name(name)
+

Name is a combined search and will search in first_name, last_name and email

+
+
Parameters:
+

name (str) – Name to search for

+
+
Returns:
+

List of users

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+get_user_by_email(email)
+

Get user by email

+
+
Parameters:
+

email (str) – Email to search for

+
+
Returns:
+

User data

+
+
Return type:
+

dict

+
+
+
+ +
+
+get_users_by_project_id(project_id)
+

Get all users in a project

+
+
Parameters:
+

project_id (int)

+
+
Returns:
+

List of users

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+get_connections_by_user_id(user_id, account_id, include_inactive=None, include_archived=None)
+

Get all project and account connections for a user. Good for checking access for a user that might have left…

+
+
Parameters:
+
    +
  • user_id (int) – User ID to get connections for

  • +
  • account_id (int) – Account ID to get connections for

  • +
  • include_inactive (bool) – Include inactive projects

  • +
  • include_archived (bool) – Include archived projects

  • +
+
+
Returns:
+

List of connections

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+get_user_by_id(user_id)
+

Get a user by ID

+
+
Parameters:
+

user_id (int)

+
+
Returns:
+

User data

+
+
Return type:
+

dict

+
+
+
+ +
+
+add_users_to_workspace(workspace_id, users, note='')
+

Add Users to Workspace

+
users=[{"email":"test@test.de","permission":"admin"}]
+
+
+
+
Parameters:
+
    +
  • workspace_id (int) – id of the workspace

  • +
  • users (list) – list of new users - possible permissions “admin”, “manager”

  • +
  • note (str) – (Optional) message for the invitation email

  • +
+
+
Returns:
+

response

+
+
+
+ +
+
+remove_users_from_workspace(workspace_id, users)
+

Remove a list of users from a workspace +Can remove by id or email

+
users=[{"email":"test@test.de"}, {"id":12345}]
+
+
+
+
Parameters:
+
    +
  • workspace_id (int) – id of the workspace

  • +
  • users (list) – list of users to remove - either remove by user email or id

  • +
+
+
Returns:
+

response

+
+
+
+ +
+
+add_users_to_project(project_id, users, note='')
+

Add Users to Project

+

possible permissions

+
    +
  • admin

  • +
  • member

  • +
  • viewer

  • +
  • reviewer

  • +
+
users=[{"email":"test@test.de","permission":"viewer"}]
+
+
+
+
Parameters:
+
    +
  • project_id (int) – id of the project

  • +
  • users (list[dict]) – list of new users

  • +
  • note (str) – (Optional) message for the invitation email

  • +
+
+
Returns:
+

response

+
+
+
+ +
+
+remove_users_from_project(project_id, users)
+

Remove a list of users from a project

+

remove by user email or id

+
users=[{"email":"test@test.de"}, {"id":12345}]
+
+
+
+
Parameters:
+
    +
  • project_id (int) – id of the project

  • +
  • users (list) – list of users to remove - either remove by user email or id

  • +
+
+
+
+ +
+
+shotgrid_create_config(syncsketch_account_id, syncsketch_project_id=None, data=None)
+

Create a new Shotgrid configuration for a SyncSketch workspace and optionally a project

+
+
Parameters:
+
    +
  • syncsketch_account_id (int)

  • +
  • syncsketch_project_id (int)

  • +
  • data (dict) – Configuration data.

  • +
+
+
Returns:
+

+
+
+
+ +
+
+shotgrid_get_playlists(syncsketch_account_id, syncsketch_project_id, shotgun_project_id=None)
+

Returns list of Shotgrid playlists modified in the last 120 days +If the syncsketch project is directly linked to a shotgrid by the workspace admin, the +param shotgun_project_id will be ignored and can be omitted during the function call

+
+
Parameters:
+
    +
  • syncsketch_account_id (int) – SyncSketch account id

  • +
  • syncsketch_project_id (int) – SyncSketch project id

  • +
  • shotgun_project_id (int) – (optional) Shotgrid project id

  • +
+
+
Returns:
+

list of Shotgrid playlists

+
+
+
+ +
+
+shotgrid_sync_review_notes(review_id)
+

Sync notes from SyncSketch review to the original shotgrid playlist +Returns task id to use in get_shotgun_sync_review_notes_progress to get progress

+

returns dict with information about the REST API call:

+
    +
  • message=<STR> “Shotgrid review notes sync started”

  • +
  • status=<STR> processing/done/failed

  • +
  • progress_url=<STR> Full url to call for progress/results

  • +
  • task_id=<STR> task_ids pass this value to the get_shotgun_sync_review_items_progress function

  • +
  • percent_complete=<INT> 0-100 value of percent complete

  • +
  • total_items=<INT> number of items being synced from shotgrid

  • +
  • remaining_items=<INT> number of items not yet pulled from shotgrid

  • +
+
+
Parameters:
+

review_id (int) – SyncSketch review id

+
+
Returns:
+

Progress information

+
+
Return type:
+

dict

+
+
+
+ +
+
+shotgrid_sync_new_item_notes(project_id, review_id, item_id)
+

Sync new notes from SyncSketch review item to the original shotgrid playlist +Returns dict with information about the REST API call

+
    +
  • sketch_upload_error=<BOOL> “True in case of error”

  • +
  • sketches=<INT> “Number of sketches synced”

  • +
  • comments=<INT> “Number of comments synced”

  • +
  • attachments=<INT> “Number of attachments synced”

  • +
  • item_name=<STR> “Name of item that was synced”

  • +
+
+
Parameters:
+
    +
  • project_id (int) – SyncSketch project id

  • +
  • review_id (int) – SyncSketch review id

  • +
  • item_id (int) – SyncSketch item id

  • +
+
+
Returns:
+

+
+
+
+ +
+
+get_shotgrid_sync_review_notes_progress(task_id)
+

Returns status of review notes sync for the task id provided in shotgun_sync_review_notes

+

Returns a dict with the following keys:

+
    +
  • message=<STR> “Shotgrid review notes sync started”

  • +
  • status=<STR> processing/done/failed

  • +
  • progress_url=<STR> Full url to call for progress/results

  • +
  • task_id=<STR> task_ids pass this value to the get_shotgun_sync_review_items_progress function

  • +
  • percent_complete=<INT> 0-100 value of percent complete

  • +
  • total_items=<INT> number of items being synced from shotgrid

  • +
  • remaining_items=<INT> number of items not yet pulled from shotgrid

  • +
+
+
Parameters:
+

task_id (str) – UUID of the task returned by shotgrid_sync_review_notes

+
+
Returns:
+

Progress information

+
+
Return type:
+

dict

+
+
+
+ +
+
+shotgrid_sync_review_items(syncsketch_project_id, playlist_code, playlist_id, review_id=None)
+

Create or update SyncSketch review with shotgrid playlist items +Returns task id to use in get_shotgun_sync_review_items_progress to get progress

+

Response format:

+
    +
  • message=<STR> “Shotgrid review item sync started”,

  • +
  • status=<STR> processing/done/failed,

  • +
  • progress_url=<STR> Full url to call for progress/results,

  • +
  • task_id=<STR> task_ids - pass this value to the get_shotgun_sync_review_items_progress function,

  • +
  • percent_complete=<INT> 0-100 value of percent complete,

  • +
  • total_items=<INT> number of items being synced from shotgrid,

  • +
  • remaining_items=<INT> number of items not yet pulled from shotgrid,

  • +
  • data=<dict>

  • +
  • review_id=<INT> review.id,

  • +
  • review_link=<STR> url link to the syncsketch player with the review pulled from shotgrid,

  • +
+
+
Parameters:
+
    +
  • syncsketch_project_id (int)

  • +
  • playlist_code (str)

  • +
  • playlist_id (int)

  • +
  • review_id (int) – (optional)

  • +
+
+
Returns:
+

+
+
Return type:
+

dict

+
+
+
+ +
+
+get_shotgrid_sync_review_items_progress(task_id)
+

Returns status of review items sync for the task id provided in shotgun_sync_review_items

+
+
Parameters:
+

task_id (str) – UUID of the task returned by shotgrid_sync_review_items

+
+
Returns:
+

DeprecationWarning

+
+
Return type:
+

dict

+
+
+
+ +
+
+add_media_v1(review_id, filepath, artist_name='', file_name='', noConvertFlag=False, itemParentId=False)
+

Convenience function to upload a file to a review. It will automatically create +an Item and attach it to the review. NOTE - if you are hosting your own media, please +use the addItem function and pass in the external_url and external_thumbnail_url

+
+
Parameters:
+
    +
  • review_id (int) – Required review_id

  • +
  • filepath (str) – path for the file on disk e.g /tmp/movie.webm

  • +
  • artist_name (str) – The name of the artist you want associated with this media file

  • +
  • file_name (str) – The name of the file. Please make sure to pass the correct file extension

  • +
  • noConvertFlag (bool) – the video you are uploading is already in a browser compatible format

  • +
  • itemParentId (int) – (Optional) set when you want to add a new version of an item. itemParentId is the id of the item you want to upload a new version for

  • +
+
+
Returns:
+

Item data

+
+
Return type:
+

dict

+
+
+
+ +
+
+get_shotgun_sync_review_items_progress(task_id)
+

Returns status of review items sync for the task id provided in shotgun_sync_review_items

+
+
Parameters:
+

task_id (str) – UUID of the task returned by shotgrid_sync_review_items

+
+
Returns:
+

DeprecationWarning

+
+
Return type:
+

dict

+
+
+
+ +
+
+shotgun_sync_review_items(syncsketch_project_id, playlist_code, playlist_id, review_id=None)
+

Create or update SyncSketch review with shotgrid playlist items +Returns task id to use in get_shotgun_sync_review_items_progress to get progress

+

Response format:

+
    +
  • message=<STR> “Shotgrid review item sync started”,

  • +
  • status=<STR> processing/done/failed,

  • +
  • progress_url=<STR> Full url to call for progress/results,

  • +
  • task_id=<STR> task_ids - pass this value to the get_shotgun_sync_review_items_progress function,

  • +
  • percent_complete=<INT> 0-100 value of percent complete,

  • +
  • total_items=<INT> number of items being synced from shotgrid,

  • +
  • remaining_items=<INT> number of items not yet pulled from shotgrid,

  • +
  • data=<dict>

  • +
  • review_id=<INT> review.id,

  • +
  • review_link=<STR> url link to the syncsketch player with the review pulled from shotgrid,

  • +
+
+
Parameters:
+
    +
  • syncsketch_project_id (int)

  • +
  • playlist_code (str)

  • +
  • playlist_id (int)

  • +
  • review_id (int) – (optional)

  • +
+
+
Returns:
+

+
+
Return type:
+

dict

+
+
+
+ +
+
+shotgun_sync_new_item_notes(project_id, review_id, item_id)
+

Sync new notes from SyncSketch review item to the original shotgrid playlist +Returns dict with information about the REST API call

+
    +
  • sketch_upload_error=<BOOL> “True in case of error”

  • +
  • sketches=<INT> “Number of sketches synced”

  • +
  • comments=<INT> “Number of comments synced”

  • +
  • attachments=<INT> “Number of attachments synced”

  • +
  • item_name=<STR> “Name of item that was synced”

  • +
+
+
Parameters:
+
    +
  • project_id (int) – SyncSketch project id

  • +
  • review_id (int) – SyncSketch review id

  • +
  • item_id (int) – SyncSketch item id

  • +
+
+
Returns:
+

+
+
+
+ +
+
+shotgun_sync_review_notes(review_id)
+

Sync notes from SyncSketch review to the original shotgrid playlist +Returns task id to use in get_shotgun_sync_review_notes_progress to get progress

+

returns dict with information about the REST API call:

+
    +
  • message=<STR> “Shotgrid review notes sync started”

  • +
  • status=<STR> processing/done/failed

  • +
  • progress_url=<STR> Full url to call for progress/results

  • +
  • task_id=<STR> task_ids pass this value to the get_shotgun_sync_review_items_progress function

  • +
  • percent_complete=<INT> 0-100 value of percent complete

  • +
  • total_items=<INT> number of items being synced from shotgrid

  • +
  • remaining_items=<INT> number of items not yet pulled from shotgrid

  • +
+
+
Parameters:
+

review_id (int) – SyncSketch review id

+
+
Returns:
+

Progress information

+
+
Return type:
+

dict

+
+
+
+ +
+
+shotgun_get_playlists(syncsketch_account_id, syncsketch_project_id, shotgun_project_id=None)
+

Returns list of Shotgrid playlists modified in the last 120 days +If the syncsketch project is directly linked to a shotgrid by the workspace admin, the +param shotgun_project_id will be ignored and can be omitted during the function call

+
+
Parameters:
+
    +
  • syncsketch_account_id (int) – SyncSketch account id

  • +
  • syncsketch_project_id (int) – SyncSketch project id

  • +
  • shotgun_project_id (int) – (optional) Shotgrid project id

  • +
+
+
Returns:
+

list of Shotgrid playlists

+
+
+
+ +
+
+shotgun_create_config(syncsketch_account_id, syncsketch_project_id=None, data=None)
+

Create a new Shotgrid configuration for a SyncSketch workspace and optionally a project

+
+
Parameters:
+
    +
  • syncsketch_account_id (int)

  • +
  • syncsketch_project_id (int)

  • +
  • data (dict) – Configuration data.

  • +
+
+
Returns:
+

+
+
+
+ +
+
+shotgun_get_projects(syncsketch_project_id)
+

Returns list of Shotgrid projects connected to your account

+
+
Parameters:
+

syncsketch_project_id (int) – SyncSketch project id

+
+
+
+ +
+ +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/_build/objects.inv b/_build/objects.inv new file mode 100644 index 0000000000000000000000000000000000000000..62892ee28cc0cce8bd2b6ad174002af709dd0a5e GIT binary patch literal 718 zcmV;<0x|s~AX9K?X>NERX>N99Zgg*Qc_4OWa&u{KZXhxWBOp+6Z)#;@bUGkYd2VA< zYh`p}XdqB|bZBpGAVE+`AWUgua$$0L3L_v^WpZ8b#rNMXCQiPX<{x4c-pm?OK%$?5XbNS z6iDsWmYid5IYw$FOZfl@!+4jP$BKv3{q_Nt^+Qb+m7zDpGC%$>!!WF$4fkgVeB8aI z=38mL9A3#ZKXb*rCwn4|fZw|@X!Y$o*>3z_Om<8N%B|L5LN?bBMl1f&UIx(3#u|iV zIBR;GX>&|{+9NJKhkMaUIIhEnyBH_IWag6Fr z1tT6bk_ih%VxW6mvdpG#D-wwHI)T&F#1n{CSm&`!VZm&$rpSMoyN3(6CZ` zu#OFY<#;q8ug5UmuB7?}w=FkFCoM7J)nI+JV9E}!ZbyjnqYZxAGaKVQDm4Xa4>DTK8+5kGGd_>9Opa7QT zoSfCDEBj9xt~yNNnvwz_H7yK8J##&;wZKarJIMiuDwjgKs+IJO*UDoSG4-4;d87mI zK6muPA=RbC*2xEn)X4=a2~#3GWbIzA{OlI!Mnh<`F5orAN#ro74+IRN`K?v$G0CYS zzRpbpn9@Uh5YLJoL6PmAti5F%dCkquZJ4k;54sz1o17fxG?5|sXU-3Kbj-~pjgR^F zOnH%Pv=yJsIL+Jj!-D*2MO0AdzWe%xS)KmD+CzTHKN>E5RsHh)kc!032N(dpr=y)s A@Bjb+ literal 0 HcmV?d00001 diff --git a/_build/search.html b/_build/search.html new file mode 100644 index 0000000..7744bca --- /dev/null +++ b/_build/search.html @@ -0,0 +1,116 @@ + + + + + + + Search — SyncSketch Python API Library 1.0.10.5 documentation + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Search

+ + + + +

+ Searching for multiple words only shows matches that contain + all words. +

+ + +
+ + + +
+ + +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/_build/searchindex.js b/_build/searchindex.js new file mode 100644 index 0000000..8d853b2 --- /dev/null +++ b/_build/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"alltitles": {"SyncSketch Python API Library documentation": [[0, null]]}, "docnames": ["index"], "envversion": {"sphinx": 62, "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}, "filenames": ["index.rst"], "indexentries": {"add_comment() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.add_comment", false]], "add_item() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.add_item", false]], "add_media() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.add_media", false]], "add_media_by_url() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.add_media_by_url", false]], "add_media_v1() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.add_media_v1", false]], "add_media_v2() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.add_media_v2", false]], "add_users_to_project() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.add_users_to_project", false]], "add_users_to_workspace() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.add_users_to_workspace", false]], "archive_project() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.archive_project", false]], "archive_review() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.archive_review", false]], "bulk_delete_items() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.bulk_delete_items", false]], "create_project() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.create_project", false]], "delete_item() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.delete_item", false]], "delete_project() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.delete_project", false]], "delete_review() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.delete_review", false]], "duplicate_project() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.duplicate_project", false]], "get_accounts() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_accounts", false]], "get_annotations() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_annotations", false]], "get_connections_by_user_id() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_connections_by_user_id", false]], "get_flattened_annotations() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_flattened_annotations", false]], "get_grease_pencil_overlays() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_grease_pencil_overlays", false]], "get_items_by_review_id() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_items_by_review_id", false]], "get_media() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_media", false]], "get_project_by_id() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_project_by_id", false]], "get_project_storage() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_project_storage", false]], "get_projects() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_projects", false]], "get_projects_by_name() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_projects_by_name", false]], "get_review_by_id() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_review_by_id", false]], "get_review_by_name() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_review_by_name", false]], "get_review_by_uuid() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_review_by_uuid", false]], "get_review_storage() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_review_storage", false]], "get_reviews_by_project_id() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_reviews_by_project_id", false]], "get_shotgrid_sync_review_items_progress() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_shotgrid_sync_review_items_progress", false]], "get_shotgrid_sync_review_notes_progress() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_shotgrid_sync_review_notes_progress", false]], "get_shotgun_sync_review_items_progress() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_shotgun_sync_review_items_progress", false]], "get_tree() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_tree", false]], "get_user_by_email() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_user_by_email", false]], "get_user_by_id() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_user_by_id", false]], "get_users_by_name() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_users_by_name", false]], "get_users_by_project_id() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.get_users_by_project_id", false]], "is_connected() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.is_connected", false]], "move_items() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.move_items", false]], "remove_users_from_project() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.remove_users_from_project", false]], "remove_users_from_workspace() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.remove_users_from_workspace", false]], "restore_project() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.restore_project", false]], "restore_review() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.restore_review", false]], "shotgrid_create_config() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgrid_create_config", false]], "shotgrid_get_playlists() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgrid_get_playlists", false]], "shotgrid_sync_new_item_notes() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgrid_sync_new_item_notes", false]], "shotgrid_sync_review_items() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgrid_sync_review_items", false]], "shotgrid_sync_review_notes() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgrid_sync_review_notes", false]], "shotgun_create_config() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgun_create_config", false]], "shotgun_get_playlists() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgun_get_playlists", false]], "shotgun_get_projects() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgun_get_projects", false]], "shotgun_sync_new_item_notes() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgun_sync_new_item_notes", false]], "shotgun_sync_review_items() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgun_sync_review_items", false]], "shotgun_sync_review_notes() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.shotgun_sync_review_notes", false]], "sort_review_items() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.sort_review_items", false]], "syncsketchapi (class in syncsketch)": [[0, "syncsketch.SyncSketchAPI", false]], "update_account() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.update_account", false]], "update_item() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.update_item", false]], "update_project() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.update_project", false]], "update_review() (syncsketch.syncsketchapi method)": [[0, "syncsketch.SyncSketchAPI.update_review", false]]}, "objects": {"syncsketch": [[0, 0, 1, "", "SyncSketchAPI"]], "syncsketch.SyncSketchAPI": [[0, 1, 1, "", "add_comment"], [0, 1, 1, "", "add_item"], [0, 1, 1, "", "add_media"], [0, 1, 1, "", "add_media_by_url"], [0, 1, 1, "", "add_media_v1"], [0, 1, 1, "", "add_media_v2"], [0, 1, 1, "", "add_users_to_project"], [0, 1, 1, "", "add_users_to_workspace"], [0, 1, 1, "", "archive_project"], [0, 1, 1, "", "archive_review"], [0, 1, 1, "", "bulk_delete_items"], [0, 1, 1, "", "create_project"], [0, 1, 1, "", "delete_item"], [0, 1, 1, "", "delete_project"], [0, 1, 1, "", "delete_review"], [0, 1, 1, "", "duplicate_project"], [0, 1, 1, "", "get_accounts"], [0, 1, 1, "", "get_annotations"], [0, 1, 1, "", "get_connections_by_user_id"], [0, 1, 1, "", "get_flattened_annotations"], [0, 1, 1, "", "get_grease_pencil_overlays"], [0, 1, 1, "", "get_items_by_review_id"], [0, 1, 1, "", "get_media"], [0, 1, 1, "", "get_project_by_id"], [0, 1, 1, "", "get_project_storage"], [0, 1, 1, "", "get_projects"], [0, 1, 1, "", "get_projects_by_name"], [0, 1, 1, "", "get_review_by_id"], [0, 1, 1, "", "get_review_by_name"], [0, 1, 1, "", "get_review_by_uuid"], [0, 1, 1, "", "get_review_storage"], [0, 1, 1, "", "get_reviews_by_project_id"], [0, 1, 1, "", "get_shotgrid_sync_review_items_progress"], [0, 1, 1, "", "get_shotgrid_sync_review_notes_progress"], [0, 1, 1, "", "get_shotgun_sync_review_items_progress"], [0, 1, 1, "", "get_tree"], [0, 1, 1, "", "get_user_by_email"], [0, 1, 1, "", "get_user_by_id"], [0, 1, 1, "", "get_users_by_name"], [0, 1, 1, "", "get_users_by_project_id"], [0, 1, 1, "", "is_connected"], [0, 1, 1, "", "move_items"], [0, 1, 1, "", "remove_users_from_project"], [0, 1, 1, "", "remove_users_from_workspace"], [0, 1, 1, "", "restore_project"], [0, 1, 1, "", "restore_review"], [0, 1, 1, "", "shotgrid_create_config"], [0, 1, 1, "", "shotgrid_get_playlists"], [0, 1, 1, "", "shotgrid_sync_new_item_notes"], [0, 1, 1, "", "shotgrid_sync_review_items"], [0, 1, 1, "", "shotgrid_sync_review_notes"], [0, 1, 1, "", "shotgun_create_config"], [0, 1, 1, "", "shotgun_get_playlists"], [0, 1, 1, "", "shotgun_get_projects"], [0, 1, 1, "", "shotgun_sync_new_item_notes"], [0, 1, 1, "", "shotgun_sync_review_items"], [0, 1, 1, "", "shotgun_sync_review_notes"], [0, 1, 1, "", "sort_review_items"], [0, 1, 1, "", "update_account"], [0, 1, 1, "", "update_item"], [0, 1, 1, "", "update_project"], [0, 1, 1, "", "update_review"]]}, "objnames": {"0": ["py", "class", "Python class"], "1": ["py", "method", "Python method"]}, "objtypes": {"0": "py:class", "1": "py:method"}, "terms": {"": 0, "0": 0, "03": 0, "1": 0, "10": 0, "100": 0, "1024": 0, "11": 0, "120": 0, "12345": 0, "2": 0, "200": 0, "2015": 0, "2017": 0, "24": 0, "3": 0, "3d": 0, "51": 0, "52": 0, "720": 0, "98": 0, "A": 0, "For": 0, "If": 0, "In": 0, "It": 0, "TO": 0, "The": 0, "To": 0, "Will": 0, "__": 0, "__init__": 0, "about": 0, "access": 0, "account": 0, "account_id": 0, "accountid": 0, "activ": 0, "ad": 0, "add": 0, "add_com": 0, "add_item": 0, "add_media": 0, "add_media_by_url": 0, "add_media_v1": 0, "add_media_v2": 0, "add_users_to_project": 0, "add_users_to_workspac": 0, "addit": 0, "additem": 0, "additional_data": 0, "additionaldata": 0, "admin": 0, "after": 0, "against": 0, "all": 0, "all_with_rel": 0, "alreadi": 0, "also": 0, "alwai": 0, "an": 0, "ani": 0, "annot": 0, "anoth": 0, "api_kei": 0, "api_vers": 0, "applic": 0, "ar": 0, "archiv": 0, "archive_project": 0, "archive_review": 0, "arrai": 0, "artist": 0, "artist_nam": 0, "associ": 0, "attach": 0, "auth": 0, "authent": 0, "author": 0, "autodesk": 0, "autodoc_member_ord": 0, "automat": 0, "avail": 0, "aw": 0, "background": 0, "base": 0, "base64": 0, "befor": 0, "being": 0, "bool": 0, "bradi": 0, "browser": 0, "bulk_delete_item": 0, "bysourc": 0, "byte": 0, "caa": 0, "call": 0, "can": 0, "case": 0, "certain": 0, "check": 0, "class": 0, "cloud": 0, "cloudhelp": 0, "code": 0, "collabor": 0, "com": 0, "combin": 0, "command": 0, "comment": 0, "commun": 0, "compat": 0, "complet": 0, "composit": 0, "configur": 0, "connect": 0, "constructor": 0, "contain": 0, "content": 0, "conveni": 0, "convert": 0, "copi": 0, "copy_review": 0, "copy_set": 0, "copy_us": 0, "correct": 0, "creat": 0, "create_project": 0, "creator": 0, "current": 0, "dai": 0, "data": 0, "db": 0, "de": 0, "deactiv": 0, "debug": 0, "definit": 0, "delet": 0, "delete_item": 0, "delete_project": 0, "delete_review": 0, "deprecationwarn": 0, "describ": 0, "descript": 0, "determin": 0, "dict": 0, "dictionari": 0, "directli": 0, "disk": 0, "django": 0, "djangoproject": 0, "doc": 0, "doe": 0, "done": 0, "down": 0, "download": 0, "dropbox": 0, "duplicate_project": 0, "durat": 0, "dure": 0, "e": 0, "either": 0, "email": 0, "empti": 0, "en": 0, "enabl": 0, "encod": 0, "endr": 0, "enu": 0, "error": 0, "etc": 0, "even": 0, "exact": 0, "exampl": 0, "exist": 0, "expir": 0, "explor": 0, "extens": 0, "external_thumbnail_url": 0, "external_url": 0, "externali": 0, "fail": 0, "failur": 0, "fals": 0, "field": 0, "file": 0, "file_nam": 0, "filepath": 0, "find": 0, "first_nam": 0, "float": 0, "follow": 0, "foreign": 0, "format": 0, "found": 0, "fp": 0, "frame": 0, "from": 0, "full": 0, "function": 0, "g": 0, "gener": 0, "get": 0, "get_account": 0, "get_annot": 0, "get_connections_by_user_id": 0, "get_flattened_annot": 0, "get_grease_pencil_overlai": 0, "get_items_by_review_id": 0, "get_media": 0, "get_project": 0, "get_project_by_id": 0, "get_project_storag": 0, "get_projects_by_nam": 0, "get_review_by_id": 0, "get_review_by_nam": 0, "get_review_by_uuid": 0, "get_review_storag": 0, "get_reviews_by_project_id": 0, "get_shotgrid_sync_review_items_progress": 0, "get_shotgrid_sync_review_notes_progress": 0, "get_shotgun_sync_review_items_progress": 0, "get_shotgun_sync_review_notes_progress": 0, "get_tre": 0, "get_user_by_email": 0, "get_user_by_id": 0, "get_users_by_nam": 0, "get_users_by_project_id": 0, "getaccount": 0, "getmedia": 0, "good": 0, "greas": 0, "greasepencil": 0, "gt": 0, "gte": 0, "ha": 0, "have": 0, "header": 0, "height": 0, "here": 0, "homedir": 0, "host": 0, "hour": 0, "htm": 0, "html": 0, "http": 0, "i": 0, "id": 0, "ignor": 0, "imag": 0, "import": 0, "improv": 0, "inact": 0, "includ": 0, "include_archiv": 0, "include_connect": 0, "include_delet": 0, "include_inact": 0, "include_tag": 0, "info": 0, "inform": 0, "insensit": 0, "instead": 0, "int": 0, "interact": 0, "intern": 0, "invit": 0, "is_connect": 0, "is_publ": 0, "item": 0, "item_data": 0, "item_id": 0, "item_nam": 0, "item_uuid": 0, "itemparentid": 0, "items_to_mov": 0, "jpg": 0, "kei": 0, "kept": 0, "knowledg": 0, "last": 0, "last_nam": 0, "latest": 0, "learn": 0, "left": 0, "like": 0, "limit": 0, "link": 0, "list": 0, "ll": 0, "load": 0, "lt": 0, "lte": 0, "make": 0, "manag": 0, "maya": 0, "media": 0, "media_url": 0, "mediaurl": 0, "member": 0, "messag": 0, "meta": 0, "method": 0, "might": 0, "mode": 0, "model": 0, "modifi": 0, "more": 0, "most": 0, "move": 0, "move_item": 0, "movi": 0, "much": 0, "multipl": 0, "name": 0, "narrow": 0, "need": 0, "nest": 0, "new": 0, "new_review_id": 0, "noconvertflag": 0, "none": 0, "normal": 0, "note": 0, "number": 0, "obj": 0, "object": 0, "offset": 0, "old": 0, "omit": 0, "one": 0, "onli": 0, "onlin": 0, "option": 0, "origin": 0, "out": 0, "output": 0, "over": 0, "overlai": 0, "own": 0, "paper": 0, "param": 0, "paramet": 0, "pass": 0, "path": 0, "payload": 0, "pencil": 0, "per": 0, "percent": 0, "percent_complet": 0, "perform": 0, "period": 0, "permiss": 0, "player": 0, "playlist": 0, "playlist_cod": 0, "playlist_id": 0, "pleas": 0, "png": 0, "possibl": 0, "process": 0, "progress": 0, "progress_url": 0, "project": 0, "project_id": 0, "provid": 0, "pull": 0, "purg": 0, "queri": 0, "rain": 0, "rang": 0, "recommend": 0, "record": 0, "regardless": 0, "remaining_item": 0, "remov": 0, "remove_users_from_project": 0, "remove_users_from_workspac": 0, "requir": 0, "respons": 0, "rest": 0, "restor": 0, "restore_project": 0, "restore_review": 0, "result": 0, "retriev": 0, "return": 0, "return_as_base64": 0, "review": 0, "review_id": 0, "review_link": 0, "reviews__nam": 0, "reviews__project__nam": 0, "revis": 0, "revisionid": 0, "s3": 0, "schema": 0, "search": 0, "searchcriteria": 0, "second": 0, "server": 0, "servic": 0, "set": 0, "shotgrid": 0, "shotgrid_create_config": 0, "shotgrid_get_playlist": 0, "shotgrid_sync_new_item_not": 0, "shotgrid_sync_review_item": 0, "shotgrid_sync_review_not": 0, "shotgun_create_config": 0, "shotgun_get_playlist": 0, "shotgun_get_project": 0, "shotgun_project_id": 0, "shotgun_sync_new_item_not": 0, "shotgun_sync_review_item": 0, "shotgun_sync_review_not": 0, "should": 0, "sign": 0, "signifi": 0, "similar": 0, "singl": 0, "size": 0, "sketch": 0, "sketch_upload_error": 0, "some": 0, "sort": 0, "sort_review_item": 0, "sortord": 0, "sourc": 0, "specif": 0, "speed": 0, "stabil": 0, "start": 0, "startswith": 0, "statu": 0, "storag": 0, "str": 0, "string": 0, "success": 0, "summari": 0, "support": 0, "sure": 0, "sync": 0, "syncsketch_account_id": 0, "syncsketch_project_id": 0, "syncsketchapi": 0, "syntax": 0, "system": 0, "tag": 0, "task": 0, "task_id": 0, "test": 0, "text": 0, "thei": 0, "thi": 0, "time": 0, "tmp": 0, "token": 0, "tool": 0, "topic": 0, "total_item": 0, "trace": 0, "tree": 0, "true": 0, "try": 0, "type": 0, "unarch": 0, "unlik": 0, "up": 0, "updat": 0, "update_account": 0, "update_item": 0, "update_project": 0, "update_review": 0, "updated_item": 0, "upload": 0, "url": 0, "us": 0, "usag": 0, "use_header_auth": 0, "useexpiringtoken": 0, "user": 0, "user_id": 0, "usernam": 0, "uuid": 0, "v1": 0, "valu": 0, "veri": 0, "version": 0, "video": 0, "viewer": 0, "visit": 0, "wa": 0, "want": 0, "we": 0, "webm": 0, "when": 0, "whether": 0, "which": 0, "width": 0, "wish": 0, "with_tracing_pap": 0, "withitem": 0, "without": 0, "work": 0, "workspac": 0, "workspace_id": 0, "would": 0, "wp": 0, "writabl": 0, "www": 0, "xml": 0, "yet": 0, "you": 0, "your": 0, "zip": 0}, "titles": ["SyncSketch Python API Library documentation"], "titleterms": {"api": 0, "document": 0, "librari": 0, "python": 0, "syncsketch": 0}}) \ No newline at end of file diff --git a/docs/_build/man/syncsketchpythonapilibrary.1 b/docs/_build/man/syncsketchpythonapilibrary.1 new file mode 100644 index 0000000..ff04ed3 --- /dev/null +++ b/docs/_build/man/syncsketchpythonapilibrary.1 @@ -0,0 +1,1567 @@ +.\" Man page generated from reStructuredText. +. +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.TH "SYNCSKETCHPYTHONAPILIBRARY" "1" "Jul 25, 2024" "" "SyncSketch Python API Library" +.SH NAME +syncsketchpythonapilibrary \- SyncSketch Python API Library 1.0.10.5 +.sp +autodoc_member_order = ‘bysource’ +.INDENT 0.0 +.TP +.B class syncsketch.SyncSketchAPI +SyncSketchAPI is a class that provides a set of methods to interact with SyncSketch API. +.INDENT 7.0 +.TP +.B __init__( +.TP +.B auth, +.TP +.B api_key, +.TP +.B host=\(dqhttps://www.syncsketch.com\(dq, +.TP +.B useExpiringToken=False, +.TP +.B debug=False, +.TP +.B api_version=\(dqv1\(dq, +.TP +.B use_header_auth=False +.TP +.B ) +Constructor for SyncSketchAPI class. +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBauth\fP (\fIstr\fP) – The username of the user. +.IP \(bu 2 +\fBapi_key\fP (\fIstr\fP) – The api key of the user. +.IP \(bu 2 +\fBhost\fP (\fIstr\fP) – The host of the SyncSketch API. +.IP \(bu 2 +\fBuseExpiringToken\fP (\fIbool\fP) – If True, the token will expire after 1 hour. +.IP \(bu 2 +\fBdebug\fP (\fIbool\fP) – If True, the debug mode will be enabled. +.IP \(bu 2 +\fBapi_version\fP (\fIstr\fP) – The version of the SyncSketch API. +.IP \(bu 2 +\fBuse_header_auth\fP (\fIbool\fP) – If True, the authentication will be done using headers. +.UNINDENT +.TP +.B Returns +SyncSketchAPI object. +.TP +.B Return type +obj +.UNINDENT +.UNINDENT +.UNINDENT +.INDENT 0.0 +.TP +.B class syncsketch.SyncSketchAPI(auth, api_key, host=\(aqhttps://www.syncsketch.com\(aq, useExpiringToken=False, debug=False, api_version=\(aqv1\(aq, use_header_auth=False) +Bases: \fBobject\fP +.sp +Convenience API to communicate with the SyncSketch Service for collaborative online reviews +.INDENT 7.0 +.TP +.B is_connected() +Convenience function to check if the API is connected to SyncSketch +Will check against Status Code 200 and return False if not which most likely would be +and authorization error +.INDENT 7.0 +.TP +.B Returns +Connection success +.TP +.B Return type +bool +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_tree(withItems=False) +Get nested tree of account, projects, reviews and optionally items for the current user +.INDENT 7.0 +.TP +.B Parameters +\fBwithItems\fP (\fIbool\fP) – Include items in the response +.TP +.B Returns +Tree data +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_accounts() +Summary +.INDENT 7.0 +.TP +.B Returns +List of workspaces the user has access to +.TP +.B Return type +list[dict] +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B update_account(account_id, data) +Update a workspace / account +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBaccount_id\fP (\fIint\fP) – the id of the item +.IP \(bu 2 +\fBdata\fP (\fIdict\fP) – normal dict with data for item +.UNINDENT +.TP +.B Returns +Workspace / Account data +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B create_project(account_id, name, description=\(aq\(aq, data=None) +Add a project to your account. Please make sure to pass the accountId which you can query using the getAccounts command. +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBaccount_id\fP (\fIint\fP) – id of the account to connect with +.IP \(bu 2 +\fBname\fP (\fIstr\fP) – Name of the project +.IP \(bu 2 +\fBdescription\fP (\fIstr\fP) – Description of the project +.IP \(bu 2 +\fBdata\fP (\fIdict\fP) – additional information e.g is_public. Find out more about available fields at /api/v1/project/schema/. +.UNINDENT +.TP +.B Returns +Project data +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_projects(include_deleted=False, include_archived=False, include_tags=False, include_connections=False, limit=100, offset=0) +Get a list of currently active projects +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBinclude_deleted\fP (\fIbool\fP) – if true, include deleted projects +.IP \(bu 2 +\fBinclude_archived\fP (\fIbool\fP) – if true, include archived projects +.IP \(bu 2 +\fBinclude_tags\fP (\fIbool\fP) – if true, include tag list on the project object +.IP \(bu 2 +\fBinclude_connections\fP (\fIbool\fP) – if true, include full user connections on the project object +.IP \(bu 2 +\fBlimit\fP (\fIint\fP) – limit the number of results +.IP \(bu 2 +\fBoffset\fP (\fIint\fP) – offset the results +.UNINDENT +.TP +.B Returns +Dict with meta information and an array of found projects +.TP +.B Return type +list[dict] +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_projects_by_name(name) +Get a project by name regardless of status +.INDENT 7.0 +.TP +.B Parameters +\fBname\fP (\fIstr\fP) – Name to search for +.TP +.B Returns +List of projects +.TP +.B Return type +list[dict] +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_project_by_id(project_id) +Get single project by id +.INDENT 7.0 +.TP +.B Parameters +\fBproject_id\fP (\fIint\fP) – Project id +.TP +.B Returns +Project data +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_project_storage(project_id) +Get project storage usage in bytes +.INDENT 7.0 +.TP +.B Parameters +\fBproject_id\fP (\fIint\fP) – Project id +.TP +.B Returns +Storage usage in bytes +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B update_project(project_id, data) +Update a project +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBproject_id\fP (\fIint\fP) – the id of the item +.IP \(bu 2 +\fBdata\fP (\fIdict\fP) – dict with new data for item +.UNINDENT +.TP +.B Returns +updated project data +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B delete_project(project_id) +Delete a project by id. +.INDENT 7.0 +.TP +.B Parameters +\fBproject_id\fP (\fIint\fP) – Project ID to delete +.TP +.B Returns + +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B duplicate_project(project_id, name=None, copy_reviews=False, copy_users=False, copy_settings=False) +Create a new project from an existing project +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBproject_id\fP (\fIint\fP) – Source project id +.IP \(bu 2 +\fBname\fP (\fIstr\fP) – New project name +.IP \(bu 2 +\fBcopy_reviews\fP (\fIbool\fP) – Whether to copy reviews (without items) +.IP \(bu 2 +\fBcopy_users\fP (\fIbool\fP) – Whether to copy users +.IP \(bu 2 +\fBcopy_settings\fP (\fIbool\fP) – Whether to copy settings +.UNINDENT +.TP +.B Returns +New project data +.TP +.B Return type +dict[str, Any] +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B archive_project(project_id) +Archive a project +.INDENT 7.0 +.TP +.B Parameters +\fBproject_id\fP (\fIint\fP) +.TP +.B Returns + +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B restore_project(project_id) +Restore (unarchive) a project +.INDENT 7.0 +.TP +.B Parameters +\fBproject_id\fP (\fIint\fP) +.TP +.B Returns + +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_reviews_by_project_id(project_id, limit=100, offset=0) +Get list of reviews by project id. +.INDENT 7.0 +.TP +.B Parameters +\fBproject_id\fP (\fIint\fP) – SyncSketch project id +.TP +.B Returns +Dict with meta information and an array of found projects +.TP +.B Return type +list[dict] +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_review_by_name(name) +Get reviews by name using a case insensitive startswith query +.INDENT 7.0 +.TP +.B Parameters +\fBname\fP – String \- Name of the review +.TP +.B Returns +Dict with meta information and an array of found projects +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_review_by_id(review_id) +Get single review by id. +.INDENT 7.0 +.TP +.B Parameters +\fBreview_id\fP – Number +.TP +.B Returns +Review Dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_review_by_uuid(uuid) +Get single review by uuid. +UUID can be found in the review URL e.g. syncsketch.com/sketch// +.INDENT 7.0 +.TP +.B Parameters +\fBuuid\fP (\fIstr\fP) – UUID of the review. +.TP +.B Returns +Review dict +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_review_storage(review_id) +Get review storage usage in bytes +.INDENT 7.0 +.TP +.B Parameters +\fBreview_id\fP (\fIint\fP) – Review ID +.TP +.B Returns +Storage usage in bytes +.TP +.B Return type +int +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B update_review(review_id, data) +Update a review +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBreview_id\fP (\fIint\fP) – the id of the item +.IP \(bu 2 +\fBdata\fP (\fIdict\fP) – dict with data for item +.UNINDENT +.TP +.B Returns +updated review data +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B sort_review_items(review_id, items) +Update a review +.sp +Example \fIitems\fP param +.INDENT 7.0 +.INDENT 3.5 +.sp +.nf +.ft C +items = [{ + \(dqid\(dq: 1, # item id + \(dqsortorder\(dq: 0, # sortorder, starting at 0 +}] +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Method output example: +.INDENT 7.0 +.INDENT 3.5 +.sp +.nf +.ft C +# number of successful items sort updated +{ \(dqupdated_items\(dq: int } +.ft P +.fi +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBreview_id\fP (\fIint\fP) – the id of the item +.IP \(bu 2 +\fBitems\fP (\fIlist\fP) – payload +.UNINDENT +.TP +.B Returns +response +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B archive_review(review_id) +Archive a review +.INDENT 7.0 +.TP +.B Parameters +\fBreview_id\fP (\fIint\fP) +.TP +.B Returns +empty response +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B restore_review(review_id) +Restore (unarchive) a review +.INDENT 7.0 +.TP +.B Parameters +\fBreview_id\fP (\fIint\fP) +.TP +.B Returns +empty response +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B delete_review(review_id) +Delete a review by id. +.INDENT 7.0 +.TP +.B Parameters +\fBreview_id\fP (\fIint\fP) – Review ID to delete +.TP +.B Returns + +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B update_item(item_id, data) +Update an item +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBitem_id\fP (\fIint\fP) – the id of the item +.IP \(bu 2 +\fBdata\fP (\fIdict\fP) – dict with data for item +.UNINDENT +.TP +.B Returns +updated item data +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B add_item(review_id, name, fps, additional_data) +create a media item record and connect it to a review. This should be used in case you want to add items with externaly hosted +media by passing in the external_url and external_thumbnail_url to the additionalData dict e.g +.INDENT 7.0 +.INDENT 3.5 +.sp +.nf +.ft C +additionalData = { + external_url: http://52.24.98.51/wp\-content/uploads/2017/03/rain.jpg + external_thumbnail_url: http://52.24.98.51/wp\-content/uploads/2017/03/rain.jpg +} +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +or +.INDENT 7.0 +.INDENT 3.5 +.sp +.nf +.ft C +additionalData = { + width:1024 + height:720 + artist: \(dqBrady Endres\(dq + duration:3 (in seconds) + description: the description here + size: size in byte + type: image | video +} +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +NOTE: you always need to pass in FPS for SyncSketch to work! +.sp +For a complete list of available fields to set, please +visit \X'tty: link https://www.syncsketch.com/api/v1/item/schema/'\fI\%https://www.syncsketch.com/api/v1/item/schema/\fP\X'tty: link' +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBreview_id\fP (\fIint\fP) – Required review_id +.IP \(bu 2 +\fBname\fP (\fIstr\fP) – Name of the item +.IP \(bu 2 +\fBfps\fP (\fIfloat\fP) – The frame per second is very important for syncsketch to determine the correct number of frames +.IP \(bu 2 +\fBadditional_data\fP (\fIdict\fP) – dictionary with item info +.UNINDENT +.TP +.B Returns +Item data +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B add_media(review_id, filepath, artist_name=\(aq\(aq, file_name=\(aq\(aq, noConvertFlag=False, itemParentId=False) +Convenience function to upload a file to a review. It will automatically create +an Item and attach it to the review. NOTE \- if you are hosting your own media, please +use the addItem function and pass in the external_url and external_thumbnail_url +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBreview_id\fP (\fIint\fP) – Required review_id +.IP \(bu 2 +\fBfilepath\fP (\fIstr\fP) – path for the file on disk e.g /tmp/movie.webm +.IP \(bu 2 +\fBartist_name\fP (\fIstr\fP) – The name of the artist you want associated with this media file +.IP \(bu 2 +\fBfile_name\fP (\fIstr\fP) – The name of the file. Please make sure to pass the correct file extension +.IP \(bu 2 +\fBnoConvertFlag\fP (\fIbool\fP) – the video you are uploading is already in a browser compatible format +.IP \(bu 2 +\fBitemParentId\fP (\fIint\fP) – (Optional) set when you want to add a new version of an item. itemParentId is the id of the item you want to upload a new version for +.UNINDENT +.TP +.B Returns +Item data +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B add_media_by_url(review_id, media_url, artist_name=\(aq\(aq, noConvertFlag=False) +Convenience function to upload a mediaURl to a review. Please use this function when you already have your files in the cloud, e.g +AWS, Dropbox, Shotgrid, etc… +.sp +We will automatically create an Item and attach it to the review. +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBreview_id\fP (\fIint\fP) – Required review_id +.IP \(bu 2 +\fBmedia_url\fP (\fIstr\fP) – url to the media you are trying to upload +.IP \(bu 2 +\fBartist_name\fP (\fIstr\fP) – The name of the artist you want associated with this media file +.IP \(bu 2 +\fBnoConvertFlag\fP (\fIbool\fP) – the video you are uploading is already in a browser compatible format and does not need to be converted +.UNINDENT +.TP +.B Returns +Item data +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B add_media_v2(review_id, filepath, file_name=\(aq\(aq, item_uuid=None, noConvertFlag=False) +Similar to add_media method, but uploads the media file directly to SyncSketche’s internal S3 instead of to +the SyncSketch server. In some cases, using this method over add_media can improve upload performance and +stability. Unlike add_media this method does not return as much data about the created item. +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBreview_id\fP (\fIint\fP) – Required review_id. +.IP \(bu 2 +\fBfilepath\fP (\fIstr\fP) – path for the file on disk e.g /tmp/movie.webm. +.IP \(bu 2 +\fBfile_name\fP (\fIstr\fP) – The name of the file. Please make sure to pass the correct file extension. +.IP \(bu 2 +\fBnoConvertFlag\fP (\fIbool\fP) – the video you are uploading is already in a browser compatible format. +.UNINDENT +.TP +.B Returns +A dict, containing “item_id” and “uuid” or None on failure. +.TP +.B Return type +Optional[dict] +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_media(searchCriteria) +This is a general search function. You can search media items by +.sp +‘id’ +‘name’ +‘status’ +‘active’ +‘creator’: ALL_WITH_RELATIONS, <– these are foreign key queries +‘reviews’: ALL_WITH_RELATIONS, <– these are foreign key queries +‘created’ using ‘exact’, ‘range’, ‘gt’, ‘gte’, ‘lt’, ‘lte’ +.sp +To query items by foreign keys please use the foreign key syntax described in the Django search definition: +\X'tty: link https://docs.djangoproject.com/en/1.11/topics/db/queries/'\fI\%https://docs.djangoproject.com/en/1.11/topics/db/queries/\fP\X'tty: link' +.sp +If you want to query by “review name” for example you would pass in +.sp +reviews__name = NAME TO SEARCH +.sp +Using the “__” syntax you can even search for items by project like +.sp +reviews__project__name = $PROJECT NAME TO SEARCH +.sp +To speed up a query you can also pass in a limit e.g limit:10 +.sp +results = s.getMedia({‘reviews__project__name’:’test’, ‘limit’: 1, ‘active’: 1}) +.sp +NOTE: Please make sure to include the active:1 query if you only want active media. Deleted files are currently +only deactivated and kept for a certain period of time before they are “purged” from the system. +.INDENT 7.0 +.TP +.B Parameters +\fBsearchCriteria\fP (\fIdict\fP) – Search params +.TP +.B Returns +List of media items +.TP +.B Return type +list[dict] +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_items_by_review_id(review_id) +Get all items in a review +.INDENT 7.0 +.TP +.B Parameters +\fBreview_id\fP (\fIint\fP) – Review ID +.TP +.B Returns +List of media items +.TP +.B Return type +list[dict] +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B delete_item(item_id) +Delete a item by id. +.INDENT 7.0 +.TP +.B Parameters +\fBitem_id\fP (\fIint\fP) – Item ID to delete +.TP +.B Returns + +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B bulk_delete_items(item_ids) +Delete multiple items by id. +.INDENT 7.0 +.TP +.B Parameters +\fBitem_ids\fP (\fIlist\fP\fI[\fP\fIint\fP\fI]\fP) – List of item IDs to delete +.TP +.B Returns + +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B move_items(new_review_id, item_data) +Move items from one review to another +.sp +item_data should be a list of dictionaries with the old review id and the item id. +The items in the list will be moved to the new review for the param new_review_id +.INDENT 7.0 +.INDENT 3.5 +.sp +.nf +.ft C +# Example item_data +# review_id is the current review an item is in +# it will be moved to the new_review_id +items_to_move = [ + {\(dqreview_id\(dq: 1, \(dqitem_id\(dq: 1}, + {\(dqreview_id\(dq: 1, \(dqitem_id\(dq: 2}, + {\(dqreview_id\(dq: 1, \(dqitem_id\(dq: 3}, +] +.ft P +.fi +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBnew_review_id\fP (\fIint\fP) – The review id to move the items to +.IP \(bu 2 +\fBitem_data\fP (\fIlist\fP\fI[\fP\fIdict\fP\fI]\fP) – List of dictionaries with the old review id and the item id +.UNINDENT +.TP +.B Returns + +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B add_comment(item_id, text, review_id, frame=0) +Add a comment to an item +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBitem_id\fP (\fIint\fP) – Item to add the comment to +.IP \(bu 2 +\fBtext\fP (\fIstr\fP) – Comment text +.IP \(bu 2 +\fBreview_id\fP (\fIint\fP) – Review you are adding the comment to +.IP \(bu 2 +\fBframe\fP (\fIint\fP) – Frame number of the video to add the comment to (if applicable) +.UNINDENT +.TP +.B Returns + +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_annotations(item_id, revisionId=False, review_id=False) +Get sketches and comments for an item. Frames have a revision id which signifies a “set of notes”. +When querying an item you’ll get the available revisions for this item. If you wish to get only the latest +revision, please get the revisionId for the latest revision. +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBitem_id\fP (\fIint\fP) – id of the media item you are querying. +.IP \(bu 2 +\fBrevisionId\fP (\fIint\fP) – Optional revisionId to narrow down the results +.IP \(bu 2 +\fBreview_id\fP (\fIint\fP) – RECOMMENDED \- retrieve annotations for a specific review only. +.UNINDENT +.TP +.B Returns +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_flattened_annotations(review_id, item_id, with_tracing_paper=False, return_as_base64=False) +Returns a list of sketches either as signed urls from s3 or base64 encoded strings. +The sketches are composited over the background frame of the item. +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBreview_id\fP (\fIint\fP) – Review ID +.IP \(bu 2 +\fBitem_id\fP (\fIint\fP) – Item ID +.IP \(bu 2 +\fBwith_tracing_paper\fP (\fIbool\fP) – Include tracing paper in the response +.IP \(bu 2 +\fBreturn_as_base64\fP (\fIbool\fP) – Return sketches as base64 encoded strings +.UNINDENT +.TP +.B Returns +List of sketches as signed urls from s3 or base64 encoded strings +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_grease_pencil_overlays(review_id, item_id, homedir=None) +Download overlay sketches for Maya Greasepencil. +.sp +Download overlay sketches for Maya Greasepencil. Function will download +a zip file which contains an XML and the sketches as png files. Maya +can load the zip file to overlay the sketches over the 3D model! +.sp +For more information visit: +\X'tty: link https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2015/ENU/Maya/files/Grease-Pencil-Tool-htm.html'\fI\%https://knowledge.autodesk.com/support/maya/learn\-explore/caas/CloudHelp/cloudhelp/2015/ENU/Maya/files/Grease\-Pencil\-Tool\-htm.html\fP\X'tty: link' +.sp +PLEASE make sure that /tmp is writable +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBreview_id\fP (\fIint\fP) – Review ID +.IP \(bu 2 +\fBitem_id\fP (\fIint\fP) – Item ID +.IP \(bu 2 +\fBhomedir\fP (\fIstr\fP) – Optional path to download the zip file to +.UNINDENT +.TP +.B Returns +filePath to the zip file with the greasePencil data. +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_users_by_name(name) +Name is a combined search and will search in first_name, last_name and email +.INDENT 7.0 +.TP +.B Parameters +\fBname\fP (\fIstr\fP) – Name to search for +.TP +.B Returns +List of users +.TP +.B Return type +list[dict] +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_user_by_email(email) +Get user by email +.INDENT 7.0 +.TP +.B Parameters +\fBemail\fP (\fIstr\fP) – Email to search for +.TP +.B Returns +User data +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_users_by_project_id(project_id) +Get all users in a project +.INDENT 7.0 +.TP +.B Parameters +\fBproject_id\fP (\fIint\fP) +.TP +.B Returns +List of users +.TP +.B Return type +list[dict] +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_connections_by_user_id(user_id, account_id, include_inactive=None, include_archived=None) +Get all project and account connections for a user. Good for checking access for a user that might have left… +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBuser_id\fP (\fIint\fP) – User ID to get connections for +.IP \(bu 2 +\fBaccount_id\fP (\fIint\fP) – Account ID to get connections for +.IP \(bu 2 +\fBinclude_inactive\fP (\fIbool\fP) – Include inactive projects +.IP \(bu 2 +\fBinclude_archived\fP (\fIbool\fP) – Include archived projects +.UNINDENT +.TP +.B Returns +List of connections +.TP +.B Return type +list[dict] +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_user_by_id(user_id) +Get a user by ID +.INDENT 7.0 +.TP +.B Parameters +\fBuser_id\fP (\fIint\fP) +.TP +.B Returns +User data +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B add_users_to_workspace(workspace_id, users, note=\(aq\(aq) +Add Users to Workspace +.INDENT 7.0 +.INDENT 3.5 +.sp +.nf +.ft C +users=[{\(dqemail\(dq:\(dqtest@test.de\(dq,\(dqpermission\(dq:\(dqadmin\(dq}] +.ft P +.fi +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBworkspace_id\fP (\fIint\fP) – id of the workspace +.IP \(bu 2 +\fBusers\fP (\fIlist\fP) – list of new users \- possible permissions “admin”, “manager” +.IP \(bu 2 +\fBnote\fP (\fIstr\fP) – (Optional) message for the invitation email +.UNINDENT +.TP +.B Returns +response +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B remove_users_from_workspace(workspace_id, users) +Remove a list of users from a workspace +Can remove by id or email +.INDENT 7.0 +.INDENT 3.5 +.sp +.nf +.ft C +users=[{\(dqemail\(dq:\(dqtest@test.de\(dq}, {\(dqid\(dq:12345}] +.ft P +.fi +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBworkspace_id\fP (\fIint\fP) – id of the workspace +.IP \(bu 2 +\fBusers\fP (\fIlist\fP) – list of users to remove \- either remove by user email or id +.UNINDENT +.TP +.B Returns +response +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B add_users_to_project(project_id, users, note=\(aq\(aq) +Add Users to Project +.sp +possible permissions +.INDENT 7.0 +.IP \(bu 2 +admin +.IP \(bu 2 +member +.IP \(bu 2 +viewer +.IP \(bu 2 +reviewer +.UNINDENT +.INDENT 7.0 +.INDENT 3.5 +.sp +.nf +.ft C +users=[{\(dqemail\(dq:\(dqtest@test.de\(dq,\(dqpermission\(dq:\(dqviewer\(dq}] +.ft P +.fi +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBproject_id\fP (\fIint\fP) – id of the project +.IP \(bu 2 +\fBusers\fP (\fIlist\fP\fI[\fP\fIdict\fP\fI]\fP) – list of new users +.IP \(bu 2 +\fBnote\fP (\fIstr\fP) – (Optional) message for the invitation email +.UNINDENT +.TP +.B Returns +response +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B remove_users_from_project(project_id, users) +Remove a list of users from a project +.sp +remove by user email or id +.INDENT 7.0 +.INDENT 3.5 +.sp +.nf +.ft C +users=[{\(dqemail\(dq:\(dqtest@test.de\(dq}, {\(dqid\(dq:12345}] +.ft P +.fi +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBproject_id\fP (\fIint\fP) – id of the project +.IP \(bu 2 +\fBusers\fP (\fIlist\fP) – list of users to remove \- either remove by user email or id +.UNINDENT +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B shotgrid_create_config(syncsketch_account_id, syncsketch_project_id=None, data=None) +Create a new Shotgrid configuration for a SyncSketch workspace and optionally a project +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBsyncsketch_account_id\fP (\fIint\fP) +.IP \(bu 2 +\fBsyncsketch_project_id\fP (\fIint\fP) +.IP \(bu 2 +\fBdata\fP (\fIdict\fP) – Configuration data. +.UNINDENT +.TP +.B Returns + +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B shotgrid_get_playlists(syncsketch_account_id, syncsketch_project_id, shotgun_project_id=None) +Returns list of Shotgrid playlists modified in the last 120 days +If the syncsketch project is directly linked to a shotgrid by the workspace admin, the +param shotgun_project_id will be ignored and can be omitted during the function call +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBsyncsketch_account_id\fP (\fIint\fP) – SyncSketch account id +.IP \(bu 2 +\fBsyncsketch_project_id\fP (\fIint\fP) – SyncSketch project id +.IP \(bu 2 +\fBshotgun_project_id\fP (\fIint\fP) – (optional) Shotgrid project id +.UNINDENT +.TP +.B Returns +list of Shotgrid playlists +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B shotgrid_sync_review_notes(review_id) +Sync notes from SyncSketch review to the original shotgrid playlist +Returns task id to use in get_shotgun_sync_review_notes_progress to get progress +.sp +returns dict with information about the REST API call: +.INDENT 7.0 +.IP \(bu 2 +message= “Shotgrid review notes sync started” +.IP \(bu 2 +status= processing/done/failed +.IP \(bu 2 +progress_url= Full url to call for progress/results +.IP \(bu 2 +task_id= task_ids pass this value to the get_shotgun_sync_review_items_progress function +.IP \(bu 2 +percent_complete= 0\-100 value of percent complete +.IP \(bu 2 +total_items= number of items being synced from shotgrid +.IP \(bu 2 +remaining_items= number of items not yet pulled from shotgrid +.UNINDENT +.INDENT 7.0 +.TP +.B Parameters +\fBreview_id\fP (\fIint\fP) – SyncSketch review id +.TP +.B Returns +Progress information +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B shotgrid_sync_new_item_notes(project_id, review_id, item_id) +Sync new notes from SyncSketch review item to the original shotgrid playlist +Returns dict with information about the REST API call +.INDENT 7.0 +.IP \(bu 2 +sketch_upload_error= “True in case of error” +.IP \(bu 2 +sketches= “Number of sketches synced” +.IP \(bu 2 +comments= “Number of comments synced” +.IP \(bu 2 +attachments= “Number of attachments synced” +.IP \(bu 2 +item_name= “Name of item that was synced” +.UNINDENT +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBproject_id\fP (\fIint\fP) – SyncSketch project id +.IP \(bu 2 +\fBreview_id\fP (\fIint\fP) – SyncSketch review id +.IP \(bu 2 +\fBitem_id\fP (\fIint\fP) – SyncSketch item id +.UNINDENT +.TP +.B Returns + +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_shotgrid_sync_review_notes_progress(task_id) +Returns status of review notes sync for the task id provided in shotgun_sync_review_notes +.sp +Returns a dict with the following keys: +.INDENT 7.0 +.IP \(bu 2 +message= “Shotgrid review notes sync started” +.IP \(bu 2 +status= processing/done/failed +.IP \(bu 2 +progress_url= Full url to call for progress/results +.IP \(bu 2 +task_id= task_ids pass this value to the get_shotgun_sync_review_items_progress function +.IP \(bu 2 +percent_complete= 0\-100 value of percent complete +.IP \(bu 2 +total_items= number of items being synced from shotgrid +.IP \(bu 2 +remaining_items= number of items not yet pulled from shotgrid +.UNINDENT +.INDENT 7.0 +.TP +.B Parameters +\fBtask_id\fP (\fIstr\fP) – UUID of the task returned by shotgrid_sync_review_notes +.TP +.B Returns +Progress information +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B shotgrid_sync_review_items(syncsketch_project_id, playlist_code, playlist_id, review_id=None) +Create or update SyncSketch review with shotgrid playlist items +Returns task id to use in get_shotgun_sync_review_items_progress to get progress +.sp +Response format: +.INDENT 7.0 +.IP \(bu 2 +message= “Shotgrid review item sync started”, +.IP \(bu 2 +status= processing/done/failed, +.IP \(bu 2 +progress_url= Full url to call for progress/results, +.IP \(bu 2 +task_id= task_ids \- pass this value to the get_shotgun_sync_review_items_progress function, +.IP \(bu 2 +percent_complete= 0\-100 value of percent complete, +.IP \(bu 2 +total_items= number of items being synced from shotgrid, +.IP \(bu 2 +remaining_items= number of items not yet pulled from shotgrid, +.IP \(bu 2 +data= +.IP \(bu 2 +review_id= review.id, +.IP \(bu 2 +review_link= url link to the syncsketch player with the review pulled from shotgrid, +.UNINDENT +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBsyncsketch_project_id\fP (\fIint\fP) +.IP \(bu 2 +\fBplaylist_code\fP (\fIstr\fP) +.IP \(bu 2 +\fBplaylist_id\fP (\fIint\fP) +.IP \(bu 2 +\fBreview_id\fP (\fIint\fP) – (optional) +.UNINDENT +.TP +.B Returns + +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_shotgrid_sync_review_items_progress(task_id) +Returns status of review items sync for the task id provided in shotgun_sync_review_items +.INDENT 7.0 +.TP +.B Parameters +\fBtask_id\fP (\fIstr\fP) – UUID of the task returned by shotgrid_sync_review_items +.TP +.B Returns +DeprecationWarning +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B add_media_v1(review_id, filepath, artist_name=\(aq\(aq, file_name=\(aq\(aq, noConvertFlag=False, itemParentId=False) +Convenience function to upload a file to a review. It will automatically create +an Item and attach it to the review. NOTE \- if you are hosting your own media, please +use the addItem function and pass in the external_url and external_thumbnail_url +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBreview_id\fP (\fIint\fP) – Required review_id +.IP \(bu 2 +\fBfilepath\fP (\fIstr\fP) – path for the file on disk e.g /tmp/movie.webm +.IP \(bu 2 +\fBartist_name\fP (\fIstr\fP) – The name of the artist you want associated with this media file +.IP \(bu 2 +\fBfile_name\fP (\fIstr\fP) – The name of the file. Please make sure to pass the correct file extension +.IP \(bu 2 +\fBnoConvertFlag\fP (\fIbool\fP) – the video you are uploading is already in a browser compatible format +.IP \(bu 2 +\fBitemParentId\fP (\fIint\fP) – (Optional) set when you want to add a new version of an item. itemParentId is the id of the item you want to upload a new version for +.UNINDENT +.TP +.B Returns +Item data +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B get_shotgun_sync_review_items_progress(task_id) +Returns status of review items sync for the task id provided in shotgun_sync_review_items +.INDENT 7.0 +.TP +.B Parameters +\fBtask_id\fP (\fIstr\fP) – UUID of the task returned by shotgrid_sync_review_items +.TP +.B Returns +DeprecationWarning +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B shotgun_sync_review_items(syncsketch_project_id, playlist_code, playlist_id, review_id=None) +Create or update SyncSketch review with shotgrid playlist items +Returns task id to use in get_shotgun_sync_review_items_progress to get progress +.sp +Response format: +.INDENT 7.0 +.IP \(bu 2 +message= “Shotgrid review item sync started”, +.IP \(bu 2 +status= processing/done/failed, +.IP \(bu 2 +progress_url= Full url to call for progress/results, +.IP \(bu 2 +task_id= task_ids \- pass this value to the get_shotgun_sync_review_items_progress function, +.IP \(bu 2 +percent_complete= 0\-100 value of percent complete, +.IP \(bu 2 +total_items= number of items being synced from shotgrid, +.IP \(bu 2 +remaining_items= number of items not yet pulled from shotgrid, +.IP \(bu 2 +data= +.IP \(bu 2 +review_id= review.id, +.IP \(bu 2 +review_link= url link to the syncsketch player with the review pulled from shotgrid, +.UNINDENT +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBsyncsketch_project_id\fP (\fIint\fP) +.IP \(bu 2 +\fBplaylist_code\fP (\fIstr\fP) +.IP \(bu 2 +\fBplaylist_id\fP (\fIint\fP) +.IP \(bu 2 +\fBreview_id\fP (\fIint\fP) – (optional) +.UNINDENT +.TP +.B Returns + +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B shotgun_sync_new_item_notes(project_id, review_id, item_id) +Sync new notes from SyncSketch review item to the original shotgrid playlist +Returns dict with information about the REST API call +.INDENT 7.0 +.IP \(bu 2 +sketch_upload_error= “True in case of error” +.IP \(bu 2 +sketches= “Number of sketches synced” +.IP \(bu 2 +comments= “Number of comments synced” +.IP \(bu 2 +attachments= “Number of attachments synced” +.IP \(bu 2 +item_name= “Name of item that was synced” +.UNINDENT +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBproject_id\fP (\fIint\fP) – SyncSketch project id +.IP \(bu 2 +\fBreview_id\fP (\fIint\fP) – SyncSketch review id +.IP \(bu 2 +\fBitem_id\fP (\fIint\fP) – SyncSketch item id +.UNINDENT +.TP +.B Returns + +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B shotgun_sync_review_notes(review_id) +Sync notes from SyncSketch review to the original shotgrid playlist +Returns task id to use in get_shotgun_sync_review_notes_progress to get progress +.sp +returns dict with information about the REST API call: +.INDENT 7.0 +.IP \(bu 2 +message= “Shotgrid review notes sync started” +.IP \(bu 2 +status= processing/done/failed +.IP \(bu 2 +progress_url= Full url to call for progress/results +.IP \(bu 2 +task_id= task_ids pass this value to the get_shotgun_sync_review_items_progress function +.IP \(bu 2 +percent_complete= 0\-100 value of percent complete +.IP \(bu 2 +total_items= number of items being synced from shotgrid +.IP \(bu 2 +remaining_items= number of items not yet pulled from shotgrid +.UNINDENT +.INDENT 7.0 +.TP +.B Parameters +\fBreview_id\fP (\fIint\fP) – SyncSketch review id +.TP +.B Returns +Progress information +.TP +.B Return type +dict +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B shotgun_get_playlists(syncsketch_account_id, syncsketch_project_id, shotgun_project_id=None) +Returns list of Shotgrid playlists modified in the last 120 days +If the syncsketch project is directly linked to a shotgrid by the workspace admin, the +param shotgun_project_id will be ignored and can be omitted during the function call +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBsyncsketch_account_id\fP (\fIint\fP) – SyncSketch account id +.IP \(bu 2 +\fBsyncsketch_project_id\fP (\fIint\fP) – SyncSketch project id +.IP \(bu 2 +\fBshotgun_project_id\fP (\fIint\fP) – (optional) Shotgrid project id +.UNINDENT +.TP +.B Returns +list of Shotgrid playlists +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B shotgun_create_config(syncsketch_account_id, syncsketch_project_id=None, data=None) +Create a new Shotgrid configuration for a SyncSketch workspace and optionally a project +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBsyncsketch_account_id\fP (\fIint\fP) +.IP \(bu 2 +\fBsyncsketch_project_id\fP (\fIint\fP) +.IP \(bu 2 +\fBdata\fP (\fIdict\fP) – Configuration data. +.UNINDENT +.TP +.B Returns + +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B shotgun_get_projects(syncsketch_project_id) +Returns list of Shotgrid projects connected to your account +.INDENT 7.0 +.TP +.B Parameters +\fBsyncsketch_project_id\fP (\fIint\fP) – SyncSketch project id +.UNINDENT +.UNINDENT +.UNINDENT +.SH AUTHOR +Brady Endres, Phil Floetotto, Nicholas Kegler dos Santos, Tyler Nickerson +.SH COPYRIGHT +2024, Brady Endres, Phil Floetotto, Nicholas Kegler dos Santos, Tyler Nickerson +.\" Generated by docutils manpage writer. +. From 92c2fb1acdb3b088ec2286270fac82f78a13946e Mon Sep 17 00:00:00 2001 From: Brady Endres Date: Thu, 25 Jul 2024 17:41:50 -0700 Subject: [PATCH 3/3] fix typo --- .github/workflows/documentation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 6ace7a6..e0d7bf4 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -16,7 +16,7 @@ jobs: pip install sphinx sphinx_rtd_theme myst_parser - name: Sphinx build run: | - sphinx-build doc _build + sphinx-build docs _build - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v3 if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}