You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by tv...@apache.org on 2013/03/12 21:04:51 UTC
[31/50] [abbrv] git commit: [5453] Added unit tests to userstats
[5453] Added unit tests to userstats
Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/a5c5aad6
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/a5c5aad6
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/a5c5aad6
Branch: refs/heads/si/5453
Commit: a5c5aad6aa23efb0aa9246121146cfcac636905f
Parents: 2090695
Author: Stefano Invernizzi <st...@apache.org>
Authored: Sun Jan 20 16:33:25 2013 +0100
Committer: Tim Van Steenburgh <tv...@gmail.com>
Committed: Tue Mar 12 16:29:57 2013 +0000
----------------------------------------------------------------------
Allura/allura/model/contrib_stats.py | 635 +++++++++++++++
.../forgeuserstats/controllers/userstats.py | 1 -
ForgeUserStats/forgeuserstats/main.py | 10 +-
ForgeUserStats/forgeuserstats/model/stats.py | 631 +--------------
ForgeUserStats/forgeuserstats/tests/test_model.py | 375 +++++++++
ForgeUserStats/forgeuserstats/tests/test_stats.py | 33 -
6 files changed, 1029 insertions(+), 656 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/a5c5aad6/Allura/allura/model/contrib_stats.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/contrib_stats.py b/Allura/allura/model/contrib_stats.py
new file mode 100644
index 0000000..8a71d86
--- /dev/null
+++ b/Allura/allura/model/contrib_stats.py
@@ -0,0 +1,635 @@
+import pymongo
+from pylons import c, g, request
+
+import bson
+from ming import schema as S
+from ming import Field, Index, collection
+from ming.orm import session, state, Mapper
+from ming.orm import FieldProperty
+from ming.orm.declarative import MappedClass
+from datetime import datetime, timedelta
+import difflib
+
+from allura.model.session import main_orm_session
+from allura.lib import helpers as h
+
+class Stats(MappedClass):
+ class __mongometa__:
+ name='stats'
+ session = main_orm_session
+ unique_indexes = [ '_id']
+
+ _id=FieldProperty(S.ObjectId)
+
+ registration_date = FieldProperty(datetime)
+ general = FieldProperty([dict(
+ category = S.ObjectId,
+ messages = [dict(
+ messagetype = str,
+ created = int,
+ modified = int)],
+ tickets = dict(
+ solved = int,
+ assigned = int,
+ revoked = int,
+ totsolvingtime = int),
+ commits = [dict(
+ lines = int,
+ number = int,
+ language = S.ObjectId)])])
+
+ lastmonth=FieldProperty(dict(
+ messages=[dict(
+ datetime=datetime,
+ created=bool,
+ categories=[S.ObjectId],
+ messagetype=str)],
+ assignedtickets=[dict(
+ datetime=datetime,
+ categories=[S.ObjectId])],
+ revokedtickets=[dict(
+ datetime=datetime,
+ categories=[S.ObjectId])],
+ solvedtickets=[dict(
+ datetime=datetime,
+ categories=[S.ObjectId],
+ solvingtime=int)],
+ commits=[dict(
+ datetime=datetime,
+ categories=[S.ObjectId],
+ programming_languages=[S.ObjectId],
+ lines=int)]))
+
+ def getCodeContribution(self):
+ days=(datetime.today() - self.registration_date).days
+ if not days:
+ days=1
+ for val in self['general']:
+ if val['category'] is None:
+ for commits in val['commits']:
+ if commits['language'] is None:
+ if days > 30:
+ return round(float(commits.lines)/days*30, 2)
+ else:
+ return float(commits.lines)
+ return 0
+
+ def getDiscussionContribution(self):
+ days=(datetime.today() - self.registration_date).days
+ if not days:
+ days=1
+ for val in self['general']:
+ if val['category'] is None:
+ for artifact in val['messages']:
+ if artifact['messagetype'] is None:
+ tot = artifact.created+artifact.modified
+ if days > 30:
+ return round(float(tot)/days*30,2)
+ else:
+ return float(tot)
+ return 0
+
+ def getTicketsContribution(self):
+ for val in self['general']:
+ if val['category'] is None:
+ tickets = val['tickets']
+ if tickets.assigned == 0:
+ return 0
+ return float(tickets.solved) / tickets.assigned
+ return 0
+
+ @classmethod
+ def getMaxAndAverageCodeContribution(self):
+ lst = list(self.query.find())
+ n = len(lst)
+ if n == 0:
+ return 0, 0
+ maxcontribution=max([x.getCodeContribution() for x in lst])
+ averagecontribution=sum([x.getCodeContribution() for x in lst]) / n
+ return maxcontribution, round(averagecontribution, 2)
+
+ @classmethod
+ def getMaxAndAverageDiscussionContribution(self):
+ lst = list(self.query.find())
+ n = len(lst)
+ if n == 0:
+ return 0, 0
+ maxcontribution=max([x.getDiscussionContribution() for x in lst])
+ averagecontribution=sum([x.getDiscussionContribution() for x in lst])/n
+ return maxcontribution, round(averagecontribution, 2)
+
+ @classmethod
+ def getMaxAndAverageTicketsSolvingPercentage(self):
+ lst = list(self.query.find())
+ n = len(lst)
+ if n == 0:
+ return 0, 0
+ maxcontribution=max([x.getTicketsContribution() for x in lst])
+ averagecontribution=sum([x.getTicketsContribution() for x in lst])/n
+ return maxcontribution, round(averagecontribution, 2)
+
+ def codeRanking(self):
+ lst = list(self.query.find())
+ totn = len(lst)
+ codcontr = self.getCodeContribution()
+ upper = len([x for x in lst if x.getCodeContribution() > codcontr])
+ return round((totn - upper) * 100.0 / totn, 2)
+
+ def discussionRanking(self):
+ lst = list(self.query.find())
+ totn = len(lst)
+ disccontr = self.getDiscussionContribution()
+ upper=len([x for x in lst if x.getDiscussionContribution()>disccontr])
+ return round((totn - upper) * 100.0 / totn, 2)
+
+ def ticketsRanking(self):
+ lst = list(self.query.find())
+ totn = len(lst)
+ ticketscontr = self.getTicketsContribution()
+ upper=len([x for x in lst if x.getTicketsContribution()>ticketscontr])
+ return round((totn - upper) * 100.0 / totn, 2)
+
+ def getCommits(self, category = None):
+ i = getElementIndex(self.general, category = category)
+ if i is None:
+ return dict(number=0, lines=0)
+ cat = self.general[i]
+ j = getElementIndex(cat.commits, language = None)
+ if j is None:
+ return dict(number=0, lines=0)
+ return dict(
+ number=cat.commits[j]['number'],
+ lines=cat.commits[j]['lines'])
+
+ def getArtifacts(self, category = None, art_type = None):
+ i = getElementIndex(self.general, category = category)
+ if i is None:
+ return dict(created=0, modified=0)
+ cat = self.general[i]
+ j = getElementIndex(cat.messages, messagetype = art_type)
+ if j is None:
+ return dict(created=0, modified=0)
+ return dict(created=cat.messages[j].created, modified=cat.messages[j].modified)
+
+ def getTickets(self, category = None):
+ i = getElementIndex(self.general, category = category)
+ if i is None:
+ return dict(
+ assigned=0,
+ solved=0,
+ revoked=0,
+ averagesolvingtime=None)
+ if self.general[i].tickets.solved > 0:
+ tot = self.general[i].tickets.totsolvingtime
+ number = self.general[i].tickets.solved
+ average = tot / number
+ else:
+ average = None
+ return dict(
+ assigned=self.general[i].tickets.assigned,
+ solved=self.general[i].tickets.solved,
+ revoked=self.general[i].tickets.revoked,
+ averagesolvingtime=_convertTimeDiff(average))
+
+ def getCommitsByCategory(self):
+ from allura.model.project import TroveCategory
+
+ by_cat = {}
+ for entry in self.general:
+ cat = entry.category
+ i = getElementIndex(entry.commits, language = None)
+ if i is None:
+ n, lines = 0, 0
+ else:
+ n, lines = entry.commits[i].number, entry.commits[i].lines
+ if cat != None:
+ cat = TroveCategory.query.get(_id = cat)
+ by_cat[cat] = dict(number=n, lines=lines)
+ return by_cat
+
+ #For the moment, commit stats by language are not used, since each project
+ #can be linked to more than one programming language and we don't know how
+ #to which programming language should be credited a line of code modified
+ #within a project including two or more languages.
+ def getCommitsByLanguage(self):
+ langlist = []
+ by_lang = {}
+ i = getElementIndex(self.general, category=None)
+ if i is None:
+ return dict(number=0, lines=0)
+ return dict([(el.language, dict(lines=el.lines, number=el.number))
+ for el in self.general[i].commits])
+
+ def getArtifactsByCategory(self, detailed=False):
+ from allura.model.project import TroveCategory
+
+ by_cat = {}
+ for entry in self.general:
+ cat = entry.category
+ if cat != None:
+ cat = TroveCategory.query.get(_id = cat)
+ if detailed:
+ by_cat[cat] = entry.messages
+ else:
+ i = getElementIndex(entry.messages, messagetype=None)
+ if i is not None:
+ by_cat[cat] = entry.messages[i]
+ else:
+ by_cat[cat] = dict(created=0, modified=0)
+ return by_cat
+
+ def getArtifactsByType(self, category=None):
+ i = getElementIndex(self.general, category = category)
+ if i is None:
+ return {}
+ entry = self.general[i].messages
+ by_type = dict([(el.messagetype, dict(created=el.created,
+ modified=el.modified))
+ for el in entry])
+ return by_type
+
+ def getTicketsByCategory(self):
+ from allura.model.project import TroveCategory
+
+ by_cat = {}
+ for entry in self.general:
+ cat = entry.category
+ if cat != None:
+ cat = TroveCategory.query.get(_id = cat)
+ a, s = entry.tickets.assigned, entry.tickets.solved
+ r, time = entry.tickets.solved, entry.tickets.totsolvingtime
+ if s:
+ average = time / s
+ else:
+ average = None
+ by_cat[cat] = dict(
+ assigned=a,
+ solved=s,
+ revoked=r,
+ averagesolvingtime=_convertTimeDiff(average))
+ return by_cat
+
+ def getLastMonthCommits(self, category = None):
+ self.checkOldArtifacts()
+ lineslist = [el.lines for el in self.lastmonth.commits
+ if category in el.categories + [None]]
+ return dict(number=len(lineslist), lines=sum(lineslist))
+
+ def getLastMonthCommitsByCategory(self):
+ from allura.model.project import TroveCategory
+
+ self.checkOldArtifacts()
+ seen = set()
+ catlist=[el.category for el in self.general
+ if el.category not in seen and not seen.add(el.category)]
+
+ by_cat = {}
+ for cat in catlist:
+ lineslist = [el.lines for el in self.lastmonth.commits
+ if cat in el.categories + [None]]
+ n = len(lineslist)
+ lines = sum(lineslist)
+ if cat != None:
+ cat = TroveCategory.query.get(_id = cat)
+ by_cat[cat] = dict(number=n, lines=lines)
+ return by_cat
+
+ def getLastMonthCommitsByLanguage(self):
+ from allura.model.project import TroveCategory
+
+ self.checkOldArtifacts()
+ seen = set()
+ langlist=[el.language for el in self.general
+ if el.language not in seen and not seen.add(el.language)]
+
+ by_lang = {}
+ for lang in langlist:
+ lineslist = [el.lines for el in self.lastmonth.commits
+ if lang in el.programming_languages + [None]]
+ n = len(lineslist)
+ lines = sum(lineslist)
+ if lang != None:
+ lang = TroveCategory.query.get(_id = lang)
+ by_lang[lang] = dict(number=n, lines=lines)
+ return by_lang
+
+ def getLastMonthArtifacts(self, category = None, art_type = None):
+ self.checkOldArtifacts()
+ cre, mod = reduce(
+ addtuple,
+ [(int(el.created),1-int(el.created))
+ for el in self.lastmonth.messages
+ if (category is None or category in el.categories) and
+ (el.messagetype == art_type or art_type is None)],
+ (0,0))
+ return dict(created=cre, modified=mod)
+
+ def getLastMonthArtifactsByType(self, category = None):
+ self.checkOldArtifacts()
+ seen = set()
+ types=[el.messagetype for el in self.lastmonth.messages
+ if el.messagetype not in seen and not seen.add(el.messagetype)]
+
+ by_type = {}
+ for t in types:
+ cre, mod = reduce(
+ addtuple,
+ [(int(el.created),1-int(el.created))
+ for el in self.lastmonth.messages
+ if el.messagetype == t and
+ category in [None]+el.categories],
+ (0,0))
+ by_type[t] = dict(created=cre, modified=mod)
+ return by_type
+
+ def getLastMonthArtifactsByCategory(self):
+ from allura.model.project import TroveCategory
+
+ self.checkOldArtifacts()
+ seen = set()
+ catlist=[el.category for el in self.general
+ if el.category not in seen and not seen.add(el.category)]
+
+ by_cat = {}
+ for cat in catlist:
+ cre, mod = reduce(
+ addtuple,
+ [(int(el.created),1-int(el.created))
+ for el in self.lastmonth.messages
+ if cat in el.categories + [None]], (0,0))
+ if cat != None:
+ cat = TroveCategory.query.get(_id = cat)
+ by_cat[cat] = dict(created=cre, modified=mod)
+ return by_cat
+
+ def getLastMonthTickets(self, category = None):
+ from allura.model.project import TroveCategory
+
+ self.checkOldArtifacts()
+ a = len([el for el in self.lastmonth.assignedtickets
+ if category in el.categories + [None]])
+ r = len([el for el in self.lastmonth.revokedtickets
+ if category in el.categories + [None]])
+ s, time = reduce(
+ addtuple,
+ [(1, el.solvingtime)
+ for el in self.lastmonth.solvedtickets
+ if category in el.categories + [None]],
+ (0,0))
+ if category!=None:
+ category = TroveCategory.query.get(_id=category)
+ if s > 0:
+ time = time / s
+ else:
+ time = None
+ return dict(
+ assigned=a,
+ revoked=r,
+ solved=s,
+ averagesolvingtime=_convertTimeDiff(time))
+
+ def getLastMonthTicketsByCategory(self):
+ from allura.model.project import TroveCategory
+
+ self.checkOldArtifacts()
+ seen = set()
+ catlist=[el.category for el in self.general
+ if el.category not in seen and not seen.add(el.category)]
+ by_cat = {}
+ for cat in catlist:
+ a = len([el for el in self.lastmonth.assignedtickets
+ if cat in el.categories + [None]])
+ r = len([el for el in self.lastmonth.revokedtickets
+ if cat in el.categories + [None]])
+ s, time = reduce(addtuple, [(1, el.solvingtime)
+ for el in self.lastmonth.solvedtickets
+ if cat in el.categories+[None]],(0,0))
+ if cat != None:
+ cat = TroveCategory.query.get(_id = cat)
+ if s > 0:
+ time = time / s
+ else:
+ time = None
+ by_cat[cat] = dict(
+ assigned=a,
+ revoked=r,
+ solved=s,
+ averagesolvingtime=_convertTimeDiff(time))
+ return by_cat
+
+ def checkOldArtifacts(self):
+ now = datetime.utcnow()
+ for m in self.lastmonth.messages:
+ if now - m.datetime > timedelta(30):
+ self.lastmonth.messages.remove(m)
+ for t in self.lastmonth.assignedtickets:
+ if now - t.datetime > timedelta(30):
+ self.lastmonth.assignedtickets.remove(t)
+ for t in self.lastmonth.revokedtickets:
+ if now - t.datetime > timedelta(30):
+ self.lastmonth.revokedtickets.remove(t)
+ for t in self.lastmonth.solvedtickets:
+ if now - t.datetime > timedelta(30):
+ self.lastmonth.solvedtickets.remove(t)
+ for c in self.lastmonth.commits:
+ if now - c.datetime > timedelta(30):
+ self.lastmonth.commits.remove(c)
+
+ def addNewArtifact(self, art_type, art_datetime, project):
+ self._updateArtifactsStats(art_type, art_datetime, project, "created")
+
+ def addModifiedArtifact(self, art_type, art_datetime, project):
+ self._updateArtifactsStats(art_type, art_datetime, project, "modified")
+
+ def addAssignedTicket(self, ticket_datetime, project):
+ topics = [t for t in project.trove_topic if t]
+ self._updateTicketsStats(topics, 'assigned')
+ self.lastmonth.assignedtickets.append(
+ dict(datetime=ticket_datetime, categories=topics))
+
+ def addRevokedTicket(self, ticket_datetime, project):
+ topics = [t for t in project.trove_topic if t]
+ self._updateTicketsStats(topics, 'revoked')
+ self.lastmonth.revokedtickets.append(
+ dict(datetime=ticket_datetime, categories=topics))
+ self.checkOldArtifacts()
+
+ def addClosedTicket(self, open_datetime, close_datetime, project):
+ topics = [t for t in project.trove_topic if t]
+ s_time=int((close_datetime-open_datetime).total_seconds())
+ self._updateTicketsStats(topics, 'solved', s_time = s_time)
+ self.lastmonth.solvedtickets.append(dict(
+ datetime=close_datetime,
+ categories=topics,
+ solvingtime=s_time))
+ self.checkOldArtifacts()
+
+ def addCommit(self, newcommit, commit_datetime, project):
+ def _computeLines(newblob, oldblob = None):
+ if oldblob:
+ listold = list(oldblob)
+ else:
+ listold = []
+ if newblob:
+ listnew = list(newblob)
+ else:
+ listnew = []
+
+ if oldblob is None:
+ lines = len(listnew)
+ elif newblob and newblob.has_html_view:
+ diff = difflib.unified_diff(
+ listold, listnew,
+ ('old' + oldblob.path()).encode('utf-8'),
+ ('new' + newblob.path()).encode('utf-8'))
+ lines = len([l for l in diff if len(l) > 0 and l[0] == '+'])-1
+ else:
+ lines = 0
+ return lines
+
+ def _addCommitData(stats, topics, languages, lines):
+ lt = topics + [None]
+ ll = languages + [None]
+ for t in lt:
+ i = getElementIndex(stats.general, category=t)
+ if i is None:
+ newstats = dict(
+ category=t,
+ commits=[],
+ messages=dict(
+ assigned=0,
+ solved=0,
+ revoked=0,
+ totsolvingtime=0),
+ tickets=[])
+ stats.general.append(newstats)
+ i = getElementIndex(stats.general, category=t)
+ for lang in ll:
+ j = getElementIndex(
+ stats.general[i]['commits'], language=lang)
+ if j is None:
+ stats.general[i]['commits'].append(dict(
+ language=lang, lines=lines, number=1))
+ else:
+ stats.general[i]['commits'][j].lines += lines
+ stats.general[i]['commits'][j].number += 1
+
+ topics = [t for t in project.trove_topic if t]
+ languages = [l for l in project.trove_language if l]
+
+ d = newcommit.diffs
+ if len(newcommit.parent_ids) > 0:
+ oldcommit = newcommit.repo.commit(newcommit.parent_ids[0])
+
+ totlines = 0
+ for changed in d.changed:
+ newblob = newcommit.tree.get_blob_by_path(changed)
+ oldblob = oldcommit.tree.get_blob_by_path(changed)
+ totlines+=_computeLines(newblob, oldblob)
+
+ for copied in d.copied:
+ newblob = newcommit.tree.get_blob_by_path(copied['new'])
+ oldblob = oldcommit.tree.get_blob_by_path(copied['old'])
+ totlines+=_computeLines(newblob, oldblob)
+
+ for added in d.added:
+ newblob = newcommit.tree.get_blob_by_path(added)
+ totlines+=_computeLines(newblob)
+
+ _addCommitData(self, topics, languages, totlines)
+
+ self.lastmonth.commits.append(dict(
+ datetime=commit_datetime,
+ categories=topics,
+ programming_languages=languages,
+ lines=totlines))
+ self.checkOldArtifacts()
+
+ def _updateArtifactsStats(self, art_type, art_datetime, project, action):
+ if action not in ['created', 'modified']:
+ return
+ topics = [t for t in project.trove_topic if t]
+ lt = [None] + topics
+ for mtype in [None, art_type]:
+ for t in lt:
+ i = getElementIndex(self.general, category = t)
+ if i is None:
+ msg = dict(
+ category=t,
+ commits=[],
+ tickets=dict(
+ solved=0,
+ assigned=0,
+ revoked=0,
+ totsolvingtime=0),
+ messages=[])
+ self.general.append(msg)
+ i = getElementIndex(self.general, category = t)
+ j = getElementIndex(
+ self.general[i]['messages'], messagetype=mtype)
+ if j is None:
+ entry = dict(messagetype=mtype, created=0, modified=0)
+ entry[action] += 1
+ self.general[i]['messages'].append(entry)
+ else:
+ self.general[i]['messages'][j][action] += 1
+
+ self.lastmonth.messages.append(dict(
+ datetime=art_datetime,
+ created=(action == 'created'),
+ categories=topics,
+ messagetype=art_type))
+ self.checkOldArtifacts()
+
+ def _updateTicketsStats(self, topics, action, s_time = None):
+ if action not in ['solved', 'assigned', 'revoked']:
+ return
+ lt = topics + [None]
+ for t in lt:
+ i = getElementIndex(self.general, category = t)
+ if i is None:
+ stats = dict(
+ category=t,
+ commits=[],
+ tickets=dict(
+ solved=0,
+ assigned=0,
+ revoked=0,
+ totsolvingtime=0),
+ messages=[])
+ self.general.append(stats)
+ i = getElementIndex(self.general, category = t)
+ self.general[i]['tickets'][action] += 1
+ if action == 'solved':
+ self.general[i]['tickets']['totsolvingtime']+=s_time
+
+def getElementIndex(el_list, **kw):
+ for i in range(len(el_list)):
+ for k in kw:
+ if el_list[i].get(k) != kw[k]:
+ break
+ else:
+ return i
+ return None
+
+def addtuple(l1, l2):
+ a, b = l1
+ x, y = l2
+ return (a+x, b+y)
+
+def _convertTimeDiff(int_seconds):
+ if int_seconds is None:
+ return None
+ diff = timedelta(seconds = int_seconds)
+ days, seconds = diff.days, diff.seconds
+ hours = seconds / 3600
+ seconds = seconds % 3600
+ minutes = seconds / 60
+ seconds = seconds % 60
+ return dict(
+ days=days,
+ hours=hours,
+ minutes=minutes,
+ seconds=seconds)
+
+Mapper.compile_all()
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/a5c5aad6/ForgeUserStats/forgeuserstats/controllers/userstats.py
----------------------------------------------------------------------
diff --git a/ForgeUserStats/forgeuserstats/controllers/userstats.py b/ForgeUserStats/forgeuserstats/controllers/userstats.py
index 2bfaf82..fe14449 100644
--- a/ForgeUserStats/forgeuserstats/controllers/userstats.py
+++ b/ForgeUserStats/forgeuserstats/controllers/userstats.py
@@ -228,7 +228,6 @@ def _getDataForCategory(category, stats):
solved='n/a',
averagesolvingtime='n/a')
for key in artifacts_by_type:
- value = artifacts_by_type[key]
artifacts_by_type[key]['pmcreated'] = 'n/a'
artifacts_by_type[key]['pmmodified']= 'n/a'
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/a5c5aad6/ForgeUserStats/forgeuserstats/main.py
----------------------------------------------------------------------
diff --git a/ForgeUserStats/forgeuserstats/main.py b/ForgeUserStats/forgeuserstats/main.py
index 43ca2f3..8eeb113 100644
--- a/ForgeUserStats/forgeuserstats/main.py
+++ b/ForgeUserStats/forgeuserstats/main.py
@@ -32,25 +32,25 @@ class UserStatsListener(EventsListener):
stats = UserStats.create(user)
if event_type == "assigned":
- stats.addAssignedTicket(ticket, project)
+ stats.addAssignedTicket(ticket.mod_date, project)
elif event_type == "revoked":
- stats.addRevokedTicket(ticket, project)
+ stats.addRevokedTicket(ticket.mod_date, project)
elif event_type == "closed":
- stats.addClosedTicket(ticket, project)
+ stats.addClosedTicket(ticket.created_date,ticket.mod_date,project)
def newCommit(self, newcommit, project, user):
stats = user.stats
if not stats:
stats = UserStats.create(user)
- stats.addCommit(newcommit, project)
+ stats.addCommit(newcommit, datetime.utcnow(), project)
def addUserLogin(self, user):
stats = user.stats
if not stats:
stats = UserStats.create(user)
- stats.addLogin()
+ stats.addLogin(datetime.utcnow())
def newOrganization(self, organization):
pass
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/a5c5aad6/ForgeUserStats/forgeuserstats/model/stats.py
----------------------------------------------------------------------
diff --git a/ForgeUserStats/forgeuserstats/model/stats.py b/ForgeUserStats/forgeuserstats/model/stats.py
index 2f5e1b9..8575e79 100644
--- a/ForgeUserStats/forgeuserstats/model/stats.py
+++ b/ForgeUserStats/forgeuserstats/model/stats.py
@@ -1,67 +1,20 @@
-import pymongo
-from pylons import c, g, request
-
-import bson
+from ming.orm import FieldProperty
from ming import schema as S
-from ming import Field, Index, collection
-from ming.orm import session, state, Mapper
-from ming.orm import FieldProperty, RelationProperty, ForeignIdProperty
-from ming.orm.declarative import MappedClass
from datetime import datetime, timedelta
-import difflib
+from ming.orm import session, Mapper
from allura.model.session import main_orm_session
-from allura.lib import helpers as h
+from allura.model.contrib_stats import Stats
-class UserStats(MappedClass):
+class UserStats(Stats):
class __mongometa__:
name='userstats'
session = main_orm_session
unique_indexes = [ '_id', 'user_id']
- _id=FieldProperty(S.ObjectId)
-
- registration_date = FieldProperty(datetime)
tot_logins_count = FieldProperty(int, if_missing = 0)
last_login = FieldProperty(datetime)
- general = FieldProperty([dict(
- category = S.ObjectId,
- messages = [dict(
- messagetype = str,
- created = int,
- modified = int)],
- tickets = dict(
- solved = int,
- assigned = int,
- revoked = int,
- totsolvingtime = int),
- commits = [dict(
- lines = int,
- number = int,
- language = S.ObjectId)])])
-
- lastmonth=FieldProperty(dict(
- logins=[datetime],
- messages=[dict(
- datetime=datetime,
- created=bool,
- categories=[S.ObjectId],
- messagetype=str)],
- assignedtickets=[dict(
- datetime=datetime,
- categories=[S.ObjectId])],
- revokedtickets=[dict(
- datetime=datetime,
- categories=[S.ObjectId])],
- solvedtickets=[dict(
- datetime=datetime,
- categories=[S.ObjectId],
- solvingtime=int)],
- commits=[dict(
- datetime=datetime,
- categories=[S.ObjectId],
- programming_languages=[S.ObjectId],
- lines=int)]))
+ lastmonthlogins=FieldProperty([datetime])
user_id = FieldProperty(S.ObjectId)
@classmethod
@@ -76,578 +29,22 @@ class UserStats(MappedClass):
session(user).flush(user)
return stats
- def getCodeContribution(self):
- days=(datetime.today() - self.registration_date).days
- if not days:
- days=1
- for val in self['general']:
- if val['category'] is None:
- for commits in val['commits']:
- if commits['language'] is None:
- if days > 30:
- return round(float(commits.lines)/days*30, 2)
- else:
- return float(commits.lines)
- return 0
-
- def getDiscussionContribution(self):
- days=(datetime.today() - self.registration_date).days
- if not days:
- days=1
- for val in self['general']:
- if val['category'] is None:
- for artifact in val['messages']:
- if artifact['messagetype'] is None:
- tot = artifact.created+artifact.modified
- if days > 30:
- return round(float(tot)/days*30,2)
- else:
- return float(tot)
- return 0
-
- def getTicketsContribution(self):
- for val in self['general']:
- if val['category'] is None:
- tickets = val['tickets']
- if tickets.assigned == 0:
- return 0
- return float(tickets.solved) / tickets.assigned
- return 0
-
- @classmethod
- def getMaxAndAverageCodeContribution(self):
- lst = list(self.query.find())
- n = len(lst)
- if n == 0:
- return 0, 0
- maxcontribution=max([x.getCodeContribution() for x in lst])
- averagecontribution=sum([x.getCodeContribution() for x in lst]) / n
- return maxcontribution, round(averagecontribution, 2)
-
- @classmethod
- def getMaxAndAverageDiscussionContribution(self):
- lst = list(self.query.find())
- n = len(lst)
- if n == 0:
- return 0, 0
- maxcontribution=max([x.getDiscussionContribution() for x in lst])
- averagecontribution=sum([x.getDiscussionContribution() for x in lst])/n
- return maxcontribution, round(averagecontribution, 2)
-
- @classmethod
- def getMaxAndAverageTicketsSolvingPercentage(self):
- lst = list(self.query.find())
- n = len(lst)
- if n == 0:
- return 0, 0
- maxcontribution=max([x.getTicketsContribution() for x in lst])
- averagecontribution=sum([x.getTicketsContribution() for x in lst])/n
- return maxcontribution, round(averagecontribution, 2)
-
- def codeRanking(self):
- lst = list(self.query.find())
- totn = len(lst)
- codcontr = self.getCodeContribution()
- upper = len([x for x in lst if x.getCodeContribution() > codcontr])
- return round((totn - upper) * 100.0 / totn, 2)
-
- def discussionRanking(self):
- lst = list(self.query.find())
- totn = len(lst)
- disccontr = self.getDiscussionContribution()
- upper=len([x for x in lst if x.getDiscussionContribution()>disccontr])
- return round((totn - upper) * 100.0 / totn, 2)
-
- def ticketsRanking(self):
- lst = list(self.query.find())
- totn = len(lst)
- ticketscontr = self.getTicketsContribution()
- upper=len([x for x in lst if x.getTicketsContribution()>ticketscontr])
- return round((totn - upper) * 100.0 / totn, 2)
-
- def getCommits(self, category = None):
- i = getElementIndex(self.general, category = category)
- if i is None:
- return dict(number=0, lines=0)
- cat = self.general[i]
- j = getElementIndex(cat.commits, language = None)
- if j is None:
- return dict(number=0, lines=0)
- return dict(
- number=cat.commits[j]['number'],
- lines=cat.commits[j]['lines'])
-
- def getArtifacts(self, category = None, art_type = None):
- i = getElementIndex(self.general, category = category)
- if i is None:
- return dict(created=0, modified=0)
- cat = self.general[i]
- j = getElementIndex(cat.messages, messagetype = art_type)
- if j is None:
- return dict(created=0, modified=0)
- return dict(created=cat.messages[j].created, modified=cat.messages[j].modified)
-
- def getTickets(self, category = None):
- i = getElementIndex(self.general, category = category)
- if i is None:
- return dict(
- assigned=0,
- solved=0,
- revoked=0,
- averagesolvingtime=None)
- if self.general[i].tickets.solved > 0:
- tot = self.general[i].tickets.totsolvingtime
- number = self.general[i].tickets.solved
- average = tot / number
- else:
- average = None
- return dict(
- assigned=self.general[i].tickets.assigned,
- solved=self.general[i].tickets.solved,
- revoked=self.general[i].tickets.revoked,
- averagesolvingtime=_convertTimeDiff(average))
-
- def getCommitsByCategory(self):
- from allura.model.project import TroveCategory
-
- by_cat = {}
- for entry in self.general:
- cat = entry.category
- i = getElementIndex(entry.commits, language = None)
- if i is None:
- n, lines = 0, 0
- else:
- n, lines = entry.commits[i].number, entry.commits[i].lines
- if cat != None:
- cat = TroveCategory.query.get(_id = cat)
- by_cat[cat] = dict(number=n, lines=lines)
- return by_cat
-
- def getCommitsByLanguage(self):
- langlist = []
- by_lang = {}
- i = getElementIndex(self.general, category=None)
- if i is None:
- return dict(number=0, lines=0)
- return dict([(el.language, dict(lines=el.lines, number=el.number))
- for el in self.general[i].commits])
-
- def getArtifactsByCategory(self, detailed=False):
- from allura.model.project import TroveCategory
-
- by_cat = {}
- for entry in self.general:
- cat = entry.category
- if cat != None:
- cat = TroveCategory.query.get(_id = cat)
- if detailed:
- by_cat[cat] = entry.messages
- else:
- i = getElementIndex(entry.messages, messagetype=None)
- if i is not None:
- by_cat[cat] = entry.messages[i]
- else:
- by_cat[cat] = dict(created=0, modified=0)
- return by_cat
-
- def getArtifactsByType(self, category=None):
- i = getElementIndex(self.general, category = category)
- if i is None:
- return {}
- entry = self.general[i].messages
- by_type = dict([(el.messagetype, dict(created=el.created,
- modified=el.modified))
- for el in entry])
- return by_type
-
- def getTicketsByCategory(self):
- from allura.model.project import TroveCategory
-
- by_cat = {}
- for entry in self.general:
- cat = entry.category
- if cat != None:
- cat = TroveCategory.query.get(_id = cat)
- a, s = entry.tickets.assigned, entry.tickets.solved
- r, time = entry.tickets.solved, entry.tickets.totsolvingtime
- if s:
- average = time / s
- else:
- average = None
- by_cat[cat] = dict(
- assigned=a,
- solved=s,
- revoked=r,
- averagesolvingtime=_convertTimeDiff(average))
- return by_cat
-
- def getLastMonthCommits(self, category = None):
- self.checkOldArtifacts()
- lineslist = [el.lines for el in self.lastmonth.commits
- if category in el.categories + [None]]
- return dict(number=len(lineslist), lines=sum(lineslist))
-
- def getLastMonthCommitsByCategory(self):
- from allura.model.project import TroveCategory
-
- self.checkOldArtifacts()
- seen = set()
- catlist=[el.category for el in self.general
- if el.category not in seen and not seen.add(el.category)]
-
- by_cat = {}
- for cat in catlist:
- lineslist = [el.lines for el in self.lastmonth.commits
- if cat in el.categories + [None]]
- n = len(lineslist)
- lines = sum(lineslist)
- if cat != None:
- cat = TroveCategory.query.get(_id = cat)
- by_cat[cat] = dict(number=n, lines=lines)
- return by_cat
-
- def getLastMonthCommitsByLanguage(self):
- from allura.model.project import TroveCategory
-
- self.checkOldArtifacts()
- seen = set()
- langlist=[el.language for el in self.general
- if el.language not in seen and not seen.add(el.language)]
-
- by_lang = {}
- for lang in langlist:
- lineslist = [el.lines for el in self.lastmonth.commits
- if lang in el.programming_languages + [None]]
- n = len(lineslist)
- lines = sum(lineslist)
- if lang != None:
- lang = TroveCategory.query.get(_id = lang)
- by_lang[lang] = dict(number=n, lines=lines)
- return by_lang
-
- def getLastMonthArtifacts(self, category = None):
- self.checkOldArtifacts()
- cre, mod = reduce(addtuple, [(int(el.created),1-int(el.created))
- for el in self.lastmonth.messages
- if category is None or
- category in el.categories], (0,0))
- return dict(created=cre, modified=mod)
-
- def getLastMonthArtifactsByType(self, category = None):
- self.checkOldArtifacts()
- seen = set()
- types=[el.messagetype for el in self.lastmonth.messages
- if el.messagetype not in seen and not seen.add(el.messagetype)]
-
- by_type = {}
- for t in types:
- cre, mod = reduce(
- addtuple,
- [(int(el.created),1-int(el.created))
- for el in self.lastmonth.messages
- if el.messagetype == t and
- category in [None]+el.categories],
- (0,0))
- by_type[t] = dict(created=cre, modified=mod)
- return by_type
-
- def getLastMonthArtifactsByCategory(self):
- from allura.model.project import TroveCategory
-
- self.checkOldArtifacts()
- seen = set()
- catlist=[el.category for el in self.general
- if el.category not in seen and not seen.add(el.category)]
-
- by_cat = {}
- for cat in catlist:
- cre, mod = reduce(
- addtuple,
- [(int(el.created),1-int(el.created))
- for el in self.lastmonth.messages
- if cat in el.categories + [None]], (0,0))
- if cat != None:
- cat = TroveCategory.query.get(_id = cat)
- by_cat[cat] = dict(created=cre, modified=mod)
- return by_cat
-
- def getLastMonthTickets(self, category = None):
- from allura.model.project import TroveCategory
-
- self.checkOldArtifacts()
- a = len([el for el in self.lastmonth.assignedtickets
- if category in el.categories + [None]])
- r = len([el for el in self.lastmonth.revokedtickets
- if category in el.categories + [None]])
- s, time = reduce(
- addtuple,
- [(1, el.solvingtime)
- for el in self.lastmonth.solvedtickets
- if category in el.categories + [None]],
- (0,0))
- if category!=None:
- category = TroveCategory.query.get(_id=category)
- if s > 0:
- time = time / s
- else:
- time = None
- return dict(
- assigned=a,
- revoked=r,
- solved=s,
- averagesolvingtime=_convertTimeDiff(time))
-
- def getLastMonthTicketsByCategory(self):
- from allura.model.project import TroveCategory
-
- self.checkOldArtifacts()
- seen = set()
- catlist=[el.category for el in self.general
- if el.category not in seen and not seen.add(el.category)]
- by_cat = {}
- for cat in catlist:
- a = len([el for el in self.lastmonth.assignedtickets
- if cat in el.categories + [None]])
- r = len([el for el in self.lastmonth.revokedtickets
- if cat in el.categories + [None]])
- s, time = reduce(addtuple, [(1, el.solvingtime)
- for el in self.lastmonth.solvedtickets
- if cat in el.categories+[None]],(0,0))
- if cat != None:
- cat = TroveCategory.query.get(_id = cat)
- if s > 0:
- time = time / s
- else:
- time = None
- by_cat[cat] = dict(
- assigned=a,
- revoked=r,
- solved=s,
- averagesolvingtime=_convertTimeDiff(time))
- return by_cat
-
def getLastMonthLogins(self):
self.checkOldArtifacts()
- return len(self.lastmonth.logins)
+ return len(self.lastmonthlogins)
def checkOldArtifacts(self):
+ super(UserStats, self).checkOldArtifacts()
now = datetime.utcnow()
- for m in self.lastmonth.messages:
- if now - m.datetime > timedelta(30):
- self.lastmonth.messages.remove(m)
- for t in self.lastmonth.assignedtickets:
- if now - t.datetime > timedelta(30):
- self.lastmonth.assignedtickets.remove(t)
- for t in self.lastmonth.revokedtickets:
- if now - t.datetime > timedelta(30):
- self.lastmonth.revokedtickets.remove(t)
- for t in self.lastmonth.solvedtickets:
- if now - t.datetime > timedelta(30):
- self.lastmonth.solvedtickets.remove(t)
-
- def addNewArtifact(self, art_type, art_datetime, project):
- self._updateArtifactsStats(art_type, art_datetime, project, "created")
-
- def addModifiedArtifact(self, art_type, art_datetime, project):
- self._updateArtifactsStats(art_type, art_datetime, project, "modified")
-
- def addAssignedTicket(self, ticket, project):
- topics = [t for t in project.trove_topic if t]
- self._updateTicketsStats(topics, 'assigned')
- self.lastmonth.assignedtickets.append(
- dict(datetime=ticket.mod_date, categories=topics))
-
- def addRevokedTicket(self, ticket, project):
- topics = [t for t in project.trove_topic if t]
- self._updateTicketsStats(topics, 'revoked')
- self.lastmonth.revokedtickets.append(
- dict(datetime=ticket.mod_date, categories=topics))
- self.checkOldArtifacts()
-
- def addClosedTicket(self, ticket, project):
- topics = [t for t in project.trove_topic if t]
- s_time=int((datetime.utcnow()-ticket.created_date).total_seconds())
- self._updateTicketsStats(topics, 'solved', s_time = s_time)
- self.lastmonth.solvedtickets.append(dict(
- datetime=ticket.mod_date,
- categories=topics,
- solvingtime=s_time))
- self.checkOldArtifacts()
-
- def addCommit(self, newcommit, project):
- def _computeLines(newblob, oldblob = None):
- if oldblob:
- listold = list(oldblob)
- else:
- listold = []
- if newblob:
- listnew = list(newblob)
- else:
- listnew = []
-
- if oldblob is None:
- lines = len(listnew)
- elif newblob and newblob.has_html_view:
- diff = difflib.unified_diff(
- listold, listnew,
- ('old' + oldblob.path()).encode('utf-8'),
- ('new' + newblob.path()).encode('utf-8'))
- lines = len([l for l in diff if len(l) > 0 and l[0] == '+'])-1
- else:
- lines = 0
- return lines
-
- def _addCommitData(stats, topics, languages, lines):
- lt = topics + [None]
- ll = languages + [None]
- for t in lt:
- i = getElementIndex(stats.general, category=t)
- if i is None:
- newstats = dict(
- category=t,
- commits=[],
- messages=dict(
- assigned=0,
- solved=0,
- revoked=0,
- totsolvingtime=0),
- tickets=[])
- stats.general.append(newstats)
- i = getElementIndex(stats.general, category=t)
- for lang in ll:
- j = getElementIndex(
- stats.general[i]['commits'], language=lang)
- if j is None:
- stats.general[i]['commits'].append(dict(
- language=lang, lines=lines, number=1))
- else:
- stats.general[i]['commits'][j].lines += lines
- stats.general[i]['commits'][j].number += 1
-
- topics = [t for t in project.trove_topic if t]
- languages = [l for l in project.trove_language if l]
- now = datetime.utcnow()
-
- d = newcommit.diffs
- if len(newcommit.parent_ids) > 0:
- oldcommit = newcommit.repo.commit(newcommit.parent_ids[0])
-
- totlines = 0
- for changed in d.changed:
- newblob = newcommit.tree.get_blob_by_path(changed)
- oldblob = oldcommit.tree.get_blob_by_path(changed)
- totlines+=_computeLines(newblob, oldblob)
-
- for copied in d.copied:
- newblob = newcommit.tree.get_blob_by_path(copied['new'])
- oldblob = oldcommit.tree.get_blob_by_path(copied['old'])
- totlines+=_computeLines(newblob, oldblob)
-
- for added in d.added:
- newblob = newcommit.tree.get_blob_by_path(added)
- totlines+=_computeLines(newblob)
-
- _addCommitData(self, topics, languages, totlines)
+ for l in self.lastmonthlogins:
+ if now - l > timedelta(30):
+ self.lastmonthlogins.remove(l)
- self.lastmonth.commits.append(dict(
- datetime=now,
- categories=topics,
- programming_languages=languages,
- lines=totlines))
- self.checkOldArtifacts()
-
- def addLogin(self):
- now = datetime.utcnow()
- self.last_login = now
+ def addLogin(self, login_datetime):
+ if (not self.last_login) or (login_datetime > self.last_login):
+ self.last_login = login_datetime
self.tot_logins_count += 1
- self.lastmonth.logins.append(now)
+ self.lastmonthlogins.append(login_datetime)
self.checkOldArtifacts()
- def _updateArtifactsStats(self, art_type, art_datetime, project, action):
- if action not in ['created', 'modified']:
- return
- topics = [t for t in project.trove_topic if t]
- lt = [None] + topics
- for mtype in [None, art_type]:
- for t in lt:
- i = getElementIndex(self.general, category = t)
- if i is None:
- msg = dict(
- category=t,
- commits=[],
- tickets=dict(
- solved=0,
- assigned=0,
- revoked=0,
- totsolvingtime=0),
- messages=[])
- self.general.append(msg)
- i = getElementIndex(self.general, category = t)
- j = getElementIndex(
- self.general[i]['messages'], messagetype=mtype)
- if j is None:
- entry = dict(messagetype=mtype, created=0, modified=0)
- entry[action] += 1
- self.general[i]['messages'].append(entry)
- else:
- self.general[i]['messages'][j][action] += 1
-
- self.lastmonth.messages.append(dict(
- datetime=art_datetime,
- created=(action == 'created'),
- categories=topics,
- messagetype=art_type))
- self.checkOldArtifacts()
-
- def _updateTicketsStats(self, topics, action, s_time = None):
- if action not in ['solved', 'assigned', 'revoked']:
- return
- lt = topics + [None]
- for t in lt:
- i = getElementIndex(self.general, category = t)
- if i is None:
- stats = dict(
- category=t,
- commits=[],
- tickets=dict(
- solved=0,
- assigned=0,
- revoked=0,
- totsolvingtime=0),
- messages=[])
- self.general.append(stats)
- i = getElementIndex(self.general, category = t)
- self.general[i]['tickets'][action] += 1
- if action == 'solved':
- self.general[i]['tickets']['totsolvingtime']+=s_time
-
-def getElementIndex(el_list, **kw):
- for i in range(len(el_list)):
- for k in kw:
- if el_list[i].get(k) != kw[k]:
- break
- else:
- return i
- return None
-
-def addtuple(l1, l2):
- a, b = l1
- x, y = l2
- return (a+x, b+y)
-
-def _convertTimeDiff(int_seconds):
- if int_seconds is None:
- return None
- diff = timedelta(seconds = int_seconds)
- days, seconds = diff.days, diff.seconds
- hours = seconds / 3600
- seconds = seconds % 3600
- minutes = seconds / 60
- seconds = seconds % 60
- return dict(
- days=days,
- hours=hours,
- minutes=minutes,
- seconds=seconds)
-
Mapper.compile_all()
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/a5c5aad6/ForgeUserStats/forgeuserstats/tests/test_model.py
----------------------------------------------------------------------
diff --git a/ForgeUserStats/forgeuserstats/tests/test_model.py b/ForgeUserStats/forgeuserstats/tests/test_model.py
new file mode 100644
index 0000000..3a9fcde
--- /dev/null
+++ b/ForgeUserStats/forgeuserstats/tests/test_model.py
@@ -0,0 +1,375 @@
+import pkg_resources
+import unittest
+from datetime import datetime, timedelta
+
+from pylons import tmpl_context as c
+
+from alluratest.controller import setup_basic_test, setup_global_objects
+from allura.tests import decorators as td
+from allura.model import User, Project, TroveCategory
+from allura import model as M
+
+from forgegit.tests import with_git
+
+class TestUserStats(unittest.TestCase):
+
+ def setUp(self):
+ from allura.model import User
+
+ setup_basic_test()
+ setup_global_objects()
+ self.user = User.register(dict(username='test-new-user',
+ display_name='Test Stats'),
+ make_project=False)
+
+ def test_init_values(self):
+ artifacts = self.user.stats.getArtifacts()
+ tickets = self.user.stats.getTickets()
+ commits = self.user.stats.getCommits()
+ assert self.user.stats.tot_logins_count == 0
+ assert artifacts['created'] == 0
+ assert artifacts['modified'] == 0
+ assert tickets['assigned'] == 0
+ assert tickets['solved'] == 0
+ assert tickets['revoked'] == 0
+ assert tickets['averagesolvingtime'] is None
+ assert commits['number'] == 0
+ assert commits['lines'] == 0
+
+ lmartifacts = self.user.stats.getLastMonthArtifacts()
+ lmtickets = self.user.stats.getLastMonthTickets()
+ lmcommits = self.user.stats.getLastMonthCommits()
+ assert self.user.stats.getLastMonthLogins() == 0
+ assert lmartifacts['created'] == 0
+ assert lmartifacts['modified'] == 0
+ assert lmtickets['assigned'] == 0
+ assert lmtickets['solved'] == 0
+ assert lmtickets['revoked'] == 0
+ assert lmtickets['averagesolvingtime'] is None
+ assert lmcommits['number'] == 0
+ assert lmcommits['lines'] == 0
+
+ @td.with_user_project('test-new-user')
+ def test_create_artifact_stats(self):
+ p = Project.query.get(shortname='u/test-new-user')
+ topic = TroveCategory.query.get(shortname='scientific')
+
+ init_lm_art = self.user.stats.getLastMonthArtifacts()
+ init_art = self.user.stats.getArtifacts()
+ init_art_wiki = self.user.stats.getArtifacts(art_type='Wiki')
+ init_art_by_type = self.user.stats.getArtifactsByType()
+ init_lm_art_by_type = self.user.stats.getLastMonthArtifactsByType()
+ init_art_sci = self.user.stats.getArtifacts(category=topic._id)
+
+ self.user.stats.addNewArtifact('Wiki', datetime.utcnow(), p)
+ lm_art = self.user.stats.getLastMonthArtifacts()
+ artifacts = self.user.stats.getArtifacts()
+ art_wiki = self.user.stats.getArtifacts(art_type='Wiki')
+ art_by_type = self.user.stats.getArtifactsByType()
+ lm_art_by_type = self.user.stats.getLastMonthArtifactsByType()
+
+ assert lm_art['created'] == init_lm_art['created'] + 1
+ assert lm_art['modified'] == init_lm_art['modified']
+ assert artifacts['created'] == init_art['created'] + 1
+ assert artifacts['modified'] == init_art['modified']
+ assert art_wiki['created'] == init_art_wiki['created'] + 1
+ assert art_wiki['modified'] == init_art_wiki['modified']
+ assert art_by_type['Wiki']['created'] == init_art_by_type['Wiki']['created'] + 1
+ assert art_by_type['Wiki']['modified'] == init_art_by_type['Wiki']['modified']
+ assert lm_art_by_type['Wiki']['created'] == init_lm_art_by_type['Wiki']['created'] + 1
+ assert lm_art_by_type['Wiki']['modified'] == init_lm_art_by_type['Wiki']['modified']
+
+ #In that case, last month stats should not be changed
+ new_date = datetime.utcnow() + timedelta(-32)
+ self.user.stats.addNewArtifact('Wiki', new_date, p)
+ lm_art = self.user.stats.getLastMonthArtifacts()
+ artifacts = self.user.stats.getArtifacts()
+ art_wiki = self.user.stats.getArtifacts(art_type='Wiki')
+ art_by_type = self.user.stats.getArtifactsByType()
+ lm_art_by_type = self.user.stats.getLastMonthArtifactsByType()
+
+ assert lm_art['created'] == init_lm_art['created'] + 1
+ assert lm_art['modified'] == init_lm_art['modified']
+ assert artifacts['created'] == init_art['created'] + 2
+ assert artifacts['modified'] == init_art['modified']
+ assert art_wiki['created'] == init_art_wiki['created'] + 2
+ assert art_wiki['modified'] == init_art_wiki['modified']
+ assert art_by_type['Wiki']['created'] == init_art_by_type['Wiki']['created'] + 2
+ assert art_by_type['Wiki']['modified'] == init_art_by_type['Wiki']['modified']
+ assert lm_art_by_type['Wiki']['created'] == init_lm_art_by_type['Wiki']['created'] + 1
+ assert lm_art_by_type['Wiki']['modified'] == init_lm_art_by_type['Wiki']['modified']
+
+ p.trove_topic = [topic._id]
+
+ self.user.stats.addNewArtifact('Wiki', datetime.utcnow(), p)
+ lm_art = self.user.stats.getLastMonthArtifacts()
+ artifacts = self.user.stats.getArtifacts()
+ art_wiki = self.user.stats.getArtifacts(art_type='Wiki')
+ art_by_type = self.user.stats.getArtifactsByType()
+ lm_art_by_type = self.user.stats.getLastMonthArtifactsByType()
+ art_sci = self.user.stats.getArtifacts(category=topic._id)
+ art_by_cat = self.user.stats.getArtifactsByCategory(detailed=True)
+
+ assert lm_art['created'] == init_lm_art['created'] + 2
+ assert lm_art['modified'] == init_lm_art['modified']
+ assert artifacts['created'] == init_art['created'] + 3
+ assert artifacts['modified'] == init_art['modified']
+ assert art_wiki['created'] == init_art_wiki['created'] + 3
+ assert art_wiki['modified'] == init_art_wiki['modified']
+ assert art_by_type['Wiki']['created'] == init_art_by_type['Wiki']['created'] + 3
+ assert art_by_type['Wiki']['modified'] == init_art_by_type['Wiki']['modified']
+ assert lm_art_by_type['Wiki']['created'] == init_lm_art_by_type['Wiki']['created'] + 2
+ assert lm_art_by_type['Wiki']['modified'] == init_lm_art_by_type['Wiki']['modified']
+ assert art_sci['created'] == init_art_sci['created'] + 1
+ assert art_sci['modified'] == init_art_sci['modified']
+ assert dict(messagetype='Wiki', created= 1, modified = 0) in art_by_cat[topic]
+ art_by_cat = self.user.stats.getArtifactsByCategory(detailed=False)
+ assert art_by_cat[topic]['created'] == 1 and art_by_cat[topic]['modified'] == 0
+
+ @td.with_user_project('test-new-user')
+ def test_modify_artifact_stats(self):
+ p = Project.query.get(shortname='u/test-new-user')
+ topic = TroveCategory.query.get(shortname='scientific')
+
+ init_lm_art = self.user.stats.getLastMonthArtifacts()
+ init_art = self.user.stats.getArtifacts()
+ init_art_wiki = self.user.stats.getArtifacts(art_type='Wiki')
+ init_art_by_type = self.user.stats.getArtifactsByType()
+ init_lm_art_by_type = self.user.stats.getLastMonthArtifactsByType()
+ init_art_sci = self.user.stats.getArtifacts(category=topic._id)
+
+ self.user.stats.addModifiedArtifact('Wiki', datetime.utcnow(), p)
+ lm_art = self.user.stats.getLastMonthArtifacts()
+ artifacts = self.user.stats.getArtifacts()
+ art_wiki = self.user.stats.getArtifacts(art_type='Wiki')
+ art_by_type = self.user.stats.getArtifactsByType()
+ lm_art_by_type = self.user.stats.getLastMonthArtifactsByType()
+
+ assert lm_art['created'] == init_lm_art['created']
+ assert lm_art['modified'] == init_lm_art['modified'] + 1
+ assert artifacts['created'] == init_art['created']
+ assert artifacts['modified'] == init_art['modified'] + 1
+ assert art_wiki['created'] == init_art_wiki['created']
+ assert art_wiki['modified'] == init_art_wiki['modified'] + 1
+ assert art_by_type['Wiki']['created'] == init_art_by_type['Wiki']['created']
+ assert art_by_type['Wiki']['modified'] == init_art_by_type['Wiki']['modified'] + 1
+ assert lm_art_by_type['Wiki']['created'] == init_lm_art_by_type['Wiki']['created']
+ assert lm_art_by_type['Wiki']['modified'] == init_lm_art_by_type['Wiki']['modified'] + 1
+
+ #In that case, last month stats should not be changed
+ new_date = datetime.utcnow() + timedelta(-32)
+ self.user.stats.addModifiedArtifact('Wiki', new_date, p)
+ lm_art = self.user.stats.getLastMonthArtifacts()
+ artifacts = self.user.stats.getArtifacts()
+ art_wiki = self.user.stats.getArtifacts(art_type='Wiki')
+ art_by_type = self.user.stats.getArtifactsByType()
+ lm_art_by_type = self.user.stats.getLastMonthArtifactsByType()
+
+ assert lm_art['created'] == init_lm_art['created']
+ assert lm_art['modified'] == init_lm_art['modified'] + 1
+ assert artifacts['created'] == init_art['created']
+ assert artifacts['modified'] == init_art['modified'] + 2
+ assert art_wiki['created'] == init_art_wiki['created']
+ assert art_wiki['modified'] == init_art_wiki['modified'] + 2
+ assert art_by_type['Wiki']['created'] == init_art_by_type['Wiki']['created']
+ assert art_by_type['Wiki']['modified'] == init_art_by_type['Wiki']['modified'] + 2
+ assert lm_art_by_type['Wiki']['created'] == init_lm_art_by_type['Wiki']['created']
+ assert lm_art_by_type['Wiki']['modified'] == init_lm_art_by_type['Wiki']['modified'] + 1
+
+ p.trove_topic = [topic._id]
+
+ self.user.stats.addModifiedArtifact('Wiki', datetime.utcnow(), p)
+ lm_art = self.user.stats.getLastMonthArtifacts()
+ artifacts = self.user.stats.getArtifacts()
+ art_wiki = self.user.stats.getArtifacts(art_type='Wiki')
+ art_by_type = self.user.stats.getArtifactsByType()
+ lm_art_by_type = self.user.stats.getLastMonthArtifactsByType()
+ art_sci = self.user.stats.getArtifacts(category=topic._id)
+ art_by_cat = self.user.stats.getArtifactsByCategory(detailed=True)
+
+ assert lm_art['created'] == init_lm_art['created']
+ assert lm_art['modified'] == init_lm_art['modified'] + 2
+ assert artifacts['created'] == init_art['created']
+ assert artifacts['modified'] == init_art['modified'] + 3
+ assert art_wiki['created'] == init_art_wiki['created']
+ assert art_wiki['modified'] == init_art_wiki['modified'] + 3
+ assert art_by_type['Wiki']['created'] == init_art_by_type['Wiki']['created']
+ assert art_by_type['Wiki']['modified'] == init_art_by_type['Wiki']['modified'] + 3
+ assert lm_art_by_type['Wiki']['created'] == init_lm_art_by_type['Wiki']['created']
+ assert lm_art_by_type['Wiki']['modified'] == init_lm_art_by_type['Wiki']['modified'] +2
+ assert art_sci['created'] == init_art_sci['created']
+ assert art_sci['modified'] == init_art_sci['modified'] + 1
+ assert dict(messagetype='Wiki', created=0, modified=1) in art_by_cat[topic]
+ art_by_cat = self.user.stats.getArtifactsByCategory(detailed=False)
+ assert art_by_cat[topic]['created'] == 0 and art_by_cat[topic]['modified'] == 1
+
+ @td.with_user_project('test-new-user')
+ def test_ticket_stats(self):
+ p = Project.query.get(shortname='u/test-new-user')
+ topic = TroveCategory.query.get(shortname='scientific')
+ create_time = datetime.utcnow() + timedelta(-5)
+
+ init_lm_tickets_art = self.user.stats.getLastMonthArtifacts(art_type='Ticket')
+ init_tickets_art = self.user.stats.getArtifacts(art_type='Ticket')
+ init_tickets_sci_art = self.user.stats.getArtifacts(category=topic._id)
+ init_tickets = self.user.stats.getTickets()
+ init_lm_tickets = self.user.stats.getLastMonthTickets()
+
+ self.user.stats.addNewArtifact('Ticket', create_time, p)
+ lm_tickets_art = self.user.stats.getLastMonthArtifacts(art_type='Ticket')
+ tickets_art = self.user.stats.getArtifacts(art_type='Ticket')
+ tickets_sci_art = self.user.stats.getArtifacts(category=topic._id)
+
+ assert lm_tickets_art['created'] == init_lm_tickets_art['created'] + 1
+ assert lm_tickets_art['modified'] == init_lm_tickets_art['modified']
+ assert tickets_art['created'] == init_tickets_art['created'] + 1
+ assert tickets_art['modified'] == init_tickets_art['modified']
+ assert tickets_sci_art['created'] == tickets_sci_art['created']
+ assert tickets_sci_art['modified'] == tickets_sci_art['modified']
+
+ p.trove_topic = [topic._id]
+
+ self.user.stats.addAssignedTicket(create_time, p)
+ tickets = self.user.stats.getTickets()
+ lm_tickets = self.user.stats.getLastMonthTickets()
+
+ assert tickets['assigned'] == init_tickets['assigned'] + 1
+ assert tickets['revoked'] == init_tickets['revoked']
+ assert tickets['solved'] == init_tickets['solved']
+ assert tickets['averagesolvingtime'] is None
+ assert lm_tickets['assigned'] == init_lm_tickets['assigned'] + 1
+ assert lm_tickets['revoked'] == init_lm_tickets['revoked']
+ assert lm_tickets['solved'] == init_lm_tickets['solved']
+ assert lm_tickets['averagesolvingtime'] is None
+
+ self.user.stats.addRevokedTicket(create_time + timedelta(-32), p)
+ tickets = self.user.stats.getTickets()
+
+ assert tickets['assigned'] == init_tickets['assigned'] + 1
+ assert tickets['revoked'] == init_tickets['revoked'] + 1
+ assert tickets['solved'] == init_tickets['solved']
+ assert tickets['averagesolvingtime'] is None
+ assert lm_tickets['assigned'] == init_lm_tickets['assigned'] + 1
+ assert lm_tickets['revoked'] == init_lm_tickets['revoked']
+ assert lm_tickets['solved'] == init_lm_tickets['solved']
+ assert lm_tickets['averagesolvingtime'] is None
+
+ self.user.stats.addClosedTicket(create_time, create_time + timedelta(1), p)
+ tickets = self.user.stats.getTickets()
+ lm_tickets = self.user.stats.getLastMonthTickets()
+
+ assert tickets['assigned'] == init_tickets['assigned'] + 1
+ assert tickets['revoked'] == init_tickets['revoked'] + 1
+ assert tickets['solved'] == init_tickets['solved'] + 1
+
+ solving_time = dict(seconds=0,minutes=0,days=1,hours=0)
+ assert tickets['averagesolvingtime'] == solving_time
+ assert lm_tickets['assigned'] == init_lm_tickets['assigned'] + 1
+ assert lm_tickets['revoked'] == init_lm_tickets['revoked']
+ assert lm_tickets['solved'] == init_lm_tickets['solved'] + 1
+ assert lm_tickets['averagesolvingtime'] == solving_time
+
+ p.trove_topic = []
+ self.user.stats.addClosedTicket(create_time, create_time + timedelta(3), p)
+ tickets = self.user.stats.getTickets()
+ lm_tickets = self.user.stats.getLastMonthTickets()
+
+ solving_time = dict(seconds=0,minutes=0,days=2,hours=0)
+
+ assert tickets['assigned'] == init_tickets['assigned'] + 1
+ assert tickets['revoked'] == init_tickets['revoked'] + 1
+ assert tickets['solved'] == init_tickets['solved'] + 2
+ assert tickets['averagesolvingtime'] == solving_time
+ assert lm_tickets['assigned'] == init_lm_tickets['assigned'] + 1
+ assert lm_tickets['revoked'] == init_lm_tickets['revoked']
+ assert lm_tickets['solved'] == init_lm_tickets['solved'] + 2
+ assert lm_tickets['averagesolvingtime'] == solving_time
+
+ by_cat = self.user.stats.getTicketsByCategory()
+ lm_by_cat = self.user.stats.getLastMonthTicketsByCategory()
+ solving_time=dict(days=1,hours=0,minutes=0,seconds=0)
+
+ assert by_cat[topic]['assigned'] == 1
+ assert by_cat[topic]['revoked'] == 1
+ assert by_cat[topic]['solved'] == 1
+ assert by_cat[topic]['averagesolvingtime'] == solving_time
+ assert lm_by_cat[topic]['assigned'] == 1
+ assert lm_by_cat[topic]['revoked'] == 0
+ assert lm_by_cat[topic]['solved'] == 1
+ assert lm_by_cat[topic]['averagesolvingtime'] == solving_time
+
+ @with_git
+ @td.with_user_project('test-new-user')
+ def test_commit_stats(self):
+ p = Project.query.get(shortname='u/test-new-user')
+ topic = TroveCategory.query.get(shortname='scientific')
+ commit_time = datetime.utcnow() + timedelta(-1)
+
+ self.user.set_password('testpassword')
+ addr = M.EmailAddress.upsert('rcopeland@geek.net')
+ self.user.claim_address('rcopeland@geek.net')
+
+ repo_dir = pkg_resources.resource_filename(
+ 'forgeuserstats', 'tests/data')
+
+ c.app.repo.fs_path = repo_dir
+ c.app.repo.name = 'testgit.git'
+ repo = c.app.repo
+ repo.refresh()
+ commit = M.repo.Commit.query.get(_id=repo.heads[0]['object_id'])
+ commit.repo = repo
+
+ init_commits = self.user.stats.getCommits()
+ assert init_commits['number'] == 4
+ init_lmcommits = self.user.stats.getLastMonthCommits()
+ assert init_lmcommits['number'] == 4
+
+ p.trove_topic = [topic._id]
+ self.user.stats.addCommit(commit, datetime.utcnow(), p)
+ commits = self.user.stats.getCommits()
+ assert commits['number'] == init_commits['number'] + 1
+ assert commits['lines'] == init_commits['lines'] + 1
+ lmcommits = self.user.stats.getLastMonthCommits()
+ assert lmcommits['number'] == init_lmcommits['number'] + 1
+ assert lmcommits['lines'] == init_lmcommits['lines'] + 1
+ by_cat = self.user.stats.getCommitsByCategory()
+ assert by_cat[topic]['number'] == 1
+ assert by_cat[topic]['lines'] == 1
+ lm_by_cat = self.user.stats.getLastMonthCommitsByCategory()
+ assert lm_by_cat[topic]['number'] == 1
+ assert lm_by_cat[topic]['lines'] == 1
+
+ self.user.stats.addCommit(commit, datetime.utcnow() + timedelta(-40), p)
+ commits = self.user.stats.getCommits()
+ assert commits['number'] == init_commits['number'] + 2
+ assert commits['lines'] == init_commits['lines'] + 2
+ lmcommits = self.user.stats.getLastMonthCommits()
+ assert lmcommits['number'] == init_lmcommits['number'] + 1
+ assert lmcommits['lines'] == init_lmcommits['lines'] + 1
+ by_cat = self.user.stats.getCommitsByCategory()
+ assert by_cat[topic]['number'] == 2
+ assert by_cat[topic]['lines'] == 2
+ lm_by_cat = self.user.stats.getLastMonthCommitsByCategory()
+ assert lm_by_cat[topic]['number'] == 1
+ assert lm_by_cat[topic]['lines'] == 1
+
+ @td.with_user_project('test-new-user')
+ def test_login_stats(self):
+ init_logins = self.user.stats.tot_logins_count
+ init_lm_logins = self.user.stats.getLastMonthLogins()
+
+ login_datetime = datetime.utcnow()
+ self.user.stats.addLogin(login_datetime)
+ logins = self.user.stats.tot_logins_count
+ lm_logins = self.user.stats.getLastMonthLogins()
+ assert logins == init_logins + 1
+ assert lm_logins == init_lm_logins + 1
+ assert abs(self.user.stats.last_login - login_datetime) < timedelta(seconds=1)
+
+ self.user.stats.addLogin(datetime.utcnow() + timedelta(-32))
+ logins = self.user.stats.tot_logins_count
+ lm_logins = self.user.stats.getLastMonthLogins()
+ assert logins == init_logins + 2
+ assert lm_logins == init_lm_logins + 1
+ assert abs(self.user.stats.last_login - login_datetime) < timedelta(seconds=1)
+
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/a5c5aad6/ForgeUserStats/forgeuserstats/tests/test_stats.py
----------------------------------------------------------------------
diff --git a/ForgeUserStats/forgeuserstats/tests/test_stats.py b/ForgeUserStats/forgeuserstats/tests/test_stats.py
index a3e3482..e4954b7 100644
--- a/ForgeUserStats/forgeuserstats/tests/test_stats.py
+++ b/ForgeUserStats/forgeuserstats/tests/test_stats.py
@@ -16,38 +16,6 @@ from forgetracker import model as TM
class TestStats(TestController):
- @td.with_user_project('test-user')
- def test_init_values(self):
- user = User.register(dict(username='test-new-user',
- display_name='Test Stats'),
- make_project=False)
-
- artifacts = user.stats.getArtifacts()
- tickets = user.stats.getTickets()
- commits = user.stats.getCommits()
- assert user.stats.tot_logins_count == 0
- assert artifacts['created'] == 0
- assert artifacts['modified'] == 0
- assert tickets['assigned'] == 0
- assert tickets['solved'] == 0
- assert tickets['revoked'] == 0
- assert tickets['averagesolvingtime'] is None
- assert commits['number'] == 0
- assert commits['lines'] == 0
-
- lmartifacts = user.stats.getLastMonthArtifacts()
- lmtickets = user.stats.getLastMonthTickets()
- lmcommits = user.stats.getLastMonthCommits()
- assert user.stats.getLastMonthLogins() == 0
- assert lmartifacts['created'] == 0
- assert lmartifacts['modified'] == 0
- assert lmtickets['assigned'] == 0
- assert lmtickets['solved'] == 0
- assert lmtickets['revoked'] == 0
- assert lmtickets['averagesolvingtime'] is None
- assert lmcommits['number'] == 0
- assert lmcommits['lines'] == 0
-
def test_login(self):
user = User.by_username('test-user')
init_logins = c.user.stats.tot_logins_count
@@ -198,4 +166,3 @@ class TestGitCommit(unittest.TestCase, TestController):
assert commits['number'] == 4
lmcommits = c.user.stats.getLastMonthCommits()
assert lmcommits['number'] == 4
-