def onstage_request_item(request): li = common.request_item(request) if current_user == push.owner: li.children.insert(0, T.div(class_='actions')(mark_tested_form(request), T.span('or', class_='sep'), withdraw_form(request))) elif current_user == request.owner: li.children.insert(0, T.div(class_='actions')(mark_tested_form(request))) return li
def __init__(self, title='pushmaster'): super(Document, self).__init__() self.title = T.title(title) if title else T.title() self.head = T.head( meta_content_type, self.title, favicon, reset_css, jquery_ui_css, pushmaster_css, ) self.dialogs = T.div(id='dialogs') self.scripts = T.div(jquery_js, jquery_ui_js, pushmaster_js, id='scripts') request_form = new_request_form() request_form(id='new-request-form') self.dialogs(request_form) push_form = new_push_form() push_form(id='new-push-form') self.dialogs(push_form) stage_form = sendtostage_form() stage_form(id='sendtostage-form') self.dialogs(stage_form) reject_form = reject_request_form() self.dialogs(reject_form) self.funcbar = funcbar() self.body = T.body(session(), navbar(), self.funcbar) self.html(self.head, self.body)
def request_display(request, push): title = T.h2(class_='request-title')( T.span(class_='subject')(request.subject), common.user_home_link(request.owner), common.display_date(request.target_date), ) div = T.div(class_='request')(title) if request.branch: div(T.h3(T.span('Branch: '), T.a(request.branch, href=config.git_branch_url % dict(branch=request.branch)))) if request.reject_reason: div( T.h3('Reject Reason:'), T.p(common.linkify(request.reject_reason), class_='reject-reason'), ) div(T.div(class_='message')(common.linkify(request.message or ''))) if request.urgent: title.attrs['class'] += ' urgent' title(common.request_badges(request)) title(T.span(request.state, class_='state')) if not push or users.get_current_user() == push.owner: div(request_actions_form(request)) return div
def new_push_form(): return T.form(action='/pushes', method='post', class_='new-push')( T.div(class_='fields')( T.input(type='hidden', name='act', value='new_push'), T.div(T.label(for_='new-push-name')('Name:')), T.div(T.input(type='text', name='name', class_='push-name', id='new-push-name')), T.div(T.button(type='submit', class_='submit')('Start New Push')), ), )
def new_request_form(push=None, subject='', message='', branch=''): label = T.a(class_='toggle', href='#')('New Request') if push else 'New Request' class_ = 'push request' if push else 'request' content = T.div(class_='content') form = T.form(action='/requests', method='post', class_=class_)( T.fieldset(class_='container')( T.legend(label), content( T.div( T.label(for_='new-request-subject')('Subject'), T.input(name='subject', id='new-request-subject', value=subject), ), T.div( T.label(for_='new-request-branch')('Branch'), T.input(name='branch', id='new-request-branch', value=branch), ), T.div( T.label(for_='new-request-message')('Message'), T.textarea(name='message', id='new-request-message', rows='40', cols='120')(message), ), T.div( T.label(for_='new-request-target-date')('Push Date'), T.input(name='target_date', id='new-request-target-date', class_='date', value=util.tznow().date().strftime('%Y-%m-%d')), ), T.fieldset(class_='flags')( T.legend('Flags'), T.div( T.input(id='new-request-urgent', type='checkbox', name='urgent', class_='checkbox'), T.label(for_='new-request-urgent', class_='checkbox')('Urgent (e.g. P0)'), ), T.div( T.input(id='new-request-tests-pass', type='checkbox', name='tests_pass', class_='checkbox'), T.label(for_='new-request-tests-pass', class_='checkbox')('Passes Buildbot'), T.input(id='new-request-tests-pass-url', name='tests_pass_url', class_='tests-pass-url'), ), T.div( T.input(id='new-request-push-plans', type='checkbox', name='push_plans', class_='checkbox'), T.label(class_='checkbox', for_='new-request-push-plans')('Push Plans'), ), T.div( T.input(id='new-request-js-serials', type='checkbox', name='js_serials', class_='checkbox'), T.label(class_='checkbox', for_='new-request-js-serials')('Bump Javascript Serials'), ), T.div( T.input(id='new-request-img-serials', type='checkbox', name='img_serials', class_='checkbox'), T.label(class_='checkbox', for_='new-request-img-serials')('Bump Image Serials'), ), ), T.button(type='submit')('Create') ), ), ) if push: content(T.input(type='hidden', name='push', value=str(push.key()))) return form
def reject_request_form(): return T.form(action='#', method='post', class_='reject-request', id='reject-request-form')( T.div(class_='fields')( hidden(act='reject', push='true', return_url=''), T.h2(class_='subject'), T.label(for_='reject-request-reason')( T.span('Reason:'), T.div(T.textarea(name='reason', id='reject-request-reason')), ), T.div(T.button(type='submit', class_='submit')('Reject')), ), )
def push_item(push): requests = query.push_requests(push) return T.li(class_='push')( T.div( common.display_datetime(push.ptime), T.a(href=push.uri)(push.name or 'push'), common.user_home_link(push.owner, logic.user_info(push.owner)), T.span(class_='state')(common.display_push_state(push)), class_='headline', ), T.ol(map(common.request_item, requests)) if requests else T.div('No requests.'), )
def sendtostage_form(): return T.form(action='#', method='post', class_='send-to-stage')( T.div(class_='fields')( T.input(type='hidden', name='act', value='sendtostage'), T.div(T.label(for_='dest-stage')('Destination:')), T.div( T.select(name='stage', class_='dest-stage', id='dest-stage')( (T.option(value=stage)(stage)) for stage in model.Push.all_stages ), ), ), T.div(T.button(type='submit', class_='submit')('Mark Deployed to Stage')), )
def request_item(request): li = common.request_item(request) if is_push_owner: li.children.insert(0, T.div(class_='actions')( T.form(class_='small', action=request.uri, method='post')( T.div(class_='fields')( T.button(type='submit')('Accept'), common.hidden(push=str(push.key()), act='accept')), ), T.span('or', class_='sep'), reject_request_link(request), ), ) return li
def request_item(request, full_request=False): li = T.li(class_='request clearfix')( display_date(request.target_date), T.span(class_='email')(T.a(href=urls.user_home(request.owner))(logic.user_info(request.owner).full_name), ':'), T.a(href=request.uri, class_='request-subject')(request.subject), ) if full_request: li( T.a(request.branch, href=config.git_branch_url % dict(branch=request.branch)), T.p(class_='content')(request.message), ) if request.target_date > util.tznow().date(): li.attrs['class'] += ' future' if request.urgent: li.attrs['class'] += ' urgent' if request.state == 'rejected': li.attrs['class'] += ' rejected' if request.owner == users.get_current_user(): li.attrs['class'] += ' own' li(request_badges(request), T.span(request.branch, class_='branch'), T.div(linkify(request.message), class_='message')) return li
def take_ownership_form(object): form = T.form(class_='small', action=object.uri, method='post')( T.div(class_='fields')( T.button(type='submit', name='act', value='take_ownership')('Take Ownership'), ), ) return form
def push_actions_form(push, requests): form = T.form(action=push.uri, method='post', class_='small') fields = T.div(class_='fields') form(fields) button_count = 0 if push.state in ('accepting', 'onstage') and filter(lambda r: r.state == 'checkedin', requests): if button_count: fields(T.span(' or ')) fields(T.button(type='button', name='sendtostage', id='send-to-stage', value=push.uri)('Mark Deployed to Stage')) button_count +=1 if push.state == 'onstage' and requests and all(r.state == 'tested' for r in requests): if button_count: fields(T.span(' or ')) fields(T.button(type='submit', name='act', value='sendtolive')('Mark Live')) button_count +=1 if push.state in ('accepting', 'onstage'): if button_count: fields(T.span(' or ')) fields(T.button(type='submit', name='act', value='abandon')('Abandon')) button_count +=1 return form
def get(self, datestr): from_date, to_date = report_date_range(datestr) doc = common.Document(title='pushmaster: reports') doc.body(T.h1('Report for ', from_date.strftime('%e %b %Y'), ' - ', to_date.strftime('%e %b %Y'))) pushes = query.pushes_for_the_week_of(from_date) for push in pushes: pushdiv = T.div(class_='push') pushdiv(T.h2( T.a(href=push.uri)( push.ltime.replace(tzinfo=timezone.UTC()).astimezone(config.timezone).strftime('%a, %l:%M %p, %e %b %Y'), T.span(' '), push.name or '', ), T.span(' by '), common.display_user_email(push.owner), )) reqlist = T.ol(class_='requests') for request in query.push_requests(push): reqlist(common.request_item(request)) pushdiv(reqlist) doc.body(pushdiv) doc.body(common.jquery_js, common.jquery_ui_js, common.pushmaster_js) doc.serialize(self.response.out)
def get(self, email): email = urllib.unquote_plus(email) doc = common.Document(title='pushmaster: recent activity: ' + email) doc.body(T.div(class_='bookmarklet')(common.bookmarklet(self.hostname))) user = users.User(email) requests = query.requests_for_user(user) pushes = query.pushes_for_user(user) if requests: doc.body( T.h3('Recent Requests'), T.ol(class_='my requests')(map(request_item, requests)), ) if pushes: doc.body( T.h3('Recent Pushes'), T.ol(class_='pushes')(map(push_item, pushes)), ) doc.serialize(self.response.out)
def session(): user = users.get_current_user() div = T.div(class_='session')( user_home_link(user), T.span(class_='sep')('|'), T.a(href=users.create_logout_url('/'))('Logout') ) return div
def funcbar(): bar = T.div(class_='func')( T.a(id='new-request', href='#')('Make Request'), T.span(class_='sep')('|'), T.a(id='new-push', href='#')('Start Push'), T.span(class_='sep')('|'), T.a(id='details', href='#')(T.span('Show Details', class_='show'), T.span('Hide Details', class_='hide')), ) return bar
def get(self): requests = query.current_requests() doc = common.Document(title='pushmaster: requests') doc.body( T.h2(('Current Requests (%d)' % len(requests)) if len(requests) > 5 else 'Current Requests'), common.request_list(requests) if requests else T.span('There are no requests at present.'), T.div(common.bookmarklet(self.hostname)), ) doc.serialize(self.response.out)
def navbar(current=None): nav = T.div(class_='nav')( T.a(href='/push/current')(T.span('Current Push')), T.span(class_='sep')('|'), T.a(href='/requests')('Requests'), T.span(class_='sep')('|'), T.a(href='/pushes')('Pushes'), T.span(class_='sep')('|'), T.a(href='/lastweek')('Last Week'), ) return nav
def accepted_request_item(request): li = common.request_item(request) if current_user == push.owner: li.children.insert(0, T.div(class_='actions')( mark_checked_in_form(request), T.span('or', class_='sep'), withdraw_form(request), T.span('or', class_='sep'), reject_request_link(request), )) return li
def request_display(request, push): title = T.h2(class_='request-title')( T.span(class_='subject')(request.subject), common.user_home_link(request.owner, logic.user_info(request.owner)), common.display_date(request.target_date), ) div = T.div(class_='request')(title) if request.branch: if '/' in request.branch: repo, _, branch = request.branch.partition('/') repo = 'devs/%s.git' % repo else: repo, branch = 'yelp-main.git', request.branch div(T.h3(T.span('Branch: '), T.a(request.branch, href=config.git_branch_url % dict(repo=repo,branch=branch)))) if push: div(T.h3(T.span('Push: '), T.a('%s %s' % (util.format_datetime(push.ptime), push.name), href=push.uri))) if request.reject_reason: div( T.h3('Reject Reason:'), T.p(common.linkify(request.reject_reason), class_='reject-reason'), ) div(T.div(class_='message')(common.linkify(request.message or ''))) if request.urgent: title.attrs['class'] += ' urgent' title(common.request_badges(request)) title(T.span(request.state, class_='state')) if not push or users.get_current_user() == push.owner: div(request_actions_form(request)) return div
def get(self, datestr=None): if datestr: from_date, to_date = report_date_range(datestr) else: for_date = last_monday_datetime() - datetime.timedelta(days=7) return self.redirect('/lastweek/' + for_date.strftime('%Y%m%d')) pushes = query.pushes_for_the_week_of(from_date) requests = [] for push in pushes: requests.extend(query.push_requests(push)) requests = sorted(requests, key=lambda r: r.mtime) doc = common.Document(title='pushmaster: weekly report: ' + datestr) teams_list = T.ul(class_='teams') doc(teams_list) nothing_messages_list = None for team in config.report_users: team_item = T.li(class_='team')(T.h3(team['name'])) teams_list(team_item) devs_list = T.ul(class_='devs') team_item(devs_list) for dev in sorted(team['dev']): dev_item = T.li(class_='dev')(T.h4(dev)) devs_list(dev_item) dev_requests = filter(lambda r: r.owner.nickname() == dev, requests) if dev_requests: requests_list = T.ol(class_='requests')(map(common.request_item, dev_requests)) dev_item(requests_list) else: # lazy (re)initialize random messages if not nothing_messages_list: nothing_messages_list = list(config.nothing_messages) import random random.shuffle(nothing_messages_list) dev_item(T.div(nothing_messages_list.pop(), class_='nothing')) if 'prod' in team: team_item(T.h4('PM: ' if len(team['prod']) == 1 else 'PMs: ', ', '.join(team['prod']), class_='pm')) doc.serialize(self.response.out)
def navbar(current=None): nav = T.div(class_='nav')( T.a(id='new-request', href='#')('Make Request'), T.span(class_='sep')('|'), T.a(id='new-push', href='#')('Start Push'), T.span(class_='sep')('|'), T.a(href='/push/current')(T.span('Current Push')), T.span(class_='sep')('|'), T.a(href='/requests')('Requests'), T.span(class_='sep')('|'), T.a(href='/pushes')('Pushes'), T.span(class_='sep')('|'), T.a(href='/reports')('Reports'), T.span(class_='sep')('|'), T.a(href='/lastweek')('Last Week'), ) return nav
def request_item(request): li = T.li(class_='request clearfix')( display_date(request.target_date), ' ', T.span(class_='email')(T.a(href=urls.user_home(request.owner))(logic.user_info(request.owner).full_name), ':'), ' ', T.a(href=request.uri, class_='request-subject')(request.subject), ' ', T.span(class_='verify')(request.time_to_verify), ) if request.target_date > util.tznow().date(): li.attrs['class'] += ' future' if request.urgent: li.attrs['class'] += ' urgent' if request.state == 'rejected': li.attrs['class'] += ' rejected' if request.owner == users.get_current_user(): li.attrs['class'] += ' own' if request.branch and '/' in request.branch: repo, _, branch = request.branch.partition('/') repo = 'devs/%s.git' % repo else: repo, branch = 'yelp-main.git', request.branch request_branch = T.a(request.branch, class_='branch', href=config.git_branch_url % dict(repo=repo,branch=branch)) li( ' ', request_badges(request), ' ', request_branch, ' ', T.div(linkify(request.message), class_='message'), ) return li
def withdraw_form(request): return T.form(class_='small', method='post', action=request.uri)( T.div(class_='fields')( T.button(type='submit')('Withdraw'), common.hidden(push='true', act='withdraw')))
def mark_checked_in_form(request): return T.form(class_='small', method='post', action=request.uri)( T.div(class_='fields')( T.button(type='submit')('Mark Checked In'), common.hidden(push='true', act='markcheckedin')))
def mark_tested_form(request): return T.form(class_='small', method='post', action=request.uri)( T.div(class_='fields')( T.button(type='submit')('Mark Verified'), common.hidden(push='true', act='marktested')))
def render_push_div(self, current_user, push, requests, pending_requests): push_div = T.div(class_='push') if current_user == push.owner: push_div(push_actions_form(push, requests)(class_='small push-action')) elif push.can_change_owner: push_div(common.take_ownership_form(push)(class_='small push-action')) header = T.h1(common.display_datetime(push.ptime), T.span(class_='name')(push.name or ''), common.user_home_link(push.owner, logic.user_info(push.owner))) if any(request.push_plans for request in requests): header(common.push_plans_badge()) if any(request.js_serials for request in requests): header(common.js_serials_badge()) if any(request.img_serials for request in requests): header(common.img_serials_badge()) push_div(header) requests_div = T.div(class_='requests') push_div(requests_div) def requests_with_state(state): return filter(lambda r: r.state == state, requests) if push.state == 'live': requests_div(accepted_list(requests_with_state('live'), state='live')) else: def onstage_request_item(request): li = common.request_item(request) if current_user == push.owner: li.children.insert(0, T.div(class_='actions')(mark_tested_form(request), T.span('or', class_='sep'), withdraw_form(request))) elif current_user == request.owner: li.children.insert(0, T.div(class_='actions')(mark_tested_form(request))) return li def withdrawable_request_item(request): li = common.request_item(request) if current_user == push.owner: li.children.insert(0, T.div(class_='actions')(withdraw_form(request))) return li def accepted_request_item(request): li = common.request_item(request) if current_user == push.owner: li.children.insert(0, T.div(class_='actions')( mark_checked_in_form(request), T.span('or', class_='sep'), withdraw_form(request), T.span('or', class_='sep'), reject_request_link(request), )) return li request_states = [ ('Verified on Stage', 'tested', withdrawable_request_item), ('On Stage (%s)' % push.stage, 'onstage', onstage_request_item), ('Checked In', 'checkedin', withdrawable_request_item), ('Accepted', 'accepted', accepted_request_item), ] for label, state, request_item in request_states: subrequests = requests_with_state(state) if subrequests: if len(subrequests) > 5: label = '%(label)s (%(count)d)' % {'label': label, 'count': len(subrequests)} requestors = ', '.join(set(request.owner.nickname() for request in subrequests)) label = '%(label)s - %(requestors)s' % {'label': label, 'requestors': requestors} requests_div(T.h3(label), accepted_list(subrequests, request_item=request_item, state=state)) if current_user == push.owner: accepted_requests = requests_with_state('accepted') if accepted_requests: requests_div(T.div('cherry-pick-branches %s' % (' '.join(['"%s"' % request.branch for request in accepted_requests if request.branch]),), class_='code')) if push.editable: if pending_requests: pending_requests_title = ('Pending Requests (%d)' % len(pending_requests)) if len(pending_requests) > 5 else 'Pending Requests' push_div(T.h2(class_='pending')(pending_requests_title), push_pending_list(push, pending_requests)) return push_div
def withdrawable_request_item(request): li = common.request_item(request) if current_user == push.owner: li.children.insert(0, T.div(class_='actions')(withdraw_form(request))) return li
def edit_request_form(request): request_id = str(request.key()) return T.form(action=request.uri, method='post', class_='edit request')( T.fieldset(class_='container')( T.legend(T.a(class_='toggle', href='#')('Edit Request')), T.div(class_='content')( T.div( T.label(for_='edit-request-subject-'+request_id)('Subject'), T.input(name='subject', id='edit-request-subject-'+request_id, value=request.subject), ), T.div( T.label(for_='edit-request-branch-'+request_id)('Branch'), T.input(name='branch', id='edit-request-branch-'+request_id, value=request.branch or ''), ), T.div( T.label(for_='edit-request-message-'+request_id)('Message'), T.textarea(name='message', id='edit-request-message-'+request_id)(request.message or ''), ), T.div( T.label(for_='edit-request-target-date-'+request_id)('Push Date'), T.input(name='target_date', id='edit-request-target-date-'+request_id, class_='date', value=request.target_date.strftime('%Y-%m-%d') if request.target_date else ''), ), T.div( T.label(for_='edit-request-time-to-verify-'+request_id)('Time needed to Verify'), T.input(name='time_to_verify', id='edit-request-time-to-verify-'+request_id, value=request.time_to_verify), ), T.fieldset(class_='flags')( T.legend('Flags'), T.div( T.input(id='edit-request-urgent-'+request_id, type='checkbox', name='urgent', class_='checkbox', checked=request.urgent), T.label(for_='edit-request-urgent-'+request_id, class_='checkbox')('Urgent (e.g. P0)'), ), T.div( T.input(id='edit-request-tests-pass-'+request_id, type='checkbox', name='tests_pass', checked=request.tests_pass, class_='checkbox'), T.label(for_='edit-request-tests-pass-'+request_id, class_='checkbox')('Passes Buildbot'), T.input(id='edit-request-tests-pass-url-'+request_id, name='tests_pass_url', class_='tests-pass-url', value=request.tests_pass_url), ), T.div( T.input(id='edit-request-push-plans-'+request_id, type='checkbox', name='push_plans', checked=request.push_plans, class_='checkbox'), T.label(for_='edit-request-push-plans-'+request_id, class_='checkbox')('Push Plans'), ), T.div( T.input(id='edit-request-img-serials-'+request_id, type='checkbox', name='img_serials', checked=request.img_serials, class_='checkbox'), T.label(for_='edit-request-img-serials-'+request_id, class_='checkbox')('Bump Image Serials'), ), ), T.div( T.button(type='submit', name='act', value='edit')('Save'), ), ), ), )