You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by ac...@apache.org on 2012/12/27 20:01:24 UTC

[1/8] [#5518] Initial version of Bitergia Metrics product integrated inside Allura. Still several issued need to be polished.

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b129d544/AlluraBitergiaMetrics/bitergiametrics/tasks.py
----------------------------------------------------------------------
diff --git a/AlluraBitergiaMetrics/bitergiametrics/tasks.py b/AlluraBitergiaMetrics/bitergiametrics/tasks.py
new file mode 100644
index 0000000..b0098b1
--- /dev/null
+++ b/AlluraBitergiaMetrics/bitergiametrics/tasks.py
@@ -0,0 +1,17 @@
+import logging
+
+from pylons import c
+from allura.lib.decorators import task
+
+log = logging.getLogger(__name__)
+
+@task
+def launch_bicho(shortname):
+    log.info("TASK: Launching Bicho tool", shortname);
+#    from forgediscussion import model as DM
+#    forum = DM.Forum.query.get(
+#        shortname=shortname, app_config_id=c.app.config._id)
+#    if forum is None:
+#        log.error("Error looking up forum: %r", shortname)
+#        return
+#    forum.update_stats()

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b129d544/AlluraBitergiaMetrics/bitergiametrics/templates/__init__.py
----------------------------------------------------------------------
diff --git a/AlluraBitergiaMetrics/bitergiametrics/templates/__init__.py b/AlluraBitergiaMetrics/bitergiametrics/templates/__init__.py
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b129d544/AlluraBitergiaMetrics/bitergiametrics/templates/metrics/bicho.html
----------------------------------------------------------------------
diff --git a/AlluraBitergiaMetrics/bitergiametrics/templates/metrics/bicho.html b/AlluraBitergiaMetrics/bitergiametrics/templates/metrics/bicho.html
new file mode 100644
index 0000000..fe174e2
--- /dev/null
+++ b/AlluraBitergiaMetrics/bitergiametrics/templates/metrics/bicho.html
@@ -0,0 +1,19 @@
+{% extends g.theme.master %}
+{% block title %}
+	{{c.project.name}} / {{c.app.config.options.mount_label}}
+{% endblock %} 
+{% block header%}
+	{{c.project.name}} / {{c.app.config.options.mount_label}}:
+	Bitergia Bicho Tool Config
+{% endblock %} 
+
+{% block content %}
+
+<form method="get">
+Now <input type="checkbox" name="when" value="now" checked>
+<input type=submit value="Launch bicho">
+</form>
+
+When: {{when}}
+
+{% endblock %}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b129d544/AlluraBitergiaMetrics/bitergiametrics/templates/metrics/index.html
----------------------------------------------------------------------
diff --git a/AlluraBitergiaMetrics/bitergiametrics/templates/metrics/index.html b/AlluraBitergiaMetrics/bitergiametrics/templates/metrics/index.html
new file mode 100644
index 0000000..9366036
--- /dev/null
+++ b/AlluraBitergiaMetrics/bitergiametrics/templates/metrics/index.html
@@ -0,0 +1,20 @@
+{% extends g.theme.master %}
+
+{% do g.register_app_css('metrics.css', compress=False) %}
+
+{% block title %}{{c.project.name}} / {{c.app.config.options.mount_label}}{% endblock %}
+
+{% block header %}{{c.project.name}} / {{c.app.config.options.mount_label}}{% endblock %}
+
+{% block content %}
+
+<p><h1>Allura Metrics</h1></p>
+
+<p>
+Proof of concept of how some parameters of the evolution of a software project can be visualized. 
+Allura integation. Visualization is still preliminary and incomplete. 
+</p>
+<div align=right>
+Powered by <a href="http://www.bitergia.com"><img src="{{g.app_static('bitergia-logo-peq.png')}}" border=0></a>
+</div>
+{% endblock %}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b129d544/AlluraBitergiaMetrics/bitergiametrics/templates/metrics/its_milestone0.html
----------------------------------------------------------------------
diff --git a/AlluraBitergiaMetrics/bitergiametrics/templates/metrics/its_milestone0.html b/AlluraBitergiaMetrics/bitergiametrics/templates/metrics/its_milestone0.html
new file mode 100644
index 0000000..1014a4b
--- /dev/null
+++ b/AlluraBitergiaMetrics/bitergiametrics/templates/metrics/its_milestone0.html
@@ -0,0 +1,37 @@
+{% extends g.theme.master %}
+
+{% do g.register_app_css('vizgrimoire.css', compress=False) %}
+{% block title %}{{c.project.name}} / {{c.app.config.options.mount_label}}{% endblock %}
+
+{% block header %}{{c.project.name}} / {{c.app.config.options.mount_label}}: Tickets{% endblock %}
+
+{% block content %}
+
+ <script type="text/javascript" src="{{g.app_static('jquery-1.7.1.min.js')}}"></script>
+
+    <div align=center>
+        <a href="{{g.app_static('allura/data/json/its-milestone0.json')}}">Tickets</a>
+    </div>
+    
+    <div>
+     <div style="float:right">
+      <h1>ENVISION</h1>
+      <div id="its-envision"></div>
+     </div>
+     <div>
+      <div id="its-open-flotr2" style="width:200px;height:200px"></div>
+      <div id="its-openers-flotr2" style="width:200px;height:200px"></div>
+      <div id="its-closed-flotr2" style="width:200px;height:200px"></div>
+      <div id="its-closers-flotr2" style="width:200px;height:200px"></div>
+     </div>
+    </div>
+      
+    <div id="report-config" data-its-data-file="{{g.app_static('allura/data/json/its-milestone0.json')}}"></div>
+      
+    <div align=right>
+        Powered by <a href="http://www.bitergia.com"><img src="{{g.app_static('bitergia-logo-peq.png')}}" border=0></a>
+    </div>
+    
+    <script type="text/javascript" src="{{g.app_static('vizgrimoire.min.js')}}"></script>    
+
+{% endblock %}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b129d544/AlluraBitergiaMetrics/bitergiametrics/templates/metrics/scm_milestone0.html
----------------------------------------------------------------------
diff --git a/AlluraBitergiaMetrics/bitergiametrics/templates/metrics/scm_milestone0.html b/AlluraBitergiaMetrics/bitergiametrics/templates/metrics/scm_milestone0.html
new file mode 100644
index 0000000..e59dbca
--- /dev/null
+++ b/AlluraBitergiaMetrics/bitergiametrics/templates/metrics/scm_milestone0.html
@@ -0,0 +1,37 @@
+{% extends g.theme.master %}
+
+{% do g.register_app_css('vizgrimoire.css', compress=False) %}
+{% block title %}{{c.project.name}} / {{c.app.config.options.mount_label}}{% endblock %}
+
+{% block header %}
+	{{c.project.name}} / {{c.app.config.options.mount_label}}: Source code
+{% endblock %}
+
+{% block content %}
+	<script type="text/javascript" src="{{g.app_static('jquery-1.7.1.min.js')}}"></script>
+
+    <div align=center>
+        <a href="{{g.app_static('allura/data/json/scm-milestone0.json')}}">SCM Data: Commits vs Committers!</a>
+    </div>
+    
+    <div>
+     <div style="float:right">
+      <h1>ENVISION</h1>
+      <div id="scm-envision"></div>
+     </div>
+     <div>
+      <div id="scm-commits-flotr2" style="width:200px;height:200px"></div>
+      <div id="scm-committers-flotr2" style="width:200px;height:200px"></div>
+      <div id="scm-files-flotr2" style="width:200px;height:200px"></div>
+      <div id="scm-branches-flotr2" style="width:200px;height:200px"></div>
+     </div>
+    </div>
+      
+    <div id="report-config" data-scm-data-file="{{g.app_static('allura/data/json/scm-milestone0.json')}}"></div>
+      
+    <div align=right>
+        Powered by <a href="http://www.bitergia.com"><img src="{{g.app_static('bitergia-logo-peq.png')}}" border=0></a>
+    </div>
+    
+    <script type="text/javascript" src="{{g.app_static('vizgrimoire.min.js')}}"></script>    
+{% endblock %}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b129d544/AlluraBitergiaMetrics/bitergiametrics/templates/metrics_widgets/__init__.py
----------------------------------------------------------------------
diff --git a/AlluraBitergiaMetrics/bitergiametrics/templates/metrics_widgets/__init__.py b/AlluraBitergiaMetrics/bitergiametrics/templates/metrics_widgets/__init__.py
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b129d544/AlluraBitergiaMetrics/bitergiametrics/templates/metrics_widgets/view_metrics.html
----------------------------------------------------------------------
diff --git a/AlluraBitergiaMetrics/bitergiametrics/templates/metrics_widgets/view_metrics.html b/AlluraBitergiaMetrics/bitergiametrics/templates/metrics_widgets/view_metrics.html
new file mode 100644
index 0000000..7289713
--- /dev/null
+++ b/AlluraBitergiaMetrics/bitergiametrics/templates/metrics_widgets/view_metrics.html
@@ -0,0 +1,8 @@
+{% import 'allura:templates/jinja_master/lib.html' as lib with context %}
+
+{{value.html_text|safe}}
+{{lib.related_artifacts(value)}}
+<em class="grid-19">
+Showing METRICS for the project
+</em>
+

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b129d544/AlluraBitergiaMetrics/bitergiametrics/tests/__init__.py
----------------------------------------------------------------------
diff --git a/AlluraBitergiaMetrics/bitergiametrics/tests/__init__.py b/AlluraBitergiaMetrics/bitergiametrics/tests/__init__.py
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b129d544/AlluraBitergiaMetrics/bitergiametrics/tests/functional/__init__.py
----------------------------------------------------------------------
diff --git a/AlluraBitergiaMetrics/bitergiametrics/tests/functional/__init__.py b/AlluraBitergiaMetrics/bitergiametrics/tests/functional/__init__.py
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b129d544/AlluraBitergiaMetrics/bitergiametrics/tests/functional/test_root.py
----------------------------------------------------------------------
diff --git a/AlluraBitergiaMetrics/bitergiametrics/tests/functional/test_root.py b/AlluraBitergiaMetrics/bitergiametrics/tests/functional/test_root.py
new file mode 100644
index 0000000..4f6a4b1
--- /dev/null
+++ b/AlluraBitergiaMetrics/bitergiametrics/tests/functional/test_root.py
@@ -0,0 +1,175 @@
+import datetime
+
+from ming.orm.ormsession import ThreadLocalORMSession
+
+from alluratest.controller import TestController
+from allura import model as M
+
+#---------x---------x---------x---------x---------x---------x---------x
+# RootController methods exposed:
+#     index, new_page, search
+# PageController methods exposed:
+#     index, edit, history, diff, raw, revert, update
+# CommentController methods exposed:
+#     reply, delete
+
+class TestRootController(TestController):
+
+    def _post(self, slug='', **kw):
+        d = {
+                'title':'My Post',
+                'text':'Nothing to see here',
+                'labels':'',
+                'state':'published'}
+        d.update(kw)
+        r = self.app.post('/blog%s/save' % slug, params=d)
+        return r
+
+    def _blog_date(self):
+        return datetime.datetime.utcnow().strftime('%Y/%m')
+
+    def test_root_index(self):
+        self._post()
+        d = self._blog_date()
+        response = self.app.get('/blog/')
+        assert 'Recent posts' in response
+        assert 'Nothing to see here' in response
+        assert '/blog/%s/my-post/edit' % d in response
+        anon_r = self.app.get('/blog/',
+                              extra_environ=dict(username='*anonymous'))
+        # anonymous user can't see Edit links
+        assert 'Nothing to see here' in anon_r
+        assert '/blog/%s/my-post/edit' % d not in anon_r
+
+    def test_root_index_draft(self):
+        self._post(state='draft')
+        d = self._blog_date()
+        response = self.app.get('/blog/')
+        assert 'Recent posts' in response
+        assert 'Nothing to see here' in response
+        assert 'Draft' in response
+        assert '/blog/%s/my-post/edit' % d in response
+        anon_r = self.app.get('/blog/',
+                              extra_environ=dict(username='*anonymous'))
+        # anonymous user can't see draft posts
+        assert 'Nothing to see here' not in anon_r
+
+    def test_root_new_post(self):
+        response = self.app.get('/blog/new')
+        assert 'Enter your title here' in response
+
+    def test_validation(self):
+        r = self._post(title='')
+        assert 'You must provide a Title' in r
+
+    def test_root_new_search(self):
+        self._post()
+        response = self.app.get('/blog/search?q=see')
+        assert 'Search' in response
+
+    def test_paging(self):
+        [self._post() for i in range(3)]
+        r = self.app.get('/blog/?limit=1&page=0')
+        assert 'Newer Entries' not in r
+        assert 'Older Entries' in r
+        r = self.app.get('/blog/?limit=1&page=1')
+        assert 'Newer Entries' in r
+        assert 'Older Entries' in r
+        r = self.app.get('/blog/?limit=1&page=2')
+        assert 'Newer Entries' in r
+        assert 'Older Entries' not in r
+
+    def test_discussion_admin(self):
+        r = self.app.get('/blog/')
+        r = self.app.get('/admin/blog/options', validate_chunk=True)
+        assert 'Allow discussion/commenting on posts' in r
+        # Turn discussion on
+        r = self.app.post('/admin/blog/set_options',
+                          params=dict(show_discussion='1'))
+        self._post()
+        d = self._blog_date()
+        r = self.app.get('/blog/%s/my-post/' % d)
+        assert '<div class="markdown_edit">' in r
+        # Turn discussion off
+        r = self.app.post('/admin/blog/set_options')
+        r = self.app.get('/blog/%s/my-post/' % d)
+        assert '<div class="markdown_edit">' not in r
+
+    def test_post_index(self):
+        self._post()
+        d = self._blog_date()
+        response = self.app.get('/blog/%s/my-post/' % d)
+        assert 'Nothing to see here' in response
+        assert '/blog/%s/my-post/edit' % d in response
+        anon_r = self.app.get('/blog/%s/my-post/' % d,
+                              extra_environ=dict(username='*anonymous'))
+        # anonymous user can't see Edit links
+        assert 'Nothing to see here' in anon_r
+        assert '/blog/%s/my-post/edit' % d not in anon_r
+        self.app.get('/blog/%s/no-my-post' % d, status=404)
+
+    def test_post_index_draft(self):
+        self._post(state='draft')
+        d = self._blog_date()
+        response = self.app.get('/blog/%s/my-post/' % d)
+        assert 'Nothing to see here' in response
+        assert 'Draft' in response
+        assert '/blog/%s/my-post/edit' % d in response
+        anon_r = self.app.get('/blog/%s/my-post/' % d,
+                              extra_environ=dict(username='*anonymous'))
+        # anonymous user can't get to draft posts
+        assert 'Nothing to see here' not in anon_r
+
+    def test_post_edit(self):
+        self._post()
+        d = self._blog_date()
+        response = self.app.get('/blog/%s/my-post/edit' % d)
+        assert 'Nothing' in response
+        # anon users can't edit
+        response = self.app.get('/blog/%s/my-post/edit' % d,
+                                extra_environ=dict(username='*anonymous'))
+        assert 'Nothing' not in response
+
+    def test_post_history(self):
+        self._post()
+        d = self._blog_date()
+        self._post('/%s/my-post' % d)
+        self._post('/%s/my-post' % d)
+        response = self.app.get('/blog/%s/my-post/history' % d)
+        assert 'My Post' in response
+        # two revisions are shown
+        assert '2 by Test Admin' in response
+        assert '1 by Test Admin' in response
+        self.app.get('/blog/%s/my-post?version=1' % d)
+        self.app.get('/blog/%s/my-post?version=foo' % d, status=404)
+
+    def test_post_diff(self):
+        self._post()
+        d = self._blog_date()
+        self._post('/%s/my-post' % d, text='sometext')
+        self.app.post('/blog/%s/my-post/revert' % d, params=dict(version='1'))
+        response = self.app.get('/blog/%s/my-post/' % d)
+        response = self.app.get('/blog/%s/my-post/diff?v1=0&v2=0' % d)
+        assert 'My Post' in response
+
+    def test_feeds(self):
+        self.app.get('/blog/feed.rss')
+        self.app.get('/blog/feed.atom')
+
+    def test_post_feeds(self):
+        self._post()
+        d = self._blog_date()
+        response = self.app.get('/blog/%s/my-post/feed.rss' % d)
+        assert 'Nothing to see' in response
+        response = self.app.get('/blog/%s/my-post/feed.atom' % d)
+        assert 'Nothing to see' in response
+
+    def test_related_artifacts(self):
+        self._post(title='one')
+        d = self._blog_date()
+        self._post(title='two', text='[blog:%s/one]' % d)
+        M.MonQTask.run_ready()
+        ThreadLocalORMSession.flush_all()
+        r= self.app.get('/blog/%s/one/' % d)
+        assert 'Related' in r
+        assert 'Blog Post: %s/two' % d in r

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b129d544/AlluraBitergiaMetrics/bitergiametrics/tests/test_roles.py
----------------------------------------------------------------------
diff --git a/AlluraBitergiaMetrics/bitergiametrics/tests/test_roles.py b/AlluraBitergiaMetrics/bitergiametrics/tests/test_roles.py
new file mode 100644
index 0000000..4394f59
--- /dev/null
+++ b/AlluraBitergiaMetrics/bitergiametrics/tests/test_roles.py
@@ -0,0 +1,28 @@
+from pylons import c, g
+
+from alluratest.controller import setup_basic_test, setup_global_objects
+from allura import model as M
+from allura.lib import security
+from allura.lib import helpers as h
+
+def setUp():
+    setup_basic_test()
+    setup_global_objects()
+    h.set_context('test', neighborhood='Projects')
+    c.project.install_app('blog', 'blog')
+    g.set_app('blog')
+
+def test_role_assignments():
+    admin = M.User.by_username('test-admin')
+    user = M.User.by_username('test-user')
+    anon = M.User.anonymous()
+    def check_access(perm):
+        pred = security.has_access(c.app, perm)
+        return pred(user=admin), pred(user=user), pred(user=anon)
+    assert check_access('configure') == (True, False, False)
+    assert check_access('read') == (True, True, True)
+    assert check_access('write') == (True, False, False)
+    assert check_access('unmoderated_post') == (True, True, False)
+    assert check_access('post') == (True, True, False)
+    assert check_access('moderate') == (True, False, False)
+    assert check_access('admin') == (True, False, False)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b129d544/AlluraBitergiaMetrics/bitergiametrics/tests/unit/__init__.py
----------------------------------------------------------------------
diff --git a/AlluraBitergiaMetrics/bitergiametrics/tests/unit/__init__.py b/AlluraBitergiaMetrics/bitergiametrics/tests/unit/__init__.py
new file mode 100644
index 0000000..e08129c
--- /dev/null
+++ b/AlluraBitergiaMetrics/bitergiametrics/tests/unit/__init__.py
@@ -0,0 +1,31 @@
+from pylons import c
+from ming.orm.ormsession import ThreadLocalORMSession
+
+from allura.websetup import bootstrap
+from allura.lib import helpers as h
+from allura.lib import plugin
+from allura import model as M
+from alluratest.controller import setup_basic_test
+
+
+def setUp():
+    setup_basic_test()
+
+class BlogTestWithModel(object):
+    def setUp(self):
+        bootstrap.wipe_database()
+        project_reg = plugin.ProjectRegistrationProvider.get()
+        c.user = bootstrap.create_user('Test User')
+        neighborhood = M.Neighborhood(name='Projects', url_prefix='/p/',
+            features=dict(private_projects = False,
+                          max_projects = None,
+                          css = 'none',
+                          google_analytics = False))
+        project_reg.register_neighborhood_project(neighborhood, [c.user])
+        c.project = neighborhood.register_project('test', c.user)
+        c.project.install_app('Blog', 'blog')
+        ThreadLocalORMSession.flush_all()
+        h.set_context('test', 'blog', neighborhood='Projects')
+
+    def tearDown(self):
+        ThreadLocalORMSession.close_all()

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b129d544/AlluraBitergiaMetrics/bitergiametrics/tests/unit/test_blog_post.py
----------------------------------------------------------------------
diff --git a/AlluraBitergiaMetrics/bitergiametrics/tests/unit/test_blog_post.py b/AlluraBitergiaMetrics/bitergiametrics/tests/unit/test_blog_post.py
new file mode 100644
index 0000000..9dcba4f
--- /dev/null
+++ b/AlluraBitergiaMetrics/bitergiametrics/tests/unit/test_blog_post.py
@@ -0,0 +1,83 @@
+from datetime import datetime
+from nose.tools import assert_equal
+
+from forgeblog import model as M
+from forgeblog.tests.unit import BlogTestWithModel
+
+def wrapped(s):
+    return '<div class="markdown_content"><p>%s</p></div>' % s
+
+class TestHtmlPreview(BlogTestWithModel):
+    def _make_post(self, text):
+        post = M.BlogPost()
+        post.text = text
+        post.make_slug()
+        return post
+
+    def test_single_long_paragraph(self):
+        text = ("Lorem ipsum dolor sit amet, consectetur adipisicing elit, "
+                "sed do eiusmod tempor incididunt ut labore et dolore magna "
+                "aliqua. Ut enim ad minim veniam, quis nostrud exercitation "
+                "ullamco laboris nisi ut aliquip ex ea commodo consequat. "
+                "Duis aute irure dolor in reprehenderit in voluptate velit "
+                "esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
+                "occaecat cupidatat non proident, sunt in culpa qui officia "
+                "deserunt mollit anim id est laborum.")
+        assert_equal(self._make_post(text).html_text_preview, wrapped(text))
+
+    def test_single_short_paragraph(self):
+        text = ("Lorem ipsum dolor sit amet, consectetur adipisicing elit, "
+                "sed do eiusmod tempor incididunt ut labore et dolore magna "
+                "aliqua. Ut enim ad minim veniam, quis nostrud exercitation "
+                "ullamco laboris nisi ut aliquip ex ea commodo consequat.")
+        assert_equal(self._make_post(text).html_text_preview, wrapped(text))
+
+    def test_multi_paragraph_short(self):
+        text = ("Lorem ipsum dolor sit amet, consectetur adipisicing elit, "
+                "sed do eiusmod tempor incididunt ut labore et dolore magna "
+                "aliqua."
+                "\n\n"
+                "Ut enim ad minim veniam, quis nostrud exercitation "
+                "ullamco laboris nisi ut aliquip ex ea commodo consequat.")
+
+        expected = ('<div class="markdown_content"><p>Lorem ipsum dolor sit '
+                    'amet, consectetur adipisicing elit, sed do eiusmod '
+                    'tempor incididunt ut labore et dolore magna aliqua.</p>\n'
+                    '<p>Ut enim ad minim veniam, quis nostrud exercitation '
+                    'ullamco laboris nisi ut aliquip ex ea commodo '
+                    'consequat.</p></div>')
+        assert_equal(self._make_post(text).html_text_preview, expected)
+
+    def test_multi_paragraph_long(self):
+        text = ("Lorem ipsum dolor sit amet, consectetur adipisicing elit, "
+                "sed do eiusmod tempor incididunt ut labore et dolore magna "
+                "aliqua."
+                "\n\n"
+                "Lorem ipsum dolor sit amet, consectetur adipisicing elit, "
+                "sed do eiusmod tempor incididunt ut labore et dolore magna "
+                "aliqua. Ut enim ad minim veniam, quis nostrud exercitation "
+                "ullamco laboris nisi ut aliquip ex ea commodo consequat. "
+                "Duis aute irure dolor in reprehenderit in voluptate velit "
+                "esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
+                "occaecat cupidatat non proident, sunt in culpa qui officia "
+                "deserunt mollit anim id est laborum."
+                "\n\n"
+                "Ut enim ad minim veniam, quis nostrud exercitation "
+                "ullamco laboris nisi ut aliquip ex ea commodo consequat.")
+
+        now = datetime.utcnow()
+        expected = ('<div class="markdown_content"><p>Lorem ipsum dolor sit '
+                    'amet, consectetur adipisicing elit, sed do eiusmod '
+                    'tempor incididunt ut labore et dolore magna aliqua.</p>\n'
+                    '<p>Lorem ipsum dolor sit amet, consectetur adipisicing '
+                    'elit, sed do eiusmod tempor incididunt ut labore et '
+                    'dolore magna aliqua. Ut enim ad minim veniam, quis '
+                    'nostrud exercitation ullamco laboris nisi ut aliquip ex '
+                    'ea commodo consequat. Duis aute irure dolor in '
+                    'reprehenderit in voluptate velit esse cillum dolore eu '
+                    'fugiat nulla pariatur. Excepteur sint occaecat cupidatat '
+                    'non proident, sunt in culpa qui officia deserunt mollit '
+                    'anim id est laborum.... '
+                    '<a href="/p/test/blog/%s/%02i/untitled/">read more</a>'
+                    '</p></div>') % (now.year, now.month)
+        assert_equal(self._make_post(text).html_text_preview, expected)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b129d544/AlluraBitergiaMetrics/bitergiametrics/version.py
----------------------------------------------------------------------
diff --git a/AlluraBitergiaMetrics/bitergiametrics/version.py b/AlluraBitergiaMetrics/bitergiametrics/version.py
new file mode 100644
index 0000000..6514373
--- /dev/null
+++ b/AlluraBitergiaMetrics/bitergiametrics/version.py
@@ -0,0 +1,2 @@
+__version_info__ = (0, 0)
+__version__ = '.'.join(map(str, __version_info__))

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b129d544/AlluraBitergiaMetrics/bitergiametrics/widgets.py
----------------------------------------------------------------------
diff --git a/AlluraBitergiaMetrics/bitergiametrics/widgets.py b/AlluraBitergiaMetrics/bitergiametrics/widgets.py
new file mode 100644
index 0000000..7887953
--- /dev/null
+++ b/AlluraBitergiaMetrics/bitergiametrics/widgets.py
@@ -0,0 +1,22 @@
+import ew as ew_core
+import ew.jinja2_ew as ew
+
+from formencode import validators as fev
+
+from allura.lib.widgets import form_fields as ffw
+from allura.lib.widgets import forms
+from allura import model as M
+
+class ViewMetricsForm(ew_core.Widget):
+    template='jinja:forgeblog:templates/metrics_widgets/view_metrics.html'
+    defaults=dict(
+        ew_core.Widget.defaults,
+        value=None,
+        subscribed=None,
+        base_post=None)
+
+    def __call__(self, **kw):
+        kw = super(ViewMetricsForm, self).__call__(**kw)
+        kw['subscribed'] = \
+            M.Mailbox.subscribed(artifact=kw.get('value'))
+        return kw
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b129d544/AlluraBitergiaMetrics/setup.cfg
----------------------------------------------------------------------
diff --git a/AlluraBitergiaMetrics/setup.cfg b/AlluraBitergiaMetrics/setup.cfg
new file mode 100644
index 0000000..01bb954
--- /dev/null
+++ b/AlluraBitergiaMetrics/setup.cfg
@@ -0,0 +1,3 @@
+[egg_info]
+tag_build = dev
+tag_svn_revision = true

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b129d544/AlluraBitergiaMetrics/setup.py
----------------------------------------------------------------------
diff --git a/AlluraBitergiaMetrics/setup.py b/AlluraBitergiaMetrics/setup.py
new file mode 100644
index 0000000..58fc8d8
--- /dev/null
+++ b/AlluraBitergiaMetrics/setup.py
@@ -0,0 +1,29 @@
+from setuptools import setup, find_packages
+import sys, os
+
+from bitergiametrics.version import __version__
+
+setup(name='BitergiaMetrics',
+      version=__version__,
+      description="",
+      long_description="""\
+""",
+      classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
+      keywords='',
+      author='Alvaro del Castillo, Bitergia',
+      author_email='acs@bitergia.com',
+      url='',
+      license='Apache License Version 2.0',
+      packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=[
+          # -*- Extra requirements: -*-
+          'allura',
+      ],
+      entry_points="""
+      # -*- Entry points: -*-
+      [allura]
+      metrics=bitergiametrics.main:BitergiaMetricsApp
+      """,
+      )

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b129d544/AlluraBitergiaMetrics/test.ini
----------------------------------------------------------------------
diff --git a/AlluraBitergiaMetrics/test.ini b/AlluraBitergiaMetrics/test.ini
new file mode 100644
index 0000000..3a32ab4
--- /dev/null
+++ b/AlluraBitergiaMetrics/test.ini
@@ -0,0 +1,54 @@
+#
+# allura - TurboGears 2 testing environment configuration
+#
+# The %(here)s variable will be replaced with the parent directory of this file
+#
+[DEFAULT]
+debug = true
+
+[server:main]
+use = egg:Paste#http
+host = 0.0.0.0
+port = 5000
+
+[app:main]
+use = config:../Allura/test.ini
+
+[app:main_without_authn]
+use = config:../Allura/test.ini#main_without_authn
+
+[app:main_with_amqp]
+use = config:../Allura/test.ini#main_with_amqp
+
+[loggers]
+keys = root, allura, tool
+
+[handlers]
+keys = test
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = test
+
+[logger_allura]
+level = DEBUG
+handlers =
+qualname = allura
+
+[logger_tool]
+level = DEBUG
+handlers =
+qualname = bitergiametrics
+
+[handler_test]
+class = FileHandler
+args = ('test.log',)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S