diff --git a/api/main.go b/api/main.go
index bc29b39..da68084 100644
--- a/api/main.go
+++ b/api/main.go
@@ -65,8 +65,7 @@ func New() *WebAPI {
api.router.POST("/project/create", LogRequestMiddleware(api.ProjectCreate))
api.router.GET("/project/get/:id", LogRequestMiddleware(api.ProjectGet))
api.router.POST("/project/update/:id", LogRequestMiddleware(api.ProjectUpdate))
- api.router.GET("/project/stats/:id", LogRequestMiddleware(api.ProjectGetStats))
- api.router.GET("/project/stats", LogRequestMiddleware(api.ProjectGetAllStats))
+ api.router.GET("/project/list", LogRequestMiddleware(api.ProjectGetAllProjects))
api.router.POST("/task/create", LogRequestMiddleware(api.TaskCreate))
api.router.GET("/task/get/:project", LogRequestMiddleware(api.TaskGetFromProject))
diff --git a/api/project.go b/api/project.go
index 84b7779..df6bfd2 100644
--- a/api/project.go
+++ b/api/project.go
@@ -43,16 +43,10 @@ type GetProjectResponse struct {
Project *storage.Project `json:"project,omitempty"`
}
-type GetProjectStatsResponse struct {
- Ok bool `json:"ok"`
- Message string `json:"message,omitempty"`
- Stats *storage.ProjectStats `json:"stats,omitempty"`
-}
-
-type GetAllProjectsStatsResponse struct {
- Ok bool `json:"ok"`
- Message string `json:"message,omitempty"`
- Stats *[]storage.ProjectStats `json:"stats,omitempty"`
+type GetAllProjectsResponse struct {
+ Ok bool `json:"ok"`
+ Message string `json:"message,omitempty"`
+ Projects *[]storage.Project `json:"projects,omitempty"`
}
func (api *WebAPI) ProjectCreate(r *Request) {
@@ -200,38 +194,12 @@ func (api *WebAPI) ProjectGet(r *Request) {
}
}
-func (api *WebAPI) ProjectGetStats(r *Request) {
+func (api *WebAPI) ProjectGetAllProjects(r *Request) {
- id, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64)
- if err != nil {
- r.Json(GetProjectStatsResponse{
- Ok: false,
- Message: "Could not parse request",
- }, 400)
- return
- }
+ projects := api.Database.GetAllProjects()
- stats := api.Database.GetProjectStats(id)
-
- if stats != nil && stats.Project != nil {
- r.OkJson(GetProjectStatsResponse{
- Ok: true,
- Stats: stats,
- })
- } else {
- r.Json(GetProjectStatsResponse{
- Ok: false,
- Message: "Project not found",
- }, 404)
- }
-}
-
-func (api *WebAPI) ProjectGetAllStats(r *Request) {
-
- stats := api.Database.GetAllProjectsStats()
-
- r.OkJson(GetAllProjectsStatsResponse{
- Ok: true,
- Stats: stats,
+ r.OkJson(GetAllProjectsResponse{
+ Ok: true,
+ Projects: projects,
})
}
diff --git a/schema.sql b/schema.sql
index 4098d24..1a7c13c 100755
--- a/schema.sql
+++ b/schema.sql
@@ -1,5 +1,6 @@
DROP TABLE IF EXISTS worker_identity, worker, project, task, log_entry,
- worker_has_access_to_project, manager, manager_has_role_on_project, project_monitoring, worker_verifies_task;
+ worker_has_access_to_project, manager, manager_has_role_on_project, project_monitoring_snapshot,
+ worker_verifies_task;
DROP TYPE IF EXISTS status;
DROP TYPE IF EXISTS log_level;
@@ -87,12 +88,15 @@ CREATE TABLE manager_has_role_on_project
project INTEGER REFERENCES project (id)
);
-CREATE TABLE project_monitoring
+CREATE TABLE project_monitoring_snapshot
(
- project INT REFERENCES project (id),
- new_task_count INT,
- failed_task_count INT,
- closed_task_count INT
+ project INT REFERENCES project (id),
+ new_task_count INT,
+ failed_task_count INT,
+ closed_task_count INT,
+ awaiting_verification_task_count INT,
+ worker_access_count INT,
+ timestamp INT
);
CREATE OR REPLACE FUNCTION on_task_delete_proc() RETURNS TRIGGER AS
diff --git a/storage/monitoring.go b/storage/monitoring.go
new file mode 100644
index 0000000..d3278b0
--- /dev/null
+++ b/storage/monitoring.go
@@ -0,0 +1,44 @@
+package storage
+
+type ProjectMonitoringSnapshot struct {
+ NewTaskCount int64
+ FailedTaskCount int64
+ ClosedTaskCount int64
+ WorkerAccessCount int64
+ TimeStamp int64
+}
+
+func (database *Database) MakeProjectSnapshots() {
+
+ db := database.getDB()
+
+ _, err := db.Exec(`
+ INSERT INTO project_monitoring_snapshot
+ (project, new_task_count, failed_task_count, closed_task_count, worker_access_count, timestamp)
+ SELECT id,
+ (SELECT COUNT(*) FROM task WHERE task.project = project.id AND status = 1),
+ (SELECT COUNT(*) FROM task WHERE task.project = project.id AND status = 2),
+ closed_task_count,
+ (SELECT COUNT(*) FROM worker_has_access_to_project wa WHERE wa.project = project.id),
+ extract(epoch from now() at time zone 'utc')
+ FROM project`)
+ handleErr(err)
+}
+
+func (database *Database) GetMonitoringSnapshots(pid int64, from int64, to int64) (ss *[]ProjectMonitoringSnapshot) {
+
+ db := database.getDB()
+
+ rows, err := db.Query(`SELECT new_task_count, failed_task_count, closed_task_count,
+ worker_access_count, timestamp FROM project_monitoring_snapshot
+ WHERE project=$1 AND timestamp BETWEEN $2 AND $3`, pid, from, to)
+ handleErr(err)
+
+ for rows.Next() {
+
+ s := ProjectMonitoringSnapshot{}
+ err := rows.Scan(&s.NewTaskCount, &s.FailedTaskCount, &s.ClosedTaskCount, &s.WorkerAccessCount, &s.TimeStamp)
+ handleErr(err)
+ }
+ return nil
+}
diff --git a/storage/project.go b/storage/project.go
index a7016f4..1eb6c98 100644
--- a/storage/project.go
+++ b/storage/project.go
@@ -22,14 +22,6 @@ type AssignedTasks struct {
TaskCount int64 `json:"task_count"`
}
-type ProjectStats struct {
- Project *Project `json:"project"`
- NewTaskCount int64 `json:"new_task_count"`
- FailedTaskCount int64 `json:"failed_task_count"`
- ClosedTaskCount int64 `json:"closed_task_count"`
- Assignees []*AssignedTasks `json:"assignees"`
-}
-
func (database *Database) SaveProject(project *Project) (int64, error) {
db := database.getDB()
id, projectErr := saveProject(project, db)
@@ -139,83 +131,28 @@ func (database *Database) UpdateProject(project *Project) error {
return nil
}
-func (database *Database) GetProjectStats(id int64) *ProjectStats {
-
- db := database.getDB()
- stats := ProjectStats{}
-
- stats.Project = getProject(id, db)
-
- if stats.Project != nil {
- row := db.QueryRow(`SELECT
- SUM(CASE WHEN status=1 THEN 1 ELSE 0 END) newCount,
- SUM(CASE WHEN status=2 THEN 1 ELSE 0 END) failedCount,
- SUM(CASE WHEN status=3 THEN 1 ELSE 0 END) closedCount
- FROM task WHERE project=$1 GROUP BY project`, id)
-
- err := row.Scan(&stats.NewTaskCount, &stats.FailedTaskCount, &stats.ClosedTaskCount)
- if err != nil {
- logrus.WithError(err).WithFields(logrus.Fields{
- "id": id,
- }).Trace("Get project stats: No task for this project")
-
- }
-
- rows, err := db.Query(`SELECT worker.alias, COUNT(*) as wc FROM TASK
- LEFT JOIN worker ON TASK.assignee = worker.id WHERE project=$1
- GROUP BY worker.id ORDER BY wc LIMIT 10`, id)
-
- stats.Assignees = []*AssignedTasks{}
-
- for rows.Next() {
- assignee := AssignedTasks{}
- var assigneeAlias sql.NullString
- err = rows.Scan(&assigneeAlias, &assignee.TaskCount)
- handleErr(err)
-
- if assigneeAlias.Valid {
- assignee.Assignee = assigneeAlias.String
- } else {
- assignee.Assignee = "unassigned"
- }
-
- stats.Assignees = append(stats.Assignees, &assignee)
- }
- }
-
- return &stats
-}
-
-func (database Database) GetAllProjectsStats() *[]ProjectStats {
- var statsList []ProjectStats
+func (database Database) GetAllProjects() *[]Project {
+ var projects []Project
db := database.getDB()
rows, err := db.Query(`SELECT
- SUM(CASE WHEN status= 1 THEN 1 ELSE 0 END) newCount,
- SUM(CASE WHEN status=2 THEN 1 ELSE 0 END) failedCount,
- SUM(CASE WHEN status=3 THEN 1 ELSE 0 END) closedCount,
- p.Id, p.priority, p.name, p.clone_url, p.git_repo, p.version, p.motd,
- p.public
- FROM task RIGHT JOIN project p on task.project = p.id
- GROUP BY p.id ORDER BY p.name`)
+ Id, priority, name, clone_url, git_repo, version, motd, public
+ FROM project
+ ORDER BY name`)
handleErr(err)
for rows.Next() {
- stats := ProjectStats{}
- p := &Project{}
- err := rows.Scan(&stats.NewTaskCount, &stats.FailedTaskCount, &stats.ClosedTaskCount,
- &p.Id, &p.Priority, &p.Name, &p.CloneUrl, &p.GitRepo, &p.Version, &p.Motd, &p.Public)
+ p := Project{}
+ err := rows.Scan(&p.Id, &p.Priority, &p.Name, &p.CloneUrl,
+ &p.GitRepo, &p.Version, &p.Motd, &p.Public)
handleErr(err)
-
- stats.Project = p
-
- statsList = append(statsList, stats)
+ projects = append(projects, p)
}
logrus.WithFields(logrus.Fields{
- "statsList": statsList,
+ "projects": projects,
}).Trace("Get all projects stats")
- return &statsList
+ return &projects
}
diff --git a/test/api_project_test.go b/test/api_project_test.go
index 1d42a19..9f62c39 100644
--- a/test/api_project_test.go
+++ b/test/api_project_test.go
@@ -122,84 +122,6 @@ func TestGetProjectNotFound(t *testing.T) {
}
}
-func TestGetProjectStats(t *testing.T) {
-
- r := createProject(api.CreateProjectRequest{
- Motd: "motd",
- Name: "Name",
- Version: "version",
- CloneUrl: "http://github.com/drone/test",
- GitRepo: "drone/test",
- Priority: 3,
- Public: true,
- })
-
- pid := r.Id
- worker := genWid()
-
- createTask(api.CreateTaskRequest{
- Priority: 1,
- Project: pid,
- MaxRetries: 0,
- Recipe: "{}",
- }, worker)
- createTask(api.CreateTaskRequest{
- Priority: 2,
- Project: pid,
- MaxRetries: 0,
- Recipe: "{}",
- }, worker)
- createTask(api.CreateTaskRequest{
- Priority: 3,
- Project: pid,
- MaxRetries: 0,
- Recipe: "{}",
- }, worker)
-
- stats := getProjectStats(pid)
-
- if stats.Ok != true {
- t.Error()
- }
-
- if stats.Stats.Project.Id != pid {
- t.Error()
- }
-
- if stats.Stats.NewTaskCount != 3 {
- t.Error()
- }
-
- if stats.Stats.Assignees[0].Assignee != "unassigned" {
- t.Error()
- }
- if stats.Stats.Assignees[0].TaskCount != 3 {
- t.Error()
- }
-}
-
-func TestGetProjectStatsNotFound(t *testing.T) {
-
- r := createProject(api.CreateProjectRequest{
- Motd: "eeeeeeeeej",
- Name: "Namaaaaaaaaaaaa",
- Version: "versionsssssssss",
- CloneUrl: "http://github.com/drone/test1",
- GitRepo: "drone/test1",
- Priority: 1,
- })
- s := getProjectStats(r.Id)
-
- if s.Ok != true {
- t.Error()
- }
-
- if s.Stats == nil {
- t.Error()
- }
-
-}
-
func TestUpdateProjectValid(t *testing.T) {
pid := createProject(api.CreateProjectRequest{
@@ -337,18 +259,6 @@ func getProject(id int64) (*api.GetProjectResponse, *http.Response) {
return &getResp, r
}
-func getProjectStats(id int64) *api.GetProjectStatsResponse {
-
- r := Get(fmt.Sprintf("/project/stats/%d", id), nil)
-
- var getResp api.GetProjectStatsResponse
- data, _ := ioutil.ReadAll(r.Body)
- err := json.Unmarshal(data, &getResp)
- handleErr(err)
-
- return &getResp
-}
-
func updateProject(request api.UpdateProjectRequest, pid int64) *api.UpdateProjectResponse {
r := Post(fmt.Sprintf("/project/update/%d", pid), request, nil)
diff --git a/test/schema.sql b/test/schema.sql
index 4098d24..1a7c13c 100755
--- a/test/schema.sql
+++ b/test/schema.sql
@@ -1,5 +1,6 @@
DROP TABLE IF EXISTS worker_identity, worker, project, task, log_entry,
- worker_has_access_to_project, manager, manager_has_role_on_project, project_monitoring, worker_verifies_task;
+ worker_has_access_to_project, manager, manager_has_role_on_project, project_monitoring_snapshot,
+ worker_verifies_task;
DROP TYPE IF EXISTS status;
DROP TYPE IF EXISTS log_level;
@@ -87,12 +88,15 @@ CREATE TABLE manager_has_role_on_project
project INTEGER REFERENCES project (id)
);
-CREATE TABLE project_monitoring
+CREATE TABLE project_monitoring_snapshot
(
- project INT REFERENCES project (id),
- new_task_count INT,
- failed_task_count INT,
- closed_task_count INT
+ project INT REFERENCES project (id),
+ new_task_count INT,
+ failed_task_count INT,
+ closed_task_count INT,
+ awaiting_verification_task_count INT,
+ worker_access_count INT,
+ timestamp INT
);
CREATE OR REPLACE FUNCTION on_task_delete_proc() RETURNS TRIGGER AS
diff --git a/web/angular/package-lock.json b/web/angular/package-lock.json
index 286d756..54ee2cf 100644
--- a/web/angular/package-lock.json
+++ b/web/angular/package-lock.json
@@ -787,6 +787,22 @@
}
}
},
+ "@ngx-translate/core": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-11.0.1.tgz",
+ "integrity": "sha512-nBCa1ZD9fAUY/3eskP3Lql2fNg8OMrYIej1/5GRsfcutx9tG/5fZLCv9m6UCw1aS+u4uK/vXjv1ctG/FdMvaWg==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "@ngx-translate/http-loader": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-4.0.0.tgz",
+ "integrity": "sha512-x8LumqydWD7eX9yQTAVeoCM9gFUIGVTUjZqbxdAUavAA3qVnk9wCQux7iHLPXpydl8vyQmLoPQR+fFU+DUDOMA==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
"@schematics/angular": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-7.2.2.tgz",
@@ -2099,6 +2115,39 @@
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
"dev": true
},
+ "chart.js": {
+ "version": "2.7.3",
+ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.7.3.tgz",
+ "integrity": "sha512-3+7k/DbR92m6BsMUYP6M0dMsMVZpMnwkUyNSAbqolHKsbIzH2Q4LWVEHHYq7v0fmEV8whXE0DrjANulw9j2K5g==",
+ "requires": {
+ "chartjs-color": "^2.1.0",
+ "moment": "^2.10.2"
+ }
+ },
+ "chartjs-color": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.2.0.tgz",
+ "integrity": "sha1-hKL7dVeH7YXDndbdjHsdiEKbrq4=",
+ "requires": {
+ "chartjs-color-string": "^0.5.0",
+ "color-convert": "^0.5.3"
+ },
+ "dependencies": {
+ "color-convert": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz",
+ "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0="
+ }
+ }
+ },
+ "chartjs-color-string": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.5.0.tgz",
+ "integrity": "sha512-amWNvCOXlOUYxZVDSa0YOab5K/lmEhbFNKI55PWc4mlv28BDzA7zaoQTGxSBgJMHIW+hGX8YUrvw/FH4LyhwSQ==",
+ "requires": {
+ "color-name": "^1.0.0"
+ }
+ },
"chokidar": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz",
@@ -2303,8 +2352,7 @@
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
- "dev": true
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"colors": {
"version": "1.1.2",
@@ -2333,7 +2381,8 @@
"commander": {
"version": "2.17.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
- "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg=="
+ "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
+ "dev": true
},
"commondir": {
"version": "1.0.1",
@@ -2695,270 +2744,6 @@
"integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=",
"dev": true
},
- "d3": {
- "version": "5.7.0",
- "resolved": "https://registry.npmjs.org/d3/-/d3-5.7.0.tgz",
- "integrity": "sha512-8KEIfx+dFm8PlbJN9PI0suazrZ41QcaAufsKE9PRcqYPWLngHIyWJZX96n6IQKePGgeSu0l7rtlueSSNq8Zc3g==",
- "requires": {
- "d3-array": "1",
- "d3-axis": "1",
- "d3-brush": "1",
- "d3-chord": "1",
- "d3-collection": "1",
- "d3-color": "1",
- "d3-contour": "1",
- "d3-dispatch": "1",
- "d3-drag": "1",
- "d3-dsv": "1",
- "d3-ease": "1",
- "d3-fetch": "1",
- "d3-force": "1",
- "d3-format": "1",
- "d3-geo": "1",
- "d3-hierarchy": "1",
- "d3-interpolate": "1",
- "d3-path": "1",
- "d3-polygon": "1",
- "d3-quadtree": "1",
- "d3-random": "1",
- "d3-scale": "2",
- "d3-scale-chromatic": "1",
- "d3-selection": "1",
- "d3-shape": "1",
- "d3-time": "1",
- "d3-time-format": "2",
- "d3-timer": "1",
- "d3-transition": "1",
- "d3-voronoi": "1",
- "d3-zoom": "1"
- }
- },
- "d3-array": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
- "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
- },
- "d3-axis": {
- "version": "1.0.12",
- "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz",
- "integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ=="
- },
- "d3-brush": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.6.tgz",
- "integrity": "sha512-lGSiF5SoSqO5/mYGD5FAeGKKS62JdA1EV7HPrU2b5rTX4qEJJtpjaGLJngjnkewQy7UnGstnFd3168wpf5z76w==",
- "requires": {
- "d3-dispatch": "1",
- "d3-drag": "1",
- "d3-interpolate": "1",
- "d3-selection": "1",
- "d3-transition": "1"
- }
- },
- "d3-chord": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz",
- "integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==",
- "requires": {
- "d3-array": "1",
- "d3-path": "1"
- }
- },
- "d3-collection": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz",
- "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A=="
- },
- "d3-color": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.2.3.tgz",
- "integrity": "sha512-x37qq3ChOTLd26hnps36lexMRhNXEtVxZ4B25rL0DVdDsGQIJGB18S7y9XDwlDD6MD/ZBzITCf4JjGMM10TZkw=="
- },
- "d3-contour": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz",
- "integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==",
- "requires": {
- "d3-array": "^1.1.1"
- }
- },
- "d3-dispatch": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.5.tgz",
- "integrity": "sha512-vwKx+lAqB1UuCeklr6Jh1bvC4SZgbSqbkGBLClItFBIYH4vqDJCA7qfoy14lXmJdnBOdxndAMxjCbImJYW7e6g=="
- },
- "d3-drag": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.3.tgz",
- "integrity": "sha512-8S3HWCAg+ilzjJsNtWW1Mutl74Nmzhb9yU6igspilaJzeZVFktmY6oO9xOh5TDk+BM2KrNFjttZNoJJmDnkjkg==",
- "requires": {
- "d3-dispatch": "1",
- "d3-selection": "1"
- }
- },
- "d3-dsv": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.0.10.tgz",
- "integrity": "sha512-vqklfpxmtO2ZER3fq/B33R/BIz3A1PV0FaZRuFM8w6jLo7sUX1BZDh73fPlr0s327rzq4H6EN1q9U+eCBCSN8g==",
- "requires": {
- "commander": "2",
- "iconv-lite": "0.4",
- "rw": "1"
- }
- },
- "d3-ease": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.5.tgz",
- "integrity": "sha512-Ct1O//ly5y5lFM9YTdu+ygq7LleSgSE4oj7vUt9tPLHUi8VCV7QoizGpdWRWAwCO9LdYzIrQDg97+hGVdsSGPQ=="
- },
- "d3-fetch": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.1.2.tgz",
- "integrity": "sha512-S2loaQCV/ZeyTyIF2oP8D1K9Z4QizUzW7cWeAOAS4U88qOt3Ucf6GsmgthuYSdyB2HyEm4CeGvkQxWsmInsIVA==",
- "requires": {
- "d3-dsv": "1"
- }
- },
- "d3-force": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.1.2.tgz",
- "integrity": "sha512-p1vcHAUF1qH7yR+e8ip7Bs61AHjLeKkIn8Z2gzwU2lwEf2wkSpWdjXG0axudTHsVFnYGlMkFaEsVy2l8tAg1Gw==",
- "requires": {
- "d3-collection": "1",
- "d3-dispatch": "1",
- "d3-quadtree": "1",
- "d3-timer": "1"
- }
- },
- "d3-format": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.3.2.tgz",
- "integrity": "sha512-Z18Dprj96ExragQ0DeGi+SYPQ7pPfRMtUXtsg/ChVIKNBCzjO8XYJvRTC1usblx52lqge56V5ect+frYTQc8WQ=="
- },
- "d3-geo": {
- "version": "1.11.3",
- "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.11.3.tgz",
- "integrity": "sha512-n30yN9qSKREvV2fxcrhmHUdXP9TNH7ZZj3C/qnaoU0cVf/Ea85+yT7HY7i8ySPwkwjCNYtmKqQFTvLFngfkItQ==",
- "requires": {
- "d3-array": "1"
- }
- },
- "d3-hierarchy": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.8.tgz",
- "integrity": "sha512-L+GHMSZNwTpiq4rt9GEsNcpLa4M96lXMR8M/nMG9p5hBE0jy6C+3hWtyZMenPQdwla249iJy7Nx0uKt3n+u9+w=="
- },
- "d3-interpolate": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.3.2.tgz",
- "integrity": "sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w==",
- "requires": {
- "d3-color": "1"
- }
- },
- "d3-path": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.7.tgz",
- "integrity": "sha512-q0cW1RpvA5c5ma2rch62mX8AYaiLX0+bdaSM2wxSU9tXjU4DNvkx9qiUvjkuWCj3p22UO/hlPivujqMiR9PDzA=="
- },
- "d3-polygon": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.5.tgz",
- "integrity": "sha512-RHhh1ZUJZfhgoqzWWuRhzQJvO7LavchhitSTHGu9oj6uuLFzYZVeBzaWTQ2qSO6bz2w55RMoOCf0MsLCDB6e0w=="
- },
- "d3-quadtree": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.5.tgz",
- "integrity": "sha512-U2tjwDFbZ75JRAg8A+cqMvqPg1G3BE7UTJn3h8DHjY/pnsAfWdbJKgyfcy7zKjqGtLAmI0q8aDSeG1TVIKRaHQ=="
- },
- "d3-random": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz",
- "integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ=="
- },
- "d3-scale": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.1.2.tgz",
- "integrity": "sha512-bESpd64ylaKzCDzvULcmHKZTlzA/6DGSVwx7QSDj/EnX9cpSevsdiwdHFYI9ouo9tNBbV3v5xztHS2uFeOzh8Q==",
- "requires": {
- "d3-array": "^1.2.0",
- "d3-collection": "1",
- "d3-format": "1",
- "d3-interpolate": "1",
- "d3-time": "1",
- "d3-time-format": "2"
- }
- },
- "d3-scale-chromatic": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.3.3.tgz",
- "integrity": "sha512-BWTipif1CimXcYfT02LKjAyItX5gKiwxuPRgr4xM58JwlLocWbjPLI7aMEjkcoOQXMkYsmNsvv3d2yl/OKuHHw==",
- "requires": {
- "d3-color": "1",
- "d3-interpolate": "1"
- }
- },
- "d3-selection": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.3.2.tgz",
- "integrity": "sha512-OoXdv1nZ7h2aKMVg3kaUFbLLK5jXUFAMLD/Tu5JA96mjf8f2a9ZUESGY+C36t8R1WFeWk/e55hy54Ml2I62CRQ=="
- },
- "d3-shape": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.2.tgz",
- "integrity": "sha512-hUGEozlKecFZ2bOSNt7ENex+4Tk9uc/m0TtTEHBvitCBxUNjhzm5hS2GrrVRD/ae4IylSmxGeqX5tWC2rASMlQ==",
- "requires": {
- "d3-path": "1"
- }
- },
- "d3-time": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.10.tgz",
- "integrity": "sha512-hF+NTLCaJHF/JqHN5hE8HVGAXPStEq6/omumPE/SxyHVrR7/qQxusFDo0t0c/44+sCGHthC7yNGFZIEgju0P8g=="
- },
- "d3-time-format": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.3.tgz",
- "integrity": "sha512-6k0a2rZryzGm5Ihx+aFMuO1GgelgIz+7HhB4PH4OEndD5q2zGn1mDfRdNrulspOfR6JXkb2sThhDK41CSK85QA==",
- "requires": {
- "d3-time": "1"
- }
- },
- "d3-timer": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.9.tgz",
- "integrity": "sha512-rT34J5HnQUHhcLvhSB9GjCkN0Ddd5Y8nCwDBG2u6wQEeYxT/Lf51fTFFkldeib/sE/J0clIe0pnCfs6g/lRbyg=="
- },
- "d3-transition": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.3.tgz",
- "integrity": "sha512-tEvo3qOXL6pZ1EzcXxFcPNxC/Ygivu5NoBY6mbzidATAeML86da+JfVIUzon3dNM6UX6zjDx+xbYDmMVtTSjuA==",
- "requires": {
- "d3-color": "1",
- "d3-dispatch": "1",
- "d3-ease": "1",
- "d3-interpolate": "1",
- "d3-selection": "^1.1.0",
- "d3-timer": "1"
- }
- },
- "d3-voronoi": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz",
- "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg=="
- },
- "d3-zoom": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.7.3.tgz",
- "integrity": "sha512-xEBSwFx5Z9T3/VrwDkMt+mr0HCzv7XjpGURJ8lWmIC8wxe32L39eWHIasEe/e7Ox8MPU4p1hvH8PKN2olLzIBg==",
- "requires": {
- "d3-dispatch": "1",
- "d3-drag": "1",
- "d3-interpolate": "1",
- "d3-selection": "1",
- "d3-transition": "1"
- }
- },
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
@@ -5235,6 +5020,7 @@
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
+ "dev": true,
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
@@ -8817,11 +8603,6 @@
"aproba": "^1.1.1"
}
},
- "rw": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
- "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q="
- },
"rxjs": {
"version": "6.3.3",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
@@ -8848,7 +8629,8 @@
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
},
"sass-graph": {
"version": "2.2.4",
diff --git a/web/angular/package.json b/web/angular/package.json
index d3310db..2bbe0d5 100644
--- a/web/angular/package.json
+++ b/web/angular/package.json
@@ -21,8 +21,10 @@
"@angular/platform-browser": "~7.2.0",
"@angular/platform-browser-dynamic": "~7.2.0",
"@angular/router": "~7.2.0",
+ "@ngx-translate/core": "^11.0.1",
+ "@ngx-translate/http-loader": "^4.0.0",
+ "chart.js": "^2.7.3",
"core-js": "^2.5.4",
- "d3": "^5.7.0",
"lodash": "^4.17.11",
"moment": "^2.23.0",
"rxjs": "~6.3.3",
@@ -34,9 +36,9 @@
"@angular/cli": "~7.2.2",
"@angular/compiler-cli": "~7.2.0",
"@angular/language-service": "~7.2.0",
- "@types/node": "~8.9.4",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
+ "@types/node": "~8.9.4",
"codelyzer": "~4.5.0",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
diff --git a/web/angular/src/app/api.service.ts b/web/angular/src/app/api.service.ts
index 4ed0249..4d60604 100755
--- a/web/angular/src/app/api.service.ts
+++ b/web/angular/src/app/api.service.ts
@@ -16,12 +16,8 @@ export class ApiService {
return this.http.post(this.url + "/logs", "{\"level\":\"info\", \"since\":10000}");
}
- getProjectStats(id: number) {
- return this.http.get(this.url + "/project/stats/" + id)
- }
-
getProjects() {
- return this.http.get(this.url + "/project/stats")
+ return this.http.get(this.url + "/project/list")
}
getProject(id: number) {
diff --git a/web/angular/src/app/app.component.css b/web/angular/src/app/app.component.css
index e69de29..37aa71c 100755
--- a/web/angular/src/app/app.component.css
+++ b/web/angular/src/app/app.component.css
@@ -0,0 +1,3 @@
+.nav-spacer {
+ flex: 1 1 auto;
+}
diff --git a/web/angular/src/app/app.component.html b/web/angular/src/app/app.component.html
index 0d02342..f8509a6 100755
--- a/web/angular/src/app/app.component.html
+++ b/web/angular/src/app/app.component.html
@@ -1,11 +1,23 @@
-
+
{{stats.project | json}}+
{{project | json}}