From 5282a3b18e314370655be2871f84ab6a97735861 Mon Sep 17 00:00:00 2001 From: Henrik Loeser Date: Fri, 5 Feb 2021 12:15:57 +0100 Subject: [PATCH] rough steps --- README.md | 56 +++++++++++++++------------- backend/.env.template | 6 +++ serverless-github-traffic-stats.png | Bin 0 -> 29184 bytes 3 files changed, 37 insertions(+), 25 deletions(-) create mode 100644 backend/.env.template create mode 100644 serverless-github-traffic-stats.png diff --git a/README.md b/README.md index be5a1c1..314749e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,34 @@ -# Github Traffic Analytics: Combining serverless and Cloud Foundry for data retrieval and analytics -This repository contains the code for an [IBM Cloud solution tutorial](https://cloud.ibm.com/docs/solution-tutorials?topic=solution-tutorials-serverless-github-traffic-analytics). In the tutorial, we create an application to automatically collect Github traffic statistics for repositories and provide the foundation for traffic analytics. Github only provides access to the traffic data for the last 14 days. If you want to analyze statistics over a longer period of time, you need to download and store that data yourself. The app and the serverless action discussed in this tutorial implement a multi-tenant-ready solution to manage repositories, automatically collect traffic data on a daily or weekly schedule, and to view and analyze the collected data. +# Github Traffic Analytics: Combining serverless web app and eventing +This repository contains code based on this [IBM Cloud solution tutorial](https://cloud.ibm.com/docs/solution-tutorials?topic=solution-tutorials-serverless-github-traffic-analytics). In the tutorial, we create an application to automatically collect Github traffic statistics for repositories and provide the foundation for traffic analytics. Github only provides access to the traffic data for the last 14 days. If you want to analyze statistics over a longer period of time, you need to download and store that data yourself. The app and the serverless action discussed in the tutorial implement a multi-tenant-ready solution to manage repositories, automatically collect traffic data on a daily or weekly schedule, and to view and analyze the collected data. -Related blogs: +The original solution was based on Cloud Foundry for the web app and Cloud Functions for the automatic, serverless collection of traffic data. The code has been modified into a single Python app. It runs as dockerized container within IBM Cloud Code Engine. The app can be accessed via its web interface as regular web app. Moreover, utilizing eventing, the app receives signals (time events) to automatically collect GitHub traffic data. If not active, IBM Cloud Code Engine automatically scales down the app. + +![Architecture diagram](serverless-github-traffic-stats.png) + +Flow: +1. User accesses the Python (web) app running in IBM Cloud Code Engine. This is to set up the managed GitHub repositories or to access the collected traffic data. +2. The app interacts with the IBM AppID service for authentication and user management and Db2 as database for user and GitHub traffic data. +3. A Code Engine ping subscription based contacts the Python app to signal a time event. +4. Based on the signaled event (3) or directed by an administrator (user), the Python app retrieves GitHub traffic data. + +## Rough steps (for now) +Many of the Code Engine-related steps can be performed in the UI (console), all using the CLI + +- cd into the backend directory +- create a Code Engine project +- create an AppID and Db2 service instance (lite plan is ok) +- build the Docker image, e.g. using `ibmcloud cr build` +- register the container registry in Code Engine +- copy [.env.template](backend/.env.template) to .env. Edit it, remove unused lines. The minimum to change is FULL_HOSTNAME and EVENT_TOKEN. See below on service binding +- Create a Code Engine secret from the .env file, e.g., `ibmcloud ce secret create -name SERCRETNAME --from-env-file .env` +- deploy the app (Docker container) to Code Engine, use the secret from before, e.g., `ibmcloud ce app create --name APPNAME --image us.icr.io/namespace/imagename:latest --env-from-secret SECRETNAME --rs REGISTRY_CREDS`. +- either bind AppID and Db2 services to the app or pass credentials in the secret (see above) +- access the app similar to described in the tutorial, initialize the app and also set the redirect URI in AppID +- add some repos in the app +- set up daily collection of traffic data by creating a subscription, e.g, `ibmcloud ce subscription ping create --name dailyStats --destination APPNAME --path /collectStats --schedule '0 6 * * *' --data '{"token":"value_from_EVENT_TOKEN"}'` + + +## Related blogs * [Tutorial: GitHub Traffic Analytics with Cloud Functions and Cloud Foundry](https://www.ibm.com/blogs/bluemix/2018/04/tutorial-github-traffic-analytics/) * [Use Db2 and IBM Cloud to analyze GitHub traffic data (tutorial)](http://blog.4loeser.net/2018/04/use-db2-and-ibm-cloud-to-analyze-github.html) * [Automated, regular database jobs with IBM Cloud Functions (and Db2)](http://blog.4loeser.net/2018/04/automated-regular-database-jobs-with.html) @@ -9,41 +36,20 @@ Related blogs: * [How to pack serverless Python actions](http://blog.4loeser.net/2018/05/how-to-pack-serverless-python-actions.html) -# Files in this repository +## Files in this repository The files in this repository have the following structure: * [backend](backend): Has the code for the Python-based server app, using the Flask framework -* [functions](functions): Code for IBM Cloud Functions which is used for the automatic collection of the Github traffic data * [slack](slack): Automated weekly reporting of GitHub statistics into Slack channels (not part of the tutorial) -Adapt the service names in [servicenames.sh](/servicenames.sh) and [/backend/manifest.yml](/backend/manifest.yml) to your environment or preferred names for deployment. Important files in the **backend** directory: * [ghstats.py](backend/ghstats.py): Flask app to manage repositories and their traffic data * [database.sql](backend/database.sql): SQL script (for Db2) which is read and executed by the app during initialization. -* [manifest.yml](backend/manifest.yml): Manifest file to simplify app deployment, contains service bindings -* [requirements.in](backend/requirements.in): Input file for automatically generating the requirements file using **pip-compile** -* [config.json.sample](backend/config.json.sample): Sample configuration file for testing the app locally. The service credentials can be taken from the service keys or obtain in the IBM Cloud console. -* [dashboard.json](backend/dashboard.json): Canned (pre-created) dashboard to use with the IBM Dynamic Dashboard Embedded service. It is adapted at runtime. - -Important files in the **functions** directory: -* [__main__.py](functions/__main__.py): Code for Cloud Functions action, written in Python, uses the Github v3 API -* ghstats.zip: Zip archive with the action code and the githubpy module included. The zip archive is used to create the action. The database schema, defined in [database.sql](backend/database.sql), can be graphically represented as (source in [Graphviz DOT notation](https://www.graphviz.org/documentation/) in [dbschema.gv.txt](dbschema.gv.txt)): ![](dbschema.png) -# Create or change embedded dashboards -An additional dashboard specification is available in file [dashboard2.json](backend/dashboard2.json). It allows to render charts like shown in the two screenshots below. Either copy the file to `dashboard.json` and use it instead of the default dashboard or modify the code to select from given dashboards. -Once you created a dashboard on your own, you can export the specification and copy it into a new file, similar to `dashboard2.json`. The app code automatically replaces the data source URL and the authentication token. -![](screenshots/EmbeddedDashboard1.png) -![](screenshots/EmbeddedDashboard2.png) - - -# Security: Rotate service credentials -If you use this solution in production, then you should rotate the service credentials on a regular basis. Many policies require to change passwords and credentials every 90 days. - -The scripts [rotateBackendCredentials.sh](rotateBackendCredentials.sh) and [rotateFunctionCredentials.sh](rotateFunctionCredentials.sh) are provided to help automate that process. # License See [LICENSE](LICENSE) for license information. diff --git a/backend/.env.template b/backend/.env.template new file mode 100644 index 0000000..da5b928 --- /dev/null +++ b/backend/.env.template @@ -0,0 +1,6 @@ +DB2_URI=db2://the-db2-uri-from-credentials:50001/BLUDB?ssl=true; +APPID_CLIENT_ID= +APPID_OAUTH_SERVER_URL= +APPID_SECRET= +FULL_HOSTNAME=https://github-traffic.your-tenantid.region.codeengine.appdomain.cloud +EVENT_TOKEN=secret_token_for_the_subscription_event \ No newline at end of file diff --git a/serverless-github-traffic-stats.png b/serverless-github-traffic-stats.png new file mode 100644 index 0000000000000000000000000000000000000000..bcf6e892b391bf3f2d4f64953e108244d0b5d114 GIT binary patch literal 29184 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 z zq?0nTV|4KTCpc)S@Im)2AKAwyCST)O$uA3aDHW2a6xv_iBLiMLJ2T`MIvjoO8No>` zkx_h0eQ@?IS9g1x>gCmmhG7tv3ja8adzyH-a}WX22yiMG%`0h=bk&nMmSJG**1Q3~ zhK83E7su*N{E^df)Auh(zx(QEe6x&ABNgm{{gWp2%Sz)3_vlGndEmR@gORr#ebj87 zU8-IJxagd{*Bx~bq1$@1k(mLaYbq|dFy33P>73b!wxm}(@gh?r#`m}C0v?0u5i@ut z31E81f7MBC_qXx&9&BA;?<|HsY*lI~TK&s9+!PRT9w9y{!u&AO1xv zv(D`^p`;-h0!MW9-Oz`T_>Lz7fBYL3y`;)Y42pg)L63a*>2}9%agWG`e2zQ`Q{qY( z35V`NTi5kzF`I>kurEYS`rK>@LV08;sm)n9G%o6Bm}28613Nd+WVbt~^0d2=KE32$ zZ1PL1daqC+e`-;_U|W0uFbl# zqI6*kc$%lpL^lFgCQ@z5&a=z@i{by^YEFGR9EZmRFdIV8b^QGCY|GXPKdmU6Q2fLcYhg|&!eTJ)4?MFS8W(T^v z??G{;L!PWD5$EN4xQxCFD|A3{qYm!|W;{!L`8f5dnKrK4s1GVPophE(adX2M2|*Ey zF-V0thw!D&<@myz_7nOH@vh~X__M}b*-juclZ%bSiq*m{oUM|9gV-v~Nm*M^_%-gv zbafD$J~gJ@qnoBUYT-<_WWAdo7owq=gFMi`T`m?>V&hPnkh z77Eh}igdD=P`unc1;Vfel`h1-Xv~>G37DmKvlJ9?HM{4@J5YNu&C{-!NsWte7X}eL z9**oyIg5UKlc)r0_iLZ~gtzHzCCP>&Z+~MFP|B{K@(8v<7Nf0R+=#FD_DG7M>F163 zWBEf*NtC3>tTeG1QQmWGkS3mpvdb=gTESQQK&swx@dd?29(1%wwiv!6X?tUsCq#to z7G^|4E9X$Zh#UgA(@&Tbc?CHigs@4t#?9kqxys1KCIBLSFF`yST&URCOvUZX%(~m{ z6rf^!Zlzt~#JLPH$0d4-3v!`tGn(I>1Y&$E1vO>rMC9v7-9TOa^Z zh8XPyBB(%9=e~f}v1g*!YnbLy{l#2=8GOqLUQJf?Vw%rT0a?s(=x6APO8u>K>2#VE zl2YG;N6!B#w_^;))|g`w(s^ReP(ipgu|{UM4<@uuMU|IdwHunr9i$98%gqy?t$Ad9 zyk{$hlGS$Fvw}3_LJcHQ5t;n$VWx7w9a>K5yeI5a9e%h&_)3SUTYbs@ zIGnpg20#C9l31*qh-+o_F5pGhy3Su5sjvf?&lJUn7%PP_1;gQ|B-Ho6W~!}?y7cs) z#l^QRYQIeS@CQ)wHR7E`u2gXQ(Bf4MEU2C7r~j?iKqgO3Efipg7wSVbHfwj_l9lMIP@@9LE_CEVTaA2*;*|2Go2o@Em!MTVkA{eH3W% z*`Yy$v`no*{sRX)ChkV$F@sTH1{RH4J1@e%_6O!6pIhind5I~QbI#yJFp0%YwMK@8 zs9IUQy}-qF)5z$J)Y@kb=e-SUr)$h#^R#(>^=7C9A`A>J7dIZ>JN~Am&xzXVGn% z+h298%7*VRjaIX>%besK1}+YoQ|N*e;Z%o6f;3*${kHj zO*#nIV)yoTVlp++{?=kN$ceNc?50CjOFCvosx2g2@_loB0Kx~Y>&Xm{%6q9 zp&Eg0-?ygcH{uqX@F@eUQN3~FSvhDFTex`^*STbeL2mUAY+Ne}T>G_Y2OUwZsHke^ z_L%A*Vtt^&*MPn>Pt1Y*^XxdXVz}m*YUH;&ebO!mnhEbOkJnquuY6*1P`z{KC9cow zXm7Cb2B3(LkOIAT*X`?8m4A&^mu<`sYJjjlNG!j=t%wI5yK9{#O445Zj4X{gF0|8$ z6;)=X@uBBC&FO_hI*eJ&8`yhflR5u=2lTFr{wz&fDlD8AecBR6e^W~r_m>+@`5Sibg+6~i@TnjtewNhk&`A/!a zaH!y?e)9|}36jBcj3C+8 zc;C<16bW9tP!IF505ItMXIut+cK}7eJ7?rZ&W#Ja&L>ALcO^d!wS~Uoh_>W!aR(OV z#oT^lj0RejuaYI@)5Y8AUQm0*OD5$^tOQ6A=pBFurZXA}yQmk`?Zv5=ggBugyR8e? zhOyU&6W7Ud-4vEwdeNI@vV9|Xg*~PZXMNl?k9^| z^#lZPoXzMd>Eu-|^utxK-^&=F%rIWF_&WK4ySg+{$xFKOi$G-?TxM!JQa$!nSn(BK z)-)k7ynM~u=c2x>%*^i@!wr1PdqDbk?jvZk&_{Uh-+bLxN#0Y=z!2|NvpB!r?cn%! zxL;ali&R@otvLrR6ddJHr%;2Ck=fFt99A^~ zO)~(_FOHA{PD85VT0~lM3UxT3W0)r1WC|#Xn9H7rek4kVp4k*f$>PME>c^9pq*Zuh zv@@fjBl9(*9KU(eb3F@cPfId1kGwh3&)qv|f{Cj>)g-BTl+I!g79<4Ph(hjibv1L zeJhkip{+F1S$9r1)#J8y=cFIX47A}k!6tPfg#e!k8hwwQ%M-Nt50QW<*RPuTZ17o;*sS89su{vWBB`xI8jXrJpm$bwZ(X?8wX*QOl4N)#N_=*EtfiK=6VJ zaFXh9g2z4FU~sfk@NX>usi8SDdJ)zMl~6Y|!)hqg6GrjQ4KAo*gx6uGuK#JW5nl-XL0u z05bGX7WBm6SzL% zeuD#Me@0F{gWN@wHnenl2)s1Q51GD_4)rRM27OXgI5!Zy6OeN|Xw5uv8nPGjYQ*&z z`GCG6CSsLTm0FX?o^ZS5lCA|$%y#dL`yqV`y4pTqcztp*XTIt%tMh(_{{v7%1F&*R zWpPpS`RZ%J>AOGtoc=Z@5g?5Qk43EgTq*lz;S?F#*)m^$Ey3RcCd}ZUs`&Q0|M1Z2 zGOq8a%tSy$aWP4=;_UtF;vV&;o*7#e=BAd3hyJU~-=wkQYnA#DhUH`Twxm8-UVS*U z^Cg1Ps_wy-)U|8K|LtlzPlMi;+g%yF!_7uS7BUi)k6=o75T(&s!e7Z9jy}af0=eN3 zfyK2-@x>e`*LKuZ29)IQko@>uG$Spp!LJd)qC)oNgV zTEl;@?@fn5qY*edz?fi~jOGbQlFHvFyaGt(X&NPT&=0VveUjjbdnEjAQ|Jr|7@Y$E zui>Zm{Ivk;(jP-sfDNypspSlT*43i@Eg$(pisOkP+rYJWA1+zH+xAHSIUz*yyE?pU zjXcl+;O>yTKSfrA-!|8-#AwGxLPk^4QHY!HNk2#!DDt!3Q2Rb9LiLY~$J_DpBxrKi zl)*T47n3lcX<$cqW!J*2o{vlaQPkW(u+^{L?_9*FV%5^X6W5zP^WP)^C6I|v9r5-e5-~2CVd2> zNFJK)?d{FJ3FwxfH&uT~z#1*)|Hj=sQ^Ql43(bJY6FVqq!#U)_D_*;@Pz&@4_E2YV zzi=6m2tJ^XyuA?E=!|w{!v@2)73=l0(nmW}^bJRQ^M$iri75@v(|LSh^aAz{9^bXt zq)E+60V`kEg*-JaL_m=hs20#_nOapfFh~%!UB}D^r=R9-6l53r6Eu1|wJF5hgn*Xu z9_DTP&AVVg=YMU-Gg_WXaI^gy%P(n%-G~AeR>rbO%)H&kI9tDAq{8mGyWF7xjODmG zvG{l{c6KiHH!lE`wU-+9?QxCIqi!R1FB20D=_-M{h>abQdb;$ikWB6YptUGq4B_-_ ze)Ie^8>EisK`mF%yIJlX;N@!C8*^1HM^vH&Rj3wrN(vJ#-(|1K9E!l;?b~zMXJ2s<>e>46E&4Ef;KhEx)6&;~Z#a zASmNXiz3Ih)8Jb5=}kPoiLdgoW2Gy|x_qyRFp^D*;}-I#M=^iH%bLJase+)L)UFAW zonu_{NPn-!b-rPLt)p`lwD8Y~IA<<0CUy-t4HW3dwS>tY-#grMAd>~2k*pRFz$PB& zaVH+6@EX_H8oRl5Z*ks?yJjmoBJNG4q}csNoTE92JQ~=xH`ZsmaJ6!ljTb?!*P!Lb z&tAWX_pH)hAsy2avm1nDbgH;EddrSD=f2xwZCoin-z)c}z{wy+Uca)O*M6uqj=k-U zS4fp6n+}LcJ4Z1`YU9dA+GkK=_81J6(Y_Ag?}irD+8)45J4_QLwfK!=!<28YUmhH= zhb|{y*4*km;yAl)XqvtSz*|ylw3HNmQJW3NP+sO^HJkg{8YzAjw<|5nhWWjY4=HN} z^j3y-0iV~e1=5>@3?vJ%uYDjB_zcRBrg8o}@Wni5UO%|N$*H9Hg#P4+ia0*Q_8@X5 z5PbmKb)GM*YzN1H9J)0I1_t`o@{y_?hK-W0- z{#`chE`B`6&kJgrKYq*1`UI2VE_QPM@Rx4&tmJd^Su%=tUNuvZOro|!VxDV1eqi}) z@3+wl0Fg91fwisp)X-_M`kgbnVfW(xO}imhac4z?@`ng>&c)8x!j?`5aU`gy;!Tot zkE39b_zZTDm6-|pXidC+aRO)EGQLcYx72o62o4{vcFEpY7`|InGk4kZTOj?9w~6)= zTqXm>rUHXTF1OM0Wyt=^z_AYoXQU|IxqFwV;6mfh_b{A?4NiM_fh(${)$v$#tF2pW z=V5363Oesg`^rj7Gr{pVUmJYAJy+fnPlE$-M8&x@%+Yl+*w5+@`BrE%dbbzRJ#x(*&1xG zg)E-|w_LwWIXxqzClqVZ`dlqh)DCy#&FmX@96ih=*xU!fGDXp3Y^;vDk+hSzinVtkfW6UFf1_7(k4GP zo8H5^P%nSdTH1iJ{n>>(c`sYBb5YrWQ@J0kN_hn$X~PV3_@7+3Il1~F<10tQ^LlQ( zG`@bip<8bBG#%612ZC+(qKTdARu?7zkM^!K9Ln$S4=M?vY%QqpBfFG+iL5D-B|F(0 zTan$MC_AN0WnZ%I%h>lUAqHVG)~qox7&DBqJZI|vY;T@d&x_~9P^oKj7%CG+dHw^~9%p%88I!W#jtG8Ae&h2E^Geh;-yQk7>U`0q0Borz=*)yJVDw`CU~I92Wu(rICxpy`?s;U+m!UysJ<@E z)iC2m?~=OJ+~iJ>&gvrn9vzZi6&5PsNWHp2Bc7KmEG)8GS}ZREo#T0@MZWPF$Piss zbsw!kAR<9ga^dV@@-!0E&348IA1tJCH>kY-y&Vb((P&Cjy?dJKasiAiETE`Z&Bv$q z*Tx2GvXnb3@aBWWX8Y%#CD*+m?`|9gQifDP=mrkgRSDwIRFEp(92+a~VXVl#^Y-(@ z=_2uDhBgm&lm^zbi$mQtNVN5!4~uQYzCf}#i)0RkrpI@ok3Sya;yC0w)z~cTE^@5q zlXI@V`>S~YEj7+45pjU+bXD~zpX^bcoSc4nCKGj-`*-G}MWdLw)gydnVUL8YTFmnc z1^&@Zn{vD?=Pk67H2dUxZltfg2mpoQb!$B&q#^UNy#c1pe9fdA#wXojw+cM@8L<1& zAdLo)yXvyu!@20FpGt-3<{bqxgoNvJ1ygq0ds>uvx2CSs|REkClLKb7u zH%#=CSM4BNb_YaBb{wAvNA~yybyv6^zyLC`BR=Yinh1JU#4jqqt!|92dj2N%BqPP9 z6r^@}{}h=7%{zUKm~q#Sj`$jE1ICdrTqRT7s8xyij75_drp97b3DY35wzk%pc_-X| z{wHuIHXtLb649a;fU@w31Re<=I7=3nT%A;bFh-m9mnY{l!#UT8=vydrZb?@(&sk3S zA6U(GkP}UjfPPQj%(DP02;3xop16|E2I9lM3$pcDHTCr-EBif=Y8u6Sg zAR~JoB!#gi`f^@t1{EGlcdv#qy0;$Iuq*gKZ_S6{#~yV71QVFJ&-G?hEcKf(ra&%=Ju&<@kY7%t-5^y>sb>vAhouBSUIX>loH>2l?(z{6irJR%*^;87 zqTOFlq-ongZsK&b`*;lc;O6~Cm!3E)Z18*}pIf!ZWafikF|L!A`^mWo9nN0?E6DU< zs1WPqQNPvE_UuUd`3~uQUm)0o-}^_kb*K;2&)I-d~?Dt{OZC0*(H9(hEkF>gwu< zXwep9fKOv&4p5C?rIL8iZxD?PB2>)-!DEqd_{IouwAfS@XFXiD7kh1%zkPx!eyArY z2Lv?MZFbOhO*p0tmo{$54o^?J`YT~jWo#e*TzVSXAff(I>IDJzI8>HB*c4M~Ib&>#t zvrvN6;+SSqwFuDV4!xE=PMReut3)Ko>A6tGSh?qYxgI$}ytQled3>C;fle9aH9R!* zCg`4-0*v_;kuJpc*D(V<#aydG3Y#H6V5}hr;;zN?y)te|_bGus3$>uV6)IoWHg|}* zxG6zrDjdO zTLr6=^#rtY_;diaIen^0re-@<%KN|%Ab2fEVDmJaAF-GRM%LL=#k`R{WsQT(%6)`F zeya+&lyFwW_AGo-Zalw)fJ?eEg#>pW8bmI^K7lY*(K{l zr_u_C`)1GA(Ov;Ht^oxB3*Gn2%FJqmzIY-_B3;ls^YHb_+GG2LZZ?#Nd0qMJxx{Ne zR%)K>C+E{}6g<|KD-cb=TUS0s82q4Q%zVI))ibj|sqr)S$ye5&&+#0cC_O2rvy3_M zl(Cxzp<4PFa63`YtWU$q^KwOVc3Zg5jTo!MW;mXx1sQMOOPqMlmZq-m`hr!&JYUGT zeD2!={+n~Yb1$ex0+&CnxB<*C7;Ybb=~0OZ?tnohb4U$k8Q?njj)uCPN-!#$O>N61 zwBnM=^+ILnbrt`cH*ZD&NT-Mg{5%fdKECy{ll1IWmf_4Ca8v{xjOKHA)%;2^Xlg-2 zKK^>&hvu_}mmZVwl$`T&kT|LzY5O>4cC+E8vvbaFiXURoBR{>^;Sy!XlZh4TpS{(@ zF1Yfizw3yQ@5%SdEzFWc^egwKFgwUVc~`#zNI5tAM`2?AsK92LD`(~m*#m?c;y`zi zSX`TAN1ci>KfX0KznHt~PYiSKmfAX_A$(RcR>LGNr`5!8%#%fHKlPfHMl@rA28&71 zBlr?7U_&<{jbm=*=mf4GV@YqzVYz==i!THdy#q3sqvz_~5bUq{O_CE ze3epK>#Gzg1W28Zh5tYk8ybQUo6bl5Rl#oL{gZ)~snqQ$&PP0}PHP?XJp(pIiJ$Lt zYsCHbS>jI@va-4Qnr+$77x8O4U%~S^#m9B@yN|CxWz)+y>``E9Su*0R!uuzOcGOjO zE)4A?GiuF5Y1-(_zCGZq9>g%M9tfB=&@-@0g34tp!g zKDo}&9hUfKwlCMCp-J=dckkDbxfhks0TBP#)e??mhKBX4CLb8$%?;r`IdKHV(z_!A zQCatNEQGG&1Rn5dCVUn^Aky`nogE7jcG3%Znse@sSn+7xP{8?Q$P`uv7T45_r!Q#C zhCF+ftiw(+%?&@ha4uO!)Dr{JEnhQx2(h2b@U>94&XC>KfhV!{nK)~G7#SF)UV`{Uaoullw}HZ14$9c0z?~Qg`d1k@K1~4 z@j5H5tJUYFzSHwKh0&WPM4KXzqRjwe6;{#wNKd4qH=nmsE8^+zwbQ}3&xa}x{U1s|JR-RJ&c%i<*p-{g{}zWR98qkr_#ef_3xu81zz z9+%^~SX{m^3y7HSd8w8l5T$AEpG)pO>=;QhSCjFYGZRz?z%YLxbuK#RA7-R=$PdxVKIlg?ChyIDS&S%UFHHosg3LY>}Yow4WOB zJcwl=QqSB`t~^lLxzmN@QdFBg7%#aTWoC6{|DP}LVryWWw)D)};!gBHQhxn3P%`?e zLn}5*^Y{TM;yP1qN_Pj-u6yUZyNb5QqSE?bhi zHBdsvjg+v6Ant3C*1A7ld+LVuYsGvl6ShfqHnNcIAFz zIN*$$MoD4e_hR)dfqoUku?foo!-UV6GoOlhw5pntoIy=RKE%}HatA=B(ke-($LwbF z`q7&1h-NLlVL+M2Oqi?8pD6y71_7d29;AWv2xEnDR(IO(`8_I$j{lrBN}YZGFm#-F z+~Yaz1`|UscDyfm38KP+U-^c1#6(J!XG$TNoH{=F0{y`?fp#YFS;Es&0HdJFO5dK% z!9ARCV1!Kw?WAAX97V9C+3Z=fo>u_kkFDhqi>yN7w>$)wdO%=8T@R)<2mPod?P0DLixbu_b0d8X1<22{b-ORfqv318O z5^Jqi8}{buG1o|mKmE;SafJ5E3J0kXKW@0sy{CsQeJP^s^!8<$UUITks2>f_m_G`z zWsiS~iZwMfX*I5Q2m4pm`YrTyZc&9ldkJM<~uEdsL90OJDuQYQ?X)?z~isQ(U= zQ(9UOf~ZbF;qK!o@|d4`OvX2*2xh~bAlf?@!x#c(5K~veo^DHq$thyj5<<;OEfna$ zN;)j8Lg92*M*Nhj7hA6tCtE^tlgR2Yq46Zz%W6WyIhqM+aDhxHx6!)~qq`6V)OwV1 zGu@F2Y< z@>a4yKhl(LJJoXVou0N`mLXY$A0uhZ-o^#L@Sf9;^OoT^&`b$=x^goN?^jUt{!n{( zd>bxCexM>_ax!-QOqZg663jpvb@JlFM>mqY&i59A6JRoHfOWQhVo@k0O7Jh8urq0y zwRzLJvLp{W8~-tSt{uVCg7%!4WZ_7c5_v85wR%xH?7Y5*^^N1f-z$L@riTuE;hRd@I~3d#1Ni#FhcIKEkdUAH9ij6D9<1 zTdGSTH&5C&))Q*>$zJA1zM(zw!31YPNl_zm{w+HDUqk{9OLeC)V|6!EEuDUyE6UMx zSeEb7hr#;?5aCp*pA|4}`Q-j9o78AW2Ih*R+s}(c+09lNI^k!#8;opqhV7td<-U7v zQrpT6tUwQMG#ea|1;Ar8$cdK@!+U)mZ(cQ~J@PPGkEAQQ0^GrkOx{6Vi&BE6>N%^P zh2G(`fgEC+%xR3&MGa@XUz$)TJC7f#>>`ug7YuR9#{5JU%)2GKK1bHwg9|X?ZXEh- zG@n^B`LWldD!b-4s4f1k<65!q&GCwV*iep5JE|7K1!2EI&NDR>VnWgd~*3WXATH#oFVt zm8AQLOAJDs1+dw_%&7ilrD|+NB}}BN?qp!n3SwY_!TIidPi7qca1o~^|B7Y$JmdaA zPg79T)G}@N_znYl;3Re+S+>F%F3RY9Yf`-Z`F)ow`?=r(KM=1Yx^r~+V&J?4T;Oy3 zcW@BD-R;1+MRo}hX>r^VgYD7oRaCPR}W#E0|tqvt+}**ttdbs95$n$N9mA(-GM zqsp{s>iytX+4|VYt=GQ~oh>fB__#q^BPw9G?$#>!mc?&UT!8-HR~9tlak&thP1b(W z;dxIOjx#LU*LY@Ec$e$AEobJ8-h~CmZ_JG9uM*36<-t zJ$}u8?WOM>0umkcGGFK~Z4#ZKBK&ixy{ybkWxW4jVd33Z-%Rx0BmHEo{*GuWOuSlc zCt<{c+yoACX)dCt(-(N)SjL*U2#~p=731$+=%tL^&TEnUHx; zar^DkAmz2K3>41L-Yz_lWn2S|;wjJ+WIk#TM9>Yv%{E z3CN*@OBr5@XN4zXG(D=AYWMZRYXj(#S{Q^a^vU8na_p3gw_xxn$-@J?K|5V?g)P_` zrwXE1z0!WXY3%pkIV>^gK5`V8mCXuqQ7w7EySiH`ly0?CAA7;GR6wg0A<<)l95@4* zKts|CkY_1n$gn@2fP{?Mu$vM{8LO_RF{v9ucchY1$ijk4ZU* z3YvS@8FT0$AzJzdb~P+1*Y|#Uw8ROU65zsWSJ$^Sg_qjE;M>_o3FFHV9SaFOsn@ke zRMp_NejQtN0$Ra(P-jt}0G+5T^HS4RY1LLe(f;J=w09h20uZtZ{2jeM73Rm3Nj`Mg z`eu!;$RXL5$fxr@rK+x~>8~_b*csBVqIMXRFlJWt%!saw|2r9jy;AGW>&il;yfT-}MC zHVJy;%P4H&7_diIFA(5vT!evEWgDe(x$c~_l?ZJA(f5Lo%(3a@a;2t+EvDoi*3ryq zlrUQ;l=S>SRZoj7<}7-0(9!&1zfi-3<6ybT!~j)UM@mT2Y|)+1xXII) zB^r;w46%XrQ+}_c85{39RzbKwGwBXpcvG%1n(7aY%ZVx%mvtGJ`P(t zUZm-E{WP05I(8_8+~ueZSP>DD+oKA}985p8OIC5|ShVJLE0c~T6$FH{!quJlp@d)` z0C^tCqAZ-cAoy#Z+){r8y{O|kCK6b8TA+eP{#hL zFv=7HcBIjM<9+JU{;fK3qlIhLHiH8qWQktsXGnVuucL!JU0-`_?A^w2e|Eui^!0hJ zD=Brb5Dh)=hZ<_0X>&^;imO@}>Qh7HKkW9Bhy6|WnZGQjX0cl5Qwpc zoVtp(n})H{Z_6KRo(h4lqjJeM5*JazQ~;P_CO`tBx*DQv7E9J*`%%geQ?pCo5DrF( zOFtr*xXq6ZTL5B^Hs|~ksq`UU%=-#}B5_ThCSHDf`F~0JEMT*O|;*TuGoBC)Zs=FpNT$y=;`I?tZ8r=t)g>QFyXGR)* zh$m;b@Orc$MfBkPtRDv=N~qN=CWoP`1JJ!aUzj$hL08z2kDNV5TiyYYa6pFk61sVd zB5sh=kY|;ezXIk&{qNrmtx(sj ztNmm>3V#&{<`P~n!T=K)oBDkU8cmZJ- z_vifW<-WLWsUALO``e%)Hmw+0pif2m}AM%Ul=;U)%a6 z!l#`lfWB?!Pozrb+oxKug0(w+Ws9i!w65=b!UZHGx*hDSX5f0-Mj%E4K+>*cVeX1W z*IOzz-ED_K3*P^i&tuS)L+^B(?7gf=j4`Qb)FuI8} zn)^Y}?hnz4FNfbK4Jpb60DcMrr4jqXC-9n&fd8EfQWD*jGZ|EE6cAC!-o0)VmZDr5 zzjX(qn7Y5G4e?&8Yc?!M7x$zJIj)qk(ghT8iH0yj2EBgPsOPvj8>}H^0c1@aWDnBj z4T%+*(!F)zm&_<6Pzs4mqp+q8IT7LmF}J8TdDsP%^-KP`7ih|&0?_pA0Iu#^C{yi% z{;a2ecaI)KU!O|?O3joF@0;qeK7s6qa6bJ>@KAG=g=;|e&W7;B>`GTUWULGcL4#@M z!XdAz_9#PiAnE+*dj(m4#D(Bb+ZHRMsKnNGs~{fx`q^iKB7~aafToSNoE4z~q3hp6 zhZiW#LagaS93Nl48pN`3x%oO-8HKIFpDkhqAXtaM83yhMqxRGJvs8P~HD{?d-}oOC z+zFN{Sf%oTEXnnrf=OQIQ~qafLE<649NjPCttj&;GCF+Al&&wVSW=2*e|(qI*G`rl z=lmC2>d)sPvmfG2Gd8&q4tj#t2T@Sz5E4Zby|t!td>a)Idb1#kIY|f~9rhi*DfgX6$+Q&q0HCM=eh+Jo;%ja2uIdaN1i=a+~kwbt|(Uhn$MZpZ=3;U4zhSZQ}K1H3vg7YoN z(WiZ1{*YHl!5#;67pfHf2Hw!rJIm^+ry0E~DF|A=EdW{MTj<4JI`rTaG zUD zgI>Um5NG}Lg$?R)`kl*AvAE7O8e6SiQ}(w-;52e0^jVy`KOIMLq`n^rR)NM~s>Ez= zHMh=ji`DG<3*qtK^64SIT-{}#i&fWmtuLDlKfVkjUh>cORtRww8%Mjosz^2N1ty=x zjx}cXw0+~nZc=;CRIy5VG>Q1uh6xddV~tZhiTu>vQ~D zgO(R*cj#|Rgv_={kIbCmc59_vXjLP8RMf=f-T>z>|1yi_OVpbvc`L!m!{W%VvSfmp zR)T)rMNq7(m-uJiyySFasqBUBqzQAK@MkwJFd2&d$M?XCX#hsRzi#^f3vgWZf0ZC5 zrr8AT7fuJ;*3X&#yp=9Ic>!Ff=s9x%=T(HxUz#KT@XuS1qTdlOlQT1x&6Z73Xi;Rm zq$Ov}>%T9yi;`)PS3*mF3~n_6h44{^uH+>f46q{%Du9@%s*Zuad=?N|f)7Dt_