Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
S
scrape-lhci-server
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Samuel Åkesson
scrape-lhci-server
Commits
d2d1d99b
Commit
d2d1d99b
authored
2 months ago
by
Samuel Åkesson
Browse files
Options
Downloads
Patches
Plain Diff
PISSS ugly
parent
eea0e2a6
No related branches found
No related tags found
No related merge requests found
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
main.py
+159
-4
159 additions, 4 deletions
main.py
pyproject.toml
+1
-0
1 addition, 0 deletions
pyproject.toml
uv.lock
+48
-0
48 additions, 0 deletions
uv.lock
with
208 additions
and
4 deletions
main.py
+
159
−
4
View file @
d2d1d99b
...
@@ -2,6 +2,9 @@ import aiohttp
...
@@ -2,6 +2,9 @@ import aiohttp
import
asyncio
import
asyncio
import
json
import
json
from
urllib.parse
import
urlparse
from
urllib.parse
import
urlparse
from
flask
import
Flask
,
jsonify
,
render_template_string
app
=
Flask
(
__name__
)
base_url
=
(
base_url
=
(
"
http://milou.lysator.liu.se:49001/v1/projects/e511835c-a77f-414c-8817-26fb5d7b99b8
"
"
http://milou.lysator.liu.se:49001/v1/projects/e511835c-a77f-414c-8817-26fb5d7b99b8
"
...
@@ -35,7 +38,7 @@ async def scrape_lcp_values():
...
@@ -35,7 +38,7 @@ async def scrape_lcp_values():
statistics_list
=
await
asyncio
.
gather
(
*
tasks
)
statistics_list
=
await
asyncio
.
gather
(
*
tasks
)
for
build
,
statistics
in
zip
(
builds
,
statistics_list
):
for
build
,
statistics
in
zip
(
builds
,
statistics_list
):
branch
=
build
[
"
branch
"
]
# Directly access branch
branch
=
build
[
"
branch
"
]
for
stat
in
statistics
:
for
stat
in
statistics
:
lcp_stat
=
next
(
lcp_stat
=
next
(
...
@@ -50,7 +53,10 @@ async def scrape_lcp_values():
...
@@ -50,7 +53,10 @@ async def scrape_lcp_values():
if
not
lcp_stat
:
if
not
lcp_stat
:
continue
continue
path
=
urlparse
(
stat
[
"
url
"
]).
path
path
=
urlparse
(
stat
[
"
url
"
]).
path
[
1
:]
if
path
==
""
:
path
=
"
index
"
lcp_entry
=
{
lcp_entry
=
{
"
lcpMillis
"
:
lcp_stat
[
"
value
"
],
"
lcpMillis
"
:
lcp_stat
[
"
value
"
],
...
@@ -69,7 +75,156 @@ async def scrape_lcp_values():
...
@@ -69,7 +75,156 @@ async def scrape_lcp_values():
return
lcp_values_by_branch
return
lcp_values_by_branch
@app.route
(
"
/<branch>/<path>
"
,
methods
=
[
"
GET
"
])
def
serve_plot
(
branch
,
path
):
with
open
(
"
lcp_values_by_branch_and_url.json
"
,
"
r
"
)
as
f
:
lcp_values_by_branch
=
json
.
load
(
f
)
if
branch
not
in
lcp_values_by_branch
or
path
not
in
lcp_values_by_branch
[
branch
]:
return
jsonify
({
"
error
"
:
"
Branch or path not found
"
}),
404
data
=
lcp_values_by_branch
[
branch
][
path
]
timestamps
=
[
entry
[
"
createdAt
"
]
for
entry
in
data
]
lcp_values
=
[
entry
[
"
lcpMillis
"
]
for
entry
in
data
]
plot_html
=
render_template_string
(
"""
<!DOCTYPE html>
<html>
<head>
<script src=
"
https://cdn.plot.ly/plotly-3.0.1.min.js
"
charset=
"
utf-8
"
></script>
</head>
<title>
LCP {{ path }} on branch {{ branch }}
</title>
<style>
html, body {
margin: 0;
padding: 0;
}
body {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
> #plot {
flex: 1;
}
}
</style>
<body>
<a href=
"
/{{branch}}
"
><- Tillbaka</a>
<div id=
"
plot
"
></div>
<script>
var data = [{
x: {{ timestamps|tojson }},
y: {{ lcp_values|tojson }},
type:
'
scatter
'
,
mode:
'
lines+markers
'
,
marker: {
size: 10,
color:
'
blue
'
}
}];
var layout = {
yaxis: {
title:
'
LCP [milliseconds]
'
},
xaxis: {
title:
'
Timestamp
'
// optional
},
};
var config = {
toImageButtonOptions: {
filename:
'
lcp_{{ branch }}_{{ path }}
'
,
format:
'
svg
'
, // one of png, svg, jpeg, webp
},
responsive: true,
};
Plotly.newPlot(
'
plot
'
, data, layout, config);
</script>
</body>
</html>
"""
,
timestamps
=
timestamps
,
lcp_values
=
lcp_values
,
branch
=
branch
,
path
=
path
,
)
return
plot_html
@app.route
(
"
/
"
,
methods
=
[
"
GET
"
])
def
serve_index
():
with
open
(
"
lcp_values_by_branch_and_url.json
"
,
"
r
"
)
as
f
:
lcp_values_by_branch
=
json
.
load
(
f
)
branches
=
list
(
lcp_values_by_branch
.
keys
())
return
render_template_string
(
"""
<!DOCTYPE html>
<html>
<head>
<title>Branches</title>
</head>
<body>
<h1>Branches</h1>
<ul>
{% for branch in branches %}
<li><a href=
"
/{{ branch }}
"
>{{ branch }}</a></li>
{% endfor %}
</ul>
</body>
</html>
"""
,
branches
=
branches
,
)
@app.route
(
"
/<branch>
"
,
methods
=
[
"
GET
"
])
def
serve_branch
(
branch
):
with
open
(
"
lcp_values_by_branch_and_url.json
"
,
"
r
"
)
as
f
:
lcp_values_by_branch
=
json
.
load
(
f
)
if
branch
not
in
lcp_values_by_branch
:
return
jsonify
({
"
error
"
:
"
Branch not found
"
}),
404
paths
=
list
(
lcp_values_by_branch
[
branch
].
keys
())
return
render_template_string
(
"""
<!DOCTYPE html>
<html>
<head>
<title>Paths for {{ branch }}</title>
<style>
code {
color: green;
}
</style>
</head>
<body>
<a href=
"
/
"
><- Tillbaka</a>
<h1>Paths for branch <code>{{ branch }}</code></h1>
<ul>
{% for path in paths %}
<li><a href=
"
/{{ branch }}/{{ path }}
"
>{{ path }}</a></li>
{% endfor %}
</ul>
</body>
</html>
"""
,
branch
=
branch
,
paths
=
paths
,
)
if
__name__
==
"
__main__
"
:
if
__name__
==
"
__main__
"
:
lcp_values
=
asyncio
.
run
(
scrape_lcp_values
())
lcp_values
_by_branch
=
asyncio
.
run
(
scrape_lcp_values
())
with
open
(
"
lcp_values_by_branch_and_url.json
"
,
"
w
"
)
as
f
:
with
open
(
"
lcp_values_by_branch_and_url.json
"
,
"
w
"
)
as
f
:
json
.
dump
(
lcp_values
,
f
,
indent
=
4
)
json
.
dump
(
lcp_values_by_branch
,
f
,
indent
=
4
)
app
.
run
(
debug
=
True
,
host
=
"
127.0.0.1
"
,
port
=
8000
)
This diff is collapsed.
Click to expand it.
pyproject.toml
+
1
−
0
View file @
d2d1d99b
...
@@ -7,6 +7,7 @@ requires-python = ">=3.13"
...
@@ -7,6 +7,7 @@ requires-python = ">=3.13"
dependencies
=
[
dependencies
=
[
"aiohttp>
=
3.11
.
18
",
"aiohttp>
=
3.11
.
18
",
"fastapi>
=
0.115
.
12
",
"fastapi>
=
0.115
.
12
",
"flask>
=
3.1
.
0
",
"jinja2>
=
3.1
.
6
",
"jinja2>
=
3.1
.
6
",
"plotly>
=
6.0
.
1
",
"plotly>
=
6.0
.
1
",
"uvicorn>
=
0.34
.
2
",
"uvicorn>
=
0.34
.
2
",
...
...
This diff is collapsed.
Click to expand it.
uv.lock
+
48
−
0
View file @
d2d1d99b
...
@@ -87,6 +87,15 @@ wheels = [
...
@@ -87,6 +87,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload_time = "2025-03-13T11:10:21.14Z" },
{ url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload_time = "2025-03-13T11:10:21.14Z" },
]
]
[[package]]
name = "blinker"
version = "1.9.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload_time = "2024-11-08T17:25:47.436Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload_time = "2024-11-08T17:25:46.184Z" },
]
[[package]]
[[package]]
name = "click"
name = "click"
version = "8.1.8"
version = "8.1.8"
...
@@ -122,6 +131,22 @@ wheels = [
...
@@ -122,6 +131,22 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/50/b3/b51f09c2ba432a576fe63758bddc81f78f0c6309d9e5c10d194313bf021e/fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d", size = 95164, upload_time = "2025-03-23T22:55:42.101Z" },
{ url = "https://files.pythonhosted.org/packages/50/b3/b51f09c2ba432a576fe63758bddc81f78f0c6309d9e5c10d194313bf021e/fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d", size = 95164, upload_time = "2025-03-23T22:55:42.101Z" },
]
]
[[package]]
name = "flask"
version = "3.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "blinker" },
{ name = "click" },
{ name = "itsdangerous" },
{ name = "jinja2" },
{ name = "werkzeug" },
]
sdist = { url = "https://files.pythonhosted.org/packages/89/50/dff6380f1c7f84135484e176e0cac8690af72fa90e932ad2a0a60e28c69b/flask-3.1.0.tar.gz", hash = "sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac", size = 680824, upload_time = "2024-11-13T18:24:38.127Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/af/47/93213ee66ef8fae3b93b3e29206f6b251e65c97bd91d8e1c5596ef15af0a/flask-3.1.0-py3-none-any.whl", hash = "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136", size = 102979, upload_time = "2024-11-13T18:24:36.135Z" },
]
[[package]]
[[package]]
name = "frozenlist"
name = "frozenlist"
version = "1.6.0"
version = "1.6.0"
...
@@ -183,6 +208,15 @@ wheels = [
...
@@ -183,6 +208,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload_time = "2024-09-15T18:07:37.964Z" },
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload_time = "2024-09-15T18:07:37.964Z" },
]
]
[[package]]
name = "itsdangerous"
version = "2.2.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload_time = "2024-04-16T21:28:15.614Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload_time = "2024-04-16T21:28:14.499Z" },
]
[[package]]
[[package]]
name = "jinja2"
name = "jinja2"
version = "3.1.6"
version = "3.1.6"
...
@@ -388,6 +422,7 @@ source = { virtual = "." }
...
@@ -388,6 +422,7 @@ source = { virtual = "." }
dependencies = [
dependencies = [
{ name = "aiohttp" },
{ name = "aiohttp" },
{ name = "fastapi" },
{ name = "fastapi" },
{ name = "flask" },
{ name = "jinja2" },
{ name = "jinja2" },
{ name = "plotly" },
{ name = "plotly" },
{ name = "uvicorn" },
{ name = "uvicorn" },
...
@@ -397,6 +432,7 @@ dependencies = [
...
@@ -397,6 +432,7 @@ dependencies = [
requires-dist = [
requires-dist = [
{ name = "aiohttp", specifier = ">=3.11.18" },
{ name = "aiohttp", specifier = ">=3.11.18" },
{ name = "fastapi", specifier = ">=0.115.12" },
{ name = "fastapi", specifier = ">=0.115.12" },
{ name = "flask", specifier = ">=3.1.0" },
{ name = "jinja2", specifier = ">=3.1.6" },
{ name = "jinja2", specifier = ">=3.1.6" },
{ name = "plotly", specifier = ">=6.0.1" },
{ name = "plotly", specifier = ">=6.0.1" },
{ name = "uvicorn", specifier = ">=0.34.2" },
{ name = "uvicorn", specifier = ">=0.34.2" },
...
@@ -457,6 +493,18 @@ wheels = [
...
@@ -457,6 +493,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b1/4b/4cef6ce21a2aaca9d852a6e84ef4f135d99fcd74fa75105e2fc0c8308acd/uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403", size = 62483, upload_time = "2025-04-19T06:02:48.42Z" },
{ url = "https://files.pythonhosted.org/packages/b1/4b/4cef6ce21a2aaca9d852a6e84ef4f135d99fcd74fa75105e2fc0c8308acd/uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403", size = 62483, upload_time = "2025-04-19T06:02:48.42Z" },
]
]
[[package]]
name = "werkzeug"
version = "3.1.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markupsafe" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9f/69/83029f1f6300c5fb2471d621ab06f6ec6b3324685a2ce0f9777fd4a8b71e/werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746", size = 806925, upload_time = "2024-11-08T15:52:18.093Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/52/24/ab44c871b0f07f491e5d2ad12c9bd7358e527510618cb1b803a88e986db1/werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", size = 224498, upload_time = "2024-11-08T15:52:16.132Z" },
]
[[package]]
[[package]]
name = "yarl"
name = "yarl"
version = "1.20.0"
version = "1.20.0"
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment