Some work on project actions

This commit is contained in:
simon987 2019-02-22 20:44:27 -05:00
parent 016676e0f3
commit d44b9924e7
25 changed files with 237 additions and 30 deletions

View File

@ -148,6 +148,7 @@ type UpdateProjectRequest struct {
Public bool `json:"public"`
Hidden bool `json:"hidden"`
Chain int64 `json:"chain"`
Paused bool `json:"paused"`
}
func (req *UpdateProjectRequest) isValid() bool {
@ -204,6 +205,9 @@ func (req *SubmitTaskRequest) IsValid() bool {
if req.Hash64 != 0 && req.UniqueString != "" {
return false
}
if req.Project == 0 {
return false
}
return true
}

View File

@ -158,6 +158,7 @@ func (api *WebAPI) UpdateProject(r *Request) {
Public: updateReq.Public,
Hidden: updateReq.Hidden,
Chain: updateReq.Chain,
Paused: updateReq.Paused,
}
sess := api.Session.StartFasthttp(r.Ctx)
manager := sess.Get("manager")

View File

@ -2,7 +2,7 @@ server:
address: "0.0.0.0:42901"
database:
conn_str: "user=task_tracker dbname=task_tracker sslmode=disable"
conn_str: "user=task_tracker password=task_tracker dbname=task_tracker sslmode=disable"
# log_levels: ["debug", "error", "trace", "info", "warn"]
log_levels: ["error", "info", "warn"]
@ -19,8 +19,8 @@ log:
session:
cookie_name: "tt"
expiration: "25m"
expiration: "8h"
monitoring:
snapshot_interval: "60s"
snapshot_interval: "120s"
history_length: "400h"

View File

@ -19,6 +19,7 @@ CREATE TABLE project
chain INT DEFAULT NULL REFERENCES project (id),
public boolean NOT NULL,
hidden boolean NOT NULL,
paused boolean NOT NULL,
name TEXT UNIQUE NOT NULL,
clone_url TEXT NOT NULL,
git_repo TEXT UNIQUE NOT NULL,

View File

@ -17,6 +17,7 @@ type Project struct {
Public bool `json:"public"`
Hidden bool `json:"hidden"`
Chain int64 `json:"chain"`
Paused bool `json:"paused"`
}
type AssignedTasks struct {
@ -28,10 +29,10 @@ func (database *Database) SaveProject(project *Project) (int64, error) {
db := database.getDB()
row := db.QueryRow(`INSERT INTO project (name, git_repo, clone_url, version, priority,
motd, public, hidden, chain)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,NULLIF($9, 0)) RETURNING id`,
motd, public, hidden, chain, paused)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,NULLIF($9, 0),$10) RETURNING id`,
project.Name, project.GitRepo, project.CloneUrl, project.Version, project.Priority, project.Motd,
project.Public, project.Hidden, project.Chain)
project.Public, project.Hidden, project.Chain, project.Paused)
var id int64
err := row.Scan(&id)
@ -57,7 +58,7 @@ func (database *Database) GetProject(id int64) *Project {
db := database.getDB()
row := db.QueryRow(`SELECT id, priority, name, clone_url, git_repo, version,
motd, public, hidden, COALESCE(chain, 0)
motd, public, hidden, COALESCE(chain, 0), paused
FROM project WHERE id=$1`, id)
project, err := scanProject(row)
@ -80,7 +81,7 @@ func scanProject(row *sql.Row) (*Project, error) {
p := &Project{}
err := row.Scan(&p.Id, &p.Priority, &p.Name, &p.CloneUrl, &p.GitRepo, &p.Version,
&p.Motd, &p.Public, &p.Hidden, &p.Chain)
&p.Motd, &p.Public, &p.Hidden, &p.Chain, &p.Paused)
return p, err
}
@ -89,7 +90,7 @@ func (database *Database) GetProjectWithRepoName(repoName string) *Project {
db := database.getDB()
row := db.QueryRow(`SELECT id, priority, name, clone_url, git_repo, version,
motd, public, hidden, COALESCE(chain, 0) FROM project WHERE LOWER(git_repo)=$1`,
motd, public, hidden, COALESCE(chain, 0), paused FROM project WHERE LOWER(git_repo)=$1`,
strings.ToLower(repoName))
project, err := scanProject(row)
@ -108,11 +109,11 @@ func (database *Database) UpdateProject(project *Project) error {
db := database.getDB()
res, err := db.Exec(`UPDATE project
SET (priority, name, clone_url, git_repo, version, motd, public, hidden, chain) =
($1,$2,$3,$4,$5,$6,$7,$8,NULLIF($9, 0))
WHERE id=$10`,
SET (priority, name, clone_url, git_repo, version, motd, public, hidden, chain, paused) =
($1,$2,$3,$4,$5,$6,$7,$8,NULLIF($9, 0), $10)
WHERE id=$11`,
project.Priority, project.Name, project.CloneUrl, project.GitRepo, project.Version, project.Motd,
project.Public, project.Hidden, project.Chain, project.Id)
project.Public, project.Hidden, project.Chain, project.Paused, project.Id)
if err != nil {
return err
}
@ -135,13 +136,13 @@ func (database Database) GetAllProjects(managerId int64) *[]Project {
var err error
if managerId == 0 {
rows, err = db.Query(`SELECT
Id, priority, name, clone_url, git_repo, version, motd, public, hidden, COALESCE(chain,0)
Id, priority, name, clone_url, git_repo, version, motd, public, hidden, COALESCE(chain,0), paused
FROM project
WHERE NOT hidden
ORDER BY name`)
} else {
rows, err = db.Query(`SELECT
Id, priority, name, clone_url, git_repo, version, motd, public, hidden, COALESCE(chain,0)
Id, priority, name, clone_url, git_repo, version, motd, public, hidden, COALESCE(chain,0), paused
FROM project
LEFT JOIN manager_has_role_on_project mhrop ON mhrop.project = id AND mhrop.manager=$1
WHERE NOT hidden OR mhrop.role & 1 = 1 OR (SELECT tracker_admin FROM manager WHERE id=$1)
@ -153,7 +154,7 @@ func (database Database) GetAllProjects(managerId int64) *[]Project {
p := Project{}
err := rows.Scan(&p.Id, &p.Priority, &p.Name, &p.CloneUrl,
&p.GitRepo, &p.Version, &p.Motd, &p.Public, &p.Hidden,
&p.Chain)
&p.Chain, &p.Paused)
handleErr(err)
projects = append(projects, p)
}

View File

@ -82,7 +82,7 @@ func (database *Database) GetTask(worker *Worker) *Task {
FROM task
INNER JOIN project project on task.project = project.id
LEFT JOIN worker_verifies_task wvt on task.id = wvt.task AND wvt.worker=$1
WHERE assignee IS NULL AND task.status=1
WHERE NOT project.paused AND assignee IS NULL AND task.status=1
AND (project.public OR (
SELECT a.role_assign FROM worker_access a WHERE a.worker=$1 AND a.project=project.id
))
@ -186,7 +186,7 @@ func (database *Database) GetTaskFromProject(worker *Worker, projectId int64) *T
FROM task
INNER JOIN project project on task.project = project.id
LEFT JOIN worker_verifies_task wvt on task.id = wvt.task AND wvt.worker=$1
WHERE assignee IS NULL AND project.id=$2 AND status=1
WHERE NOT project.paused AND assignee IS NULL AND project.id=$2 AND status=1
AND (project.public OR (
SELECT a.role_assign FROM worker_access a WHERE a.worker=$1 AND a.project=$2
))

View File

@ -142,6 +142,7 @@ func TestUpdateProjectValid(t *testing.T) {
Motd: "MotdB",
Public: false,
Hidden: true,
Paused: true,
}, pid, testAdminCtx)
if updateResp.Ok != true {
@ -168,6 +169,9 @@ func TestUpdateProjectValid(t *testing.T) {
if proj.Project.Hidden != true {
t.Error()
}
if proj.Project.Paused != true {
t.Error()
}
}
func TestUpdateProjectInvalid(t *testing.T) {
@ -444,6 +448,42 @@ func TestAdminShouldSeeHiddenProjectInList(t *testing.T) {
}
}
func TestPausedProjectShouldNotDispatchTasks(t *testing.T) {
createTask(api.SubmitTaskRequest{
Project: testProject,
Recipe: "...",
}, testWorker)
createTask(api.SubmitTaskRequest{
Project: testProject,
Recipe: "...",
}, testWorker)
createTask(api.SubmitTaskRequest{
Project: testProject,
Recipe: "...",
}, testWorker)
task1 := getTaskFromProject(testProject, testWorker).Content.Task
if task1 == nil {
t.Error()
}
updateProject(api.UpdateProjectRequest{
Paused: true,
Name: "generictestproject",
}, testProject, testAdminCtx)
task2 := getTaskFromProject(testProject, testWorker).Content.Task
if task2 != nil {
t.Error()
}
updateProject(api.UpdateProjectRequest{
Paused: false,
Name: "generictestproject",
}, testProject, testAdminCtx)
}
func createProjectAsAdmin(req api.CreateProjectRequest) CreateProjectAR {
return createProject(req, testAdminCtx)
}

View File

@ -2,7 +2,7 @@ server:
address: "127.0.0.1:5001"
database:
conn_str: "user=task_tracker dbname=task_tracker_test sslmode=disable"
conn_str: "user=task_tracker password=task_tracker dbname=task_tracker sslmode=disable"
log_levels: ["debug", "error", "trace", "info", "warn"]
git:

View File

@ -19,6 +19,7 @@ CREATE TABLE project
chain INT DEFAULT NULL REFERENCES project (id),
public boolean NOT NULL,
hidden boolean NOT NULL,
paused boolean NOT NULL,
name TEXT UNIQUE NOT NULL,
clone_url TEXT NOT NULL,
git_repo TEXT UNIQUE NOT NULL,

View File

@ -0,0 +1,13 @@
<div class="container">
<mat-card class="mat-elevation-z8">
<mat-card-header>
<mat-card-title>{{"admin_panel.title"|translate}}</mat-card-title>
<mat-card-subtitle>{{"admin_panel.subtitle"|translate}}</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
</mat-card-content>
<mat-card-actions></mat-card-actions>
</mat-card>
</div>

View File

@ -0,0 +1,16 @@
import {Component, OnInit} from '@angular/core';
@Component({
selector: 'app-admin-panel',
templateUrl: './admin-panel.component.html',
styleUrls: ['./admin-panel.component.css']
})
export class AdminPanelComponent implements OnInit {
constructor() {
}
ngOnInit() {
}
}

View File

@ -12,6 +12,7 @@ import {
MatButtonToggleModule,
MatCardModule,
MatCheckboxModule,
MatDialogModule,
MatDividerModule,
MatExpansionModule,
MatFormFieldModule,
@ -55,6 +56,8 @@ import {ManagerSelectComponent} from './manager-select/manager-select.component'
import {ProjectIconComponent} from './project-icon/project-icon.component';
import {IndexComponent} from './index/index.component';
import {ProjectSecretComponent} from './project-secret/project-secret.component';
import {AdminPanelComponent} from './admin-panel/admin-panel.component';
import {AreYouSureComponent} from './are-you-sure/are-you-sure.component';
export function createTranslateLoader(http: HttpClient) {
@ -81,6 +84,8 @@ export function createTranslateLoader(http: HttpClient) {
ProjectIconComponent,
IndexComponent,
ProjectSecretComponent,
AdminPanelComponent,
AreYouSureComponent,
],
imports: [
BrowserModule,
@ -120,7 +125,8 @@ export function createTranslateLoader(http: HttpClient) {
MatTabsModule,
MatListModule,
MatButtonToggleModule,
MatStepperModule
MatStepperModule,
MatDialogModule,
],
exports: [],
@ -131,6 +137,7 @@ export function createTranslateLoader(http: HttpClient) {
],
entryComponents: [
SnackBarComponent,
AreYouSureComponent
],
bootstrap: [AppComponent]
})

View File

@ -0,0 +1,3 @@
.mat-dialog-actions {
justify-content: end;
}

View File

@ -0,0 +1,8 @@
<h1 mat-dialog-title>{{"dialog.confirmation"|translate}}</h1>
<div mat-dialog-content>
<p>{{"dialog.are_you_sure"|translate}}</p>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onNoClick()">{{"dialog.no"|translate}}</button>
<button mat-button color="warn" (click)="onYesClick()">{{"dialog.ok"|translate}}</button>
</div>

View File

@ -0,0 +1,25 @@
import {Component, OnInit} from '@angular/core';
import {MatDialogRef} from "@angular/material";
@Component({
selector: 'app-are-you-sure',
templateUrl: './are-you-sure.component.html',
styleUrls: ['./are-you-sure.component.css']
})
export class AreYouSureComponent implements OnInit {
constructor(public dialogRef: MatDialogRef<AreYouSureComponent>) {
}
ngOnInit() {
}
onNoClick() {
this.dialogRef.close(false)
}
onYesClick() {
this.dialogRef.close(true)
}
}

View File

@ -9,4 +9,5 @@ export interface Project {
public: boolean;
chain: number;
hidden: boolean;
paused: boolean;
}

View File

@ -38,3 +38,11 @@
display: none;
}
}
.project-actions {
margin-top: 1em;
}
.project-actions button {
margin-right: 1em;
}

View File

@ -52,6 +52,28 @@
<pre>{{project | json}}</pre>
</mat-expansion-panel>
<mat-expansion-panel *ngIf="project && auth.logged" class="project-actions">
<mat-expansion-panel-header>
<mat-panel-title>{{"dashboard.actions" | translate}}</mat-panel-title>
</mat-expansion-panel-header>
<button mat-raised-button color="accent" (click)="resetFailedTasks()">
<mat-icon>replay</mat-icon>
{{"dashboard.reset_failed"|translate}}
</button>
<button mat-raised-button
color="primary"
(click)="pauseProject()"
*ngIf="!project.paused"
[title]="'dashboard.pause_hint'|translate">{{"dashboard.pause"|translate}}</button>
<button mat-raised-button color="primary" (click)="pauseProject()">
<mat-icon>pause</mat-icon>
{{"dashboard.pause"|translate}}</button>
<button mat-raised-button color="warn" (click)="hardReset()">
<mat-icon>warning</mat-icon>
{{"dashboard.hard_reset"|translate}}
</button>
</mat-expansion-panel>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button [routerLink]="'/projects'">Back</button>

View File

@ -8,6 +8,8 @@ import {AssignedTasks, MonitoringSnapshot} from "../models/monitoring";
import {TranslateService} from "@ngx-translate/core";
import {MessengerService} from "../messenger.service";
import {AuthService} from "../auth.service";
import {MatDialog} from "@angular/material";
import {AreYouSureComponent} from "../are-you-sure/are-you-sure.component";
@Component({
@ -48,6 +50,7 @@ export class ProjectDashboardComponent implements OnInit {
private route: ActivatedRoute,
private translate: TranslateService,
public auth: AuthService,
public dialog: MatDialog,
private messenger: MessengerService) {
}
@ -340,4 +343,32 @@ export class ProjectDashboardComponent implements OnInit {
this.messenger.show(t))
})
}
resetFailedTasks() {
this.dialog.open(AreYouSureComponent, {
width: '250px',
}).afterClosed().subscribe(result => {
if (result) {
alert("yes")
}
});
}
pauseProject() {
this.dialog.open(AreYouSureComponent, {
width: '250px',
}).afterClosed().subscribe(result => {
if (result) {
this.project.paused = true;
this.apiService.updateProject(this.project).subscribe(() => {
this.translate.get("messenger.acknowledged").subscribe(t =>
this.messenger.show(t))
})
}
});
}
hardReset() {
}
}

