Skip to content

Commit 77d038d

Browse files
committed
ci_build/: Re-design build-logs webpage
The newly created webpage combines the previous two webpages- info.txt and log/index.html. This web-page combines the results of both the pages and shows them in a better UI/UX with additional features of filtering and searching within the existing logs. The logs are fetched from a JSON file which is created from the logs stored in the log file _site/community.log Closes coala#256
1 parent 7f2877a commit 77d038d

File tree

11 files changed

+404
-51
lines changed

11 files changed

+404
-51
lines changed

.moban.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ packages:
99
- gci
1010
- gsoc
1111
- gamification
12-
- log
12+
- ci_build
1313
- meta_review
1414
- model
1515
- unassigned_issues

.nocover.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ nocover_file_globs:
88
- community/git.py
99
- gci/*.py
1010
- gsoc/*.py
11-
- log/*.py
11+
- ci_build/*.py
1212
- meta_review/handler.py
1313
- model/*.py
1414
- openhub/*.py

ci_build/view_log.py

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import re
2+
import json
3+
import os
4+
import sys
5+
6+
from django.views.generic import TemplateView
7+
8+
from community.views import get_header_and_footer
9+
from community.git import (
10+
get_org_name,
11+
get_owner,
12+
get_deploy_url,
13+
get_upstream_deploy_url
14+
)
15+
16+
17+
class BuildLogsView(TemplateView):
18+
template_name = 'build_logs.html'
19+
20+
def copy_build_logs_json(self, ci_build_jsons):
21+
"""
22+
Copy the build logs detailed JSON file from ./_site directory to
23+
./static and ./public directories
24+
:param ci_build_jsons: A dict of directories path
25+
:return: A boolean, whether the build file is copied
26+
"""
27+
if os.path.isfile(ci_build_jsons['public_path']):
28+
if sys.platform == 'linux':
29+
os.popen('cp {} {}'.format(
30+
ci_build_jsons['site_path'],
31+
ci_build_jsons['public_path']))
32+
os.popen('cp {} {}'.format(
33+
ci_build_jsons['site_path'],
34+
ci_build_jsons['static_path']))
35+
else:
36+
os.popen('copy {} {}'.format(
37+
ci_build_jsons['site_path'],
38+
ci_build_jsons['public_path']))
39+
os.popen('copy {} {}'.format(
40+
ci_build_jsons['site_path'],
41+
ci_build_jsons['static_path']))
42+
return True
43+
return False
44+
45+
def create_and_copy_build_logs_json(self, logs, level_specific_logs):
46+
"""
47+
Create a build logs detailed json file in ./_site directory and copy
48+
that file in the ./static and ./public/static directories
49+
:param logs: A list of all lines in build log file
50+
:param level_specific_logs: A dict containing logs divided in their
51+
respective categories
52+
:return: A boolean, whether the files were copied or not
53+
"""
54+
ci_build_jsons = {
55+
'site_path': './_site/ci-build-detailed-logs.json',
56+
'public_path': './public/static/ci-build-detailed-logs.json',
57+
'static_path': './static/ci-build-detailed-logs.json'
58+
}
59+
with open(ci_build_jsons['site_path'], 'w+') as build_logs_file:
60+
data = {
61+
'logs': logs,
62+
'logs_level_Specific': level_specific_logs
63+
}
64+
json.dump(data, build_logs_file, indent=4)
65+
return self.copy_build_logs_json(ci_build_jsons)
66+
67+
def get_build_logs(self, log_file_path):
68+
"""
69+
:param log_file_path: build logs file path
70+
:return: a tuple of two where the first element in tuple refers to
71+
a list of build logs in the file, and the second element is a dict
72+
which categorizes the build logs into 5 categories - INFO, DEBUG,
73+
WARNING, ERROR nad CRITICAL
74+
"""
75+
log_lines = []
76+
log_level_specific_lines = {
77+
'INFO': [],
78+
'DEBUG': [],
79+
'WARNING': [],
80+
'ERROR': [],
81+
'CRITICAL': []
82+
}
83+
with open(log_file_path) as log_file:
84+
previous_found_level = None
85+
for line in log_file:
86+
log_lines.append(line)
87+
levels = re.findall(r'\[[A-Z]+]', line)
88+
if levels:
89+
level = levels[0]
90+
level = previous_found_level = level[1:-1]
91+
log_level_specific_lines[level].append(line)
92+
elif previous_found_level:
93+
log_level_specific_lines[previous_found_level].append(
94+
line)
95+
return log_lines, log_level_specific_lines
96+
97+
def check_build_logs_stored(self):
98+
"""
99+
Check whether the build logs json file is copied to _site and public
100+
directories or not
101+
:return: A Boolean
102+
"""
103+
log_file_path = './_site/community.log'
104+
log_file_exists = os.path.isfile(log_file_path)
105+
if log_file_exists:
106+
logs, level_specific_logs = self.get_build_logs(log_file_path)
107+
return self.create_and_copy_build_logs_json(logs,
108+
level_specific_logs)
109+
return False
110+
111+
def get_build_info(self):
112+
"""
113+
Get the information about build, like who deployed the website i.e.
114+
owner, name of the organization or user etc.
115+
:return: A dict having information about build related details
116+
"""
117+
data = {
118+
'Org name': get_org_name(),
119+
'Owner': get_owner(),
120+
'Deploy URL': get_deploy_url(),
121+
}
122+
try:
123+
data['Upstream deploy URL'] = get_upstream_deploy_url()
124+
except RuntimeError:
125+
data['Upstream deploy URL'] = 'Not found'
126+
return data
127+
128+
def get_context_data(self, **kwargs):
129+
context = super().get_context_data(**kwargs)
130+
context = get_header_and_footer(context)
131+
context['build_info'] = self.get_build_info()
132+
context['logs_stored'] = self.check_build_logs_stored()
133+
return context

community/urls.py

+5-11
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
from django.conf.urls.static import static
77
from django.conf import settings
88

9-
from community.views import HomePageView, info
9+
from community.views import HomePageView
1010
from gci.views import index as gci_index
1111
from gci.feeds import LatestTasksFeed as gci_tasks_rss
12-
from log.view_log import index as log_index
12+
from ci_build.view_log import BuildLogsView
1313
from data.views import index as contributors_index
1414
from gamification.views import index as gamification_index
1515
from meta_review.views import index as meta_review_index
@@ -78,12 +78,6 @@ def get_organization():
7878
distill_func=get_index,
7979
distill_file='index.html',
8080
),
81-
distill_url(
82-
'info.txt', info,
83-
name='index',
84-
distill_func=get_index,
85-
distill_file='info.txt',
86-
),
8781
distill_url(
8882
r'gci/tasks/rss.xml', gci_tasks_rss(),
8983
name='gci-tasks-rss',
@@ -97,10 +91,10 @@ def get_organization():
9791
distill_file='gci/index.html',
9892
),
9993
distill_url(
100-
r'log/', log_index,
101-
name='log',
94+
r'ci/build/', BuildLogsView.as_view(),
95+
name='ci_build',
10296
distill_func=get_index,
103-
distill_file='log/index.html',
97+
distill_file='ci/build/index.html',
10498
),
10599
distill_url(
106100
r'contributors/$', contributors_index,

community/views.py

-21
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,10 @@
44

55
from trav import Travis
66

7-
from django.http import HttpResponse
87
from django.views.generic.base import TemplateView
98

109
from .git import (
11-
get_deploy_url,
1210
get_org_name,
13-
get_owner,
14-
get_upstream_deploy_url,
1511
get_remote_url
1612
)
1713
from data.models import Team
@@ -114,20 +110,3 @@ def get_context_data(self, **kwargs):
114110
context['top_gamification_users'] = self.get_top_gamification_users(
115111
count=5)
116112
return context
117-
118-
119-
def info(request):
120-
data = {
121-
'Org name': get_org_name(),
122-
'Owner': get_owner(),
123-
'Deploy URL': get_deploy_url(),
124-
}
125-
try:
126-
upstream_deploy_url = get_upstream_deploy_url()
127-
data['Upstream deploy URL'] = upstream_deploy_url
128-
except RuntimeError:
129-
data['Upstream deploy URL'] = 'Not found'
130-
131-
s = '\n'.join(name + ': ' + value
132-
for name, value in data.items())
133-
return HttpResponse(s)

log/view_log.py

-12
This file was deleted.

setup.cfg

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ testpaths =
1414
gci
1515
gsoc
1616
gamification
17-
log
17+
ci_build
1818
meta_review
1919
model
2020
unassigned_issues
@@ -69,7 +69,7 @@ source =
6969
gci
7070
gsoc
7171
gamification
72-
log
72+
ci_build
7373
meta_review
7474
model
7575
unassigned_issues
@@ -80,7 +80,7 @@ omit =
8080
community/git.py
8181
gci/*.py
8282
gsoc/*.py
83-
log/*.py
83+
ci_build/*.py
8484
meta_review/handler.py
8585
model/*.py
8686
openhub/*.py

static/css/build_logs.css

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
.build-info-section section,
2+
.build-logs-section section {
3+
min-width: 300px;
4+
width: 80%;
5+
}
6+
7+
.build-information,
8+
.build-logs {
9+
background-color: black;
10+
padding-left: 10px;
11+
font-weight: bold;
12+
color: white;
13+
}
14+
15+
.build-information p {
16+
font-size: 1.5em;
17+
margin: 0;
18+
}
19+
20+
.build-logs {
21+
max-height: 900px;
22+
overflow: scroll;
23+
overflow-x: hidden;
24+
overflow-y: auto;
25+
}
26+
27+
.build-logs p {
28+
margin: 0;
29+
}
30+
31+
.build-logs-section .log-chooser {
32+
width: 25%;
33+
min-width: 150px;
34+
border-radius: 100px;
35+
box-shadow: 0 0 25px 2px black;
36+
color: #454343;
37+
background-color: #c7da99;
38+
padding-left: 10px;
39+
margin: auto 0 auto auto;
40+
}
41+
42+
.build-logs-section .log-chooser input,
43+
.build-logs-section .log-chooser input:focus:not(.browser-default) {
44+
border-bottom: none;
45+
margin-bottom: 0;
46+
}
47+
48+
.build-logs-section .small-screen,
49+
.build-logs-section .fa-close {
50+
display: none;
51+
}
52+
53+
.form-fields {
54+
margin: auto 0 auto auto;
55+
width: 60%;
56+
padding-top: 10px;
57+
}
58+
59+
.search-field {
60+
width: 60%;
61+
min-width: 180px;
62+
}
63+
64+
.section-header {
65+
display: flex;
66+
align-items: center;
67+
}
68+
69+
.section-header form {
70+
display: flex;
71+
}
72+
73+
@media only screen and (max-width: 660px) {
74+
.build-logs-section .search-field {
75+
display: none;
76+
}
77+
.build-logs-section .small-screen {
78+
display: flex;
79+
align-items: center;
80+
margin: auto;
81+
margin-right: 3px;
82+
font-size: 2em;
83+
}
84+
}

0 commit comments

Comments
 (0)