Use tracker-specific ticket IDs

This commit is contained in:
Drew DeVault 2017-09-14 07:20:47 -04:00
parent a44e5a68a6
commit abc404e79c
6 changed files with 92 additions and 15 deletions

View File

@ -0,0 +1,62 @@
"""Add tracker-scoped ticket IDs
Revision ID: 237974dd94c4
Revises: None
Create Date: 2017-09-14 07:01:22.888804
"""
# revision identifiers, used by Alembic.
revision = '237974dd94c4'
down_revision = None
from alembic import op
import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, Session as BaseSession, relationship
Session = sessionmaker()
Base = declarative_base()
class Ticket(Base):
__tablename__ = 'ticket'
id = sa.Column(sa.Integer, primary_key=True)
scoped_id = sa.Column(sa.Integer)
tracker_id = sa.Column(sa.Integer, sa.ForeignKey("tracker.id"), nullable=False)
tracker = sa.orm.relationship("Tracker", backref=sa.orm.backref("tickets"))
class Tracker(Base):
__tablename__ = 'tracker'
id = sa.Column(sa.Integer, primary_key=True)
next_ticket_id = sa.Column(sa.Integer, nullable=False, default=1)
def upgrade():
bind = op.get_bind()
session = Session(bind=bind)
op.add_column('ticket', sa.Column('scoped_id', sa.Integer()))
op.add_column('tracker', sa.Column('next_ticket_id',
sa.Integer(),
nullable=False,
server_default='1'))
session.commit()
for ticket in session.query(Ticket).all():
ticket.scoped_id = ticket.id
session.commit()
for tracker in session.query(Tracker).all():
tracker.next_ticket_id = max(ticket.id for ticket in tracker.tickets) + 1
session.commit()
op.create_index(op.f('ix_ticket_scoped_id'), 'ticket', ['scoped_id'])
op.create_unique_constraint('uq_ticket_scoped_id_tracker_id', 'ticket',
['scoped_id', 'tracker_id'])
op.alter_column('ticket', 'scoped_id', nullable=False)
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('tracker', 'next_ticket_id')
op.drop_index(op.f('ix_ticket_scoped_id'), table_name='ticket')
op.drop_column('ticket', 'scoped_id')
# ### end Alembic commands ###

View File

@ -155,10 +155,13 @@ def tracker_submit_POST(owner, name):
ticket = Ticket()
ticket.submitter_id = current_user.id
ticket.tracker_id = tracker.id
ticket.scoped_id = tracker.next_ticket_id
tracker.next_ticket_id += 1
ticket.user_agent = request.headers.get("User-Agent")
ticket.title = title
ticket.description = desc
db.session.add(ticket)
# TODO: Handle unique constraint failure (contention) and retry?
db.session.commit()
if another:
@ -170,7 +173,7 @@ def tracker_submit_POST(owner, name):
return redirect(url_for(".ticket_GET",
owner="~" + tracker.owner.username,
name=name,
ticket_id=ticket.id))
ticket_id=ticket.scoped_id))
def get_access(tracker, ticket):
# TODO: flesh out
@ -182,17 +185,26 @@ def get_access(tracker, ticket):
return ticket.user_perms or tracker.default_user_perms
return ticket.anonymous_perms or tracker.default_anonymous_perms
def get_ticket(tracker, ticket_id):
ticket = (Ticket.query
.filter(Ticket.scoped_id == ticket_id)
.filter(Ticket.tracker_id == tracker.id)
).first()
if not ticket:
return None, None
access = get_access(tracker, ticket)
if not TicketAccess.browse in access:
return None, None
return ticket, access
@tracker.route("/<owner>/<path:name>/<int:ticket_id>")
def ticket_GET(owner, name, ticket_id):
tracker = get_tracker(owner, name)
if not tracker:
abort(404)
ticket = Ticket.query.get(ticket_id)
ticket, access = get_ticket(tracker, ticket_id)
if not ticket:
abort(404)
access = get_access(tracker, ticket)
if not TicketAccess.browse in access:
abort(404)
return render_template("ticket.html",
tracker=tracker,
ticket=ticket,
@ -204,12 +216,9 @@ def ticket_comment_POST(owner, name, ticket_id):
tracker = get_tracker(owner, name)
if not tracker:
abort(404)
ticket = Ticket.query.get(ticket_id)
ticket, access = get_ticket(tracker, ticket_id)
if not ticket:
abort(404)
access = get_access(tracker, ticket)
if not TicketAccess.browse in access:
abort(404)
valid = Validation(request)
text = valid.optional("comment")
@ -264,9 +273,9 @@ def ticket_comment_POST(owner, name, ticket_id):
return redirect(url_for(".ticket_GET",
owner="~" + tracker.owner.username,
name=tracker.name,
ticket_id=ticket.id) + "#comment-" + str(comment.id))
ticket_id=ticket.scoped_id) + "#comment-" + str(comment.id))
else:
return redirect(url_for(".ticket_GET",
owner="~" + tracker.owner.username,
name=tracker.name,
ticket_id=ticket.id))
ticket_id=ticket.scoped_id))

View File

@ -4,7 +4,7 @@
<div class="row">
<div class="col-md-12">
<h2>
{{ format_tracker_name(tracker, full=True) }}/#{{ticket.id}}:
{{ format_tracker_name(tracker, full=True) }}/#{{ticket.scoped_id}}:
{{ticket.title}}
</h2>
</div>
@ -61,7 +61,7 @@
url_for(".ticket_comment_POST",
owner="~" + tracker.owner.username,
name=tracker.name,
ticket_id=ticket.id
ticket_id=ticket.scoped_id
)
}}">
<div class="form-group {{ valid.cls("comment") }}">

View File

@ -92,13 +92,13 @@
<td><a href="{{url_for(".ticket_GET",
owner="~" + tracker.owner.username,
name=tracker.name,
ticket_id=ticket.id)}}">#{{ticket.id}}</a></td>
ticket_id=ticket.scoped_id)}}">#{{ticket.scoped_id}}</a></td>
<td>{{ ticket.title }}</td>
<td>{{ ticket.updated | date }}</td>
<td><a href="{{url_for(".ticket_GET",
owner="~" + tracker.owner.username,
name=tracker.name,
ticket_id=ticket.id)}}">{{ ticket.submitter.username }}</a></td>
ticket_id=ticket.scoped_id)}}">{{ ticket.submitter.username }}</a></td>
</tr>
{% endfor %}
</tbody>

View File

@ -11,6 +11,11 @@ class Ticket(Base):
tracker_id = sa.Column(sa.Integer, sa.ForeignKey("tracker.id"), nullable=False)
tracker = sa.orm.relationship("Tracker", backref=sa.orm.backref("tickets"))
scoped_id = sa.Column(sa.Integer,
nullable=False,
index=True,
unique=sa.UniqueConstraint('scoped_id', 'tracker_id'))
dupe_of_id = sa.Column(sa.Integer, sa.ForeignKey("ticket.id"))
dupe_of = sa.orm.relationship("Ticket",
backref=sa.orm.backref("dupes"),

View File

@ -14,6 +14,7 @@ class Tracker(Base):
May include slashes to serve as categories (nesting is supported,
builds.sr.ht style)
"""
next_ticket_id = sa.Column(sa.Integer, nullable=False, default=1)
description = sa.Column(sa.Unicode(8192))
"""Markdown"""