def edit_poster(poster_id): sqlsession = Session() # Look up Poster poster = (sqlsession.query(models.Poster) .options( joinedload(models.Poster.emails)) .filter(models.Poster.id == poster_id)).one() if request.method == 'POST': changed = False if 'remove_email' in request.form: email = request.form['remove_email'] (sqlsession.query(models.PosterEmail) .filter(models.PosterEmail.poster_id == poster_id) .filter(models.PosterEmail.address == email)).delete() changed = True if 'add_email' in request.form: email = request.form['add_email'] new_email = models.PosterEmail(address=email, poster=poster) sqlsession.add(new_email) changed = True if 'name' in request.form: poster.name = request.form['name'] sqlsession.add(poster) changed = True if changed: sqlsession.commit() return redirect(url_for('edit_poster', poster_id=poster_id), 303) return render_template('edit_poster.html', poster=poster, msg=request.form.get('msg'), error=request.form.get('error'))
def assign_task(thread_id): """Assign a new task to a thread. """ sqlsession = Session() thread = (sqlsession.query(models.Thread) .filter(models.Thread.id == thread_id)).one() task = (sqlsession.query(models.Task) .filter(models.Task.id == request.form['task'])).one() poster_req = (sqlsession.query(models.Poster) .filter(models.Poster.id == request.form['poster'])) poster = poster_req.one() # Note that, although the Poster definitely exists, here we don't check # that he took part in the thread try: # Assign task task_assignation = models.TaskAssignation( thread=thread, task=task, poster=poster) sqlsession.add(task_assignation) # Update Poster's score poster_req.update({ models.Poster.score: models.Poster.score + task.reward}) sqlsession.commit() except IntegrityError: pass return redirect(url_for('thread', thread_id=thread_id), 303)
def scores(): """Scores. Display a list of contributors with their current number of points. """ sqlsession = Session() posters = (sqlsession.query(models.Poster) .order_by(models.Poster.score.desc())).all() return render_template('scores.html', posters=posters)
def add_email(): """Create a new Poster or add an address to an existing Poster. """ sqlsession = Session() email = request.form['email'] # Look up a Poster with that email poster_email = (sqlsession.query(models.PosterEmail) .options( joinedload(models.PosterEmail.poster)) .filter(models.PosterEmail.address == email)) try: poster_email = poster_email.one() except NoResultFound: pass else: return redirect(url_for( 'edit_poster', poster=poster_email.poster.id, error="This email is already associated to a poster")) # If we got poster the info to create a new poster if 'name' in request.form and 'poster_id' not in request.form: new_poster = models.Poster(name=request.form['name']) sqlsession.add(new_poster) new_email = models.PosterEmail(address=email, poster=new_poster) sqlsession.add(new_email) sqlsession.commit() return redirect(url_for('edit_poster', poster_id=new_poster.id, msg='Poster created'), 303) elif 'name' not in request.form and 'poster_id' in request.form: poster_id = int(request.form['poster_id']) poster = (sqlsession.query(models.Poster) .filter(models.Poster.id == poster_id)).one() new_email = models.PosterEmail(address=email, poster=poster) sqlsession.add(new_email) sqlsession.commit() return redirect(url_for('edit_poster', poster_id=poster.id), 303) # If both are set, something is going on, just display the forms again # Renders the form that will allow to choose an existing Poster or to # create a new one # In both cases, redirect here all_posters = sqlsession.query(models.Poster).all() return render_template('add_email.html', email=email, posters=all_posters)
def vote(): """Main view. Shows the list of threads that require resolution. """ sqlsession = Session() threads = (sqlsession.query(models.Thread) .options( joinedload(models.Thread.task_assignations) .joinedload(models.TaskAssignation.task), joinedload(models.Thread.messages)) .order_by(models.Thread.last_msg.desc())).all() return render_template('vote.html', threads=threads)
def thread(thread_id): """Shows a thread and allows to vote on it. """ sqlsession = Session() thread = (sqlsession.query(models.Thread) .options( joinedload(models.Thread.messages) .joinedload(models.Message.poster_email) .joinedload(models.PosterEmail.poster), joinedload(models.Thread.task_assignations)) .filter(models.Thread.id == thread_id)).one() tasks = (sqlsession.query(models.Task)).all() posters = set(msg.poster_email.poster for msg in thread.messages if msg.poster_email is not None) # The email addresses participating in this thread but not yet associated # to a Poster registerable_senders = [msg.from_ for msg in thread.messages if not msg.poster_email] return render_template('thread.html', thread=thread, tasks=tasks, posters=posters, registerable_senders=registerable_senders)
def main(): logging.basicConfig(level=logging.INFO) host, use_ssl, port, user, password = config.INBOX if callable(user): user = user() if callable(password): password = password() for msg in get_messages(host, use_ssl, port, user, password): # Feed it to Python's standard RFC 2822-based message parser parser = Parser() msg = parser.parsestr(msg) # Headers of interest msgid = msg["Message-ID"] replyto = msg["In-Reply-To"] subject = decode_subject(msg["Subject"]) from_ = email.utils.parseaddr(msg["From"])[1] date = email.utils.parsedate_tz(msg["Date"]) if date: date = datetime.fromtimestamp(email.utils.mktime_tz(date)) logger.debug("Parsing message from %r" % (from_,)) # Find text content if msg.is_multipart(): logger.debug("Message is multipart with %d parts" % (len(msg.get_payload()),)) # RFC 2046 says that the last part is preferred text = None is_html = True for part in msg.get_payload(): if part.get_content_type() == "text/plain" or (part.get_content_type() == "text/html" and is_html): charset = part.get_charsets()[0] text = part.get_payload(decode=True).decode(charset, "replace") is_html = part.get_content_type() == "text/html" if text is not None: logger.debug("Found a text part (text/%s)" % ("html" if is_html else "plain")) else: logger.debug("Didn't find a text part") if text is None: text = msg.preamble is_html = False if text: logger.debug("Using preamble") else: charset = msg.get_charsets()[0] text = msg.get_payload(decode=True).decode(charset, "replace") content_type = msg.get_content_type() is_html = content_type == "text/html" logger.debug("Message is not multipart (%s)" % (content_type,)) if not text: logger.warning("Message from %r has no text!" % (from_,)) text = "(No text content found)" elif is_html: try: import html2text except ImportError: warnings.warn("Can't convert HTML to text -- html2text " "library not found") else: logger.debug("Converting HTML with html2text") h = html2text.HTML2Text() text = h.handle(text) is_html = False # Find thread this message is a part of sqlsession = Session() thread = None if replyto: try: parent_msg = sqlsession.query(models.Message).filter(models.Message.id == replyto).one() except NoResultFound: pass else: thread = parent_msg.thread logger.debug("Message is part of existing thread %d" % (thread.id,)) if thread is None: thread = models.Thread(last_msg=date) thread_created = True sqlsession.add(thread) else: thread_created = False # FIXME : This should be synchronized somehow # Update last_msg date field if thread.last_msg < date: thread.last_msg = date sqlsession.add(thread) # Insert message message = models.Message(id=msgid, thread=thread, date=date, from_=from_, subject=subject, text=text) sqlsession.add(message) try: sqlsession.commit() except IntegrityError: sqlsession.rollback() logger.info("Got IntegrityError inserting message, skipping") else: if thread_created: logger.debug("Created new thread %d" % (thread.id,))