View File

@ -1,3 +1,5 @@
<mat-icon *ngIf="project.public" [title]="'project.public' | translate">public</mat-icon>
<mat-icon *ngIf="!project.public && !project.hidden" [title]="'project.private'|translate">lock</mat-icon>
<mat-icon *ngIf="project.hidden" [title]="'project.hidden'|translate">visibility_off</mat-icon>
<mat-icon *ngIf="project.public && !project.paused" [title]="'project.public' | translate">public</mat-icon>
<mat-icon *ngIf="!project.public && !project.hidden && !project.paused" [title]="'project.private'|translate">lock
</mat-icon>
<mat-icon *ngIf="project.hidden && !project.paused" [title]="'project.hidden'|translate">visibility_off</mat-icon>
<mat-icon *ngIf="project.paused" [title]="'project.paused'|translate">pause</mat-icon>

View File

@ -5,3 +5,11 @@ button {
mat-panel-title > project-icon {
margin-right: 1em;
}
.paused {
color: #9a9a9a;
}
.mat-expansion-panel-header-description {
flex-grow: 0;
}

View File

@ -12,11 +12,12 @@
<mat-accordion>
<mat-expansion-panel *ngFor="let project of projects" style="margin-top: 1em">
<mat-expansion-panel-header>
<mat-panel-title>
<mat-panel-title [class.paused]="project.paused">
<project-icon [project]="project"></project-icon>
<span style="width: 3em">{{project.id}}</span>{{project.name}}
<span style="width: 3em; align-self: center">{{project.id}}</span>
<span style="align-self: center">{{project.name}}</span>
</mat-panel-title>
<mat-panel-description>{{project.motd}}</mat-panel-description>
<mat-panel-description style="align-self: center">{{project.motd}}</mat-panel-description>
</mat-expansion-panel-header>
<pre>{{project | json}}</pre>
<div>

View File

@ -74,7 +74,12 @@
"title": "Dashboard for",
"metadata": "Project metadata",
"empty": "No tasks",
"refresh": "Refresh"
"refresh": "Refresh",
"actions": "Actions",
"reset_failed": "Reset failed tasks",
"pause": "Pause",
"resume": "Resume",
"hard_reset": "Hard_reset"
},
"login": {
"title": "Login",
@ -121,7 +126,8 @@
},
"messenger": {
"close": "Close",
"unauthorized": "Unauthorized"
"unauthorized": "Unauthorized",
"acknowledged": "Changes saved"
},
"manager_list": {
"title": "Manager list",
@ -149,5 +155,11 @@
"secret": "Secret",
"update": "Update",
"ok": "Updated"
},
"dialog": {
"confirmation": "Confirmation",
"are_you_sure": "Are you sure?",
"no": "No",
"ok": "Ok"
}
}

View File

@ -74,7 +74,8 @@
"title": "Tableau de bord pour ",
"metadata": "Métadonnés du projet",
"empty": "Aucune tâche",
"refresh": "Rafraîchir"
"refresh": "Rafraîchir",
"actions": "Actions"
},
"login": {
"title": "Ouvrir un session",
@ -123,7 +124,8 @@
},
"messenger": {
"close": "Fermer",
"unauthorized": "Non autorisé"
"unauthorized": "Non autorisé",
"acknowledged": "Changements enregistrés"
},
"manager_list": {
"title": "Liste ",