feat: initial project structure

- GitLabClient: REST Events API z paginacją, pobieranie członków grupy
- Aggregator: zliczanie commitów, MR, komentarzy per użytkownik
- Exporter: generowanie pliku Excel (openpyxl) ze stylami
- main.py: CLI (click) + .env support
- README, .env.example, requirements.txt, .gitignore
This commit is contained in:
2026-03-04 18:27:01 +00:00
commit 066539c89c
8 changed files with 484 additions and 0 deletions

69
aggregator.py Normal file
View File

@@ -0,0 +1,69 @@
"""
Agregator — przetwarza eventy GitLab na statystyki per użytkownik.
"""
from collections import defaultdict
from dataclasses import dataclass, field
@dataclass
class UserStats:
name: str = ""
username: str = ""
user_id: int = 0
commits: int = 0
merge_requests: int = 0
comments: int = 0
other: int = 0
@property
def total_contributions(self) -> int:
return self.commits + self.merge_requests + self.comments + self.other
class Aggregator:
def __init__(self):
self._stats: dict[int, UserStats] = defaultdict(UserStats)
def process_events(self, events: list[dict]) -> None:
"""
Przetwarza listę eventów GitLab i agreguje statystyki.
Typy akcji:
- pushed → commit
- merged → merge request
- commented → komentarz
- created/closed/reopened → inne
"""
for event in events:
author = event.get("author") or {}
uid = author.get("id") or event.get("author_id")
if not uid:
continue
stats = self._stats[uid]
stats.user_id = uid
stats.name = author.get("name", stats.name)
stats.username = author.get("username", stats.username)
action = event.get("action_name", "")
if action == "pushed to" or action == "pushed new":
# Liczymy commity z push events
push_data = event.get("push_data") or {}
commit_count = push_data.get("commit_count", 1)
stats.commits += max(commit_count, 1)
elif action in ("accepted", "merged"):
stats.merge_requests += 1
elif action == "commented on":
stats.comments += 1
else:
stats.other += 1
def get_stats(self) -> list[UserStats]:
"""Zwraca listę statystyk posortowaną malejąco po contributions."""
return sorted(
self._stats.values(),
key=lambda s: s.total_contributions,
reverse=True,
)