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 (
"embed"
"encoding/json"
"errors"
"fmt"
"path/filepath"
"ttime/internal/types"
"github.com/gofiber/fiber/v2/log"
"github.com/jmoiron/sqlx"
_ "modernc.org/sqlite"
)
@ -41,6 +43,7 @@ type Database interface {
SignWeeklyReport(reportId int, projectManagerId int) error
IsSiteAdmin(username 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)
UpdateWeeklyReport(projectName string, userName string, week int, developmentTime int, meetingTime int, adminTime int, ownWorkTime int, studyTime int, testingTime int) error
RemoveProject(projectname string) error
@ -94,6 +97,17 @@ const removeUserFromProjectQuery = `DELETE FROM user_roles
WHERE user_id = (SELECT id FROM users WHERE username = ?)
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
func DbConnect(dbpath string) Database {
// Open the database
@ -111,6 +125,24 @@ func DbConnect(dbpath string) Database {
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 {
var dbPassword string
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"`
}
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 {
// The name of the project, as it appears in the database
ProjectName string `json:"projectName"`

View file

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

View file

@ -11,6 +11,7 @@ import {
NewProject,
WeeklyReport,
StrNameChange,
Statistics,
} from "../Types/goTypes";
/**
@ -258,6 +259,17 @@ interface API {
reportId: number,
token: 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 */
@ -962,4 +974,30 @@ export const api: API = {
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 */;
}
export interface Statistics {
totalDevelopmentTime: number /* int */;
totalMeetingTime: number /* int */;
totalAdminTime: number /* int */;
totalOwnWorkTime: number /* int */;
totalStudyTime: number /* int */;
totalTestingTime: number /* int */;
}
export interface UpdateWeeklyReport {
/**
* 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"
unsignReportPath = base_url + "/api/unsignReport"
deleteReportPath = base_url + "/api/deleteReport"
getStatisticsPath = base_url + "/api/getStatistics"
debug_output = False
@ -162,3 +163,11 @@ def deleteReport(report_id: int):
return requests.delete(
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")
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__":
@ -650,3 +690,4 @@ if __name__ == "__main__":
test_change_user_name()
test_update_weekly_report()
test_get_other_users_report_as_pm()
test_get_statistics()