def open(id, redirect_after=True): """ Open an issue. :param id: issue id :param redirect_after: return a redirect. This happens by default, but turning it off may be desirable for async requests. """ tracker, config = setup() issue = tracker.issue(id) if issue: issue.status = 'open' comment = Comment(issue) comment.event = True comment.event_data = 'opened' comment.author = config.user comment.save() issue.save() tracker.autocommit('Re-opened issue %s/%s' % (issue.id[:6], comment.id[:6]), config.user) if redirect_after: return redirect(url_for('issues.view', id=issue.id)) else: return True return False
def view(id): """ Render the issue view to display information about a single issue. :param id: id of the issue to view. """ tracker, config = setup() issue = tracker.issue(id) if request.method == 'POST': comment = Comment(issue) comment.content = request.form['content'] comment.author['name'] = config.user['name'] comment.author['email'] = config.user['email'] comment.save() if issue.save(): # ping the issue (updated = now) tracker.autocommit(message='Commented on issue %s/%s' % \ (issue.id[:6], comment.id[:6]), author=config.user) else: flash('There was an error saving your comment.') return redirect(url_for('issues.view', id=issue.id)) else: issue.updated = relative_time(issue.updated) issue.created = relative_time(issue.created) issue.content = markdown_to_html(issue.content) comments = issue.comments() header = 'Viewing Issue <span class="fancy-monospace">%s</span>' \ % issue.id[:6] if comments: map_attr(comments, 'timestamp', relative_time) map_attr(comments, 'content', markdown_to_html) return render_template('issue.html', issue=issue, comments=comments, selected='issues', config=config, header=header, tracker=tracker)
def new(): """Render the new issue view.""" tracker, config = setup() header = 'Create a new issue' if request.method == 'POST': issue = Issue(tracker) issue.content = request.form['content'] issue.title = request.form['title'] labels = request.form['labels'] if labels: issue.labels = labels.split(',') else: issue.labels = [] issue.author['name'] = config.user['name'] issue.author['email'] = config.user['email'] if issue.save(): tracker.autocommit(message='Created a new issue %s' % issue.id[:6], author=config.user) return redirect(url_for('issues.view', id=issue.id)) else: flash('There was an error saving your issue.') return render_template('new.html', selected='issues', header=header, tracker=tracker) else: return render_template('new.html', selected='issues', header=header, tracker=tracker)
def members(): tracker, config = setup() header = "Everyone who's altered the %s time continuum." % tracker.config.name raw_history = tracker.history(all=True) # get the unique set of authors (in this history segment) users = set(strip_email(c.author) for c in raw_history) return render_template('members.html', header=header, tracker=tracker, users=users)
def doc_raw(doc): """ Returns the given doc in raw form. :param doc: name of the doc. """ tracker, config = setup() doc = tracker.doc(doc) return to_json({'content': doc.read()})
def doc(doc): """ Returns the given doc as html. :param doc: name of the doc. """ tracker, config = setup() doc = tracker.doc(doc) return to_json({'content': doc.read(convert=True)})
def main(): tracker, config = setup() if request.method == 'POST': tracker.config.name = request.form['name'] tracker.config.save() header = 'Settings for %s' % tracker.config.name return render_template('settings.html', tracker=tracker, name=tracker.config.name, selected='settings', header=header)
def label(label, status=None): """ Returns issues that have a given label. :param label: label to filter by. :param status: status to filter by, in addition to the label. """ tracker, config = setup() issues = [i.fields for i in tracker.issues(label=label, status=status)] return to_json(issues)
def issue(id): """ Return the issue with the given id. :param id: id of the issue. Only the number of characters necessary to uniquely identify it are required, but using more is a good idea. """ tracker, config = setup() return to_json(tracker.issue(id).fields)
def search(query, status=None): """ Returns issues that match the search query. :param query: string to search issues for. :param status: status to filter by, in addition to the search query. """ tracker, config = setup() issues = [i.fields for i in tracker.query().search(sstr=query, status=status)] return to_json(issues)
def index(status='open'): """ Render the issue index view. :param status: 'open' or 'closed' """ tracker, config = setup() # get the url params order = request.args.get('order', 'updated') direc = request.args.get('dir', 'asc') page = int(request.args.get('page', 1)) label = request.args.get('label', None) # verify the params order = order if order in ['id', 'title', 'author'] else 'updated' reverse = True if direc == 'asc' else False per_page = 15 offset = (page - 1) * per_page if page > 1 else 0 # run our query issues_ = tracker.query().select(limit=per_page, offset=offset, status=status, order_by=order, reverse=reverse, label=label) # humanize the timestamps map_attr(issues_, 'updated', relative_time) map_attr(issues_, 'created', relative_time) # get the number of issues by status n = tracker.query().count(status) # get the number of pages n_pages = n / per_page if n % per_page > 1: n_pages += 1 # get pages to link to pages = pager(page, n_pages) if n > per_page else None # set the header header = 'Viewing %s issues for %s' % (status, tracker.config.name) if label: header += ' with label <span class="fancy-monospace">' header += '%s</span>' % label if request.json is not None: return to_json(request.json) else: return render_template('issues.html', issues_=issues_, selected='issues', status=status, order=order, page=page, pages=pages, num_pages=n_pages, asc=reverse, header=header, n=n, tracker=tracker)
def search(query, status=None): """ Returns issues that match the search query. :param query: string to search issues for. :param status: status to filter by, in addition to the search query. """ tracker, config = setup() issues = [ i.fields for i in tracker.query().search(sstr=query, status=status) ] return to_json(issues)
def doc_edit(doc): """ Given a POST request that contains an 'edited' field, the given doc's raw content is replaced. :param doc: name of the doc to edit. :returns: {'success': True} if there were no errors. """ tracker, config = setup() success = False if request.form: doc = tracker.doc(doc) doc.write(request.form['edited']) success = True return to_json({'success': success})
def search(query=None): tracker, config = setup() if query is None: query = request.args.get('query', 'test') header = "Search results for '%s'" % query issues_ = tracker.query().search(query) for i in issues_: blurb = i.content if i.comments(): blurb += ' ... ' + ' ... '.join(c.content for c in i.comments() if type(c.content) is str) blurb = highlight(blurb, query) i.content = blurb i.title = highlight(i.title, query, False) return render_template('search.html', issues_=issues_, selected='issues', tracker=tracker, header=header)
def main(name=None): tracker, config = setup() header = 'Documentation for %s' % tracker.config.name docs = tracker.docs() if name: try: doc = tracker.doc(name) converted = doc.read(convert=True) markdown = doc.read() except OSError: abort(404) else: doc = docs[0] converted = doc.read(convert=True) markdown = doc.read() return render_template('docs.html', tracker=tracker, docs=docs, doc=doc, markdown=markdown, converted=converted, selected='docs', header=header)
def open_issues(): """Returns open issues.""" tracker, config = setup() issues = [i.fields for i in tracker.issues(status='open')] return to_json(issues)
def settings(): """Settings""" tracker, config = setup() return render_template('/settings.html')
def main(): tracker, config = setup() # get the offset param (intended for javascript requests) offset = int(request.args.get('offset', 0)) # Get up to 20 commits starting at whatever offset. raw_history = tracker.history(n=offset+20, offset=offset) # Don't want the performance hit of checking how many commits there are. # Instead, we're guessing that there's more if we get a full 20 in the # first batch. more_history = False if len(raw_history) < 20 else True # get the unique set of authors (in this history segment) users = set(strip_email(c.author) for c in raw_history) docs = tracker.docs() history = [] for c in raw_history: split = c.author.index('<') name = c.author[:split] email = c.author[(split + 1):-1] time = relative_time(c.commit_time) message, obj = interpret(c.message, tracker) snippet = title = link = button = None if type(obj) is Comment: snippet = cut(obj.content, 190) link = url_for('issues.view', id=obj.issue.id[:6]) + '#comment-' + obj.id[:6] button = obj.issue.id[:6] if type(obj) is Issue: title = obj.title snippet = cut(obj.content, 190) link = url_for('issues.view', id=obj.id[:6]) button = obj.id[:6] history.append({'user': {'name': name, 'email': email}, 'message': message.strip(), 'time': time, 'link': link, 'button': button, 'title': title, 'snippet': snippet } ) # If the request is async, we're all set. if request.is_xhr: return to_json(history) # Otherwise, we have to get some more info. header = 'Recent Activity for %s' % tracker.config.name # Issue counts n_open = tracker.query().count('open') n_closed = tracker.query().count('closed') n_total = n_open + n_closed # Graph percentages # the subtraction is spacing for the CSS. g_open = (float(n_open) / n_total) * 100 - 2 if n_total > 0 else 0 g_closed = (float(n_closed) / n_total) * 100 - 2 if n_total > 0 else 0 g_open = 1 if g_open < 1 else g_open g_closed = 1 if g_closed < 1 else g_closed return render_template('feed.html', history=history, selected='feed', header=header, tracker=tracker, users=users, docs=docs, n_open=n_open, n_closed=n_closed, n_total=n_total, g_open=g_open, g_closed=g_closed, more_history=more_history)
def main(): tracker, config = setup() # get the offset param (intended for javascript requests) offset = int(request.args.get('offset', 0)) # Get up to 20 commits starting at whatever offset. raw_history = tracker.history(n=offset + 20, offset=offset) # Don't want the performance hit of checking how many commits there are. # Instead, we're guessing that there's more if we get a full 20 in the # first batch. more_history = False if len(raw_history) < 20 else True # get the unique set of authors (in this history segment) users = set(strip_email(c.author) for c in raw_history) docs = tracker.docs() history = [] for c in raw_history: split = c.author.index('<') name = c.author[:split] email = c.author[(split + 1):-1] time = relative_time(c.commit_time) message, obj = interpret(c.message, tracker) snippet = title = link = button = None if type(obj) is Comment: snippet = cut(obj.content, 190) link = url_for('issues.view', id=obj.issue.id[:6]) + '#comment-' + obj.id[:6] button = obj.issue.id[:6] if type(obj) is Issue: title = obj.title snippet = cut(obj.content, 190) link = url_for('issues.view', id=obj.id[:6]) button = obj.id[:6] history.append({ 'user': { 'name': name, 'email': email }, 'message': message.strip(), 'time': time, 'link': link, 'button': button, 'title': title, 'snippet': snippet }) # If the request is async, we're all set. if request.is_xhr: return to_json(history) # Otherwise, we have to get some more info. header = 'Recent Activity for %s' % tracker.config.name # Issue counts n_open = tracker.query().count('open') n_closed = tracker.query().count('closed') n_total = n_open + n_closed # Graph percentages # the subtraction is spacing for the CSS. g_open = (float(n_open) / n_total) * 100 - 2 if n_total > 0 else 0 g_closed = (float(n_closed) / n_total) * 100 - 2 if n_total > 0 else 0 g_open = 1 if g_open < 1 else g_open g_closed = 1 if g_closed < 1 else g_closed return render_template('feed.html', history=history, selected='feed', header=header, tracker=tracker, users=users, docs=docs, n_open=n_open, n_closed=n_closed, n_total=n_total, g_open=g_open, g_closed=g_closed, more_history=more_history)
def closed_issues(): """Returns closed issues.""" tracker, config = setup() issues = [i.fields for i in tracker.issues(status='closed')] return to_json(issues)