def sent(handler): handler.title('Messages') requirePriv(handler, 'User') print tabs.where('sent') undelay(handler) Markdown.head('div.message .body pre code') messages = Message.loadAll(senderid = handler.session['user'].id, orderby = '-timestamp') for message in messages: printMessage(message, False)
def showSprintRetrospective(handler, id): requirePriv(handler, 'User') id = int(id) sprint = Sprint.load(id) if not sprint or sprint.isHidden(handler.session['user']): ErrorBox.die('Sprints', "No sprint with ID <b>%d</b>" % id) elif not sprint.canView(handler.session['user']): ErrorBox.die('Private', "You must be a sprint member to view this sprint") editing = (sprint.owner == handler.session['user']) handler.title(sprint.safe.name) drawNavArrows(sprint, handler.session['user'], 'retrospective') print tabs(sprint, ('wrapup', 'retrospective')) Markdown.head() print "<script type=\"text/javascript\">" print "var sprint_id = %d;" % sprint.id print "var editing = %s;" % toJS(editing) print "</script>" if not (sprint.isReview() or sprint.isOver()): ErrorBox.die('Sprint Open', "The retrospective isn't available until the sprint is in review") retro = Retrospective.load(sprint) if retro is None: if editing: print "<form method=\"post\" action=\"/sprints/%d/retrospective/start\">" % sprint.id print Button("Start Retrospective").post().positive() print "</form>" else: print ErrorBox("This sprint has no retrospective") done() print "The sprint retrospective asks two main questions:<br>" print "<ul class=\"retrospective-list\">" print "<li class=\"good\">What went well during the sprint?</li>" print "<li class=\"bad\">What could be improved in the next sprint?</li>" print "</ul>" if editing: print "For now the list of categories is fixed; eventually you'll be able to modify them per-sprint. Click an existing item to edit it, or the margin icon to toggle it. Fill in the text area at the end of the category to add a new item; clear a text area to delete it. <a href=\"/help/markdown\">Markdown</a> is available if necessary" print "<div class=\"retrospective markdown\">" for category, entries in retro.iteritems(): print "<div class=\"category\" data-id=\"%d\">" % category.id print "<div class=\"name\">%s</div>" % stripTags(category.name) for entry in sorted(entries, key = lambda entry: 0 if entry.good else 1): print "<div class=\"entry %s\" data-id=\"%d\"><textarea>%s</textarea></div>" % ('good' if entry.good else 'bad', entry.id, stripTags(entry.body)) if not editing and len(entries) == 0: print "<div class=\"none\">No entries</div>" print "</div>" print "</div>"
def inbox(handler): handler.title('Messages') requirePriv(handler, 'User') print InfoBox("Note", "You can control your automated message subscriptions from the <a href=\"/prefs\">preferences</a> page") print tabs.where('inbox') undelay(handler) Markdown.head('div.message .body pre code') messages = Message.loadAll(userid = handler.session['user'].id, orderby = '-timestamp') for message in messages: printMessage(message, True) if not message.read: message.read = True message.save()
def render(self): for case in switch(self.language): if case('markdown'): return Markdown.render(self.body) elif case('html'): return self.body else: # Shouldn't happen return self.body
def sprintRetrospectiveRender(handler, sprintid, p_id, p_catid, p_body = None, p_good = None): def die(msg): print msg done() handler.wrappers = False if p_id != 'new': p_id = to_int(p_id, 'id', die) p_catid = to_int(p_catid, 'catid', die) if p_good is not None: p_good = to_bool(p_good) if not handler.session['user']: die("You must be logged in to modify sprint info") sprintid = int(sprintid) sprint = Sprint.load(sprintid) if not sprint or sprint.isHidden(handler.session['user']): die("There is no sprint with ID %d" % sprintid) elif sprint.owner != handler.session['user'] and (p_body is not None or p_good is not None): die("Only the scrummaster can edit the retrospective") elif not (sprint.isReview() or sprint.isOver()): die("The retrospective isn't available until the sprint is in review") if p_id == 'new': if p_body is None or p_good is None: die("Missing body/good") entry = RetroEntry(p_catid, p_body, p_good) else: entry = RetroEntry.load(p_id) if p_body is not None: entry.body = p_body if p_good is not None: entry.good = p_good if entry.body == '': entry.delete() handler.responseCode = 299 print toJS({'id': entry.id, 'deleted': True}); return entry.save() handler.responseCode = 299 print toJS({'id': entry.id, 'body': Markdown.render(entry.body), 'good': entry.good})
def user(handler, username): user = User.load(username = username) if not user: ErrorBox.die('User', "No user named <b>%s</b>" % stripTags(username)) Markdown.head('form#message-form .body pre code') print "<script src=\"/static/jquery.typing-0.2.0.min.js\" type=\"text/javascript\"></script>" print "<script src=\"/static/users.js\" type=\"text/javascript\"></script>" Chart.include() undelay(handler) handler.title(user.safe.username) handler.replace('$bodytitle$', '', 1) print "<img src=\"%s\" class=\"gravatar\">" % user.getAvatar(64) print "<h1>%s</h1>" % user.safe.username if isDevMode(handler): print "<div class=\"debugtext\">User ID: %d</div>" % user.id print "<div class=\"clear\"></div>" if handler.session['user'] and handler.session['user'].hasPrivilege('Admin'): print "<h3>Admin</h3>" print "<form method=\"post\" action=\"/admin/users\">" print "<input type=\"hidden\" name=\"username\" value=\"%s\">" % user.username print "<button type=\"submit\" class=\"btn\" name=\"action\" value=\"resetpw\">Reset password</button>" print "<button type=\"submit\" class=\"btn\" name=\"action\" value=\"impersonate\">Impersonate</button>" print "<button type=\"submit\" class=\"btn\" name=\"action\" value=\"sessions\">Manage sessions</button>" print "<button type=\"submit\" class=\"btn\" name=\"action\" value=\"privileges\">Manage privileges</button>" print "</form>" if user == handler.session['user']: print "<h3>Avatar</h3>" if user.hasLocalAvatar(): print "Your avatar is currently <a href=\"/users/%s/avatar/set\">locally hosted</a>" % user.username else: print "Your avatar can be changed at <a href=\"http://gravatar.com/\" target=\"_new\">http://gravatar.com/</a>. It must be associated with the e-mail <b>%s</b>, and be rated PG. You can also host an avatar <a href=\"/users/%s/avatar/set\">locally</a>, if necessary" % (user.getEmail(), user.username) print "<h3>Authentication</h3>" print "Your sprint tool password can be changed <a href=\"/resetpw\">here</a>.", if settings.kerberosRealm: print "You can also use your %s kerberos password to login" % settings.kerberosRealm, print "<br><br>" if user.hotpKey == '': print "You also have the option to use two-factor authentication via <a href=\"http://en.wikipedia.org/wiki/HOTP\">HOTP</a>. You can use <a href=\"http://support.google.com/a/bin/answer.py?hl=en&answer=1037451\">Google Authenticator</a> to generate verification codes<br><br>" print "<form method=\"post\" action=\"/security/two-factor\">" print "<button type=\"submit\" class=\"btn danger\" name=\"action\" value=\"enable\">Enable two-factor authentication</button>" print "</form>" else: print "You are currently using two-factor authentication<br><br>" print "<form method=\"post\" action=\"/security/two-factor\">" print "<button type=\"submit\" class=\"btn danger\" name=\"action\" value=\"enable\">Reset HOTP key</button>" print "<button type=\"submit\" class=\"btn danger\" name=\"action\" value=\"disable\">Disable two-factor authentication</button>" print "</form>" print "<h3>Messages</h3>" print "Your inbox and sent messages can be viewed <a href=\"/messages/inbox\">here</a><br>" print "<h3>Last seen</h3>" if not user.lastseen: print "Never" elif dateToTs(getNow()) - user.lastseen < 60: print "Just now" else: print "%s ago" % timesince(tsToDate(user.lastseen)) if handler.session['user'] and handler.session['user'] != user: print "<h3>Message</h3>" print "<small>(Messages are formatted in <a target=\"_blank\" href=\"/help/markdown\">markdown</a>)</small>" print "<form id=\"message-form\" method=\"post\" action=\"/messages/send\">" print "<input type=\"hidden\" name=\"userid\" value=\"%d\">" % user.id print "<textarea name=\"body\" class=\"large\"></textarea>" print "<div class=\"body markdown\"><div id=\"preview\"></div></div>" print Button('Send').post().positive() print "</form>" print "<h3>Project distribution</h3>" sprints = filter(lambda s: user in s.members, Sprint.loadAllActive()) sprintHours = map(lambda s: (s, Availability(s).getAllForward(getNow(), user)), sprints) projectHours = map(lambda (p, g): (p, sum(hours for sprint, hours in g)), groupby(sprintHours, lambda (s, a): s.project)) # For now at least, don't show projects with no hours projectHours = filter(lambda (p, h): h > 0, projectHours) if len(projectHours) > 0: chart = Chart('chart') chart.title.text = '' chart.tooltip.formatter = "function() {return '<b>' + this.point.name + '</b>: ' + this.point.y + '%';}" chart.plotOptions.pie.allowPointSelect = True chart.plotOptions.pie.cursor = 'pointer' chart.plotOptions.pie.dataLabels.enabled = False chart.plotOptions.pie.showInLegend = True chart.credits.enabled = False chart.series = seriesList = [] series = { 'type': 'pie', 'name': '', 'data': [] } seriesList.append(series) total = sum(hours for project, hours in projectHours) for project, hours in projectHours: series['data'].append([project.name, float("%2.2f" % (100 * hours / total))]) chart.js() chart.placeholder() else: print "Not a member of any active sprints"
# Make sure file exists if (not isfile(args.input_file)): print(f"{c.FAIL}Error:{c.ENDC} Input file does not exist.") sys.exit(1) # Record start time t1 = datetime.now() # Otherwise, process the input file print(f"Processing {c.UNDERLINE}{args.input_file}{c.ENDC} ... ") # Set document variables word_count, sentence_count, paragraph_count, overused_phrase_count, repeated_word_count, avoid_word_count, complex_words, syllable_count, fog_index, reading_ease, grade_level = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 # Instantiate Markdown parser md = Markdown() # Read the template fd = open("./assets/template.html", "r") template = fd.read().split("<!-- DIVIDER -->") fd.close() # Open the output file if (args.output_file == None): args.output_file = "./index.html" open(args.output_file, "w").close() outfile = open(args.output_file, "a") # Process file infile = open(args.input_file, "r")
def render(self): return Markdown.render(self.body)
def task(handler, ids): requirePriv(handler, "User") Chart.include() Markdown.head(".note .text .body pre code") print '<script src="/static/jquery.typing-0.2.0.min.js" type="text/javascript"></script>' undelay(handler) tasks = {} if "," not in ids: # Single ID ids = [int(ids)] tasks[ids[0]] = Task.load(ids[0]) def header(task, text, level): if level == 1: handler.title(text) else: print "<h%d>%s</h%d>" % (level, text, level) else: # Many IDs ids = map(int, uniq(ids.split(","))) tasks = {id: Task.load(id) for id in ids} handler.title("Task Information") if not all(tasks.values()): ids = [str(id) for (id, task) in tasks.iteritems() if not task] ErrorBox.die( "No %s with %s %s" % ("task" if len(ids) == 1 else "tasks", "ID" if len(ids) == 1 else "IDs", ", ".join(ids)) ) if len(set(task.sprint for task in tasks.values())) == 1: # All in the same sprint print '<small>(<a href="/sprints/%d?search=highlight:%s">Show in backlog view</a>)</small><br><br>' % ( tasks.values()[0].sprint.id, ",".join(map(str, ids)), ) for id in ids: print '<a href="#task%d">%s</a><br>' % (id, tasks[id].safe.name) def header(task, text, level): if level == 1: print "<hr>" print '<a name="task%d"></a>' % task.id print '<a href="#task%d"><h2>%s</h2></a>' % (task.id, text) else: print "<h%d>%s</h%d>" % (level + 1, text, level + 1) for id in ids: task = tasks[id] if not task or task.sprint.isHidden(handler.session["user"]): ErrorBox.die("Tasks", "No task with ID <b>%d</b>" % id) elif not task.sprint.canView(handler.session["user"]): ErrorBox.die("Private", "You must be a sprint member to view this sprint's tasks") revs = task.getRevisions() startRev = task.getStartRevision() header(task, task.safe.name, 1) header(task, "Info", 2) print 'Part of <a href="/sprints/%d">%s</a>, <a href="/sprints/%d#group%d">%s</a>' % ( task.sprintid, task.sprint, task.sprintid, task.groupid, task.group, ), if task.goal: print 'to meet the goal <img class="bumpdown" src="/static/images/tag-%s.png"> <a href="/sprints/%d?search=goal:%s">%s</a>' % ( task.goal.color, task.sprintid, task.goal.color, task.goal.safe.name, ), print "<br>" print "Assigned to %s<br>" % ", ".join(map(str, task.assigned)) print "Last changed %s ago<br><br>" % timesince(tsToDate(task.timestamp)) hours, total, lbl = task.hours, startRev.hours, "<b>%s</b>" % statuses[task.status].text if task.deleted: if task.sprint.canEdit(handler.session["user"]): print '<form method="post" action="/sprints/%d">' % task.sprint.id print '<input type="hidden" name="id" value="%d">' % task.id print '<input type="hidden" name="rev_id" value="%d">' % task.revision print '<input type="hidden" name="field" value="deleted">' print '<input type="hidden" name="value" value="false">' print "Deleted (%s)" % Button("undelete", id="undelete").mini().positive() print "</form>" else: print "Deleted" print "<br>" elif task.status == "complete": print ProgressBar(lbl, total - hours, total, zeroDivZero=True, style="progress-current-green") elif task.status in ("blocked", "canceled", "deferred", "split"): hours = filter(lambda rev: rev.hours > 0, revs) hours = hours[-1].hours if len(hours) > 0 else 0 print ProgressBar(lbl, total - hours, total, zeroDivZero=True, style="progress-current-red") else: print ProgressBar(lbl, total - hours, total, zeroDivZero=True) header(task, "Notes", 2) for note in task.getNotes(): print '<div id="note%d" class="note">' % note.id print '<form method="post" action="/tasks/%d/notes/%d/modify">' % (id, note.id) print '<div class="avatar"><img src="%s"></div>' % note.user.getAvatar() print '<div class="text">' print '<div class="title"><a class="timestamp" href="#note%d">%s</a> by <span class="author">%s</span>' % ( note.id, tsToDate(note.timestamp).replace(microsecond=0), note.user.safe.username, ) if note.user == handler.session["user"]: print '<button name="action" value="delete" class="fancy mini danger">delete</button>' print "</div>" print '<div class="body markdown">%s</div>' % note.render() print "</div>" print "</form>" print "</div>" print '<div class="note new-note">' print '<form method="post" action="/tasks/%d/notes/new">' % id print '<div class="avatar"><div><img src="%s"></div></div>' % handler.session["user"].getAvatar() print '<div class="text">' print '<div class="title">' print "<b>New note</b>" print '<a target="_blank" href="/help/markdown" class="fancy mini">help</a>' print "</div>" print '<div class="body"><textarea name="body" class="large"></textarea></div>' print Button("Post").post().positive() print "<hr>" print '<div class="body markdown"><div id="preview"></div></div>' print "</div>" print "</form>" print "</div>" print '<button class="btn start-new-note">Add Note</button>' print '<div class="clear"></div>' header(task, "History", 2) chart = TaskChart("chart%d" % id, task) chart.js() chart.placeholder() showHistory(task, False) print "<br>"
def GenFile(iname): # Instantiate document statistics # fk_wc is a special word count for the Flesch-Kincaid readability test # word_count is a by-paragraph word count # total_sentences is a count of all sentences in document # total_word_count is the word count for the entire document # total_overused_words is the count of overused words # total_repeated_words is the count of unique repeated words in the document, # not including the individual repeats # total_avoid_words is the ocunt of words to avoid in the document # complex_words is a running count of words with over three syllables # syllable_count is a running cound of syllables in the document fk_wc = 0 word_count = [] total_sentences = 0 total_word_count = 0 total_overused_words = 0 total_repeated_words = 0 total_avoid_words = 0 complex_words = 0 syllable_count = 0 # Open th template file, read its contents, and split them for easy access later template_fd = open("template.html", "r") template = template_fd.read().split("<!--Divider-->") template_fd.close() # Clear the output file, then write the opening HTML tags o_fd = open("index.html", "w").close() o_fd = open("index.html", "a") o_fd.write(template[0]) # Open the source file fd = open(iname, "r") # Read the title from the source file title = fd.readline().strip() if (title[0] == "#"): title = title.split("](") title = "<h2 class='linkpost'><a href=\"" + title[ 1][:-3] + "\">" + title[0][3:] + "</a></h2>" else: title = "<h2 class='original'>" + title + "</h2>\n" # Get rid of the title separator (=) and the following blank line fd.readline() # Write the opening <article> tag and article title o_fd.write("<article>\n") o_fd.write(title) block = False # Iterate over each line in the file for line in iter(fd.readline, ""): fk_wc += line.count(" ") + 1 # Save a "backup" of the line, for searching a sanitized version of it backup = line # If we're looking at an empty line, just skip over it; else continue if (len(line.strip()) == 0): continue # Do not collect stats on code snippets. Write them to the file and # move on. if (line[0:4] == "<pre" or block == True): if (line.find("</pre>") == -1): block = True else: block = False # Do not collect stats on images. Write them to the file and move on. if (line[0:2] == "![" or line[0:4] == "<pre"): o_fd.write(Markdown(line, "https://zacs.site/") + "\n") continue # Instantiate paragraph-specific statistics wc = 0 # Word count for current paragraph overused_words = 0 # Number of overused words repeated_words = 0 # Number of repeated words avoid_words = 0 # Number of words to avoid dict_count = {} # A dictionary that will count occurences of each word # For each word in the list of overused words to avoid, search the # paragraph case insensitively. If a match is found, increment the count # of overused words in the paragraph and document, then highlight it. for word in overlap: m = re.search("[^\w]" + word + "[^\w]", line, re.IGNORECASE) if (m): overused_words += line.lower().count(word) total_overused_words += overused_words # The first replace will capture matches with uppercase letters # that start a sentence, or regular lowercase words; if the first # replace targeted matches with uppercase letters that start a # sentence, the second replace will capture all other occurences of # that word that may exist throughout the document. line = line.replace( m.group(0), " <span class='replace'>" + m.group(0) + "</span> ") if (m.group(0) != m.group(0).lower()): line = line.replace( m.group(0).lower(), " <span class='replace'>" + m.group(0).lower().strip() + "</span> ") # For each word in the sentence, count repetitions. If there are three or more # of the same word in a sentnece, highlight all occurences. Also check for be # verbs as well, and highlight them accordingly. # for word in backup.split(" "): for word in re.split("(\s|--)", backup): if ("](" in word): word = word.split("](")[0] # This strips any special characters from the word, such as punctuation. stripped = re.sub(r"^[\W]+", "", word.strip()) stripped = re.sub(r"[\W]+$", "", stripped) if (len(stripped) == 0): continue wc += 1 # First check if we have decided to exclude the word, as in the case of "the", # "of", "a", "for", or similar words. If true, skip the word; else, proceed. if (stripped.lower() not in exclude): # If the word already exists in the dictionary, increment its count; else # instantiate it to 1 if (stripped.lower() in dict_count): dict_count[stripped.lower()] += 1 else: dict_count[stripped.lower()] = 1 # Once there are at least three occurences of a word in the paragraph, # highlight it as a repeat word and incrememnt the number of unique words # repeated in the document. if (dict_count[stripped.lower()] == 3): line = re.sub( r"([^\w])" + stripped + r"([^\w])", r"\1<span class='repeat " + stripped + "'>" + stripped + r"</span>\2", line) repeated_words += 1 total_repeated_words += 1 # Check for be verbs, "ly" words in the document. If found, highlight # them and increment the be verb count. if (stripped.lower() in be_verbs) or (stripped.lower()[-2:] == "ly"): line = re.sub( r"([^\w])" + stripped + r"([^\w])", r"\1<span class='avoid'>" + stripped + r"</span>\2", line) avoid_words += 1 total_avoid_words += 1 # To calculate the number of complex words, first exclude proper nouns. Next, # exclude compound words, then strip -es, -ed, and -ing endings. Finally, if # the number of syllables in the remaining word is >= 3, found a complex word. if (not (re.search("^[A-Z]", stripped))): if ("-" not in stripped): if (SyllableCount(stripped.lower()) >= 3): start = line.find(word) length = len(word) end = start + length # print("Searched: '%s'" % line[start:end]) # print("With ends: '%s'" % line[start-1:end+1]) # print("Preceeding character: '%s'" % line[start-1]) # print("After character: '%s'" % line[end]) # print(re.match("[\>\w]", line[start-1])) # print(re.match("[\<\w]", line[end])) # print(line) # print if not ("http" in stripped or re.match("[\>\w]", line[start - 1]) or re.match("[\<\w]", line[end])): line = line.replace( stripped, "<span class='complex_word'>" + stripped + "</span>") # line = re.sub((r"[^\>\w]")+stripped+(r"[^\<\w]"), "\1<span class='complex_word'>"+stripped+"</span>\2", line) complex_words += 1 # sleep(1) syllable_count += SyllableCount(stripped.lower()) word_count.append(wc) # Count sentences in paragraph, and add that number to the running sentence total sentences = (len(re.findall("\.[^\w]", line)) + len(re.findall("[?!]", line))) or 1 total_sentences += sentences if (line[0:1] != "* " and line[0] != "#" and line[0:3] != "<pre" and block == False and line[-7:].strip() != "</pre>"): # Write the paragraph stats div to the output file, then the parsed line. o_fd.write( "<div class='floating_stats'><div>Words: %d. Sentences: %d</div><div>Overused phrase: %d</div><div>Repeated: %d; Avoid: %d</div></div>\n" % (word_count[-1], sentences, overused_words, repeated_words, avoid_words)) o_fd.write(Markdown(line, "https://zacs.site/") + "\n") # Close the source file fd.close() # Write closing <article> tag o_fd.write("</article>") # Sum the paragraph word counts into a single count, for document stats for num in word_count: total_word_count += int(num) # Get a timestamp for the document stats d = datetime.datetime.now() utime = "%d-%d-%d %d:%d:%d" % (d.year, d.month, d.day, d.hour, d.minute, d.second) # Calculate Gunning Fog Index # estimates the years of formal education needed to understand the text on a first reading. gfi = 0.4 * (float(total_word_count) / float(total_sentences) + 100.0 * float(complex_words) / float(total_word_count)) # Calculate Flesch-Kincaid Readability Test # higher scores indicate material that is easier to read; lower numbers indicate difficulty. fkr = 206.835 - 1.015 * (float(fk_wc) / float(total_sentences)) - 84.6 * ( float(syllable_count) / float(fk_wc)) if (fkr <= 30.0): fkr = "<span class='extreme'>%3.2f</span>" % (fkr) elif (fkr <= 50.0): fkr = "<span class='hard'>%3.2f</span>" % (fkr) elif (fkr <= 60.0): fkr = "<span class='tough'>%3.2f</span>" % (fkr) elif (fkr <= 70.0): fkr = "<span class='plain'>%3.2f</span>" % (fkr) elif (fkr <= 80.0): fkr = "<span class='fair'>%3.2f</span>" % (fkr) elif (fkr <= 90.0): fkr = "<span class='easy'>%3.2f</span>" % (fkr) elif (fkr <= 100.00): fkr = "<span class='simple'>%3.2f</span>" % (fkr) # Calculate the Flesch-Kincaid Grade level: # the number of years of education generally required to understand this text. fgl = 0.39 * float(total_word_count) / float( total_sentences) + 11.8 * float(syllable_count) / float( total_word_count) - 15.59 # Write the closing HTML to the output file, with document stats. Close it. o_fd.write(template[1] % (utime, utime, total_word_count, str(total_word_count / 200.0) + " mins", total_sentences, len(word_count), total_word_count / len(word_count), total_overused_words, total_repeated_words, total_avoid_words, gfi, fkr, fgl)) o_fd.close()