Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/vote val #13

Merged
merged 2 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions fileManager.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"context"
"fmt"
"log/slog"
"net/url"
"os"

Expand Down Expand Up @@ -123,13 +124,13 @@ func (ls *localStorage) StoreFile(b []byte, ext string) (string, error) {
}
f, err := os.CreateTemp(ls.dirName, fmt.Sprintf("idea-*.%s", ext))
if err != nil {
Logr.Error("Error Creating Local TMP file", "err", err.Error())
slog.Error("Error Creating Local TMP file", "err", err.Error())
return "", err
}
defer f.Close()
_, err = f.Write(b)
if err != nil {
Logr.Error("Error writing bytes to a temp file", "err", err.Error())
slog.Error("Error writing bytes to a temp file", "err", err.Error())
return "", err
}

Expand Down
5 changes: 3 additions & 2 deletions images.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package goodidea

import (
"context"
"log/slog"

"github.com/jackc/pgx/v5"
)
Expand All @@ -14,7 +15,7 @@ func saveTaskImages(i uint32, p []string) {
ctx := context.Background()
for _, pth := range p {
if _, err := DB.Exec(ctx, "INSERT INTO task_imgs(task_id, img_path) VALUES ($1, $2)", i, pth); err != nil {
Logr.Error("Unable to save image path record", "task", i, "imagePath", pth)
slog.Error("Unable to save image path record", "task", i, "imagePath", pth)
}
}
}
Expand Down Expand Up @@ -42,7 +43,7 @@ func getTaskImages(i uint32) ([]string, error) {
var s string
err = rows.Scan(&s)
if err != nil {
Logr.Error("Unable to marshal image path response into string", "err", err.Error())
slog.Error("Unable to marshal image path response into string", "err", err.Error())
return imgs, err
}
imgs = append(imgs, s)
Expand Down
6 changes: 1 addition & 5 deletions lambda/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,7 @@ func init() {
log.Fatal("Unable to create a connection to the database", err)
}
}
//defer goodidea.DB.Close()

if goodidea.Logr == nil {
goodidea.SetupLogger()
}
goodidea.SetupLogger()

//Set up mux router
router := goodidea.NewServer()
Expand Down
21 changes: 15 additions & 6 deletions logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ import (
"os"
)

var (
Logr *slog.Logger
)

// ControllerError - An error with nested response
type ControllerError struct {
//Msg is a string field to capture a custom error message
Expand All @@ -32,6 +28,19 @@ func (e *ControllerError) Error() string {
}

func SetupLogger() {
l := slog.New(slog.NewJSONHandler(os.Stdout, nil))
Logr = l
lvl := slog.LevelInfo
switch os.Getenv("LOG_LEVEL") {
case "error":
lvl = slog.LevelError
case "warn":
lvl = slog.LevelWarn
case "debug":
lvl = slog.LevelDebug
}

logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: lvl,
}))

slog.SetDefault(logger)
}
76 changes: 48 additions & 28 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"html/template"
"io"
"log/slog"
"net/http"
"strconv"
"strings"
Expand All @@ -17,12 +18,12 @@ var tmpl *template.Template
func index(w http.ResponseWriter, r *http.Request) {
tsks, err := getAllTasks(25)
if err != nil {
Logr.Error("Could not get all tasks from db", err)
slog.Error("Could not get all tasks from db", err)
}

err = tmpl.ExecuteTemplate(w, "index.html", tsks)
if err != nil {
Logr.Error("Could not execute template", err)
slog.Error("Could not execute template", err)
}
}

Expand All @@ -37,30 +38,49 @@ func listTasks(w http.ResponseWriter, r *http.Request) {
taskList, err = getSomeTasks(title)
}
if err != nil {
Logr.Error("Could not get tasks from db", err)
slog.Error("Could not get tasks from db", err)
return
}

err = tmpl.ExecuteTemplate(w, "list-tasks.html", taskList)
if err != nil {
Logr.Error("Could not execute template", err)
slog.Error("Could not execute template", err)
}
}

