예제 #1
0
// handleViewMealPlan handles HTTP requests for the meal plan viewer.
func handleViewMealPlan(w http.ResponseWriter, r *http.Request) {
	mpID, ok := getUint64Var(r, "mealplanid")
	if !ok {
		httpError(w, BadRequestError)
		return
	}

	var mp *mpdata.MealPlan

	err := mpdb.WithConnection(func(db *sql.DB) (err error) {
		return mpdb.WithTransaction(db, func(tx *sql.Tx) (err error) {
			mp, err = mpdb.GetMealPlan(tx, mpID)
			return err
		})
	})

	if err != nil {
		serverError(w, err)
		return
	}

	if mp == nil {
		httpError(w, NotFoundError)
		return
	}

	renderTemplate(w, "view-mp.html", mp)
}
예제 #2
0
// handleEditMealForm handles HTTP requests for the "edit meal" form.
func handleEditMealForm(w http.ResponseWriter, r *http.Request) {
	mealID, ok := getUint64Var(r, "mealid")
	if !ok {
		httpError(w, BadRequestError)
		return
	}

	var mt mpdata.MealWithTags

	err := mpdb.WithConnection(func(db *sql.DB) (err error) {
		return mpdb.WithTransaction(db, func(tx *sql.Tx) (err error) {
			mt, err = mpdb.GetMealWithTags(tx, mealID)
			return err
		})
	})

	if err != nil {
		serverError(w, err)
		return
	}

	if mt.Meal == nil {
		httpError(w, NotFoundError)
		return
	}

	renderTemplate(w, "edit-meal-form.html", mt)
}
예제 #3
0
// handleAddMealAction handles HTTP requests for submission of the "new meal"
// form.
func handleAddMealAction(w http.ResponseWriter, r *http.Request) {
	// Parse the POST request body
	err := r.ParseForm()
	if err != nil {
		serverError(w, err)
		return
	}

	// Create a MealWithTags value from the form fields
	mt := mpdata.MealWithTags{
		Meal: &mpdata.Meal{
			Name:      r.FormValue("name"),
			RecipeURL: r.FormValue("recipe"),
			Favourite: r.FormValue("favourite") != "",
		},
		Tags: r.Form["tags"],
	}

	// Add the record to the database
	err = mpdb.WithConnection(func(db *sql.DB) (err error) {
		return mpdb.WithTransaction(db, func(tx *sql.Tx) (err error) {
			return mpdb.AddMealWithTags(tx, mt)
		})
	})

	if err != nil {
		serverError(w, err)
		return
	}

	// Redirect to list of meals
	redirect(w, http.StatusSeeOther, "/meals?highlight="+strconv.FormatUint(mt.Meal.ID, 10))
}
예제 #4
0
// fetchMealPlans handles an API call to return a list of meal plans that
// overlap with a specified inclusive date range. Expected parameters: from, to.
// Returns: an array of meal plan objects.
func fetchMealPlans(params url.Values) (response JSONResponse) {
	from, err := time.Parse(mpdata.JSONDateFormat, params.Get("from"))
	if err != nil {
		return JSONResponse{Error: "Invalid or missing 'from' parameter"}
	}

	to, err := time.Parse(mpdata.JSONDateFormat, params.Get("to"))
	if err != nil {
		return JSONResponse{Error: "Invalid or missing 'to' parameter"}
	}

	var mps []*mpdata.MealPlan

	err = mpdb.WithConnection(func(db *sql.DB) (err error) {
		return mpdb.WithTransaction(db, func(tx *sql.Tx) (err error) {
			mps, err = mpdb.ListMealPlansBetween(tx, from, to)
			return err
		})
	})

	if err != nil {
		log.Printf("Database error: %s\n", err.Error())
		return JSONResponse{Error: "Database error"}
	}

	return JSONResponse{Success: mps}
}
예제 #5
0
// fetchSuggestions handles an API call to generate suggestions for a given date.
// Expected parameters: date. Returns: an array of suggestion objects.
func fetchSuggestions(params url.Values) (response JSONResponse) {
	mpID, err := strconv.ParseUint(params.Get("mealplanid"), 10, 64)
	if err != nil {
		return JSONResponse{Error: "Invalid or missing 'mealplanid' parameter"}
	}

	dateServed, err := time.Parse(mpdata.JSONDateFormat, params.Get("date"))
	if err != nil {
		return JSONResponse{Error: "Invalid or missing 'date' parameter"}
	}

	var suggs []*mpdata.Suggestion

	err = mpdb.WithConnection(func(db *sql.DB) (err error) {
		return mpdb.WithTransaction(db, func(tx *sql.Tx) (err error) {
			suggs, err = mpdb.GenerateSuggestions(tx, mpID, dateServed)
			return err
		})
	})

	if err != nil {
		log.Printf("Database error: %s\n", err.Error())
		return JSONResponse{Error: "Database error"}
	}

	return JSONResponse{Success: suggs}
}
예제 #6
0
// fetchMealList handles an API call to fetch a list of all meals in the
// database. Expected parameters: none. Returns: an array of meal/tags objects.
func fetchMealList(params url.Values) (response JSONResponse) {
	query := params.Get("query")
	var words []string

	if query != "" {
		words = wordRegexp.FindAllString(query, -1)
	}

	var mts []mpdata.MealWithTags

	err := mpdb.WithConnection(func(db *sql.DB) (err error) {
		return mpdb.WithTransaction(db, func(tx *sql.Tx) (err error) {
			if query == "" {
				mts, err = mpdb.ListMealsWithTags(tx, true)
			} else {
				mts, err = mpdb.SearchMealsWithTags(tx, words, true)
			}

			return err
		})
	})

	if err != nil {
		log.Printf("Database error: %s\n", err.Error())
		return JSONResponse{Error: "Database error"}
	}

	return JSONResponse{Success: mts}
}
예제 #7
0
// handleDeleteMealPlanAction handles HTTP requests for submission of the
// meal plan deletion form.
func handleDeleteMealPlanAction(w http.ResponseWriter, r *http.Request) {
	mpID, ok := getUint64Var(r, "mealplanid")
	if !ok {
		httpError(w, BadRequestError)
		return
	}

	err := mpdb.WithConnection(func(db *sql.DB) (err error) {
		return mpdb.WithTransaction(db, func(tx *sql.Tx) (err error) {
			err = mpdb.DeleteServings(tx, mpID)
			if err != nil {
				return err
			}

			return mpdb.DeleteMealPlan(tx, mpID)
		})
	})

	if err != nil {
		serverError(w, err)
		return
	}

	redirect(w, http.StatusSeeOther, "/mealplans")
}
예제 #8
0
// handleDeleteMealPlanForm handles HTTP requests for the meal plan deletion
// confirmation page.
func handleDeleteMealPlanForm(w http.ResponseWriter, r *http.Request) {
	mpID, ok := getUint64Var(r, "mealplanid")
	if !ok {
		httpError(w, BadRequestError)
		return
	}

	var mp *mpdata.MealPlan
	var numServings int

	err := mpdb.WithConnection(func(db *sql.DB) (err error) {
		return mpdb.WithTransaction(db, func(tx *sql.Tx) (err error) {
			mp, err = mpdb.GetMealPlan(tx, mpID)
			if err != nil {
				return err
			}

			numServings, err = mpdb.CountServings(tx, mpID)
			return err
		})
	})

	if err != nil {
		serverError(w, err)
		return
	}

	if mp == nil {
		httpError(w, NotFoundError)
		return
	}

	renderTemplate(w, "delete-mp-form.html", deleteMPData{mp, numServings})
}
예제 #9
0
// handleCreateMealPlanAction handles HTTP requests for the submission of the
// meal plan creation form.
func handleCreateMealPlanAction(w http.ResponseWriter, r *http.Request) {
	// Parse the POST request body
	err := r.ParseForm()
	if err != nil {
		serverError(w, err)
		return
	}

	startDate, err := time.Parse(mpdata.DatepickerDateFormat, r.FormValue("start"))
	if err != nil {
		httpError(w, BadRequestError)
		return
	}

	endDate, err := time.Parse(mpdata.DatepickerDateFormat, r.FormValue("end"))
	if err != nil {
		httpError(w, BadRequestError)
		return
	}

	if startDate.After(endDate) {
		httpError(w, BadRequestError)
		return
	}

	auto := r.FormValue("auto") == "true"

	// Create a MealPlan object
	mp := &mpdata.MealPlan{
		StartDate: startDate,
		EndDate:   endDate,
	}

	err = mpdb.WithConnection(func(db *sql.DB) (err error) {
		return mpdb.WithTransaction(db, func(tx *sql.Tx) (err error) {
			// Add mp to the database
			err = mpdb.AddMealPlan(tx, mp)
			if err != nil {
				return err
			}

			// Optionally fill the meal plan automatically
			if auto {
				err = mpdb.AutoFillMealPlan(tx, mp)
				if err != nil {
					return err
				}
			}

			return nil
		})
	})
	if err != nil {
		serverError(w, err)
		return
	}

	redirect(w, http.StatusSeeOther, "/mealplans/"+strconv.FormatUint(mp.ID, 10)+"/edit")
}
예제 #10
0
// fetchServings handles an API call to list all the servings for a given meal
// plan. Expected parameters: mealplanid. Returns: an array of
// fetchServingsRecord objects.
func fetchServings(params url.Values) (response JSONResponse) {
	mpID, err := strconv.ParseUint(params.Get("mealplanid"), 10, 64)
	if err != nil {
		return JSONResponse{Error: "Invalid or missing 'mealplanid' parameter"}
	}

	var results []*fetchServingsRecord

	err = mpdb.WithConnection(func(db *sql.DB) (err error) {
		return mpdb.WithTransaction(db, func(tx *sql.Tx) (err error) {
			mps, err := mpdb.GetMealPlanWithServings(tx, mpID)
			if err != nil {
				return err
			}

			if mps.MealPlan == nil {
				return nil
			}

			for _, date := range mps.MealPlan.Days() {
				ts := &fetchServingsRecord{
					Date: date.Format(mpdata.JSONDateFormat),
				}

				for _, serving := range mps.Servings {
					if serving.Date == date {
						ts.HasMeal = true
						ts.MealID = serving.MealID

						meal, err := mpdb.GetMeal(tx, serving.MealID)
						if err != nil {
							return err
						}

						if meal == nil {
							log.Printf("Warning: meal plan %d -> serving %s points to nonexistent meal %d\n", mpID, date.Format("2006-01-02"), serving.MealID)
							ts.MealName = "???"
						} else {
							ts.MealName = meal.Name
						}

						break
					}
				}

				results = append(results, ts)
			}

			return err
		})
	})

	if err != nil {
		log.Printf("Database error: %s\n", err.Error())
		return JSONResponse{Error: "Database error"}
	}

	return JSONResponse{Success: results}
}
예제 #11
0
// fetchAllTags handles an API call to obtain a list of all tags present in the
// database, without duplicates and in alphabetical order. Expected parameters:
// none. Returns: an array of tags.
func fetchAllTags(params url.Values) (response JSONResponse) {
	var tags []string

	err := mpdb.WithConnection(func(db *sql.DB) (err error) {
		return mpdb.WithTransaction(db, func(tx *sql.Tx) (err error) {
			tags, err = mpdb.ListAllTags(tx, true)
			return err
		})
	})

	if err != nil {
		log.Printf("Database error: %s\n", err.Error())
		return JSONResponse{Error: "Database error"}
	}

	return JSONResponse{Success: tags}
}
예제 #12
0
// handleEditMealAction handles HTTP requests for submission of the "edit meal"
// form.
func handleEditMealAction(w http.ResponseWriter, r *http.Request) {
	// Get the meal ID from the URL
	mealID, ok := getUint64Var(r, "mealid")
	if !ok {
		httpError(w, BadRequestError)
		return
	}

	// Parse the POST request body
	err := r.ParseForm()
	if err != nil {
		serverError(w, err)
		return
	}

	// Create a MealWithTags value from the form fields
	mt := mpdata.MealWithTags{
		Meal: &mpdata.Meal{
			ID:        mealID,
			Name:      r.FormValue("name"),
			RecipeURL: r.FormValue("recipe"),
			Favourite: r.FormValue("favourite") != "",
		},
		Tags: r.Form["tags"],
	}

	// Update the database record
	err = mpdb.WithConnection(func(db *sql.DB) (err error) {
		return mpdb.WithTransaction(db, func(tx *sql.Tx) (err error) {
			return mpdb.UpdateMealWithTags(tx, mt)
		})
	})
	if err != nil {
		serverError(w, err)
		return
	}

	// Redirect to list of meals
	redirect(w, http.StatusSeeOther, "/meals?highlight="+strconv.FormatUint(mealID, 10))
}
예제 #13
0
// updateServing implements an API call to update a meal serving for a meal
// plan with a new meal ID, removing the old serving if it already exists.
// Expected parameters: mealplanid, date, mealid. Returns: nothing.
func updateServing(params url.Values) (response JSONResponse) {
	mpID, err := strconv.ParseUint(params.Get("mealplanid"), 10, 64)
	if err != nil {
		return JSONResponse{Error: "Invalid or missing 'mealplanid' parameter"}
	}

	dateServed, err := time.Parse(mpdata.JSONDateFormat, params.Get("date"))
	if err != nil {
		return JSONResponse{Error: "Invalid or missing 'date' parameter"}
	}

	mealID, err := strconv.ParseUint(params.Get("mealid"), 10, 64)
	if err != nil {
		return JSONResponse{Error: "Invalid or missing 'mealid' parameter"}
	}

	err = mpdb.WithConnection(func(db *sql.DB) (err error) {
		return mpdb.WithTransaction(db, func(tx *sql.Tx) (err error) {
			err = mpdb.DeleteServing(tx, mpID, dateServed)
			if err != nil {
				return err
			}

			s := &mpdata.Serving{
				MealPlanID: mpID,
				Date:       dateServed,
				MealID:     mealID,
			}

			return mpdb.AddServing(tx, s)
		})
	})

	if err != nil {
		log.Printf("Database error: %s\n", err.Error())
		return JSONResponse{Error: "Database error"}
	}

	return JSONResponse{Success: nil}
}
예제 #14
0
// toggleFavourite implements an API call to toggle the "favourite" status of
// a given meal. Expected paramaters: mealid. Returns: the updated "favourite"
// status of the meal.
func toggleFavourite(params url.Values) (response JSONResponse) {
	mealID, err := strconv.ParseUint(params.Get("mealid"), 10, 64)
	if err != nil {
		return JSONResponse{Error: "Invalid or missing 'mealid' parameter"}
	}

	var isFavourite bool

	err = mpdb.WithConnection(func(db *sql.DB) (err error) {
		return mpdb.WithTransaction(db, func(tx *sql.Tx) (err error) {
			isFavourite, err = mpdb.ToggleFavourite(tx, mealID)
			return err
		})
	})

	if err != nil {
		log.Printf("Database error: %s\n", err.Error())
		return JSONResponse{Error: "Database error"}
	}

	return JSONResponse{Success: isFavourite}
}
예제 #15
0
// updateNotes implements an API call to update the notes associated with a
// meal plan. Expected parameters: mealplanid, notes. Returns: nothing.
func updateNotes(params url.Values) (response JSONResponse) {
	mpID, err := strconv.ParseUint(params.Get("mealplanid"), 10, 64)
	if err != nil {
		return JSONResponse{Error: "Invalid or missing 'mealplanid' parameter"}
	}

	notes := params.Get("notes")
	if err != nil {
		return JSONResponse{Error: "Invalid or missing 'notes' parameter"}
	}

	err = mpdb.WithConnection(func(db *sql.DB) (err error) {
		return mpdb.WithTransaction(db, func(tx *sql.Tx) (err error) {
			return mpdb.UpdateNotes(tx, mpID, notes)
		})
	})

	if err != nil {
		log.Printf("Database error: %s\n", err.Error())
		return JSONResponse{Error: "Database error"}
	}

	return JSONResponse{Success: nil}
}
예제 #16
0
// deleteMeal handles an API call to delete a meal. Expected parameters: mealid.
// Returns: nothing.
func deleteMeal(params url.Values) (response JSONResponse) {
	mealID, err := strconv.ParseUint(params.Get("mealid"), 10, 64)
	if err != nil {
		return JSONResponse{Error: "Invalid or missing 'mealid' parameter"}
	}

	err = mpdb.WithConnection(func(db *sql.DB) (err error) {
		return mpdb.WithTransaction(db, func(tx *sql.Tx) (err error) {
			err = mpdb.DeleteServingsOf(tx, mealID)
			if err != nil {
				return err
			}

			return mpdb.DeleteMealWithTags(tx, mealID)
		})
	})

	if err != nil {
		log.Printf("Database error: %s\n", err.Error())
		return JSONResponse{Error: "Database error"}
	}

	return JSONResponse{Success: nil}
}