From 9c1fee201cfd963393e61a0ee87e8a9030142cc4 Mon Sep 17 00:00:00 2001 From: PepperLola Date: Wed, 26 Jun 2024 11:30:04 -0700 Subject: [PATCH 1/8] added authorization and user info retrieval, no error handling yet --- exporter/SynthesisFusionAddin/.gitignore | 4 +- exporter/SynthesisFusionAddin/Synthesis.py | 14 +- exporter/SynthesisFusionAddin/src/APS/APS.py | 124 ++++++++++++++++++ .../src/Resources/APS/16x16-disabled.png | Bin 0 -> 1469 bytes .../src/Resources/APS/16x16-normal.png | Bin 0 -> 1535 bytes .../src/Resources/APS/32x32-disabled.png | Bin 0 -> 2002 bytes .../src/Resources/APS/32x32-normal.png | Bin 0 -> 2176 bytes .../src/Resources/APS/64x64-normal.png | Bin 0 -> 3335 bytes .../src/UI/ConfigCommand.py | 9 ++ .../src/UI/ShowAPSAuthCommand.py | 111 ++++++++++++++++ 10 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 exporter/SynthesisFusionAddin/src/APS/APS.py create mode 100644 exporter/SynthesisFusionAddin/src/Resources/APS/16x16-disabled.png create mode 100644 exporter/SynthesisFusionAddin/src/Resources/APS/16x16-normal.png create mode 100644 exporter/SynthesisFusionAddin/src/Resources/APS/32x32-disabled.png create mode 100644 exporter/SynthesisFusionAddin/src/Resources/APS/32x32-normal.png create mode 100644 exporter/SynthesisFusionAddin/src/Resources/APS/64x64-normal.png create mode 100644 exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py diff --git a/exporter/SynthesisFusionAddin/.gitignore b/exporter/SynthesisFusionAddin/.gitignore index eca33a87a8..9e33b772e7 100644 --- a/exporter/SynthesisFusionAddin/.gitignore +++ b/exporter/SynthesisFusionAddin/.gitignore @@ -107,4 +107,6 @@ site-packages # env files **/.env -proto/proto_out \ No newline at end of file +proto/proto_out + +.aps_auth diff --git a/exporter/SynthesisFusionAddin/Synthesis.py b/exporter/SynthesisFusionAddin/Synthesis.py index 4ed3a0fede..b50675b789 100644 --- a/exporter/SynthesisFusionAddin/Synthesis.py +++ b/exporter/SynthesisFusionAddin/Synthesis.py @@ -1,9 +1,10 @@ from .src.general_imports import root_logger, gm, INTERNAL_ID, APP_NAME, DESCRIPTION -from .src.UI import HUI, Handlers, Camera, Helper, ConfigCommand +from .src.UI import HUI, Handlers, Camera, Helper, ConfigCommand, ShowAPSAuthCommand from .src.UI.Toolbar import Toolbar from .src.Types.OString import OString from .src.configure import setAnalytics, unload_config +from .src.APS import APS from shutil import rmtree import logging.handlers, traceback, importlib.util, os @@ -129,3 +130,14 @@ def register_ui() -> None: ) gm.elements.append(commandButton) + + apsButton = HUI.HButton( + "APS", + work_panel, + Helper.check_solid_open, + ShowAPSAuthCommand.ShowAPSAuthCommandCreatedHandler, + description=f"APS TEST", + command=True, + ) + + gm.elements.append(apsButton) diff --git a/exporter/SynthesisFusionAddin/src/APS/APS.py b/exporter/SynthesisFusionAddin/src/APS/APS.py new file mode 100644 index 0000000000..2f44454e53 --- /dev/null +++ b/exporter/SynthesisFusionAddin/src/APS/APS.py @@ -0,0 +1,124 @@ +from dataclasses import dataclass +import pickle +from src.general_imports import root_logger, gm, INTERNAL_ID, APP_NAME, DESCRIPTION, my_addin_path +import time +import webbrowser +import logging +import urllib.parse +import urllib.request +import json +from src.UI.OsHelper import getOSPath +import os +import adsk.core, traceback + +CLIENT_ID = 'GCxaewcLjsYlK8ud7Ka9AKf9dPwMR3e4GlybyfhAK2zvl3tU' +auth_path = os.path.abspath(os.path.join(my_addin_path, "..", ".aps_auth")) + +APS_AUTH = None +APS_USER_INFO = None + +@dataclass +class APSAuth: + access_token: str + refresh_token: str + expires_in: int + token_type: str + +@dataclass +class APSUserInfo: + name: str + given_name: str + family_name: str + preferred_username: str + email: str + email_verified: bool + profile: str + locale: str + country_code: str + about_me: str + language: str + company: str + picture: str + +def getAPSAuth() -> APSAuth | None: + return APS_AUTH + +def _res_json(res): + return json.loads(res.read().decode(res.info().get_param('charset') or 'utf-8')) + +def getCodeChallenge() -> str: + endpoint = 'http://localhost:3003/api/aps/challenge/' + res = urllib.request.urlopen(endpoint) + data = _res_json(res) + logging.getLogger(f"{INTERNAL_ID}").info(f"CHALLENGE: {data}") + return data["challenge"] + +def getAuth() -> APSAuth: + global APS_AUTH + if APS_AUTH is not None: + return APS_AUTH + try: + with open(auth_path, 'rb') as f: + p = pickle.load(f) + logging.getLogger(f"{INTERNAL_ID}").info(f"LOADING PICKLED FILE: {p}") + APS_AUTH = APSAuth( + access_token=p["access_token"], + refresh_token=p["refresh_token"], + expires_in=p["expires_in"], + token_type=p["token_type"] + ) + except: + raise Exception("Need to sign in!") + if APS_USER_INFO is None: + loadUserInfo() + return APS_AUTH + +def convertAuthToken(code: str): + global APS_AUTH + authUrl = f'http://localhost:3003/api/aps/code/?code={code}&redirect_uri={urllib.parse.quote_plus("http://localhost:3003/api/aps/exporter/")}' + res = urllib.request.urlopen(authUrl) + data = _res_json(res)['response'] + logging.getLogger(f"{INTERNAL_ID}").info(f"AUTH TOKEN: {data}") + APS_AUTH = APSAuth( + access_token=data["access_token"], + refresh_token=data["refresh_token"], + expires_in=data["expires_in"], + token_type=data["token_type"] + ) + with open(auth_path, 'wb') as f: + pickle.dump(data, f) + f.close() + + loadUserInfo() + +def loadUserInfo() -> APSUserInfo | None: + global APS_AUTH + if not APS_AUTH: + return None + global APS_USER_INFO + req = urllib.request.Request("https://api.userprofile.autodesk.com/userinfo") + req.add_header(key="Authorization", val=APS_AUTH.access_token) + res = urllib.request.urlopen(req) + data = _res_json(res) + logging.getLogger(f"{INTERNAL_ID}").info(f"USER INFO: {data}") + APS_USER_INFO = APSUserInfo( + name=data["name"], + given_name=data["given_name"], + family_name=data["family_name"], + preferred_username=data["preferred_username"], + email=data["email"], + email_verified=data["email_verified"], + profile=data["profile"], + locale=data["locale"], + country_code=data["country_code"], + about_me=data["about_me"], + language=data["language"], + company=data["company"], + picture=data["picture"] + ) + return APS_USER_INFO + +def getUserInfo() -> APSUserInfo | None: + if APS_USER_INFO is not None: + return APS_USER_INFO + return loadUserInfo() diff --git a/exporter/SynthesisFusionAddin/src/Resources/APS/16x16-disabled.png b/exporter/SynthesisFusionAddin/src/Resources/APS/16x16-disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..f4ba1b8f8393d4c4dbf6f24a1034a85484b017c6 GIT binary patch literal 1469 zcmaJ>eNYr-7(XI1V89QQ2vN2b$Pf1R^KL)lWbSScxa5#dDr^$nyWNF7xZ9iCMNXnA z%@K()(itJfGG`i)!NG>eaReM8KWNHqCUeY!nub5huxuo2m>lh0;LJZ9clO~e@(J+Fp1vo5El%?mJ50wfWnBk{$E)ogyUEnw zmV8G@0O!PT$!5)DN51@rKgnht3k6Msy@ja4TgcX$y87n4GaES zNr!w=wGy(yH;2x^kRsaPmn|O56Lv~9N=aix+SFK9;cu+^gKKm>%%NyQLxZ`2G^>$n6lYl$#R!xj z5TJo*J3_h;MMB!DIgoi6Uef##C9ErI2r^&=pIWcmU=Z|NHo>sRb00mV%@+-n4~+_8 z6gOjNFleMWtF7r?>Hk}>6dA*Kn61OYVl2s!1Y@`3 zxQl05JMQEyEQ!%p7ful|TX#s)g^*v0`Bi}5d0XOlEnKTq=l^bNg-U%C_w9#uL(0`YQ8(^B%pt zBX4)cft*vei|;DqpX}r(4_{bz=nmg5?RGqqvm}v+7e`N5?VpjUOj~+V!okElZYHaU z$~-R**s%=W+!5dB6?a|<96b8r@JDZsO^(hqUGVj9vpLTEwBYsL&#S^qF;Cjm?aXU+ z>Ak(5-Tdb*>nkG4B)wnuER~s3v3A)-nLOvis}8Ox-We@;r!?>E!WC`BceC;WV=o-J zIwcHEz@4SHZvF7P_3wXA8)(~ZetdOmb;p-IFCDvavgM+hl`!t;N!zM4Y#p31Kb_j_ zn|b5C!tFy_S`rJc1S}GkG2EKf0BPA%cbnG#V*7KZX*}&OxWDzsvJ&_6UEb_HLetxp zRP9x0(TGUIomkg>dCfQ1si#g%S1(-Z`Z;%H>H_MJC%GG$`8;`l0 guh#vM?kKsQJh`PS^W>?4Hsc@ac9rqR9ou&O2Pq5pmjD0& literal 0 HcmV?d00001 diff --git a/exporter/SynthesisFusionAddin/src/Resources/APS/16x16-normal.png b/exporter/SynthesisFusionAddin/src/Resources/APS/16x16-normal.png new file mode 100644 index 0000000000000000000000000000000000000000..34948454a985666777913757ecc4a1fa6102867e GIT binary patch literal 1535 zcmaJ>eNYr-7+;VJgU}<tz!T z%C$<=iCjBy2!R#JiB#wLaG&Ou>y(maU9M>MSBlN`A}1jQ`59@B7!MF4vMIo^NH}Wn zF()zx%!Bv17(?JO6|>%nxZ)n+fUgX8tGWy`HUbrK9ETasCQAYtq(XwY5GHYwzzA?N zD8cX~#SvO+@qg?x9DGiK2n>VKG6&2pf(? zBJmW*v<`}#45jkpl zJx&CCu}O-=<2Z{G1ViHtiQ8F%U>Tyw#j+%c({7rhsRYJ@Yzy-^oTFHZWL++TSnT0A z7vc8gaTLzj7ZWs@u=Pd_Q;3Rk!mk4SPS}zUwdLKqESRcZsjA@w8OrKZQ#I;T4R*Uh z=LrU$?-N8N8W%1|bPDupJavz(D-E(#q^l8ltk}HrfM6(+39$l2qIQW2p)|`%sKD59 zloDA9ltt!3d3nf$y>wp@$9W4RAR#Rg7mwRO~2f`{LDbgO>xG+&?o;+I(^*Qu|2enM%P)LrE9gUIpNVcJCa*pD7hsa%RD=JWb1*tG=0PCzx8;%-*xP-wjaNC745&h=I(&}?a93r zndY6HHTHdKO8?)F$yI&Vhnp55wR0P;_Ixe5Ts=>m8k}o)oQq9wS=8fS5$Rsp)Vem( z-;f~}bnLyA>7R1v;+58o*A;6<-^e?kcUJfQa<(mX0C{R!=I)9=0>+vmM{e-$NY>*+ wcwgE6{1Z8?ySKetQZQ>q?u})0*YkZ)-=$sRbA!Vz@xQQlvEQ?=@RfD{0oLsno&W#< literal 0 HcmV?d00001 diff --git a/exporter/SynthesisFusionAddin/src/Resources/APS/32x32-disabled.png b/exporter/SynthesisFusionAddin/src/Resources/APS/32x32-disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..770208ec0e4889c412d946e67f8dcd6bc23b24ec GIT binary patch literal 2002 zcmaJ?dsGu=77tR;66t{z&;=^fAQmV|W+v}hB8EgDDbYwH(g+8J$wUGq6DI=+gk_a= zU7$z>!=c+0 zpjaMG0onLYqgl$D?zzMQjCv_+yFd-9Epj}^m{d&S?-Xy=V#RrwM9+%b;Oi4>M`#2S zPN9I^ly9~ob}4Hf7@^2oj5+1esV2!ysSIm+*N@ z7%#Jp7f3(}Pt1eGG8qJIQb;5+NUjh_c%VqQ3F5;`wo0>=Ld_Vy{LCu^oEo-Isc%aO$;zC=0+1@Ov zFo^Xh{q50D4aW^TZ`A2i&+{s7tzp*q<(HRVSF8xq4f_oPWyRVX-IGtwZ%v;2?arZEd{nS-=mD;p_Lt zddf8A<>k9?h1mH?%m+j)YN~?&PFCa^S6E(9v*I6_vX5F-?nY=(49Jc+Mk~7n7G*3 zZzd+Z`~Ldw>5-8XXMO$PosRfJiG>^0ExJ87Z)(@AUw@{gsHoz~6`8rErKPK9eE%D9 zabYzzH8IQOoy*R>%{>QXtlm{xTHV?jalE4g%gbXH_a5s1eUom_!-siM9!jM$CpS0V zZ01aV5Omn(8ec!9`pl;)*Q>g^+G4jyI(A#Io40Q_cXkH)`uZkq*-}q9n{*!V`L?#G zPlt!;>LphfR~-y`Gvb=u*{t&@i}_taK_G@QA7N9z0f;@7P7zQd$JVIHrh zzP^9|_oMeMr@lV8+WttcNPR6?r8%*&uuyocy}eu7OZddj%*>pb?2eB3OKO(Gu|u_S z+qS^u^{Xl?D>a#!)At1369WUU4qnlmxct>uYx9L6(+5g=8yguoj+Y2tX(+pu`_!v7 zYp;{JLbZ{4ops2XLLv=fsq`N}K%}?kZ+_jo@yGX$9|&AgH|ewY+kMY#pZi?Q^>n#h z>1lgC&u}Z7KW2spY#oZI)JpNr3*WT)BY();QS5MB^47%)g+T&=U~0x$qEe~+R;{{v zqO0qJhw|X?3r8ZACxcfu+{r&$`_Jte8KZ>1R;37i9{F`|BTa-|yLN>>8yOi9|7xV! zxjOo-w@)8UUlS9eV9YxtbJaw_NFYag>2KQaF3I-KwN S%~7rU$Ew`4S#dHxtL%T(C<5XD literal 0 HcmV?d00001 diff --git a/exporter/SynthesisFusionAddin/src/Resources/APS/32x32-normal.png b/exporter/SynthesisFusionAddin/src/Resources/APS/32x32-normal.png new file mode 100644 index 0000000000000000000000000000000000000000..77458fce7d136cceab5bce620c5501e226f82445 GIT binary patch literal 2176 zcmaJ@dsGu=77w^a2#O*PmHHSG-3pRqW|EKxL=6cEL4q`*Qbl2sOdvuYCL;t8X|+&_ zqJS%u9_kCJwX(PZt!S%gK|w);V^=I%c~b=x1X@^L-3fy2AD+&cZ@&5N+{f?U`@84N zXTd>|1teDzfk0Rw^%u$U9k47LBK|ZbBn;!5l}Rq~B^1{$e}NzV5h;)f2!zYW>?V-8 z_}SLr|DlOMu&=Z%t7`q%Dgt57X-!Bd7AgzmDfL@YKqrkh?%N0QQkP2aU()aN~6YrY4ke4;ucZp_hEc84*E?tT7yjX*1OI$ zQ#8DMbTeY0Lo|@C)ml=V);3{s^iM4B)Ha308&J9&HR<;ml_;)GnKj1C`1gS{2vMJz4@WPABROnBB-sxx7TPKnOO)!=bwY~jDPh%(DcBq)1T)#F8YBbsX=dU;=@u6Kg;j4DJ1xa2Ynd4O z+mW%H-i{rr!)MKi4^_r;>Kc5WHBylvBvkS;H!*PM*5%a|BhS{G%Tfl{me_}Q#eAf5 ze5_i`=tvXi4}8iHGa|9%s2}V0SEbc5H;FRJtNc3ld#1`97kBJ@zpgDUf^6sOBF22Y zBB-TBH+P(FXl}Yb^{OkzVHwr$arlAk@Uij9dQJVv$szAwpVqd8bDHCvD4jfJM~nw@ zp-Z*H>GhGWTZR>fZ*i#fL5r>R0qh?0NpIh1L~*s3M28~X4ZhZ8(q4yLIVq#wck_7#HR~=U z%#{7k-Tt6BE6UF}|1 zV#<3h;57q8Rd{4gnCV-aC3e-(5jP5K$BI_mYt4Qxc}|~KWBJxb~jwp{=hEJ^U5=De6v2$It&^w*)ZQXF87?zqxtwxbp^A$Zk>DvbqBI zh1X7WW!swT+Xq*TgIsLsmhUL50qI={MQ!DFDkFYl{$9szdQFPkION*ke<=Br%f%kI zzb+2FR8!@7)BBNi|8uEJR@zVU&(nJiLrL~U9+OtK%esoty;YuskY6b0J)lREo)eC{ zJl(e+|AJVND_Snx(Mir1_F3C}7Tef)GjZuuJS9fvvSa*uNakdbvwwY({hnR4!(_+O zkE-fOOLfhKpNGA;m1osFGB0Y0lRRmD>F(vsF`sV9Kh;))Q*7sz+dQ+rnjj4prMb}$ z+dwt>7GR992R^{{fjQ^&=6T}U-74Kw>dEG_owqaRt#5L_eN0?utrj0e+Mhn@l(ue7 z;9;>8Rz+$~_kqG)q{Ozq{p~$xoHEupw~hAG+ZDYof%Z2RwwTvVyb3aZ`F?B9g^ypJ zJ0Utyn;?#q<~AmOOrtcQ=SHz*C{;tuIsV7ouu>wC_-TZ9)19+jYc^I_zPR!82zZs) zZ8H!!eo%IQMbC|)*uu(yp$FvQP1(bDs!uV0Sfm*k{bY4fOWBH?eIpweF>9!g)630s z@>WI<^&h-dPP*dsFF~*-^MH_cM8hsgrX2~M9R6}3an~vI*|tC8v)05EHgyL)@;b&J zPxjldfJx~dnK_qT9<>K7&1PhsD0TQ>%9H0Ddu7v5cqLyad6sHioS9+gkRvbn@2bu% zgL|yI@BfzMxx_&bOyQ!e=AAta#tFc+!aTPS-Q4#3zc;s6(?`}Ni6vtnZ2NG*?$D?2 z4W5m&l5815wA}i#hQGJG-hVWXWkKj?1aSNW=f^yHN8{+4rjaOqmuw7xQ+D6G+!1*E$TV=Qu_%r;CZ z9DVV9lf(ScMI>VD#86STW2Ia9t|g66lB^jg_s;+A3UW_&sm~5*x|x5egTK(^9l!Ea qiQ#r$+>(aZK+DGSkp;b01MEuDv9?<8gF7t00aCvpQL%5t!T$q3?qw4I literal 0 HcmV?d00001 diff --git a/exporter/SynthesisFusionAddin/src/Resources/APS/64x64-normal.png b/exporter/SynthesisFusionAddin/src/Resources/APS/64x64-normal.png new file mode 100644 index 0000000000000000000000000000000000000000..3076b744ca20f1537e6a0475c423a89d5e862b07 GIT binary patch literal 3335 zcmaJ^c{o)28y{P?i^^7_rXfo;`!EZ#jeSWIk+m^1n3#R+l_aF-P9;jELYHW>lr`D1 zlr6%9Y+a%3U9@0+qvihYANRTEIq&(N_dTC?f8Re&qO+5=oQ#?b005A)wZRjFr|CLL zZ4f^5sH{QZA;KY8n*$!SsZRq5O>of7uyN9uSAn;J?XTd^55 z;P+zVsQ(fS5`p%|5RnM50U7BJMqw~yFcED410zWoval=^tiQfK=%+sUA4U8zw?c_9 zA?B~Ra4-WH(I18I2gChQ7%-NM@&jW~7$O+%hsL1b2o##)4+8_{xMZQ zCic5jII`;-1O0nt*15mejzSmi8e6zj$?D);0D#n-E#Ax}+-Evn&e^3`xlzCfJ6x5j zoD6)Klj*m|;A*bv-c0S$653IRS1uY|4!CZ$;6{F+VIMcIn|zV)f3K&H)#sFyUVokz z@N|w%}wW2R;X9_DACF9z3|H()B)lc>hpMW$o^LgfJ}nkfu??+ z&q~7$&8iER6j1>wG9z*bQeUO<77@M5vAAmhH@o**U55ogkXMM!M%eX=D5DtjG->7} zf3s_Az^Akz{SUI-9@(bugLO~4U?QXMzBcGxuJwHV+AfrFN_yC@buh7Ai+}A1=jmF$ zD=wjILFJS9$q#@lV|ilYyJ|DLZlB*O?@9(;eMOiQ&1p3iz`<4?B*eob6Qc8jIE%kGF{38pn?%ALnVzqNVzu@I6W7XyZZwtVP(Q3(_gvVtS)}+bwDamR1djLApl@$) zToj8v;C=C2URD;17Oj?~>=~{Q=9d781)gTvZ#M^-k_BFa?8fjAi;`;!^Eb?-I~La%u)Y}_oe(i-*ZP`hUahL{(5v*hi8M)E?c(P=zj^6SjGz_-gQ z@!U_XZ%bL+l-?dfQNl7WfhRTm=qm)CIjRGq4m6%ogWWjo%wlYFvQ`XkXn$%@@Hq85 z=t$3w5ikSvbqqLPAu#Jp$)B>@^d?%=8$9q_N_*#~YL~}iOYip#EF|wnNFVQ1)zo_^ z75j(l&b4OnRHx46mKrTB(b9UA*C}m(d|Y*$kxojPek|r|)yc23IsX2)v9mjPz0uu# z4f9Ll(wvj= z>%OStdMYg8>~airq`-liNboV=ZE7fQudntn_(=Cn7m1!5(aMMFq|2$Sx$RYn8U&k? zj@YtY3d@!!&tvwY`C!Ik!F;X}Mm%0NUytzDO0Hze#;lNbw#D|D3V_B1P}ayoWVudL zeBf$))OtDRQjNsD0h98Oh!xgDaG?M*Shp?Vu^RZR#Q@vdLx*>1*!m>Uxd7P^71ctE zdm)<^I0jbm0x_B_IwT&c?Hd7y6;rwA?k#-kuyqDjwkr-W8;NBfUwG$8RRNN7a%EQ1 zJ;Qh^c*VONiDz%t$8F??_}u@ZGHOlF(ru=GoQOH>TQ6+%ik|6C&uEq$dLVJaP%W|S z8$7Uqmfe2u`K_xPvYmD(7=(z-j+}pdJL%z$ChIu~po6R7VTlO^>izv5Nax(lUHPq( zF@Z?a=nsvrFD}EKY{YpdxYKVeT~=0Z+uOGImp7%h1-Gh}S?@E~0wiC{t1MseB`l)1 zgG<)NOkRdn`DOpeU5gm*%PtbZk8b37;g-2P{;L?l4+;5&~+SigMSoOWoy*eJ}1u#>x z?A`FR1_9C=U-dTe49V=Dhpc{fJETmS%C@wz-4WMv0XaWVYvMQm@Ei~~b;f>+U95ZJ zq(smcoof7rW3S*W=Go9Bb!6I`FYOQNPfxyd-w}fU?bDcJMBLu1;uFi$`jOsCOK#Q< zgW#_GtJSBe&raTQ9_H7Wj4bYIcCQHD($c!P1%r~@I@H`4^Y&SsO>9fku?^DAnlK<0 zgRpYI=Re+RDe!53HX&d3#e`DZenTp+RlwO{hadztUHaq-%W#eJ*7+trUlE+he+cl2jAK zxSlm_WHkmk5?fG=dPJ98UXhotP|j*>vp6&;dZpR7a+Vf)PqR^hmO0D5=xo1nYxf&f zu$W@8)aP))hLwm_rR)0&6b}_`Do=56h*}HT>l>!l=dSUwE3h<1(b7{-&uXWUxkYTW zqzdO6M&oWCCeD7$I&ki|jW~7YrM0s-&m4QI&87Qs7r!cE-%F8Mnn(3T-{5Ctql`uM zS?TM0R(U1aOV*WRiL%y9Rn1FSMU^LIWRR|CeZ98d+a8a5g8Z1xUfNTk$hH%O=i2C+ zh7V+91EQq$iu)(|OMkvdd!Axwcbo9`%RO~*O91J(J|u$?_5Ap&7qM4L!VP7j51RKb zU@+?U1tvY%zARIpQz>Fz{3_~pJNq>=j$!)6f!}!sT9Vzp?Zke=qB+OK8~P`x9w_~u zN3D}wXy2WefBcz-zf{Wfhd%J$*L+Mn>t>oR&bHdlZ_;Z-%ll3Pv&dfe^3hZUb@@FW zVC{v^c?Es#vExd-6B)%Jf0ca8o}G+*s!~W{9Vov2JUgcbePL`?(q8N@eZifzjI~hT zUdf>g)uK|Rt)2>C5s6zkt4P*R@7;(%Dc$M@?+39?Ix4)4%H$V{90jFn0f4$7m)v5I S-@N`eVr%Jye_(#()c*iIn!beq literal 0 HcmV?d00001 diff --git a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py index 4c540f5bd5..e2f8d60407 100644 --- a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py +++ b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py @@ -19,6 +19,7 @@ JointParentType, ) from .Configuration.SerialCommand import SerialCommand +from ..APS.APS import getAuth, getUserInfo import adsk.core, adsk.fusion, traceback, logging, os from types import SimpleNamespace @@ -940,6 +941,14 @@ def notify(self, args): enabled=True, ) + getAuth() + user_info = getUserInfo() + apsSettings = INPUTS_ROOT.addTabCommandInput( + "aps_settings", f"APS Settings ({user_info.given_name if user_info else 'Not Signed In'})" + ) + apsSettings.tooltip = "Configuration settings for Autodesk Platform Services." + aps_input = apsSettings.children + # clear all selections before instantiating handlers. gm.ui.activeSelections.clear() diff --git a/exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py b/exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py new file mode 100644 index 0000000000..da42703594 --- /dev/null +++ b/exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py @@ -0,0 +1,111 @@ +from src.general_imports import root_logger, gm, INTERNAL_ID, APP_NAME, DESCRIPTION, my_addin_path +from src.APS.APS import CLIENT_ID, convertAuthToken, getCodeChallenge +import time +import webbrowser +import logging +import urllib.parse +import urllib.request +import json +import adsk.core, traceback +import os +from src.APS.APS import auth_path + +class ShowAPSAuthCommandExecuteHandler(adsk.core.CommandEventHandler): + def __init__(self): + super().__init__() + def notify(self, args): + try: + logging.getLogger(f"{INTERNAL_ID}").info(auth_path) + logging.getLogger(f"{INTERNAL_ID}").info(os.path.abspath(auth_path)) + logging.getLogger(f"{INTERNAL_ID}").info(my_addin_path) + palette = gm.ui.palettes.itemById('authPalette') + if not palette: + callbackUrl = 'http://localhost:3003/api/aps/exporter/' + challenge = getCodeChallenge() + params = { + 'response_type': 'code', + 'client_id': CLIENT_ID, + 'redirect_uri': urllib.parse.quote_plus(callbackUrl), + 'scope': 'data:read', + 'nonce': time.time(), + 'prompt': 'login', + 'code_challenge': challenge, + 'code_challenge_method': 'S256' + } + query = "&".join(map(lambda pair: f"{pair[0]}={pair[1]}", params.items())) + url = 'https://developer.api.autodesk.com/authentication/v2/authorize?' + query + logging.getLogger(f"{INTERNAL_ID}").info(url) + palette = gm.ui.palettes.add('authPalette', 'APS Authentication', url, True, True, True, 400, 400) + palette.dockingState = adsk.core.PaletteDockingStates.PaletteDockStateRight + # register events + onHTMLEvent = MyHTMLEventHandler() + palette.incomingFromHTML.add(onHTMLEvent) + gm.handlers.append(onHTMLEvent) + + onClosed = MyCloseEventHandler() + palette.closed.add(onClosed) + gm.handlers.append(onClosed) + else: + palette.isVisible = True + except: + gm.ui.messageBox('Command executed failed: {}'.format(traceback.format_exc())) + logging.getLogger(f"{INTERNAL_ID}").error('Command executed failed: {}'.format(traceback.format_exc())) + +class ShowAPSAuthCommandCreatedHandler(adsk.core.CommandCreatedEventHandler): + def __init__(self, configure): + super().__init__() + def notify(self, args): + try: + command = args.command + onExecute = ShowAPSAuthCommandExecuteHandler() + command.execute.add(onExecute) + gm.handlers.append(onExecute) + except: + gm.ui.messageBox("Failed:\n{}".format(traceback.format_exc())) + logging.getLogger(f"{INTERNAL_ID}").error("Failed:\n{}".format(traceback.format_exc())) + +class SendInfoCommandExecuteHandler(adsk.core.CommandEventHandler): + def __init__(self): + super().__init__() + def notify(self, args): + pass + +class SendInfoCommandCreatedHandler(adsk.core.CommandCreatedEventHandler): + def __init__(self): + super().__init__() + def notify(self, args): + try: + command = args.command + onExecute = SendInfoCommandExecuteHandler() + command.execute.add(onExecute) + gm.handlers.append(onExecute) + except: + gm.ui.messageBox("Failed:\n{}".format(traceback.format_exc())) + logging.getLogger(f"{INTERNAL_ID}").error("Failed:\n{}".format(traceback.format_exc())) + +class MyCloseEventHandler(adsk.core.UserInterfaceGeneralEventHandler): + def __init__(self): + super().__init__() + def notify(self, args): + try: + logging.getLogger(f"{INTERNAL_ID}").info('Close button is clicked') + # gm.ui.messageBox('Close button is clicked') + except: + gm.ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) + logging.getLogger(f"{INTERNAL_ID}").error('Failed:\n{}'.format(traceback.format_exc())) + +class MyHTMLEventHandler(adsk.core.HTMLEventHandler): + def __init__(self): + super().__init__() + def notify(self, args): + try: + htmlArgs = adsk.core.HTMLEventArgs.cast(args) + data = json.loads(htmlArgs.data) + msg = "An event has been fired from the html to Fusion with the following data:\n" + msg += f" Command: {htmlArgs.action}\n data: {htmlArgs.data}\n code: {data['code']}" + logging.getLogger(f"{INTERNAL_ID}").info(msg) + # gm.ui.messageBox(msg) + convertAuthToken(data['code']) + except: + gm.ui.messageBox('Failed:\n'.format(traceback.format_exc())) + logging.getLogger(f"{INTERNAL_ID}").error('Failed:\n'.format(traceback.format_exc())) From e80a2ec81e0ff782ab60812407fcf031d3c9fb58 Mon Sep 17 00:00:00 2001 From: PepperLola Date: Thu, 27 Jun 2024 15:28:04 -0700 Subject: [PATCH 2/8] error handling on web request failures --- exporter/SynthesisFusionAddin/src/APS/APS.py | 133 ++++++++++++------ .../src/UI/ConfigCommand.py | 2 +- .../src/UI/ShowAPSAuthCommand.py | 6 +- 3 files changed, 97 insertions(+), 44 deletions(-) diff --git a/exporter/SynthesisFusionAddin/src/APS/APS.py b/exporter/SynthesisFusionAddin/src/APS/APS.py index 2f44454e53..699185a51f 100644 --- a/exporter/SynthesisFusionAddin/src/APS/APS.py +++ b/exporter/SynthesisFusionAddin/src/APS/APS.py @@ -2,14 +2,12 @@ import pickle from src.general_imports import root_logger, gm, INTERNAL_ID, APP_NAME, DESCRIPTION, my_addin_path import time -import webbrowser +import pathlib import logging import urllib.parse import urllib.request import json -from src.UI.OsHelper import getOSPath import os -import adsk.core, traceback CLIENT_ID = 'GCxaewcLjsYlK8ud7Ka9AKf9dPwMR3e4GlybyfhAK2zvl3tU' auth_path = os.path.abspath(os.path.join(my_addin_path, "..", ".aps_auth")) @@ -22,6 +20,7 @@ class APSAuth: access_token: str refresh_token: str expires_in: int + expires_at: int token_type: str @dataclass @@ -46,12 +45,16 @@ def getAPSAuth() -> APSAuth | None: def _res_json(res): return json.loads(res.read().decode(res.info().get_param('charset') or 'utf-8')) -def getCodeChallenge() -> str: +def getCodeChallenge() -> str | None: endpoint = 'http://localhost:3003/api/aps/challenge/' - res = urllib.request.urlopen(endpoint) - data = _res_json(res) - logging.getLogger(f"{INTERNAL_ID}").info(f"CHALLENGE: {data}") - return data["challenge"] + try: + res = urllib.request.urlopen(endpoint) + data = _res_json(res) + logging.getLogger(f"{INTERNAL_ID}").info(f"CHALLENGE: {data}") + return data["challenge"] + except urllib.request.HTTPError as e: + logging.getLogger(f"{INTERNAL_ID}").error(f"Code Challenge Error:\n{e.code} - {e.reason}") + gm.ui.messageBox("There was an error reaching the Synthesis servers.") def getAuth() -> APSAuth: global APS_AUTH @@ -65,10 +68,14 @@ def getAuth() -> APSAuth: access_token=p["access_token"], refresh_token=p["refresh_token"], expires_in=p["expires_in"], + expires_at=int(p["expires_in"]*1000), token_type=p["token_type"] ) except: raise Exception("Need to sign in!") + curr_time = int(time.time() * 1000) + if curr_time >= APS_AUTH.expires_at: + refreshAuthToken() if APS_USER_INFO is None: loadUserInfo() return APS_AUTH @@ -76,20 +83,61 @@ def getAuth() -> APSAuth: def convertAuthToken(code: str): global APS_AUTH authUrl = f'http://localhost:3003/api/aps/code/?code={code}&redirect_uri={urllib.parse.quote_plus("http://localhost:3003/api/aps/exporter/")}' - res = urllib.request.urlopen(authUrl) - data = _res_json(res)['response'] - logging.getLogger(f"{INTERNAL_ID}").info(f"AUTH TOKEN: {data}") - APS_AUTH = APSAuth( - access_token=data["access_token"], - refresh_token=data["refresh_token"], - expires_in=data["expires_in"], - token_type=data["token_type"] - ) - with open(auth_path, 'wb') as f: - pickle.dump(data, f) - f.close() + try: + res = urllib.request.urlopen(authUrl) + data = _res_json(res)['response'] + logging.getLogger(f"{INTERNAL_ID}").info(f"AUTH TOKEN: {data}") + APS_AUTH = APSAuth( + access_token=data["access_token"], + refresh_token=data["refresh_token"], + expires_in=data["expires_in"], + expires_at=int(data["expires_in"]*1000), + token_type=data["token_type"] + ) + with open(auth_path, 'wb') as f: + pickle.dump(data, f) + f.close() - loadUserInfo() + loadUserInfo() + except urllib.request.HTTPError as e: + removeAuth() + logging.getLogger(f"{INTERNAL_ID}").error(f"Auth Token Error:\n{e.code} - {e.reason}") + gm.ui.messageBox("There was an error signing in. Please retry.") + +def removeAuth(): + global APS_AUTH, APS_USER_INFO + APS_AUTH = None + APS_USER_INFO = None + pathlib.Path.unlink(pathlib.Path(auth_path)) + +def refreshAuthToken(): + global APS_AUTH + if APS_AUTH is None or APS_AUTH.refresh_token is None: + raise Exception("No refresh token found.") + body = urllib.parse.urlencode({ + 'client_id': CLIENT_ID, + 'grant_type': 'refresh_token', + 'refresh_token': APS_AUTH.refresh_token, + 'scope': 'data:read' + }).encode('utf-8') + req = urllib.request.Request('https://developer.api.autodesk.com/authentication/v2/token', data=body) + req.method = 'POST' + req.add_header(key='Content-Type', val='application/x-www-form-urlencoded') + try: + res = urllib.request.urlopen(req) + data = _res_json(res) + logging.getLogger(f"{INTERNAL_ID}").info(f"REFRESH TOKEN: {data}") + APS_AUTH = APSAuth( + access_token=data["access_token"], + refresh_token=data["refresh_token"], + expires_in=data["expires_in"], + expires_at=int(data["expires_in"]*1000), + token_type=data["token_type"] + ) + except urllib.request.HTTPError as e: + removeAuth() + logging.getLogger(f"{INTERNAL_ID}").error(f"Refresh Error:\n{e.code} - {e.reason}") + gm.ui.messageBox("Please sign in again.") def loadUserInfo() -> APSUserInfo | None: global APS_AUTH @@ -98,25 +146,30 @@ def loadUserInfo() -> APSUserInfo | None: global APS_USER_INFO req = urllib.request.Request("https://api.userprofile.autodesk.com/userinfo") req.add_header(key="Authorization", val=APS_AUTH.access_token) - res = urllib.request.urlopen(req) - data = _res_json(res) - logging.getLogger(f"{INTERNAL_ID}").info(f"USER INFO: {data}") - APS_USER_INFO = APSUserInfo( - name=data["name"], - given_name=data["given_name"], - family_name=data["family_name"], - preferred_username=data["preferred_username"], - email=data["email"], - email_verified=data["email_verified"], - profile=data["profile"], - locale=data["locale"], - country_code=data["country_code"], - about_me=data["about_me"], - language=data["language"], - company=data["company"], - picture=data["picture"] - ) - return APS_USER_INFO + try: + res = urllib.request.urlopen(req) + data = _res_json(res) + logging.getLogger(f"{INTERNAL_ID}").info(f"USER INFO: {data}") + APS_USER_INFO = APSUserInfo( + name=data["name"], + given_name=data["given_name"], + family_name=data["family_name"], + preferred_username=data["preferred_username"], + email=data["email"], + email_verified=data["email_verified"], + profile=data["profile"], + locale=data["locale"], + country_code=data["country_code"], + about_me=data["about_me"], + language=data["language"], + company=data["company"], + picture=data["picture"] + ) + return APS_USER_INFO + except urllib.request.HTTPError as e: + removeAuth() + logging.getLogger(f"{INTERNAL_ID}").error(f"User Info Error:\n{e.code} - {e.reason}") + gm.ui.messageBox("Please sign in again.") def getUserInfo() -> APSUserInfo | None: if APS_USER_INFO is not None: diff --git a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py index e2f8d60407..8fb21c665b 100644 --- a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py +++ b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py @@ -19,7 +19,7 @@ JointParentType, ) from .Configuration.SerialCommand import SerialCommand -from ..APS.APS import getAuth, getUserInfo +from ..APS.APS import getAuth, getUserInfo, refreshAuthToken import adsk.core, adsk.fusion, traceback, logging, os from types import SimpleNamespace diff --git a/exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py b/exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py index da42703594..a44fb5f5a3 100644 --- a/exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py +++ b/exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py @@ -15,13 +15,13 @@ def __init__(self): super().__init__() def notify(self, args): try: - logging.getLogger(f"{INTERNAL_ID}").info(auth_path) - logging.getLogger(f"{INTERNAL_ID}").info(os.path.abspath(auth_path)) - logging.getLogger(f"{INTERNAL_ID}").info(my_addin_path) palette = gm.ui.palettes.itemById('authPalette') if not palette: callbackUrl = 'http://localhost:3003/api/aps/exporter/' challenge = getCodeChallenge() + if challenge is None: + logging.getLogger(f"{INTERNAL_ID}").error("Code challenge is None when attempting to authorize for APS.") + return params = { 'response_type': 'code', 'client_id': CLIENT_ID, From 7c4d8d278bc721ca6a75fb059360d89fc3da4665 Mon Sep 17 00:00:00 2001 From: BrandonPacewic Date: Fri, 28 Jun 2024 10:02:54 -0700 Subject: [PATCH 3/8] Corrected imports --- exporter/SynthesisFusionAddin/Synthesis.py | 3 +++ exporter/SynthesisFusionAddin/src/APS/APS.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/exporter/SynthesisFusionAddin/Synthesis.py b/exporter/SynthesisFusionAddin/Synthesis.py index 6e7dd95178..37403d9f82 100644 --- a/exporter/SynthesisFusionAddin/Synthesis.py +++ b/exporter/SynthesisFusionAddin/Synthesis.py @@ -1,3 +1,6 @@ +import logging +import os +import traceback from shutil import rmtree import adsk.core diff --git a/exporter/SynthesisFusionAddin/src/APS/APS.py b/exporter/SynthesisFusionAddin/src/APS/APS.py index 0167297599..d27715dd42 100644 --- a/exporter/SynthesisFusionAddin/src/APS/APS.py +++ b/exporter/SynthesisFusionAddin/src/APS/APS.py @@ -8,7 +8,7 @@ import urllib.request from dataclasses import dataclass -from src.general_imports import ( +from ..general_imports import ( APP_NAME, DESCRIPTION, INTERNAL_ID, From 076e4aac60955c08c792c743cad6c54e918826b5 Mon Sep 17 00:00:00 2001 From: PepperLola Date: Wed, 3 Jul 2024 14:42:24 -0700 Subject: [PATCH 4/8] properly delete palette on close and login delete unnecessary logs with sensitive data only handle errors in the palette so it can delete/reset itself on error --- exporter/SynthesisFusionAddin/src/APS/APS.py | 46 +++++++------------ .../src/UI/ShowAPSAuthCommand.py | 21 +++++++-- 2 files changed, 32 insertions(+), 35 deletions(-) diff --git a/exporter/SynthesisFusionAddin/src/APS/APS.py b/exporter/SynthesisFusionAddin/src/APS/APS.py index 699185a51f..8b9940431a 100644 --- a/exporter/SynthesisFusionAddin/src/APS/APS.py +++ b/exporter/SynthesisFusionAddin/src/APS/APS.py @@ -47,14 +47,9 @@ def _res_json(res): def getCodeChallenge() -> str | None: endpoint = 'http://localhost:3003/api/aps/challenge/' - try: - res = urllib.request.urlopen(endpoint) - data = _res_json(res) - logging.getLogger(f"{INTERNAL_ID}").info(f"CHALLENGE: {data}") - return data["challenge"] - except urllib.request.HTTPError as e: - logging.getLogger(f"{INTERNAL_ID}").error(f"Code Challenge Error:\n{e.code} - {e.reason}") - gm.ui.messageBox("There was an error reaching the Synthesis servers.") + res = urllib.request.urlopen(endpoint) + data = _res_json(res) + return data["challenge"] def getAuth() -> APSAuth: global APS_AUTH @@ -63,7 +58,6 @@ def getAuth() -> APSAuth: try: with open(auth_path, 'rb') as f: p = pickle.load(f) - logging.getLogger(f"{INTERNAL_ID}").info(f"LOADING PICKLED FILE: {p}") APS_AUTH = APSAuth( access_token=p["access_token"], refresh_token=p["refresh_token"], @@ -83,26 +77,20 @@ def getAuth() -> APSAuth: def convertAuthToken(code: str): global APS_AUTH authUrl = f'http://localhost:3003/api/aps/code/?code={code}&redirect_uri={urllib.parse.quote_plus("http://localhost:3003/api/aps/exporter/")}' - try: - res = urllib.request.urlopen(authUrl) - data = _res_json(res)['response'] - logging.getLogger(f"{INTERNAL_ID}").info(f"AUTH TOKEN: {data}") - APS_AUTH = APSAuth( - access_token=data["access_token"], - refresh_token=data["refresh_token"], - expires_in=data["expires_in"], - expires_at=int(data["expires_in"]*1000), - token_type=data["token_type"] - ) - with open(auth_path, 'wb') as f: - pickle.dump(data, f) - f.close() + res = urllib.request.urlopen(authUrl) + data = _res_json(res)['response'] + APS_AUTH = APSAuth( + access_token=data["access_token"], + refresh_token=data["refresh_token"], + expires_in=data["expires_in"], + expires_at=int(data["expires_in"]*1000), + token_type=data["token_type"] + ) + with open(auth_path, 'wb') as f: + pickle.dump(data, f) + f.close() - loadUserInfo() - except urllib.request.HTTPError as e: - removeAuth() - logging.getLogger(f"{INTERNAL_ID}").error(f"Auth Token Error:\n{e.code} - {e.reason}") - gm.ui.messageBox("There was an error signing in. Please retry.") + loadUserInfo() def removeAuth(): global APS_AUTH, APS_USER_INFO @@ -126,7 +114,6 @@ def refreshAuthToken(): try: res = urllib.request.urlopen(req) data = _res_json(res) - logging.getLogger(f"{INTERNAL_ID}").info(f"REFRESH TOKEN: {data}") APS_AUTH = APSAuth( access_token=data["access_token"], refresh_token=data["refresh_token"], @@ -149,7 +136,6 @@ def loadUserInfo() -> APSUserInfo | None: try: res = urllib.request.urlopen(req) data = _res_json(res) - logging.getLogger(f"{INTERNAL_ID}").info(f"USER INFO: {data}") APS_USER_INFO = APSUserInfo( name=data["name"], given_name=data["given_name"], diff --git a/exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py b/exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py index a44fb5f5a3..bf98e7ca00 100644 --- a/exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py +++ b/exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py @@ -10,11 +10,14 @@ import os from src.APS.APS import auth_path +palette = None + class ShowAPSAuthCommandExecuteHandler(adsk.core.CommandEventHandler): def __init__(self): super().__init__() def notify(self, args): try: + global palette palette = gm.ui.palettes.itemById('authPalette') if not palette: callbackUrl = 'http://localhost:3003/api/aps/exporter/' @@ -34,7 +37,6 @@ def notify(self, args): } query = "&".join(map(lambda pair: f"{pair[0]}={pair[1]}", params.items())) url = 'https://developer.api.autodesk.com/authentication/v2/authorize?' + query - logging.getLogger(f"{INTERNAL_ID}").info(url) palette = gm.ui.palettes.add('authPalette', 'APS Authentication', url, True, True, True, 400, 400) palette.dockingState = adsk.core.PaletteDockingStates.PaletteDockStateRight # register events @@ -50,6 +52,8 @@ def notify(self, args): except: gm.ui.messageBox('Command executed failed: {}'.format(traceback.format_exc())) logging.getLogger(f"{INTERNAL_ID}").error('Command executed failed: {}'.format(traceback.format_exc())) + if palette: + palette.deleteMe() class ShowAPSAuthCommandCreatedHandler(adsk.core.CommandCreatedEventHandler): def __init__(self, configure): @@ -63,6 +67,8 @@ def notify(self, args): except: gm.ui.messageBox("Failed:\n{}".format(traceback.format_exc())) logging.getLogger(f"{INTERNAL_ID}").error("Failed:\n{}".format(traceback.format_exc())) + if palette: + palette.deleteMe() class SendInfoCommandExecuteHandler(adsk.core.CommandEventHandler): def __init__(self): @@ -82,17 +88,22 @@ def notify(self, args): except: gm.ui.messageBox("Failed:\n{}".format(traceback.format_exc())) logging.getLogger(f"{INTERNAL_ID}").error("Failed:\n{}".format(traceback.format_exc())) + if palette: + palette.deleteMe() class MyCloseEventHandler(adsk.core.UserInterfaceGeneralEventHandler): def __init__(self): super().__init__() def notify(self, args): try: - logging.getLogger(f"{INTERNAL_ID}").info('Close button is clicked') + if palette: + palette.deleteMe() # gm.ui.messageBox('Close button is clicked') except: gm.ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) logging.getLogger(f"{INTERNAL_ID}").error('Failed:\n{}'.format(traceback.format_exc())) + if palette: + palette.deleteMe() class MyHTMLEventHandler(adsk.core.HTMLEventHandler): def __init__(self): @@ -101,11 +112,11 @@ def notify(self, args): try: htmlArgs = adsk.core.HTMLEventArgs.cast(args) data = json.loads(htmlArgs.data) - msg = "An event has been fired from the html to Fusion with the following data:\n" - msg += f" Command: {htmlArgs.action}\n data: {htmlArgs.data}\n code: {data['code']}" - logging.getLogger(f"{INTERNAL_ID}").info(msg) # gm.ui.messageBox(msg) + convertAuthToken(data['code']) except: gm.ui.messageBox('Failed:\n'.format(traceback.format_exc())) logging.getLogger(f"{INTERNAL_ID}").error('Failed:\n'.format(traceback.format_exc())) + if palette: + palette.deleteMe() From 8503dbe7e6a73f3129b8c112a46d0c1e383f608f Mon Sep 17 00:00:00 2001 From: PepperLola Date: Mon, 8 Jul 2024 16:28:16 -0700 Subject: [PATCH 5/8] ran formatter --- exporter/SynthesisFusionAddin/src/APS/APS.py | 10 ++++---- .../src/Analytics/poster.py | 6 ++--- .../src/Analyzer/timer.py | 8 +------ .../src/UI/ConfigCommand.py | 4 +++- .../src/UI/ShowAPSAuthCommand.py | 24 ++++++++++--------- 5 files changed, 25 insertions(+), 27 deletions(-) diff --git a/exporter/SynthesisFusionAddin/src/APS/APS.py b/exporter/SynthesisFusionAddin/src/APS/APS.py index 393e75cecf..8c72d7623c 100644 --- a/exporter/SynthesisFusionAddin/src/APS/APS.py +++ b/exporter/SynthesisFusionAddin/src/APS/APS.py @@ -59,7 +59,7 @@ def _res_json(res): def getCodeChallenge() -> str | None: - endpoint = 'http://localhost:3003/api/aps/challenge/' + endpoint = "http://localhost:3003/api/aps/challenge/" res = urllib.request.urlopen(endpoint) data = _res_json(res) return data["challenge"] @@ -93,15 +93,15 @@ def convertAuthToken(code: str): global APS_AUTH authUrl = f'http://localhost:3003/api/aps/code/?code={code}&redirect_uri={urllib.parse.quote_plus("http://localhost:3003/api/aps/exporter/")}' res = urllib.request.urlopen(authUrl) - data = _res_json(res)['response'] + data = _res_json(res)["response"] APS_AUTH = APSAuth( access_token=data["access_token"], refresh_token=data["refresh_token"], expires_in=data["expires_in"], - expires_at=int(data["expires_in"]*1000), - token_type=data["token_type"] + expires_at=int(data["expires_in"] * 1000), + token_type=data["token_type"], ) - with open(auth_path, 'wb') as f: + with open(auth_path, "wb") as f: pickle.dump(data, f) f.close() diff --git a/exporter/SynthesisFusionAddin/src/Analytics/poster.py b/exporter/SynthesisFusionAddin/src/Analytics/poster.py index 1c5056ff72..096701fb20 100644 --- a/exporter/SynthesisFusionAddin/src/Analytics/poster.py +++ b/exporter/SynthesisFusionAddin/src/Analytics/poster.py @@ -161,9 +161,9 @@ def __send(self, body) -> bool: try: # define user agent so this works headers = {} - headers["User-Agent"] = ( - "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.27 Safari/537.17" - ) + headers[ + "User-Agent" + ] = "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.27 Safari/537.17" # print(f"{self.url}/collect?{body}") diff --git a/exporter/SynthesisFusionAddin/src/Analyzer/timer.py b/exporter/SynthesisFusionAddin/src/Analyzer/timer.py index 41bb86135f..5395a45637 100644 --- a/exporter/SynthesisFusionAddin/src/Analyzer/timer.py +++ b/exporter/SynthesisFusionAddin/src/Analyzer/timer.py @@ -12,13 +12,7 @@ class Timer: def __init__(self): self.logger = logging.getLogger(f"{INTERNAL_ID}.Analyzer.Timer") - ( - self.filename, - self.line_number, - self.funcName, - self.lines, - _, - ) = inspect.getframeinfo( + (self.filename, self.line_number, self.funcName, self.lines, _,) = inspect.getframeinfo( inspect.currentframe().f_back.f_back ) # func name doesn't always work here diff --git a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py index febb617ec4..38e7298cba 100644 --- a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py +++ b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py @@ -280,7 +280,9 @@ def notify(self, args): ) weight_unit.listItems.add("‎", imperialUnits, IconPaths.massIcons["LBS"]) # add listdropdown mass options - weight_unit.listItems.add("‎", not imperialUnits, IconPaths.massIcons["KG"]) # add listdropdown mass options + weight_unit.listItems.add( + "‎", not imperialUnits, IconPaths.massIcons["KG"] + ) # add listdropdown mass options weight_unit.tooltip = "Unit of mass" weight_unit.tooltipDescription = "
Configure the unit of mass for the weight calculation." diff --git a/exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py b/exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py index c426403ece..7c28b8e1e9 100644 --- a/exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py +++ b/exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py @@ -19,9 +19,9 @@ root_logger, ) - palette = None + class ShowAPSAuthCommandExecuteHandler(adsk.core.CommandEventHandler): def __init__(self): super().__init__() @@ -29,7 +29,7 @@ def __init__(self): def notify(self, args): try: global palette - palette = gm.ui.palettes.itemById('authPalette') + palette = gm.ui.palettes.itemById("authPalette") if not palette: callbackUrl = "http://localhost:3003/api/aps/exporter/" challenge = getCodeChallenge() @@ -49,8 +49,8 @@ def notify(self, args): "code_challenge_method": "S256", } query = "&".join(map(lambda pair: f"{pair[0]}={pair[1]}", params.items())) - url = 'https://developer.api.autodesk.com/authentication/v2/authorize?' + query - palette = gm.ui.palettes.add('authPalette', 'APS Authentication', url, True, True, True, 400, 400) + url = "https://developer.api.autodesk.com/authentication/v2/authorize?" + query + palette = gm.ui.palettes.add("authPalette", "APS Authentication", url, True, True, True, 400, 400) palette.dockingState = adsk.core.PaletteDockingStates.PaletteDockStateRight # register events onHTMLEvent = MyHTMLEventHandler() @@ -63,11 +63,12 @@ def notify(self, args): else: palette.isVisible = True except: - gm.ui.messageBox('Command executed failed: {}'.format(traceback.format_exc())) - logging.getLogger(f"{INTERNAL_ID}").error('Command executed failed: {}'.format(traceback.format_exc())) + gm.ui.messageBox("Command executed failed: {}".format(traceback.format_exc())) + logging.getLogger(f"{INTERNAL_ID}").error("Command executed failed: {}".format(traceback.format_exc())) if palette: palette.deleteMe() + class ShowAPSAuthCommandCreatedHandler(adsk.core.CommandCreatedEventHandler): def __init__(self, configure): super().__init__() @@ -120,11 +121,12 @@ def notify(self, args): palette.deleteMe() # gm.ui.messageBox('Close button is clicked') except: - gm.ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) - logging.getLogger(f"{INTERNAL_ID}").error('Failed:\n{}'.format(traceback.format_exc())) + gm.ui.messageBox("Failed:\n{}".format(traceback.format_exc())) + logging.getLogger(f"{INTERNAL_ID}").error("Failed:\n{}".format(traceback.format_exc())) if palette: palette.deleteMe() + class MyHTMLEventHandler(adsk.core.HTMLEventHandler): def __init__(self): super().__init__() @@ -135,9 +137,9 @@ def notify(self, args): data = json.loads(htmlArgs.data) # gm.ui.messageBox(msg) - convertAuthToken(data['code']) + convertAuthToken(data["code"]) except: - gm.ui.messageBox('Failed:\n'.format(traceback.format_exc())) - logging.getLogger(f"{INTERNAL_ID}").error('Failed:\n'.format(traceback.format_exc())) + gm.ui.messageBox("Failed:\n".format(traceback.format_exc())) + logging.getLogger(f"{INTERNAL_ID}").error("Failed:\n".format(traceback.format_exc())) if palette: palette.deleteMe() From 8769d60cf0f6e26dc574a8d58173eea27a536447 Mon Sep 17 00:00:00 2001 From: PepperLola Date: Mon, 8 Jul 2024 16:31:23 -0700 Subject: [PATCH 6/8] reinstalled black and reformatted --- exporter/SynthesisFusionAddin/src/Analytics/poster.py | 6 +++--- exporter/SynthesisFusionAddin/src/Analyzer/timer.py | 8 +++++++- exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py | 4 +--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/exporter/SynthesisFusionAddin/src/Analytics/poster.py b/exporter/SynthesisFusionAddin/src/Analytics/poster.py index 096701fb20..1c5056ff72 100644 --- a/exporter/SynthesisFusionAddin/src/Analytics/poster.py +++ b/exporter/SynthesisFusionAddin/src/Analytics/poster.py @@ -161,9 +161,9 @@ def __send(self, body) -> bool: try: # define user agent so this works headers = {} - headers[ - "User-Agent" - ] = "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.27 Safari/537.17" + headers["User-Agent"] = ( + "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.27 Safari/537.17" + ) # print(f"{self.url}/collect?{body}") diff --git a/exporter/SynthesisFusionAddin/src/Analyzer/timer.py b/exporter/SynthesisFusionAddin/src/Analyzer/timer.py index 5395a45637..41bb86135f 100644 --- a/exporter/SynthesisFusionAddin/src/Analyzer/timer.py +++ b/exporter/SynthesisFusionAddin/src/Analyzer/timer.py @@ -12,7 +12,13 @@ class Timer: def __init__(self): self.logger = logging.getLogger(f"{INTERNAL_ID}.Analyzer.Timer") - (self.filename, self.line_number, self.funcName, self.lines, _,) = inspect.getframeinfo( + ( + self.filename, + self.line_number, + self.funcName, + self.lines, + _, + ) = inspect.getframeinfo( inspect.currentframe().f_back.f_back ) # func name doesn't always work here diff --git a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py index 38e7298cba..febb617ec4 100644 --- a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py +++ b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py @@ -280,9 +280,7 @@ def notify(self, args): ) weight_unit.listItems.add("‎", imperialUnits, IconPaths.massIcons["LBS"]) # add listdropdown mass options - weight_unit.listItems.add( - "‎", not imperialUnits, IconPaths.massIcons["KG"] - ) # add listdropdown mass options + weight_unit.listItems.add("‎", not imperialUnits, IconPaths.massIcons["KG"]) # add listdropdown mass options weight_unit.tooltip = "Unit of mass" weight_unit.tooltipDescription = "
Configure the unit of mass for the weight calculation." From 64412df2c6e43f951137ce9f687a7466cc8e497a Mon Sep 17 00:00:00 2001 From: Hunter Barclay Date: Tue, 9 Jul 2024 12:34:27 -0600 Subject: [PATCH 7/8] Updated ports --- exporter/SynthesisFusionAddin/src/APS/APS.py | 5 ++--- exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/exporter/SynthesisFusionAddin/src/APS/APS.py b/exporter/SynthesisFusionAddin/src/APS/APS.py index 8c72d7623c..a501c854d5 100644 --- a/exporter/SynthesisFusionAddin/src/APS/APS.py +++ b/exporter/SynthesisFusionAddin/src/APS/APS.py @@ -23,7 +23,6 @@ APS_AUTH = None APS_USER_INFO = None - @dataclass class APSAuth: access_token: str @@ -59,7 +58,7 @@ def _res_json(res): def getCodeChallenge() -> str | None: - endpoint = "http://localhost:3003/api/aps/challenge/" + endpoint = "http://localhost:80/api/aps/challenge/" res = urllib.request.urlopen(endpoint) data = _res_json(res) return data["challenge"] @@ -91,7 +90,7 @@ def getAuth() -> APSAuth: def convertAuthToken(code: str): global APS_AUTH - authUrl = f'http://localhost:3003/api/aps/code/?code={code}&redirect_uri={urllib.parse.quote_plus("http://localhost:3003/api/aps/exporter/")}' + authUrl = f'http://localhost:80/api/aps/code/?code={code}&redirect_uri={urllib.parse.quote_plus("http://localhost:80/api/aps/exporter/")}' res = urllib.request.urlopen(authUrl) data = _res_json(res)["response"] APS_AUTH = APSAuth( diff --git a/exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py b/exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py index 7c28b8e1e9..bacc6e094b 100644 --- a/exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py +++ b/exporter/SynthesisFusionAddin/src/UI/ShowAPSAuthCommand.py @@ -31,7 +31,7 @@ def notify(self, args): global palette palette = gm.ui.palettes.itemById("authPalette") if not palette: - callbackUrl = "http://localhost:3003/api/aps/exporter/" + callbackUrl = "http://localhost:80/api/aps/exporter/" challenge = getCodeChallenge() if challenge is None: logging.getLogger(f"{INTERNAL_ID}").error( From af127b2652eec87c8c098d39726c991051708da0 Mon Sep 17 00:00:00 2001 From: Hunter Barclay Date: Tue, 9 Jul 2024 12:37:52 -0600 Subject: [PATCH 8/8] Format --- exporter/SynthesisFusionAddin/src/APS/APS.py | 1 + 1 file changed, 1 insertion(+) diff --git a/exporter/SynthesisFusionAddin/src/APS/APS.py b/exporter/SynthesisFusionAddin/src/APS/APS.py index a501c854d5..e31964e561 100644 --- a/exporter/SynthesisFusionAddin/src/APS/APS.py +++ b/exporter/SynthesisFusionAddin/src/APS/APS.py @@ -23,6 +23,7 @@ APS_AUTH = None APS_USER_INFO = None + @dataclass class APSAuth: access_token: str