Skip to content
Snippets Groups Projects
Commit 8202406f authored by Sebastien Helleu's avatar Sebastien Helleu
Browse files

Make options title/repository optional, add option -m/--max, major code...

Make options title/repository optional, add option -m/--max, major code cleanup, full PEP8 compliance

All changes:
- chart title and repository are now optional and must be set with two
  new options:
    -t, --title (default: hardcoded in script, depends on the chart)
    -r, --repo (default: '.', ie current directory)
- add option -m/--max: max different entries in chart: after this
  number, an entry is counted in "others" (only for charts "authors" and
  "files_type")
- if filename is "-", display SVG content on standard output
- import the new division operator and the print function
- replace %-formatting with .format()
- full PEP8 compliance
- major code cleanup: add comments, dynamically build the names for week
  days and months, use a dynamic name for functions building a chart.
parent 9a367bbb
No related branches found
No related tags found
No related merge requests found
Loading
@@ -18,20 +18,26 @@ installed with this command:
Loading
@@ -18,20 +18,26 @@ installed with this command:
   
Note: cairosvg is required to generate PNG files. Note: cairosvg is required to generate PNG files.
   
## Usage
See output of command:
$ python gitchart.py -h
## Examples ## Examples
   
Generate pie chart with authors: Generate pie chart with authors:
   
$ python gitchart.py authors "Git authors" /path/to/gitrepo/ authors.svg $ python gitchart.py -t "Git authors on project X" -r /path/to/gitrepo/ authors authors.svg
   
Generate bar chart with commits by year: Generate bar chart with commits by year:
   
$ python gitchart.py commits_year "Git commits by year" /path/to/gitrepo/ commits_year.svg $ python gitchart.py -r /path/to/gitrepo/ commits_year commits_year.svg
   
Generate bar chart with commits by version (tag): Generate bar chart with commits by version (tag):
   
$ cd /path/to/gitrepo/ $ cd /path/to/gitrepo/
$ git tag | python /path/to/gitchart.py commits_version "Git commits by version" . /tmp/commits_version.svg $ git tag | python /path/to/gitchart.py commits_version /tmp/commits_version.svg
   
## Demo ## Demo
   
Loading
Loading
Loading
@@ -21,7 +21,6 @@
Loading
@@ -21,7 +21,6 @@
# (http://pygal.org). # (http://pygal.org).
# #
# Charts supported: # Charts supported:
#
# | | | format of data # | | | format of data
# name | chart | description | expected (stdin) # name | chart | description | expected (stdin)
# -------------------+-------+---------------------------+----------------- # -------------------+-------+---------------------------+-----------------
Loading
@@ -36,7 +35,10 @@
Loading
@@ -36,7 +35,10 @@
# files_type | pie | files by type (extension) | - # files_type | pie | files by type (extension) | -
# #
   
from __future__ import division, print_function
import argparse import argparse
import datetime
import os import os
import pygal import pygal
import re import re
Loading
@@ -45,164 +47,178 @@ import subprocess
Loading
@@ -45,164 +47,178 @@ import subprocess
import sys import sys
import traceback import traceback
   
NAME = 'gitchart.py' VERSION = '0.5'
VERSION = '0.4'
AUTHOR = 'Sebastien Helleu <flashcode@flashtux.org>'
   
   
class GitChart: class GitChart:
"""Generate a git stat chart.""" """Generate a git stat chart."""
   
