Skip to content

Commit

Permalink
WIP: Kiwi TCMS bug tracker. Fixes #699
Browse files Browse the repository at this point in the history
  • Loading branch information
atodorov committed Sep 10, 2019
1 parent 309e982 commit 648fc67
Show file tree
Hide file tree
Showing 12 changed files with 540 additions and 0 deletions.
Empty file added tcms/bugs/__init__.py
Empty file.
62 changes: 62 additions & 0 deletions tcms/bugs/forms.py
@@ -0,0 +1,62 @@
# Copyright (c) 2019 Alexander Todorov <atodorov@MrSenko.com>

# Licensed under the GPL 2.0: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html

from django import forms

from django.utils.translation import ugettext_lazy as _

from tcms.core.widgets import SimpleMDE
from tcms.core.forms.fields import UserField
from tcms.management.models import Product, Version, Build


class NewBugForm(forms.Form):
summary = forms.CharField()
assignee = UserField(required=False)

product = forms.ModelChoiceField(
queryset=Product.objects.all(),
empty_label=None,
)

version = forms.ModelChoiceField(
queryset=Version.objects.none(),
empty_label=None,
)

build = forms.ModelChoiceField(
queryset=Build.objects.none(),
empty_label=None,
)

text = forms.CharField(
widget=SimpleMDE(),
required=False,
initial=_("""Description of problem:
How often reproducible:
Steps to Reproduce:
1.
2.
3.
Actual results:
Expected results:
Additional info:
"""))

def populate(self, product_id=None):
if product_id:
self.fields['version'].queryset = Version.objects.filter(product_id=product_id)
self.fields['build'].queryset = Build.objects.filter(product_id=product_id)
else:
self.fields['version'].queryset = Version.objects.all()
self.fields['build'].queryset = Build.objects.all()
45 changes: 45 additions & 0 deletions tcms/bugs/migrations/0001_initial.py
@@ -0,0 +1,45 @@
# Generated by Django 2.2.5 on 2019-09-03 20:30

from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('management', '0005_order_by_name'),
('testruns', '0006_rename_test_case_run_to_test_execution'),
]

operations = [
migrations.CreateModel(
name='Bug',
fields=[
('id', models.AutoField(auto_created=True,
primary_key=True,
serialize=False, verbose_name='ID')),
('summary', models.CharField(db_index=True, max_length=255)),
('created_at', models.DateTimeField(auto_now_add=True,
db_index=True)),
('status', models.BooleanField(db_index=True, default=True)),
('assignee', models.ForeignKey(on_delete=models.deletion.CASCADE,
related_name='assigned_bugs',
to=settings.AUTH_USER_MODEL)),
('build', models.ForeignKey(on_delete=models.deletion.CASCADE,
to='management.Build')),
('executions', models.ManyToManyField(related_name='bugs',
to='testruns.TestExecution')),
('product', models.ForeignKey(on_delete=models.deletion.CASCADE,
to='management.Product')),
('reporter', models.ForeignKey(on_delete=models.deletion.CASCADE,
related_name='repoted_bugs',
to=settings.AUTH_USER_MODEL)),
('tags', models.ManyToManyField(related_name='bugs', to='management.Tag')),
('version', models.ForeignKey(on_delete=models.deletion.CASCADE,
to='management.Version')),
],
),
]
Empty file.
31 changes: 31 additions & 0 deletions tcms/bugs/models.py
@@ -0,0 +1,31 @@
# Copyright (c) 2019 Alexander Todorov <atodorov@MrSenko.com>

# Licensed under the GPL 2.0: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html

from django.conf import settings
from django.db import models


class Bug(models.Model):
summary = models.CharField(max_length=255, db_index=True)
created_at = models.DateTimeField(auto_now_add=True,
db_index=True)
# True means bug is still open
status = models.BooleanField(default=True, db_index=True)

reporter = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='repoted_bugs')
assignee = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='assigned_bugs')

product = models.ForeignKey('management.Product', on_delete=models.CASCADE)
version = models.ForeignKey('management.Version', on_delete=models.CASCADE)
build = models.ForeignKey('management.Build', on_delete=models.CASCADE)

executions = models.ManyToManyField('testruns.TestExecution', related_name='bugs')
tags = models.ManyToManyField('management.Tag', related_name='bugs')

def __str__(self):
return "BUG-%d: %s " % (self.pk, self.summary)
36 changes: 36 additions & 0 deletions tcms/bugs/static/bugs/js/mutable.js
@@ -0,0 +1,36 @@
/*
Used in mutable.html and clone.html
*/
$(document).ready((function () {
if ($('#id_version').find('option').length === 0) {
populateProductVersion();
}

$('#add_id_product').click(function () {
return showRelatedObjectPopup(this);
});

$('#add_id_version').click(function () {
return showRelatedObjectPopup(this);
});

document.getElementById('id_product').onchange = function () {
$('#id_product').selectpicker('refresh');
populateProductVersion();
};

document.getElementById('id_version').onchange = function () {
$('#id_version').selectpicker('refresh');
}
}));

