Exemplo n.º 1
0
func main() {
	logrus.Println("---- Ulule CLI ----")

	linenoise.LoadHistory("/var/linenoise_history")

	username, apikey := credentials.Get(linenoise.Line)

	ululeClient := clientapi.New(username, apikey)

	linenoise.SetCompletionHandler(completionHandler)

	for {
		cmd, err := linenoise.Line("> ")
		if err != nil {
			logrus.Fatal(err)
		}

		linenoise.AddHistory(cmd)
		linenoise.SaveHistory("/var/linenoise_history")

		args := strings.Split(cmd, " ")

		if len(args) > 0 {
			switch args[0] {
			case "project":
				if len(args) > 1 {
					switch args[1] {
					case "list":
						projects, err := ululeClient.GetProjects("created")
						if err != nil {
							logrus.Fatal(err)
						}
						// logrus.Printf("projects: %#v", projects[0])
						for _, project := range projects {
							percentage := int(float32(project.AmountRaised) / float32(project.Goal) * 100.0)
							percentageStr := strconv.Itoa(percentage)
							fmt.Println(project.Id, "|", project.Slug, "|", project.AmountRaised, project.CurrencyDisplay, "|", percentageStr+"%")
						}
					case "select":
						if len(args) > 2 {
							selectedProject, err = ululeClient.GetProject(args[2])
							if err != nil {
								fmt.Println(err.Error())
							} else {
								fmt.Println("project selected:", selectedProject.Id, "|", selectedProject.Slug)
							}
						} else {
							fmt.Println("error: `project select` expects a project id or slug argument")
						}
					case "supporters":
						if selectedProject == nil {
							fmt.Println("error: `project supporters` needs one project to be selected with `project select`")
						} else {
							offset := 0
							limit := 20
							for {
								linenoise.Clear()
								supporters, err, lastPage := ululeClient.GetProjectSupporters(int(selectedProject.Id), limit, offset)
								if err != nil {
									fmt.Println(err.Error())
									break
								} else {
									for _, supporter := range supporters {
										fmt.Println(supporter.Id, "|", supporter.UserName, "|", supporter.FirstName, supporter.LastName)
									}
									if lastPage {
										break
									} else {
										str, err := linenoise.Line("`enter` for next page, 'q' to exit> ")
										if err != nil {
											logrus.Fatal(err)
										}
										if str == "q" {
											break
										}
										offset += limit
									}
								}
							}
						}
					case "orders":
						if selectedProject == nil {
							fmt.Println("error: `project orders` needs one project to be selected with `project select`")
						} else {
							offset := 0
							limit := 20
							for {
								linenoise.Clear()
								orders, err, lastPage := ululeClient.GetProjectOrders(int(selectedProject.Id), limit, offset)
								if err != nil {
									fmt.Println(err.Error())
									break
								} else {
									for _, order := range orders {
										fmt.Println(int(order.Id), "|", order.Total, selectedProject.CurrencyDisplay, "|", order.StatusDisplay, "("+strconv.Itoa(int(order.Status))+")", "|", order.User.Email)
									}
									if lastPage {
										break
									} else {
										str, err := linenoise.Line("`enter` for next page, 'q' to exit> ")
										if err != nil {
											logrus.Fatal(err)
										}
										if str == "q" {
											break
										}
										offset += limit
									}
								}
							}
						}
					}
				}
			}
		}
	}
}
Exemplo n.º 2
0
func main() {
	username, apikey := credentials.Get(linenoise.Line)
	ululeClient := clientapi.New(username, apikey)

	projects, err := ululeClient.GetProjects("created")
	if err != nil {
		logrus.Fatal(err)
	}
	for _, project := range projects {
		percentage := int(float32(project.AmountRaised) / float32(project.Goal) * 100.0)
		percentageStr := strconv.Itoa(percentage)
		fmt.Println(project.Id, "|", project.Slug, "|", project.AmountRaised, project.CurrencyDisplay, "|", percentageStr+"%")
	}

	projectIdStr, err := linenoise.Line("project id> ")
	if err != nil {
		logrus.Fatal(err)
	}

	projectId, err := strconv.Atoi(projectIdStr)
	if err != nil {
		logrus.Fatal(err)
	}

	// Allows to simply sync Ulule orders several
	// times under specific names
	syncName, err := linenoise.Line("sync name> ")
	if err != nil {
		logrus.Fatal(err)
	}

	// leave blank to sync them all
	// otherwise, only invalid orders from a previous sync will
	// be considered
	syncOnlyInvalids := false
	invalidOrdersSyncName, err := linenoise.Line("only invalid orders from sync> ")
	if err != nil {
		logrus.Fatal(err)
	}

	conn, err := redis.Dial("tcp", "127.0.0.1:6379")
	if err != nil {
		logrus.Fatal(err)
	}

	invalidOrders := make(map[string]struct{})

	if invalidOrdersSyncName != "" {
		syncOnlyInvalids = true
		invalids, errInvalidOrders := redis.Strings(redis.Values(conn.Do("SMEMBERS", invalidOrdersSyncName+"_invalidOrders")))
		if errInvalidOrders != nil {
			logrus.Fatal(errInvalidOrders)
		}
		for _, invalid := range invalids {
			invalidOrders[invalid] = struct{}{}
		}
	}

	// store syncName
	_, err = conn.Do("SADD", "syncs", syncName)
	if err != nil {
		logrus.Fatal(err)
	}

	// get projects information
	project, err := ululeClient.GetProject(strconv.Itoa(projectId))
	if err != nil {
		logrus.Fatal(err)
	}

	err = conn.Send("MULTI")
	if err != nil {
		logrus.Fatal(err)
	}

	logrus.Println("sync project \"" + project.Slug + "\"")

	err = conn.Send("HMSET", syncName+"_project",
		"id", project.Id,
		"url", project.Url,
		"goal", project.Goal,
		"goalRaised", project.GoalRaised,
		"amountRaised", project.AmountRaised,
		"commentCount", project.CommentCount,
		"committed", project.Committed,
		"currency", project.Currency,
		"currencyDisplay", project.CurrencyDisplay,
		"dateEnd", project.DateEnd,
		"dateStart", project.DateStart,
		"finished", project.Finished,
		"slug", project.Slug,
		"supportersCount", project.SupportersCount,
		"timeZone", project.TimeZone,
		"nbrewards", len(project.Rewards),
	)
	if err != nil {
		logrus.Fatal(err)
	}

	for i, reward := range project.Rewards {
		index := strconv.Itoa(i)
		err = conn.Send("HMSET", syncName+"_project",
			"reward"+index+"_id", reward.Id,
			"reward"+index+"_available", reward.Available,
			"reward"+index+"_price", reward.Price,
			"reward"+index+"_stock", reward.Stock,
			"reward"+index+"_stockAvailable", reward.StockAvailable,
			"reward"+index+"_stockTaken", reward.StockTaken,
		)
		if err != nil {
			logrus.Fatal(err)
		}

		logrus.Println("sync reward \"" + strconv.Itoa(int(reward.Id)) + " (" + strconv.Itoa(reward.Price) + " " + project.CurrencyDisplay + ")\"")
	}

	_, err = conn.Do("EXEC")
	if err != nil {
		logrus.Fatal(err)
	}

	// sync orders
	offset := 0
	limit := 1000

	lastPage := false
	orders := make([]*clientapi.Order, 0)

	for !lastPage {
		var newOrders []*clientapi.Order
		newOrders, err, lastPage = ululeClient.GetProjectOrders(projectId, limit, offset)
		if err != nil {
			logrus.Fatal(err)
		}
		offset += len(newOrders)
		orders = append(orders, newOrders...)
		logrus.Println("orders:", offset)
	}

	// TODO: get supporters as well to be able to anotate anonymous users
	// anonymous = user ids in orders - user ids in supporters
	// in other words, if a user id from orders can't be found in the
	// list of supporters, it means the user wants to be anonymous

	for _, order := range orders {

		if syncOnlyInvalids == true {
			_, ok := invalidOrders[strconv.Itoa(int(order.Id))]
			if ok == false {
				continue
			}
		}

		// don't save cancelled orders
		if order.Status != clientapi.OrderStatusCancelled {

			err := conn.Send("MULTI")
			if err != nil {
				logrus.Fatal(err)
			}

			err = conn.Send("SADD", syncName, strconv.Itoa(int(order.Id)))
			if err != nil {
				logrus.Fatal(err)
			}

			// order.User.FirstName & order.User.LastName are not reliable
			// as these entries are totally optional, even for paying supporters
			// it's better to rely on order.ShippingAddress.FirstName &
			// order.ShippingAddress.LastName. If order.ShippingAddress doesn't
			// exist, keep empty strings for first and last names.

			firstName := ""
			lastName := ""

			shippingAddress := &clientapi.Address{}
			if order.ShippingAddress != nil {
				shippingAddress = order.ShippingAddress
				firstName = shippingAddress.FirstName
				lastName = shippingAddress.LastName

				// get rid of extra lines in Address1
				shippingAddress.Address1 = strings.Trim(shippingAddress.Address1, " \n\r")
				shippingAddress.Address1 = strings.Replace(shippingAddress.Address1, "\n", " ", -1)
				shippingAddress.Address1 = strings.Replace(shippingAddress.Address1, "\r", " ", -1)
				shippingAddress.Address1 = strings.Replace(shippingAddress.Address1, "  ", " ", -1)

				// get rid of extra lines in Address2
				shippingAddress.Address2 = strings.Trim(shippingAddress.Address2, " \n\r")
				shippingAddress.Address2 = strings.Replace(shippingAddress.Address2, "\n", " ", -1)
				shippingAddress.Address2 = strings.Replace(shippingAddress.Address2, "\r", " ", -1)
				shippingAddress.Address2 = strings.Replace(shippingAddress.Address2, "  ", " ", -1)

				// Apparenlty, some people think address2 is a confirmation of address1
				// so they put the exact same thing on both sides
				// in this case, just replacing address2 by empty string
				if shippingAddress.Address2 == shippingAddress.Address1 {
					shippingAddress.Address2 = ""
				}
			}

			// quick format for first & last name

			// first name
			firstName = strings.ToLower(firstName)
			firstName = strings.Trim(firstName, " -")
			// split on ' '
			firstNameParts := strings.Split(firstName, " ")
			for i, part := range firstNameParts {
				if len(part) > 1 {
					firstNameParts[i] = strings.ToUpper(string(part[0])) + part[1:]
				} else if len(part) > 0 {
					firstNameParts[i] = strings.ToUpper(string(part[0]))
				}
			}
			firstName = strings.Join(firstNameParts, " ")
			// split on '-'
			firstNameParts = strings.Split(firstName, "-")
			for i, part := range firstNameParts {
				if len(part) > 1 {
					firstNameParts[i] = strings.ToUpper(string(part[0])) + part[1:]
				} else if len(part) > 0 {
					firstNameParts[i] = strings.ToUpper(string(part[0]))
				}
			}
			firstName = strings.Join(firstNameParts, "-")
			firstName = strings.Trim(firstName, " ")

			// last name
			lastName = strings.ToLower(lastName)
			lastName = strings.Trim(lastName, " -")
			// split on ' '
			lastNameParts := strings.Split(lastName, " ")
			for i, part := range lastNameParts {
				if len(part) > 1 {
					lastNameParts[i] = strings.ToUpper(string(part[0])) + part[1:]
				} else if len(part) > 0 {
					lastNameParts[i] = strings.ToUpper(string(part[0]))
				}
			}
			lastName = strings.Join(lastNameParts, " ")
			// split on '-'
			lastNameParts = strings.Split(lastName, "-")
			for i, part := range lastNameParts {
				if len(part) > 1 {
					lastNameParts[i] = strings.ToUpper(string(part[0])) + part[1:]
				} else if len(part) > 0 {
					lastNameParts[i] = strings.ToUpper(string(part[0]))
				}
			}
			lastName = strings.Join(lastNameParts, "-")
			lastName = strings.Trim(lastName, " ")

			// TODO: improve formating considering dashes and other name patterns

			billingAddress := shippingAddress
			if order.BillingAddress != nil {
				billingAddress = order.BillingAddress

				// get rid of extra lines in Address1
				billingAddress.Address1 = strings.Trim(billingAddress.Address1, " \n\r")
				billingAddress.Address1 = strings.Replace(billingAddress.Address1, "\n", " ", -1)
				billingAddress.Address1 = strings.Replace(billingAddress.Address1, "\r", " ", -1)
				billingAddress.Address1 = strings.Replace(billingAddress.Address1, "  ", " ", -1)

				// get rid of extra lines in Address2
				billingAddress.Address2 = strings.Trim(billingAddress.Address2, " \n\r")
				billingAddress.Address2 = strings.Replace(billingAddress.Address2, "\n", " ", -1)
				billingAddress.Address2 = strings.Replace(billingAddress.Address2, "\r", " ", -1)
				billingAddress.Address2 = strings.Replace(billingAddress.Address2, "  ", " ", -1)

				// Apparenlty, some people think address2 is a confirmation of address1
				// so they put the exact same thing on both sides
				// in this case, just replacing address2 by empty string
				if billingAddress.Address2 == billingAddress.Address1 {
					billingAddress.Address2 = ""
				}
			}

			// if len(order.Items) != 1 {
			// 	fmt.Println("items:", len(order.Items), "url:", order.Url, "|", order.Total, "|", order.StatusDisplay, "|", order.User.UserName)
			// }

			err = conn.Send("HMSET", syncName+"_order_"+strconv.Itoa(int(order.Id)),

				// repeating hash key in values for flexibility
				"orderId", strconv.Itoa(int(order.Id)),

				"email", order.User.Email,
				"firstName", firstName,
				"lastName", lastName,
				"name", order.User.Name,
				"username", order.User.UserName,
				"datejoined", order.User.DateJoined,
				"userurl", order.User.Url,
				"userid", order.User.Id,
				// TODO: add anonymous field, value: 0 or 1

				"total", order.Total,
				"method", order.PaymentMethod,
				"status", order.Status,
				"statusDisplay", order.StatusDisplay,
				"url", order.Url,

				"shippingAddr1", shippingAddress.Address1,
				"shippingAddr2", shippingAddress.Address2,
				"shippingCity", shippingAddress.City,
				"shippingCountry", shippingAddress.Country,
				"shippingCode", shippingAddress.PostalCode,
				"shippingState", shippingAddress.State,
				"shippingPhoneNumber", shippingAddress.PhoneNumber,
				"shippingEntityName", shippingAddress.EntityName,

				"billingAddr1", billingAddress.Address1,
				"billingAddr2", billingAddress.Address2,
				"billingCity", billingAddress.City,
				"billingCountry", billingAddress.Country,
				"billingCode", billingAddress.PostalCode,
				"billingState", billingAddress.State,
				"billingPhoneNumber", billingAddress.PhoneNumber,
				"billingEntityName", billingAddress.EntityName,

				"nbItems", len(order.Items),
			)
			if err != nil {
				logrus.Fatal(err)
			}

			for i, item := range order.Items {
				index := strconv.Itoa(i)
				err = conn.Send("HMSET", syncName+"_order_"+strconv.Itoa(int(order.Id)),
					"item"+index+"_quantity", item.Quantity,
					"item"+index+"_unitprice", item.UnitPrice,
					"item"+index+"_product", item.Product,
				)
			}

			// Put invalid orders in a set.
			// Invalid orders are the ones that can't be sent as there's something
			// incorrect with the shipping address. Basically if some fields are empty
			// This allows to build a new list later based on invalid orders that
			// got fixed by contributors.

			if (shippingAddress.Address1 == "" && shippingAddress.Address2 == "") || firstName == "" || lastName == "" || shippingAddress.City == "" || shippingAddress.Country == "" || shippingAddress.PostalCode == "" {
				conn.Send("SADD", syncName+"_invalidOrders", strconv.Itoa(int(order.Id)))
			}

			_, err = conn.Do("EXEC")
			if err != nil {
				logrus.Fatal(err)
			}
		}
	}

	nb, err := conn.Do("SCARD", syncName)
	if err != nil {
		logrus.Fatal(err)
	}
	logrus.Println("nb orders:", nb)
}