def __init__(self): charts = {
self.charts = {'authors': self._chart_authors, 'authors': 'Authors',
'commits_hour': self._chart_commits_hour, 'commits_hour': 'Commits by hour of day',
'commits_hour_week': self._chart_commits_hour_week, 'commits_hour_week': 'Commits by hour of week',
'commits_day': self._chart_commits_day, 'commits_day': 'Commits by day of week',
'commits_month': self._chart_commits_month, 'commits_month': 'Commits by month of year',
'commits_year': self._chart_commits_year, 'commits_year': 'Commits by year',
'commits_year_month': self._chart_commits_year_month, 'commits_year_month': 'Commits by year/month',
'commits_version': self._chart_commits_version, 'commits_version': 'Commits by version',
'files_type': self._chart_files_type, 'files_type': 'Files by extension',
} }
# define a Pygal style with transparent background and custom colors weekdays = [datetime.date(2001, 1, day).strftime('%a')
self.style = pygal.style.Style( for day in range(1, 8)]
background='transparent', months = [datetime.date(2001, month, 1).strftime('%b')
plot_background='transparent', for month in range(1, 13)]
foreground='rgba(0, 0, 0, 0.9)', # Pygal style with transparent background and custom colors
foreground_light='rgba(0, 0, 0, 0.6)', style = pygal.style.Style(background='transparent',
foreground_dark='rgba(0, 0, 0, 0.2)', plot_background='transparent',
opacity_hover='.4', foreground='rgba(0, 0, 0, 0.9)',
colors=('#9999ff', '#8cedff', '#b6e354', '#feed6c', '#ff9966', '#ff0000', '#ff00cc', '#899ca1', '#bf4646')) foreground_light='rgba(0, 0, 0, 0.6)',
self.days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] foreground_dark='rgba(0, 0, 0, 0.2)',
self.months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] opacity_hover='.4',
colors=('#9999ff', '#8cedff', '#b6e354',
'#feed6c', '#ff9966', '#ff0000',
'#ff00cc', '#899ca1', '#bf4646'))
def __init__(self, chart_name, title=None, repository='.', output=None,
max_entries=20, in_data=None):
self.chart_name = chart_name
self.title = title if title is not None else self.charts[chart_name]
self.title = None
self.repository = repository
self.output = output
self.max_entries = max_entries
self.in_data = in_data
   
def _git_command(self, repository, command, command2=None): def _git_command(self, command1, command2=None):
"""Execute one or two piped git commands return lines as a list.""" """
Execute one or two piped git commands.
Return the output lines as a list.
"""
if command2: if command2:
# pipe the two commands and return output # pipe the two commands and return output
p1 = subprocess.Popen(command, stdout=subprocess.PIPE, cwd=repository) p1 = subprocess.Popen(command1, stdout=subprocess.PIPE,
p2 = subprocess.Popen(command2, stdin=p1.stdout, stdout=subprocess.PIPE) cwd=self.repository)
p2 = subprocess.Popen(command2, stdin=p1.stdout,
stdout=subprocess.PIPE)
p1.stdout.close() p1.stdout.close()
return p2.communicate()[0].decode('utf-8').strip().split('\n') return p2.communicate()[0].decode('utf-8').strip().split('\n')
else: else:
# execute a single git command and return output # execute a single git cmd and return output
p = subprocess.Popen(command, stdout=subprocess.PIPE, cwd=repository) p = subprocess.Popen(command1, stdout=subprocess.PIPE,
cwd=self.repository)
return p.communicate()[0].decode('utf-8').strip().split('\n') return p.communicate()[0].decode('utf-8').strip().split('\n')
   
def _generate_bar_chart(self, title, data, output, sorted_keys=None, x_labels=None, x_label_rotation=0): def _generate_bar_chart(self, data, sorted_keys=None, x_labels=None,
x_label_rotation=0):
"""Generate a bar chart.""" """Generate a bar chart."""
bar_chart = pygal.Bar(style=self.style, show_legend=False, x_label_rotation=x_label_rotation, label_font_size=12) bar_chart = pygal.Bar(style=self.style, show_legend=False,
bar_chart.title = title x_label_rotation=x_label_rotation,
label_font_size=12)
bar_chart.title = self.title
if not sorted_keys: if not sorted_keys:
sorted_keys = sorted(data) sorted_keys = sorted(data)
bar_chart.x_labels = x_labels if x_labels else sorted_keys bar_chart.x_labels = x_labels if x_labels else sorted_keys
bar_chart.add('', [data[n] for n in sorted_keys]) bar_chart.add('', [data[n] for n in sorted_keys])
self._render(bar_chart, output) self._render(bar_chart)
   
