getStatistics handler, db-interface and ts API

This commit is contained in:
Imbus 2024-04-14 07:49:39 +02:00
parent 2d2b63938c
commit fe9d5f74bb
8 changed files with 188 additions and 1 deletions

View file

@ -2,11 +2,13 @@ package database
import ( import (
"embed" "embed"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"path/filepath" "path/filepath"
"ttime/internal/types" "ttime/internal/types"
"github.com/gofiber/fiber/v2/log"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
_ "modernc.org/sqlite" _ "modernc.org/sqlite"
) )
@ -41,6 +43,7 @@ type Database interface {
SignWeeklyReport(reportId int, projectManagerId int) error SignWeeklyReport(reportId int, projectManagerId int) error
IsSiteAdmin(username string) (bool, error) IsSiteAdmin(username string) (bool, error)
IsProjectManager(username string, projectname string) (bool, error) IsProjectManager(username string, projectname string) (bool, error)
ReportStatistics(username string, projectName string) (*types.Statistics, error)
GetProjectTimes(projectName string) (map[string]int, error) GetProjectTimes(projectName string) (map[string]int, error)
UpdateWeeklyReport(projectName string, userName string, week int, developmentTime int, meetingTime int, adminTime int, ownWorkTime int, studyTime int, testingTime int) error UpdateWeeklyReport(projectName string, userName string, week int, developmentTime int, meetingTime int, adminTime int, ownWorkTime int, studyTime int, testingTime int) error
RemoveProject(projectname string) error RemoveProject(projectname string) error
@ -94,6 +97,17 @@ const removeUserFromProjectQuery = `DELETE FROM user_roles
WHERE user_id = (SELECT id FROM users WHERE username = ?) WHERE user_id = (SELECT id FROM users WHERE username = ?)
AND project_id = (SELECT id FROM projects WHERE name = ?)` AND project_id = (SELECT id FROM projects WHERE name = ?)`
const reportStatistics = `SELECT SUM(development_time) AS total_development_time,
SUM(meeting_time) AS total_meeting_time,
SUM(admin_time) AS total_admin_time,
SUM(own_work_time) AS total_own_work_time,
SUM(study_time) AS total_study_time,
SUM(testing_time) AS total_testing_time
FROM weekly_reports
WHERE user_id = (SELECT id FROM users WHERE username = ?)
AND project_id = (SELECT id FROM projects WHERE name = ?)
GROUP BY user_id, project_id`
// DbConnect connects to the database // DbConnect connects to the database
func DbConnect(dbpath string) Database { func DbConnect(dbpath string) Database {
// Open the database // Open the database
@ -111,6 +125,24 @@ func DbConnect(dbpath string) Database {
return &Db{db} return &Db{db}
} }
func (d *Db) ReportStatistics(username string, projectName string) (*types.Statistics, error) {
var result types.Statistics
err := d.Get(&result, reportStatistics, username, projectName)
if err != nil {
return nil, err
}
serialized, err := json.Marshal(result)
if err != nil {
return nil, err
}
log.Info(string(serialized))
return &result, nil
}
func (d *Db) CheckUser(username string, password string) bool { func (d *Db) CheckUser(username string, password string) bool {
var dbPassword string var dbPassword string
err := d.Get(&dbPassword, "SELECT password FROM users WHERE username = ?", username) err := d.Get(&dbPassword, "SELECT password FROM users WHERE username = ?", username)

View file

@ -0,0 +1,50 @@
package reports
import (
db "ttime/internal/database"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/log"
"github.com/golang-jwt/jwt/v5"
)
func GetStatistics(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)
// Extract project name from query parameters
projectName := c.Query("projectName")
log.Info(username, " trying to get statistics for project: ", projectName)
if projectName == "" {
log.Info("Missing project name")
return c.Status(400).SendString("Missing project name")
}
// If the user is not a project manager, they can't view statistics
pm, err := db.GetDb(c).IsProjectManager(username, projectName)
if err != nil {
log.Info("Error checking if user is project manager:", err)
return c.Status(500).SendString(err.Error())
}
if !pm {
log.Info("Unauthorized access")
return c.Status(403).SendString("Unauthorized access")
}
// Retrieve statistics for the project from the database
statistics, err := db.GetDb(c).ReportStatistics(username, projectName)
if err != nil {
log.Error("Error getting statistics for project:", projectName, ":", err)
return c.Status(500).SendString(err.Error())
}
log.Info("Returning statistics")
// Return the retrieved statistics
return c.JSON(statistics)
}

View file

@ -66,6 +66,15 @@ type WeeklyReport struct {
SignedBy *int `json:"signedBy" db:"signed_by"` SignedBy *int `json:"signedBy" db:"signed_by"`
} }
type Statistics struct {
TotalDevelopmentTime int `json:"totalDevelopmentTime" db:"total_development_time"`
TotalMeetingTime int `json:"totalMeetingTime" db:"total_meeting_time"`
TotalAdminTime int `json:"totalAdminTime" db:"total_admin_time"`
TotalOwnWorkTime int `json:"totalOwnWorkTime" db:"total_own_work_time"`
TotalStudyTime int `json:"totalStudyTime" db:"total_study_time"`
TotalTestingTime int `json:"totalTestingTime" db:"total_testing_time"`
}
type UpdateWeeklyReport struct { type UpdateWeeklyReport struct {
// The name of the project, as it appears in the database // The name of the project, as it appears in the database
ProjectName string `json:"projectName"` ProjectName string `json:"projectName"`

View file

@ -126,12 +126,12 @@ func main() {
api.Delete("/removeProject/:projectName", projects.RemoveProject) api.Delete("/removeProject/:projectName", projects.RemoveProject)
api.Delete("/project/:projectID", projects.DeleteProject) api.Delete("/project/:projectID", projects.DeleteProject)
// All report related routes // All report related routes
// reportGroup := api.Group("/report") // Not currently in use // reportGroup := api.Group("/report") // Not currently in use
api.Get("/getWeeklyReport", reports.GetWeeklyReport) api.Get("/getWeeklyReport", reports.GetWeeklyReport)
api.Get("/getUnsignedReports/:projectName", reports.GetUnsignedReports) api.Get("/getUnsignedReports/:projectName", reports.GetUnsignedReports)
api.Get("/getAllWeeklyReports/:projectName", reports.GetAllWeeklyReports) api.Get("/getAllWeeklyReports/:projectName", reports.GetAllWeeklyReports)
api.Get("/getStatistics", reports.GetStatistics)
api.Post("/submitWeeklyReport", reports.SubmitWeeklyReport) api.Post("/submitWeeklyReport", reports.SubmitWeeklyReport)
api.Put("/signReport/:reportId", reports.SignReport) api.Put("/signReport/:reportId", reports.SignReport)
api.Put("/updateWeeklyReport", reports.UpdateWeeklyReport) api.Put("/updateWeeklyReport", reports.UpdateWeeklyReport)

View file

@ -11,6 +11,7 @@ import {
NewProject, NewProject,
WeeklyReport, WeeklyReport,
StrNameChange, StrNameChange,
Statistics,
} from "../Types/goTypes"; } from "../Types/goTypes";
/** /**
@ -258,6 +259,17 @@ interface API {
reportId: number, reportId: number,
token: string, token: string,
): Promise<APIResponse<string>>; ): Promise<APIResponse<string>>;
/**
* Retrieves the total time spent on a project for a particular user (the user is determined by the token)
*
* @param {string} projectName The name of the project
* @param {string} token The authentication token
*/
getStatistics(
projectName: string,
token: string,
): Promise<APIResponse<Statistics>>;
} }
/** An instance of the API */ /** An instance of the API */
@ -962,4 +974,30 @@ export const api: API = {
return { success: false, message: "Failed to delete report" }; return { success: false, message: "Failed to delete report" };
} }
}, },
async getStatistics(
token: string,
projectName: string,
): Promise<APIResponse<Statistics>> {
try {
const response = await fetch(
`/api/getStatistics/?projectName=${projectName}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
},
);
if (!response.ok) {
return { success: false, message: "Failed to get statistics" };
} else {
const data = (await response.json()) as Statistics;
return { success: true, data };
}
} catch (e) {
return { success: false, message: "Failed to get statistics" };
}
},
}; };