// TODO: Handle errors and report them back to the UI
func updateScore(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseUint(mux.Vars(r)["id"], 10, 64)
if err != nil {
Logr.Error("Could not parse id", err)
slog.Error("Could not parse id", "err", err)
return
}

err = r.ParseForm()
if err != nil {
Logr.Error("Error parsing form", err)
slog.Error("Error parsing form", "err", err)
return
}

vote := r.FormValue(fmt.Sprintf("scorekeeper%d", id))
var val int8 = 0
switch vote {
case "inc":
val = 1
case "inc2":
val = 2
case "dec":
val = -1
case "dec2":
val = -2
default:
slog.Error("Did not understand the new vote value", "vote", vote)
return
}

score, err := updateTaskScore(uint32(id), r.FormValue(fmt.Sprintf("scorekeeper%d", id)) == "inc")
score, err := updateTaskScore(uint32(id), val)
if err != nil {
Logr.Error("Could not update task score", err)
slog.Error("Could not update task score", err)
}

fmt.Fprintf(w, fmt.Sprintf("%d", score))
Expand All @@ -72,7 +92,7 @@ func createTask(w http.ResponseWriter, r *http.Request) {
//TODO: MultipartReader to transform this to a steam
err := r.ParseMultipartForm(32 << 20) //32MB
if err != nil {
Logr.Error("Error parsing form", err)
slog.Error("Error parsing form", err)
}

title := r.FormValue("title")
Expand All @@ -84,7 +104,7 @@ func createTask(w http.ResponseWriter, r *http.Request) {
body := r.FormValue("details")
taskID, err := addTask(title, body)
if err != nil {
Logr.Error("Could not create a new task", "error", err)
slog.Error("Could not create a new task", "error", err)
fmt.Fprintf(w, "<p>Could not create a new task</p>")
return
}
Expand All @@ -103,7 +123,7 @@ func createTask(w http.ResponseWriter, r *http.Request) {

err = tmpl.ExecuteTemplate(w, "make-task.html", task)
if err != nil {
Logr.Error("Could not execute template", err)
slog.Error("Could not execute template", err)
}

//Add any images which may have been sent along with the form data
Expand All @@ -119,21 +139,21 @@ func createTask(w http.ResponseWriter, r *http.Request) {

nameComponents := strings.Split(fh.Filename, ".")
if len(nameComponents) != 2 {
Logr.Error("Could not find the extension of the uploaded file", "error", err)
slog.Error("Could not find the extension of the uploaded file", "error", err)
return
}

b, err := io.ReadAll(f)
if err != nil {
Logr.Error("could not read bytes out of file sent", "error", err)
slog.Error("could not read bytes out of file sent", "error", err)
return
}

//TODO: can the rest of the following be done in a go routine?
m := NewFileManager()
s, err := m.StoreFile(b, nameComponents[1])
if err != nil {
Logr.Error("Unable to store images", "task", taskID, "error", err.Error())
slog.Error("Unable to store images", "task", taskID, "error", err.Error())
return
}

Expand All @@ -146,47 +166,47 @@ func createTask(w http.ResponseWriter, r *http.Request) {
func viewTask(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseUint(mux.Vars(r)["id"], 10, 64)
if err != nil {
Logr.Error("Could not parse id", err)
slog.Error("Could not parse id", err)
fmt.Fprintf(w, "ERROR! Could not get task ID from request")
return
}
//TODO: Why not get all of the comments for the task in one query?
tsk, err := getTasksByID(uint32(id))
if err != nil {
Logr.Error("could not get task by id", "taskID", id, "error", err)
slog.Error("could not get task by id", "taskID", id, "error", err)
fmt.Fprintf(w, "Error could not get a task with this ID")
return
}

comments, err := getAllTaskComments(uint32(id))
if err != nil {
Logr.Error("could not get comments for task", "taskID", id, "error", err)
slog.Error("could not get comments for task", "taskID", id, "error", err)
}

tsk.Comments = comments

err = tmpl.ExecuteTemplate(w, "show-task.html", tsk)
if err != nil {
Logr.Error("could not render template for task", "taskID", id, "error", err)
slog.Error("could not render template for task", "taskID", id, "error", err)
}
}

func markTaskComplete(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseUint(mux.Vars(r)["id"], 10, 64)
if err != nil {
Logr.Error("Could not parse id", "error", err)
slog.Error("Could not parse id", "error", err)
fmt.Fprintf(w, "ERROR! Could not get task ID from request")
return
}
if err := toggleStatus(uint32(id)); err != nil {
Logr.Error("Could not mark task complete", "error", err, "id", id)
slog.Error("Could not mark task complete", "error", err, "id", id)
fmt.Fprintf(w, "<p>ERROR! could not mark complete</p>")
return
}

tsk, err := getTasksByID(uint32(id))
if err != nil {
Logr.Error("could not get task by id", "taskID", id, "error", err)
slog.Error("could not get task by id", "taskID", id, "error", err)
fmt.Fprintf(w, "Error could not get a task with this ID")
return
}
Expand All @@ -198,22 +218,22 @@ func markTaskComplete(w http.ResponseWriter, r *http.Request) {
}

if err = t1.Execute(w, tsk.Status); err != nil {
Logr.Error("Could not execute the template for marking a task complete", "taskID", id, "error", err)
slog.Error("Could not execute the template for marking a task complete", "taskID", id, "error", err)
fmt.Fprintf(w, "couldn't execute template")
}
}

func postComment(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseUint(mux.Vars(r)["id"], 10, 64)
if err != nil {
Logr.Error("Could not parse id", "error", err)
slog.Error("Could not parse id", "error", err)
fmt.Fprintf(w, "ERROR! Could not get task ID from request")
return
}

err = r.ParseForm()
if err != nil {
Logr.Error("Error parsing form", "error", err)
slog.Error("Error parsing form", "error", err)
fmt.Fprintf(w, "<p>ERROR! Could not pase the form data</p>")
return
}
Expand All @@ -228,7 +248,7 @@ func postComment(w http.ResponseWriter, r *http.Request) {
}

if err := addComment(uint32(id), pu, r.FormValue("comments")); err != nil {
Logr.Error("Could not save a new comment", "error", err)
slog.Error("Could not save a new comment", "error", err)
fmt.Fprintf(w, "<p>ERROR! could not insert the comment into the DB</p>")
return
}
Expand All @@ -239,20 +259,20 @@ func postComment(w http.ResponseWriter, r *http.Request) {
Comment{TaskID: uint32(id), User: pu, Content: r.FormValue("comments"), CreatedAt: time.Now()},
)
if err != nil {
Logr.Error("could not render template for new comment", "error", err)
slog.Error("could not render template for new comment", "error", err)
}
}

func displayTaskImages(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseUint(mux.Vars(r)["id"], 10, 64)
if err != nil {
Logr.Error("Could not parse id", "err", err)
slog.Error("Could not parse id", "err", err)
fmt.Fprintf(w, "ERROR! Could not get task ID from request")
return
}
paths, err := getTaskImages(uint32(id))
if err != nil {
Logr.Error("could not get image paths", "task", id, "err", err)
slog.Error("could not get image paths", "task", id, "err", err)
fmt.Fprintf(w, "ERROR! Could not get images for task %d", id)
return
}
Expand Down
39 changes: 37 additions & 2 deletions src/cookies.js → src/votes.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,40 @@ const setVoteListeners = () => {
for (let j = 0; j < radios.length; j++) {
const id = radios[j].id;
const voteInfo = id.split("-");
radios[j].onclick = function () {
setCookie(voteInfo[0], voteInfo[1], 1);
radios[j].onclick = () => {
radios[j].checked = true;
if (voteInfo.length > 1) {
setCookie(voteInfo[0], voteInfo[1], 60);

const changeDirection = voteInfo[1] === "up" ? "down" : "up";
updateVoteMagnitude(voteInfo[0], changeDirection);
}
};
}
};

/**
* @Function{updateVoteMagnitude}
* If we vote one direction, then change our mind, our vote needs to be twice
* as large. 1 to undo our last vote, and 1 to apply the directional vote we want
* @param{string} taskId - The task that was just voted on
* @param{string} direction - The direction which needs to be updated
*/
const updateVoteMagnitude = (taskId, direction) => {
const btn = document.getElementById(`${taskId}-${direction}`);
//If we cannot find it for some reason I guess we give up
if (!btn) {
return;
}

let newVal = "inc2";
if (direction === "down") {
newVal = "dec2";
}

btn.value = newVal;
};

/**
* @Function{setCookie}
* record that a user voted on this task by setting a cookie
Expand Down Expand Up @@ -85,6 +113,13 @@ const applyPreviousVotes = () => {
//Could have a cookie for a task not shown
if (radio !== undefined && radio !== null) {
radio.checked = true;

//Input Ids look like taskId-[up|down]
const voteInfo = previousVotes[v].split("-");
if (voteInfo.length >= 2) {
const changeDirection = voteInfo[1] === "up" ? "down" : "up";
updateVoteMagnitude(voteInfo[0], changeDirection);
}
}
}
};
Expand Down
Loading