changeset 2627:8f93193cfd9b

Added +tickets view having list of tickets
author Saurabh Kathpalia <saurabh.kathpalia95@gmail.com>
date Tue, 17 Jun 2014 13:31:40 +0530
parents 1663ebfcb4c4
children 1a3f0d0c362a
files MoinMoin/apps/frontend/views.py MoinMoin/items/ticket.py MoinMoin/static/js/tickets.js MoinMoin/templates/tickets.html MoinMoin/themes/basic/static/css/basic.css MoinMoin/themes/basic/static/custom-less/basic.less MoinMoin/themes/basic/static/img/search.png MoinMoin/themes/basic/templates/layout.html
diffstat 8 files changed, 244 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/apps/frontend/views.py	Sun Jun 08 15:42:40 2014 +0200
+++ b/MoinMoin/apps/frontend/views.py	Tue Jun 17 13:31:40 2014 +0530
@@ -56,7 +56,7 @@
 from MoinMoin import user, util
 from MoinMoin.constants.keys import *
 from MoinMoin.constants.namespaces import *
-from MoinMoin.constants.itemtypes import ITEMTYPE_DEFAULT
+from MoinMoin.constants.itemtypes import ITEMTYPE_DEFAULT, ITEMTYPE_TICKET
 from MoinMoin.constants.chartypes import CHARS_UPPER, CHARS_LOWER
 from MoinMoin.util import crypto
 from MoinMoin.util.interwiki import url_for_item, split_fqname, CompositeName
@@ -2199,6 +2199,37 @@
     return response
 
 
+@frontend.route('/+tickets', methods=['GET', 'POST'])
+def tickets():
+    """
+    Show a list of ticket items
+    """
+    if request.method == 'POST':
+        query = request.form['q']
+        status = request.form['status']
+    else:
+        query = None
+        status = u'open'
+    idx_name = ALL_REVS
+    qp = flaskg.storage.query_parser([TAGS, SUMMARY, CONTENT, ITEMID], idx_name=idx_name)
+    terms = [Term(ITEMTYPE, ITEMTYPE_TICKET)]
+    if query:
+        terms.append(qp.parse(query))
+    if status == u'open':
+        terms.append(Term(CLOSED, False))
+    elif status == u'closed':
+        terms.append(Term(CLOSED, True))
+    q = And(terms)
+
+    with flaskg.storage.indexer.ix[LATEST_REVS].searcher() as searcher:
+        results = searcher.search(q, limit=None)
+        return render_template('tickets.html',
+                               results=results,
+                               query=query,
+                               status=status,
+        )
+
+
 @frontend.errorhandler(404)
 def page_not_found(e):
     return render_template('404.html',
--- a/MoinMoin/items/ticket.py	Sun Jun 08 15:42:40 2014 +0200
+++ b/MoinMoin/items/ticket.py	Tue Jun 17 13:31:40 2014 +0530
@@ -20,7 +20,7 @@
 from MoinMoin.i18n import L_
 from MoinMoin.themes import render_template
 from MoinMoin.forms import (Form, OptionalText, OptionalMultilineText, SmallNatural, Tags,
-                            Reference, BackReference, SelectSubmit)
+                            Reference, BackReference, SelectSubmit, Text)
 from MoinMoin.storage.middleware.protecting import AccessDenied
 from MoinMoin.constants.keys import (ITEMTYPE, CONTENTTYPE, ITEMID, CURRENT,
                                      SUPERSEDED_BY, SUBSCRIPTIONS, DEPENDS_ON, NAME, SUMMARY)
@@ -34,7 +34,7 @@
 USER_QUERY = Term(CONTENTTYPE, CONTENTTYPE_USER)
 TICKET_QUERY = Term(ITEMTYPE, ITEMTYPE_TICKET)
 
-Rating = SmallNatural.using(optional=True).with_properties(lower=1, upper=5)
+Rating = SmallNatural.using(optional=False).with_properties(lower=1, upper=5)
 
 
 def get_itemid_short_summary(rev):
@@ -63,7 +63,7 @@
 
 
 class TicketMetaForm(Form):
-    summary = OptionalText.using(label=L_("Summary")).with_properties(placeholder=L_("One-line summary of the item"))
+    summary = Text.using(label=L_("Summary")).with_properties(placeholder=L_("One-line summary of the item"))
     effort = Rating.using(label=L_("Effort"))
     difficulty = Rating.using(label=L_("Difficulty"))
     severity = Rating.using(label=L_("Severity"))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/static/js/tickets.js	Tue Jun 17 13:31:40 2014 +0530
