mirror of
https://github.com/simon987/task_tracker.git
synced 2025-04-19 18:16:45 +00:00
Worker management UI for managers
This commit is contained in:
parent
72c8e18044
commit
39dd89b001
@ -72,6 +72,14 @@ func (api *WebAPI) GetWorker(r *Request) {
|
|||||||
|
|
||||||
if worker != nil {
|
if worker != nil {
|
||||||
|
|
||||||
|
sess := api.Session.StartFasthttp(r.Ctx)
|
||||||
|
manager := sess.Get("manager")
|
||||||
|
|
||||||
|
var secret []byte = nil
|
||||||
|
if manager != nil && manager.(*storage.Manager).WebsiteAdmin {
|
||||||
|
secret = worker.Secret
|
||||||
|
}
|
||||||
|
|
||||||
r.OkJson(JsonResponse{
|
r.OkJson(JsonResponse{
|
||||||
Ok: true,
|
Ok: true,
|
||||||
Content: GetWorkerResponse{
|
Content: GetWorkerResponse{
|
||||||
@ -79,6 +87,8 @@ func (api *WebAPI) GetWorker(r *Request) {
|
|||||||
Alias: worker.Alias,
|
Alias: worker.Alias,
|
||||||
Id: worker.Id,
|
Id: worker.Id,
|
||||||
Created: worker.Created,
|
Created: worker.Created,
|
||||||
|
Paused: worker.Paused,
|
||||||
|
Secret: secret,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -15,6 +15,8 @@ type Worker struct {
|
|||||||
type WorkerStats struct {
|
type WorkerStats struct {
|
||||||
Alias string `json:"alias"`
|
Alias string `json:"alias"`
|
||||||
ClosedTaskCount int64 `json:"closed_task_count"`
|
ClosedTaskCount int64 `json:"closed_task_count"`
|
||||||
|
Paused bool `json:"paused"`
|
||||||
|
Id int64 `json:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WorkerAccess struct {
|
type WorkerAccess struct {
|
||||||
@ -173,7 +175,7 @@ func (database *Database) GetAllAccesses(projectId int64) *[]WorkerAccess {
|
|||||||
|
|
||||||
db := database.getDB()
|
db := database.getDB()
|
||||||
|
|
||||||
rows, err := db.Query(`SELECT id, alias, created, role_assign, role_submit, request
|
rows, err := db.Query(`SELECT id, alias, created, paused, role_assign, role_submit, request
|
||||||
FROM worker_access
|
FROM worker_access
|
||||||
INNER JOIN worker w on worker_access.worker = w.id
|
INNER JOIN worker w on worker_access.worker = w.id
|
||||||
WHERE project=$1 ORDER BY request, alias`,
|
WHERE project=$1 ORDER BY request, alias`,
|
||||||
@ -186,7 +188,7 @@ func (database *Database) GetAllAccesses(projectId int64) *[]WorkerAccess {
|
|||||||
wa := WorkerAccess{
|
wa := WorkerAccess{
|
||||||
Project: projectId,
|
Project: projectId,
|
||||||
}
|
}
|
||||||
_ = rows.Scan(&wa.Worker.Id, &wa.Worker.Alias, &wa.Worker.Created,
|
_ = rows.Scan(&wa.Worker.Id, &wa.Worker.Alias, &wa.Worker.Created, &wa.Worker.Paused,
|
||||||
&wa.Assign, &wa.Submit, &wa.Request)
|
&wa.Assign, &wa.Submit, &wa.Request)
|
||||||
requests = append(requests, wa)
|
requests = append(requests, wa)
|
||||||
}
|
}
|
||||||
@ -197,13 +199,15 @@ func (database *Database) GetAllAccesses(projectId int64) *[]WorkerAccess {
|
|||||||
func (database *Database) GetAllWorkerStats() *[]WorkerStats {
|
func (database *Database) GetAllWorkerStats() *[]WorkerStats {
|
||||||
|
|
||||||
db := database.getDB()
|
db := database.getDB()
|
||||||
rows, err := db.Query(`SELECT alias, closed_task_count FROM worker WHERE closed_task_count>0 LIMIT 50`)
|
rows, err := db.Query(`SELECT alias, closed_task_count, paused, worker.id
|
||||||
|
FROM worker WHERE closed_task_count>0 LIMIT 50`)
|
||||||
|
|
||||||
handleErr(err)
|
handleErr(err)
|
||||||
|
|
||||||
stats := make([]WorkerStats, 0)
|
stats := make([]WorkerStats, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
s := WorkerStats{}
|
s := WorkerStats{}
|
||||||
_ = rows.Scan(&s.Alias, &s.ClosedTaskCount)
|
_ = rows.Scan(&s.Alias, &s.ClosedTaskCount, &s.Paused, &s.Id)
|
||||||
stats = append(stats, s)
|
stats = append(stats, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -636,6 +636,10 @@ func TestTaskChainUpdateRequiresRole(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetAccessList(t *testing.T) {
|
||||||
|
//TODO!
|
||||||
|
}
|
||||||
|
|
||||||
func createProjectAsAdmin(req api.CreateProjectRequest) CreateProjectAR {
|
func createProjectAsAdmin(req api.CreateProjectRequest) CreateProjectAR {
|
||||||
return createProject(req, testAdminCtx)
|
return createProject(req, testAdminCtx)
|
||||||
}
|
}
|
||||||
|
@ -126,4 +126,13 @@ export class ApiService {
|
|||||||
return this.http.post(this.url + `/project/reclaim_assigned_tasks/${pid}`, null, this.options);
|
return this.http.post(this.url + `/project/reclaim_assigned_tasks/${pid}`, null, this.options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
workerSetPaused(wid: number, paused: boolean) {
|
||||||
|
return this.http.post(this.url + '/worker/set_paused',
|
||||||
|
{'worker': wid, 'paused': paused}, this.options);
|
||||||
|
}
|
||||||
|
|
||||||
|
getWorker(wid: number) {
|
||||||
|
return this.http.get(this.url + `/worker/get/${wid}`, this.options);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,4 +3,5 @@ export interface Worker {
|
|||||||
alias: string;
|
alias: string;
|
||||||
created: number;
|
created: number;
|
||||||
secret: string;
|
secret: string;
|
||||||
|
paused: boolean;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,10 @@ button {
|
|||||||
color: #757575;
|
color: #757575;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.paused {
|
||||||
|
color: #757575;
|
||||||
|
}
|
||||||
|
|
||||||
mat-checkbox {
|
mat-checkbox {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
@ -12,16 +12,24 @@
|
|||||||
<mat-card-content *ngIf="!(unauthorized || !auth.account)">
|
<mat-card-content *ngIf="!(unauthorized || !auth.account)">
|
||||||
<h3>{{"perms.workers" | translate}}</h3>
|
<h3>{{"perms.workers" | translate}}</h3>
|
||||||
<mat-list *ngIf="accesses && accesses.length>0">
|
<mat-list *ngIf="accesses && accesses.length>0">
|
||||||
<mat-list-item *ngFor="let wa of accesses" [class.request]="wa.request">
|
<mat-list-item *ngFor="let wa of accesses" [class.request]="wa.request"
|
||||||
|
[class.paused]="wa.worker.paused">
|
||||||
|
<mat-icon mat-list-icon *ngIf="wa.worker.paused" [title]="'workers.paused'|translate">pause
|
||||||
|
</mat-icon>
|
||||||
<mat-icon mat-list-icon *ngIf="wa.submit" [title]="'perms.submit'|translate">library_add</mat-icon>
|
<mat-icon mat-list-icon *ngIf="wa.submit" [title]="'perms.submit'|translate">library_add</mat-icon>
|
||||||
<mat-icon mat-list-icon *ngIf="wa.assign" [title]="'perms.assign'|translate">get_app</mat-icon>
|
<mat-icon mat-list-icon *ngIf="wa.assign" [title]="'perms.assign'|translate">get_app</mat-icon>
|
||||||
<h4 mat-line>{{wa.worker.alias}} {{wa.request ? ('perms.pending' | translate) : ''}}</h4>
|
<h4 mat-line>{{wa.worker.alias}} {{wa.request ? ('perms.pending' | translate) : ''}}
|
||||||
|
{{wa.worker.paused ? ('workers.paused' | translate) : ''}}</h4>
|
||||||
<div mat-line>
|
<div mat-line>
|
||||||
Id=<span class="text-mono">{{wa.worker.id}}</span>, {{"perms.created" | translate}}
|
Id=<span class="text-mono">{{wa.worker.id}}</span>, {{"perms.created" | translate}}
|
||||||
<span
|
<span
|
||||||
class="text-mono">{{moment.unix(wa.worker.created).utc().format("UTC YYYY-MM-DD HH:mm:ss")}}</span>
|
class="text-mono">{{moment.unix(wa.worker.created).utc().format("UTC YYYY-MM-DD HH:mm:ss")}}</span>
|
||||||
</div>
|
</div>
|
||||||
<span style="flex: 1 1 auto;"></span>
|
<span style="flex: 1 1 auto;"></span>
|
||||||
|
<button mat-raised-button color="secondary" [title]="'workers.pause' | translate"
|
||||||
|
(click)="togglePaused(wa.worker)">
|
||||||
|
<mat-icon>pause</mat-icon>
|
||||||
|
</button>
|
||||||
<button mat-raised-button color="primary" [title]="'perms.grant' | translate"
|
<button mat-raised-button color="primary" [title]="'perms.grant' | translate"
|
||||||
*ngIf="wa.request" (click)="acceptRequest(wa)">
|
*ngIf="wa.request" (click)="acceptRequest(wa)">
|
||||||
<mat-icon>check</mat-icon>
|
<mat-icon>check</mat-icon>
|
||||||
|
@ -9,6 +9,7 @@ import {AuthService} from '../auth.service';
|
|||||||
import {Manager, ManagerRoleOnProject} from '../models/manager';
|
import {Manager, ManagerRoleOnProject} from '../models/manager';
|
||||||
import {MessengerService} from '../messenger.service';
|
import {MessengerService} from '../messenger.service';
|
||||||
import {TranslateService} from '@ngx-translate/core';
|
import {TranslateService} from '@ngx-translate/core';
|
||||||
|
import {Worker} from "../models/worker";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-project-perms',
|
selector: 'app-project-perms',
|
||||||
@ -56,6 +57,14 @@ export class ProjectPermsComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public togglePaused(w: Worker) {
|
||||||
|
this.apiService.workerSetPaused(w.id, !w.paused)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.getProjectAccesses();
|
||||||
|
this.translate.get('perms.set').subscribe(t => this.messenger.show(t));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private getProject() {
|
private getProject() {
|
||||||
this.apiService.getProject(this.projectId).subscribe(data => {
|
this.apiService.getProject(this.projectId).subscribe(data => {
|
||||||
this.project = data['content']['project'];
|
this.project = data['content']['project'];
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
button {
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
@ -14,5 +14,32 @@
|
|||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<canvas id="worker-stats"></canvas>
|
<canvas id="worker-stats"></canvas>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
|
|
||||||
|
|
||||||
|
<mat-expansion-panel *ngIf="authService.logged" class="">
|
||||||
|
<mat-expansion-panel-header>
|
||||||
|
<mat-panel-title>{{"workers.manage" | translate}}</mat-panel-title>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
|
||||||
|
<mat-list>
|
||||||
|
<mat-list-item *ngFor="let worker of workers" [class.paused]="worker.paused">
|
||||||
|
<mat-icon mat-list-icon *ngIf="worker.paused" [title]="'workers.paused'|translate">pause</mat-icon>
|
||||||
|
<mat-icon mat-list-icon *ngIf="!worker.paused">_blank_</mat-icon>
|
||||||
|
<h4 mat-line>{{worker.alias}}</h4>
|
||||||
|
<div mat-line *ngIf="workerInfo && workerInfo.id === worker.id">
|
||||||
|
<p class="text-mono">{{workerInfo|json}}</p>
|
||||||
|
</div>
|
||||||
|
<span style="flex: 1 1 auto;"></span>
|
||||||
|
<button mat-raised-button color="secondary" [title]="'workers.pause' | translate"
|
||||||
|
(click)="togglePaused(worker)">
|
||||||
|
<mat-icon>pause</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-raised-button color="secondary" [title]="'workers.info' | translate"
|
||||||
|
(click)="getInfo(worker)">
|
||||||
|
<mat-icon>info</mat-icon>
|
||||||
|
</button>
|
||||||
|
</mat-list-item>
|
||||||
|
</mat-list>
|
||||||
|
</mat-expansion-panel>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,6 +2,10 @@ import {Component, OnInit} from '@angular/core';
|
|||||||
import {ApiService} from '../api.service';
|
import {ApiService} from '../api.service';
|
||||||
|
|
||||||
import {Chart} from 'chart.js';
|
import {Chart} from 'chart.js';
|
||||||
|
import {AuthService} from "../auth.service";
|
||||||
|
import {Worker} from "../models/worker";
|
||||||
|
import {TranslateService} from "@ngx-translate/core";
|
||||||
|
import {MessengerService} from "../messenger.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-worker-dashboard',
|
selector: 'app-worker-dashboard',
|
||||||
@ -11,8 +15,13 @@ import {Chart} from 'chart.js';
|
|||||||
export class WorkerDashboardComponent implements OnInit {
|
export class WorkerDashboardComponent implements OnInit {
|
||||||
|
|
||||||
private chart: Chart;
|
private chart: Chart;
|
||||||
|
workers: Worker[];
|
||||||
|
workerInfo: Worker;
|
||||||
|
|
||||||
constructor(private apiService: ApiService) {
|
constructor(private apiService: ApiService,
|
||||||
|
private translate: TranslateService,
|
||||||
|
private messenger: MessengerService,
|
||||||
|
public authService: AuthService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@ -20,10 +29,36 @@ export class WorkerDashboardComponent implements OnInit {
|
|||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public togglePaused(w: Worker) {
|
||||||
|
|
||||||
|
this.workerInfo = undefined;
|
||||||
|
|
||||||
|
this.apiService.workerSetPaused(w.id, !w.paused)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.refresh();
|
||||||
|
this.translate.get('perms.set').subscribe(t => this.messenger.show(t));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getInfo(w: Worker) {
|
||||||
|
|
||||||
|
if (this.workerInfo && this.workerInfo.id == w.id) {
|
||||||
|
this.workerInfo = undefined;
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.apiService.getWorker(w.id)
|
||||||
|
.subscribe(data => {
|
||||||
|
this.workerInfo = data['content']['worker'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public refresh() {
|
public refresh() {
|
||||||
this.apiService.getWorkerStats()
|
this.apiService.getWorkerStats()
|
||||||
.subscribe(data => {
|
.subscribe(data => {
|
||||||
this.updateChart(data['content']['stats']);
|
this.updateChart(data['content']['stats']);
|
||||||
|
this.workers = data['content']['stats'].sort((a, b) =>
|
||||||
|
(a.alias > b.alias) ? 1 : -1);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,10 @@
|
|||||||
},
|
},
|
||||||
"workers": {
|
"workers": {
|
||||||
"title": "Completed tasks per worker",
|
"title": "Completed tasks per worker",
|
||||||
"subtitle": "Real-time data for all projects"
|
"subtitle": "Real-time data for all projects",
|
||||||
|
"paused": "(paused)",
|
||||||
|
"pause": "Pause",
|
||||||
|
"manage": "Manager workers"
|
||||||
},
|
},
|
||||||
"perms": {
|
"perms": {
|
||||||
"title": "Project permissions",
|
"title": "Project permissions",
|
||||||
|
@ -109,7 +109,10 @@
|
|||||||
},
|
},
|
||||||
"workers": {
|
"workers": {
|
||||||
"title": "Tâches complétés par worker",
|
"title": "Tâches complétés par worker",
|
||||||
"subtitle": "Données en temps réél pour tous les projets"
|
"subtitle": "Données en temps réél pour tous les projets",
|
||||||
|
"paused": "(en pause)",
|
||||||
|
"pause": "Pauser",
|
||||||
|
"manage": "Gérer les workers"
|
||||||
},
|
},
|
||||||
"perms": {
|
"perms": {
|
||||||
"title": "Permissions du projet",
|
"title": "Permissions du projet",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user