// TODO: this entire file is mostly duplicate with testplans/js/mutable.js
function populateProductVersion() {
let href = $('#add_id_version')[0].href;
$('#add_id_version')[0].href = href.slice(0, href.indexOf('&product'));
$('#add_id_version')[0].href += `&product=${$('#id_product').val()}`;

$('#id_version').find('option').remove();

update_version_select_from_product();
}
166 changes: 166 additions & 0 deletions tcms/bugs/templates/bugs/get.html
@@ -0,0 +1,166 @@
{% extends "base.html" %}
{% load i18n %}
{% load static %}
{% load comments %}
{% load extra_filters %}
{% load attachments_tags %}

{% block title %}BUG-{{ object.pk }}: {{ object.summary }}{% endblock %}
{% block body_class %}cards-pf{% endblock %}

{% block contents %}
<div class="container-cards-pf">
<!-- Important: if you need to nest additional .row within a .row.row-cards-pf, do *not* use .row-cards-pf on the nested .row -->
<h1 class="col-md-12" style="margin-top: 0">
<span id="object_pk"
data-pk="{{ object.pk }}"
data-perm-remove-tag="{{ perms.bugs.delete_bugtag }}"
>BUG-{{ object.pk }}: {{ object.summary }}</span>

{% if object.status %}
<span class="fa fa-envelope-open-o" title="{% trans 'Open' %}"></span>
{% else %}
<span class="fa fa-times" title="{% trans 'Closed' %}"></span>
{% endif %}
</h1>

<div class="row row-cards-pf">
<div class="col-xs-12 col-sm-12 col-md-9">
<div class="card-pf card-pf-accented">
<div class="card-pf-body">
<div class="markdown-text">
{{ object.text|markdown2html }}
</div>
</div>
</div>
</div>

<div class="col-xs-12 col-sm-12 col-md-3">

<div class="card-pf card-pf-accented card-pf-aggregate-status">
<h2 class="card-pf-title" style="text-align: left">
<span class="fa fa-calendar"></span>{{ object.created_at }}
</h2>

<h2 class="card-pf-title" style="text-align: left">
<span class="fa pficon-user"></span>{% trans 'Reporter' %}:
<a href="{% url "tcms-profile" object.reporter.username %}">{{ object.reporter.username }}</a>
</h2>

<h2 class="card-pf-title" style="text-align: left">
<span class="fa pficon-user"></span>{% trans 'Assignee' %}:
<a href="{% url "tcms-profile" object.assignee.username %}">{{ object.assignee.username }}</a>
</h2>

<h2 class="card-pf-title" style="text-align: left">
<span id="product_pk"
data-pk="{{ object.product.pk }}"
class="fa fa-shopping-cart"></span>{% trans 'Product' %}:
{{ object.product }}
</h2>

<h2 class="card-pf-title" style="text-align: left">
<span class="fa fa-refresh"></span>{% trans 'Version' %}:
{{ object.version }}
</h2>

<h2 class="card-pf-title" style="text-align: left">
<span class="fa fa-wrench"></span>{% trans 'Build' %}:
{{ object.build }}
</h2>

<div class="card-pf-body"></div>
</div>
</div>
</div>

<div class="row row-cards-pf">
<div class="col-xs-12 col-sm-6 col-md-3">
<div class="card-pf card-pf-accented">
<h2 class="card-pf-title">
<span class="fa fa-tags"></span>
{% trans 'Tags' %}
</h2>

<div class="card-pf-body">
<table class="table" id="tags">
<thead>
<tr>
<th>{% trans 'Name' %}</th>
<th></th>
</tr>
</thead>

{% if perms.bugs.add_bugtag %}
<tfoot>
<tr>
<th>
<div class="input-group input-group-sm mb-3">
<input type="text" class="form-control typeahead" id="id_tags">
</div>
</th>
<th>
<a href="#tags" id="add-tag" title="{% trans 'Add' %}">
<span class="fa fa-plus"></span>
</a>
</th>
</tr>
</tfoot>
{% endif %}
</table>
</div>
</div>
</div>

<div class="col-xs-12 col-sm-6 col-md-6">
<div class="card-pf card-pf-accented">
<h2 class="card-pf-title">
<span class="fa fa-paperclip"></span>
{% trans 'Attachments' %}
</h2>

<div class="card-pf-body">
<table class="table" id="attachments">
<thead>
<tr>
<th>{% trans 'File' %}</th>
<th>{% trans 'Owner' %}</th>
<th>{% trans 'Date' %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% get_attachments_for object as attachments_list %}
{% for attachment in attachments_list %}
<tr>
<td><a href="{{ attachment.attachment_file.url }}">{{ attachment.filename }}</a></td>
<td><a href="{% url "tcms-profile" attachment.creator %}">{{ attachment.creator }}</a></td>
<td>{{ attachment.created }}</td>
<td>{% attachment_delete_link attachment %}</td>
</tr>
{% empty %}
<tr><td colspan="4">{% trans 'No records found' %}</td></tr>
{% endfor %}
</tbody>
{% if perms.attachments.add_attachment %}
<tfoot>
<tr>
<th colspan="4">
{% attachment_form object %}
</th>
</tr>
</tfoot>
{% endif %}
</table>
</div>
</div>
</div>

</div><!-- /row -->
</div>

<script src="{% static 'typeahead.js/dist/typeahead.jquery.min.js' %}"></script>

<script src="{% static 'js/utils.js' %}"></script>
<script src="{% static 'js/jsonrpc.js' %}"></script>
{% endblock %}

0 comments on commit 648fc67

Please sign in to comment.