From 4c2607629d3de9c03005710496a12cd84a7467d0 Mon Sep 17 00:00:00 2001 From: Jongmok Hong Date: Thu, 27 Sep 2018 06:04:10 +0900 Subject: Add a schedule suspend feature in extended rows. Test: go/vtslab-schedule-dev/redirect/20180927t073313-dot-vtslab-schedule-dev Bug: 116058216 --- .../src/app/menu/schedule/schedule.component.html | 12 ++++++++- .../src/app/menu/schedule/schedule.component.ts | 29 ++++++++++++++++++++- .../src/app/menu/schedule/schedule.service.ts | 7 +++++ gae/frontend/src/app/model/schedule.ts | 12 +++++++++ gae/webapp/src/endpoint/endpoint_base.py | 2 ++ gae/webapp/src/endpoint/schedule_info.py | 30 ++++++++++++++++++++++ gae/webapp/src/proto/model.py | 16 +++++++++++- 7 files changed, 105 insertions(+), 3 deletions(-) diff --git a/gae/frontend/src/app/menu/schedule/schedule.component.html b/gae/frontend/src/app/menu/schedule/schedule.component.html index 67088fc..c567639 100644 --- a/gae/frontend/src/app/menu/schedule/schedule.component.html +++ b/gae/frontend/src/app/menu/schedule/schedule.component.html @@ -84,7 +84,10 @@ - + + +
+ +
+
diff --git a/gae/frontend/src/app/menu/schedule/schedule.component.ts b/gae/frontend/src/app/menu/schedule/schedule.component.ts index 61f98fd..57ebd7b 100644 --- a/gae/frontend/src/app/menu/schedule/schedule.component.ts +++ b/gae/frontend/src/app/menu/schedule/schedule.component.ts @@ -15,11 +15,12 @@ */ import { Component, OnInit, ViewChild } from '@angular/core'; import { MatSnackBar, MatTableDataSource, PageEvent } from '@angular/material'; +import { animate, state, style, transition, trigger } from "@angular/animations"; import { FilterComponent } from '../../shared/filter/filter.component'; import { FilterItem } from '../../model/filter_item'; import { MenuBaseClass } from '../menu_base'; -import { Schedule } from '../../model/schedule'; +import { Schedule, ScheduleSuspendResponse } from '../../model/schedule'; import { ScheduleService } from './schedule.service'; @@ -29,6 +30,13 @@ import { ScheduleService } from './schedule.service'; templateUrl: './schedule.component.html', providers: [ ScheduleService ], styleUrls: ['./schedule.component.scss'], + animations: [ + trigger('detailExpand', [ + state('void', style({height: '0px', minHeight: '0', visibility: 'hidden'})), + state('*', style({height: '*', visibility: 'visible'})), + transition('void <=> *', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')), + ]), + ], }) export class ScheduleComponent extends MenuBaseClass implements OnInit { columnTitles = [ @@ -112,6 +120,25 @@ export class ScheduleComponent extends MenuBaseClass implements OnInit { ); } + /** Toggles a schedule from suspend to resume, or vice versa. */ + suspendSchedule(schedules: ScheduleSuspendResponse[]) { + this.scheduleService.suspendSchedule(schedules) + .subscribe( + (response) => { + if (response.schedules) { + let self = this; + response.schedules.forEach(function(schedule) { + const original = self.dataSource.data.filter(x => x.urlsafe_key === schedule.urlsafe_key); + if (original) { + original[0].suspended = schedule.suspend; + } + }) + } + }, + (error) => this.showSnackbar(`[${error.status}] ${error.name}`) + ); + } + /** Hooks a page event and handles properly. */ onPageEvent(event: PageEvent) { this.pageSize = event.pageSize; diff --git a/gae/frontend/src/app/menu/schedule/schedule.service.ts b/gae/frontend/src/app/menu/schedule/schedule.service.ts index 86c831d..ae534dd 100644 --- a/gae/frontend/src/app/menu/schedule/schedule.service.ts +++ b/gae/frontend/src/app/menu/schedule/schedule.service.ts @@ -22,6 +22,7 @@ import { Observable } from 'rxjs'; import { environment } from '../../../environments/environment'; import { ScheduleWrapper } from '../../model/schedule_wrapper'; import { ServiceBase } from '../../shared/servicebase'; +import { ScheduleSuspendResponse, ScheduleSuspendResponseWrapper } from '../../model/schedule'; @Injectable() @@ -41,4 +42,10 @@ export class ScheduleService extends ServiceBase { return this.httpClient.post(url, {size: size, offset: offset, filter: filterInfo, sort: sort, direction: direction}) .pipe(catchError(this.handleError)); } + + suspendSchedule(schedules: ScheduleSuspendResponse[]): Observable { + const url = this.url + 'suspend'; + return this.httpClient.post(url, {schedules: schedules}) + .pipe(catchError(this.handleError)); + } } diff --git a/gae/frontend/src/app/model/schedule.ts b/gae/frontend/src/app/model/schedule.ts index 756cb00..5046f2f 100644 --- a/gae/frontend/src/app/model/schedule.ts +++ b/gae/frontend/src/app/model/schedule.ts @@ -59,4 +59,16 @@ export class Schedule { image_package_repo_base: string = void 0; timestamp = void 0; owner: string[] = void 0; + + suspended: boolean = void 0; + urlsafe_key: string = void 0; +} + +export interface ScheduleSuspendResponseWrapper { + schedules: ScheduleSuspendResponse[]; +} + +export interface ScheduleSuspendResponse { + urlsafe_key: string; + suspend: boolean; } diff --git a/gae/webapp/src/endpoint/endpoint_base.py b/gae/webapp/src/endpoint/endpoint_base.py index 0e429dd..d0dddd5 100644 --- a/gae/webapp/src/endpoint/endpoint_base.py +++ b/gae/webapp/src/endpoint/endpoint_base.py @@ -323,6 +323,8 @@ class EndpointBase(remote.Service): resource=entity, reference=message) for attr in assigned_attributes: entity_dict[attr] = getattr(entity, attr, None) + if hasattr(message, "urlsafe_key"): + entity_dict["urlsafe_key"] = entity.key.urlsafe() return_list.append(entity_dict) return return_list, more diff --git a/gae/webapp/src/endpoint/schedule_info.py b/gae/webapp/src/endpoint/schedule_info.py index ce13a40..e353902 100644 --- a/gae/webapp/src/endpoint/schedule_info.py +++ b/gae/webapp/src/endpoint/schedule_info.py @@ -21,8 +21,11 @@ from google.appengine.ext import ndb from webapp.src import vtslab_status as Status from webapp.src.endpoint import endpoint_base from webapp.src.proto import model +from webapp.src.utils import email_util SCHEDULE_INFO_RESOURCE = endpoints.ResourceContainer(model.ScheduleInfoMessage) +SCHEDULE_SUSPEND_RESOURCE = endpoints.ResourceContainer( + model.ScheduleSuspendMessage) @endpoints.api(name="schedule", version="v1") @@ -134,6 +137,33 @@ class ScheduleInfoApi(endpoint_base.EndpointBase): return model.CountResponseMessage(count=count) + @endpoints.method( + SCHEDULE_SUSPEND_RESOURCE, + model.ScheduleSuspendMessage, + path="suspend", + http_method="POST", + name="suspend") + def suspend(self, request): + """Toggles a schedule from suspend to resume, or vice versa.""" + schedules_to_put = [] + schedules_to_return = [] + for schedule in request.schedules: + schedule_key = ndb.key.Key(urlsafe=schedule.urlsafe_key) + schedule_entity = schedule_key.get() + if schedule.suspend: # to suspend + schedule_entity.suspended = True + else: # to resume + schedule_entity.error_count = 0 + schedule_entity.suspended = False + schedules_to_put.append(schedule_entity) + schedules_to_return.append({"urlsafe_key": schedule.urlsafe_key, + "suspend": schedule_entity.suspended}) + # TODO(jongmok): Minimize a number of emails by merging schedules. + email_util.send_schedule_suspension_notification(schedule_entity) + + ndb.put_multi(schedules_to_put) + return model.ScheduleSuspendMessage(schedules=schedules_to_return) + @endpoints.api(name="green_schedule_info", version="v1") class GreenScheduleInfoApi(endpoint_base.EndpointBase): diff --git a/gae/webapp/src/proto/model.py b/gae/webapp/src/proto/model.py index 52b7a8a..56b4566 100644 --- a/gae/webapp/src/proto/model.py +++ b/gae/webapp/src/proto/model.py @@ -114,7 +114,7 @@ class ScheduleControlInfoMessage(messages.Message): class ScheduleInfoMessage(messages.Message): """A message for representing an individual schedule entry.""" - # Next ID = 36 + # Next ID = 38 # schedule name for green build schedule, optional. name = messages.StringField(16) schedule_type = messages.StringField(19) @@ -162,6 +162,9 @@ class ScheduleInfoMessage(messages.Message): timestamp = message_types.DateTimeField(34) owner = messages.StringField(35, repeated=True) + suspended = messages.BooleanField(36) + urlsafe_key = messages.StringField(37) + class LabModel(ndb.Model): """A model for representing an individual lab entry.""" @@ -428,3 +431,14 @@ class CountRequestMessage(messages.Message): class CountResponseMessage(messages.Message): """A message of a count of entities to respond to /count endpoints.""" count = messages.IntegerField(1) + + +class ScheduleSuspendMessage(messages.Message): + """A response message to schedule endpoint API's /suspend method.""" + + class SingleScheduleSuspendMessage(messages.Message): + urlsafe_key = messages.StringField(1) + suspend = messages.BooleanField(2) + + schedules = messages.MessageField( + SingleScheduleSuspendMessage, 1, repeated=True) -- cgit v1.2.3