From 73fe125870eae99d373cbb80aaeb0118fe72a038 Mon Sep 17 00:00:00 2001 From: Gisle Aune Date: Sun, 7 Oct 2018 12:16:53 +0200 Subject: [PATCH] models2: Added log character updates. --- cmd/rpdata-server/main.go | 11 ++++ graph2/queries/character.go | 7 ++ graph2/queries/post.go | 29 ++++++++- internal/task/task.go | 84 ++++++++++++++++++++++++ models/characters/list.go | 6 +- models/logs/update-characters.go | 106 +++++++++++++++++++++++++++++++ test.prof | Bin 0 -> 26847 bytes 7 files changed, 239 insertions(+), 4 deletions(-) create mode 100644 internal/task/task.go create mode 100644 models/logs/update-characters.go create mode 100644 test.prof diff --git a/cmd/rpdata-server/main.go b/cmd/rpdata-server/main.go index 4e8686e..e1f59ba 100644 --- a/cmd/rpdata-server/main.go +++ b/cmd/rpdata-server/main.go @@ -8,6 +8,8 @@ import ( "runtime/debug" "strings" + "git.aiterp.net/rpdata/api/models/logs" + "git.aiterp.net/rpdata/api/graph2" "git.aiterp.net/rpdata/api/internal/auth" "git.aiterp.net/rpdata/api/internal/loader" @@ -24,6 +26,15 @@ func main() { http.Handle("/", handler.Playground("RPData API", "/graphql")) http.Handle("/graphql", queryHandler()) + go func() { + err := logs.RunFullUpdate() + if err != nil { + log.Println(err) + } + + log.Println("Characters updated") + }() + log.Fatal(http.ListenAndServe(":8081", nil)) } diff --git a/graph2/queries/character.go b/graph2/queries/character.go index 3e5dde3..d45d9d0 100644 --- a/graph2/queries/character.go +++ b/graph2/queries/character.go @@ -9,6 +9,7 @@ import ( "git.aiterp.net/rpdata/api/internal/auth" "git.aiterp.net/rpdata/api/models" "git.aiterp.net/rpdata/api/models/characters" + "git.aiterp.net/rpdata/api/models/logs" ) // Queries @@ -59,6 +60,8 @@ func (r *mutationResolver) AddCharacter(ctx context.Context, input input.Charact author = *input.Author } + logs.ScheduleFullUpdate() + return characters.Add(input.Nick, input.Name, shortName, author, description) } @@ -77,6 +80,8 @@ func (r *mutationResolver) AddCharacterNick(ctx context.Context, input input.Cha return models.Character{}, errors.New("You are not permitted to edit this character") } + logs.ScheduleFullUpdate() + return characters.AddNick(character, input.Nick) } @@ -91,6 +96,8 @@ func (r *mutationResolver) RemoveCharacterNick(ctx context.Context, input input. return models.Character{}, errors.New("You are not permitted to edit this character") } + logs.ScheduleFullUpdate() + return characters.RemoveNick(character, input.Nick) } diff --git a/graph2/queries/post.go b/graph2/queries/post.go index 570cc07..86d3797 100644 --- a/graph2/queries/post.go +++ b/graph2/queries/post.go @@ -52,7 +52,14 @@ func (r *mutationResolver) AddPost(ctx context.Context, input input.PostAddInput return models.Post{}, err } - return posts.Add(log, input.Time, input.Kind, input.Nick, input.Text) + post, err := posts.Add(log, input.Time, input.Kind, input.Nick, input.Text) + if err != nil { + return models.Post{}, err + } + + go logs.UpdateCharacters(log) + + return post, nil } func (r *mutationResolver) EditPost(ctx context.Context, input input.PostEditInput) (models.Post, error) { @@ -66,6 +73,17 @@ func (r *mutationResolver) EditPost(ctx context.Context, input input.PostEditInp return models.Post{}, errors.New("Post not found") } + if input.Nick != nil { + go func() { + log, err := logs.FindID(post.LogID) + if err != nil { + return + } + + logs.UpdateCharacters(log) + }() + } + return posts.Edit(post, input.Time, input.Kind, input.Nick, input.Text) } @@ -94,5 +112,14 @@ func (r *mutationResolver) RemovePost(ctx context.Context, input input.PostRemov return models.Post{}, errors.New("Post not found (before removing, of course)") } + go func() { + log, err := logs.FindID(post.LogID) + if err != nil { + return + } + + logs.UpdateCharacters(log) + }() + return posts.Remove(post) } diff --git a/internal/task/task.go b/internal/task/task.go new file mode 100644 index 0000000..bd67894 --- /dev/null +++ b/internal/task/task.go @@ -0,0 +1,84 @@ +package task + +import ( + "context" + "sync" + "time" +) + +var globalCtx, globalCancel = context.WithCancel(context.Background()) + +// A Task is a wrapper around a function that offers scheduling +// and syncronization. +type Task struct { + mutex sync.Mutex + ctx context.Context + ctxCancel context.CancelFunc + waitDuration time.Duration + scheduled bool + callback func() error +} + +// Context gets the task's context. +func (task *Task) Context() context.Context { + return task.ctx +} + +// Stop stops a task. This should not be done to stop a single run, +// but as a way to cancel it forever. +func (task *Task) Stop() { + task.ctxCancel() +} + +// Schedule schedules a task for later execution. +func (task *Task) Schedule() { + // Don't if the context is closed. + select { + case <-task.Context().Done(): + return + default: + } + + task.mutex.Lock() + if task.scheduled { + task.mutex.Unlock() + return + } + task.scheduled = true + task.mutex.Unlock() + + go func() { + <-time.After(task.waitDuration) + + // Mark as no longer scheduled + task.mutex.Lock() + task.scheduled = false + task.mutex.Unlock() + + // Don't if the task is cancelled + select { + case <-task.Context().Done(): + return + default: + } + + task.callback() + }() +} + +// New makes a new task with the callback. +func New(waitDuration time.Duration, callback func() error) *Task { + ctx, ctxCancel := context.WithCancel(globalCtx) + + return &Task{ + callback: callback, + ctx: ctx, + ctxCancel: ctxCancel, + waitDuration: waitDuration, + } +} + +// StopAll stops all tasks. +func StopAll() { + globalCancel() +} diff --git a/models/characters/list.go b/models/characters/list.go index 9b72798..5820be2 100644 --- a/models/characters/list.go +++ b/models/characters/list.go @@ -25,10 +25,10 @@ func List(filter *Filter) ([]models.Character, error) { query["_id"] = filter.IDs[0] } - if len(filter.Nicks) > 1 { - query["nicks"] = bson.M{"$in": filter.Nicks} - } else if len(filter.Nicks) == 1 { + if len(filter.Nicks) == 1 { query["nicks"] = filter.Nicks[0] + } else if filter.Nicks != nil { + query["nicks"] = bson.M{"$in": filter.Nicks} } if len(filter.Names) > 1 { diff --git a/models/logs/update-characters.go b/models/logs/update-characters.go new file mode 100644 index 0000000..b665853 --- /dev/null +++ b/models/logs/update-characters.go @@ -0,0 +1,106 @@ +package logs + +import ( + "strings" + "time" + + "git.aiterp.net/rpdata/api/internal/task" + "git.aiterp.net/rpdata/api/models" + "git.aiterp.net/rpdata/api/models/characters" + "git.aiterp.net/rpdata/api/models/posts" + "github.com/globalsign/mgo/bson" +) + +var updateTask = task.New(time.Second*60, RunFullUpdate) + +// UpdateCharacters updates the characters for the given log. +func UpdateCharacters(log models.Log) (models.Log, error) { + posts, err := posts.List(&posts.Filter{LogID: &log.ShortID, Kind: []string{"action", "text", "chars"}, Limit: 0}) + if err != nil { + return models.Log{}, err + } + + added := make(map[string]bool) + removed := make(map[string]bool) + for _, post := range posts { + if post.Kind == "text" || post.Kind == "action" { + if strings.HasPrefix(post.Text, "(") || strings.Contains(post.Nick, "(") || strings.Contains(post.Nick, "[E]") { + continue + } + + // Clean up the nick (remove possessive suffix, comma, formatting stuff) + if strings.HasSuffix(post.Nick, "'s") || strings.HasSuffix(post.Nick, "`s") { + post.Nick = post.Nick[:len(post.Nick)-2] + } else if strings.HasSuffix(post.Nick, "'") || strings.HasSuffix(post.Nick, "`") || strings.HasSuffix(post.Nick, ",") || strings.HasSuffix(post.Nick, "\x0f") { + post.Nick = post.Nick[:len(post.Nick)-1] + } + + added[post.Nick] = true + } + if post.Kind == "chars" { + tokens := strings.Fields(post.Text) + for _, token := range tokens { + if strings.HasPrefix(token, "-") { + removed[token[1:]] = true + } else { + added[strings.Replace(token, "+", "", 1)] = true + } + } + } + } + + nicks := make([]string, 0, len(added)) + for nick := range added { + if added[nick] && !removed[nick] { + nicks = append(nicks, nick) + } + } + + characters, err := characters.List(&characters.Filter{Nicks: nicks}) + if err != nil { + return models.Log{}, err + } + + characterIDs := make([]string, len(characters)) + for i, char := range characters { + characterIDs[i] = char.ID + } + + err = collection.UpdateId(log.ID, bson.M{"$set": bson.M{"characterIds": characterIDs}}) + if err != nil { + return models.Log{}, err + } + + log.CharacterIDs = characterIDs + + return log, nil +} + +// RunFullUpdate runs a full update on all logs. +func RunFullUpdate() error { + iter := iter(bson.M{}, 0) + err := iter.Err() + if err != nil { + return err + } + + log := models.Log{} + for iter.Next(&log) { + _, err = UpdateCharacters(log) + if err != nil { + return err + } + } + + err = iter.Err() + if err != nil { + return err + } + + return nil +} + +// ScheduleFullUpdate runs a full character update within the next 60 seconds. +func ScheduleFullUpdate() { + updateTask.Schedule() +} diff --git a/test.prof b/test.prof new file mode 100644 index 0000000000000000000000000000000000000000..72784d41501c58cdc58249a7aff54c0416bc95c2 GIT binary patch literal 26847 zcmV)!K#;#5iwFP!00004|Fpezd>zNRKm5)tO)S}(Jpn&*(&qS-Y`2vgHBGqz7qv8aM1F5D|KgUrm#%*@Qp%*^cf^UUs?nO#}AzfaTqPa>b0ot=5+!SD0T z?3)+ox9qd`?3VFQRx%mkw35jTZoc30;^w=*`;++l)6(%r%ot|`dfjbGV-_R4sl!Pv z#t65-irO*GDBK+16x%4@Nktpw4meR3nFVe4h+j;8+7!J_o&-svw++kN_#=(C0M$D>cgQdnhZu~#%D@h3%75`Pk&j6M}h zVO3yB-@f=%GXH^SO6FCeY7}BAECpum5l>a-Tr^eYsgN3dD#jUyJ*O|=JgV?B9!;z8 zYEUiubc{0>%I4q3wiLe2+lv(b3_KJ4MvSuvY`cF8+c>}J&7bpc!Z$_R<1pibXmj}} zug&Gp!n3072{^P$v_<%>$HZJBJPOfh^%!SQ!sSaNjF`n9=Uu(F$9W85(QiefoN#2G zN>vFpOv|=xhdov)DYg!Tc6wF29|$;Wza-{e1rA4i{$arSLke(*lF zJZO`(bz)zwt#5ns7?C9om-8zGl$WwB`mB0CHv`Y2Zx4!)yvExQPt`7VDEUKC^g09u~hfMb4-@9^IDJ^m8B6n#0y*$<&zAJJBWANR;z zgTDf=h_*Bs+HoyDSCe0OR%}yE{wlm0eJz&4YQxck*NvFTYVq%grds@Ucs=??EQS3D zb`7|JP2cC$MAP^AoA74ztyl_s4zACiX~s056jheN- z|AQZ<)&9|QG0xIq%Ct2&had6<&xl8U$lr#yqp!s{`!QUYAm*3G2gO8N8h;1g5pB=I z&Sj#lHb3dL)#mTQyQ1wUaBqcZ`w_qDwf%^{2k(h>y#R*~UBk&g$D?A%=lJ{Ze)NM_ z3i~OXyC<5``Hw|YI{y$pjMj;zu%AJPeqzZ#_BGy*`A6`PSn|)I`5@8seIDZ^65*7W z?pgL&rN`2L{QOT|`03An{#-1D{Q`#e=z;S5JbzmZ{XG8|K92q&#@R2S_pa8+bwAPe#y^y&;62TK!$kkW$1EWv=QD46;KL$1s?P)!{z>pHxLu} z6|V>NqM0$yUWHA!=Hh<;ny>TZ=GVMF)Q`qvoV^A&JBYU5@Uh-Ye#09;1JU+6T*4&;cocVOaGu|co$ ziB$ycUgf!v8_kPx_Ad0?(-OygjSunKUgPd{*Nb&*f+2}{hs&t+J4W!fGuuUQyfHKuZGV9l-9+2F{FvAFF8>?+ zO|<Ce9j9XO_xXK@&{*OQX`9J^iUkU9}7Y0oduG)M2H-aAT@xR00qyLDd zuuoxdRHg2xA!=m6!bxH-@AH+e80CHbPxz;pOFft&xBo6W4Nu8oDreNw zce^UfgcG-gF8F|NwS-H4-WG#>!2cWmTM(;0^t&rK=R>~FJcE0Ex zN2XOT%6z4MT<6;cu;ZjqR(1GjlapGNSRGzUpp*!8l?9`>w8IVgi0{C6B(aZp69P?0 zv?+;k))3C!5JL7bZ;i^N68o4pBhZWp5zK~O-GzAmfnP_Ct;GJon-gg6wSERY7K+wa z_!ZB;YXq~ei_u=?E2?l3arxuCbmzK1RbmaY8fJgiNQh$&%-GikXY>hgg|AFvpYRq0 zT99Z0l5XhVc5Du4-xMC+e?Y!1P z*wR~Y#b5Zi6hY>{@b(1S6Tufn(5_6hzQgz7%Byjkmv-FnOeHrb_r<*Yg1;6PCD^no zD~6rB1?B#W_eDz7J?_cOcM#L~GWn#Om^n1Uh=8E`dXv#RD(%hm4a*iWC96 zxWw0TpF@)cZE>(q`52si68n^QBG8FMJ0n@YfD21Ra|S=>HD~ZL0%d;lmoQ?Cul!n| zws=A+zogH<_%fmXs{;ug5#0vZ%!B@h$|qcF?T~jMB{d*qV6e*=21A z%x)b*`9PDFLopW3k0kafirEzYChv-cFbap_zZ7RpYeVK`jP0*YE@#5cz*)@y#;nA zFjIHvB2P)isoaTl4lJ;Yz`2RSzUA;AiPhxr0R#q+=s#kdbs@0(s@Rn``G;cpT?s60 z>eH?z(k_)h#!I_juT_bckuKy9UDfE3YW!JVdi7LS;XuOd>`uOB$meq6BtaQL$JLkfmvmu zJ)e*BRC_)jN?<674#W0d1a6HRiq9AD;a)rb0mDgj1h)4kFyY=oZ2v1?exR564Jig?u!D(Ih$s+xrr@v`z$wMSQJiNQ?Mb0%J+^<(SL*5ty+> zG#B%(IQ>ejn2#ecjznLMr1BJAI&|q9m3Vj3gY+c5NN>`I^dZs=q`rKh7g!8r+XfC(vxz!$wTzHx@;(c(v`xM z{3&lr1QQM;Fx9gegkMAl`J3|LCx;RYJBEc;?6TnmMxPU|atS|)TUm*f@bLu3ljvV# zE*n8$$8n!}*Kj&YMGhw;f)g4^;Jh5_vNxeo1jFKgD1h$|B zkJG2h*?0oiW;aLi`<(w>h~MXY0)Yu6IuW^W0)b8KeO=HCSMn53=B54m{k&4;@nnKh z1ToGg5*XfFSf~aV$;UXGL}0&UmCph!IFU>WFbijs39QxoD$jLtpl4M!g+P~yV^AW$ z;Bz2mvM+hA&}v`sNdzW&s`5)dnZRUERZb-^X@pR&jrk=EtIKOyRN_1o~CL@iTO?1Di>p?G+($f9FCy z5Pk|3eiHjTpGIIBiB88i`LSe`nIzyKxoj4J&4GPUuGcJWS6numK-X!)g8zeeL;aM* z{=sJum_ed5agXK@7~8Qe?*BjeYhwTZ$!8ImMWVB@c`kwOYnx*8fAa#-{NH>IfjJ~P z7ujPTftlUf<6is^KbRug|A)^bFi!*u5jLMduQBau%x0y8Pr$G!8ULD3V7}jD0fEz- zM2{wfmp#Ertz`Ub0f7a6kA(!L?Ginj65b49_GJ8PA%TT{k3|G_>=Qkj5kAoC(Twm# z1Qz){787WHxHHb9IpL>plaukU#RL|U=n@i(uq6a0%VC) zE(m@gyk)TO^5EWcwt~RM*-B(JQCSfX$p~9XVDRqdxTuzd4?>lggnum~unfZ!m#reu zaj)>-p5SvlQ}6K7xs$3_;w#B2QZ?uet|ri0B3w%;N3yLUaHxL=d_yb3&m#btjDIaB zupCJrsqE8@ttM;@iKVi&1a70JW3$#sXW{BBvjyk0CVT~f6(ssI&0AX=aAsV#j=;K^ zBIfuOUu_9fv7W%eR=$o}go^+fMx~UIb!5FJ51efva71dUUvTv8eSe#8Ad^S{$%?Rz z1ZK|Yfvamngwn5-jDM{puu_PR%Qg|1B_ris4kwYvrL$LQBiTg!@D+N8FV9b~7@_Yt;>z{~}sak%z`_r|PT68^P@z#0<$SSBD+aF4N!F19s)DGB@%wllOo?pR@oC?+g<_(WRxZ(?n&7}ft$@tfL0_%OLI7VP_TY(#NCj1SK zV$+%M4Fooj=tktC;{?Vp5*oXV@OBZ=qm1xP1U8}Ch(*{50=w6X9!&_J{)A9ACkfo& zDt_xi_%+W~bRm2*fzAF?rwFW>B}mhicxkRA{A&w=EvUtn7j%>yBge@Ja*~`v?R}cS z%E7`)HsGhx%~5H+(_yvXvNHr8UJ}Ag_-te|klt0w84~ovU3Qj0vk*u44s+63jU!xk zj=*Ax65jJrLZ5jnp9_WM=LsAd>boqPajz6OeO~uRE)Y1@*4KgPFVZ(NrKxe5Sx8qbRahI3gZ}kDYOk&m96#_lZ z2{GtK_yi+nBlq7#1(^_sZiH_ou+q%y7(7(7bE`fu>D2sWsg>N*J`-Y&aA;LLClr&Q&ea50KaZjh&g`rBnU z2~5}XZ;^ti>GOqWF1EdtX93g4tV3bBxd?u2h6u#H5wqb%GeFzVtQBu)>)dwNb> z55jj4*g>N4ScKgn@L+}L(341@t|#F;3GBoUF1t%$?RsA+&-bk6Jp!Ft`9GE7PZ4&X zK#L6<@jbl=zgR;kmR^MKBCv}@UyHfy0fD(&g}YkJhauqiZMmYS6|`}S+$ML(UGn-p z^7-;m)ivXmENHei*5pgn#WOu-o(Zn^9P>SIE|r{5p!E67Ht784Y?w%_)qLa?=$h zK`}&gS}rPULE(DHY4?|()Pg3!zQ{MFG(Gu?Up@*ha#>3Xm!<7$iwfvT>9!@eqyc=f z6@^K%xTiUCtYYt0G|aHADV!YX%fc{E7JT3*r8N!9LK_M@Wb&!I*VzZJQ`&?&bJmu^ zNQtq%Bb>H&6i)Q*jFCeh!e19m--qx$1ojA~cUgN1O*i|z)(&~Cssu2qw55_R?Pz-% z1J;4UAuomc0pI4aR|~{qBdjBZUK5+*cK0RxjAvi_626zfUUXL?tP_P58zs-*t0B@4 zzJ6vMXh+(KK0pJ}nZgRG@-USgi?A{Z`%Ve>tRL}e5t8t)eFXM-?pYTK!vm`28L7}8 z?@Y^Rmk^bJb*0e9bGQD^*P{+3VJER`xjpMfVYgmqqd6ztXuy9@W!)*9!!5H}f5K;z zn8n@@TURRyf7nl8KZzD;9MYYB90H1ZP?#vSIVOf8Zh}}nsPF8$tS5zw8wCXaGT-YZ z9(-$J_oM>$b6GD6+dKQ}r@zO-KG~Cd1zET^g?0mc+bpb&lCj>joXg&a!k~w9QHKp6 z`~pULN%+?R0tY-**q6eDo8ra4<|{pk@L!zLhxVm1PzsmYjzzH>MEG8uWFtPyi}D7c+J(bjl;_j3 z@=&Tn))6+0!rU1`OAjV|x2L5C6Mlri5l>4Gr?7FaucdEzTH0r^lwovuNK22Pu;#k& zubuL?Ty`E25-83_QkXZ(kFMha-}L)d9!Yh;%Vnb|%nx~XzQdL@N~!Z$DjQ8% z$GiRu_}CEd#!}fh3dd0{Y&MkezAR?3U-MO-{0t@h7=dFX8rK}DaRE_r*?0;aC8uLT zMw{Ds8ZHr;Kw-PgX4MsWbw5Q?WdaRlt0q!7vr?eC!wBYQY9-?z#|a!K(Gv(!O`>pC zheMQwn@A@Gwk(xRrZDK9a5sk&-Vrt6pLi25f*DTuNdhM^BdqDr$uy9Jb=edO`#Sh8 zb#IImo{&UMniAObsT79X6_#fN;XS-fZ|2c+1mUL$oFdUbYLlHB+`tIP%1yO~Pf#^nO<&FW>9D`$LIXd1?T(hQh7#zjKF46SR?JN zU#5WeR$I-?;A&#k*enXijtK2GlJI*@%x0qy01{5-NWxDOI8CDQT1oix83JceTSwSz z3fE7Gr$!Tg%j-Rw@UsNYdL8CaXnJ0B=s|c7RGQC7fqi@yolWPIQ=D@t-0UI}=wk?f zQz*_cgr6gDjzrI+t)E9>*>F)hlE$}I4iH zy$%xyzf9mVc5vAe3av|p8%Ov^G_Bu~6sb0!E}#qPBKqQDx+Jti zE?Y`rW(WcBnI(B?&?GOTaQV2fp}*lGm7=hMWx<}yDNL0S=pocWPe@NSX?cKCS5UaL zUx@kZyghbQ_GLwRPkJSVlXnGf`6qrKH(i<4m0^ipMPci9;f>Vg10LggLBJ~N)j_yy zHHG=1go&^AlUD~ekSweTR{=wG@{0_Ol_+3-n?gg=GnSEPRQl zrPhnArR%~P*=6e~^xPyS{xUy_Zj{P{uctE9-9X`Jz}HfpHY7T2q_9;oZydKjDzf0h*-(xR@7PH#mTV@cxgNnDzAp9PIdn9@v z-?EQF+cwRx$4tV9U};Sf{`G*s1Fw2vKZUb5e2cLYEk+Hw{m<^8JLxXEo4&Y*?xp+a zei}k^U3P%NggZhnd?-?BnqD|SgdL?Yzok&hvj}hM5q1{g4+%UZ z(NY?VuwxXa9PWsOolW=!)XK^DR}%_NXmmP~{y2p$lSg5XIfTzdHcG<3no?*=qd!B) zlfvSW0t=r@_@zq1GR`Hu8HHwEhm#a~&lg_tJY+7>VIJYlDKw|i@5NkpibCf=uJUQQ zd6A>^7(Gr;(3A8O{fW$1o~AHQ7A>woa9!bKr>PFDS7m1?^hW2vLJ9K(?jOQO@Ey0x zX96M?VP`3{Y&H&;JD>0kmBnP|6W)SC3vaULD69x1OO;YOOV0(j_B@4~^VgyiRhu8g zYED-!jGb4@xj>=)5MMRlMNJQKb}1L=(}9wk2)jt3)wpIjzXgQv!VOEpzgkjgNuxzE zmtCUJeU_hTT7v+D0)j5mOH@xQx$H89GYID+jDHc`LZyZ;)8D_{=tYDjuTU6rT`2U0 zgnulgb0OiaD72!{*2qLxDa_f}8t1f#@RlBnEF!!Og*G($$5@12qtI!y=&+dZQC^3| zgtw*8*6VPcLf`v>QI`;Y)9bK=@OBj1VF#Dpps;$UUoU?HSM_~K$=DTom0qLQ=?&VR zbig8|@<<+MHz|yfcI{hS*Y`JRFsJXbTNK7hEgQmCZ_$7$i998RuW|zO64(xBw<)ZX zFML55(mNDxB>>aSD7vaP#&mW&J;R( z9a3o!GJ|q#jxuM|EW+*>k02cp$B%*;;9x!OQV(1+Gu06HQE_3 zwl_K$vFfa&0qsW3M#OC?;hlBSdXe8i-&`f9IvRukU!DF&MYVj%akvk`#OTvld4sjOS=?MbHO&oU!em+G=E28;~UJF1Cv(I%40x*BjB zeQldZ}L9~ z=JmavD&36$8W3SU3}|vvfEpVUbpjg+??s^(jn<4sSWg2UcpWw+(6mj2_omR>>(I-9 zBP}KCO+fRdY+Vndr_oE>aF_KqVAUPp%iD%7;**lN$-Rw$m)FODn=)S};Nn!N=6N5Z zT)wQY0pk!R!-L3EF>Y0P_P*f_?q|SW8KdvQ7+pnS{R|bM_cvgz41CTak0~FczYz$0 zI2&L<3whR~j-a??uK_`dS7QSW*y(}H9#&=}#C#D+q3AZy2!hEj8)QHW2^So}cuv*h z5ArxA!Uh|#Gyr^R0o7n53~UZD;C2WH^5>j1MB`k}h8l3Shkv5%_W`$UsG%2aM%XX| z4h@po$PGv`m9;och8bbg>ayVmOqDdovP+G|!-F(VWg`q&d$|k^jR@Opwwdq&=%pm% zUwtU_pdZ6&-N_DI6N`cdddqrbwUOalgw5x(Dx-pK~VildCt#u%l?B5bSy zyP8Wwv$ZluOmdt72W6@I{|I{f(p!0~F;3eVmyI`|eSbf@u@l#+vK!-#P(iTVix-of8Z^78{z#a^rz99wUV%P z0EGdl!PH_W8an)`z3r*0!9?gJ1Fp&91w7KNb)97BT{)XRmR~fEiLZZAF1p-rLlmZklGm#>>r-6}{7k5ia-0 znP$jS2Gb1~9D?}$$tFz?Y~u_A+62&VMZ+0J$T-b3U{FY4{6VYC3=7OG19oG`XR#fG ze-yJZ-V+D>lkkUu6b7P;`nVjR(kvqp-bL7K10Hy4Y5?I^kQxdbpIuHp%`sr_Nxu+z zEZXgeB==);j4VI77h!V^Xg5w&|Li3En&(gKBzzEsK@>|$T{h2vPFva`Wk2#tK2%NI zTw|W$kL0rX29(Kg?I_Of8*&*>%r~AcAAF>;1qKYl)2}w$MR+GBaulmQUuzfPgDDKA zB1a*GZGmo7wJj-EYPZmUHG%j(q9W&o#v>x3tbc~E7R(bx4&k`w1j9{$E*-`^0OGEW>z)&qU0-XisG-Pgx~iH z!qynj(kuMkLwGx{@OKa4Ln#c!r?jAFm9g4bV~AR>wFZooXWpiwucM%+wT71RjIebE zbQmpIeJ|n7JXYUJ_%I5?1gl5bdIKKr>VVVUNBEuU!gtJXVar($7P!h*nZjHj$__-_+pZ>*$CFtxNM67n`CD^e4^R(Ek;P4Z8c!aC4XR1 z4X0G!)?i=Gwi&QuR&y*T|6H61P*wihbnb~LEN*Q%RAIXTbME->I~ZKZ_8?zHs!J(? zDTa(aL!c#RI}F$`NgR)Q&p#+5>9WK4G$c#E2r?JZrQNEWV(5N&b7{t45w*f~dw?zedh={8DWc*_^h0(%)aoHXNCXV#o zlk2Fe6$|e+_9*8hmF+d4C2o$*4iml-Ll(lvdE#}L@G%s|AUYYTDtWBR-r$zGY@Y#J zD#-Qh)3un(_8YLS0;bs?95t04FrZyKQ6O-H@b0L|8_6>8BUrf!V`-oa{6N6FaoIrw z2FtpImO&vts0(o_J7mDp1R~N5{U5KgP*VjHAXeBUBE3+<;pvmt!n_ zlyE!=RVx|)8b@IqjgF@=mz^+RoGdCFfJmq+={#qH6@(N$D9>?xzf%Smx| z+JK|d!)p{UP^XPRg}uwp7%;E`zv)bnXIysHfUd~P7$1)J1m9m#%Gr>ea@jcp9?0c~ z9MW@v<-6>>0UbjbJzoPncHWQ~J(pcDU}We}m;d@I7mQN9MA~H+4d^6Yo12JGDc9!W zqt){z15Qh)@G`~$D(t#sl$9$M*EbZO> z=?&wiq4``cyJbMXkZbSndeW_cYk%8-9ibrGr*zWoK*u`<48n>JNXs`$P{!$axt(J+ip2(@nuPZet1+qZZSQ8U& zoe^5`6y|y$?sR<)b3(WEi;l0plO2WUUQYLJ{^cTBdnzfZ4!z7en=4&!?!epMS?MxwKAaYte#9gf|y7hh=6LQ*+li>uSO# zsnqertkTq7%~xO3wgXr<6ZUyYkic~ynjYz9is}~4ckdi5_DE&jO_+fuHC&mf?QM56 z931p8p-djlo`7+?V)q{A4ia){dYUlmoGj4ljLxdk7d_2jRV%PwCd`pofAFAye=e~~ zFEbE-RA;?S=+j-K_RbN$`Ux+!*C~+NJ4g6T3NsNZQS9A2$Rd4A7%5%4jW|u^s`W9C z8X+;R&ia~g7Y}CH=o0xQ*g^pW9& zSbr1t2lD=^EW5w?6PfKFU_#sTLI_Gc7ejH^05fy|exL~lI|z$$p77ra`+uJBSrleb zJQy5dgG^YzSy#Jg=T8ULD1VYUc`B5be;L$`_^7x4^<7~>-0b12O5dkit5-BA(w zULt%uCQ_2|uelWFQZMKqYQlmkEzxXTCVZG@2rd&okHS0}&5OBgmUnAHiwum4mF2`EJB10H=$3$)ZN9x|AeU%C{x}_Rp!r76c z*zXNluH|=38Dj<&o6E+UFh{>TN?XuaeL)d6&V+7>@|ZcOF_pF-7qs%4`0q7B;_tHY zCM=LvV}Lh~PnwkR<*dd86L!xOw?cl#%e*@;CYo?o9(57NG*t#?f;mwaVa_I*&axPK((7m;5+ZMkoa^*_>hyBLThRvZ*F)lpgyz zT$!5L)POiexU55SFg=YKbk3%kutgf_pK{a|{Z#GK82E3=mMc6WvUq>+e#C*p+|zAX@BHe<#epn zhKRXrl?lBhmKiGTSrxGUSfXsgHLO0sBAxw=>)z;U&l`2w8WR>si{+>0O

!&Eu7l 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 z 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