Commits

Anonymous committed b4bb58d

VCS data sources tested with both `hg` and `svn` repos. Minor bugs fixed.

- Unnecessary imports removed.
- Bug fixed: The oldest revision in repository was not previously
enumerated.
- Bug fixed: Now equal revision IDs maybe supplied in to select a unique
revision.
- Exceptions renamed (i.e. `Error` suffix).
- Added MIME type for MoinMoin encoder.
- Revision identifiers are strings (i.e. not numbers).
- Method `normalize_rev` was added in `VersionControlRPC` handler.
- Using `set` instead of `dict` to ensure unique items in result sets.
- Added macro aliases for Google's Visualization gadgets.

  • Participants
  • Parent commits effee9c

Comments (0)

Files changed (21)

File trac-dev/gviz/CHANGES

     [[MotionChart(_table_query_url=[gviz:google:sheet:1234:B3-G17], 
     _table_query_refresh_interval=5)]]
 
-- Aliases for Google's visualisation gadgets are offered by default.
+- Aliases for Google's visualization gadgets are offered by default.
 
 - TracLinks expressions are supported in config options used to 
   define iGoogleGadget macro aliases.
 - Important ! Architectural change. From now on Request objects 
   can be supplied in to IGVizDataProvider.get_data_schema method. 
   This change allows to implement more dynamic data providers 
-  since schemas can vary according to the run-time values provided 
+  since schemas can vary depending upon the run-time values provided 
   for some parameters.
 
 - New ! GVizReportProvider now offers the data included in custom Trac 
   provider, and also the first example where schema can change 
   depending upon the report specification.
 
+- New ! VersionControlRPC now provides an interface to Trac's Version 
+  Control API via XML-RPC protocol. Therefore information about files 
+  in the project's repository and also about changesets is available 
+  using standard protocols. It is posible to list files and folders 
+  (`ls`) recursively with depth limit and filters written like 
+  UNIX filename patterns. File attributes (e.g. size, content type, 
+  ...), data about revisions (e.g. message, author, ...) in a given 
+  (time) interval, file and folder history, and full description of 
+  changes performed at a given revision, are some of the features 
+  supported so far. Looking forward for further useful data to 
+  be published ! This feature was "tested" using Mercurial SCM (`hg`) 
+  and Subversion (`svn`), but the code *SHOULD* work as long as 
+  connectors fully compatible with Trac's Version Control API be used. 
+  However the different approaches taken by different VCS may lead to 
+  wrong behaviors. So if you find any bug, please let me know.
+
+- New ! GVizSourceFiles now offers information about individual 
+  files and those located in a folder. This data source supports filters 
+  (i.e. UNIX filename patterns), and recursive lookup with optional 
+  depth value. It is also possible to get the data for an specific 
+  revision. The results are mostly file attributes (e.g. size, 
+  extension, content type ...), but the actual values depend upon 
+  the capabilities supported by the specific repository connector in 
+  use.
+
+- New ! GVizSourceFileHistory now makes available the modifications 
+  performed on  a group of files during a given time interval. 
+  Boundary values may be either datetime values or revision 
+  identifiers (supported by the specific repository connector). 
+  Clients'll get back detailed information about the changes performed 
+  on the given files in the different revisions together with the 
+  file's attributes at that specific time.
+  This data source supports filters (i.e. UNIX filename 
+  patterns) as well as recursive lookup with optional depth value. 
+  Multiple datetime formats are supported as usual.
+
+- New ! GVizChangesetDetails now provides a detailed report 
+  containing all the individual changes comitted at 
+  once onto the repository and recorded in separate changesets. It 
+  is possible to specify multiple changeset identifiers (supported by 
+  the specific repository connector) (and | or) time intervals.
+  Boundary values may be either datetime values or changeset
+  identifiers.
+
+- New ! GVizChangesets now provides meta-data describing the 
+  different changesets committed to the repository related to a 
+  Trac environment (i.e. revision ID, messages, author, and date). It 
+  is possible to limit the results by specifying a time interval.
+  Boundary values may be either datetime values or changeset
+  identifiers.
+
+- All the data providers interacting with the repository have been 
+  "tested" using the connector for Mercurial SCM (i.e. `hg`). In this 
+  case there are a few open issues that seem to be related with the 
+  lack of support in TracMercurial for `copy` events (but it's not a 
+  definitive conclusion). The current implementation *SHOULD* work 
+  with other connectors compatible with Trac's Version Control API, 
+  but THERE IS NO WARRANTY OF ANY KIND if you use another VCS. 
+  However if your connector is not causing any trouble then you the 
+  use of the aforementionned data sources *SHOULD NOT* cause any 
+  damage to both your environment and your repository. Further tests 
+  will be performed in the future but there's no clear schedule. 
+  You could be the first one to test them. Suggestions, patches and 
+  further feedback is welcome in order to overcome the actual 
+  limitations. 
+
 - Added api.IHashLibrary interface so that plugins and extensions can 
   contribute with other secure hash, message digest and checksum 
   algorithms in order to compute `sig` field in GViz API responses.
   a third (i.e. `checked`) element. It also handles unicode 
   characters appropriately.
 
-- Few optimizations ... bah!
+- Few optimizations and refactorings (e.g. unnecessary imports have 
+  been removed) ... bah!
 
 What's new in version 1.3.2
 ---------------------------
 What's new in version 1.1.0
 ---------------------------
 
-- Support has been added to implement Google Visualisation API data 
+- Support has been added to implement Google Visualization API data 
   sources by reusing existing Trac XML RPC components.
 
 - Some (but not all) data sources for Trac ticket system are provided.
 ---------------------------
 
 - An architecture is available so as to provide a project's data in
-  the format specified in Google Visualisation API protocol 
+  the format specified in Google Visualization API protocol 
   specification (version 0.5) api.TracGVizSystem.
 
 - Multiple protocol handlers (e.g. for different versions, and 
   protocol evolution) are allowed by implementing the interface
   api.IGVizProtocolHandler. There is native support for version 0.5 of
-  Google Visualisation API protocol through GViz_0_5.
+  Google Visualization API protocol through GViz_0_5.
 
 - It is possible to register new data sources by implementing the 
   interface api.IGVizDataProvider
   (class stdfmt.GVizJsonEncoder), HTML (class stdfmt.GVizHtmlEncoder),
   CSV (class stdfmt.GVizCSVEncoder).
 
-- The exception conditions mentioned in Google Visualisation API
+- The exception conditions mentioned in Google Visualization API
   protocol specification (version 0.5) have been identified.

File trac-dev/gviz/README

 
-= Trac integration with Google Visualisation API =
+= Trac integration with Google Visualization API =
 