@@ -0,0 +1,40 @@
+$(document).ready(function(){
+
+    // adds filter option in /+tickets view
+    // User can click on on any td element in the table to filter according to the value in that
+    function filter(selected_column) {
+        return function () {
+            var table = document.getElementById('ticket-list');
+            var cols = table.rows[0].cells.length;
+            var columns = table.getElementsByTagName("td");
+            var rows = table.getElementsByTagName("tr");
+            var selected_row = parseInt(selected_column/cols) + 1;
+            selected_column = selected_column%cols;
+            var data_to_filter = table.rows[selected_row].cells[selected_column].innerHTML;
+            var len = columns.length;
+
+            for ( var i = len-1; i >= 0; i-- ) {
+                if( i%cols == selected_column ) {
+                    var row = parseInt(i/cols) + 1;
+                    var data = table.rows[row].cells[selected_column].innerHTML;
+                    if( data != data_to_filter ) {
+                        rows[row].remove();
+                        i = i - selected_column;
+                    }
+                }
+            }
+        };
+    }
+    
+    var table = document.getElementById('ticket-list');
+    var cols = table.rows[0].cells.length;
+    var columns = table.getElementsByTagName("td");
+    for (var  i = 0; i < columns.length; i++ ) {
+    // listener not required for Summary and Itemid columns
+        if ( i%cols != 0 && i%cols != 1 ) {
+            columns[i].onclick = filter(i);
+        }
+    }
+
+});
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/templates/tickets.html	Tue Jun 17 13:31:40 2014 +0530
@@ -0,0 +1,88 @@
+{% extends theme("layout.html") %}
+{% set search_form=None %}
+<head>
+    {% block head %}
+    <title>{{_("Tickets")}}</title>
+    {{ super() }}
+    {% endblock %}
+</head>
+
+{% block content %}
+<h1>{{_("Tickets")}}</h1>
+
+{% set status_values = ['all', 'open', 'closed']  %}
+{{_("Filter:")}}
+{% for status_value in status_values %}
+    <form action="{{ url_for('frontend.tickets') }}" method="post" class='moin-ticketsearch-form'>
+        <input type="hidden" name="q" value="{{ query if query }}" >
+        &nbsp;<input type="hidden" name="status" value="{{ status_value }}">
+        <input type="submit" value="{{ status_value.capitalize() }}" title="{{ _('Show %(status)s tickets', status=status_value) }}" class="{{ 'ticket-query-button active' if status == status_value  else 'ticket-query-button'}}">
+    </form>
+{% endfor %}
+
+<form action="{{ url_for('frontend.tickets') }}" method="post">
+    <input type="hidden" name="status" value="{{ status }}">
+    <input type="text" name="q" value="{{ query if query }}" id="moin-ticketsearch-query" class="form-control" placeholder="{{_('Find tickets')}}">
+</form>
+<br>
+
+<p>
+{% if results %}
+    {{ _("%(result_len)d Tickets found.", result_len=results|length) }}
+    <table class="table table-hover" id="ticket-list">
+    <thead>
+        <th id="ticket-itemid">{{_("ID")}}</th>
+        <th id="ticket-summary">{{_("Summary")}}</th>
+        <th id="ticket-status">{{_("Status")}}</th>
+        <th id="ticket-effort" title="{{ _('Effort') }}">E</th>
+        <th id="ticket-difficulty" title="{{ _('Difficulty') }}">D</th>
+        <th id="ticket-severity" title="{{ _('Severity') }}">S</th>
+        <th id="ticket-priority" title="{{ _('Priority') }}">P</th>
+        <th id="ticket-tags">{{_("Tags")}}</th>
+    </thead>
+    <tbody>
+    {% for result in results %}
+        <tr>
+            <td>
+                <a href="{{  url_for_item(result['itemid'], field='itemid', namespace=result['namespace'])}}" title="{{ _('ITEMID: %(itemid)s', itemid=result['itemid'])}}">
+                    {{result['itemid'] | shorten_id}}
+                </a>
+            <td>
+                <a href="{{  url_for_item(result['itemid'], field='itemid', namespace=result['namespace'])}}" title="{{ _('ITEMID: %(itemid)s', itemid=result['itemid'])}}">
+                    {{ result['summary'][:50] }}
+                </a>
+            </td>
+            <td title="{{ _('Filter by status: %(status)s', status='Closed' if result['closed'] else 'Open') }}">
+                {{ _("Closed") if result['closed'] else _("Open") }}
+            </td>
+            <td title="{{ _('Filter by effort: %(effort)d', effort=result['effort']) }}">
+                {{ result['effort'] }}
+            </td>
+            <td title="{{ _('Filter by difficulty: %(difficulty)d', difficulty=result['difficulty']) }}">
+                {{ result['difficulty'] }}
+            </td>
+            <td title="{{ _('Filter by severity: %(severity)d', severity=result['severity']) }}">
+                {{ result['severity'] }}
+            </td>
+            <td title="{{ _('Filter by priority: %(priority)d', priority=result['priority']) }}">
+                {{ result['priority'] }}
+            </td>
+            <td>
+                {% for tag in result['tags'] %}
+                    {{ tag }}
+                {% endfor %}
+            </td>
+        </tr>
+    {% endfor %}
+    </tbody>
+    </table>
+{% else %}
+    {{ _("No Ticket found.") }}
+{% endif %}
+</p>
+{% endblock %}
+{% block body_scripts %}
+{{ super() }}
+    <script src="{{ url_for('static', filename='js/tickets.js') }}"></script>
+{% endblock %}
+
--- a/MoinMoin/themes/basic/static/css/basic.css	Sun Jun 08 15:42:40 2014 +0200
+++ b/MoinMoin/themes/basic/static/css/basic.css	Tue Jun 17 13:31:40 2014 +0530
@@ -6350,3 +6350,37 @@
     width: auto;
   }
 }
