Skip to content

Commit c6670aa

Browse files
committed
add precomit and ticket analytics view
1 parent 21f3c5b commit c6670aa

47 files changed

Lines changed: 3660 additions & 22 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.pre-commit-config.yaml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
repos:
2+
- repo: https://github.com/astral-sh/ruff-pre-commit
3+
rev: v0.15.13
4+
hooks:
5+
- id: ruff-check
6+
args: [--fix]
7+
- id: ruff-format

‎CONTRIBUTING.md‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,21 @@ Thank you for improving `glpi-python-client`.
88
python -m venv .venv
99
.venv\Scripts\activate
1010
python -m pip install -e .[dev]
11+
python -m pre_commit install
12+
python -m pre_commit run --all-files
1113
python -m pytest
1214
```
1315

16+
The repository ships a root `.pre-commit-config.yaml` that runs Ruff on each
17+
commit. The lint hook applies safe fixes first, then Ruff formats the touched
18+
files.
19+
1420
## Quality Checks
1521

1622
Run the focused checks before opening a pull request:
1723

1824
```bash
25+
python -m pre_commit run --all-files
1926
python -m pytest
2027
python -m ruff check .
2128
python -m mypy glpi_python_client
@@ -26,7 +33,7 @@ python -m mypy glpi_python_client
2633
To build the documentation locally:
2734

2835
```bash
29-
python -m sphinx -W --keep-going -b html docs docs/_build/html pa
36+
python -m sphinx -W --keep-going -b html docs docs/_build/html
3037
```
3138

3239
## GitHub Actions

‎conftest.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
pytest_plugins = ("glpi_python_client.testing.fixtures",)
1+
pytest_plugins = ("glpi_python_client.testing.fixtures",)

‎docs/api_reference.rst‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ Legacy v1 Session
3030
Models
3131
------
3232

33+
.. autoclass:: GlpiEntity
34+
:members:
35+
:undoc-members:
36+
:show-inheritance:
37+
3338
.. autoclass:: GlpiUser
3439
:members:
3540
:undoc-members:
@@ -70,6 +75,29 @@ Models
7075
:undoc-members:
7176
:show-inheritance:
7277

78+
.. autoclass:: GlpiTicketContext
79+
:members:
80+
:undoc-members:
81+
:show-inheritance:
82+
83+
Enums
84+
-----
85+
86+
.. autoclass:: GlpiTicketStatus
87+
:members:
88+
:undoc-members:
89+
:show-inheritance:
90+
91+
.. autoclass:: GlpiPriority
92+
:members:
93+
:undoc-members:
94+
:show-inheritance:
95+
96+
.. autoclass:: GlpiTicketType
97+
:members:
98+
:undoc-members:
99+
:show-inheritance:
100+
73101
Package Metadata
74102
----------------
75103

‎docs/development.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,19 @@ python -m venv .venv
99
.venv\Scripts\activate
1010
python -m pip install --upgrade pip
1111
python -m pip install -e .[dev]
12+
python -m pre_commit install
1213
```
1314

15+
The repository ships a root `.pre-commit-config.yaml` that runs Ruff on each
16+
commit. The lint hook applies safe fixes first, then Ruff formats the touched
17+
files.
18+
1419
## Checks
1520

1621
Run these before publishing or opening a pull request:
1722

1823
```bash
24+
python -m pre_commit run --all-files
1925
python -m pytest
2026
python -m ruff check .
2127
python -m mypy glpi_python_client

‎docs/development_rtd.rst‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ Create a virtual environment and install the development dependencies:
1212
.venv\Scripts\activate
1313
python -m pip install --upgrade pip
1414
python -m pip install -e .[dev]
15+
python -m pre_commit install
16+
17+
The repository ships a root ``.pre-commit-config.yaml`` that runs Ruff on each
18+
commit. The lint hook applies safe fixes first, then Ruff formats the touched
19+
files.
1520

1621
Quality Checks
1722
--------------
@@ -20,6 +25,7 @@ Run the focused checks before opening a pull request:
2025

2126
.. code-block:: console
2227
28+
python -m pre_commit run --all-files
2329
python -m pytest
2430
python -m ruff check .
2531
python -m mypy glpi_python_client

