[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnue] r9332 - in trunk/gnue-forms/src: GFObjects input/displayHandlers
From: |
johannes |
Subject: |
[gnue] r9332 - in trunk/gnue-forms/src: GFObjects input/displayHandlers |
Date: |
Fri, 26 Jan 2007 04:08:59 -0600 (CST) |
Author: johannes
Date: 2007-01-26 04:08:58 -0600 (Fri, 26 Jan 2007)
New Revision: 9332
Modified:
trunk/gnue-forms/src/GFObjects/GFField.py
trunk/gnue-forms/src/input/displayHandlers/Cursor.py
trunk/gnue-forms/src/input/displayHandlers/datehandler.py
Log:
Auto-completion and display string parsing for date fields
Modified: trunk/gnue-forms/src/GFObjects/GFField.py
===================================================================
--- trunk/gnue-forms/src/GFObjects/GFField.py 2007-01-26 08:01:55 UTC (rev
9331)
+++ trunk/gnue-forms/src/GFObjects/GFField.py 2007-01-26 10:08:58 UTC (rev
9332)
@@ -249,7 +249,7 @@
# Autocomplete a value for this field
# -------------------------------------------------------------------------
- def autocomplete(self, value):
+ def autocomplete(self, value, cursor):
"""
Return the first valid user value that starts with the provided part.
@@ -257,19 +257,22 @@
@param value: User entered string.
@type value: unicode
- @returns: Autocompleted string.
- @rtype: unicode
+ @param cursor: Position of the current insertion point
+ @type cursor: integer
+ @returns: tuple of autocompleted string and the new position of the
+ insertion point
+ @rtype: tuple of (unicode, integer)
"""
if not self.__is_lookup:
- return value
+ return (value, cursor)
for allowed in self.__lookup_list:
if allowed.startswith(value):
- return allowed
+ return (allowed, cursor)
# Nothing found, return original user input.
- return value
+ return (value, cursor)
# -------------------------------------------------------------------------
Modified: trunk/gnue-forms/src/input/displayHandlers/Cursor.py
===================================================================
--- trunk/gnue-forms/src/input/displayHandlers/Cursor.py 2007-01-26
08:01:55 UTC (rev 9331)
+++ trunk/gnue-forms/src/input/displayHandlers/Cursor.py 2007-01-26
10:08:58 UTC (rev 9332)
@@ -311,12 +311,12 @@
if new_text.startswith(self.display[:ende]) and len(new_text) > ende:
start_sel = len(new_text)
- new_text = self._autocomplete_(new_text)
- new_text = self.field.autocomplete(new_text)
+ (new_text, start_sel) = self._autocomplete_(new_text, start_sel)
if len(new_text) > start_sel:
new_selection = (start_sel, len(new_text))
else:
new_selection = (None, None)
+ new_cursor = start_sel
else:
new_selection = (None, None)
@@ -336,7 +336,7 @@
# Do autocompletion
# -------------------------------------------------------------------------
- def _autocomplete_(self, new_value):
+ def _autocomplete_(self, new_value, new_cursor):
"""
Descandants can override this method to introduce autocompletion. This
method get's called from __change_text() if the current cursor is at
@@ -344,7 +344,7 @@
@returns: the new (autocompleted) value to use
"""
- return new_value
+ return self.field.autocomplete(new_value, new_cursor)
# =========================================================================
# Cursor movement functions
Modified: trunk/gnue-forms/src/input/displayHandlers/datehandler.py
===================================================================
--- trunk/gnue-forms/src/input/displayHandlers/datehandler.py 2007-01-26
08:01:55 UTC (rev 9331)
+++ trunk/gnue-forms/src/input/displayHandlers/datehandler.py 2007-01-26
10:08:58 UTC (rev 9332)
@@ -1,5 +1,9 @@
-# This file is part of GNU Enterprise.
+# GNU Enterprise Forms - Display handler - Date related display handlers
#
+# Copyright 2001-2007 Free Software Foundation
+#
+# This file is part of GNU Enterprise
+#
# GNU Enterprise is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
@@ -15,18 +19,16 @@
# write to the Free Software Foundation, Inc., 59 Temple Place
# - Suite 330, Boston, MA 02111-1307, USA.
#
-# Copyright 2002-2007 Free Software Foundation
-#
-# FILE:
-# GFDisplayHandler.py
-#
-# $Id$
+# $Id: $
"""
-DisplayHandler classes for Forms input validation
+DisplayHandler classes for input validation of date, time and datetime values
"""
__revision__ = "$Id:$"
-import sys, time, datetime
+import sys
+import time
+import datetime
+import re
from gnue.common.apps import errors
from gnue.forms.input.displayHandlers.Cursor import BaseCursor
@@ -36,88 +38,293 @@
# =============================================================================
class InvalidDateLiteral (errors.UserError):
- def __init__ (self, value):
- msg = u_("'%(value)s' is not a valid date-literal") % {'value': value}
- errors.UserError.__init__ (self, msg)
+ def __init__ (self, value):
+ msg = u_("'%(value)s' is not a valid date-literal") % {'value': value}
+ errors.UserError.__init__ (self, msg)
-
# =============================================================================
-# Display handler for Date and Time values
+# Base class for date related handlers
# =============================================================================
-class Date(BaseCursor):
- pass
+class DateRelated(BaseCursor):
+ def get_date_order(self, format):
+
+ sample = datetime.date(1978, 3, 21)
+ text = sample.strftime(str(format))
+ order = []
+ inter = []
+ pat = re.compile('^(\d+)(\D*)')
+
+ match = pat.match(text)
+ while match:
+ part, rest = match.groups()
+ if part in ['1978', '78']:
+ if len(part) == 2:
+ order.append('year')
+ else:
+ order.append('Year')
+ elif part in ['03', '3']:
+ order.append('month')
+ elif part == '21':
+ order.append('day')
+ else:
+ order.append('?')
+
+ if rest:
+ inter.append(rest)
+
+ text = text[len(part + rest):]
+ if not text:
+ break
+
+ match = pat.match(text)
+
+ return (order, inter)
+
+
+
+
class Time(BaseCursor):
pass
-class DateTime(BaseCursor):
- """
- Class to handle the display and entry of date based fields.
- """
- def __init__(self, entry, eventHandler, subEventHandler, displayMask,
- inputMask):
- BaseCursor.__init__(self, entry, eventHandler, subEventHandler,
- displayMask, inputMask)
- self.__displayMask = displayMask
- self.__inputMask = inputMask
+class Date(DateRelated):
+ # -------------------------------------------------------------------------
+ # Constructor
+ # -------------------------------------------------------------------------
- def build_display(self, value, editing):
- if editing:
- if self.__inputMask:
- format = self.__inputMask
- else:
- format = "%m/%d/%Y"
- else:
- if self.__displayMask:
- format = self.__displayMask
- else:
- format = "%m/%d/%y"
+ def __init__(self, entry, eventHandler, subEventHandler, display_mask,
+ input_mask):
- if value in (None, ""):
- return ""
- try:
- return value.strftime (str (format))
- except AttributeError:
- return str(value)
+ DateRelated.__init__(self, entry, eventHandler, subEventHandler,
+ display_mask, input_mask)
+ self.__input_mask = input_mask or '%x'
+ self.__display_mask = display_mask or '%x'
- def parse_display(self, display):
- if not len(display):
- return None
+ self.__order, self.__inter = self.get_date_order(self.__input_mask)
- # TODO: Replace with format mask
- if self.__inputMask:
- try:
- tempVal = time.strptime (display, self.__inputMask)
- return datetime.datetime(*tempVal[0:6])
- except ValueError:
+
+ # -------------------------------------------------------------------------
+ # Create a display string for the current value
+ # -------------------------------------------------------------------------
+
+ def build_display(self, value, editing):
+ """
+ """
+
+ if editing:
+ mask = self.__input_mask
+ else:
+ mask = self.__display_mask
+
+ if value in (None, ""):
+ return ""
+
+ try:
+ return value.strftime(str(mask))
+
+ except AttributeError:
+ return str(value)
+
+
+ # -------------------------------------------------------------------------
+ # Try to figure out which date the user meant
+ # -------------------------------------------------------------------------
+
+ def parse_display(self, display):
+ """
+ """
+
+ try:
+ # First have a look wether the input follows the requested format
+ temp = time.strptime(display, self.__input_mask)
+ return datetime.date(*temp[0:3])
+
+ except ValueError:
+ pass
+
+ today = datetime.date.today()
+
+ # Ok, now let's do some guessing.
+
+ # If the input is a number of length 2 we treat it as day
+ if display.isdigit() and len(display) <= 2:
+ return today.replace(day=int(display))
+
+ # If the input is a 4-digit number or a string with two numbers
+ # separated by a non-digit string we treat it as "day and month"
+ # according to the order of the original input mask
+ match = re.match('^(\d+)\D+(\d+)\s*$', display)
+ if (display.isdigit() and len(display) == 4) or match is not None:
+ if match:
+ (val1, val2) = match.groups()
+ else:
+ val1, val2 = display[:2], display[2:]
+
+ if self.__order.index('day') < self.__order.index('month'):
+ day = int(val1)
+ month = int(val2)
+ else:
+ day = int(val2)
+ month = int(val1)
+
+ return today.replace(day=day, month=month)
+
+ # If the input is a 6-digit number or a triple of numeric values
+ # separated by non-digit characters it is likely a complete date
+ match = re.match('^(\d+)\D+(\d+)\D+(\d+).*$', display)
+ if (display.isdigit() and len(display) == 6) or match is not None:
+ if match:
+ values = match.groups()
+ else:
+ values = display[:2], display[2:4], display[4:]
+
+ kw = {}
+ for index, item in enumerate(values):
+ value = int(item)
+ if index == 2 and value < 100:
+ # FIXME: need a better algorithm here
+ if value > 50:
+ value += 1900
+ else:
+ value += 2000
+ kw[self.__order[index].lower()] = value
+
+ return datetime.date(**kw)
+
+ # If the input is a 8-digit number it should be a complete date. We
+ # derive the order of the elements from the order as given in the input
+ # mask.
+ if display.isdigit() and len(display) == 8:
+ for item in self.__order:
+ if item.lower() == 'year':
+ year = int(display[:4])
+ display = display[4:]
+ elif item == 'month':
+ month = int(display[:2])
+ display = display[2:]
+ elif item == 'day':
+ day = int(display[:2])
+ display = display[2:]
+
+ return datetime.date(day=day, month=month, year=year)
+
raise InvalidDateLiteral, display
-
- # TODO: Candidate for maketrans?
- # TODO: This logic does not work for timestamps
- # as it skips the hour,minute, second
- value = display.replace('.','/').replace('-','/')
-
- # Support for quick entry like '123102' for '12/31/02'
- if len(value) in (6, 8) and value.find('/') == -1:
- month = value[:2]
- day = value[2:4]
- year = value[4:]
- elif value.find ('/') == -1:
- raise InvalidDateLiteral, value
- else:
- month, day, year = value.split('/')
+ # -------------------------------------------------------------------------
+ # Autocomplete a user input
+ # -------------------------------------------------------------------------
- # TODO: Shouldn't the 50 be a config option
- year = int(year)
- if year < 100:
- if year > 50:
- year = year + 1900
- else:
- year = year + 2000
+ def _autocomplete_(self, new_text, new_cursor):
- return datetime.datetime(year, int(month), int(day))
+ # We do not autocomplete dates starting with the year
+ if self.__order[0].lower() == 'year':
+ return new_text, new_cursor
+
+ today = datetime.date.today().strftime(str(self.__input_mask))
+
+ # If the text is a number it must be the first component
+ if new_text.isdigit():
+ if self.__order[0] != 'month':
+ new_text = new_text + today[today.index(self.__inter[0]):]
+ return new_text, new_cursor
+
+ else:
+ parts = new_text.split(self.__inter[0], 1)
+ if len(parts) <> 2:
+ return new_text, new_cursor
+
+ lead = parts[0] + self.__inter[0]
+ rest = parts[1]
+ # If we have nothing after the first intersection symbol, we have
+ # to complete the rest
+ if not rest:
+ if self.__order[0] <> 'month':
+ new_text = parts[0] + today[today.index(self.__inter[0]):]
+ return new_text, new_cursor
+
+ parts = rest.split(self.__inter[1], 1)
+ # If we have the first and the second component, stick it together
+ if len(parts) == 1:
+ lead += parts[0]
+ new_cursor = len(lead)
+ new_text = lead + today[today.rindex(self.__inter[1]):]
+
+ return new_text, new_cursor
+
+ return new_text, new_cursor
+
+
+class DateTime(BaseCursor):
+ """
+ Class to handle the display and entry of date based fields.
+ """
+ def __init__(self, entry, eventHandler, subEventHandler, displayMask,
+ inputMask):
+ BaseCursor.__init__(self, entry, eventHandler, subEventHandler,
+ displayMask, inputMask)
+ self.__displayMask = displayMask
+ self.__inputMask = inputMask
+
+
+ def build_display(self, value, editing):
+ if editing:
+ if self.__inputMask:
+ format = self.__inputMask
+ else:
+ format = "%m/%d/%Y"
+ else:
+ if self.__displayMask:
+ format = self.__displayMask
+ else:
+ format = "%m/%d/%y"
+
+ if value in (None, ""):
+ return ""
+ try:
+ return value.strftime (str (format))
+ except AttributeError:
+ return str(value)
+
+
+ def parse_display(self, display):
+ if not len(display):
+ return None
+
+ # TODO: Replace with format mask
+ if self.__inputMask:
+ try:
+ tempVal = time.strptime (display, self.__inputMask)
+ return datetime.datetime(*tempVal[0:6])
+ except ValueError:
+ raise InvalidDateLiteral, display
+
+ # TODO: Candidate for maketrans?
+ # TODO: This logic does not work for timestamps
+ # as it skips the hour,minute, second
+ value = display.replace('.','/').replace('-','/')
+
+ # Support for quick entry like '123102' for '12/31/02'
+ if len(value) in (6, 8) and value.find('/') == -1:
+ month = value[:2]
+ day = value[2:4]
+ year = value[4:]
+
+ elif value.find ('/') == -1:
+ raise InvalidDateLiteral, value
+
+ else:
+ month, day, year = value.split('/')
+
+ # TODO: Shouldn't the 50 be a config option
+ year = int(year)
+ if year < 100:
+ if year > 50:
+ year = year + 1900
+ else:
+ year = year + 2000
+
+ return datetime.datetime(year, int(month), int(day))
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [gnue] r9332 - in trunk/gnue-forms/src: GFObjects input/displayHandlers,
johannes <=