From 782405fe657976dcfaea9f0aaed3df5ccaa754b7 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 2 Dec 2013 14:55:04 -0500 Subject: [PATCH] - Add log view under repos - Make the logs a bit nicer by adding context-sensitive icons - Fix some of the log descriptions --- data/model.py | 5 ++- endpoints/api.py | 34 ++++++++++++++---- static/css/quay.css | 5 +++ static/js/app.js | 64 ++++++++++++++++++++++++++------- static/js/controllers.js | 5 +++ static/js/graphing.js | 10 ++++-- static/partials/repo-admin.html | 8 ++++- 7 files changed, 108 insertions(+), 23 deletions(-) diff --git a/data/model.py b/data/model.py index ac56af88d..0ebc95125 100644 --- a/data/model.py +++ b/data/model.py @@ -1058,9 +1058,12 @@ def delete_webhook(namespace_name, repository_name, public_id): webhook.delete_instance() return webhook -def list_logs(user_or_organization_name): +def list_logs(user_or_organization_name, repository = None): week_ago = datetime.today() - timedelta(7) # One week joined = LogEntry.select().join(User) + if repository: + joined = joined.where(LogEntry.repository == repository) + return joined.where(User.username == user_or_organization_name, LogEntry.datetime >= week_ago).order_by(LogEntry.datetime.desc()) def log_action(kind_name, user_or_organization_name, performer=None, repository=None, diff --git a/endpoints/api.py b/endpoints/api.py index 8afc2bb9f..c21bb652d 100644 --- a/endpoints/api.py +++ b/endpoints/api.py @@ -713,6 +713,27 @@ def update_repo_api(namespace, repository): abort(403) +@app.route('/api/repository//logs', methods=['GET']) +@api_login_required +@parse_repository_name +def repo_logs_api(namespace, repository): + permission = AdministerRepositoryPermission(namespace, repository) + if permission.can(): + print namespace + print repository + repo = model.get_repository(namespace, repository) + print repo + if not repo: + abort(404) + + logs = model.list_logs(namespace, repository = repo) + return jsonify({ + 'logs': [log_view(log) for log in logs] + }) + + abort(403) + + @app.route('/api/repository//changevisibility', methods=['POST']) @api_login_required @@ -1201,7 +1222,7 @@ def change_team_permissions(namespace, repository, teamname): new_permission['role']) log_action('change_repo_permission', namespace, - {'team': team, 'repo': repository, 'role': new_permission['role']}, + {'team': teamname, 'repo': repository, 'role': new_permission['role']}, repo=model.get_repository(namespace, repository)) resp = jsonify(role_view(perm)) @@ -1324,7 +1345,7 @@ def change_token(namespace, repository, code): new_permission['role']) log_action('change_repo_permission', namespace, - {'repo': repository, 'token': token.friendly_name, 'code': code}, + {'repo': repository, 'token': token.friendly_name, 'code': code, 'role': new_permission['role']}, repo = model.get_repository(namespace, repository)) resp = jsonify(token_view(token)) @@ -1688,10 +1709,7 @@ def delete_org_robot(orgname, robot_shortname): abort(403) -@app.route('/api/organization//logs', methods=['GET']) -@api_login_required -def org_logs_api(orgname): - def log_view(log): +def log_view(log): view = { 'kind': log.kind.name, 'metadata': json.loads(log.metadata_json), @@ -1707,6 +1725,9 @@ def org_logs_api(orgname): return view +@app.route('/api/organization//logs', methods=['GET']) +@api_login_required +def org_logs_api(orgname): permission = AdministerOrganizationPermission(orgname) if permission.can(): logs = model.list_logs(orgname) @@ -1715,3 +1736,4 @@ def org_logs_api(orgname): }) abort(403) + diff --git a/static/css/quay.css b/static/css/quay.css index 83e16f588..641c3a1d3 100644 --- a/static/css/quay.css +++ b/static/css/quay.css @@ -21,6 +21,7 @@ html, body { border-bottom: 1px dashed #aaa; } + .entity-reference .prefix { color: #aaa; } @@ -142,6 +143,10 @@ html, body { float: right; } +.logs-view-element .log i.fa { + margin-right: 6px; +} + .logs-view-element .log .circle { display: inline-block; width: 12px; diff --git a/static/js/app.js b/static/js/app.js index 766900ec7..5ad1c9482 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -678,7 +678,8 @@ quayApp.directive('logsView', function () { scope: { 'organization': '=organization', 'user': '=user', - 'visible': '=visible' + 'visible': '=visible', + 'repository': '=repository' }, controller: function($scope, $element, $sce, Restangular) { $scope.loading = true; @@ -696,17 +697,33 @@ quayApp.directive('logsView', function () { 'create_repo': 'Create Repository: {repo}', 'push_repo': 'Push to repository: {repo}', 'pull_repo': function(metadata) { - if (metadata.token) { - return 'Pull repository {repo} via token {token}'; - } else if (metadata.username) { - return 'Pull repository {repo} by {username}'; - } else { - return 'Public pull of repository {repo} by {_ip}'; - } + if (metadata.token) { + return 'Pull repository {repo} via token {token}'; + } else if (metadata.username) { + return 'Pull repository {repo} by {username}'; + } else { + return 'Public pull of repository {repo} by {_ip}'; + } }, 'delete_repo': 'Delete repository: {repo}', - 'change_repo_permission': 'Change permission for user {username} in repository {repo} to {role}', - 'delete_repo_permission': 'Remove permission for user {username} from repository {repo}', + 'change_repo_permission': function(metadata) { + if (metadata.username) { + return 'Change permission for user {username} in repository {repo} to {role}'; + } else if (metadata.team) { + return 'Change permission for team {team} in repository {repo} to {role}'; + } else if (metadata.token) { + return 'Change permission for token {token} in repository {repo} to {role}'; + } + }, + 'delete_repo_permission': function(metadata) { + if (metadata.username) { + return 'Remove permission for user {username} from repository {repo}'; + } else if (metadata.team) { + return 'Remove permission for team {team} from repository {repo}'; + } else if (metadata.token) { + return 'Remove permission for token {token} from repository {repo}'; + } + }, 'change_repo_visibility': 'Change visibility for repository {repo} to {visibility}', 'add_repo_accesstoken': 'Create access token {token} in repository {repo}', 'delete_repo_accesstoken': 'Delete access token {token} in repository {repo}', @@ -751,14 +768,20 @@ quayApp.directive('logsView', function () { }; var update = function() { - if (!$scope.visible || (!$scope.organization && !$scope.user)) { + if (!$scope.visible || (!$scope.organization && !$scope.user && !$scope.repository)) { return; } $scope.loading = true; - var url = $scope.organization ? getRestUrl('organization', $scope.organization.name, 'logs') : - getRestUrl('user/logs'); + var url = getRestUrl('user/logs'); + if ($scope.organization) { + url = getRestUrl('organization', $scope.organization.name, 'logs'); + } + if ($scope.repository) { + url = getRestUrl('repository', $scope.repository.namespace, $scope.repository.name, 'logs'); + } + var loadLogs = Restangular.one(url); loadLogs.customGET().then(function(resp) { if (!$scope.chart) { @@ -788,6 +811,14 @@ quayApp.directive('logsView', function () { }; $scope.getDescription = function(log) { + var fieldIcons = { + 'username': 'user', + 'team': 'group', + 'token': 'key', + 'repo': 'hdd', + 'robot': 'wrench' + }; + log.metadata['_ip'] = log.ip; var description = logDescriptions[log.kind] || logTitles[log.kind] || log.kind; @@ -799,6 +830,12 @@ quayApp.directive('logsView', function () { if (log.metadata.hasOwnProperty(key)) { var markedDown = getMarkedDown(log.metadata[key].toString()); markedDown = markedDown.substr('

'.length, markedDown.length - '

'.length); + + var icon = fieldIcons[key]; + if (icon) { + markedDown = '' + markedDown; + } + description = description.replace('{' + key + '}', '' + markedDown + ''); } } @@ -807,6 +844,7 @@ quayApp.directive('logsView', function () { $scope.$watch('organization', update); $scope.$watch('user', update); + $scope.$watch('repository', update); $scope.$watch('visible', update); } }; diff --git a/static/js/controllers.js b/static/js/controllers.js index 5e913b7c2..4d02f0571 100644 --- a/static/js/controllers.js +++ b/static/js/controllers.js @@ -426,6 +426,11 @@ function RepoAdminCtrl($scope, Restangular, $routeParams, $rootScope) { var name = $routeParams.name; $scope.permissions = {'team': [], 'user': []}; + $scope.logsShown = 0; + + $scope.loadLogs = function() { + $scope.logsShown++; + }; $scope.grantRole = function() { $('#confirmaddoutsideModal').modal('hide'); diff --git a/static/js/graphing.js b/static/js/graphing.js index 798f8a1f1..52a7a5958 100644 --- a/static/js/graphing.js +++ b/static/js/graphing.js @@ -1289,7 +1289,12 @@ LogUsageChart.prototype.buildData_ = function(logs) { var log = logs[i]; var title = this.titleMap_[log.kind] || log.kind; var datetime = parseDate(log.datetime); - var formatted = (datetime.getMonth() + 1) + '/' + datetime.getDate(); + var dateDay = datetime.getDate(); + if (dateDay < 10) { + dateDay = '0' + dateDay; + } + + var formatted = (datetime.getMonth() + 1) + '/' + dateDay; var adjusted = new Date(datetime.getFullYear(), datetime.getMonth(), datetime.getDate()); var key = title + '_' + formatted; var found = map[key]; @@ -1374,7 +1379,8 @@ LogUsageChart.prototype.buildData_ = function(logs) { * Renders the tooltip when hovering over an element in the chart. */ LogUsageChart.prototype.renderTooltip_ = function(d, e) { - var entry = this.entries_[d + '_' + e]; + var key = d + '_' + e; + var entry = this.entries_[key]; if (!entry) { entry = {'count': 0}; } diff --git a/static/partials/repo-admin.html b/static/partials/repo-admin.html index 7b5b0f588..eda830d97 100644 --- a/static/partials/repo-admin.html +++ b/static/partials/repo-admin.html @@ -23,13 +23,19 @@
  • Web Hooks
  • Public/Private
  • Delete
  • +
  • Usage Logs
  • -
    +
    + +
    +
    +
    +