diff --git a/docs/_data/navigation.yml b/docs/_data/navigation.yml
index 096c1d83..a9ccbd0e 100644
--- a/docs/_data/navigation.yml
+++ b/docs/_data/navigation.yml
@@ -43,6 +43,8 @@
url: "/housekeeping-git-requests"
- title: "API Requests"
url: "/housekeeping-api-requests"
+ - title: "Repository Location"
+ url: "/housekeeping-repo-personal-non-owner-push"
- title: "Forks"
url: "/housekeeping-forks"
- title: "Recommendations"
diff --git a/docs/demo-data/repositories-personal-non-owner-push-detailed.tsv b/docs/demo-data/repositories-personal-non-owner-push-detailed.tsv
new file mode 100644
index 00000000..2ad0f7e1
--- /dev/null
+++ b/docs/demo-data/repositories-personal-non-owner-push-detailed.tsv
@@ -0,0 +1,3 @@
+repository
+alice/something
+bob/else
diff --git a/docs/demo-data/repositories-personal-non-owner-push.tsv b/docs/demo-data/repositories-personal-non-owner-push.tsv
new file mode 100644
index 00000000..a9447e43
--- /dev/null
+++ b/docs/demo-data/repositories-personal-non-owner-push.tsv
@@ -0,0 +1,4 @@
+date personal repositories with nonowner pushes
+2018-01-25 98
+2018-01-24 108
+2018-01-23 110
diff --git a/docs/housekeeping-repo-personal-non-owner-push.html b/docs/housekeeping-repo-personal-non-owner-push.html
new file mode 100644
index 00000000..11c9709a
--- /dev/null
+++ b/docs/housekeeping-repo-personal-non-owner-push.html
@@ -0,0 +1,24 @@
+---
+layout: default
+title: Forks
+permalink: /housekeeping-repo-personal-non-owner-push
+---
+
+
+
Personal Repositories with Nonowner Pushes in the Last 4 Weeks
+
+
+
+ Repositories in user accounts should only be pushed to by their owners as these repositories might become unavailable or deleted if the owner leaves the company.
+
+
Repositories, in which people actively collaborate, should be transferred to an organization account.
+
+
+
+
+
diff --git a/updater/reports/ReportReposPersonalNonOwnerPush.py b/updater/reports/ReportReposPersonalNonOwnerPush.py
new file mode 100644
index 00000000..3fe2c135
--- /dev/null
+++ b/updater/reports/ReportReposPersonalNonOwnerPush.py
@@ -0,0 +1,38 @@
+from .ReportDaily import *
+
+# Find personal repositories that non-owners are pushing to.
+# These repositories should be moved into organizations.
+# Only look at active users (not suspended!) and only look at pushes
+# of the last 4 weeks.
+class ReportReposPersonalNonOwnerPush(ReportDaily):
+ def name(self):
+ return "repositories-personal-non-owner-push"
+
+ def updateDailyData(self):
+ self.detailedHeader, self.detailedData = self.parseData(self.executeQuery(self.query()))
+ if len(self.data) == 0:
+ self.header = ["date", "personal repositories with nonowner pushes"]
+ self.data.append([str(self.yesterday()), len(self.detailedData)])
+ self.truncateData(self.timeRangeTotal())
+ self.sortDataByDate()
+
+ def query(self):
+ fourWeeksAgo = self.daysAgo(28)
+ query = '''
+ SELECT
+ CONCAT(users.login, "/", repositories.name) as "repository"
+ FROM
+ repositories
+ JOIN users ON repositories.owner_id = users.id
+ JOIN pushes ON pushes.repository_id = repositories.id
+ WHERE
+ users.type = "user"
+ AND users.suspended_at IS NULL
+ AND CAST(pushes.created_at AS DATE) BETWEEN "''' + str(fourWeeksAgo) + '''" AND "''' + str(self.yesterday()) + '''"
+ AND pushes.pusher_id != users.id
+ GROUP BY
+ repositories.id
+ ORDER BY
+ 1
+ '''
+ return query
diff --git a/updater/update-stats.py b/updater/update-stats.py
index 1b1c65df..fe637d4f 100755
--- a/updater/update-stats.py
+++ b/updater/update-stats.py
@@ -23,6 +23,7 @@
from reports.ReportPRUsage import *
from reports.ReportRepoActivity import *
from reports.ReportRepositoryHistory import *
+from reports.ReportReposPersonalNonOwnerPush import *
from reports.ReportTeamsTotal import *
from reports.ReportTokenlessAuth import *
from reports.ReportUsers import *
@@ -93,6 +94,7 @@ def main():
ReportPRUsage(configuration, dataDirectory, metaStats).update()
ReportRepoActivity(configuration, dataDirectory, metaStats).update()
ReportRepositoryHistory(configuration, dataDirectory, metaStats).update()
+ ReportReposPersonalNonOwnerPush(configuration, dataDirectory, metaStats).update()
ReportTeamsTotal(configuration, dataDirectory, metaStats).update()
ReportTokenlessAuth(configuration, dataDirectory, metaStats).update()
ReportUsers(configuration, dataDirectory, metaStats).update()