View file

@ -124,6 +124,14 @@ export interface WeeklyReport {
*/ */
signedBy?: number /* int */; signedBy?: number /* int */;
} }
export interface Statistics {
totalDevelopmentTime: number /* int */;
totalMeetingTime: number /* int */;
totalAdminTime: number /* int */;
totalOwnWorkTime: number /* int */;
totalStudyTime: number /* int */;
totalTestingTime: number /* int */;
}
export interface UpdateWeeklyReport { export interface UpdateWeeklyReport {
/** /**
* The name of the project, as it appears in the database * The name of the project, as it appears in the database

View file

@ -36,6 +36,7 @@ removeProjectPath = base_url + "/api/removeProject"
promoteToPmPath = base_url + "/api/promoteToPm" promoteToPmPath = base_url + "/api/promoteToPm"
unsignReportPath = base_url + "/api/unsignReport" unsignReportPath = base_url + "/api/unsignReport"
deleteReportPath = base_url + "/api/deleteReport" deleteReportPath = base_url + "/api/deleteReport"
getStatisticsPath = base_url + "/api/getStatistics"
debug_output = False debug_output = False
@ -162,3 +163,11 @@ def deleteReport(report_id: int):
return requests.delete( return requests.delete(
deleteReportPath + "/" + str(report_id), deleteReportPath + "/" + str(report_id),
) )
def getStatistics(token: string, projectName: string):
response = requests.get(
getStatisticsPath,
headers = {"Authorization": "Bearer " + token},
params={"projectName": projectName}
)
return response.json()

View file

@ -625,6 +625,46 @@ def test_delete_report():
gprint("test_delete_report successful") gprint("test_delete_report successful")
def test_get_statistics():
# Create admin
admin_username = randomString()
admin_password = randomString()
project_name = "project" + randomString()
token = register_and_login(admin_username, admin_password)
response = create_project(token, project_name)
assert response.status_code == 200, "Create project failed"
response = submitReport(token, {
"projectName": project_name,
"week": 1,
"developmentTime": 10,
"meetingTime": 5,
"adminTime": 5,
"ownWorkTime": 10,
"studyTime": 10,
"testingTime": 10,
})
response = submitReport(token, {
"projectName": project_name,
"week": 2,
"developmentTime": 10,
"meetingTime": 5,
"adminTime": 5,
"ownWorkTime": 10,
"studyTime": 10,
"testingTime": 10,
})
assert response.status_code == 200, "Submit report failed"
stats = getStatistics(token, project_name)
assert stats["totalDevelopmentTime"] == 20, "Total development time is not correct"
gprint("test_get_statistics successful")
if __name__ == "__main__": if __name__ == "__main__":
@ -650,3 +690,4 @@ if __name__ == "__main__":
test_change_user_name() test_change_user_name()
test_update_weekly_report() test_update_weekly_report()
test_get_other_users_report_as_pm() test_get_other_users_report_as_pm()
test_get_statistics()