+#moin-ticketsearch-query {
+  background-image: url('../img/search.png');
+  background-repeat: no-repeat;
+  background-position: 5px;
+  padding-left: 24px;
+  width: 200px;
+  right: 40px;
+  position: absolute;
+}
+#ticket-list thead th:hover {
+  background: #e8e8e8;
+}
+#ticket-list tr td {
+  cursor: pointer;
+}
+#ticket-list tr td:first-child a {
+  font-family: monospace;
+}
+.ticket-query-button {
+  background: #ffffff;
+  padding: 1px 7px;
+  text-shadow: 0 1px 0 #fff;
+  border: 1px solid transparent;
+}
+.ticket-query-button.active, .ticket-query-button:hover {
+  background: -webkit-linear-gradient(top,#ccc 0,#ddd 13%);
+  color: #333;
+  box-sizing: border-box;
+  border: 1px solid transparent;
+  border-radius: 3px;
+}
+.moin-ticketsearch-form {
+ display: inline;
+}
--- a/MoinMoin/themes/basic/static/custom-less/basic.less	Sun Jun 08 15:42:40 2014 +0200
+++ b/MoinMoin/themes/basic/static/custom-less/basic.less	Tue Jun 17 13:31:40 2014 +0530
@@ -324,3 +324,37 @@
     width: auto;
   }
 }
+#moin-ticketsearch-query {
+  background-image: url('../img/search.png');
+  background-repeat: no-repeat;
+  background-position: 5px;
+  padding-left: 24px;
+  width: 200px;
+  right: 40px;
+  position: absolute;
+}
+#ticket-list thead th:hover {
+  background: #e8e8e8;
+}
+#ticket-list tr td {
+  cursor: pointer;
+}
+#ticket-list tr td:first-child a {
+  font-family: monospace;
+}
+.ticket-query-button {
+  background: #ffffff;
+  padding: 1px 7px;
+  text-shadow: 0 1px 0 #fff;
+  border: 1px solid transparent;
+}
+.ticket-query-button.active, .ticket-query-button:hover {
+  background: -webkit-linear-gradient(top,#ccc 0,#ddd 13%);
+  color: #333;
+  box-sizing: border-box;
+  border: 1px solid transparent;
+  border-radius: 3px;
+}
+.moin-ticketsearch-form {
+ display: inline;
+}
Binary file MoinMoin/themes/basic/static/img/search.png has changed
--- a/MoinMoin/themes/basic/templates/layout.html	Sun Jun 08 15:42:40 2014 +0200
+++ b/MoinMoin/themes/basic/templates/layout.html	Tue Jun 17 13:31:40 2014 +0530
@@ -116,18 +116,20 @@
                                         </ul>
                                     </div>
                                 </div>
-                                <div class="col-lg-3">
-                                    <form class="moin-navbar-form" action="{{ url_for('frontend.search') }}" method="get" role="search">
-                                        <div class="input-group">
-                                            <input name='q' type="text" class="form-control" placeholder="Search ...">
-                                            <div class="input-group-btn">
-                                                <button class="btn btn-primary" type="submit">
-                                                    <i class="icon-search"></i>
-                                                </button>
+                                {% if search_form %}
+                                    <div class="col-lg-3">
+                                        <form class="moin-navbar-form" action="{{ url_for('frontend.search') }}" method="get" role="search">
+                                            <div class="input-group">
+                                                <input name='q' type="text" class="form-control" placeholder="Search ...">
+                                                <div class="input-group-btn">
+                                                    <button class="btn btn-primary" type="submit">
+                                                        <i class="icon-search"></i>
+                                                    </button>
+                                                </div>
                                             </div>
-                                        </div>
-                                    </form>
-                                </div>
+                                        </form>
+                                    </div>
+                                {% endif %}
                             </div>
                         </nav> <!-- navbar-inverse-->
                     </div> <!-- column-12 -->