def _chart_authors(self, title, repository, output, in_data=None, limit=20): def _chart_authors(self):
"""Generate pie chart with git authors.""" """Generate pie chart with git authors."""
# format of lines in stdout: 278 John Doe # format of lines in stdout: 278 John Doe
stdout = self._git_command(repository, ['git', 'log', '--pretty=short'], ['git', 'shortlog', '-sn']) stdout = self._git_command(['git', 'log', '--pretty=short'],
pie_chart = pygal.Pie(style=self.style, truncate_legend=100, value_font_size=12) ['git', 'shortlog', '-sn'])
pie_chart.title = title or 'Authors' pie_chart = pygal.Pie(style=self.style, truncate_legend=100,
value_font_size=12)
pie_chart.title = self.title
count = 0 count = 0
count_others = 0 count_others = 0
sum_others = 0 sum_others = 0
for author in stdout: for author in stdout:
(number, name) = author.strip().split('\t', 1) (number, name) = author.strip().split('\t', 1)
count += 1 count += 1
if limit <= 0 or count <= limit: if self.max_entries <= 0 or count <= self.max_entries:
pie_chart.add('%s (%s)' % (name, number), int(number)) pie_chart.add(name + ' ({0})'.format(number), int(number))
else: else:
count_others += 1 count_others += 1
sum_others += int(number) sum_others += int(number)
if count_others: if count_others:
pie_chart.add('%d others (%d)' % (count_others, sum_others), sum_others) pie_chart.add('{0} others ({1})'.format(count_others, sum_others),
self._render(pie_chart, output) sum_others)
self._render(pie_chart)
return True return True
   
def _chart_commits_hour(self, title, repository, output, in_data=None): def _chart_commits_hour(self):
"""Generate bar chart with commits by hour of day.""" """Generate bar chart with commits by hour of day."""
# format of lines in stdout: 2013-03-15 18:27:55 +0100 # format of lines in stdout: 2013-03-15 18:27:55 +0100
stdout = self._git_command(repository, ['git', 'log', '--date=iso', '--pretty=format:%ad']) stdout = self._git_command(['git', 'log', '--date=iso',
commits = {} '--pretty=format:%ad'])
commits = {'{0:02d}'.format(hour): 0 for hour in range(0, 24)}
for line in stdout: for line in stdout:
hour = line.split()[1].split(':')[0] commits[line.split()[1].split(':')[0]] += 1
commits[hour] = commits.get(hour, 0) + 1 self._generate_bar_chart(commits)
# ensure each hour of day has a value (set 0 for hours without commits)
for i in range(0, 24):
hour = '%02d' % i
if not hour in commits:
commits[hour] = 0
self._generate_bar_chart(title or 'Commits by hour of day', commits, output)
return True return True
   
def _chart_commits_hour_week(self, title, repository, output, in_data=None): def _chart_commits_hour_week(self):
"""Generate dot chart with commits by hour of week.""" """Generate dot chart with commits by hour of week."""
# format of lines in stdout: Fri, 15 Mar 2013 18:27:55 +0100 # format of lines in stdout: Fri, 15 Mar 2013 18:27:55 +0100
stdout = self._git_command(repository, ['git', 'log', '--date=rfc', '--pretty=format:%ad']) stdout = self._git_command(['git', 'log', '--date=rfc',
commits = {} '--pretty=format:%ad'])
commits = {day: {'{0:02d}'.format(hour): 0 for hour in range(0, 24)}
for day in self.weekdays}
for line in stdout: for line in stdout:
items = line.split() wday, _, _, _, hour, _ = line.split()
day = items[0][:-1] commits[wday[:-1]][hour.split(':')[0]] += 1
hour = items[-2].split(':')[0]
if not day in commits:
commits[day] = {}
commits[day][hour] = commits[day].get(hour, 0) + 1
dot_chart = pygal.Dot(style=self.style) dot_chart = pygal.Dot(style=self.style)
dot_chart.title = title or 'Commits by hour of week' dot_chart.title = self.title
dot_chart.x_labels = ['%02d' % hour for hour in range(0, 24)] dot_chart.x_labels = ['{0:02d}'.format(hour) for hour in range(0, 24)]
for day in sorted(commits, key=self.days.index): for day in self.weekdays:
# ensure each hour of day has a value (set 0 for hours without commits)
for i in range(0, 24):
hour = '%02d' % i
if not hour in commits[day]:
commits[day][hour] = 0
dot_chart.add(day, commits[day]) dot_chart.add(day, commits[day])
self._render(dot_chart, output) self._render(dot_chart)
return True return True
   
