Author: croberts
Date: 2012-08-15 19:50:50 +0000 (Wed, 15 Aug 2012)
New Revision: 5447
Modified:
trunk/cumin/python/cumin/objectselector.py
trunk/cumin/python/cumin/objectselector.strings
trunk/rosemary/python/rosemary/sqlfilter.py
trunk/wooly/python/wooly/forms.py
trunk/wooly/python/wooly/forms.strings
Log:
Ground work for completing BZ705358 (Allow search column(s) to be selectable).
Modified: trunk/cumin/python/cumin/objectselector.py
===================================================================
--- trunk/cumin/python/cumin/objectselector.py 2012-08-15 18:44:54 UTC (rev 5446)
+++ trunk/cumin/python/cumin/objectselector.py 2012-08-15 19:50:50 UTC (rev 5447)
@@ -4,14 +4,14 @@
from cumin.util import Identifiable, xml_escape
from rosemary.model import RosemaryClass, RosemaryAttribute, RosemaryReference
-from rosemary.sqlfilter import SqlLikeFilter
+from rosemary.sqlfilter import SqlFilter, SqlLikeFilter, SqlValueFilter,
SqlDateValueFilter
from wooly.util import StringCatalog, Writer, escape_entity
from wooly.datatable import DataTable, DataTableColumn
from wooly import Attribute, Widget, SessionAttribute, Parameter
-from wooly.forms import Form, StringInput, FoldingFieldSubmitForm
+from wooly.forms import Form, StringInput, FoldingFieldSubmitForm, OptionInputSet
from cumin.objecttask import ObjectTaskButton, ObjectTask
-from wooly.parameters import IntegerParameter, ListParameter
+from wooly.parameters import IntegerParameter, ListParameter, StringParameter
from wooly.table import CheckboxColumnCell, CheckboxColumnHeader,\
CheckboxColumnInput, LinkColumn
from wooly.widgets import WidgetSet, ItemSet
@@ -91,7 +91,7 @@
def add_like_filter(self, attribute, column, type=SqlLikeFilter.CONTAINS):
assert isinstance(attribute, Attribute), attribute
- assert isinstance(column, DataTableColumn), column
+# assert isinstance(column, DataTableColumn), column
self.like_specs.append((column, attribute, type))
@@ -260,6 +260,9 @@
self.switches = ObjectSelectorSwitches(app, "switches")
self.add_child(self.switches)
+ self.selectablefilters = ObjectSelectorSelectableFilters(app,
"selectablefilters")
+ self.add_child(self.selectablefilters)
+
self.filters = ObjectSelectorFilters(app, "filters")
self.add_child(self.filters)
@@ -303,7 +306,16 @@
self.table.add_like_filter(search.param, this)
self.filters.add_child(search)
+
+ def add_selectable_search_filter(self, inputSet):
+ search = StringInput(self.app, "search")
+ search.param.default = ""
+ search.title = "Search in column"
+ self.selectablefilters.add_child(search)
+ self.selectablefilters.add_child(inputSet)
+
+
def render_search_id(self, session):
if len(self.filters.children):
return self.filters.children[0].render_id(session)
@@ -351,7 +363,121 @@
self.checkbox_column = ObjectCheckboxColumn \
(app, "id", cls._id, self.ids)
self.add_column(self.checkbox_column)
+
+class SelectableSearchObjectTable(ObjectTable):
+ def __init__(self, app, name, cls):
+ super(SelectableSearchObjectTable, self).__init__(app, name, cls)
+
+ ## think about a do_process that will set up values from the URL maybe??
+ def get_data_values(self, session):
+ values = dict()
+ customfield = self.parent.select_input.get(session)
+ customvalue =
self.parent.selectablefilters.children_by_name['search'].get(session)
+ operator = self.parent.select_input.operator_param.get(session)
+
+ #since these are added at runtime, we need to clear out the old ones each time
around
+ self.adapter.query.filters = [filter for filter in self.adapter.query.filters if
not isinstance(filter, self.SelectableFieldFilter)]
+ if customfield is None or customvalue is None or customvalue == "":
+ return values
+ try:
+ sql_column = self.cls._properties_by_name[customfield].sql_column
+ except:
+ sql_column = self.cls._statistics_by_name[customfield].sql_column
+
+ pre = ""
+ post = ""
+
+ if sql_column.type.literal == "timestamp":
+ self.adapter.query.add_filter(self.SelectableFieldDateValueFilter(sql_column,
customvalue))
+ elif sql_column.type.literal == "int8":
+ if operator == ">=":
+ self.adapter.query.add_filter(self.SelectableFieldValueFilter(sql_column,
">="))
+ elif operator == ">":
+ self.adapter.query.add_filter(self.SelectableFieldValueFilter(sql_column,
">"))
+ elif operator == "<=":
+ self.adapter.query.add_filter(self.SelectableFieldValueFilter(sql_column,
"<="))
+ elif operator == "<":
+ self.adapter.query.add_filter(self.SelectableFieldValueFilter(sql_column,
"<"))
+ else:
+
self.adapter.query.add_filter(self.SelectableFieldValueFilter(sql_column))
+ else:
+ post = "%"
+ self.adapter.query.add_filter(self.SelectableFieldLikeFilter(sql_column))
+
+ if not "%" in customvalue:
+ pre = type == SqlLikeFilter.CONTAINS and "%%" or ""
+ else:
+ post = ""
+ values[customfield] = "%s%s%s" % (pre, customvalue, post)
+
+ return values
+
+ class SearchFieldOptions(OptionInputSet):
+ def __init__(self, app, param):
+ super(SelectableSearchObjectTable.SearchFieldOptions, self).__init__(app,
"select_input", param)
+
+ self.operator_param = StringParameter(app, "operator_param")
+ self.add_parameter(self.operator_param)
+
+ self.operator_selector = self.SearchOperatorOptions(app,
self.operator_param)
+ self.add_child(self.operator_selector)
+
+ def do_get_items(self, session):
+ return []
+
+ def render_item_value(self, session, item):
+ return item.name
+
+ def render_item_content(self, session, item):
+ return xml_escape(item.title)
+
+ def render_item_selected_attr(self, session, item):
+ if item.name == self.param.get(session):
+ return "selected=\"selected\""
+
+ def render_onchange(self, session):
+ search_box_name = self.parent.children_by_name['search'].param.path
+ return "change_input_text(this, '%s');" % search_box_name
+
+ def render_item_type(self, session, item):
+ return item.sql_column.type.literal
+
+ def render_select_box(self, session):
+ return self.parent.children_by_name['search'].path
+
+ class SearchOperatorOptions(OptionInputSet):
+ def __init__(self, app, param):
+
super(SelectableSearchObjectTable.SearchFieldOptions.SearchOperatorOptions,
self).__init__(app, "operator_input", param)
+
+ def do_get_items(self, session):
+ return ["=", ">", ">=",
"<", "<="]
+
+ def render_item_value(self, session, item):
+ return xml_escape(item)
+
+ def render_item_content(self, session, item):
+ return xml_escape(item)
+
+ def render_item_selected_attr(self, session, item):
+ if item == self.param.get(session):
+ return "selected=\"selected\""
+
+ def render_onchange(self, session):
+ return ""
+
+ class SelectableFieldFilter(object):
+ pass
+
+ class SelectableFieldLikeFilter(SqlLikeFilter, SelectableFieldFilter):
+ pass
+
+ class SelectableFieldValueFilter(SqlValueFilter, SelectableFieldFilter):
+ pass
+
+ class SelectableFieldDateValueFilter(SqlDateValueFilter, SelectableFieldFilter):
+ pass
+
class ObjectCheckboxColumn(ObjectTableColumn):
def __init__(self, app, name, attr, selection):
super(ObjectCheckboxColumn, self).__init__(app, name, attr)
@@ -431,6 +557,9 @@
class ObjectSelectorFilters(ObjectSelectorControl):
pass
+class ObjectSelectorSelectableFilters(ObjectSelectorControl):
+ pass
+
class ObjectSelectorButtons(ObjectSelectorControl):
pass
Modified: trunk/cumin/python/cumin/objectselector.strings
===================================================================
--- trunk/cumin/python/cumin/objectselector.strings 2012-08-15 18:44:54 UTC (rev 5446)
+++ trunk/cumin/python/cumin/objectselector.strings 2012-08-15 19:50:50 UTC (rev 5447)
@@ -24,6 +24,20 @@
font-size: 0.75em;
}
+div.ObjectSelectorSelectableFilters {
+ float: left;
+ padding-bottom: 0.25em;
+}
+
+div.ObjectSelectorSelectableFilters ul {
+ float: left;
+ margin: 0;
+}
+
+div.ObjectSelectorSelectableFilters input,select {
+ font-size: 0.75em;
+}
+
div.ObjectSelectorButtons {
background-color: #e7e7f7;
padding: 0.35em 0.75em;
@@ -50,6 +64,7 @@
<form id="{id}.form" method="post" action="?">
{filters}
+ {selectablefilters}
{buttons}
{table}
@@ -136,6 +151,40 @@
<input type="submit" value="Search" onclick="return
clearOffsetInput(this.form)" tabindex="100"/><input
type="submit" value="Clear" onclick="return
clearSearchInput('{id}')" tabindex="100"/>
</div><div style="margin-bottom: 0.5em; clear:both;"></div>
+[ObjectSelectorSelectableFilters.javascript]
+function clearSearchInput(id) {
+ var parent = $(id);
+ if (parent) {
+ var searchInput = parent.getElement("input");
+ if (searchInput) {
+ searchInput.value = "";
+ }
+ }
+ return true;
+}
+
+function clearOffsetInput(id) {
+ if (id) {
+ var regex = new RegExp("\.offset$");
+ var inputs = id.getElements("input");
+ if(inputs) {
+ for (i = 0; i < inputs.length; i++) {
+ var input_name = inputs[i].name;
+ var match = regex.exec(input_name);
+ if(match != null) {
+ inputs[i].value = 0;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+
+[ObjectSelectorSelectableFilters.html]
+<div id="{id}" class="{class}">
+ <ul>{select_input}{search}</ul><input type="submit"
value="Search" onclick="return clearOffsetInput(this.form)"
tabindex="100"/><input type="submit" value="Clear"
onclick="return clearSearchInput('{id}')"
tabindex="100"/></div><div style="margin-bottom: 0.5em;
clear:both;"></div>
+
[ExportButton.css]
div.ExportButton {
float:right;
@@ -249,3 +298,37 @@
a.TableColumnHeader span.down {
background: url(resource?name=sort-arrow-down.png) right 4px no-repeat;
}
+
+[SearchFieldOptions.javascript]
+function change_input_text(select_box, text) {
+ operator_toggle(select_box);
+ newText = "Search in " + select_box.options[select_box.selectedIndex].text +
" column";
+ select_box.getParent().getElement("label").textContent = newText;
+}
+
+function operator_toggle(select_box) {
+ field_type = select_box[select_box.selectedIndex].getAttribute("type");
+ if (field_type != "int8") {
+ select_box.getNext().setAttribute("style", "display:none");
+
select_box.getParent().getElement("label").setStyle("left","114px")
+ } else {
+ select_box.getNext().setAttribute("style", "display:inline");
+
select_box.getParent().getElement("label").setStyle("left","159px")
+ }
+}
+
+[SearchFieldOptions.html]
+ <script type="text/javascript">
+//<![CDATA[
+ window.addEvent('domready', function () {
+ select_box = $("{select_box}").getParent().getElement("select")
+ newText = "Search in " + select_box.options[select_box.selectedIndex].text +
" column";
+ $("{select_box}").getParent().getElement("label").textContent =
newText;
+ operator_toggle(select_box);
+ });
+//]]>
+ </script>
+<select id="{id}" name="{name}" tabindex="{tab_index}"
{disabled_attr} onchange="{onchange}">{items}</select>{operator_input}
+
+[SearchFieldOptions.item_html]
+<option value="{item_value}" type="{item_type}"
{item_selected_attr}>{item_content}</option>
\ No newline at end of file
Modified: trunk/rosemary/python/rosemary/sqlfilter.py
===================================================================
--- trunk/rosemary/python/rosemary/sqlfilter.py 2012-08-15 18:44:54 UTC (rev 5446)
+++ trunk/rosemary/python/rosemary/sqlfilter.py 2012-08-15 19:50:50 UTC (rev 5447)
@@ -38,3 +38,16 @@
CONTAINS = "C"
def __init__(self, this, operator="like"):
super(SqlLikeFilter, self).__init__(this, operator)
+
+class SqlDateValueFilter(SqlFilter):
+ def __init__(self, this, that, operator="="):
+ super(SqlDateValueFilter, self).__init__()
+
+ assert isinstance(operator, str)
+
+ self.this = getattr(this, "identifier", this)
+ self.that = getattr(that, "identifier", that)
+ self.operator = operator
+
+ def emit(self):
+ return "date_trunc('day', %s) %s date_trunc('day',
date('%s'))" % (self.this, self.operator, self.that)
\ No newline at end of file
Modified: trunk/wooly/python/wooly/forms.py
===================================================================
--- trunk/wooly/python/wooly/forms.py 2012-08-15 18:44:54 UTC (rev 5446)
+++ trunk/wooly/python/wooly/forms.py 2012-08-15 19:50:50 UTC (rev 5447)
@@ -352,7 +352,10 @@
def render_item_selected_attr(self, session, item):
return None
-
+
+ def render_onchange(self, session):
+ return None
+
class FormField(Widget):
def __init__(self, app, name):
super(FormField, self).__init__(app, name)
Modified: trunk/wooly/python/wooly/forms.strings
===================================================================
--- trunk/wooly/python/wooly/forms.strings 2012-08-15 18:44:54 UTC (rev 5446)
+++ trunk/wooly/python/wooly/forms.strings 2012-08-15 19:50:50 UTC (rev 5447)
@@ -70,7 +70,7 @@
<button class="{class}" type="{type}"
tabindex="{tab_index}" {disabled_attr} name="{name}"
value="{value}" onclick="return {onclick}('{name}',
'{value}')">{content}</button>
[ScalarInput.html]
-<input type="text" name="{name}" value="{value}"
tabindex="{tab_index}" {disabled_attr} size="{size}" {title}/>
+<input type="text" id="{id}" name="{name}"
value="{value}" tabindex="{tab_index}" {disabled_attr}
size="{size}" {title}/>
[MultilineStringInput.html]
<textarea name="{name}" tabindex="{tab_index}" {disabled_attr}
rows="{rows}" cols="{columns}">{value}</textarea>
@@ -147,7 +147,7 @@
</tr>
[OptionInputSet.html]
-<select name="{name}" tabindex="{tab_index}"
{disabled_attr}>{items}</select>
+<select id="{id}" name="{name}" tabindex="{tab_index}"
{disabled_attr} onchange="{onchange}">{items}</select>
[OptionInputSet.item_html]
<option value="{item_value}"
{item_selected_attr}>{item_content}</option>