diff --git a/.moban.yaml b/.moban.yaml index f8b46e9c..db3709b8 100644 --- a/.moban.yaml +++ b/.moban.yaml @@ -9,7 +9,7 @@ packages: - gci - gsoc - gamification - - log + - ci_build - meta_review - model - twitter diff --git a/.nocover.yaml b/.nocover.yaml index 987773ce..62ff6ba5 100644 --- a/.nocover.yaml +++ b/.nocover.yaml @@ -8,7 +8,7 @@ nocover_file_globs: - community/git.py - gci/*.py - gsoc/*.py - - log/*.py + - ci_build/*.py - meta_review/handler.py - model/*.py - openhub/*.py diff --git a/ci_build/view_log.py b/ci_build/view_log.py new file mode 100644 index 00000000..cff96617 --- /dev/null +++ b/ci_build/view_log.py @@ -0,0 +1,103 @@ +import re +import json +import os +import sys + +from django.views.generic import TemplateView + +from community.views import get_header_and_footer +from community.git import ( + get_org_name, + get_owner, + get_deploy_url, + get_upstream_deploy_url +) + + +class BuildLogsView(TemplateView): + template_name = 'build_logs.html' + + def get_build_info(self): + data = { + 'Org name': get_org_name(), + 'Owner': get_owner(), + 'Deploy URL': get_deploy_url(), + } + try: + upstream_deploy_url = get_upstream_deploy_url() + data['Upstream deploy URL'] = upstream_deploy_url + except RuntimeError: + data['Upstream deploy URL'] = 'Not found' + return data + + def get_build_logs(self, log_file_path): + log_lines = [] + log_level_specific_lines = { + 'INFO': [], + 'DEBUG': [], + 'WARNING': [], + 'ERROR': [], + 'CRITICAL': [] + } + with open(log_file_path) as log_file: + previous_found_level = None + for line in log_file: + log_lines.append(line) + levels = re.findall(r'\[[A-Z]+]', line) + if levels: + level = levels[0] + level = previous_found_level = level[1:-1] + log_level_specific_lines[level].append(line) + elif previous_found_level: + log_level_specific_lines[previous_found_level].append( + line) + return log_lines, log_level_specific_lines + + def create_and_copy_build_logs_json(self, logs, level_specific_logs): + ci_build_jsons = { + 'site_path': './_site/ci-build-detailed-logs.json', + 'public_path': './public/static/ci-build-detailed-logs.json', + 'static_path': './static/ci-build-detailed-logs.json' + } + with open(ci_build_jsons['site_path'], 'w+') as build_logs_file: + data = { + 'logs': logs, + 'logs_level_Specific': level_specific_logs + } + json.dump(data, build_logs_file, indent=4) + return self.copy_build_logs_json(ci_build_jsons) + + def copy_build_logs_json(self, ci_build_jsons): + if os.path.isfile(ci_build_jsons['public_path']): + if sys.platform == 'linux': + os.popen('cp {} {}'.format( + ci_build_jsons['site_path'], + ci_build_jsons['public_path'])) + os.popen('cp {} {}'.format( + ci_build_jsons['site_path'], + ci_build_jsons['static_path'])) + else: + os.popen('copy {} {}'.format( + ci_build_jsons['site_path'], + ci_build_jsons['public_path'])) + os.popen('copy {} {}'.format( + ci_build_jsons['site_path'], + ci_build_jsons['static_path'])) + return True + return False + + def check_build_logs_stored(self): + log_file_path = './_site/community.log' + log_file_exists = os.path.isfile(log_file_path) + if log_file_exists: + logs, level_specific_logs = self.get_build_logs(log_file_path) + return self.create_and_copy_build_logs_json(logs, + level_specific_logs) + return False + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context = get_header_and_footer(context) + context['build_info'] = self.get_build_info() + context['logs_stored'] = self.check_build_logs_stored() + return context diff --git a/community/urls.py b/community/urls.py index c03de2e2..f2ac4b41 100644 --- a/community/urls.py +++ b/community/urls.py @@ -6,11 +6,11 @@ from django.conf.urls.static import static from django.conf import settings -from community.views import HomePageView, info +from community.views import HomePageView from gci.views import index as gci_index from gci.feeds import LatestTasksFeed as gci_tasks_rss from twitter.view_twitter import index as twitter_index -from log.view_log import index as log_index +from ci_build.view_log import BuildLogsView from data.views import index as contributors_index from gamification.views import index as gamification_index from meta_review.views import index as meta_review_index @@ -79,12 +79,6 @@ def get_organization(): distill_func=get_index, distill_file='index.html', ), - distill_url( - 'info.txt', info, - name='index', - distill_func=get_index, - distill_file='info.txt', - ), distill_url( r'gci/tasks/rss.xml', gci_tasks_rss(), name='gci-tasks-rss', @@ -104,10 +98,10 @@ def get_organization(): distill_file='twitter/index.html', ), distill_url( - r'log/', log_index, - name='log', + r'CI/Build/', BuildLogsView.as_view(), + name='ci_build', distill_func=get_index, - distill_file='log/index.html', + distill_file='CI/Build/index.html', ), distill_url( r'contributors/$', contributors_index, diff --git a/community/views.py b/community/views.py index 4f98bebe..8e691fee 100644 --- a/community/views.py +++ b/community/views.py @@ -4,14 +4,10 @@ from trav import Travis -from django.http import HttpResponse from django.views.generic.base import TemplateView from .git import ( - get_deploy_url, get_org_name, - get_owner, - get_upstream_deploy_url, get_remote_url ) from data.models import Team @@ -111,20 +107,3 @@ def get_context_data(self, **kwargs): context['top_gamification_users'] = self.get_top_gamification_users( count=5) return context - - -def info(request): - data = { - 'Org name': get_org_name(), - 'Owner': get_owner(), - 'Deploy URL': get_deploy_url(), - } - try: - upstream_deploy_url = get_upstream_deploy_url() - data['Upstream deploy URL'] = upstream_deploy_url - except RuntimeError: - data['Upstream deploy URL'] = 'Not found' - - s = '\n'.join(name + ': ' + value - for name, value in data.items()) - return HttpResponse(s) diff --git a/log/view_log.py b/log/view_log.py deleted file mode 100644 index facc4190..00000000 --- a/log/view_log.py +++ /dev/null @@ -1,12 +0,0 @@ -from django.http import HttpResponse - - -def index(request): - logs = get_logs() - return HttpResponse('
'.join(logs)) - - -def get_logs(): - with open('./_site/community.log') as log_file: - for line in log_file: - yield line.rstrip() diff --git a/setup.cfg b/setup.cfg index 4087933a..711d1e40 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,7 +14,7 @@ testpaths = gci gsoc gamification - log + ci_build meta_review model twitter @@ -70,7 +70,7 @@ source = gci gsoc gamification - log + ci_build meta_review model twitter @@ -82,7 +82,7 @@ omit = community/git.py gci/*.py gsoc/*.py - log/*.py + ci_build/*.py meta_review/handler.py model/*.py openhub/*.py diff --git a/static/css/build_logs.css b/static/css/build_logs.css new file mode 100644 index 00000000..36d1af38 --- /dev/null +++ b/static/css/build_logs.css @@ -0,0 +1,86 @@ +.build-info-section section, +.build-logs-section section { + min-width: 300px; + width: 80%; +} + +.build-information, +.build-logs { + background-color: black; + padding-left: 10px; + font-weight: bold; + color: white; +} + +.build-information p { + font-size: 1.5em; + margin: 0; +} + +.build-logs { + max-height: 900px; + overflow: scroll; + overflow-x: hidden; + overflow-y: auto; +} + +.build-logs p { + margin: 0; +} + +.build-logs-section .log-chooser { + width: 25%; + min-width: 150px; + border-radius: 100px; + box-shadow: 0px 0px 25px 2px black; + color: #454343; + background-color: #c7da99; + padding-left: 10px; + margin: auto; + margin-right: 0; +} + +.build-logs-section .log-chooser input, +.build-logs-section .log-chooser input:focus:not(.browser-default) { + border-bottom: none; + margin-bottom: 0; +} + +.build-logs-section .small-screen, +.build-logs-section .fa-close { + display: none; +} + +.form-fields { + margin: auto; + margin-right: 0; + width: 60%; + padding-top: 10px; +} + +.search-field { + width: 60%; + min-width: 180px; +} + +.section-header { + display: flex; + align-items: center; +} + +.section-header form { + display: flex; +} + +@media only screen and (max-width: 660px) { + .build-logs-section .search-field { + display: none; + } + .build-logs-section .small-screen { + display: flex; + align-items: center; + margin: auto; + margin-right: 3px; + font-size: 2em; + } +} diff --git a/static/js/build_logs.js b/static/js/build_logs.js new file mode 100644 index 00000000..534db251 --- /dev/null +++ b/static/js/build_logs.js @@ -0,0 +1,102 @@ +$(document).ready(function(){ + $('select').formSelect(); + + var log_chooser_input = $('#log-chooser-input'); + var search_input = $('#search'); + var search_icon = $('.build-logs-section .small-screen'); + var close_icon = $('.build-logs-section .fa-close'); + var log_chooser_div = $('.build-logs-section .log-chooser'); + var search_field_div = $('.build-logs-section .search-field'); + var log_type = null; + var logs_data = null; + var searched_keyword = ''; + + function addLogsHTML(info){ + var info_el = $('

').text(info); + $('.build-logs').append(info_el); + } + + function updateBuildLogsHTML(){ + $('.build-logs p').remove(); + if(logs_data.length > 0) { + for(var entry in logs_data){ + if(logs_data[entry]){ + addLogsHTML(logs_data[entry]); + } + } + } + else { + var info = 'There are no log entries for tag ' + log_type + '.'; + addLogsHTML(info); + } + } + + function updateBuildLogs(type){ + $.getJSON("/static/ci-build-detailed-logs.json", function(data) { + log_type = type; + if(log_type === 'logs') { + logs_data = data[log_type]; + } + else { + logs_data = data.logs_level_Specific[log_type]; + } + updateBuildLogsHTML(); + }) + .fail(function(data, textStatus, error) { + var err = "Request Failed: " + textStatus + ", " + error; + console.error(err); + }); + } + + function searchBuildLogs(){ + var found = false; + var info = ''; + for(var entry in logs_data){ + if(logs_data[entry]){ + info = logs_data[entry]; + if(info.includes(searched_keyword)){ + found = true; + addLogsHTML(info); + } + } + } + if(!found){ + if(log_type === 'logs'){ + info = searched_keyword + ' not found in logs!'; + } + else { + info = searched_keyword + ' not found in ' + log_type + + ' level logs!'; + } + addLogsHTML(info); + } + } + + updateBuildLogs('logs'); + + log_chooser_input.on('change', function(){ + updateBuildLogs(log_chooser_input.val()); + }); + + search_input.on('keypress', function(key){ + if(key.which === 13){ + searched_keyword = search_input.val(); + $('.build-logs p').remove(); + searchBuildLogs(); + } + }); + + search_icon.on('click', function(){ + search_icon.css('display', 'none'); + close_icon.css('display', 'block'); + log_chooser_div.css('display', 'none'); + search_field_div.css('display', 'flex'); + }); + close_icon.on('click', function(){ + search_icon.css('display', 'flex'); + close_icon.css('display', 'none'); + log_chooser_div.css('display', 'flex'); + search_field_div.css('display', 'none'); + }); + +}); diff --git a/templates/base.html b/templates/base.html index cdc564a2..9638dfdc 100644 --- a/templates/base.html +++ b/templates/base.html @@ -29,7 +29,7 @@