feat(worktime): apply baseline classes via ansible
This commit is contained in:
@@ -11,6 +11,8 @@
|
|||||||
aw_bootstrap_dir: "/tmp/aw-rus-bootstrap"
|
aw_bootstrap_dir: "/tmp/aw-rus-bootstrap"
|
||||||
aw_ru_patch_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-ru-patch.js') | hash('sha1') | truncate(12, true, '') }}"
|
aw_ru_patch_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-ru-patch.js') | hash('sha1') | truncate(12, true, '') }}"
|
||||||
aw_sw_cleanup_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-sw-cleanup.js') | hash('sha1') | truncate(12, true, '') }}"
|
aw_sw_cleanup_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-sw-cleanup.js') | hash('sha1') | truncate(12, true, '') }}"
|
||||||
|
aw_worktime_classes: "{{ lookup('file', aw_repo_root + '/aw-server/settings/classes-worktime.json') | from_json }}"
|
||||||
|
aw_default_views: "{{ lookup('file', aw_repo_root + '/aw-server/settings/views-default.json') | from_json }}"
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Install base packages
|
- name: Install base packages
|
||||||
@@ -143,6 +145,24 @@
|
|||||||
delay: 3
|
delay: 3
|
||||||
until: aw_api.status == 200
|
until: aw_api.status == 200
|
||||||
|
|
||||||
|
- name: Apply baseline worktime settings (classes)
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/classes"
|
||||||
|
method: POST
|
||||||
|
body: "{{ aw_worktime_classes }}"
|
||||||
|
body_format: json
|
||||||
|
status_code: 201
|
||||||
|
when: aw_apply_worktime_settings | default(false) | bool
|
||||||
|
|
||||||
|
- name: Apply baseline views (include DLP and worktime)
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/views"
|
||||||
|
method: POST
|
||||||
|
body: "{{ aw_default_views }}"
|
||||||
|
body_format: json
|
||||||
|
status_code: 201
|
||||||
|
when: aw_apply_worktime_settings | default(false) | bool
|
||||||
|
|
||||||
handlers:
|
handlers:
|
||||||
- name: Reload systemd
|
- name: Reload systemd
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
|
|||||||
@@ -9,3 +9,7 @@ aw_server_user: "activitywatch"
|
|||||||
aw_server_group: "activitywatch"
|
aw_server_group: "activitywatch"
|
||||||
|
|
||||||
aw_repo_root: "/home/igor/tmp/AWatch-rus"
|
aw_repo_root: "/home/igor/tmp/AWatch-rus"
|
||||||
|
|
||||||
|
# Optional: apply a baseline worktime-focused categorization and views via AW settings API.
|
||||||
|
# WARNING: this overwrites existing server-side settings/classes/views.
|
||||||
|
aw_apply_worktime_settings: false
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": ["Работа"],
|
||||||
|
"rule": { "type": "none" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": ["Работа", "1С"],
|
||||||
|
"rule": {
|
||||||
|
"type": "regex",
|
||||||
|
"regex": "\\b(1cv8s?|1cv8c|1cestart)\\.exe\\b|1С:Предприятие|Запуск 1С:Предприятия|Загрузка конфигурационной информации|Доступ к информационной базе",
|
||||||
|
"ignore_case": true
|
||||||
|
},
|
||||||
|
"data": { "color": "#194D33" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": ["Работа", "Документы"],
|
||||||
|
"rule": {
|
||||||
|
"type": "regex",
|
||||||
|
"regex": "\\b(winword|excel|powerpnt|outlook|acrord32|acrord64)\\.exe\\b|Adobe Reader|Acrobat",
|
||||||
|
"ignore_case": true
|
||||||
|
},
|
||||||
|
"data": { "color": "#2E7D32" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": ["Работа", "Коммуникации"],
|
||||||
|
"rule": {
|
||||||
|
"type": "regex",
|
||||||
|
"regex": "\\b(teams|telegram|slack|thunderbird|zoom|skype|whatsapp|viber|discord)\\.exe\\b|Mattermost|Element|Riot",
|
||||||
|
"ignore_case": true
|
||||||
|
},
|
||||||
|
"data": { "color": "#1E88E5" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"name": ["Работа", "Администрирование"],
|
||||||
|
"rule": {
|
||||||
|
"type": "regex",
|
||||||
|
"regex": "\\b(mstsc|putty|kitty|winscp|anydesk|teamviewer|vncviewer|mmc|regedit|services|control|powershell|cmd)\\.exe\\b",
|
||||||
|
"ignore_case": true
|
||||||
|
},
|
||||||
|
"data": { "color": "#6D4C41" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"name": ["Интернет"],
|
||||||
|
"rule": { "type": "none" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"name": ["Интернет", "Браузер"],
|
||||||
|
"rule": {
|
||||||
|
"type": "regex",
|
||||||
|
"regex": "\\b(chrome|msedge|firefox|opera|brave|vivaldi|browser)\\.exe\\b",
|
||||||
|
"ignore_case": true
|
||||||
|
},
|
||||||
|
"data": { "color": "#00897B" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"name": ["Система"],
|
||||||
|
"rule": { "type": "none" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"name": ["Система", "Windows"],
|
||||||
|
"rule": {
|
||||||
|
"type": "regex",
|
||||||
|
"regex": "\\b(SearchHost|explorer|ShellExperienceHost|ApplicationFrameHost|RuntimeBroker|sihost|dwm|svchost|fontdrvhost|userinit)\\.exe\\b|\\\\Windows\\\\System32",
|
||||||
|
"ignore_case": true
|
||||||
|
},
|
||||||
|
"data": { "color": "#607D8B" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"name": ["ActivityWatch"],
|
||||||
|
"rule": {
|
||||||
|
"type": "regex",
|
||||||
|
"regex": "ActivityWatch|\\baw-(watcher|qt)\\.exe\\b",
|
||||||
|
"ignore_case": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "summary",
|
||||||
|
"name": "Summary",
|
||||||
|
"elements": [
|
||||||
|
{ "type": "top_titles", "size": 3 },
|
||||||
|
{ "type": "timeline_barchart", "size": 3 },
|
||||||
|
{ "type": "top_categories", "size": 3 },
|
||||||
|
{ "type": "category_tree", "size": 3 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "window",
|
||||||
|
"name": "Window",
|
||||||
|
"elements": [
|
||||||
|
{ "type": "top_apps", "size": 3, "props": {} }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "DLP",
|
||||||
|
"name": "DLP",
|
||||||
|
"elements": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "worktime",
|
||||||
|
"name": "Worktime",
|
||||||
|
"elements": [
|
||||||
|
{ "type": "top_categories", "size": 3, "props": {} },
|
||||||
|
{ "type": "timeline_barchart", "size": 3, "props": {} },
|
||||||
|
{ "type": "category_tree", "size": 3, "props": {} },
|
||||||
|
{ "type": "top_apps", "size": 3, "props": {} }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -34,6 +34,12 @@ ls -l /opt/activitywatch/webui-ru/js/
|
|||||||
- `index.html` содержит оба include;
|
- `index.html` содержит оба include;
|
||||||
- `service-worker.js` заменён cleanup-версией.
|
- `service-worker.js` заменён cleanup-версией.
|
||||||
|
|
||||||
|
## Рабочее время (worktime)
|
||||||
|
|
||||||
|
Если в деплое включен `aw_apply_worktime_settings: true`, playbook применяет базовую категоризацию (classes) и view `worktime`.
|
||||||
|
|
||||||
|
Контрольные AQL-шаблоны для расчета рабочего времени: `docs/worktime_aql_detmir.md`.
|
||||||
|
|
||||||
## Типовые инциденты
|
## Типовые инциденты
|
||||||
|
|
||||||
### Сервис не стартует
|
### Сервис не стартует
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
# DetMir: Worktime (AQL templates)
|
||||||
|
|
||||||
|
Цель: получить «рабочее время» по данным ActivityWatch с учетом:
|
||||||
|
|
||||||
|
- активного времени (`not-afk`);
|
||||||
|
- категоризации (classes) для оконных событий;
|
||||||
|
- отдельного категоризованного веб-потока `aw-detmir-web-category_<HOST>` для доменов.
|
||||||
|
|
||||||
|
Ниже — шаблоны AQL для страницы `Query` в AW Web UI.
|
||||||
|
|
||||||
|
## Рабочее время по приложениям (окна)
|
||||||
|
|
||||||
|
Подходит для расчета рабочего времени в толстых клиентах (1С, документы, админка).
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
events = flood(query_bucket("aw-watcher-window_SHARKON2025"));
|
||||||
|
not_afk = flood(query_bucket("aw-watcher-afk_SHARKON2025"));
|
||||||
|
not_afk = filter_keyvals(not_afk, "status", ["not-afk"]);
|
||||||
|
|
||||||
|
events = filter_period_intersect(events, not_afk);
|
||||||
|
events = categorize(events, __CATEGORIES__);
|
||||||
|
|
||||||
|
work = filter_keyvals(events, "$category", [
|
||||||
|
["Работа", "1С"],
|
||||||
|
["Работа", "Документы"],
|
||||||
|
["Работа", "Коммуникации"],
|
||||||
|
["Работа", "Администрирование"]
|
||||||
|
]);
|
||||||
|
|
||||||
|
work = merge_events_by_keys(work, ["$category", "app"]);
|
||||||
|
RETURN = sort_by_duration(work);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Рабочее время в вебе (по доменам)
|
||||||
|
|
||||||
|
Требует, чтобы на клиенте работал browser collector и писал в:
|
||||||
|
`aw-detmir-web-category_<HOST>` поля `categoryGroup`, `rootDomain`.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
web = flood(query_bucket("aw-detmir-web-category_SHARKON2025"));
|
||||||
|
not_afk = flood(query_bucket("aw-watcher-afk_SHARKON2025"));
|
||||||
|
not_afk = filter_keyvals(not_afk, "status", ["not-afk"]);
|
||||||
|
|
||||||
|
web = filter_period_intersect(web, not_afk);
|
||||||
|
web = filter_keyvals(web, "categoryGroup", ["work"]);
|
||||||
|
|
||||||
|
web = merge_events_by_keys(web, ["rootDomain", "category"]);
|
||||||
|
RETURN = sort_by_duration(web);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sanity-check: «куда уходит время»
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
events = flood(query_bucket("aw-watcher-window_SHARKON2025"));
|
||||||
|
not_afk = flood(query_bucket("aw-watcher-afk_SHARKON2025"));
|
||||||
|
not_afk = filter_keyvals(not_afk, "status", ["not-afk"]);
|
||||||
|
|
||||||
|
events = filter_period_intersect(events, not_afk);
|
||||||
|
events = categorize(events, __CATEGORIES__);
|
||||||
|
|
||||||
|
events = merge_events_by_keys(events, ["$category"]);
|
||||||
|
RETURN = sort_by_duration(events);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Замечания
|
||||||
|
|
||||||
|
- Для других хостов замените суффикс `_SHARKON2025` на нужный hostname.
|
||||||
|
- Если web-поток пустой, рабочее время в браузере корректно посчитать по доменам не получится. Тогда либо:
|
||||||
|
- чинить/запускать browser collector;
|
||||||
|
- либо временно считать браузер в `window` как «Интернет/Браузер» без разделения на work/personal.
|
||||||
Reference in New Issue
Block a user