def _chart_commits_day(self, title, repository, output, in_data=None): def _chart_commits_day(self):
"""Generate bar chart with commits by day of week.""" """Generate bar chart with commits by day of week."""
# format of lines in stdout: Fri, 15 Mar 2013 18:27:55 +0100 # format of lines in stdout: Fri, 15 Mar 2013 18:27:55 +0100
stdout = self._git_command(repository, ['git', 'log', '--date=rfc', '--pretty=format:%ad']) stdout = self._git_command(['git', 'log', '--date=rfc',
commits = {} '--pretty=format:%ad'])
commits = {day: 0 for day in self.weekdays}
for line in stdout: for line in stdout:
day = line.split(',')[0] commits[line.split(',')[0]] += 1
commits[day] = commits.get(day, 0) + 1 self._generate_bar_chart(commits,
self._generate_bar_chart(title or 'Commits by day of week', commits, output, sorted_keys=sorted(commits, key=self.days.index)) sorted_keys=sorted(commits,
key=self.weekdays.index))
return True return True
   
def _chart_commits_month(self, title, repository, output, in_data=None): def _chart_commits_month(self):
"""Generate bar chart with commits by month of year.""" """Generate bar chart with commits by month of year."""
# format of lines in stdout: 2013-03-15 # format of lines in stdout: 2013-03-15
stdout = self._git_command(repository, ['git', 'log', '--date=short', '--pretty=format:%ad']) stdout = self._git_command(['git', 'log', '--date=short',
commits = {} '--pretty=format:%ad'])
commits = {month: 0 for month in self.months}
for line in stdout: for line in stdout:
month = int(line.split('-')[1]) month = int(line.split('-')[1]) - 1
commits[month] = commits.get(month, 0) + 1 commits[self.months[month]] += 1
# transform month number to name (1 => Jan, 2 => Feb,...) self._generate_bar_chart(commits,
commits2 = {} sorted_keys=sorted(commits,
for month in commits: key=self.months.index))
commits2[self.months[month - 1]] = commits[month]
self._generate_bar_chart(title or 'Commits by month of year', commits2, output, sorted_keys=sorted(commits2, key=self.months.index))
return True return True
   
def _chart_commits_year(self, title, repository, output, in_data=None): def _chart_commits_year(self):
"""Generate bar chart with commits by year.""" """Generate bar chart with commits by year."""
# format of lines in stdout: 2013-03-15 # format of lines in stdout: 2013-03-15
stdout = self._git_command(repository, ['git', 'log', '--date=short', '--pretty=format:%ad']) stdout = self._git_command(['git', 'log', '--date=short',
'--pretty=format:%ad'])
commits = {} commits = {}
for line in stdout: for line in stdout:
year = line.split('-')[0] year = line.split('-')[0]
commits[year] = commits.get(year, 0) + 1 commits[year] = commits.get(year, 0) + 1
self._generate_bar_chart(title or 'Commits by year', commits, output) self._generate_bar_chart(commits)
return True return True
   
