Add event list

This commit is contained in:
Drew DeVault 2017-11-09 10:09:50 -05:00
parent e9f219e391
commit dfb0799672
8 changed files with 227 additions and 14 deletions

View File

@ -19,3 +19,33 @@ select {
margin-top: 1rem;
}
}
blockquote {
padding-left: 0.5rem;
border-left: 3px solid #aaa;
color: #333;
}
.events {
h4 {
margin-bottom: 0;
padding: 0;
}
.event {
padding: 0.5rem;
border-bottom: 1px solid #888;
&:first-child {
margin-top: 0.5rem;
}
&:nth-child(2n) {
background: #f8f8f8;
}
&:last-child {
padding-bottom: 0;
}
}
}

View File

@ -0,0 +1,33 @@
"""Add events
Revision ID: 132ee194cefe
Revises: 5fb6db84ce55
Create Date: 2017-11-09 09:26:18.763954
"""
# revision identifiers, used by Alembic.
revision = '132ee194cefe'
down_revision = '5fb6db84ce55'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.create_table('event',
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('created', sa.DateTime, nullable=False),
sa.Column('event_type', sa.Integer, nullable=False),
sa.Column('old_status', sa.Integer, default=0),
sa.Column('old_resolution', sa.Integer, default=0),
sa.Column('new_status', sa.Integer, default=0),
sa.Column('new_resolution', sa.Integer, default=0),
sa.Column('user_id', sa.Integer, sa.ForeignKey("user.id"), nullable=False),
sa.Column('ticket_id', sa.Integer, sa.ForeignKey("ticket.id"), nullable=False),
sa.Column('comment_id', sa.Integer, sa.ForeignKey("ticket_comment.id"))
)
def downgrade():
op.drop_table('event')

View File

@ -1,6 +1,6 @@
from flask import Blueprint, render_template
from flask_login import current_user
from todosrht.types import Tracker
from todosrht.types import Tracker, Event, EventType
html = Blueprint('html', __name__)
@ -12,5 +12,11 @@ def index():
your_trackers = (Tracker.query
.filter(Tracker.owner_id == current_user.id)
.order_by(Tracker.updated.desc())).all()
events = (Event.query
.filter(Event.user_id == current_user.id)
.order_by(Event.created.desc())
.limit(10)).all()
return render_template("dashboard.html",
your_trackers=your_trackers)
your_trackers=your_trackers,
events=events,
EventType=EventType)

View File

@ -5,7 +5,7 @@ from flask import session
from flask_login import current_user
from todosrht.decorators import loginrequired
from todosrht.types import Tracker, User, Ticket, TicketStatus, TicketAccess, TicketSeen
from todosrht.types import TicketComment, TicketResolution
from todosrht.types import TicketComment, TicketResolution, Event, EventType
from todosrht.blueprints.tracker import get_access, get_tracker
from todosrht.email import notify
from srht.config import cfg
@ -92,6 +92,9 @@ def ticket_comment_POST(owner, name, ticket_id):
else:
comment = None
old_status = ticket.status
old_resolution = ticket.resolution
if resolve and TicketAccess.edit in access:
try:
resolution = TicketResolution(int(resolution))
@ -113,7 +116,7 @@ def ticket_comment_POST(owner, name, ticket_id):
**valid.kwargs)
tracker.updated = datetime.utcnow()
db.session.commit()
db.session.flush()
if comment:
ticket_url = url_for(".ticket_GET",
@ -142,13 +145,35 @@ def ticket_comment_POST(owner, name, ticket_id):
resolution=resolution.name if resolution else None,
ticket_url=ticket_url.replace("%7E", "~")) # hack
def _add_event(sub):
event = Event()
event.event_type = 0
event.user_id = sub.user_id
event.ticket_id = ticket.id
if comment:
event.event_type |= EventType.comment
event.comment_id = comment.id
if ticket.status != old_status or ticket.resolution != old_resolution:
event.event_type |= EventType.status_change
event.old_status = old_status
event.old_resolution = old_resolution
event.new_status = ticket.status
event.new_resolution = ticket.resolution
db.session.add(event)
updated_users = set()
for sub in tracker.subscriptions:
updated_users.update([sub.user_id])
_add_event(sub)
if sub.user_id == current_user.id:
subscribed = True
continue
_notify(sub)
for sub in ticket.subscriptions:
if sub.user_id in updated_users:
continue
_add_event(sub)
if sub.user_id == current_user.id:
subscribed = True
continue
@ -159,6 +184,8 @@ def ticket_comment_POST(owner, name, ticket_id):
sub.ticket_id = ticket.id
sub.user_id = user.id
db.session.add(sub)
db.session.commit()
_add_event(sub)
db.session.commit()
return redirect(ticket_url)

View File

@ -5,8 +5,9 @@ from flask import session
from flask_login import current_user
from todosrht.decorators import loginrequired
from todosrht.email import notify
from todosrht.types import Tracker, User, Ticket, TicketStatus, TicketAccess, TicketSeen
from todosrht.types import Tracker, User, Ticket, TicketStatus, TicketAccess
from todosrht.types import TicketComment, TicketResolution, TicketSubscription
from todosrht.types import TicketSeen, Event, EventType
from srht.config import cfg
from srht.database import db
from srht.validation import Validation
@ -201,7 +202,7 @@ def tracker_submit_POST(owner, name):
db.session.add(ticket)
tracker.updated = datetime.utcnow()
# TODO: Handle unique constraint failure (contention) and retry?
db.session.commit()
db.session.flush()
ticket_url = url_for("ticket.ticket_GET",
owner="~" + tracker.owner.username,
@ -209,6 +210,12 @@ def tracker_submit_POST(owner, name):
ticket_id=ticket.scoped_id)
for sub in tracker.subscriptions:
event = Event()
event.event_type = EventType.created
event.user_id = sub.user_id
event.ticket_id = ticket.id
db.session.add(event)
if sub.user_id == ticket.submitter_id:
subscribed = True
continue
@ -225,7 +232,8 @@ def tracker_submit_POST(owner, name):
sub.ticket_id = ticket.id
sub.user_id = user.id
db.session.add(sub)
db.session.commit()
db.session.commit()
if another:
session["another"] = True

View File

@ -1,23 +1,92 @@
{% extends "todo.html" %}
{% block content %}
<div class="container">
<h2>todo</h2>
<p>Welcome to todo.sr.ht, {{ current_user.username }}!</p>
<h2 style="margin-bottom: 1rem">todo</h2>
<div class="row">
<div class="col-md-4">
<h3>Your Trackers</h3>
{% if your_trackers %}
<div class="tracker-list">
{% for tracker in your_trackers %}
<h4>{{ format_tracker_name(tracker, full=True) }}</h4>
<span class="text-muted">Last active {{ tracker.updated | date }}</span>
{% endfor %}
</div>
{% else %}
<p>
Nothing here yet! Would you like to
<a href="{{ url_for("tracker.create_GET") }}">create one</a>?
</p>
{% endif %}
</div>
{#
<div class="col-md-4">
<h3>Updated Tickets</h3>
<div class="col-md-6">
<h3>Recent Activity</h3>
{% if events %}
<div class="events">
{% for event in events %}
<div class="event">
<h4>
<a href="{{ url_for("ticket.ticket_GET",
owner="~" + event.ticket.tracker.owner.username,
name=event.ticket.tracker.name,
ticket_id=event.ticket.scoped_id) }}">
#{{event.ticket.scoped_id}}
</a> {{ event.ticket.title }}
<small class="pull-right">
{{ event.created | date }}
</small>
</h4>
{% if EventType.created in event.event_type %}
<p>
Ticket created by
<a href="/~{{ event.ticket.submitter.username }}">
~{{ event.ticket.submitter.username }}
</a>
on {{ format_tracker_name(event.ticket.tracker, full=True) }}:
</p>
{% endif %}
{% if EventType.comment in event.event_type %}
<p>
Comment by
<a href="/~{{ event.ticket.submitter.username }}">
~{{ event.ticket.submitter.username }}
</a>
on {{ format_tracker_name(event.ticket.tracker, full=True) }}:
</p>
{% endif %}
<blockquote style="margin-bottom: 0">
{% if event.comment %}
{{ event.comment.text | md }}
{% else %}
{% if EventType.created in event.event_type %}
{{ event.ticket.description | md }}
{% endif %}
{% endif %}
</blockquote>
{% if EventType.status_change in event.event_type %}
<p>
<strong class="text-success">
{{ event.old_status.name.upper() }}
{% if event.old_status == TicketStatus.resolved %}
{{ event.old_resolution.name.upper() }}
{% endif %}
</strong>
<span class="fa fa-arrow-right"></span>
<strong class="text-success">
{{ event.new_status.name.upper() }}
{% if event.new_status == TicketStatus.resolved %}
{{ event.new_resolution.name.upper() }}
{% endif %}
</strong>
</p>
{% endif %}
</div>
{% endfor %}
{% else %}
<p>Nothing here yet!</p>
{% endif %}
</div>
</div>
#}
</div>
</div>
{% endblock %}

View File

@ -6,3 +6,4 @@ from .ticketseen import TicketSeen
from .ticket import Ticket
from .ticketsubscription import TicketSubscription
from .ticketcomment import TicketComment
from .event import Event, EventType

39
todosrht/types/event.py Normal file
View File

@ -0,0 +1,39 @@
import sqlalchemy as sa
import sqlalchemy_utils as sau
from srht.flagtype import FlagType
from srht.database import Base
from todosrht.types.ticketstatus import TicketStatus, TicketResolution
from enum import IntFlag
class EventType(IntFlag):
created = 1
comment = 2
status_change = 4
class Event(Base):
"""
Maps events on tickets to interested users.
"""
__tablename__ = 'event'
id = sa.Column(sa.Integer, primary_key=True)
created = sa.Column(sa.DateTime, nullable=False)
event_type = sa.Column(FlagType(EventType), nullable=False)
old_status = sa.Column(FlagType(TicketStatus), default=0)
old_resolution = sa.Column(FlagType(TicketResolution), default=0)
new_status = sa.Column(FlagType(TicketStatus), default=0)
new_resolution = sa.Column(FlagType(TicketResolution), default=0)
user_id = sa.Column(sa.Integer, sa.ForeignKey("user.id"), nullable=False)
user = sa.orm.relationship("User", backref=sa.orm.backref("events"))
ticket_id = sa.Column(sa.Integer, sa.ForeignKey("ticket.id"), nullable=False)
ticket = sa.orm.relationship("Ticket")
comment_id = sa.Column(sa.Integer, sa.ForeignKey("ticket_comment.id"))
comment = sa.orm.relationship("TicketComment")
def __repr__(self):
return '<Event {}>'.format(self.id)