AppyPrinciplesappy.pod
appy.pod Command-line programs ogrep.py - Find & replace within POD templates

After years of experiencing countless success stories using Appy on various projects, you may end up with a plethora of POD templates. Consequently, these needs may emerge:

  • searching keywords within POD templates ;
  • performing find / replace operations within your templates.

This what ogrep.py is all about.

ogrep stands for "OpenDocument Grep" and is meant to perform grep-like searches within a given POD template or within all POD templates being found, recursively, in a given folder. Although it is possible, with ogrep, to grep any part of an OpenDocument template (be it an ODT or ODS template), the main focus is on searching and/or replacing keywords within POD-specific zones, ie, comments (notes) and fields.

Searching for keywords

Suppose you have this template, named Agenda.py, in the current working directory. A part of this template could look like this:

If you want to find all usages of method getShownValue in your template, you may use ogrep.py this way:

~/projets/appy/bin/ogrep.py getShownValue Agenda.odt

The result would be:

pod/Agenda.odt matches 1 time(s).

Searching for keyword item would match more results:

~/projets/appy/bin/ogrep.py item Agenda.odt
pod/Agenda.odt matches 5 time(s).

If you want more output, use options -v or -vv:

~/projets/appy/bin/ogrep.py getShownValue Agenda.odt -v
:: 1 match for Agenda.odt ::
  Content:
item.proposingGroup.getShownValue()

With even more verbosity, you get this result.

~/projets/appy/bin/ogrep.py getShownValue Agenda.odt -vv
:: 1 match for Agenda.odt ::
  Match::on::field (tag text:conditional-text)
  Keyword(s) (regex): getShownValue 
  Content:
item.proposingGroup.getShownValue()

All the examples so far illustrated keywords found within fields. The following example matches a keyword in a comment. Template Agenda.py contains these comments:

Performing the following ogrep produces this result:

~/projets/appy/bin/ogrep.py is.mpty Agenda.odt -vv
 
:: 6 matches for Agenda.odt ::
 
  Match::on::note (tag office:annotation)
  Keyword(s) (regex): is.mpty
  By Auteur inconnu on 2014-05-01T19:05:06
 
  Content:
 
do text if not tool.isEmpty('logo')
from document(at=tool.path('logo'))
 
  Match::on::note (tag office:annotation)
  Keyword(s) (regex): is.mpty
  By Auteur inconnu on 2014-05-01T19:10:58
  
  Content:
 
do text if not tool.isEmpty('organization')

...

This example illustrates another element: the keyword is, by default, interpreted as a regular expression. The dot matching anything, is.mpty matches isEmpty, found 6 times in the POD template (the result was truncated to the 2 first results for conciseness).

If you want the keyword to be interpreted as a strict string, use option -s:

~/projets/appy/bin/ogrep.py is.mpty Agenda.odt -vv -s
No match found.

The hereabove examples were about searching for keywords within a single POD template. It is also possible to search (and replace, see below) for keywords, recursively, within all POD templates found in a folder hierarchy, lie in this example.

./ogrep.py "search" ../../HubSessions
../../HubSessions/pod/Revenues.ods matches 1 time(s).
../../HubSessions/pod/Tasks.ods matches 2 time(s).
../../HubSessions/pod/sub/Items.ods matches 2 time(s).
../../HubSessions/pod/Items.odt matches 3 time(s).
../../HubSessions/Meetings.odt matches 3 time(s).
../../HubSessions/pod/a/b/c/Tasks.odt matches 3 time(s).

Also note that ogrep works fine with ODT as well as ODS POD templates.

Performing replacements

Performing a find / replace with ogrep is as simple as adding option -r repl to an ogrep command. Before performing such a delicate operation, a dry run mode has been implemented via option -d. Suppose you want to replace string search_title with the more elegant camelCased searchTitle. You may test it with this command:

./ogrep.py search_title ../../HubSessions -r searchTitle -vv -d

Every match would look like this:

:: 1 match for ../../HubSessions/pod/Revenues.ods ::
 
  Match::on::field (tag table:table-cell)
  Keyword(s) (regex): search_title  
  These changes would have been done:
 
- '%s — Extrait du %s' % (search.translated or _('search_title'), d(now, withHour=False))
?                                                       ^^
 
+ '%s — Extrait du %s' % (search.translated or _('searchTitle'), d(now, withHour=False))
?                                                       ^

By default, differences between the POD zones before or after having performed the replacement are outputed using the standard diff syntax. If you prefer colors, use the -n option:

./ogrep.py search_title ../../HubSessions -r searchTitle -vv -d -n

Here is the result:

In order to perform the true replacement, simply remove the -d option.

Reference

All program options are documented hereafter.

usage: ogrep.py [-h] [-r REPL] [-v] [-vv] [-d] [-c] [-s] [-n] keyword path
 
positional arguments:
  keyword               is the regular expression (or string if -s) to search within the file(s). From the
                        command-line, special chars possibly included in the keyword (and also the
                        replacement string, if passed) must be escaped. For example, cus(tom)_(agenda) must
                        be written as cus\(tom\)_\(agenda\). \2_\1 must be written as \\2_\\1.
  path                  is the path to a file or folder. If a file is passed, you must pass the path to an
                        ODF file (odt or ods). ogrep will be run on this file only. If a folder is passed,
                        ogrep will be run on all ODF files found in this folder and sub-folders.
optional arguments:
  -h, --help            show this help message and exit
  -r REPL, --repl REPL  if this replacement string is passed, within matched ODF files, the keyword will be
                        replaced with it. The replacement string may contain references to groups possibly
                        defined within the "keyword" regular expression, but only via the "\" notation.
  -v, --verbose         dump more info about the find/replace zones (only available if option "-c" is not
                        used).
  -vv                   similar to -v, must with even more *v*erbosity.
  -d, --dry-run         in the context of a replacement, if this option is set, no replacement will actually
                        be performed, but output will give info (detailed if -v, minimal if not) about what
                        replacements would have been performed without this option.
  -c, --in-content      if unset, the find/replace process will only apply to the POD-controlled zones of
                        POD template(s): notes and fields. If the option is set, it will occur in any part
                        of the template(s).
  -s, --as-string       by default, keyword is interpreted as a regular expression. If you want it to be a
                        plain *s*tring instead, set this option.
  -n, --nice            if set in conjunction with -v or -vv, changes (being effective or potential,
                        depending on -d) will be colorized. Else, changes will be rendered as standard diff