‎docs/usage.md‎

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,42 @@ category. If your GLPI workflow requires any of those values, set them on
138138
`status`, `type`, `category`, `location`, `date_creation`, `date_mod`,
139139
`date_close`, `user_recipient`, `user_editor`, and `team`.
140140

141+
When you request extra ticket fields that do not map to typed `GlpiTicket`
142+
attributes, the package preserves them in `ticket.extra_payload` instead of
143+
dropping them. This keeps the modeled fields typed while still exposing the raw
144+
requested GLPI keys through a public field.
145+
146+
```python
147+
tickets = glpi.search_ticket_records(
148+
query='status.id=in=(1,2)',
149+
fields=("resolution_date", "date_solve"),
150+
)
151+
152+
first_ticket = tickets[0]
153+
print(first_ticket.extra_payload["resolution_date"])
154+
print(first_ticket.extra_payload["date_solve"])
155+
```
156+
157+
## Entities
158+
159+
Use `search_entities()` when you need typed entity lookup from the public
160+
package root.
161+
162+
```python
163+
from glpi_python_client import GlpiEntity
164+
165+
entities = glpi.search_entities(
166+
rsql_filter='name=like=*novahe*',
167+
limit=50,
168+
start=0,
169+
)
170+
171+
for entity in entities:
172+
print(entity.entity_id, entity.name, entity.complete_name)
173+
```
174+
175+
Unmodeled entity payload keys are preserved in `GlpiEntity.extra_payload`.
176+
141177
## Models and Content Formatting
142178

143179
Public GLPI objects are field-validated Pydantic models. Create and update GLPI
@@ -204,6 +240,78 @@ solutions = glpi.get_solution_records("123")
204240
Public client methods accept GLPI identifiers as either `str` or `int` and
205241
normalize them into request paths as needed.
206242

243+
## Tasks And Duration Statistics
244+
245+
Use `search_task_records()` for global task searches and `get_task_durations()`
246+
when you need aggregated duration reports.
247+
248+
```python
249+
tasks = glpi.search_task_records(
250+
query='date=ge=2026-01-01;date=le=2026-01-31',
251+
fields=("id", "tickets_id", "users_id", "actiontime", "date", "content"),
252+
sort="date:desc",
253+
)
254+
255+
summary = glpi.get_task_durations(
256+
start_date="2026-01-01",
257+
end_date="2026-01-31",
258+
entity_name="Novahe",
259+
return_task_details=True,
260+
)
261+
262+
print(summary["total_duration"])
263+
print(summary["duration_by_user"])
264+
```
265+
266+
`GlpiTask` keeps typed fields such as `ticket_id`, `user_id`, `duration`,
267+
`date`, and `entity`. Additional task payload keys remain available through
268+
`GlpiTask.extra_payload`.
269+
270+
## Ticket Statistics And User Activity
271+
272+
Public enums keep the GLPI numeric constants at the package root and can be
273+
used directly in filters.
274+
275+
```python
276+
from glpi_python_client import GlpiPriority, GlpiTicketStatus, GlpiTicketType
277+
278+
open_ticket_query = GlpiTicketStatus.NEW.rsql_equals("status")
279+
request_query = GlpiTicketType.REQUEST.rsql_equals("type")
280+
281+
stats = glpi.get_ticket_statistics(
282+
entity_name="Novahe",
283+
start_date="2026-01-01",
284+
end_date="2026-01-31",
285+
extra_filter=f"{open_ticket_query};{request_query}",
286+
)
287+
288+
activity = glpi.get_user_activity(
289+
email="jane.doe@example.com",
290+
start_date="2026-01-01",
291+
end_date="2026-01-31",
292+
)
293+
294+
print(stats["entities"])
295+
print(activity["users"])
296+
```
297+
298+
The statistics output groups counts by entity, status, priority, and type. The
299+
activity output groups requester counts, technician counts, and nested task
300+
duration summaries by user.
301+
302+
## Ticket Context
303+
304+
Use `get_ticket_context()` when you need the core ticket together with the
305+
common timeline and document records in one public object.
306+
307+
```python
308+
bundle = glpi.get_ticket_context("123")
309+
310+
print(bundle.ticket.id)
311+
print(len(bundle.tasks), len(bundle.followups), len(bundle.solutions))
312+
print(len(bundle.documents))
313+
```
314+
207315
## Users and Locations
208316

