This commit is contained in:
Бахирев 2024-05-07 09:37:18 +03:00
parent fefc1e65b5
commit 0c795fe9d0
47 changed files with 434 additions and 170 deletions

View file

@ -223,7 +223,7 @@ By default, the image will run at ```http://127.0.0.1:80/```. If it doesn't work
#### 📐 Architecture
<img src="https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg" width="70%" />
1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report.
3. [Log visualization UI](https://github.com/bakhirev/assayo) **(you here)** displays report. Needs a log file for work.

View file

@ -1 +1 @@
<!doctype html><html><head><meta name="viewport" content="width=device-width,height=device-height,initial-scale=1,user-scalable=no,maximum-scale=1"><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta http-equiv="Cache-Control" content="no-cache"><meta http-equiv="cleartype" content="on"><meta name="HandheldFriendly" content="True"><meta name="format-detection" content="telephone=no"><meta name="format-detection" content="address=no"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="theme-color" content="white"/><script type="text/javascript">var report=[]</script><script src='./log.txt'></script><script src='./log-1.txt'></script><script src='./log-2.txt'></script><script src='./log-3.txt'></script><script src='./log-4.txt'></script><script src='./log-5.txt'></script><script src='./log-6.txt'></script><script src='../log.txt'></script><script src='../log-1.txt'></script><script src='../log-2.txt'></script><script src='../log-3.txt'></script><script src='../log-4.txt'></script><script src='../log-5.txt'></script><script src='../log-6.txt'></script><script src='../../log.txt'></script><script src='../../log-1.txt'></script><script src='../../log-2.txt'></script><script src='../../log-3.txt'></script><script src='../../log-4.txt'></script><script src='../../log-5.txt'></script><script src='../../log-6.txt'></script><script src='/log.txt'></script><script src='/log-1.txt'></script><script src='/log-2.txt'></script><script src='/log-3.txt'></script><script src='/log-4.txt'></script><script src='/log-5.txt'></script><script src='/log-6.txt'></script><link rel="icon" href="./favicon.svg"/><link rel="apple-touch-icon" href="./logo192.png"/><link rel="manifest" href="./manifest.json"/><title>Git Statistics</title><meta name="description" content="Simple and fast report on git commit history."><meta name="keywords" content="git, statistics, audit, history, log, monitoring, employee control"><meta name="author" content="Bakhirev Aleksei"><meta name="copyright" content="(c) Bakhirev Aleksei"><meta http-equiv="Reply-to" content="alexey-bakhirev@yandex.ru"><meta name="application-name" content="Git statistics"><meta name="msapplication-tooltip" content="Simple and fast report on Git commit history."><meta property="og:title" content="Git Statistics"><meta property="og:description" content="Simple and fast report on Git commit history."><meta property="og:image" content="https://assayo.online/assets/seo/custom_icon_256.png"><meta property="og:site_name" content="Assayo"><meta property="og:url" content="https://assayo.online/"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="Git Statistics"><meta name="twitter:description" content="Simple and fast report on Git commit history."><meta name="twitter:creator" content="Bakhirev Aleksei"><meta name="twitter:image:src" content="https://assayo.online/assets/seo/custom_icon_256.png"><meta name="twitter:domain" content="assayo.online"><meta name="twitter:site" content="assayo.online"><meta itemprop="name" content="Git Statistics"><meta itemprop="description" content="Simple and fast report on Git commit history."><meta itemprop="image" content="https://assayo.online/assets/seo/custom_icon_256.png"><script defer="defer" src="./static/index.js"></script><link href="./static/index.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html><head><meta name="viewport" content="width=device-width,height=device-height,initial-scale=1,user-scalable=no,maximum-scale=1"><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta http-equiv="Cache-Control" content="no-cache"><meta http-equiv="cleartype" content="on"><meta name="HandheldFriendly" content="True"><meta name="format-detection" content="telephone=no"><meta name="format-detection" content="address=no"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="theme-color" content="white"/><meta name="defaultLanguage" content="ru"><meta name="availableLanguages" content="en, es, fr, ja, pt, de, zh, ru"><link rel="canonical" href="https://assayo.online/demo/"><script type="text/javascript">var report=[]</script><script src='./log.txt'></script><script src='./log-1.txt'></script><script src='./log-2.txt'></script><script src='./log-3.txt'></script><script src='./log-4.txt'></script><script src='./log-5.txt'></script><script src='./log-6.txt'></script><script src='../log.txt'></script><script src='../log-1.txt'></script><script src='../log-2.txt'></script><script src='../log-3.txt'></script><script src='../log-4.txt'></script><script src='../log-5.txt'></script><script src='../log-6.txt'></script><script src='../../log.txt'></script><script src='../../log-1.txt'></script><script src='../../log-2.txt'></script><script src='../../log-3.txt'></script><script src='../../log-4.txt'></script><script src='../../log-5.txt'></script><script src='../../log-6.txt'></script><script src='/log.txt'></script><script src='/log-1.txt'></script><script src='/log-2.txt'></script><script src='/log-3.txt'></script><script src='/log-4.txt'></script><script src='/log-5.txt'></script><script src='/log-6.txt'></script><link rel="icon" href="./favicon.svg"/><link rel="apple-touch-icon" href="./logo192.png"/><link rel="manifest" href="./manifest.json"/><title>Git Statistics</title><meta name="description" content="Simple and fast report on git commit history."><meta name="keywords" content="git, statistics, audit, history, log, monitoring, employee control"><meta name="author" content="Bakhirev Aleksei"><meta name="copyright" content="(c) Bakhirev Aleksei"><meta http-equiv="Reply-to" content="alexey-bakhirev@yandex.ru"><meta name="application-name" content="Git statistics"><meta name="msapplication-tooltip" content="Simple and fast report on Git commit history."><meta property="og:title" content="Git Statistics"><meta property="og:description" content="Simple and fast report on Git commit history."><meta property="og:image" content="https://assayo.online/assets/seo/custom_icon_256.png"><meta property="og:site_name" content="Assayo"><meta property="og:url" content="https://assayo.online/"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="Git Statistics"><meta name="twitter:description" content="Simple and fast report on Git commit history."><meta name="twitter:creator" content="Bakhirev Aleksei"><meta name="twitter:image:src" content="https://assayo.online/assets/seo/custom_icon_256.png"><meta name="twitter:domain" content="assayo.online"><meta name="twitter:site" content="assayo.online"><meta itemprop="name" content="Git Statistics"><meta itemprop="description" content="Simple and fast report on Git commit history."><meta itemprop="image" content="https://assayo.online/assets/seo/custom_icon_256.png"><script defer="defer" src="./static/index.js"></script><link href="./static/index.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -200,7 +200,7 @@ Standardmäßig wird das abbild an der folgenden adresse ausgeführt ```http://1
#### 📐 Architecture
<img src="https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg" width="70%" />
1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report.
3. [Log visualization UI](https://github.com/bakhirev/assayo) **(you here)** displays report. Needs a log file for work.
<a name="link-28"></a>

View file

@ -202,7 +202,7 @@ By default, the image will run at ```http://127.0.0.1:80/```. If it doesn't work
#### 📐 Architecture
<img src="https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg" width="70%" />
1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report.
3. [Log visualization UI](https://github.com/bakhirev/assayo) **(you here)** displays report. Needs a log file for work.
<a name="link-28"></a>

View file

@ -203,7 +203,7 @@ Por defecto, la imagen se ejecutará en la siguiente dirección ```http://127.0.
#### 📐 Architecture
<img src="https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg" width="70%" />
1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report.
3. [Log visualization UI](https://github.com/bakhirev/assayo) **(you here)** displays report. Needs a log file for work.
<a name="link-28"></a>

View file

@ -200,7 +200,7 @@ Par défaut, l'image s'exécute à ```http://127.0.0.1:80/```. Si cela ne foncti
#### 📐 Architecture
<img src="https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg" width="70%" />
1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report.
3. [Log visualization UI](https://github.com/bakhirev/assayo) **(you here)** displays report. Needs a log file for work.
<a name="link-28"></a>

View file

@ -201,7 +201,7 @@ you_url - gitのログのコンテナーのURLアドレス;
#### 📐 Architecture
<img src="https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg" width="70%" />
1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report.
3. [Log visualization UI](https://github.com/bakhirev/assayo) **(you here)** displays report. Needs a log file for work.
<a name="link-28"></a>

View file

@ -201,7 +201,7 @@ Por padrão, a imagem será iniciada no endereço ```http://127.0.0.1:80/```. Se
#### 📐 Architecture
<img src="https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg" width="70%" />
1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report.
3. [Log visualization UI](https://github.com/bakhirev/assayo) **(you here)** displays report. Needs a log file for work.
<a name="link-28"></a>

View file

@ -201,7 +201,7 @@ you_url - URL адресс вашего контейнера с логами
#### 📐 Архитектура
<img src="https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg" width="70%" />
1. [Reports showcase UI](###) отображение списка отчётов. Каждый отчёт имеет название, описание и список репозиториев.
1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) отображение списка отчётов. Каждый отчёт имеет название, описание и список репозиториев.
2. [Crawler service](https://github.com/bakhirev/assayo-crawler) сборка, склейка, обработка логов из репозиториев для отчётов.
3. [Log visualization UI](https://github.com/bakhirev/assayo) **(вы тут)** отображение отчётов. Для работы ему нужен log файл с данными.
<a name="link-28"></a>

View file

@ -198,7 +198,7 @@ you_url - git日志的容器的URL地址;
#### 🛠️ Architecture
<img src="https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg" width="70%" />
1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report.
3. [Log visualization UI](https://github.com/bakhirev/assayo) **(you here)** displays report. Needs a log file for work.
<a name="link-27"></a>

View file

@ -304,7 +304,7 @@
"img": "<img src=\"https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg\" width=\"70%\" />"
},
{
"p": "1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories."
"p": "1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories."
},
{
"p": "2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report."

View file

@ -308,7 +308,7 @@
"img": "<img src=\"https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg\" width=\"70%\" />"
},
{
"p": "1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories."
"p": "1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories."
},
{
"p": "2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report."

View file

@ -311,7 +311,7 @@
"img": "<img src=\"https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg\" width=\"70%\" />"
},
{
"p": "1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories."
"p": "1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories."
},
{
"p": "2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report."

View file

@ -302,7 +302,7 @@
"img": "<img src=\"https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg\" width=\"70%\" />"
},
{
"p": "1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories."
"p": "1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories."
},
{
"p": "2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report."

View file

@ -307,7 +307,7 @@
"img": "<img src=\"https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg\" width=\"70%\" />"
},
{
"p": "1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories."
"p": "1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories."
},
{
"p": "2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report."

View file

@ -307,7 +307,7 @@
"img": "<img src=\"https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg\" width=\"70%\" />"
},
{
"p": "1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories."
"p": "1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories."
},
{
"p": "2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report."

View file

@ -305,7 +305,7 @@
"img": "<img src=\"https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg\" width=\"70%\" />"
},
{
"p": "1. [Reports showcase UI](###) отображение списка отчётов. Каждый отчёт имеет название, описание и список репозиториев."
"p": "1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) отображение списка отчётов. Каждый отчёт имеет название, описание и список репозиториев."
},
{
"p": "2. [Crawler service](https://github.com/bakhirev/assayo-crawler) сборка, склейка, обработка логов из репозиториев для отчётов."

View file

@ -304,7 +304,7 @@
"img": "<img src=\"https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg\" width=\"70%\" />"
},
{
"p": "1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories."
"p": "1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories."
},
{
"p": "2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report."

View file

@ -166,7 +166,7 @@ Standardmäßig wird das abbild an der folgenden adresse ausgeführt ```http://1
#### Architecture
<img src="https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg" width="70%" />
1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report.
3. [Log visualization UI](https://github.com/bakhirev/assayo) **(you here)** displays report. Needs a log file for work.

View file

@ -181,7 +181,7 @@ By default, the image will run at ```http://127.0.0.1:80/```. If it doesn't work
#### Architecture
<img src="https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg" width="70%" />
1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report.
3. [Log visualization UI](https://github.com/bakhirev/assayo) **(you here)** displays report. Needs a log file for work.

View file

@ -170,7 +170,7 @@ Por defecto, la imagen se ejecutará en la siguiente dirección ```http://127.0.
#### Architecture
<img src="https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg" width="70%" />
1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report.
3. [Log visualization UI](https://github.com/bakhirev/assayo) **(you here)** displays report. Needs a log file for work.

View file

@ -165,7 +165,7 @@ Par défaut, l'image s'exécute à ```http://127.0.0.1:80/```. Si cela ne foncti
#### Architecture
<img src="https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg" width="70%" />
1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report.
3. [Log visualization UI](https://github.com/bakhirev/assayo) **(you here)** displays report. Needs a log file for work.

View file

@ -168,7 +168,7 @@ you_url - gitのログのコンテナーのURLアドレス;
#### Architecture
<img src="https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg" width="70%" />
1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report.
3. [Log visualization UI](https://github.com/bakhirev/assayo) **(you here)** displays report. Needs a log file for work.

View file

@ -168,7 +168,7 @@ Por padrão, a imagem será iniciada no endereço ```http://127.0.0.1:80/```. Se
#### Architecture
<img src="https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg" width="70%" />
1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report.
3. [Log visualization UI](https://github.com/bakhirev/assayo) **(you here)** displays report. Needs a log file for work.

View file

@ -180,7 +180,7 @@ you_url - URL адресс вашего контейнера с логами
#### Архитектура
<img src="https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg" width="70%" />
1. [Reports showcase UI](###) отображение списка отчётов. Каждый отчёт имеет название, описание и список репозиториев.
1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) отображение списка отчётов. Каждый отчёт имеет название, описание и список репозиториев.
2. [Crawler service](https://github.com/bakhirev/assayo-crawler) сборка, склейка, обработка логов из репозиториев для отчётов.
3. [Log visualization UI](https://github.com/bakhirev/assayo) **(вы тут)** отображение отчётов. Для работы ему нужен log файл с данными.

View file

@ -169,7 +169,7 @@ you_url - git日志的容器的URL地址;
#### Architecture
<img src="https://raw.githubusercontent.com/bakhirev/assayo-crawler/12af4410fc93384cafb108a4429e43f9a874dbaa/schema.svg" width="70%" />
1. [Reports showcase UI](###) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
1. [Reports showcase UI](https://github.com/bakhirev/assayo-showcase) displays a list of available reports. Each report consists of a title, description, and a list of repositories.
2. [Crawler service](https://github.com/bakhirev/assayo-crawler) collects repository logs for the report.
3. [Log visualization UI](https://github.com/bakhirev/assayo) **(you here)** displays report. Needs a log file for work.

View file

@ -12,6 +12,10 @@
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="theme-color" content="white"/>
<meta name="defaultLanguage" content="ru">
<meta name="availableLanguages" content="en, es, fr, ja, pt, de, zh, ru">
<link rel="canonical" href="https://assayo.online/demo/">
<script type="text/javascript">
var report = [];
</script>
@ -28,11 +32,13 @@
<meta name="application-name" content="Git statistics">
<meta name="msapplication-tooltip" content="Simple and fast report on Git commit history.">
<meta property="og:title" content="Git Statistics">
<meta property="og:description" content="Simple and fast report on Git commit history.">
<meta property="og:image" content="https://assayo.online/assets/seo/custom_icon_256.png">
<meta property="og:site_name" content="Assayo">
<meta property="og:url" content="https://assayo.online/">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Git Statistics">
<meta name="twitter:description" content="Simple and fast report on Git commit history.">
@ -40,6 +46,7 @@
<meta name="twitter:image:src" content="https://assayo.online/assets/seo/custom_icon_256.png">
<meta name="twitter:domain" content="assayo.online">
<meta name="twitter:site" content="assayo.online">
<meta itemprop="name" content="Git Statistics">
<meta itemprop="description" content="Simple and fast report on Git commit history.">
<meta itemprop="image" content="https://assayo.online/assets/seo/custom_icon_256.png">

View file

@ -21,7 +21,7 @@ function LineChart({
details,
className,
}: ILineChartProps): React.ReactElement | null {
if (value === 0) return null;
if (!value || value === 0) return null;
const width = Math.round((value ?? 100) * (100 / options.max));
@ -67,7 +67,7 @@ function LineChart({
}
LineChart.defaultProps = {
value: 100,
value: 0,
details: undefined,
className: '',
};

View file

@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect } from 'react';
import Logo from 'ts/pages/PageWrapper/components/sidebar/Logo';
@ -7,6 +7,14 @@ import progress from './progress.module.scss';
function SplashScreen(): React.ReactElement | null {
useEffect(() => {
const overflow = document.body.style.overflow;
document.body.style.overflow = 'hidden';
setTimeout(() => {
document.body.style.overflow = overflow;
}, 5400);
}, []);
return (
<div className={style.splash_screen}>
<div className={style.splash_screen_container}>

View file

@ -1,15 +1,21 @@
import React from 'react';
import { IColumn } from '../interfaces/Column';
import IHashMap from 'ts/interfaces/HashMap';
import { ColumnTypesEnum, IColumn, IRowsConfig } from '../interfaces/Column';
import DefaultCell from './cells/CellDefault';
import DetailsCell from './cells/CellDetails';
import style from '../styles/index.module.scss';
import { getRowId } from '../helpers/getRowsConfig';
interface IBodyProps {
rows: any[];
columns: IColumn[];
disabledRow?: (row: any) => boolean;
className?: string;
rowsConfig?: IHashMap<IRowsConfig>;
updateRowsConfig?: (config: IRowsConfig) => void;
}
function Body({
@ -17,44 +23,70 @@ function Body({
disabledRow,
columns,
className,
rowsConfig,
updateRowsConfig,
}: IBodyProps) {
const formattedRows = rows?.map((row: any, index: number) => {
const formattedRows: any = [];
const getSubRow = columns
.find((column: IColumn) => column.template === ColumnTypesEnum.DETAILS)
?.formatter;
rows?.forEach((row: any, rowIndex: number) => {
const rowConfig = (rowsConfig || {})[getRowId(row, rowIndex)];
const cells = columns.map((column: IColumn, columnIndex: number) => {
const value = column.properties
? row[column.properties]
: row;
const key = `${column.title}_${columnIndex}`;
const formattedValue = column.formatter
? column.formatter(value)
: value;
const content: any = typeof column.template === 'function'
? column.template(formattedValue, row)
: `${column.prefixes ?? ''}${formattedValue ?? ''}${column.suffixes ?? ''}`;
if (column.template === ColumnTypesEnum.DETAILS) {
return (
<DetailsCell
key={key}
column={column}
row={row}
rowConfig={rowConfig}
updateRowsConfig={updateRowsConfig}
/>
);
}
return (
<DefaultCell
key={`${column.title}_${columnIndex}`}
key={key}
column={column}
row={row}
>
{content}
</DefaultCell>
/>
);
});
const rowClassName = disabledRow && disabledRow(row)
? style.disabled
: '';
const rowClassName = [
style.table_row,
className,
];
return (
if (disabledRow && disabledRow(row)) {
rowClassName.push(style.table_row_hide);
}
if (rowConfig?.details) {
rowClassName.push(style.table_row_selected);
}
formattedRows.push(
<div
key={index}
className={`${style.table_row} ${rowClassName} ${className}`}
key={rowIndex}
className={rowClassName.join('')}
>
{cells}
</div>
);
</div>);
if (!rowConfig?.details || !getSubRow) return;
formattedRows.push(
<div
key={`${rowIndex}-detail`}
className={`${style.table_sub_row} ${className}`}
>
{getSubRow(row) || null}
</div>);
});
return (

View file

@ -48,7 +48,7 @@ Column.defaultProps = {
title: '',
prefixes: [''],
suffixes: [''],
formatter: (value: any) => value,
formatter: undefined,
className: '',
isDisabled: false,
isFixed: false,

View file

@ -1,4 +1,4 @@
import React, { ReactNode } from 'react';
import React from 'react';
import { IColumn } from '../../interfaces/Column';
import style from '../../styles/index.module.scss';
@ -7,14 +7,12 @@ interface IDefaultCellProps {
column: IColumn,
row: any,
className?: string,
children?: ReactNode | string | number | boolean | null;
}
function DefaultCell({
column,
row,
className,
children,
}: IDefaultCellProps): JSX.Element {
const columnClassName = typeof column.className === 'function'
? column.className('body', row)
@ -24,8 +22,20 @@ function DefaultCell({
? (() => { if (column.onClick) column.onClick(row); })
: undefined;
const cellTitle = typeof children === 'string' && children.length > 20
? children
const value = column.properties
? row[column.properties]
: row;
const formattedValue = column.formatter
? column.formatter(value)
: value;
const content: any = typeof column.template === 'function'
? column.template(formattedValue, row)
: `${column.prefixes ?? ''}${formattedValue ?? ''}${column.suffixes ?? ''}`;
const cellTitle = typeof content === 'string' && content.length > 20
? content
: null;
return (
@ -39,7 +49,7 @@ function DefaultCell({
}} // @ts-ignore
onClick={onClick}
>
{children}
{content}
</div>
);
}

View file

@ -0,0 +1,68 @@
import React from 'react';
import { IColumn, IRowsConfig } from '../../interfaces/Column';
import style from '../../styles/index.module.scss';
interface IDefaultCellProps {
column: IColumn,
row: any,
className?: string,
rowConfig?: IRowsConfig;
updateRowsConfig?: (config: IRowsConfig) => void;
}
function DetailsCell({
column,
row,
className,
rowConfig,
updateRowsConfig,
}: IDefaultCellProps): JSX.Element {
const config = rowConfig || { id: 1 };
const columnClassName = typeof column.className === 'function'
? column.className('body', row)
: column.className;
const iconClassName = config?.details
? style.table_cell_icon_open
: style.table_cell_icon_close;
const hasIcon = ((column.properties && row[column.properties])
|| !column.properties
|| !column.properties?.length)
&& column.formatter;
const onClick = () => {
if (!hasIcon || !updateRowsConfig) return;
updateRowsConfig({
...config,
details: !config?.details,
});
};
return (
<div
key={column.title} // @ts-ignore
className={`${style.table_cell} ${className || ''} ${columnClassName || ''}`}
style={{
width: column.width,
cursor: 'pointer',
}} // @ts-ignore
onClick={onClick}
>
{hasIcon && (
<img
className={iconClassName}
src="./assets/menu/arrow_right.svg"
/>
)}
</div>
);
}
DetailsCell.defaultPeops = {
className: '',
};
export default DetailsCell;

View file

@ -0,0 +1,31 @@
import IHashMap from 'ts/interfaces/HashMap';
import { IRowsConfig } from '../interfaces/Column';
export function getRowId(row: any, index: number) {
return row?.id || row?.uuid || `${row?.title}-${index}`;
}
function getNewConfig(id: any): IRowsConfig {
return {
id: id,
details: false,
disabled: false,
};
}
function getRowsConfig(
rows: any[] = [],
oldConfigs: IHashMap<IRowsConfig> = {},
): IHashMap<IRowsConfig> {
const configs = {};
rows.map((row: any, index: number) => {
const id = getRowId(row, index);
configs[id] = oldConfigs[id] || getNewConfig(id);
});
return configs;
}
export default getRowsConfig;

View file

@ -1,13 +1,15 @@
import React, { useEffect, useState } from 'react';
import ISort from 'ts/interfaces/Sort';
import IHashMap from 'ts/interfaces/HashMap';
import { IColumn } from './interfaces/Column';
import { IColumn, IRowsConfig } from './interfaces/Column';
import Header from './components/Header';
import Body from './components/Body';
import getAdaptiveColumnWidth from './helpers/getAdaptiveColumnWidth';
import getColumnConfigs from './helpers/getColumnConfigs';
import getDefaultProps from './helpers/getDefaultProps';
import getRowsConfig from './helpers/getRowsConfig';
import style from './styles/index.module.scss';
@ -27,6 +29,7 @@ function Table({
children,
}: ITableProps): React.ReactElement | null {
const [offsetWidth, setOffsetWidth] = useState<number>(0);
const [rowsConfig, setRowsConfig] = useState<IHashMap<IRowsConfig>>({});
if (!rows || !rows.length) return null;
@ -37,9 +40,20 @@ function Table({
setOffsetWidth(currentWidth);
}, [currentWidth]);
useEffect(() => {
const newRowsConfig = getRowsConfig(rows, rowsConfig);
setRowsConfig(newRowsConfig);
}, [rows]);
const defaultColumns = getDefaultProps(children) as IColumn[];
const adaptiveWidth = getAdaptiveColumnWidth(defaultColumns, offsetWidth);
const columns = getColumnConfigs(defaultColumns, adaptiveWidth, sort);
const updateRowsConfig = (config: IRowsConfig) => {
setRowsConfig({
...rowsConfig,
[config.id]: config,
});
};
return (
<div
@ -57,6 +71,8 @@ function Table({
rows={rows}
columns={columns}
disabledRow={disabledRow}
rowsConfig={rowsConfig}
updateRowsConfig={updateRowsConfig}
/>
</div>
</div>

View file

@ -1,51 +1,61 @@
export type ColumnType = 'STRING' | 'NUMBER' | 'SHORT_NUMBER';
export type ColumnType = 'STRING' | 'NUMBER' | 'SHORT_NUMBER' | 'DETAILS';
/** Тип столбца определяет тип содержимого всех ячеек столбца */
export enum ColumnTypesEnum {
STRING = 'STRING',
NUMBER = 'NUMBER',
SHORT_NUMBER = 'SHORT_NUMBER',
DETAILS = 'DETAILS',
}
export interface IColumn {
/** Тип столбца */
template?: ColumnTypesEnum | Function,
template?: ColumnTypesEnum | Function;
/** Уникальный ключ столбца */
properties?: string,
properties?: string;
/** Заголовок столбца */
title?: string,
title?: string;
/** Префиксы для заголовка столбца */
prefixes?: string,
/** Суффиксы для заголовка столбца (%, $ и т.д.) */
suffixes?: string,
prefixes?: string;
/** Суффиксы для заголовка столбца (%; $ и т.д.) */
suffixes?: string;
/** Функция для форматирования данных в столбце */
formatter?: Function,
formatter?: Function;
/** Направление сортировки */
sortDirection?: number,
sortDirection?: number;
/** Фиксированный столбец */
isFixed?: boolean,
isFixed?: boolean;
/** Сортировка столбца */
isSortable?: boolean | string,
isSortable?: boolean | string;
/** Изменение ширины столбца */
isResizable?: boolean,
isResizable?: boolean;
/** Drag-and-Drop столбца */
isDraggable?: boolean,
isDraggable?: boolean;
/** Видимость столбца */
isShow?: boolean,
isShow?: boolean;
/** Клас для колонки */
className?: string | Function
/** Стилья для колонки */
style?: Function,
style?: Function;
/** Минимальная ширина столбца если он адаптивен */
minWidth?: number,
minWidth?: number;
/** Ширина столбца заданная в верстке */
defaultWidth?: number,
defaultWidth?: number;
/** Ширина столбца установленная пользователем */
userWidth?: number,
userWidth?: number;
/** Ширина столбца итоговая */
width?: number,
width?: number;
/** Клик на ячейку */
onClick?: Function,
onClick?: Function;
}
export interface IRowsConfig {
/** ID строки */
id: string | number;
/** Строка раскрыта */
details?: boolean;
/** Строка не активна */
disabled?: boolean;
}

View file

@ -10,32 +10,46 @@
break-inside: auto;
--table-cell-height: 48px;
--table-bar-width: 350px;
}
.table_tree {
&_tree {
--table-cell-height: 22px;
--table-bar-width: 200px;
}
.table_row {
&_row {
position: relative;
font-weight: 100;
display: block;
white-space: nowrap;
border-bottom: 1px solid #EEEEEE;
break-inside: auto;
}
.table_row:last-child {
&:last-child {
border-bottom: none;
}
.table_row_hide {
&_hide {
opacity: 0.2;
}
.table_cell,
.table_header_cell {
&_selected {
background-color: #FFFAF0;
}
}
&_sub_row {
position: relative;
font-weight: 100;
display: block;
padding-top: 24px;
padding-left: 12px;
white-space: nowrap;
border-bottom: 1px solid #EEEEEE;
break-inside: auto;
}
&_cell,
&_header_cell {
font-size: var(--font-xs);
z-index: 0;
@ -50,11 +64,11 @@
vertical-align: top;
}
.table_cell {
&_cell {
padding: 0 4px;
}
.table_header_cell {
&_header_cell {
font-weight: bold;
height: var(--table-cell-height);
padding: 0 4px;
@ -62,22 +76,39 @@
background-color: #FFFFFF;
}
.table_cell:first-child,
.table_header_cell:first-child {
&_cell:first-child,
&_header_cell:first-child {
position: sticky;
top: 0;
left: 0;
z-index: 1;
}
.table_cell:first-child {
&_cell:first-child {
background-color: rgba(255, 255, 255, 0.9);
}
.table_cell_number {
&_cell_number {
text-align: right;
}
&_cell_icon {
&_open,
&_close {
display: inline-block;
width: 32px;
height: 32px;
margin-top: 8px;
cursor: pointer;
transition: transform 0.5s;
}
&_open {
transform: rotate(90deg);
}
}
}
.disabled {
opacity: 0.4;
filter: grayscale(0.6);

View file

@ -27,7 +27,8 @@ export default class DataGripByPR {
} else {
this.#updateCommitByTaskNumber(commit);
}
} else if (commit.commitType !== COMMIT_TYPE.AUTO_MERGE && !this.pr[commit.prId]) {
} else if (!this.pr[commit.prId]
&& [COMMIT_TYPE.PR_BITBUCKET, COMMIT_TYPE.PR_GITHUB].includes(commit.commitType)) {
this.#addCommitByPR(commit);
}
}

View file

@ -15,11 +15,14 @@ export default class DataGripByRelease {
statistic: any[] = [];
lastPrList: any[] = [];
statisticByName: IHashMap<any> = [];
clear() {
this.release = {};
this.statistic = [];
this.lastPrList = [];
}
addCommit(commit: ISystemCommit) {
@ -29,6 +32,8 @@ export default class DataGripByRelease {
} else {
this.#addRelease(commit);
}
} else if (commit.commitType === COMMIT_TYPE.PR_GITHUB || commit.commitType === COMMIT_TYPE.PR_BITBUCKET) {
this.lastPrList.push(commit);
}
}
@ -58,12 +63,18 @@ export default class DataGripByRelease {
to: null,
delayInDays: 0,
waitingInDays: 0,
pr: this.lastPrList,
prLength: this.lastPrList.length,
};
this.lastPrList = [];
}
updateTotalInfo() {
let prev: any = null;
this.lastPrList = [];
this.statistic = Object.entries(this.release)
.sort((a: any, b: any) => a[1].firstCommit.milliseconds - b[1].firstCommit.milliseconds)
.map((a: any) => {

View file

@ -39,17 +39,17 @@ export default function getUserInfo(logString: string): ICommit | ISystemCommit
const isSystemPR = message.indexOf('Pull request #') === 0;
const isSystemMerge = message.indexOf('Merge pull request #') === 0;
const isMerge = message.indexOf('Merge branch ') === 0
|| message.indexOf('Merge commit ') === 0;
const isAutoMerge = message.indexOf('Automatic merge from') === 0
const isMerge = message.indexOf('Merge commit ') === 0
|| message.indexOf('Merge branch ') === 0
|| message.indexOf('Merge remote-tracking branch') === 0;
const isAutoMerge = message.indexOf('Automatic merge from') === 0;
const isSystemCommit = isSystemPR
|| isSystemMerge
|| isMerge
|| isAutoMerge;
if (isSystemCommit) {
let commitType = COMMIT_TYPE.AUTO_MERGE;
let commitType = COMMIT_TYPE.MERGE;
let prId, repository, branch, toBranch, task, taskNumber;
if (isSystemMerge) {
commitType = COMMIT_TYPE.PR_GITHUB;
@ -63,6 +63,7 @@ export default function getUserInfo(logString: string): ICommit | ISystemCommit
prId = messageParts.shift();
task = getTask(messageParts.join(':'));
} else if (isAutoMerge) {
commitType = COMMIT_TYPE.AUTO_MERGE;
[, branch, toBranch ] = message
.replace(/(Automatic\smerge\sfrom\s)|(\s->\s)/gim, ',')
.replace(/(Merge\sremote-tracking\sbranch\s')|('\sinto\s)/gim, ',')

View file

@ -27,6 +27,7 @@ export interface ILog {
export const COMMIT_TYPE = {
PR_BITBUCKET: 'PR_BITBUCKET',
PR_GITHUB: 'PR_GITHUB',
MERGE: 'MERGE',
AUTO_MERGE: 'AUTO_MERGE',
};

View file

@ -1,3 +1,3 @@
export default interface IHashMap<T> {
[key: string]: T;
[key: string | number]: T;
}

View file

@ -1,8 +1,8 @@
import React, { useEffect, useState } from 'react';
import { Routes, Route } from 'react-router-dom';
import { Route, Routes } from 'react-router-dom';
import { observer } from 'mobx-react-lite';
import dataGripStore from 'ts/store/DataGrip';
import dataGripStore, { DataParseStatusEnum } from 'ts/store/DataGrip';
import DropZone from 'ts/components/DropZone';
import SplashScreen from 'ts/components/SplashScreen';
import Confirm from 'ts/components/ModalWindow/Confirm';
@ -78,7 +78,7 @@ function ViewWithCharts({ showSplashScreen }: IViewWithChartsProps) {
);
}
function ViewWithText() {
function ViewWithWelcome() {
return (
<Routes>
<Route
@ -91,19 +91,25 @@ function ViewWithText() {
);
}
const Success = observer((): React.ReactElement => {
const Success = observer(() => {
const [showSplashScreen, setShowSplashScreen] = useState<boolean>(true);
const showChart = dataGripStore.showApplication;
const status = dataGripStore.status;
useEffect(() => {
// @ts-ignore
dataGripStore.setCommits(window?.report || []);
}, []);
if (status === DataParseStatusEnum.PROCESSING) return null;
return (
<>
{showChart && <ViewWithCharts showSplashScreen={showSplashScreen} />}
{!showChart && <ViewWithText />}
{status === DataParseStatusEnum.DONE && (
<ViewWithCharts showSplashScreen={showSplashScreen} />
)}
{status === DataParseStatusEnum.WAITING && (
<ViewWithWelcome />
)}
<DropZone
onChange={(type: string, data: any[]) => {
setShowSplashScreen(false);

View file

@ -18,6 +18,8 @@ import LineChart from 'ts/components/LineChart';
import { getMax } from 'ts/pages/Common/helpers/getMax';
import { getDate } from 'ts/helpers/formatter';
import AllPR from './PR/All';
interface IReleaseViewProps {
response?: IPagination<any>;
updateSort?: Function;
@ -42,6 +44,22 @@ function ReleaseView({ response, updateSort, rowsForExcel, mode }: IReleaseViewP
type={mode === 'print' ? 'cards' : undefined}
columnCount={mode === 'print' ? 3 : undefined}
>
<Column
isFixed
template={ColumnTypesEnum.DETAILS}
width={40}
formatter={(row: any) => {
const content = row.pr.map((commit: any) => (
dataGripStore?.dataGrip?.pr?.pr?.[commit.prId]
));
console.log(dataGripStore?.dataGrip?.pr?.pr?.['2810']);
return (
<AllPR // @ts-ignore
response={{ content }}
/>
);
}}
/>
<Column
isFixed
template={ColumnTypesEnum.STRING}
@ -63,6 +81,10 @@ function ReleaseView({ response, updateSort, rowsForExcel, mode }: IReleaseViewP
properties="to"
formatter={getDate}
/>
<Column
template={ColumnTypesEnum.SHORT_NUMBER}
properties="prLength"
/>
<Column
template={ColumnTypesEnum.SHORT_NUMBER}
properties="delayInDays"

View file

@ -12,10 +12,16 @@ import { applicationHasCustom } from 'ts/helpers/RPC';
import settingsStore from './Settings';
export enum DataParseStatusEnum {
WAITING = 'waiting',
PROCESSING = 'processing',
DONE = 'done',
}
interface IDataGripStore {
commits: ICommit[];
dataGrip: any;
showApplication: boolean;
status: DataParseStatusEnum;
setCommits: (log?: string[]) => void;
}
@ -32,13 +38,13 @@ class DataGripStore implements IDataGripStore {
dataGrip: any = null;
showApplication: boolean = false;
status: DataParseStatusEnum = DataParseStatusEnum.PROCESSING;
constructor() {
makeObservable(this, {
commits: observable,
dataGrip: observable,
showApplication: observable,
status: observable,
setCommits: action,
});
}
@ -65,8 +71,11 @@ class DataGripStore implements IDataGripStore {
this.removedFileList = removed.fileList;
this.removedFileTree = getFileTreeWithStatistic(removed.fileTree);
this.showApplication = !!this.commits.length;// && !!dataGrip.author.list.length;
if (this.showApplication) {
this.status = this.commits.length
? DataParseStatusEnum.DONE
: DataParseStatusEnum.WAITING;
if (this.status === DataParseStatusEnum.DONE) {
setDefaultValues(dataGrip.firstLastCommit.minData, dataGrip.firstLastCommit.maxData);
settingsStore.updateByCommits(
this.commits,