What did you do last week, month, year?

Comfortably gather status report data (e.g. list of committed changes) for given week, month, quarter, year or selected date range. By default all available stats for this week are reported. Detailed documentation available at

The stats module contains the core of the stats gathering functionality. Some basic functionality like exceptions, config, user and date handling is placed in the base module. Generic utilities can be found in the utils module. Option parsing and other command line stuff resides in the cli module.


Stats & StatsGroup, the core of the data gathering

class did.stats.EmptyStats(option, name=None, parent=None, user=None)

Custom stats group for header & footer


Nothing to do for empty stats


Name only for empty stats

class did.stats.EmptyStatsGroup(option, name=None, parent=None, user=None)

Header & Footer stats group

class did.stats.Stats(option, name=None, parent=None, user=None, options=None)

General statistics


Add option for self to the parser group object.


Check the stats if enabled.

dest = None

Check whether we’re enabled (or if parent is).


Fetch the stats (to be implemented by respective class).


Show summary header.


Merge another stats.

property name

Use the first line of docs string unless name set.

option = None
parent = None

Display indented statistics.

stats = None
class did.stats.StatsGroup(option, name=None, parent=None, user=None, options=None)

Stats group


Add option group and all children options.


Check all children stats.


Stats groups do not fetch anything


Merge all children stats.

order = 500

List all children stats.

class did.stats.StatsGroupPlugin(name, bases, attrs)
ignore = {'EmptyStatsGroup', 'StatsGroup', 'StatsGroupPlugin', 'UserStats'}
registry = {}
class did.stats.UserStats(user=None, options=None, config=None)

User statistics in one place


Add options for each stats group.


Create a StatsGroup instance for each configured plugin


Config, Date, User and Exceptions

class did.base.Config(config=None, path=None)

User config file

property email

User email(s)

static example()

Return config example

item(section, it)

Return content of given item in selected section

parser = None
static path()

Detect config file path

property plugins

Custom plugins

property quarter

The first month of the quarter, 1 by default

section(section, skip=['type', 'order'])

Return section items, skip selected (type/order by default)


Return all sections (optionally of given kind only)

property width

Maximum width of the report

exception did.base.ConfigError

Stats configuration problem

exception did.base.ConfigFileError

Problem with the config file

class did.base.Date(date=None)

Date parsing for common word formats

static last_month()

Return start and end date of this month.

static last_quarter()

Return start and end date of this quarter.

static last_week()

Return start and end date of the last week.

static last_year()

Return start and end date of the last year

static period(argument)

Detect desired time period for the argument

static this_month()

Return start and end date of this month.

static this_quarter()

Return start and end date of this quarter.

static this_week()

Return start and end date of the current week.

static this_year()

Return start and end date of this year

exception did.base.GeneralError

General stats error

exception did.base.OptionError

Invalid command line

exception did.base.ReportError

Report generation error

class did.base.User(email, stats=None)

User information

The User object holds name, login and email which are used for performing queries by individual plugins. This information is parsed from given email address. Both short & full email format are supported:
Name Surname <>

In addition, it’s possible to provide email and login aliases for individual stats. This is useful if you use different email/login for different services. The syntax consists of stats: login or stats: email pairs appended at the end of the email address:; bz:; gh: githublogin

Use config section name to identify stats where given alias should be used. The exactly same syntax can be used both in the config file and on the command line. Finally it’s also possible to include the alias directly in the respective config section:

type = github
url =
login = psss
alias(aliases, stats)

Apply the login/email alias if configured.


Create a user copy with alias enabled for given stats.


Logging, config, constants & utilities

class did.utils.Coloring(*args, **kwargs)

Coloring configuration


True if coloring is currently enabled


Get the current color mode


Set the coloring mode

If enabled, some objects (like case run Status) are printed in color to easily spot failures, errors and so on. By default the feature is enabled when script is attached to a terminal. Possible values are:

COLOR=0 ... COLOR_OFF .... coloring disabled
COLOR=1 ... COLOR_ON ..... coloring enabled
COLOR=2 ... COLOR_AUTO ... if terminal attached (default)

Environment variable COLOR can be used to set up the coloring to the desired mode without modifying code.

class did.utils.Logging(name='did')

Logging Configuration

COLORS = {4: 'magenta', 7: 'cyan', 10: 'green', 20: 'blue', 30: 'yellow', 40: 'red'}
class ColoredFormatter(fmt=None, datefmt=None, style='%')

Custom color formatter for logging


Format the specified record as text.

The record’s attribute dictionary is used as the operand to a string formatting operation which yields the returned string. Before formatting the dictionary, a couple of preparatory steps are carried out. The message attribute of the record is computed using LogRecord.getMessage(). If the formatting string uses the time (as determined by a call to usesTime(), formatTime() is called to format the event time. If there is exception information, it is formatted using formatException() and appended to the message.

MAPPING = {0: 30, 1: 20, 2: 10, 3: 7, 4: 4, 5: 1}

Get the current log level


Set the default log level

If the level is not specified environment variable DEBUG is used with the following meaning:

DEBUG=0 ... LOG_WARN (default)
DEBUG=5 ... LOG_ALL (log all messages)

Transliterate special unicode characters into pure ascii

did.utils.color(text, color=None, background=None, light=False, enabled=True)

Return text in desired color if coloring enabled

Available colors: black red green yellow blue magenta cyan white. Alternatively color can be prefixed with “light”, e.g. lightgreen.


Show text as a header., newline=True)

Log provided info message to the standard error output

did.utils.item(text, level=0, options=None)

Print indented item.

did.utils.listed(items, singular=None, plural=None, max=None, quote='')

Convert an iterable into a nice, human readable list or description:

listed(range(1)) .................... 0
listed(range(2)) .................... 0 and 1
listed(range(3), quote='"') ......... "0", "1" and "2"
listed(range(4), max=3) ............. 0, 1, 2 and 1 more
listed(range(5), 'number', max=3) ... 0, 1, 2 and 2 more numbers
listed(range(6), 'category') ........ 6 categories
listed(7, "leaf", "leaves") ......... 7 leaves

If singular form is provided but max not set the description-only mode is activated as shown in the last two examples. Also, an int can be used in this case to get a simple inflection functionality.

did.utils.load_components(*paths, **kwargs)

Load all components on the paths

Each path should be a package or module. All components beneath a path are loaded. This method works whether the package or module is on the filesystem or in an .egg. If it’s in an egg, the egg must already be on the PYTHONPATH.


paths (str): A package or module to load

Keyword Args:
include (str): A regular expression of packages and modules to

include. Defaults to ‘.*’

exclude (str): A regular expression of packges and modules to

exclude. Defaults to ‘test’

continue_on_error (bool): If True, continue importing even if

something raises an ImportError. If False, raise the first ImportError.


int: The total number of modules loaded.




Naively pluralize words

did.utils.shorted(text, width=79)

Shorten text, make sure it’s not cut in the middle of a word

did.utils.split(values, separator=re.compile('[ ,]+'))

Convert space-or-comma-separated values into a single list

Common use case for this is merging content of options with multiple values allowed into a single list of strings thus allowing any of the formats below and converts them into [‘a’, ‘b’, ‘c’]:

--option a --option b --option c ... ['a', 'b', 'c']
--option a,b --option c ............ ['a,b', 'c']
--option 'a b c' ................... ['a b c']

Accepts both string and list. By default space and comma are used as value separators. Use any regular expression for custom separator.


Command line interface for did

This module takes care of processing command line options and running the main loop which gathers all individual stats.

class did.cli.Options(arguments=None)

Command line options parser


Perform additional check for given options


Parse the options.


Parse options, gather stats and show the results

Takes optional parameter arguments which can be either command line string or list of options. This is very useful for testing purposes. Function returns a tuple of the form:

([user_stats], team_stats)

with the list of all gathered stats objects.