-This plugin has been developped in order to expose the data managed by [http://trac.edgewall.org/ Trac] to widgets implemented using [http://code.google.com/apis/visualization Google Visualisation API]. The following data is provided so far in the form of [http://code.google.com/apis/visualization data tables]:
+This plugin has been developped in order to expose the data managed by [http://trac.edgewall.org/ Trac] to widgets implemented using [http://code.google.com/apis/visualization Google Visualization API]. The following data is provided so far in the form of [http://code.google.com/apis/visualization data tables]:
 
   - Project components.
   - Project milestones.
   
   - The [http://code.google.com/apis/visualization/documentation/dev/gviz_api_lib.html Data Source Python Library]
     must be installed so that the outputs be consistent with
-    [http://code.google.com/apis/visualization/ Google Visualisation API] [http://code.google.com/apis/visualization/documentation/dev/implementing_data_source.html data source protocol specification], and widgets can actually render the data managed by [http://trac.edgewall.org/ Trac] environments.
+    [http://code.google.com/apis/visualization/ Google Visualization API] [http://code.google.com/apis/visualization/documentation/dev/implementing_data_source.html data source protocol specification], and widgets can actually render the data managed by [http://trac.edgewall.org/ Trac] environments.
 
 == Installation ==
 
     - '''Volunteer''' a little, [mailto://flioops.project@gmail.com?subject=TracGVizPlugin%20Patch submit patches], it will be great if many others contribute as well ... don't hesitate [mailto://flioops.project@gmail.com?subject=TracGVizPlugin%20add_member be part of the team].
     - Donate to this project (link available hopefully soon), so as to be able to leave everything else behind and fulfill your particular request. '''Time''' is more than '''gold'''.
   - We try to do everything so as to not fail if unknown properties are sent as part of the request. However, if you discover any bug, '''please''' [query:status=new|assigned|reopened|closed&component=plugin_trac_gviz find out related tickets]. If none is found then [/newticket?component=plugin_trac_gviz create a new ticket]. 
-  - Conversely, built-in protocol handlers parse only the properties that they expect. If new versions introduce new properties, then the system uses the handler for the latest available version, and tries to send a response back to the client. [code.google.com/apis/visualization/documentation/querylanguage.html Google Visualisation API query language] [ticket:xxx is not supported yet], but hopefully soon.
+  - Conversely, built-in protocol handlers parse only the properties that they expect. If new versions introduce new properties, then the system uses the handler for the latest available version, and tries to send a response back to the client. [code.google.com/apis/visualization/documentation/querylanguage.html Google Visualization API query language] [ticket:xxx is not supported yet], but hopefully soon.
   - Some data sources '''may''' accept custom parameters for custom visualizations. However, they '''always''' return responses in the standard format.
   - Since third-party sites might be willing to use the data managed by your [http://trac.edgewall.org/ Trac] instance, it is very important to [ticket:xxx document the data source requirements] carefully. Due to the infinite possibilities offered by the plugin and [http://trac.edgewall.org/ Trac] architectures, and also since plugin developpers might want to implement their own data sources, the only feasible way to manage all this complexity is to automate documentation tasks. This feature is not ready, but will include (... in a near future ...) the following: 
     - Any custom parameters that you accept,
-    - Whether or not [code.google.com/apis/visualization/documentation/querylanguage.html Google Visualisation API query language] is supported, and ...
+    - Whether or not [code.google.com/apis/visualization/documentation/querylanguage.html Google Visualization API query language] is supported, and ...
     - What kind of data is returned, and the structure of that data (what the rows and columns represent, and any labeling).
   - Integration with the [TracPermissions permissions system] has not being finished ... [ticket:xxx yet]. Therefore no standard security precautions have been taken, and the site may accept requests from unknown clients. Take this into consideration if you store any sensitive data.
   - All request and response strings '''should''' be UTF-8 encoded. Else, [/newticket?component=plugin_trac_gvi let us know].

File trac-dev/gviz/TODO

 Outstanding tasks
 -----------------
 
-+ Add a page to view the images in a gadget border.
-
-+ Add a page to list available borders.
-
-+ Provide default border images for gadgets.
-
-+ Remove unnecessary imports.
-
-+ Add an RPC handler to provide information about source code in VCS.
-
-+ Add GViz provider to offer the data about all files located 
-  in a given folder managed by VCS (PARAMS: path, recursive, 
-  revision, depth, filter COLS: fnm, sz, lastRev, modified, 
-  ext, mime-type, log).
-
-+ Add GViz provider to offer historical data about files in 
-  locations managed by VCS (PARAMS: path, revStart, revEnd, 
-  dateStart, dateEnd, COLS: fnm, sz, rev, date, ext, mime-type, log).
-
-+ Add GViz provider to offer changes in a changeset.
-
-+ Test GViz providers for VCS (`svn` + `hg` + `git` ? ).
-
-+ Include all the names of Google's Visualisation Gadgets in 
++ Include all the names of Google's Visualization Gadgets in 
   GadgetAliases.GOOGLE_GADGETS mapping.
 
-+ Fix MIME types returned by MoinMoin and reStructuredText encoders.
++ Fix MIME types returned by MoinMoin (text/x.moin.wiki) and reStructuredText 
+  (text/restructured, test/x-rst) encoders.
 
-- Add `limit` parameter for methods in VersionSourceRPC.
+- Test GViz providers for VCS (`svn` + `git` ? + `mtn`).
+
+- Find the root cause for missing changes in revisions `1`, `3` and 
+  `8` (e.g. lack of support in TracMercurial for `copy` events?)
+
+- Add `limit` parameter for history methods in VersionControlRPC.
 
 - Implement data source provider for Ticket->Query
 
 - Ensure that columns are always arranged in the same order (i.e. use 
   ordered dicts)
 
+- Optimize the calls to TracRPC's Method objects by preventing 
+  them from building lists out of generators.
+
 - GViz API QL: Allow to select the columns to be returned, their 
   order, and labels.
 
 
 - Execute reports defined using URLs to saved custom queries.
 
+- Provide default border images for gadgets.
+
+- Add a page to view the images in a gadget border (i.e. preview).
+
+- Add a page to list available borders.
+

File trac-dev/gviz/__init__.py

 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-r"""Trac Data Sources for Google Visualisation API. Embed iGoogle gadgets using WikiFormatting.
+r"""Trac Data Sources for Google Visualization API. Embed iGoogle gadgets using WikiFormatting.
 
 This package (plugin) provides components so that Trac be able to
 use widgets and related technologies provided by Google.
 
 - It allows to feed data managed by Trac to widgets based on Google 
-  Visualisation API. 
+  Visualization API. 
 - It allows embedding iGoogle Gadgets in wiki pages using WikiFormatting.
 
 Copyright 2009-2011 Olemis Lang <olemis at gmail.com>
 Licensed under the Apache License, Version 2.0 
 """
 
-from trac.log import logger_factory
-
-from os.path import join
+# Ignore errors to avoid Internal Server Errors
+from trac.core import TracError
+TracError.__str__ = lambda self: unicode(self).encode('ascii', 'ignore')
 
 __all__ = 'TracGVizSystem', 'GViz_0_5', 'GVizJsonEncoder'\
             'GVizCSVEncoder', 'GVizHtmlEncoder'

File trac-dev/gviz/api.py

 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-r"""Trac Data Source able to feed widgets implemented with Google Visualisation API.
+r"""Trac Data Source able to feed widgets implemented with Google Visualization API.
 
 Components allowing Trac to export a project (environment)'s data so
 that different widgets based on Google Visualization (GViz) API
 """
 
 from trac.core import Interface, Component, ExtensionPoint, \
-                      implements, TracError
-from trac.config import Option
-from trac.env import IEnvironmentSetupParticipant
+                      implements
 from trac.perm import IPermissionRequestor, PermissionError
-from trac.util import get_pkginfo
 from trac.util.datefmt import utc
 from trac.web.api import IRequestHandler, RequestDone
-from pkg_resources import resource_string, resource_filename
 from trac.web.chrome import ITemplateProvider
 
+from pkg_resources import resource_filename
 
 try:
     import gviz_api
 except ImportError:
     import _gviz_api as gviz_api
-# from babel.support import Translations
-from os import listdir, environ
-from os.path import isdir
-import hashlib
 import sys
 from xmlrpclib import DateTime
 from datetime import datetime
 class GVizException(Exception):
   r"""Base class for all exception types defined in this package"""
 
-class GVizProviderNotFound(GVizException):
+class GVizProviderNotFoundError(GVizException):
     r"""Exception raised to denote that there is no GViz provider able
     to handle the request.
     """
 
-class GVizNotSupported(GVizException):
+class GVizNotSupportedError(GVizException):
     r"""Exception raised to denote that an unsopported feature has been
     requested by the client.
     """
 
-class GVizBadRequest(GVizException):
+class GVizBadRequestError(GVizException):
     r"""Exception raised to denote that the client request contains 
     wrong fields and/or values, or that required data is missing.
     """
 
-class GVizUnknownProvider(GVizException):
+class GVizUnknownProviderError(GVizException):
     r"""Exception raised to denote that there is no provider able to
     to handle the client request.
     """
 
 class GVizDataNotModifiedError(GVizException):
     r"""Exception raised when the hash generated from the data
-    returned by the GViz provider matches the hash sent by the 
+    returned by the GViz provider matches the hash value sent by the 
     client.
     """
     message = ''
 
-class GVizInvalidConfig(GVizException):
+class GVizInvalidConfigError(GVizException):
     r"""Exception raised when an invalid, incorrect or unsupported 
     value has been assigned to a configuration option.
     """
         
         - If the request is addressed to a specific data source then
           send back the corresponding data table's contents in one of
-          the formats supported by Google Visualisation API (defaults
+          the formats supported by Google Visualization API (defaults
           to JSON Response Format).
         
         - If the request is addressed directly to the `root path` (i.e. 
                 provider = self._cache[url_path]
             except (KeyError, PermissionError), exc:
                 if isinstance(exc, KeyError):
-                  exc = GVizUnknownProvider('Unknown provider "%s"' %
+                  exc = GVizUnknownProviderError('Unknown provider "%s"' %
                                               (url_path,))
                 handler = self._handlers[self._latest]
                 handler.output_response(req, None, exc, None)
                         params = dict(i.split(':', 1) \
                                 for i in str(params).split(';'))
                     except:
-                        exc = GVizBadRequest('Syntax error in "%s"' %
+                        exc = GVizBadRequestError('Syntax error in "%s"' %
                                             (params,))
                         handler = self._handlers[self._latest]
                         handler.output_response(req, None, exc, \
                             # TODO: Provide a better match for protocol handlers
                         except KeyError, exc:
                             handler = self._handlers[self._latest]
-                            exc = GVizNotSupported('Unsupported protocol version '
+                            exc = GVizNotSupportedError('Unsupported protocol version '
                                     '"%s"' % (exc.message,))
                             handler.output_response(req, None, exc, \
                                                     None, **std_params)
                         simple interface defined in `hashlib` module.
     """
 
-# TODO : Implement the different data source providers
-
 # TODO : Cache the requests and optimize the call/response life cycle
 #            by using the `reqId` parameter.
 

File trac-dev/gviz/extfmt.py

     def get_content_type(self):
         """MIME-type in this case is `text/plain` AFAIK.
         """
-        # TODO : Return more suitable mime type
-        return 'text/plain'
+        return 'text/x.moin.wiki'
 
 class GVizRstEncoder(Component):
     """reStructuredText encoder for Google Visualization API.

File trac-dev/gviz/ig/api.py

 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-"""Core components to integrate Trac with Google Gadgets technology
+r"""Core components to integrate Trac with Google Gadgets technology
 (i.e. iGoogle container). 
 
 This package states how to enhance and extend the different features
 """
 
 from trac.core import Interface, Component, ExtensionPoint, implements
-from trac.config import Option
-from trac.util import get_pkginfo
-from trac.web.api import IRequestHandler, RequestDone
-from trac.wiki.api import IWikiSyntaxProvider
-from pkg_resources import resource_string, resource_filename
-
-from genshi.builder import tag
-
-from os import listdir, environ
-from os.path import isdir
 
 class IGadgetBorderImgStore(Interface):
-    """The interface implemented by the components responsible for
+    r"""The interface implemented by the components responsible for
     storing the pictures to use in gadgets borders, and keep track of
     the border sets.
     
                 objects from where contents will be retrieved.
         """
     def load_image(id, img_id):
-        """Retrieve the contents of a picture in an image-based 
+        r"""Retrieve the contents of a picture in an image-based 
         gadget border.
         
         @param id the border identifier
 BORDER_IMAGES = ['tl', 'tr', 'bl', 'br', 'l', 'r', 'b', 'tt', 'tn']
 
 class IGadgetBorderProvider(Interface):
-    """Interface implemented by all those components offering borders
+    r"""Interface implemented by all those components offering borders
     gadgets will be surronded with. This includes both image-based 
     and CSS-based borders.
     """
     def get_namespace():
-        """Specify the namespace identifying this borders provider.
+        r"""Specify the namespace identifying this borders provider.
         
         @return a string or sequence of strings
         """
     def get_border_spec(border_id):
-        """Retrieve the string to set the `border` parameter in the 
+        r"""Retrieve the string to set the `border` parameter in the 
         URL needed to paste the gadget into the HTML of the target 
         web page.
         
                 for more details.
         """
     def get_description():
-        """Describe this border provider.
+        r"""Describe this border provider.
         
         @return an informative string describing this border provider.
         """
     def enum_borders():
-        """List all the borders managed by this particular 
+        r"""List all the borders managed by this particular 
         border provider.
         
         @return a sequence containing all the available border ids
         """
 
 class GadgetBordersManager(Component):
-    """Component responsible for managing gadget borders.
+    r"""Component responsible for managing gadget borders.
     """
     bprov = ExtensionPoint(IGadgetBorderProvider)
     

File trac-dev/gviz/ig/db.py

 Licensed under the Apache License, Version 2.0 
 """
 
-from trac.core import Interface, Component, ExtensionPoint, implements
+from trac.core import Component, implements
 from trac.config import Option
-from trac.util import get_pkginfo
-from trac.web.api import IRequestHandler, RequestDone
-from pkg_resources import resource_string, resource_filename
 from trac.env import IEnvironmentSetupParticipant
-from trac.perm import IPermissionRequestor
-from trac.web.api import IRequestHandler
-from trac.web.chrome import ITemplateProvider, add_stylesheet
 
+from os import makedirs, listdir
 from os.path import join, exists, isabs
-from os import makedirs, listdir
+from shutil import copyfileobj
 import types
-from shutil import copyfileobj
 
 from api import IGadgetBorderImgStore, BORDER_IMAGES
 
     
     # IEnvironmentSetupParticipant methods
     def environment_created(self):
-        """Create `htdocs/gadget/border` folder.
+        r"""Create `htdocs/gadget/border` folder.
         """
         self.log.info('IG: Creating gadget borders folder in %s', self.path)
         makedirs(self.path)
     
     def environment_needs_upgrade(self, db):
-        """Create `htdocs/gadget/border` folder if not already there.
+        r"""Create `htdocs/gadget/border` folder if not already there.
         """
         if not exists(self.path):
             self.log.info('IG: Creating gadget borders folder in %s', \
         return join(self.path, id, img_id + '.gif')
     
     def store_images(self, id, **img_data):
-        """Store gadget border images under the environment's 
+        r"""Store gadget border images under the environment's 
         `htdocs/gadgets/borders` folder.
         """
         path = join(self.path, id)
                 fo.close()
         
     def load_image(self, id, img_id):
-        """Retrieve the contents of a given picture in an image-based 
+        r"""Retrieve the contents of a given picture in an image-based 
         gadget border.
         """
         fnm = self._image_file_name(id, img_id)
             return None
     
     def enum_img_borders(self):
-        """Return the name of the folders containing images.
+        r"""Return the name of the folders containing images.
         """
         return listdir(self.path)

File trac-dev/gviz/ig/web_ui.py

 Licensed under the Apache License, Version 2.0 
 """
 
-from trac.core import Interface, Component, ExtensionPoint, \
-        implements, TracError
-from trac.config import Option, ExtensionOption
-from trac.util import get_pkginfo
+from trac.core import Component, implements, TracError
+from trac.config import ExtensionOption
+from trac.perm import IPermissionRequestor
 from trac.web.api import IRequestHandler, RequestDone
-from pkg_resources import resource_string, resource_filename
-from trac.env import IEnvironmentSetupParticipant
-from trac.perm import IPermissionRequestor
-from trac.util.translation import _
-from trac.web.api import IRequestHandler
 from trac.web.chrome import ITemplateProvider, add_stylesheet, \
         add_warning
 
 from genshi.builder import tag
+from pkg_resources import resource_filename
 
-from os.path import join, exists, basename
-from os import makedirs
+from os.path import basename
 
 from api import IGadgetBorderImgStore, BORDER_IMAGES, \
         IGadgetBorderProvider
         return False
     
     def process_request(self, req):
-        """If no parameters are specified then show a web interface
+        r"""If no parameters are specified then show a web interface
         to create a new (update an existing) image-based gadget 
         border. Otherwise assume this request has been sent so as to
         update the associated images files.
     
     # ITemplateProvider methods
     def get_htdocs_dirs(self):
-        """Return the path to the `htdocs` folder.
+        r"""Return the path to the `htdocs` folder.
         """
         yield ('ig', resource_filename('tracgviz.ig', 'htdocs'))
     
     def get_templates_dirs(self):
-        """Return the path to the `templates` folder.
+        r"""Return the path to the `templates` folder.
         """
         yield resource_filename('tracgviz.ig', 'templates')
     
     def get_namespace(self):
-        """ Install in `local.img` namespace.
+        r""" Install in `local.img` namespace.
         """
         return ('local', 'img')
     
     def get_border_spec(self, border_id):
-        """Retrieve an image-based border specification.
+        r"""Retrieve an image-based border specification.
         
         @param border_id a string identifying the target border
         @return the URL pointing to the border image set
         return "Border images hosted by Trac."
     
     def enum_borders(self):
-        """List all the borders stored in the images repository.
+        r"""List all the borders stored in the images repository.
         
         @return a sequence containing all the available border ids
         """
         return tag.a(label, class_='wiki', href= url, title=title)
     
     def get_link_resolvers(self):
-        """Return an iterable over (namespace, formatter) tuples.
+        r"""Return an iterable over (namespace, formatter) tuples.
 
         Each formatter should be a function of the form
         fmt(formatter, ns, target, label), and should

File trac-dev/gviz/ig/wiki.py

 
 __all__ = 'iGoogleGadgetMacro', 'GoogleVizGadgets', 'GadgetAliases'
 
-from trac.core import Interface, Component, ExtensionPoint, \
-        implements, TracError
-from trac.config import Option, ListOption
+from trac.core import Component, implements, TracError
+from trac.config import ListOption
 from trac.env import Environment
 from trac.mimeview.api import Context
-from trac.util import get_pkginfo
-from trac.web.api import IRequestHandler, RequestDone
-from pkg_resources import resource_string, resource_filename
-from trac.env import IEnvironmentSetupParticipant
-from trac.perm import IPermissionRequestor
-from trac.web.api import IRequestHandler
-from trac.web.chrome import ITemplateProvider, add_stylesheet
-from trac.wiki.api import IWikiSyntaxProvider, parse_args, \
-                          IWikiMacroProvider
+from trac.wiki.api import parse_args, IWikiMacroProvider
+from trac.wiki.formatter import Formatter
 from trac.wiki.macros import WikiMacroBase
-from trac.wiki.formatter import Formatter
 
 from genshi.core import Markup
-from genshi.builder import tag, Element
+from genshi.builder import Element
 
-from os.path import join, exists
-from os import makedirs
 from itertools import chain
-from string import strip
-from urlparse import urlunparse, urlparse
+from urlparse import urlunparse
 from urllib import urlencode
 
 from tracgviz.api import ITracLinksHandler
             return self.extract_link(link)
     
 class iGoogleGadgetMacro(WikiMacroBase):
-    r"""A quick and easy way to paste iGoogle gadgets in your wiki 
+    r"""A quick and easy way to embed iGoogle gadgets in your wiki 
     pages.
     
     This macro accepts the following parameters:
         yield (('gadget', 'google', 'modules'), self._format_link)
 
 class GadgetAliases(Component):
-  r"""A class allowing to write aliases for favorite gadgets in wiki 
-  pages, so it's quite easy to remember how to embed it. For further 
-  details read the documentation for option `gadgets.aliases`.
+  r"""A class allowing to write aliases for favorite gadgets to be 
+  embedded in wiki pages, so it's quite easy to remember how to 
+  embed it. For further details read the documentation for option 
+  `gadgets.aliases`.
   """
   implements(IWikiMacroProvider)
   
   GOOGLE_GADGETS = [
-                      ('MotionChart',   'motionchart'),
-                      ('LineChart',     'line-chart'),
+                      ('MotionChart',       'motionchart'),
+                      ('GeoMap',            'geomap'),
+                      ('AnnotatedTimeLine', 'time-series-line'),
+                      ('Sparkline',         'image-sparkline'),
+                      ('Gauge',             'gauge'),
+                      ('AreaChart',         'area-chart'),
+                      ('ImageAreaChart',    'image-area-chart'),
+                      ('BarChart',          'bar-chart'),
+                      ('ColumnChart',       'column-chart'),
+                      ('LineChart',         'line-chart'),
+                      ('PieChart',          'pie-chart'),
+                      ('ScatterChart',      'scatter-chart'),
+                      ('ImageBarChart',     'image-bar-chart'),
+                      ('ImageLineChart',    'image-line-chart'),
+                      ('ImagePieChart',     'image-pie-chart'),
+                      ('SimpleTable',       'simple-table'),
+                      ('SpreadsheetTable',  'table'),
+                      ('IntensityMap',      'heatmap'),
+                      ('Map',               'map'),
+                      ('OrgChart',          'orgchart'),
+                      ('WebSearch',         'web-search'),
+                      ('ImageSearch',       'image-search'),
                     ]
   aliases = ListOption('gadgets', 'aliases', \
               default=','.join('='.join([nm, GOOGLE_MODULES_URL % (gvid,)]) \

File trac-dev/gviz/proto.py

 Licensed under the Apache License, Version 2.0 
 """
 
-from trac.core import Interface, Component, ExtensionPoint, implements
-from trac.config import Option
-from trac.env import IEnvironmentSetupParticipant
 from trac.perm import PermissionError
 from trac.ticket.query import QuerySyntaxError
-from trac.util import get_pkginfo
-from trac.web.api import IRequestHandler
-from pkg_resources import resource_string, resource_filename
-
-# from babel.support import Translations
-from os import listdir, environ
-from os.path import isdir
 
 from api import IGVizProtocolHandler
 from util import BaseGVizHandler, send_response
 
 # GViz-specific exceptions
-from api import GVizDataNotModifiedError, GVizNotSupported, \
-                GVizUnknownProvider, GVizDataNotModifiedError, \
-                GVizBadRequest, GVizNotAuthenticatedError, \
-                GVizInvalidConfig
+from api import GVizDataNotModifiedError, GVizNotSupportedError, \
+                GVizUnknownProviderError, GVizDataNotModifiedError, \
+                GVizBadRequestError, GVizNotAuthenticatedError, \
+                GVizInvalidConfigError
 
 JSON_MIME_TYPE = 'text/plain'
 
 class GViz_0_5(BaseGVizHandler):
-    r"""Implementation of the Google Visualisation API data source
+    r"""Implementation of the Google Visualization API data source
     protocol (version 0.5)
     """
-    # implements(IGvizProtocolHandler)
     
     # IGvizProtocolHandler methods
     def get_version(self):
                             'Trac : Authentication required'],
                     PermissionError : \
                             ['access_denied', 'Access Denied'],
-                    GVizNotSupported : \
+                    GVizNotSupportedError : \
                             ['not_supported', 'Not supported'],
                     NotImplementedError : \
                             ['not_supported', 'Not implemented'],
                     QuerySyntaxError : \
                             ['internal_error', 'Syntax error in ' \
                                                 'Trac query'],
-                    GVizUnknownProvider : \
+                    GVizUnknownProviderError : \
                             ['unknown_data_source_id', 'Not Found'],
-                    GVizBadRequest : \
+                    GVizBadRequestError : \
                             ['invalid_request', 'Bad request'],
-                    GVizInvalidConfig : \
+                    GVizInvalidConfigError : \
                             ['internal_error', 'Invalid configuration'],
                    }
     
             send_response(_req, 200, contents, \
                                 mimetype=e.get_content_type())
         else:
-            exc = GVizNotSupported('Invalid format id %s' % (out,))
+            exc = GVizNotSupportedError('Invalid format id %s' % (out,))
             self._error(_req, exc, reqId, version, responseHandler)

File trac-dev/gviz/rpc.py

 #   limitations under the License.
 
 r"""RPC handlers not included in TracXmlRpcPlugin and used to 
-implement some data providers supporting Google Visualisation API
+implement some data providers supporting Google Visualization API
 protocol.
 
 Copyright 2009-2011 Olemis Lang <olemis at gmail.com>
 from trac.versioncontrol.web_ui.browser import CHUNK_SIZE 
 from trac.web.href import Href
 
-from datetime import datetime
+from datetime import datetime, date, time
 from fnmatch import fnmatch
-from itertools import imap, repeat
+from itertools import imap
 from os.path import splitext
 from tracrpc.api import IXMLRPCHandler
 import types
     sources = ExtensionPoint(ITimelineEventProvider)
 
     def __init__(self):
-#        self._module = TimelineModule(self.env)
         self._event_data = TimelineModule(self.env)._event_data
     
     # IXMLRPCHandler methods
   if isinstance(timestamp, datetime):
     return timestamp
   elif isinstance(timestamp, xmlrpclib.DateTime):
-    return rpc_to_datetime(timestamp)
+    return rpc_to_datetime(timestamp, req)
   elif isinstance(default, (datetime, date, time)): # Return default
     return default
   else:
     return datetime.now(req.tz)
 
 def _filter_revs(seq, repos, req, start, stop, full, \
-                  accessor=None):
+                  accessor=None, log=None):
   r"""Filter values in seq so that only references to revisions 
   commited during a time interval be enumerated. 
   Items are binary tuples of the form `(revision id, changeset object)`.
   """
   if seq is not None:
     seq = iter(seq)
+  if log is None:
+    class VoidLogger:
+      def __getattr__(self, attrnm):
+        return lambda *args, **kwds: None
+    log = VoidLogger()
   DO_NOTHING, DO_RETURN, DO_YIELD = xrange(3)
   load_chgset = True
-  if isinstance(start, int) and isinstance(stop, int):
+  if isinstance(start, types.StringTypes) and \
+      isinstance(stop, types.StringTypes):
     load_chgset = False
-    if repos.rev_older_than(start, stop):
+    if not repos.rev_older_than(stop, start):
       def cond(rev, chgset):
         if repos.rev_older_than(rev, start):
           return DO_RETURN
-        elif repos.rev_older_than(rev, stop):
+        elif repos.rev_older_than(stop, rev):
+          return DO_NOTHING
+        else:
           return DO_YIELD
-        else:
-          return DO_NOTHING
     else:
       return                      # `start` committed after `stop`
-  elif isinstance(start, int):
+  elif isinstance(start, types.StringTypes):
     if stop is None:
       load_chgset = False
       def cond(rev, chgset):
             return DO_YIELD
           else:
             return DO_NOTHING  
-  elif isinstance(stop, int):
+  elif isinstance(stop, types.StringTypes):
     if start is None:
+      start = 0
       load_chgset = False
       def cond(rev, chgset):
-        # Start in `stop` and stop in oldest so no need for DO_NOTHING ;)
-        if chgset.date < ts:
-            return DO_RETURN
-        else:
-          return DO_YIELD
+        # Start in `stop` and stop in oldest so no need for DO_RETURN 
+        # No need for DO_NOTHING since iterators will start from stop 
+        # (assuming that revision numbers are valid and that's a 
+        # precondition ;)
+        return DO_YIELD
     else:
       ts = _normalize_timestamp(repos, req, start, _epoc)
-      start = 0
       def cond(rev, chgset):
         # We start from `stop` so no need for DO_NOTHING ;)
         if chgset.date < ts:
           return DO_NOTHING
   # Search backwards
   load_chgset = load_chgset or full
-  while true:         # Stops when StopIteration is raised by seq
-    item = seq.next()
-    if accessor:
-      rev = accessor(item)
-    else:
-      rev = item
-    if repos.authz.has_permission_for_changeset(rev):
-      try:
-        chgset = load_chgset and repos.get_changeset(rev) or None
-      except NoSuchChangeset:
-        continue
-      action = cond(rev, chgset)
-      if action == DO_RETURN:
-          return
-      elif action == DO_YIELD:    # Implicit DO_NOTHING
-        if full:
-          yield item, chgset
-        else:
-          yield item, None
+  try:
+    while True:         # Stops when StopIteration is raised by seq
+      item = seq.next()
+      log.debug("IG: Processing item %s", item)
+      if accessor:
+        rev = accessor(item)
+      else:
+        rev = item
+      log.debug("IG: Processing revision %s", rev)
+      if repos.authz.has_permission_for_changeset(rev):
+        try:
+          chgset = load_chgset and repos.get_changeset(rev) or None
+          action = cond(rev, chgset)
+        except NoSuchChangeset:
+          continue
+        if action == DO_RETURN:
+            return
+        elif action == DO_YIELD:    # Implicit DO_NOTHING
+          if full:
+            yield item, chgset
+          else:
+            yield item, None
+  except NoSuchChangeset:
+    return
 
 class VersionControlRPC(Component):
     r""" An interface to Trac's Repository and RepositoryManager.
     
     def xmlrpc_methods(self):
         yield ('BROWSER_VIEW', 
-                ((list, list, str, int, bool, int),
-                 (list, list, str, int, bool), 
-                 (list, list, str, int), 
+                ((list, list, str, str, bool, int),
+                 (list, list, str, str, bool), 
+                 (list, list, str, str), 
+                 (list, list, str), 
                  (list, list),), 
                  self.ls)
         yield ('BROWSER_VIEW', 
-                ((list, list, int),
+                ((list, list, str),
                  (list, list),), 
                  self.getFileAttributes)
-        opt_types = [int, xmlrpclib.DateTime]
+        opt_types = [str, xmlrpclib.DateTime]
         yield ('CHANGESET_VIEW', 
                 tuple(rpc_opt_sigs(list, None, opt_types, \
                                     opt_types, [bool])
                   ), 
                  self.getRevisions)
         yield ('CHANGESET_VIEW', 
-                ((list, str, int, xmlrpclib.DateTime),
-                 (list, str, int, int),
-                 (list, str, int),
+                ((list, str, str, xmlrpclib.DateTime),
+                 (list, str, str, str),
+                 (list, str, str),
                  (list, str),), 
                  self.getFileHistory)
+        yield ('CHANGESET_VIEW', 
+                ((list, str),
+                 (list, ),), 
+                 self.enumChanges)
+        yield ('CHANGESET_VIEW', 
+                ((list, str),
+                 (list, ),), 
+                 self.normalize_rev)
                  
     # Exported methods
     def ls(self, req, files, filter_by=None, rev=None, rec=False, depth=None):
                           the files inside the input folders. File 
                           names are not generated in order so files 
                           in a folder and its subfolders may appear 
-                          in different positions.
+                          in different positions. If the revision ID 
+                          supplied in is invalid then an empty list 
+                          is returned.
         """
         repos = RepositoryManager(self.env).get_repository(req.authname)
-        if rev < 0: rev = None
+        if rev is None: rev = repos.youngest_rev
         if depth < 0: depth = None
         no_depth = depth is None
-        already = dict()
+        already = set()
         
         for item in files:
           if isinstance(item, types.StringTypes):
               path = item
             except NoSuchNode:
               continue
+            except NoSuchChangeset:
+              return
           else:
             d, node = item
             path = node.path
-          if not already.has_key(path): # Dont process filename twice
+          if path not in already: # Dont process filename twice
             if node.isfile:
-              if not filter_by or fnmatch(fnm, filter_by):
+              if (not filter_by) or fnmatch(path, filter_by):
                 yield path
-                already[path] = None    # Mark filename
             elif node.isdir:
-              for child in node.get_entries:
-                if not filter_by or fnmatch(fnm, filter_by):
+              for child in node.get_entries():
+                if (not filter_by) or fnmatch(child.path, filter_by):
                   yield child.path
-                  already[child.path] = None  # Mark filename
                 if child.isdir and rec and (no_depth or d < depth):
                   files.append([d + 1, child])
+                elif child.isfile:
+                  already.add(child.path)     # Mark filename
+                else:
+                  self.log.error("Unknown node type %s at %s", \
+                                                node.kind, node.path)
             else:
               self.log.error("Unknown node type %s at %s", \
                                                 node.kind, node.path)
+            already.add(path)                 # Mark filename
     
     def getFileAttributes(self, req, files, rev=None):
         r"""Retrieve the attributes of a group of files. The root 
                                       (`chgrev`)
         """
         repos = RepositoryManager(self.env).get_repository(req.authname)
-        if rev < 0:
-          rev = None
+        if rev is None:
+          rev = repos.youngest_rev
         mimeview = Mimeview(self.env)
         changesets = {}
         for path in files:
           try:
             node = repos.get_node(path, rev)
           except NoSuchNode:
-            yield None
+            yield {}
             continue
           _rev = node.rev
           attrs = dict(path=node.path, kind=node.kind, lastrev=_rev)
             except NoSuchChangeset:
               changesets[_rev] = attrs['changed'] = attrs['log'] = None
           if node.isdir:
-            attrs.update(dict(sz=0, ext='', mime=''))
+            attrs.update(dict(size=0, ext='', mime=''))
           elif node.isfile:
             # Copycat ! from trac.versioncontrol.web_ui.browser
             # MIME type detection 
                 mime_type = mimeview.get_mimetype(node.name, _chunk) or \
                             mime_type or 'text/plain'
             
-            attrs.update(sz=node.get_content_length(), \
-                          ext=splitext(node.path)[-1][:1], \
+            attrs.update(size=node.get_content_length(), \
+                          ext=splitext(node.path)[-1][1:], \
                           mime=mime_type)
           else:
             self.log.error("Unknown node type %s at %s", \
         """
         repos = RepositoryManager(self.env).get_repository(req.authname)
         def iter_revs(lastrev):
+          r"""Iterate over all revisions in repository.
+          
+          @param lastrev      start from this revision backwards. 
+                              This value should be a normalized rev 
+                              number
+          @return             a list containing normalized rev numbers
+          """
           rev = lastrev
-          while rev:
+          oldest_rev = repos.oldest_rev
+          while rev != oldest_rev:
             yield rev
-            rev = repos.previous_rev(rev)
-        seq = iter_revs(isinstance(until, int) and until or \
-                                                  repos.youngest_rev)
-        seq = _filter_revs(seq, repos, req, since, until, full)
-        if full:
-          return (tuple(getattr(chg, a) for a in self.REV_ATTRS) \
-                    for rev, chg in seq)
+            rev = repos.normalize_rev(repos.previous_rev(rev))
+          yield oldest_rev
+        try:
+          _rev = isinstance(until, types.StringTypes) and \
+                              repos.normalize_rev(until) or \
+                              repos.youngest_rev
+          self.log.debug("IG: Stop at revision %r between [%r, %r]", \
+                          _rev, repos.oldest_rev, repos.youngest_rev)
+          seq = iter_revs(_rev)
+        except NoSuchChangeset:
+          return []
         else:
-          return (rev for rev, chg in seq)
+          seq = _filter_revs(seq, repos, req, since, until, full, \
+                              log=self.log)
+          if full:
+            return (tuple(getattr(chg, a) for a in self.REV_ATTRS) \
+                      for rev, chg in seq)
+          else:
+            return (rev for rev, chg in seq)
     
     def getFileHistory(self, req, path, rev=None, since=None):
         r"""Retrieve information about all the changes performed on a 
                           the request has no access to that 
                           particular changeset.
         """
-        if not isinstance(rev, (int, type(None))):
-          raise ValueError("Revision number must be an integer value")
+        if not isinstance(rev, (type(None), types.StringTypes)):
+          raise ValueError("Revision ID must be a string")
         repos = RepositoryManager(self.env).get_repository(req.authname)
         try:
-          node = repos.get_node(filename, rev)
+          node = repos.get_node(path, rev)
         except NoSuchNode:
           return []
         seq = node.get_history()
         seq = _filter_revs(seq, repos, req, since, rev, False, \
-                            accessor=lambda x: x[1])
+                            accessor=lambda x: x[1], log=self.log)
         return (x[0] for x in seq)
     
     def enumChanges(self, req, rev=None):
         r"""Enumerate all the changes performed at a given revision.
         
-        @param rev        The revision 
-                          numbers are allowed in this argument.
+        @param rev        The ID of the target revision (changeset). 
+                          Only numbers are allowed in this argument.
         @return           A list of tuples describing every change in 
                           the changeset. The tuple will contain 
                           `(path, kind, change, base_path, base_rev)`,
                                         ''deleted'' path  for a 
                                         DELETE change).
                           - base_path : the source path for the
-                                        action (`None` in the case of 
-                                        an ADD change).
+                                        action (empty string  in the 
+                                        case of an ADD change).
                           - base_rev  : the source rev for the
-                                        action (`-1` in the case of 
-                                        an ADD change).
+                                        action (empty string in the 
+                                        case of an ADD change).
                           
                           Note: Some revisions may be skipped if 
                           permissions state that the user performing 
                           particular changeset.
         """
         repos = RepositoryManager(self.env).get_repository(req.authname)
+        if rev is None:
+          rev = repos.youngest_rev
         try:
           chgset = repos.get_changeset(rev)
         except NoSuchChangeset:
           return []
-        return chgset.get_changes()
+        return ((p, k, chg, bp or '', brev or '') \
+                      for p, k, chg, bp, brev in chgset.get_changes())
+    
+    def normalize_rev(self, req, rev=None):
+        r"""Return a canonical representation of a revision.
 
+        It's up to the backend to decide which string values of `rev` 
+        (usually provided by the user) should be accepted, and how they 
+        should be normalized. Some backends may for instance want to 
+        match against known tags or branch names.
+        
+        In addition, if `rev` is missing or '', the youngest revision 
+        should be returned.
+        
+        @param rev        The ID of the target revision (changeset).
+        @return           A list of tuples describing every change in 
+                          the changeset. The tuple will contain 
+        """
+        repos = RepositoryManager(self.env).get_repository(req.authname)
+        return repos.normalize_rev(rev)

File trac-dev/gviz/search.py

 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-"""Data sources used to share data generated by Trac search engine. 
+r"""Data sources used to share data generated by Trac search engine. 
 This includes search filters and results.
 
 Note: It relies on Trac XML-RPC plugin.
 Copyright 2009-2011 Olemis Lang <olemis at gmail.com>
 Licensed under the Apache License, Version 2.0 
 """
-
-__all__ = 'GVizSearchFiltersProvider', 'GVizSearchProvider'
 
-from util import GVizXMLRPCAdapter, dummy_request
-from api import gviz_col, gviz_param, GVizBadRequest
-import types
+__all__ = 'GVizSearchFiltersProvider', 'GVizSearchProvider'
 
-class GVizSearchFiltersProvider(GVizXMLRPCAdapter):
-    """Returns all the search filters installed in the environment.
-    
-    This component depends on tracrpc.ticket.SearchRPC. The later 
-    must be enabled. Please read 
-    https://opensvn.csie.org/traccgi/swlcu/wiki/En/Devel/TracGViz/DataSources#Preconditions
-    for further details.
-    """
+from util import GVizXMLRPCAdapter
+from api import gviz_col, gviz_param, GVizBadRequestError
+import types
+
+class GVizSearchFiltersProvider(GVizXMLRPCAdapter):
+    r"""Returns all the search filters installed in the environment.
+    
+    This component depends on tracrpc.ticket.SearchRPC. The later 
+    must be enabled. Please read 
+    https://opensvn.csie.org/traccgi/swlcu/wiki/En/Devel/TracGViz/DataSources#Preconditions
+    for further details.
+    """
     # IGVizDataProvider methods
     def get_data_schema(self):
         return [('name', 'string'), ('desc', 'string', 'description')]
-    
+    
     @gviz_col('name', "The name of the search filter")
     @gviz_col('desc', "Search filter description")
     def get_data(self, req, tq, **tqx):
-        """Get search filters' data.
+        r"""Get search filters' data.
         
-        @return a list of search filters with each element in the 
+        @return a list of search filters with each element in the 
                 form (name, description).
         """
         return (fi[:2] for fi in self._rpc_obj.getSearchFilters(req))
-    
-    def xmlrpc_namespace(self):
-        """Use Search XML-RPC.
+    
+    def xmlrpc_namespace(self):
+        r"""Use Search XML-RPC.
         """
         return ('search',)
-    def gviz_namespace(self):
-        """Access the data at `search/filters`.
+    def gviz_namespace(self):
+        r"""Access the data at `search/filters`.
         """
         return ('search', 'filters')
-
-class GVizSearchProvider(GVizXMLRPCAdapter):
-    """Provides the data retrieved by Trac built-in search engine. It 
-    consists of the occurrences of keywords and substrings in wiki 
-    pages, tickets, changeset descriptions and other specific 
-    extensions.
-    
-    This component depends on tracrpc.ticket.SearchRPC. The later 
-    must be enabled. Please read 
-    https://opensvn.csie.org/traccgi/swlcu/wiki/En/Devel/TracGViz/DataSources#Preconditions
-    for further details.
-    """
+
+class GVizSearchProvider(GVizXMLRPCAdapter):
+    r"""Provides the data retrieved by Trac built-in search engine. It 
+    consists of the occurrences of keywords and substrings in wiki 
+    pages, tickets, changeset descriptions and other specific 
+    extensions.
+    
+    This component depends on tracrpc.ticket.SearchRPC. The later 
+    must be enabled. Please read 
+    https://opensvn.csie.org/traccgi/swlcu/wiki/En/Devel/TracGViz/DataSources#Preconditions
+    for further details.
+    """
     # IGVizDataProvider methods
     def get_data_schema(self):
-        return [('url', 'string'), ('title', 'string'), \
-                ('date', 'datetime'), ('author', 'string'), \
+        return [('url', 'string'), ('title', 'string'), \
+                ('date', 'datetime'), ('author', 'string'), \
                 ('excerpt', 'string')]
-    
+    
     @gviz_col('url', "URL to the resource where a match was found.")
     @gviz_col('title', "Result title.")
-    @gviz_col('date', "Timestamp. It's usually the date and time "
+    @gviz_col('date', "Timestamp. It's usually the date and time "
                 "of the last modification made to the resource.")
     @gviz_col('author', "The name of the author of the last change.")
     @gviz_col('excerpt', "Brief text illustrating the contents found.")
     @gviz_param('q', "The query string.")
-    @gviz_param('filter', "Restricts the search to specific filters. "
-                "Specify this parameter multiple times in order to "
-                "select multiple search filters. Defaults to all if "
+    @gviz_param('filter', "Restricts the search to specific filters. "
+                "Specify this parameter multiple times in order to "
+                "select multiple search filters. Defaults to all if "
                 "not provided.")
     def get_data(self, req, tq, q=None, filter=None, **tqx):
-        """Retrieve search results.
-        """
-        if not q:
-            raise GVizBadRequest("No query was specified")
-        if isinstance(filter, types.StringTypes):
-            filter = [filter]
-        self.log.debug("IG: Filter : %s", filter)
-        result = self._rpc_obj.performSearch(req, q, filter)
-        self.log.debug("IG: Search result : %s", result)
+        r"""Retrieve search results.
+        """
+        if not q:
+            raise GVizBadRequestError("No query was specified")
+        if isinstance(filter, types.StringTypes):
+            filter = [filter]
+        self.log.debug("IG: Filter : %s", filter)
+        result = self._rpc_obj.performSearch(req, q, filter)
+        self.log.debug("IG: Search result : %s", result)
         return result
-    
-    def gviz_namespace(self):
+    
+    def gviz_namespace(self):
         return ('search',)
         

File trac-dev/gviz/setup.cfg

-[egg_info]
-tag_build = 
-tag_date = 0
-tag_svn_revision = 0
-
+[egg_info]
+tag_build = 
+tag_date = 0
+tag_svn_revision = 0
+

File trac-dev/gviz/stdfmt.py

 from api import IGVizTableEncoder
 from util import render_gviz_value
 
-# TODO : Implement order by and select statements
-
 class GVizJsonEncoder(Component):
     """JSON encoder for Google Visualization API.
     """

File trac-dev/gviz/stdhash.py

 Licensed under the Apache License, Version 2.0 
 """
 
-from trac.core import Component, ExtensionPoint, implements, TracError
-from trac.config import Option
+from trac.core import Component, implements
 
-from api import IHashLibrary, GVizInvalidConfig
+from api import IHashLibrary, GVizInvalidConfigError
 
 import hashlib
 from zlib import adler32, crc32
       try:
         return hashlib.new(method_name)
       except ValueError:
-        raise GVizInvalidConfig("Unsupported hash algorithm '%s'" \
+        raise GVizInvalidConfigError("Unsupported hash algorithm '%s'" \
                                   % (method_name,))
     else:
       raise
     try:
       ho = self.ZLibChecksumObject(method_name)
     except ValueError:
-      raise GVizInvalidConfig("Unsupported hash algorithm '%s'" \
+      raise GVizInvalidConfigError("Unsupported hash algorithm '%s'" \
                                   % (method_name,))
     else:
       if data is not None:

File trac-dev/gviz/ticket.py

 Licensed under the Apache License, Version 2.0 
 """
 
-from trac.core import Component, implements, TracError
-from trac.ticket.query import Query, QuerySyntaxError
-from trac.ticket.report import ReportModule
 from trac.ticket.roadmap import RoadmapModule, \
                                 get_tickets_for_milestone, \
                                 apply_ticket_permissions, \
-                                get_ticket_stats, milestone_stats_data
-from trac.util.datefmt import utc, _epoc
-from trac.util.text import to_unicode
+                                get_ticket_stats
+from trac.util.datefmt import _epoc
 from trac.util.translation import _
-from trac.web.href import Href
 
-from api import gviz_col, gviz_param, GVizBadRequest
+from api import gviz_col, gviz_param, GVizBadRequestError
 from util import GVizXMLRPCAdapter, map_with_id, map_many_with_id, \
                     map_value_with_id, dummy_request, get_column_desc, \
                     REQFIELDS_DESC, REQFIELDS_DEFAULTS
 
-from datetime import datetime
 from xmlrpclib import DateTime
 from itertools import chain, repeat, imap
 import types
-from urlparse import urlunparse, urlparse
 
 #--------------------------------------------------
 # Ticket models and enums
 class GVizMilestoneProvider(GVizModelDataSource):
     r"""Data source providing data related to project milestones 
     (including progress, and other metadata). This component depends on 
-    tracrpc.ticket.%sRPC. The later must be enabled. Please read 
+    tracrpc.ticket.TicketRPC. The later must be enabled. Please read 
     https://opensvn.csie.org/traccgi/swlcu/wiki/En/Devel/TracGViz/DataSources#Preconditions
     for further details.
     """
                 return self._rpc_obj.getRecentChanges( \
                             req, DateTime(_epoc))
         except ValueError, exc:
-            raise GVizBadRequest("Invalid ticket id : %s" % (exc.message,))
+            raise GVizBadRequestError("Invalid ticket id : %s" % (exc.message,))
     
     from util import convert_req_date as _convert_date
     _convert_date = staticmethod(_convert_date)
                 if at is not None:
                     at = (t for t in at if  t < since)
         except:
-            raise GVizBadRequest("Invalid datetime value")
+            raise GVizBadRequestError("Invalid datetime value")
         
         ids = self._retrieve_tickets(req, id, since)
         self.log.debug('Modified tickets ids %s', ids)
         try:
           rid = int(req.args['id'])
         except:
-          raise GVizBadRequest(_("Invalid report ID (id parameter"))
+          raise GVizBadRequestError(_("Invalid report ID (id parameter"))
         enumcols = self._rpc_obj.enum_columns
         spec_fields = self.spec_fields
         return dict([

File trac-dev/gviz/timeline.py

 
 __all__ = 'GVizTimelineEvents', 'GVizTimelineFilters'
 
-from trac.core import implements, ExtensionPoint, Component
-from trac.mimeview.api import Context
-from trac.timeline.api import ITimelineEventProvider
-from trac.timeline.web_ui import TimelineModule
-from trac.util.datefmt import _epoc
-
-from datetime import datetime
-from itertools import chain
-from tracrpc.api import IXMLRPCHandler
 import types
-import xmlrpclib
 
 from api import gviz_col, gviz_param
 from util import GVizXMLRPCAdapter, rpc_to_datetime, convert_req_date, \

File trac-dev/gviz/util.py

           'get_column_desc', 'TYPES_2_GVIZ', 'get_column_desc', \
           'rpc_opt_sigs', 'REQFIELDS_DESC', 'REQFIELDS_DEFAULTS'
 
-from trac.core import Interface, Component, ExtensionPoint, implements, \
-                        TracError
+from trac.core import Component, ExtensionPoint, implements
 from trac.config import Option
-from trac.util import get_pkginfo
-from trac.web.api import IRequestHandler, RequestDone, Request
+from trac.web.api import RequestDone, Request
 from trac.web.chrome import Chrome
 from trac.web.main import RequestDispatcher
-from pkg_resources import resource_string, resource_filename
 
 from tracrpc.api import XMLRPCSystem, Method
 
-# from babel.support import Translations
 from BaseHTTPServer import DEFAULT_ERROR_MESSAGE, BaseHTTPRequestHandler
 from datetime import datetime, date, time
 from itertools import takewhile, chain, imap, repeat, izip
-from os import listdir, environ
-from os.path import isdir
-import types
 from xmlrpclib import DateTime
 
 from api import IGVizProtocolHandler, IGVizTableEncoder, \
-                IGVizDataProvider, IHashLibrary, GVizBadRequest
+                IGVizDataProvider, IHashLibrary, GVizBadRequestError
 
 __metaclass__ = type
 
 
 class BaseGVizHandler(Component):
     r"""This class encloses the functionality which might be present
-    in most versions of Google Visualisation API. It can be reused by
+    in most versions of Google Visualization API. It can be reused by
     specific subclasses implementing a specific protocol version
-    as defined by the Google Visualisation API.
+    as defined by the Google Visualization API.
     """
     abstract = True
     implements(IGVizProtocolHandler)
                         doc="""The algorithm used to generate a hash """
                             """of the data sent back to the client. This """
                             """feature is defined by Google """
-                            """Visualisation API since version 0.5 so as """
+                            """Visualization API since version 0.5 so as """
                             """to optimize the request / response """
                             """mechanism to make rational use of the """
                             """available bandwith.""")
     @staticmethod
     def fmt_supports_version(encoder, version):
         r"""Return whether a data table encoder supports a specific
-        version of Google Visualisation API."""
+        version of Google Visualization API."""
         
         rels = {
                 '>' : tuple.__gt__,
                                 "or the component is disabled. "
                                 "Contact your Trac administrator.")
 
+#class Method(Method):
+#    r"""A faster XML-RPC method implementation since it returns 
+#    iterators instead of lists.
+#    """
+#    def __call__(self, req, args):
+#        req.perm.assert_permission(self.permission)
+#        result = self.callable(req, *args)
+#        # If result is null, return a zero
+#        if result is None:
+#            result = 0
+#        elif isinstance(result, dict):
+#            for key,val in result.iteritems():
+#                if isinstance(val, datetime.datetime):
+#                    result[key] = to_datetime(val)
+#            #pass
+#        elif not isinstance(result, basestring):
+#            # Try and convert result to a list
+#            try:
+#                result = (i for i in result)
+#            except TypeError:
+#                pass
+#        return (result,)
+
 class RPCHelperObject:
     r"""A proxy class needed to assert the permissions handled by 
-    XMLRPCSystem, instead of going directly to the RPC method.
+    XMLRPCSystem, instead of using directly to the RPC method.
     """
     def __init__(self, rpc_obj):
         methods = (Method(rpc_obj, *mi) for mi in rpc_obj.xmlrpc_methods())
           when = DateTime(when)
       return when
     except:
-      raise GVizBadRequest("Invalid datetime value or wrong date format.")
+      raise GVizBadRequestError("Invalid datetime value or wrong date format.")
 
 def rpc_to_datetime(DT, req):
     r"""Return the datetime object representing the xmlrpclib.DateTime 

File trac-dev/gviz/vcs.py

 Licensed under the Apache License, Version 2.0 
 """
 
-from trac.core import Component, implements, TracError
-from trac.ticket.query import Query, QuerySyntaxError
-from trac.util.datefmt import utc, _epoc
-from trac.util.text import to_unicode
-from trac.util.translation import _
-from trac.web.href import Href
-
-from api import gviz_col, gviz_param, GVizBadRequest
+from api import gviz_col, gviz_param, GVizBadRequestError
 from util import GVizXMLRPCAdapter, convert_req_date, \
                   REQFIELDS_DESC, REQFIELDS_DEFAULTS
 
-from datetime import datetime
-from xmlrpclib import DateTime
-from itertools import chain, repeat, izip
 import types
-from urlparse import urlunparse, urlparse
 
 #--------------------------------------------------
 # Source files
 
 class GVizSourceBase(GVizXMLRPCAdapter):
   abstract = True
-  def _normalize_rev(self, rev, allow_date=True, \
-                          req=None, fmt=REQFIELDS_DEFAULTS['datefmt']):
+  
+  FA_CODE = compile('(path, kind, size, ext, mime, changed, lastrev)', \
+                            '<string>', 'eval')
+  
+  def _normalize_rev(self, rev, req, fmt=REQFIELDS_DEFAULTS['datefmt']):
     r"""Converts a value in a client request making reference to a 
     revision committed to the source repository.
     
     @param rev          the value received in the HTTP request.
-    @param allow_date   accept strings representing datetime values.
     @param fmt          datetime format used to convert string (not 
                         needed if allow_date is false).
     @param req          request object (not needed if allow_date is 
                         false, mandatory otherwise).
     """
-    if rev is not None:
-      try:
-        return int(rev)
-      except:
-        if rev == 'HEAD':
-          return None        # Default to youngest revision
-        elif allow_date:
-          return convert_req_date(rev, fmt, req, False)
-        else:
-          raise GVizBadRequest("Invalid revision identifier")
-    else:
-      return None
+    try:
+      return convert_req_date(rev, fmt, req, False)
+    except GVizBadRequestError:
+      return rev
   
-  def _requested_files(req, files, filter=None, rev=None, \
+  def _requested_files(self, req, files, filter=None, rev=None, \
                         rec=False, depth=None):
     if isinstance(files, types.StringTypes):
-      files = (files,)
-    rev = self._normalize_rev(rev, False)
-    return self._rpc_obj.ls(self, req, files, filter, rev, rec, depth)
+      files = [files]
+    if depth is not None:
+      try:
+        depth = int(depth)
+      except ValueError:
+        raise GVizBadRequestError("Recursion depth must be an integer value")
+    return self._rpc_obj.ls(req, files, filter, rev, rec, depth)
     
   def xmlrpc_namespace(self):
       r"""Use Version Control XML-RPC.
     for further details.
     """
     
-    FA_CODE = compile('(path, kind, size, ext, mime, changed, lastrev)', \
-                              '<string>', 'eval')
-    
     # IGVizDataProvider methods
     def get_data_schema(self, req):
         r"""Return file attributes.
         return [('path', 'string'), ('kind', 'string'), \
                 ('size', 'number'), \
                 ('ext', 'string'), ('mime', 'string'), \
-                ('changed', 'datetime'), ('lastrev', 'number')]
+                ('changed', 'datetime'), ('lastrev', 'string'),
+                ('log', 'string'), ]
     
     @gviz_param('file',     "target files and|or folders. Can be "
                               "specified more than once in order to "
                           "modifications to this file were "
                           "commited")
     @gviz_col('log',     "Commit message for last revision "
-                          "(`chgrev`)")
-    def get_data(self, req, tq, file, filter=None, rev=None, \
+                          "(`lastrev`)")
+    def get_data(self, req, tq, file='/', filter=None, rev=None, \
                         rec=False, depth=None, **tqx):
         r"""Retrieve files attributes.
         """
         files = self._requested_files(req, file, filter, rev, rec, \
                                         depth)
-        rev = self._normalize_rev(rev, False)
+        if rec is not False:
+          rec = True
+        revs = dict()
+        
         fa_meth = self._rpc_obj.getFileAttributes
-        return (eval(self.FA_CODE, attrs) \
-                    for attrs in fa_meth(req, files, rev) \
-                    if attrs is not None)
+        ra_meth = self._rpc_obj.getRevisions
+        return (eval(self.FA_CODE, attrs) + \
+                    (revs.setdefault(attrs['lastrev'], \
+                                      iter(ra_meth(req, \
+                                              str(attrs['lastrev']), \
+                                              str(attrs['lastrev']), \
+                                              True)).next()[1]),) \
+                    for attrs in fa_meth(req, files, rev) if attrs)
     
     def gviz_namespace(self):
         return ('source', 'files')
         r"""Return event fields.
         """
         return [('path', 'string'), ('target', 'string'), \
-                ('rev', 'number'), ('chg', 'string')]
+                ('rev', 'string'), ('chg', 'string'),
+                ('kind', 'string'), ('size', 'number'), \
+                ('ext', 'string'), ('mime', 'string'), \
+                ('changed', 'datetime'), ('log', 'string'), 
+                ]
     
     @gviz_param('file',     "target files and|or folders. Can be "
                               "specified more than once in order to "
                           "  * edit    : target was modified"
                           "  * move    : target was moved to "
                                       "another location")
-    def get_data(self, req, tq, file, filter=None, rev=None, \
+    @gviz_col('kind',     "the type of node (e.g. one of "
+                          "'file' or 'dir') at `target`.")
+    @gviz_col('size',    "size of `target` in bytes")
+    @gviz_col('ext',     "extension of `target`")
+    @gviz_col('mime',    "MIME type of `target` if known")
+    @gviz_col('changed', "Modification date of `target` (date of `rev`)")
+    @gviz_col('log',     "Commit message for last revision "
+                          "(`lastrev`)")
+    def get_data(self, req, tq, file='/', filter=None, rev=None, \
                         rec=False, depth=None, since=None, \
                         fmt=REQFIELDS_DEFAULTS['datefmt'], **tqx):
         r"""Retrieve changes in file history.
         """
         files = self._requested_files(req, file, filter, rev, rec, \
                                         depth)
-        rev = self._normalize_rev(rev, False)
         if since is not None:
-          since = self._normalize_rev(rev, True, req, fmt)
+          since = self._normalize_rev(since, req, fmt)
         fh_meth = self._rpc_obj.getFileHistory
-        return ((path,) + e \
-                              for path in files \
-                              for e in fh_meth(req, path, rev, since)
-                              )
+        fa_meth = self._rpc_obj.getFileAttributes
+        ra_meth = self._rpc_obj.getRevisions
+        revs = dict()
+        def_attrs = {'size' : None, 'mime' : None, 'changed' : None,
+                      'kind' : None, 'last_rev': None}
+        for path in files:
+          for target, _rev, chg in fh_meth(req, path, rev, since):
+            attrs = fa_meth(req, [target], _rev)[0] or \
+                    def_attrs.copy().update(dict(path=target, \
+                                          ext=splitext(target)[-1][1:]))
+            yield (path, target, _rev, chg) + \
+                    eval(self.FA_CODE, attrs)[1:-1] + \
+                    (revs.setdefault(_rev, \
+                                      iter(ra_meth(req, str(_rev), 
+                                            str(_rev), True)).next()[1]),)
     
     def gviz_namespace(self):
-        return ('source', 'filechgs')
+        return ('source', 'file', 'history')
 
 #--------------------------------------------------
 # Chagesets
     def get_data_schema(self, req):
         r"""Return event fields.
         """
-        return [('rev', 'number'), ('chg', 'string'), \
+        return [('rev', 'string'), ('chg', 'string'), \
                 ('kind', 'string'), ('target', 'string'), \
-                ('base_path', 'string'), ('base_rev', 'number') \
+                ('base_path', 'string'), ('base_rev', 'string') \
                 ]
     
     @gviz_param('rev',       "target revision number. If missing (or "
     @gviz_col('base_rev', "the source rev for the"
                           "action (`-1` in the case of "
                           "an ADD change).")
-    def get_data(self, req, tq, rev=None, since=None, until=None, **tqx):
+    def get_data(self, req, tq, rev=None, since=None, until=None, 
+                                fmt=REQFIELDS_DESC['datefmt'], **tqx):
         r"""Retrieve changes in revision.
         """
         rpc_obj = self._rpc_obj
-        already = dict()
+        already = set()
         if not isinstance(rev, list):
           rev = (rev,)
-        for r in [self._normalize_rev(x, False) for x in rev]:
-          for chg in rpc_obj.enumChanges(req, r):
-            yield (r,) + chg
-          already[r] = True       # Mark revision
+        for r in rev:
+          try:
+            r = rpc_obj.normalize_rev(req, r)
+          except:
+            raise
+          if r not in already:
+            for chg in rpc_obj.enumChanges(req, r):
+              yield (r,) + chg
+            already.add(r)       # Mark revision
         if (since, until) != (None, None):
-          since = self._normalize_rev(since, True, req, fmt)
-          until = self._normalize_rev(until, True, req, fmt)
-          for r, chgset in rpc_obj.getRevisions(req, since, until):
-            if not already.get(r):
+          if since is not None:
+            since = self._normalize_rev(since, req, fmt)
+          if until is not None:
+            until = self._normalize_rev(until, req, fmt)
+          for r in rpc_obj.getRevisions(req, since, until):
+            if r not in already:
               for chg in rpc_obj.enumChanges(req, r):
                 yield (r,) + chg
-              
-        
     
     def gviz_namespace(self):
         return ('source', 'changes')
     def get_data_schema(self, req):
         r"""Return fields in revision log.
         """
-        return [('rev', 'number'), ('message', 'string'), \
+        return [('rev', 'string'), ('message', 'string'), \
                  ('author', 'string'), ('date', 'datetime')]
     
     @gviz_param('since',    "boundary value. Revisions older than "
                         fmt=REQFIELDS_DEFAULTS['datefmt'], **tqx):
         r"""Retrieve revision log.
         """
-        since = self._normalize_rev(since, True, req, fmt)
-        until = self._normalize_rev(until, True, req, fmt)
-        return self._rpc_obj.getRevisions(req, since, until, True)
+        if since is not None:
+          since = self._normalize_rev(since, req, fmt)
+        if until is not None:
+          until = self._normalize_rev(until, req, fmt)
+        obj = self._rpc_obj.getRevisions(req, since, until, True)
+        self.log.debug("IG: RPC result %s ", obj)
+        return obj
     
     def gviz_namespace(self):
         return ('source', 'revlog')

File trac-dev/gviz/wiki.py

 #   limitations under the License.
 
 r"""This module contains every WikiFirmatting extension being related 
-to Trac GViz system.
+to Trac GViz system, as well as data sources that interact with Trac 
+Wiki module.
 
 Copyright 2009-2011 Olemis Lang <olemis at gmail.com>
 Licensed under the Apache License, Version 2.0 
             'GoogleAppsConnector', 'LinksTreeDispatcher', \
             'GVizWikiPages'
 
-from trac.core import Interface, Component, ExtensionPoint, \
-        implements, TracError
+from trac.core import Component, ExtensionPoint, implements, TracError
 from trac.attachment import Attachment
-from trac.config import Option
-from trac.mimeview.api import Context
-from trac.util import get_pkginfo
-from trac.web.api import IRequestHandler, RequestDone
 from trac.web.href import Href
-from pkg_resources import resource_string, resource_filename
-from trac.env import IEnvironmentSetupParticipant
-from trac.perm import IPermissionRequestor
-from trac.web.api import IRequestHandler
-from trac.web.chrome import ITemplateProvider, add_stylesheet
-from trac.wiki.api import IWikiSyntaxProvider, parse_args
+from trac.wiki.api import IWikiSyntaxProvider
 from trac.wiki.macros import WikiMacroBase
-from trac.wiki.formatter import Formatter, system_message
+from trac.wiki.formatter import system_message
 
 from genshi.core import Markup
-from genshi.builder import tag, Element
+from genshi.builder import tag
 from genshi.template import MarkupTemplate
 
 from cgi import parse_qs
 from fnmatch import fnmatch
 from inspect import getargspec
 from itertools import chain, izip, repeat
-from os.path import join, exists
-from os import makedirs
 from re import compile
 import types
-from urlparse import urlunparse
-from urllib import urlencode
 from xmlrpclib import DateTime
 
 from api import TracGVizSystem, gviz_api, ITracLinksHandler, \
-                GVizBadRequest, gviz_col, gviz_param
+                GVizBadRequestError, gviz_col, gviz_param
 from util import GVizXMLRPCAdapter, convert_req_date, \
                 REQFIELDS_DESC, REQFIELDS_DEFAULTS
 
 
 class GoogleDocsConnector(Component):
     r"""Provide shortcuts to the features found in Google Spreadsheets 
-    being related to Google Visualisation API.
+    being related to Google Visualization API.
     
     Syntax -> 
     gviz:google:sheet:<spreadsheet_id>[:[<sheet_name>][:<top_cell>-<bottom_cell>]][?[headers=<number>]]
             if since is not None:
                 since = convert_req_date(since, fmt, req)
         except:
-            raise GVizBadRequest('Invalid date time value %s' % (since,))
+            raise GVizBadRequestError('Invalid date time value %s' % (since,))
         pages = self._retrieve_pages(req, name, since)
         for page in pages:
             info = obj.getPageInfo(req, page)
                 _since = convert_req_date(since, fmt, req, False)
                 since = DateTime(_since)
         except:
-            raise GVizBadRequest('Invalid date time value %s' % (since,))
+            raise GVizBadRequestError('Invalid date time value %s' % (since,))
         pages = self._retrieve_pages(req, name, since)
         for page in pages:
             for path in obj.listAttachments(req, page):