From 4b58ff6da8933fa6e14ede3940d4a30aea1c1713 Mon Sep 17 00:00:00 2001 From: Tom Victor Date: Fri, 16 Feb 2024 08:03:12 +0100 Subject: [PATCH 1/5] SKILL-13: complete user listing --- handlers/user.go | 11 +++++++++++ managers/user.go | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/handlers/user.go b/handlers/user.go index 1fb9abd..ad42183 100644 --- a/handlers/user.go +++ b/handlers/user.go @@ -24,6 +24,7 @@ func NewUserHandlerFrom(userManager *managers.UserManager) *UserHandler { func (userHandler *UserHandler) RegisterUserApis(r *gin.Engine) { userGroup := r.Group(userHandler.groupName) userGroup.POST("", userHandler.Create) + userGroup.GET("", userHandler.List) } func (userHandler *UserHandler) Create(ctx *gin.Context) { @@ -44,3 +45,13 @@ func (userHandler *UserHandler) Create(ctx *gin.Context) { ctx.JSON(http.StatusOK, newUser) } +func (userHandler *UserHandler) List(ctx *gin.Context) { + + allUsers, err := userHandler.userManager.List() + + if err != nil { + fmt.Println("failed to get users") + } + + ctx.JSON(http.StatusOK, allUsers) +} diff --git a/managers/user.go b/managers/user.go index 23a4141..cf2ce1c 100644 --- a/managers/user.go +++ b/managers/user.go @@ -26,3 +26,9 @@ func (userMgr *UserManager) Create(userData *common.UserCreationInput) (*models. return newUser, nil } + +func (userMgr *UserManager) List() ([]models.User, error) { + users := []models.User{} + database.DB.Find(&users) + return users, nil +} From 6a71fb850c12f2ba2d621128a08d0b4b5c4c2f34 Mon Sep 17 00:00:00 2001 From: Tom Victor Date: Fri, 16 Feb 2024 08:13:14 +0100 Subject: [PATCH 2/5] SKILL-14: complete user detail api --- handlers/user.go | 18 ++++++++++++++++++ managers/user.go | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/handlers/user.go b/handlers/user.go index ad42183..db40b11 100644 --- a/handlers/user.go +++ b/handlers/user.go @@ -25,6 +25,7 @@ func (userHandler *UserHandler) RegisterUserApis(r *gin.Engine) { userGroup := r.Group(userHandler.groupName) userGroup.POST("", userHandler.Create) userGroup.GET("", userHandler.List) + userGroup.GET(":userid/", userHandler.Detail) } func (userHandler *UserHandler) Create(ctx *gin.Context) { @@ -45,6 +46,7 @@ func (userHandler *UserHandler) Create(ctx *gin.Context) { ctx.JSON(http.StatusOK, newUser) } + func (userHandler *UserHandler) List(ctx *gin.Context) { allUsers, err := userHandler.userManager.List() @@ -55,3 +57,19 @@ func (userHandler *UserHandler) List(ctx *gin.Context) { ctx.JSON(http.StatusOK, allUsers) } + +func (userHandler *UserHandler) Detail(ctx *gin.Context) { + + userId, ok := ctx.Params.Get("userid") + + if !ok { + fmt.Println("invalid userid") + } + user, err := userHandler.userManager.Get(userId) + + if err != nil { + fmt.Println("failed to get user") + } + + ctx.JSON(http.StatusOK, user) +} diff --git a/managers/user.go b/managers/user.go index cf2ce1c..9c2b0af 100644 --- a/managers/user.go +++ b/managers/user.go @@ -32,3 +32,11 @@ func (userMgr *UserManager) List() ([]models.User, error) { database.DB.Find(&users) return users, nil } + +func (userMgr *UserManager) Get(id string) (models.User, error) { + user := models.User{} + + database.DB.First(&user, id) + + return user, nil +} From 0e632b98f2e15c055adc47bba1323e713092a86d Mon Sep 17 00:00:00 2001 From: Tom Victor Date: Fri, 16 Feb 2024 08:54:39 +0100 Subject: [PATCH 3/5] SKILL-15: complete user deletion --- common/user.go | 31 +++++++++++++++++++++++++++++++ handlers/user.go | 32 +++++++++++++++++++++++++++++--- managers/user.go | 8 ++++++++ test.db | Bin 16384 -> 16384 bytes 4 files changed, 68 insertions(+), 3 deletions(-) diff --git a/common/user.go b/common/user.go index 86a57c8..1a2e398 100644 --- a/common/user.go +++ b/common/user.go @@ -1,5 +1,11 @@ package common +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + type UserCreationInput struct { FullName string `json:"full_name"` Email string `json:"email"` @@ -8,3 +14,28 @@ type UserCreationInput struct { func NewUserCreationInput() *UserCreationInput { return &UserCreationInput{} } + +type requestResponse struct { + Message string `json:"message"` + Status uint `json:"status"` +} + +func SuccessResponse(ctx *gin.Context, msg string) { + + response := requestResponse{ + msg, + http.StatusOK, + } + + ctx.JSON(http.StatusOK, response) +} + +func BadResponse(ctx *gin.Context, msg string) { + + response := requestResponse{ + msg, + http.StatusBadRequest, + } + + ctx.JSON(http.StatusBadRequest, response) +} diff --git a/handlers/user.go b/handlers/user.go index db40b11..79c9533 100644 --- a/handlers/user.go +++ b/handlers/user.go @@ -26,6 +26,7 @@ func (userHandler *UserHandler) RegisterUserApis(r *gin.Engine) { userGroup.POST("", userHandler.Create) userGroup.GET("", userHandler.List) userGroup.GET(":userid/", userHandler.Detail) + userGroup.DELETE(":userid/", userHandler.Delete) } func (userHandler *UserHandler) Create(ctx *gin.Context) { @@ -35,13 +36,15 @@ func (userHandler *UserHandler) Create(ctx *gin.Context) { err := ctx.BindJSON(&userData) if err != nil { - fmt.Println("Failed to bind data") + common.BadResponse(ctx, "Failed to bind data") + return } newUser, err := userHandler.userManager.Create(userData) if err != nil { - fmt.Println("failed to create user") + common.BadResponse(ctx, "failed to create user") + return } ctx.JSON(http.StatusOK, newUser) @@ -52,7 +55,8 @@ func (userHandler *UserHandler) List(ctx *gin.Context) { allUsers, err := userHandler.userManager.List() if err != nil { - fmt.Println("failed to get users") + common.BadResponse(ctx, "failed to get users") + return } ctx.JSON(http.StatusOK, allUsers) @@ -67,9 +71,31 @@ func (userHandler *UserHandler) Detail(ctx *gin.Context) { } user, err := userHandler.userManager.Get(userId) + if user.ID == 0 { + + common.BadResponse(ctx, "no user present") + return + } + if err != nil { fmt.Println("failed to get user") } ctx.JSON(http.StatusOK, user) } + +func (userHandler *UserHandler) Delete(ctx *gin.Context) { + + userId, ok := ctx.Params.Get("userid") + + if !ok { + fmt.Println("invalid userid") + } + err := userHandler.userManager.Delete(userId) + + if err != nil { + fmt.Println("failed to get user") + } + + common.SuccessResponse(ctx, "Deleted user") +} diff --git a/managers/user.go b/managers/user.go index 9c2b0af..d0493af 100644 --- a/managers/user.go +++ b/managers/user.go @@ -40,3 +40,11 @@ func (userMgr *UserManager) Get(id string) (models.User, error) { return user, nil } + +func (userMgr *UserManager) Delete(id string) error { + user := models.User{} + + database.DB.First(&user, id) + database.DB.Delete(&user) + return nil +} diff --git a/test.db b/test.db index b1acf072820eabf34cf9a13f7712f7168a34f129..7526bc2f0437fa28a14859a2ce9a94f49219b25b 100644 GIT binary patch delta 172 zcmZo@U~Fh$oFL68I8nx#QE+3zLVj+3I|e>3V+Q{DT*jMu1k}0M>zUXYe0_ZSXh}FSecmVnHX6bni@`Sl$YDQMXHO1g@J(~aq>es@y(I)D+Dx BCCLB) delta 115 zcmV-(0F3{DfB}Gj0gxL35Rn{10T8iZp$`iH000gOv;YsE4?eRD5GM+ekb$$b8j1u0 zUjmb%9~F~I8ZxtoA9@fY0000E54!*l{15jJ@(=9~=nvx$-VfOi)DO=O$`8d4zz@5# V5g?Zjle<4W2?PTG0RjU9012HHA|3z$ From 19572ca211b37ecb763e605ab28a5f82b2679cab Mon Sep 17 00:00:00 2001 From: Tom Victor Date: Fri, 16 Feb 2024 09:18:47 +0100 Subject: [PATCH 4/5] SKILL-18: complete update api --- common/user.go | 7 +++++++ handlers/user.go | 28 ++++++++++++++++++++++++++++ managers/user.go | 16 ++++++++++++++++ test.db | Bin 16384 -> 16384 bytes 4 files changed, 51 insertions(+) diff --git a/common/user.go b/common/user.go index 1a2e398..c91cb8f 100644 --- a/common/user.go +++ b/common/user.go @@ -10,10 +10,17 @@ type UserCreationInput struct { FullName string `json:"full_name"` Email string `json:"email"` } +type UserUpdateInput struct { + FullName string `json:"full_name"` + Email string `json:"email"` +} func NewUserCreationInput() *UserCreationInput { return &UserCreationInput{} } +func NewUserUpdateInput() *UserUpdateInput { + return &UserUpdateInput{} +} type requestResponse struct { Message string `json:"message"` diff --git a/handlers/user.go b/handlers/user.go index 79c9533..339bd2a 100644 --- a/handlers/user.go +++ b/handlers/user.go @@ -27,6 +27,7 @@ func (userHandler *UserHandler) RegisterUserApis(r *gin.Engine) { userGroup.GET("", userHandler.List) userGroup.GET(":userid/", userHandler.Detail) userGroup.DELETE(":userid/", userHandler.Delete) + userGroup.PATCH(":userid/", userHandler.Update) } func (userHandler *UserHandler) Create(ctx *gin.Context) { @@ -99,3 +100,30 @@ func (userHandler *UserHandler) Delete(ctx *gin.Context) { common.SuccessResponse(ctx, "Deleted user") } + +func (userHandler *UserHandler) Update(ctx *gin.Context) { + + userId, ok := ctx.Params.Get("userid") + + if !ok { + fmt.Println("invalid userid") + } + + userData := common.NewUserUpdateInput() + + err := ctx.BindJSON(&userData) + + if err != nil { + common.BadResponse(ctx, "Failed to bind data") + return + } + + user, err := userHandler.userManager.Update(userId, userData) + + if err != nil { + common.BadResponse(ctx, "failed to update user") + return + } + + ctx.JSON(http.StatusOK, user) +} diff --git a/managers/user.go b/managers/user.go index d0493af..f4b507a 100644 --- a/managers/user.go +++ b/managers/user.go @@ -41,6 +41,22 @@ func (userMgr *UserManager) Get(id string) (models.User, error) { return user, nil } +func (userMgr *UserManager) Update(userId string, userData *common.UserUpdateInput) (*models.User, error) { + + user := models.User{} + + database.DB.First(&user, userId) + + // user.FullName = userData.FullName + // user.Email = userData.Email + + // database.DB.Save(&user) + + database.DB.Model(&user).Updates(models.User{FullName: userData.FullName, Email: userData.Email}) + + return &user, nil +} + func (userMgr *UserManager) Delete(id string) error { user := models.User{} diff --git a/test.db b/test.db index 7526bc2f0437fa28a14859a2ce9a94f49219b25b..8c22d7da0cde6ec116cd15bd7a3de002e4b9992f 100644 GIT binary patch delta 133 zcmZo@U~Fh$oFL7pFj2;tQDI}kLVjMpy9|6>#ti)Pxs3TOHuDLb=VM`DV2GalP%fM^ zm6@Hv*Ox)kcJebhO=U9$14}DI3oAo&Jrh$SBMU=q14AnVgVN&EB11z6xw%)qRDkOf h10Vl12L3<%-#7CZJmcpBn#an_!py+P%*@Qd2>`%2AKw4~ delta 114 zcmZo@U~Fh$oFL68I8nx#QE+3zLVjL;I|e>3V+Q{DT*iENH}eUc=VOXzp1e?Abh5v^ z^kjZ{&B^Pe*jN}C7!oHxloOxqC#5%8O`uB92fuq From d02b57df1a4d348fe5b46a15ecd68f0a33f691e2 Mon Sep 17 00:00:00 2001 From: Tom Victor Date: Fri, 16 Feb 2024 13:19:00 +0100 Subject: [PATCH 5/5] SKILL-18: code refactoring --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 3 +- common/response.go | 32 +++++++++++++++++ common/user.go | 36 ++----------------- frontend/src/components/createUser.js | 6 ++-- frontend/src/components/userlist.js | 4 +-- handlers/user.go | 50 +++++++++++++------------- main.go | 4 +-- managers/user.go | 47 ++++++++++++------------ models/user.go | 16 ++++++--- {database => storage}/db.go | 4 +-- test.db | Bin 16384 -> 0 bytes 12 files changed, 108 insertions(+), 94 deletions(-) delete mode 100644 .DS_Store create mode 100644 common/response.go rename {database => storage}/db.go (86%) delete mode 100644 test.db diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 { data.map((item)=>( - + )) } diff --git a/handlers/user.go b/handlers/user.go index 339bd2a..c22ef71 100644 --- a/handlers/user.go +++ b/handlers/user.go @@ -11,26 +11,27 @@ import ( type UserHandler struct { groupName string - userManager *managers.UserManager + userManager managers.UserManager } -func NewUserHandlerFrom(userManager *managers.UserManager) *UserHandler { +func NewUserHandlerFrom(userManager managers.UserManager) *UserHandler { return &UserHandler{ "api/users", userManager, } } -func (userHandler *UserHandler) RegisterUserApis(r *gin.Engine) { - userGroup := r.Group(userHandler.groupName) - userGroup.POST("", userHandler.Create) - userGroup.GET("", userHandler.List) - userGroup.GET(":userid/", userHandler.Detail) - userGroup.DELETE(":userid/", userHandler.Delete) - userGroup.PATCH(":userid/", userHandler.Update) +func (handler *UserHandler) RegisterUserApis(r *gin.Engine) { + userGroup := r.Group(handler.groupName) + + userGroup.POST("", handler.Create) + userGroup.GET("", handler.List) + userGroup.GET(":userid/", handler.Detail) + userGroup.DELETE(":userid/", handler.Delete) + userGroup.PATCH(":userid/", handler.Update) } -func (userHandler *UserHandler) Create(ctx *gin.Context) { +func (handler *UserHandler) Create(ctx *gin.Context) { userData := common.NewUserCreationInput() @@ -41,7 +42,7 @@ func (userHandler *UserHandler) Create(ctx *gin.Context) { return } - newUser, err := userHandler.userManager.Create(userData) + newUser, err := handler.userManager.Create(userData) if err != nil { common.BadResponse(ctx, "failed to create user") @@ -51,9 +52,9 @@ func (userHandler *UserHandler) Create(ctx *gin.Context) { ctx.JSON(http.StatusOK, newUser) } -func (userHandler *UserHandler) List(ctx *gin.Context) { +func (handler *UserHandler) List(ctx *gin.Context) { - allUsers, err := userHandler.userManager.List() + allUsers, err := handler.userManager.List() if err != nil { common.BadResponse(ctx, "failed to get users") @@ -63,50 +64,49 @@ func (userHandler *UserHandler) List(ctx *gin.Context) { ctx.JSON(http.StatusOK, allUsers) } -func (userHandler *UserHandler) Detail(ctx *gin.Context) { +func (handler *UserHandler) Detail(ctx *gin.Context) { userId, ok := ctx.Params.Get("userid") if !ok { fmt.Println("invalid userid") } - user, err := userHandler.userManager.Get(userId) + user, err := handler.userManager.Get(userId) if user.ID == 0 { - common.BadResponse(ctx, "no user present") return } if err != nil { - fmt.Println("failed to get user") + common.BadResponse(ctx, "failed to get user") } ctx.JSON(http.StatusOK, user) } -func (userHandler *UserHandler) Delete(ctx *gin.Context) { +func (handler *UserHandler) Delete(ctx *gin.Context) { userId, ok := ctx.Params.Get("userid") if !ok { - fmt.Println("invalid userid") + common.BadResponse(ctx, "invalid userid") } - err := userHandler.userManager.Delete(userId) + err := handler.userManager.Delete(userId) if err != nil { - fmt.Println("failed to get user") + common.BadResponse(ctx, "failed to delete user") } common.SuccessResponse(ctx, "Deleted user") } -func (userHandler *UserHandler) Update(ctx *gin.Context) { +func (handler *UserHandler) Update(ctx *gin.Context) { userId, ok := ctx.Params.Get("userid") if !ok { - fmt.Println("invalid userid") + common.BadResponse(ctx, "failed to delete user") } userData := common.NewUserUpdateInput() @@ -114,11 +114,11 @@ func (userHandler *UserHandler) Update(ctx *gin.Context) { err := ctx.BindJSON(&userData) if err != nil { - common.BadResponse(ctx, "Failed to bind data") + common.BadResponse(ctx, "failed to bind data") return } - user, err := userHandler.userManager.Update(userId, userData) + user, err := handler.userManager.Update(userId, userData) if err != nil { common.BadResponse(ctx, "failed to update user") diff --git a/main.go b/main.go index e5e4449..077bdc1 100644 --- a/main.go +++ b/main.go @@ -3,14 +3,14 @@ package main import ( "fmt" - "github.com/buildfromzero/skill-map/database" "github.com/buildfromzero/skill-map/handlers" "github.com/buildfromzero/skill-map/managers" + "github.com/buildfromzero/skill-map/storage" "github.com/gin-gonic/gin" ) func init() { - database.Initialize() + storage.InitializeDatabase() } func main() { diff --git a/managers/user.go b/managers/user.go index f4b507a..1a0a34a 100644 --- a/managers/user.go +++ b/managers/user.go @@ -4,21 +4,30 @@ import ( "errors" "github.com/buildfromzero/skill-map/common" - "github.com/buildfromzero/skill-map/database" "github.com/buildfromzero/skill-map/models" + "github.com/buildfromzero/skill-map/storage" ) -type UserManager struct { +type UserManager interface { + Create(userData *common.UserCreationInput) (*models.User, error) + List() ([]models.User, error) + Get(id string) (models.User, error) + Update(userId string, userData *common.UserUpdateInput) (*models.User, error) + Delete(id string) error +} + +type userManager struct { + // DatabaseDriver // dbClient } -func NewUserManager() *UserManager { - return &UserManager{} +func NewUserManager() UserManager { + return &userManager{} } -func (userMgr *UserManager) Create(userData *common.UserCreationInput) (*models.User, error) { +func (userMgr *userManager) Create(userData *common.UserCreationInput) (*models.User, error) { newUser := &models.User{FullName: userData.FullName, Email: userData.Email} - database.DB.Create(newUser) + storage.DB.Create(newUser) if newUser.ID == 0 { return nil, errors.New("user creation failed") @@ -27,40 +36,34 @@ func (userMgr *UserManager) Create(userData *common.UserCreationInput) (*models. return newUser, nil } -func (userMgr *UserManager) List() ([]models.User, error) { +func (userMgr *userManager) List() ([]models.User, error) { users := []models.User{} - database.DB.Find(&users) + storage.DB.Find(&users) return users, nil } -func (userMgr *UserManager) Get(id string) (models.User, error) { +func (userMgr *userManager) Get(id string) (models.User, error) { user := models.User{} - database.DB.First(&user, id) + storage.DB.First(&user, id) return user, nil } -func (userMgr *UserManager) Update(userId string, userData *common.UserUpdateInput) (*models.User, error) { +func (userMgr *userManager) Update(userId string, userData *common.UserUpdateInput) (*models.User, error) { user := models.User{} - database.DB.First(&user, userId) - - // user.FullName = userData.FullName - // user.Email = userData.Email - - // database.DB.Save(&user) - - database.DB.Model(&user).Updates(models.User{FullName: userData.FullName, Email: userData.Email}) + storage.DB.First(&user, userId) + storage.DB.Model(&user).Updates(models.User{FullName: userData.FullName, Email: userData.Email}) return &user, nil } -func (userMgr *UserManager) Delete(id string) error { +func (userMgr *userManager) Delete(id string) error { user := models.User{} - database.DB.First(&user, id) - database.DB.Delete(&user) + storage.DB.First(&user, id) + storage.DB.Delete(&user) return nil } diff --git a/models/user.go b/models/user.go index 9b5b014..7ed6851 100644 --- a/models/user.go +++ b/models/user.go @@ -1,9 +1,17 @@ package models -import "gorm.io/gorm" +import ( + "time" + + "gorm.io/gorm" +) type User struct { - gorm.Model - FullName string - Email string + // gorm.Model + ID uint `gorm:"primarykey" json:"id"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` + FullName string `json:"fullName"` + Email string `json:"email"` } diff --git a/database/db.go b/storage/db.go similarity index 86% rename from database/db.go rename to storage/db.go index 7aad781..bb63b7b 100644 --- a/database/db.go +++ b/storage/db.go @@ -1,4 +1,4 @@ -package database +package storage import ( "github.com/buildfromzero/skill-map/models" @@ -8,7 +8,7 @@ import ( var DB *gorm.DB -func Initialize() { +func InitializeDatabase() { var err error DB, err = gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) if err != nil { diff --git a/test.db b/test.db deleted file mode 100644 index 8c22d7da0cde6ec116cd15bd7a3de002e4b9992f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI3zi%5i6vufw+luw$7@&g?WZ}@khV8>0kJSAr3dBu?z;NWGt^(8uf;-tq2LdFk zkxqdT6e!XybAhb=C%Uymmo8blbm`D8UAlBBPy{GClswt3q@&y}UD`+J-jRAFzmK0I z37vF$|Gm4tOpw#zXwc1whpr>TK(`1%2&rR59+#;kGqYvm(s9L^Ap7Zyx~yPYpCY_x zy_P3PKmZ5;0U!VbfB+Bx0zd!=00AHX1QdZ|v)tef#miT4QU=ILf-meKCI0m-oQ& zSUj1Cvy;W5d7;zUedlhAEY#gN>kh=WY`OWI8RbT!VSI8Y?=u(8O=+(EMKokX2Aq)I zStcHf5xIZ3*WNumBKxf)viqQOZ|^`hYPSwL+sVmDbf=?ENGfw#PYNcJ4^>{Vn0;&a>@A47$Dkw6IyXzDHKYL-@0bhXd;)>t_AS`or39wU;%g`l^~% zZ&p5sX$b^?01yBIKmZ5;0U+?dCh&2ojN0w?8$NX?+i~cQ%ZbY(#v{sY&kdO8Z#iz{ zIRDt+Bu)@~QrdeTNri?$mRcdVN90jks@ne!v`B9H*2i_|w`n-nR-JdEDmQ zrSxLJf9F^65~kv1#cfhF!|agB)YOGr^yey`Qt>n@6^#mySg4?ctjTClRMGi! z6{=KdQRR5OjQ01@jX7en7Zy=!`+mT=R%FqArAB-$AIai0=FN!9SrkwnXl4Q0&<&vC^rfnAm8>GlY>|$Fz+u42s4j;eh-?)HsAEm zH7z%}Fmi*)^=-x}4KCKaS^=&*wZ(-H8(KsjkG#-ka#aVKMa`n3zc_;FUaEeZH}J1W zz90bsAOHk_01yBIKmZ5;0U!VbfB+Bx0{xS7d)K?B;tAc+)7JiBF@Ax(T8UKWT z#6RHg@VEFI{0e`KzrtTgCnO*M1b_e#00KY&2mk>f00e*l5C8%|;9nr{rr9=T9~{s< q9aUN}kzpxQSEi;+Rhf!1>&jeHrmW1GGONm{fAFuGDrcG|TK@wI)(|ZK