209317
```python

‎docs/user_guide.rst‎

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,117 @@ Replace ``_room_code`` and ``_asset_tag`` with the field names expected by your
161161
GLPI instance or plugin. The same ``extra_payload`` pattern works with the
162162
other public payload-backed models.
163163

164+
Requested Ticket Fields Not Modeled Directly
165+
--------------------------------------------
166+
167+
When you request extra ticket fields that do not map to typed
168+
:class:`glpi_python_client.GlpiTicket` attributes, the package preserves those
169+
values in ``ticket.extra_payload`` instead of dropping them.
170+
171+
.. code-block:: python
172+
173+
tickets = glpi.search_ticket_records(
174+
query='status.id=in=(1,2)',
175+
fields=("resolution_date", "date_solve"),
176+
)
177+
178+
first_ticket = tickets[0]
179+
print(first_ticket.extra_payload["resolution_date"])
180+
print(first_ticket.extra_payload["date_solve"])
181+
182+
Entity Search
183+
-------------
184+
185+
Use :meth:`glpi_python_client.GlpiClient.search_entities` when you need typed
186+
entity lookup from the public package root.
187+
188+
.. code-block:: python
189+
190+
entities = glpi.search_entities(
191+
rsql_filter='name=like=*novahe*',
192+
limit=50,
193+
start=0,
194+
)
195+
196+
for entity in entities:
197+
print(entity.entity_id, entity.name, entity.complete_name)
198+
199+
Unmodeled entity payload keys remain available through
200+
:attr:`glpi_python_client.GlpiEntity.extra_payload`.
201+
202+
Task Search and Duration Statistics
203+
-----------------------------------
204+
205+
Use :meth:`glpi_python_client.GlpiClient.search_task_records` for global task
206+
searches and :meth:`glpi_python_client.GlpiClient.get_task_durations` when you
207+
need aggregated duration reports.
208+
209+
.. code-block:: python
210+
211+
tasks = glpi.search_task_records(
212+
query='date=ge=2026-01-01;date=le=2026-01-31',
213+
fields=("id", "tickets_id", "users_id", "actiontime", "date", "content"),
214+
sort="date:desc",
215+
)
216+
217+
summary = glpi.get_task_durations(
218+
start_date="2026-01-01",
219+
end_date="2026-01-31",
220+
entity_name="Novahe",
221+
return_task_details=True,
222+
)
223+
224+
print(summary["total_duration"])
225+
print(summary["duration_by_user"])
226+
227+
:class:`glpi_python_client.GlpiTask` keeps typed fields such as ``ticket_id``,
228+
``user_id``, ``duration``, ``date``, and ``entity``. Additional task payload
229+
keys remain available through ``extra_payload``.
230+
231+
Ticket Statistics and User Activity
232+
-----------------------------------
233+
234+
Public enums keep the common GLPI numeric constants at the package root and
235+
can be used directly when composing RSQL filters.
236+
237+
.. code-block:: python
238+
239+
from glpi_python_client import GlpiTicketStatus, GlpiTicketType
240+
241+
open_ticket_query = GlpiTicketStatus.NEW.rsql_equals("status")
242+
request_query = GlpiTicketType.REQUEST.rsql_equals("type")
243+
244+
stats = glpi.get_ticket_statistics(
245+
entity_name="Novahe",
246+
start_date="2026-01-01",
247+
end_date="2026-01-31",
248+
extra_filter=f"{open_ticket_query};{request_query}",
249+
)
250+
251+
activity = glpi.get_user_activity(
252+
email="jane.doe@example.com",
253+
start_date="2026-01-01",
254+
end_date="2026-01-31",
255+
)
256+
257+
print(stats["entities"])
258+
print(activity["users"])
259+
260+
Ticket Context Bundles
261+
----------------------
262+
263+
Use :meth:`glpi_python_client.GlpiClient.get_ticket_context` when one workflow
264+
needs the primary ticket together with the common timeline and document
265+
records.
266+
267+
.. code-block:: python
268+
269+
bundle = glpi.get_ticket_context("123")
270+
271+
print(bundle.ticket.id)
272+
print(len(bundle.tasks), len(bundle.followups), len(bundle.solutions))
273+
print(len(bundle.documents))
274+
164275
Utility Constructor
165276
-------------------
166277

0 commit comments

Comments
 (0)