def _chart_commits_year_month(self, title, repository, output, in_data=None): def _chart_commits_year_month(self):
"""Generate bar chart with commits by year/month.""" """Generate bar chart with commits by year/month."""
# format of lines in stdout: 2013-03-15 # format of lines in stdout: 2013-03-15
stdout = self._git_command(repository, ['git', 'log', '--date=short', '--pretty=format:%ad']) stdout = self._git_command(['git', 'log', '--date=short',
'--pretty=format:%ad'])
commits = {} commits = {}
min_date = 999999 min_date = 999999
max_date = 0 max_date = 0
Loading
@@ -211,12 +227,12 @@ class GitChart:
Loading
@@ -211,12 +227,12 @@ class GitChart:
date = (int(year) * 100) + int(month) date = (int(year) * 100) + int(month)
min_date = min(min_date, date) min_date = min(min_date, date)
max_date = max(max_date, date) max_date = max(max_date, date)
year_month = '%s-%s' % (year, month) year_month = '{0}-{1}'.format(year, month)
commits[year_month] = commits.get(year_month, 0) + 1 commits[year_month] = commits.get(year_month, 0) + 1
if min_date != 999999: if min_date != 999999:
date = min_date date = min_date
while date < max_date: while date < max_date:
year_month = '%04d-%02d' % (date / 100, date % 100) year_month = '{0:04d}-{1:02d}'.format(date // 100, date % 100)
commits[year_month] = commits.get(year_month, 0) commits[year_month] = commits.get(year_month, 0)
if date % 100 == 12: if date % 100 == 12:
# next year, for example: 201312 => 201401 (+89) # next year, for example: 201312 => 201401 (+89)
Loading
@@ -224,119 +240,133 @@ class GitChart:
Loading
@@ -224,119 +240,133 @@ class GitChart:
else: else:
date += 1 date += 1
x_labels = sorted(commits) x_labels = sorted(commits)
# if there are more than 20 commits, keep one x label on 10 (starting from the end) # if there are more than 20 commits, keep one x label on 10
# (starting from the end)
if len(commits) > 20: if len(commits) > 20:
n = 0 n = 0
for i in range(len(x_labels) - 1, -1, -1): for i in range(len(x_labels) - 1, -1, -1):
if n % 10 != 0: if n % 10 != 0:
x_labels[i] = '' x_labels[i] = ''
n += 1 n += 1
self._generate_bar_chart(title or 'Commits by year/month', commits, output, x_labels=x_labels, x_label_rotation=45) self._generate_bar_chart(commits, x_labels=x_labels,
x_label_rotation=45)
return True return True
   
def _chart_commits_version(self, title, repository, output, in_data=None): def _chart_commits_version(self):
"""Generate bar chart with commits by version (tag).""" """Generate bar chart with commits by version (tag)."""
if not in_data: if not self.in_data:
return False return False
commits = {} commits = {}
oldtag = '' oldtag = ''
for tag in in_data.strip().split('\n'): for tag in self.in_data.strip().split('\n'):
commits[tag] = len(self._git_command(repository, ['git', 'log', '%s..%s' % (oldtag, tag) if oldtag else tag, '--pretty=oneline'])) # transform version to keep only digits with period as separator,
# examples:
# release-0-0-1 => 0.0.1
# v0.3.0 => 0.3.0
tag2 = re.sub('([^0-9]+)', ' ', tag).strip().replace(' ', '.')
commits[tag2] = len(
self._git_command(['git', 'log',
oldtag + '..' + tag if oldtag else tag,
'--pretty=oneline']))
oldtag = tag oldtag = tag
# transform version to keep only digits with period as separator, examples: self._generate_bar_chart(commits, x_label_rotation=90)
# release-0-0-1 => 0.0.1
# v0.3.0 => 0.3.0
commits2 = {}
for tag in commits:
commits2[re.sub('([^0-9]+)', ' ', tag).strip().replace(' ', '.')] = commits[tag]
self._generate_bar_chart(title or 'Commits by version', commits2, output, x_label_rotation=90)
return True return True
   
def _chart_files_type(self, title, repository, output, in_data=None, limit=20): def _chart_files_type(self):
"""Generate pie chart with files by extension.""" """Generate pie chart with files by extension."""
# format of lines in stdout: path/to/file.c # format of lines in stdout: path/to/file.c
stdout = self._git_command(repository, ['git', 'ls-tree', '-r', '--name-only', 'HEAD']) stdout = self._git_command(['git', 'ls-tree', '-r', '--name-only',
'HEAD'])
extensions = {} extensions = {}
for line in stdout: for line in stdout:
ext = os.path.splitext(line)[1] ext = os.path.splitext(line)[1]
if not ext: if not ext:
ext = '(no extension)' ext = '(no extension)'
extensions[ext] = extensions.get(ext, 0) + 1 extensions[ext] = extensions.get(ext, 0) + 1
pie_chart = pygal.Pie(style=self.style, truncate_legend=100, value_font_size=12) pie_chart = pygal.Pie(style=self.style, truncate_legend=100,
pie_chart.title = title or 'Files by extension' value_font_size=12)
pie_chart.title = self.title
count = 0 count = 0
count_others = 0 count_others = 0
sum_others = 0 sum_others = 0
for ext in sorted(extensions, key=extensions.get, reverse=True): for ext in sorted(extensions, key=extensions.get, reverse=True):
count += 1 count += 1
if limit <= 0 or count <= limit: if self.max_entries <= 0 or count <= self.max_entries:
pie_chart.add('%s (%d)' % (ext, extensions[ext]), extensions[ext]) pie_chart.add(ext + ' ({0})'.format(extensions[ext]),
extensions[ext])
else: else:
count_others += 1 count_others += 1
sum_others += extensions[ext] sum_others += extensions[ext]
if count_others: if count_others:
pie_chart.add('%d others (%d)' % (count_others, sum_others), sum_others) pie_chart.add('{0} others ({1})'.format(count_others, sum_others),
self._render(pie_chart, output) sum_others)
self._render(pie_chart)
return True return True
   
def _render(self, chart, output): def _render(self, chart):
if output.endswith('.png'): if self.output == '-':
chart.render_to_png(output) # display SVG on stdout
print(chart.render())
elif self.output.lower().endswith('.png'):
# write PNG in file
chart.render_to_png(self.output)
else: else:
chart.render_to_file(output) # write SVG in file
chart.render_to_file(self.output)
   
def generate(self, chart, title, repository, output, in_data=None): def generate(self):
"""Generate a chart, and return True if OK, False if error.""" """Generate a chart, and return True if OK, False if error."""
if chart not in self.charts:
return False
try: try:
return self.charts[chart](title, repository, output, in_data) # call function to build chart (name of function is dynamic)
return getattr(self, '_chart_' + self.chart_name)()
except: except:
print ('Error:\n%s' % traceback.print_exc()) traceback.print_exc()
return False return False
   
   
# build the GitChart object if __name__ == "__main__":
chart = GitChart() # parse command line arguments
parser = argparse.ArgumentParser(
# parse command line arguments formatter_class=argparse.ArgumentDefaultsHelpFormatter,
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description='Generate statistic charts for a git repository.',
description=''' epilog='Return value: 0 = success, 1 = error.')
%s %s (C) 2013 %s parser.add_argument('-t', '--title',
help='override the default chart title')
parser.add_argument('-r', '--repo', default='.',
help='directory with git repository')
parser.add_argument('-m', '--max', type=int, default=20,
help='max different entries in chart: after this '
'number, an entry is counted in "others" (only for '
'charts "authors" and "files_type"), 0=unlimited')
parser.add_argument('chart', metavar='chart',
choices=sorted(GitChart.charts),
help='name of chart, one of: ' +
', '.join(sorted(GitChart.charts)))
parser.add_argument('output', help='output file (svg or png), special '
'value "-" displays SVG content on standard output')
parser.add_argument('-v', '--version', action='version', version=VERSION)
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
args = parser.parse_args(sys.argv[1:])
   
Generate statistic charts for a git repository. # read data on standard input
''' % (NAME, VERSION, AUTHOR), in_data = ''
epilog=''' while True:
Return value: inr, outr, exceptr = select.select([sys.stdin], [], [], 0)
0: chart successfully generated if not inr:
1: error break
''') data = os.read(sys.stdin.fileno(), 4096)
parser.add_argument('chart', metavar='chart', choices=chart.charts.keys(), if not data:
help='name of chart, one of: %s' % ', '.join(chart.charts)) break
parser.add_argument('title', help='title of chart') in_data += data.decode('utf-8')
parser.add_argument('repository', help='directory with git repository')
parser.add_argument('output', help='output file (/path/to/file.svg or /path/to/file.png)')
parser.add_argument('-v', '--version', action='version', version=VERSION)
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
args = parser.parse_args(sys.argv[1:])
# read data on standard input
in_data = ''
while True:
inr, outr, exceptr = select.select([sys.stdin], [], [], 0)
if not inr:
break
data = os.read(sys.stdin.fileno(), 4096)
if not data:
break
in_data += data.decode('utf-8')
   
# generate chart # generate chart
if chart.generate(args.chart, args.title, args.repository, args.output, in_data=in_data): chart = GitChart(args.chart, args.title, args.repo, args.output, args.max,
sys.exit(0) in_data)
if chart.generate():
sys.exit(0)
   
# error # error
print('Failed to generate chart: %s' % args) print('ERROR: failed to generate chart:', vars(args))
sys.exit(1) sys.exit(1)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment