diff --git a/Makefile b/Makefile index 97db62e..668ccf1 100644 --- a/Makefile +++ b/Makefile @@ -27,10 +27,6 @@ clean: remove-podman-containers cd backend && make clean @echo "Cleaned up!" -.PHONY: itest -itest: - python testing.py - # Cleans up everything related to podman, not just the project. Make sure you understand what this means. podman-clean: podman system reset --force diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index ef365cd..c13308b 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -4,6 +4,7 @@ import ( "embed" "os" "path/filepath" + "time" "ttime/internal/types" "github.com/jmoiron/sqlx" @@ -14,14 +15,13 @@ import ( type Database interface { // Insert a new user into the database, password should be hashed before calling AddUser(username string, password string) error - CheckUser(username string, password string) bool RemoveUser(username string) error PromoteToAdmin(username string) error GetUserId(username string) (int, error) AddProject(name string, description string, username string) error Migrate(dirname string) error GetProjectId(projectname string) (int, error) - AddWeeklyReport(projectName string, userName string, week int, developmentTime int, meetingTime int, adminTime int, ownWorkTime int, studyTime int, testingTime int) error + AddTimeReport(projectName string, userName string, activityType string, start time.Time, end time.Time) error AddUserToProject(username string, projectname string, role string) error ChangeUserRole(username string, projectname string, role string) error GetAllUsersProject(projectname string) ([]UserProjectMember, error) @@ -50,16 +50,27 @@ var scripts embed.FS const userInsert = "INSERT INTO users (username, password) VALUES (?, ?)" const projectInsert = "INSERT INTO projects (name, description, owner_user_id) SELECT ?, ?, id FROM users WHERE username = ?" const promoteToAdmin = "INSERT INTO site_admin (admin_id) SELECT id FROM users WHERE username = ?" -const addWeeklyReport = `WITH UserLookup AS (SELECT id FROM users WHERE username = ?), +const addTimeReport = `WITH UserLookup AS (SELECT id FROM users WHERE username = ?), ProjectLookup AS (SELECT id FROM projects WHERE name = ?) - INSERT INTO weekly_reports (project_id, user_id, week, development_time, meeting_time, admin_time, own_work_time, study_time, testing_time) - VALUES ((SELECT id FROM ProjectLookup), (SELECT id FROM UserLookup),?, ?, ?, ?, ?, ?, ?);` + INSERT INTO time_reports (project_id, user_id, activity_type, start, end) + VALUES ((SELECT id FROM ProjectLookup), (SELECT id FROM UserLookup),?, ?, ?);` const addUserToProject = "INSERT INTO user_roles (user_id, project_id, p_role) VALUES (?, ?, ?)" // WIP const changeUserRole = "UPDATE user_roles SET p_role = ? WHERE user_id = ? AND project_id = ?" -const getProjectsForUser = `SELECT projects.id, projects.name, projects.description, projects.owner_user_id - FROM projects JOIN user_roles ON projects.id = user_roles.project_id - JOIN users ON user_roles.user_id = users.id WHERE users.username = ?;` +const getProjectsForUser = ` +SELECT + projects.id, + projects.name, + projects.description, + projects.owner_user_id +FROM + projects +JOIN + user_roles ON projects.id = user_roles.project_id +JOIN + users ON user_roles.user_id = users.id +WHERE + users.username = ?;` // DbConnect connects to the database func DbConnect(dbpath string) Database { @@ -78,15 +89,6 @@ func DbConnect(dbpath string) Database { return &Db{db} } -func (d *Db) CheckUser(username string, password string) bool { - var dbPassword string - err := d.Get(&dbPassword, "SELECT password FROM users WHERE username = ?", username) - if err != nil { - return false - } - return dbPassword == password -} - // GetProjectsForUser retrieves all projects associated with a specific user. func (d *Db) GetProjectsForUser(username string) ([]types.Project, error) { var projects []types.Project @@ -108,8 +110,9 @@ func (d *Db) GetProject(projectId int) (types.Project, error) { return project, err } -func (d *Db) AddWeeklyReport(projectName string, userName string, week int, developmentTime int, meetingTime int, adminTime int, ownWorkTime int, studyTime int, testingTime int) error { - _, err := d.Exec(addWeeklyReport, userName, projectName, week, developmentTime, meetingTime, adminTime, ownWorkTime, studyTime, testingTime) +// AddTimeReport adds a time report for a specific project and user. +func (d *Db) AddTimeReport(projectName string, userName string, activityType string, start time.Time, end time.Time) error { // WIP + _, err := d.Exec(addTimeReport, userName, projectName, activityType, start, end) return err } diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index 5438d66..9118e2f 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -2,6 +2,7 @@ package database import ( "testing" + "time" ) // Tests are not guaranteed to be sequential @@ -92,7 +93,7 @@ func TestPromoteToAdmin(t *testing.T) { } } -func TestAddWeeklyReport(t *testing.T) { +func TestAddTimeReport(t *testing.T) { db, err := setupState() if err != nil { t.Error("setupState failed:", err) @@ -108,9 +109,12 @@ func TestAddWeeklyReport(t *testing.T) { t.Error("AddProject failed:", err) } - err = db.AddWeeklyReport("testproject", "testuser", 1, 1, 1, 1, 1, 1, 1) + var now = time.Now() + var then = now.Add(time.Hour) + + err = db.AddTimeReport("testproject", "testuser", "activity", now, then) if err != nil { - t.Error("AddWeeklyReport failed:", err) + t.Error("AddTimeReport failed:", err) } } @@ -130,9 +134,12 @@ func TestAddUserToProject(t *testing.T) { t.Error("AddProject failed:", err) } - err = db.AddWeeklyReport("testproject", "testuser", 1, 1, 1, 1, 1, 1, 1) + var now = time.Now() + var then = now.Add(time.Hour) + + err = db.AddTimeReport("testproject", "testuser", "activity", now, then) if err != nil { - t.Error("AddWeeklyReport failed:", err) + t.Error("AddTimeReport failed:", err) } err = db.AddUserToProject("testuser", "testproject", "user") diff --git a/backend/internal/database/migrations/0030_time_reports.sql b/backend/internal/database/migrations/0030_time_reports.sql new file mode 100644 index 0000000..7c169c2 --- /dev/null +++ b/backend/internal/database/migrations/0030_time_reports.sql @@ -0,0 +1,22 @@ +CREATE TABLE IF NOT EXISTS time_reports ( + id INTEGER PRIMARY KEY, + project_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + activity_type TEXT NOT NULL, + start DATETIME NOT NULL, + end DATETIME NOT NULL, + FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE + FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE + FOREIGN KEY (activity_type) REFERENCES activity_types (name) ON DELETE CASCADE +); + +CREATE TRIGGER IF NOT EXISTS time_reports_start_before_end + BEFORE INSERT ON time_reports + FOR EACH ROW + BEGIN + SELECT + CASE + WHEN NEW.start >= NEW.end THEN + RAISE (ABORT, 'start must be before end') + END; + END; \ No newline at end of file diff --git a/backend/internal/database/migrations/0035_weekly_report.sql b/backend/internal/database/migrations/0035_weekly_report.sql deleted file mode 100644 index 0e29b97..0000000 --- a/backend/internal/database/migrations/0035_weekly_report.sql +++ /dev/null @@ -1,14 +0,0 @@ -CREATE TABLE weekly_reports ( - user_id INTEGER, - project_id INTEGER, - week INTEGER, - development_time INTEGER, - meeting_time INTEGER, - admin_time INTEGER, - own_work_time INTEGER, - study_time INTEGER, - testing_time INTEGER, - FOREIGN KEY (user_id) REFERENCES users(id), - FOREIGN KEY (project_id) REFERENCES projects(id) - PRIMARY KEY (user_id, project_id, week) -) \ No newline at end of file diff --git a/backend/internal/database/migrations/0040_time_report_collections.sql b/backend/internal/database/migrations/0040_time_report_collections.sql new file mode 100644 index 0000000..be406ff --- /dev/null +++ b/backend/internal/database/migrations/0040_time_report_collections.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS report_collection ( + id INTEGER PRIMARY KEY, + owner_id INTEGER NOT NULL, + project_id INTEGER NOT NULL, + date DATE NOT NULL, + signed_by INTEGER, -- NULL if not signed + FOREIGN KEY (owner_id) REFERENCES users (id) + FOREIGN KEY (signed_by) REFERENCES users (id) +); \ No newline at end of file diff --git a/backend/internal/database/migrations/0070_salts.sql b/backend/internal/database/migrations/0070_salts.sql new file mode 100644 index 0000000..de9757d --- /dev/null +++ b/backend/internal/database/migrations/0070_salts.sql @@ -0,0 +1,16 @@ +-- It is unclear weather this table will be used + +-- Create the table to store hash salts +CREATE TABLE IF NOT EXISTS salts ( + id INTEGER PRIMARY KEY, + salt TEXT NOT NULL +); + +-- Commented out for now, no time for good practices, which is atrocious +-- Create a trigger to automatically generate a salt when inserting a new user record +-- CREATE TRIGGER generate_salt_trigger +-- AFTER INSERT ON users +-- BEGIN +-- INSERT INTO salts (salt) VALUES (randomblob(16)); +-- UPDATE users SET salt_id = (SELECT last_insert_rowid()) WHERE id = new.id; +-- END; diff --git a/backend/internal/database/migrations/0080_activity_types.sql b/backend/internal/database/migrations/0080_activity_types.sql new file mode 100644 index 0000000..d984d58 --- /dev/null +++ b/backend/internal/database/migrations/0080_activity_types.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS activity_types ( + name TEXT PRIMARY KEY +); + +INSERT OR IGNORE INTO activity_types (name) VALUES ('Development'); +INSERT OR IGNORE INTO activity_types (name) VALUES ('Meeting'); +INSERT OR IGNORE INTO activity_types (name) VALUES ('Administration'); +INSERT OR IGNORE INTO activity_types (name) VALUES ('Own Work'); +INSERT OR IGNORE INTO activity_types (name) VALUES ('Studies'); +INSErt OR IGNORE INTO activity_types (name) VALUES ('Testing'); \ No newline at end of file diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index 2378f7b..91d46a9 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -18,7 +18,6 @@ type GlobalState interface { LoginRenew(c *fiber.Ctx) error // To renew the token CreateProject(c *fiber.Ctx) error // To create a new project GetUserProjects(c *fiber.Ctx) error // To get all projects - SubmitWeeklyReport(c *fiber.Ctx) error // GetProject(c *fiber.Ctx) error // To get a specific project // UpdateProject(c *fiber.Ctx) error // To update a project // DeleteProject(c *fiber.Ctx) error // To delete a project @@ -78,17 +77,12 @@ func (gs *GState) Register(c *fiber.Ctx) error { // This path should obviously be protected in the future // UserDelete deletes a user from the database func (gs *GState) UserDelete(c *fiber.Ctx) error { - // Read from path parameters - username := c.Params("username") - - // Read username from Locals - auth_username := c.Locals("user").(*jwt.Token).Claims.(jwt.MapClaims)["name"].(string) - - if username != auth_username { - return c.Status(403).SendString("You can only delete yourself") + u := new(types.User) + if err := c.BodyParser(u); err != nil { + return c.Status(400).SendString(err.Error()) } - if err := gs.Db.RemoveUser(username); err != nil { + if err := gs.Db.RemoveUser(u.Username); err != nil { return c.Status(500).SendString(err.Error()) } @@ -106,20 +100,18 @@ func (gs *GState) IncrementButtonCount(c *fiber.Ctx) error { // Login is a simple login handler that returns a JWT token func (gs *GState) Login(c *fiber.Ctx) error { - // The body type is identical to a NewUser - u := new(types.NewUser) - if err := c.BodyParser(u); err != nil { - return c.Status(400).SendString(err.Error()) - } + // To test: curl --data "user=user&pass=pass" http://localhost:8080/api/login + user := c.FormValue("user") + pass := c.FormValue("pass") - if !gs.Db.CheckUser(u.Username, u.Password) { - println("User not found") + // Throws Unauthorized error + if user != "user" || pass != "pass" { return c.SendStatus(fiber.StatusUnauthorized) } // Create the Claims claims := jwt.MapClaims{ - "name": u.Username, + "name": user, "admin": false, "exp": time.Now().Add(time.Hour * 72).Unix(), } @@ -167,9 +159,9 @@ func (gs *GState) CreateProject(c *fiber.Ctx) error { // Get the username from the token and set it as the owner of the project // This is ugly but claims := user.Claims.(jwt.MapClaims) - owner := claims["name"].(string) + p.Owner = claims["name"].(string) - if err := gs.Db.AddProject(p.Name, p.Description, owner); err != nil { + if err := gs.Db.AddProject(p.Name, p.Description, p.Owner); err != nil { return c.Status(500).SendString(err.Error()) } @@ -255,29 +247,3 @@ func (gs *GState) GetProject(c *fiber.Ctx) error { // Return the project as JSON return c.JSON(project) } - -func (gs *GState) SubmitWeeklyReport(c *fiber.Ctx) error { - // Extract the necessary parameters from the token - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - username := claims["name"].(string) - - report := new(types.NewWeeklyReport) - if err := c.BodyParser(report); err != nil { - return c.Status(400).SendString(err.Error()) - } - - // Make sure all the fields of the report are valid - if report.Week < 1 || report.Week > 52 { - return c.Status(400).SendString("Invalid week number") - } - if report.DevelopmentTime < 0 || report.MeetingTime < 0 || report.AdminTime < 0 || report.OwnWorkTime < 0 || report.StudyTime < 0 || report.TestingTime < 0 { - return c.Status(400).SendString("Invalid time report") - } - - if err := gs.Db.AddWeeklyReport(report.ProjectName, username, report.Week, report.DevelopmentTime, report.MeetingTime, report.AdminTime, report.OwnWorkTime, report.StudyTime, report.TestingTime); err != nil { - return c.Status(500).SendString(err.Error()) - } - - return c.Status(200).SendString("Time report added") -} diff --git a/backend/internal/types/WeeklyReport.go b/backend/internal/types/WeeklyReport.go deleted file mode 100644 index e0ea1ef..0000000 --- a/backend/internal/types/WeeklyReport.go +++ /dev/null @@ -1,21 +0,0 @@ -package types - -// This is what should be submitted to the server, the username will be derived from the JWT token -type NewWeeklyReport struct { - // The name of the project, as it appears in the database - ProjectName string `json:"projectName"` - // The week number - Week int `json:"week"` - // Total time spent on development - DevelopmentTime int `json:"developmentTime"` - // Total time spent in meetings - MeetingTime int `json:"meetingTime"` - // Total time spent on administrative tasks - AdminTime int `json:"adminTime"` - // Total time spent on personal projects - OwnWorkTime int `json:"ownWorkTime"` - // Total time spent on studying - StudyTime int `json:"studyTime"` - // Total time spent on testing - TestingTime int `json:"testingTime"` -} diff --git a/backend/internal/types/project.go b/backend/internal/types/project.go index 7e1747f..8fcfaf5 100644 --- a/backend/internal/types/project.go +++ b/backend/internal/types/project.go @@ -8,8 +8,9 @@ type Project struct { Owner string `json:"owner" db:"owner_user_id"` } -// As it arrives from the client, Owner is derived from the JWT token +// As it arrives from the client type NewProject struct { Name string `json:"name"` Description string `json:"description"` + Owner string `json:"owner"` } diff --git a/backend/internal/types/users.go b/backend/internal/types/users.go index e9dff67..233ec71 100644 --- a/backend/internal/types/users.go +++ b/backend/internal/types/users.go @@ -16,7 +16,6 @@ func (u *User) ToPublicUser() (*PublicUser, error) { }, nil } -// Should be used when registering, for example type NewUser struct { Username string `json:"username"` Password string `json:"password"` diff --git a/backend/main.go b/backend/main.go index 9ba2556..4e0935c 100644 --- a/backend/main.go +++ b/backend/main.go @@ -68,10 +68,9 @@ func main() { SigningKey: jwtware.SigningKey{Key: []byte("secret")}, })) - server.Post("/api/submitReport", gs.SubmitWeeklyReport) server.Get("/api/getUserProjects", gs.GetUserProjects) server.Post("/api/loginrenew", gs.LoginRenew) - server.Delete("/api/userdelete/:username", gs.UserDelete) // Perhaps just use POST to avoid headaches + server.Delete("/api/userdelete", gs.UserDelete) // Perhaps just use POST to avoid headaches server.Post("/api/project", gs.CreateProject) // Announce the port we are listening on and start the server diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index ed64f71..248ad37 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -1,6 +1,5 @@ import { NewProject, Project } from "../Types/Project"; import { NewUser, User } from "../Types/Users"; -import { NewWeeklyReport } from "../Types/goTypes"; // This type of pattern should be hard to misuse interface APIResponse { @@ -21,13 +20,8 @@ interface API { project: NewProject, token: string, ): Promise>; - /** Submit a weekly report */ - submitWeeklyReport(project: NewWeeklyReport, token: string): Promise>; /** Renew the token */ renewToken(token: string): Promise>; - /** Gets all the projects of a user*/ - getUserProjects(username: string, token: string): Promise>; - } // Export an instance of the API @@ -55,7 +49,7 @@ export const api: API = { async removeUser( username: string, - token: string + token: string, ): Promise> { try { const response = await fetch("/api/userdelete", { @@ -80,7 +74,7 @@ export const api: API = { async createProject( project: NewProject, - token: string + token: string, ): Promise> { try { const response = await fetch("/api/project", { @@ -123,51 +117,4 @@ export const api: API = { return { success: false, message: "Failed to renew token" }; } }, - - async getUserProjects(token: string): Promise> { - try { - const response = await fetch("/api/getUserProjects", { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }); - - if (!response.ok) { - return Promise.resolve({ success: false, message: "Failed to get user projects" }); - } else { - const data = (await response.json()) as Project[]; - return Promise.resolve({ success: true, data }); - } - } - catch (e) { - return Promise.resolve({ success: false, message: "Failed to get user projects" }); - } - }, - - submitWeeklyReport: function (project: NewWeeklyReport, token: string): Promise> { - try { - return fetch("/api/submitWeeklyReport", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - body: JSON.stringify(project), - }) - .then((response) => { - if (!response.ok) { - return { success: false, message: "Failed to submit weekly report" }; - } else { - return response.json(); - } - }) - .then((data) => { - return { success: true, data }; - }); - } catch (e) { - return Promise.resolve({ success: false, message: "Failed to submit weekly report" }); - } - } }; diff --git a/frontend/src/Components/NewWeeklyReport.tsx b/frontend/src/Components/TimeReport.tsx similarity index 80% rename from frontend/src/Components/NewWeeklyReport.tsx rename to frontend/src/Components/TimeReport.tsx index 1f613ac..cb33ad9 100644 --- a/frontend/src/Components/NewWeeklyReport.tsx +++ b/frontend/src/Components/TimeReport.tsx @@ -1,32 +1,30 @@ import { useState } from "react"; -import { NewWeeklyReport } from "../Types/goTypes"; +import { TimeReport } from "../Types/TimeReport"; import { api } from "../API/API"; import { useNavigate } from "react-router-dom"; import Button from "./Button"; export default function NewTimeReport(): JSX.Element { - const [projectName, setProjectName] = useState(""); - const [week, setWeek] = useState(0); - const [developmentTime, setDevelopmentTime] = useState(0); - const [meetingTime, setMeetingTime] = useState(0); - const [adminTime, setAdminTime] = useState(0); - const [ownWorkTime, setOwnWorkTime] = useState(0); - const [studyTime, setStudyTime] = useState(0); - const [testingTime, setTestingTime] = useState(0); + const [week, setWeek] = useState(""); + const [development, setDevelopment] = useState("0"); + const [meeting, setMeeting] = useState("0"); + const [administration, setAdministration] = useState("0"); + const [ownwork, setOwnWork] = useState("0"); + const [studies, setStudies] = useState("0"); + const [testing, setTesting] = useState("0"); const handleNewTimeReport = async (): Promise => { - const newTimeReport: NewWeeklyReport = { - projectName, + const newTimeReport: TimeReport = { week, - developmentTime, - meetingTime, - adminTime, - ownWorkTime, - studyTime, - testingTime, + development, + meeting, + administration, + ownwork, + studies, + testing, }; await Promise.resolve(); - // await api.submitWeeklyReport(newTimeReport, token); Token is not defined yet + // await api.registerTimeReport(newTimeReport); This needs to be implemented! }; const navigate = useNavigate(); @@ -36,7 +34,7 @@ export default function NewTimeReport(): JSX.Element {
{ - if (week === 0) { + if (week === "") { alert("Please enter a week number"); e.preventDefault(); return; @@ -52,7 +50,7 @@ export default function NewTimeReport(): JSX.Element { type="week" placeholder="Week" onChange={(e) => { - const weekNumber = parseInt(e.target.value.split("-W")[1]); + const weekNumber = e.target.value.split("-W")[1]; setWeek(weekNumber); }} onKeyDown={(event) => { @@ -81,9 +79,9 @@ export default function NewTimeReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={developmentTime} + value={development} onChange={(e) => { - setDevelopmentTime(parseInt(e.target.value)); + setDevelopment(e.target.value); }} onKeyDown={(event) => { const keyValue = event.key; @@ -100,9 +98,9 @@ export default function NewTimeReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={meetingTime} + value={meeting} onChange={(e) => { - setMeetingTime(parseInt(e.target.value)); + setMeeting(e.target.value); }} onKeyDown={(event) => { const keyValue = event.key; @@ -119,9 +117,9 @@ export default function NewTimeReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={adminTime} + value={administration} onChange={(e) => { - setAdminTime(parseInt(e.target.value)); + setAdministration(e.target.value); }} onKeyDown={(event) => { const keyValue = event.key; @@ -138,9 +136,9 @@ export default function NewTimeReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={ownWorkTime} + value={ownwork} onChange={(e) => { - setOwnWorkTime(parseInt(e.target.value)); + setOwnWork(e.target.value); }} onKeyDown={(event) => { const keyValue = event.key; @@ -157,9 +155,9 @@ export default function NewTimeReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={studyTime} + value={studies} onChange={(e) => { - setStudyTime(parseInt(e.target.value)); + setStudies(e.target.value); }} onKeyDown={(event) => { const keyValue = event.key; @@ -176,9 +174,9 @@ export default function NewTimeReport(): JSX.Element { type="number" min="0" className="border-2 border-black rounded-md text-center w-1/2" - value={testingTime} + value={testing} onChange={(e) => { - setTestingTime(parseInt(e.target.value)); + setTesting(e.target.value); }} onKeyDown={(event) => { const keyValue = event.key; diff --git a/frontend/src/Pages/ProjectManagerPages/PMTotalTimeActivity.tsx b/frontend/src/Pages/ProjectManagerPages/PMTotalTimeActivity.tsx index 63e623a..5ae5a4b 100644 --- a/frontend/src/Pages/ProjectManagerPages/PMTotalTimeActivity.tsx +++ b/frontend/src/Pages/ProjectManagerPages/PMTotalTimeActivity.tsx @@ -1,6 +1,6 @@ import BasicWindow from "../../Components/BasicWindow"; import Button from "../../Components/Button"; -import TimeReport from "../../Components/NewWeeklyReport"; +import TimeReport from "../../Components/TimeReport"; function PMTotalTimeActivity(): JSX.Element { const content = ( diff --git a/frontend/src/Pages/ProjectManagerPages/PMViewUnsignedReport.tsx b/frontend/src/Pages/ProjectManagerPages/PMViewUnsignedReport.tsx index f9d5998..8bf9143 100644 --- a/frontend/src/Pages/ProjectManagerPages/PMViewUnsignedReport.tsx +++ b/frontend/src/Pages/ProjectManagerPages/PMViewUnsignedReport.tsx @@ -1,6 +1,6 @@ import BasicWindow from "../../Components/BasicWindow"; import Button from "../../Components/Button"; -import TimeReport from "../../Components/NewWeeklyReport"; +import TimeReport from "../../Components/TimeReport"; function PMViewUnsignedReport(): JSX.Element { const content = ( diff --git a/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx b/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx index 97e49e3..af3bbc1 100644 --- a/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx +++ b/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx @@ -1,6 +1,6 @@ import BasicWindow from "../../Components/BasicWindow"; import Button from "../../Components/Button"; -import NewTimeReport from "../../Components/NewWeeklyReport"; +import NewTimeReport from "../../Components/TimeReport"; function UserEditTimeReportPage(): JSX.Element { const content = ( diff --git a/frontend/src/Pages/UserPages/UserNewTimeReportPage.tsx b/frontend/src/Pages/UserPages/UserNewTimeReportPage.tsx index d4db5a4..ca84770 100644 --- a/frontend/src/Pages/UserPages/UserNewTimeReportPage.tsx +++ b/frontend/src/Pages/UserPages/UserNewTimeReportPage.tsx @@ -1,6 +1,6 @@ import BasicWindow from "../../Components/BasicWindow"; import Button from "../../Components/Button"; -import NewTimeReport from "../../Components/NewWeeklyReport"; +import NewTimeReport from "../../Components/TimeReport"; import { Link } from "react-router-dom"; function UserNewTimeReportPage(): JSX.Element { diff --git a/frontend/src/Types/goTypes.ts b/frontend/src/Types/goTypes.ts deleted file mode 100644 index 89084b7..0000000 --- a/frontend/src/Types/goTypes.ts +++ /dev/null @@ -1,88 +0,0 @@ -// Code generated by tygo. DO NOT EDIT. - -////////// -// source: WeeklyReport.go - -/** - * This is what should be submitted to the server, the username will be derived from the JWT token - */ -export interface NewWeeklyReport { - /** - * The name of the project, as it appears in the database - */ - projectName: string; - /** - * The week number - */ - week: number /* int */; - /** - * Total time spent on development - */ - developmentTime: number /* int */; - /** - * Total time spent in meetings - */ - meetingTime: number /* int */; - /** - * Total time spent on administrative tasks - */ - adminTime: number /* int */; - /** - * Total time spent on personal projects - */ - ownWorkTime: number /* int */; - /** - * Total time spent on studying - */ - studyTime: number /* int */; - /** - * Total time spent on testing - */ - testingTime: number /* int */; -} - -////////// -// source: project.go - -/** - * Project is a struct that holds the information about a project - */ -export interface Project { - id: number /* int */; - name: string; - description: string; - owner: string; -} -/** - * As it arrives from the client, Owner is derived from the JWT token - */ -export interface NewProject { - name: string; - description: string; -} - -////////// -// source: users.go - -/** - * User struct represents a user in the system - */ -export interface User { - userId: string; - username: string; - password: string; -} -/** - * Should be used when registering, for example - */ -export interface NewUser { - username: string; - password: string; -} -/** - * PublicUser represents a user that is safe to send over the API (no password) - */ -export interface PublicUser { - userId: string; - username: string; -} diff --git a/testing.py b/testing.py deleted file mode 100644 index d2c64fe..0000000 --- a/testing.py +++ /dev/null @@ -1,97 +0,0 @@ -import requests -import string -import random - - -def randomString(len=10): - """Generate a random string of fixed length""" - letters = string.ascii_lowercase - return "".join(random.choice(letters) for i in range(len)) - - -# Defined once per test run -username = randomString() -projectName = randomString() - -# The base URL of the API -base_url = "http://localhost:8080" - -# Endpoint to test -registerPath = base_url + "/api/register" -loginPath = base_url + "/api/login" -addProjectPath = base_url + "/api/project" -submitReportPath = base_url + "/api/submitReport" - - -# Posts the username and password to the register endpoint -def register(username: string, password: string): - print("Registering with username: ", username, " and password: ", password) - response = requests.post( - registerPath, json={"username": username, "password": password} - ) - print(response.text) - return response - - -# Posts the username and password to the login endpoint -def login(username: string, password: string): - print("Logging in with username: ", username, " and password: ", password) - response = requests.post( - loginPath, json={"username": username, "password": password} - ) - print(response.text) - return response - - -def test_login(): - response = login(username, "always_same") - assert response.status_code == 200, "Login failed" - print("Login successful") - return response.json()["token"] - - -def test_create_user(): - response = register(username, "always_same") - assert response.status_code == 200, "Registration failed" - print("Registration successful") - - -def test_add_project(): - loginResponse = login(username, "always_same") - token = loginResponse.json()["token"] - response = requests.post( - addProjectPath, - json={"name": projectName, "description": "This is a project"}, - headers={"Authorization": "Bearer " + token}, - ) - print(response.text) - assert response.status_code == 200, "Add project failed" - print("Add project successful") - - -def test_submit_report(): - token = login(username, "always_same").json()["token"] - response = requests.post( - submitReportPath, - json={ - "projectName": "report1", - "week": 1, - "developmentTime": 10, - "meetingTime": 5, - "adminTime": 5, - "ownWorkTime": 10, - "studyTime": 10, - "testingTime": 10, - }, - headers={"Authorization": "Bearer " + token}, - ) - print(response.text) - assert response.status_code == 200, "Submit report failed" - print("Submit report successful") - - -if __name__ == "__main__": - test_create_user() - test_login() - test_add_project() - test_submit_report()