operx-cli 0.7.7__tar.gz → 0.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {operx_cli-0.7.7 → operx_cli-0.8.0}/PKG-INFO +1 -1
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/AGENTES.md +1 -1
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/__init__.py +1 -1
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/ambiental.py +3 -22
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/ordenes.py +7 -1
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli.egg-info/PKG-INFO +1 -1
- {operx_cli-0.7.7 → operx_cli-0.8.0}/pyproject.toml +1 -1
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_cards.py +1 -1
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_json_writes.py +1 -4
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_ordenes.py +14 -1
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_sst_ambiental.py +0 -17
- {operx_cli-0.7.7 → operx_cli-0.8.0}/README.md +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/agent_guide.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/api_fields.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/app.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/browser_login.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/client.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/__init__.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/account.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/articulos.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/avisos.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/campos.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/common.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/completar.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/empresas.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/inventario.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/lecturas.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/muestreo.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/personas.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/planta.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/preventivos.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/raw.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/registros.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/reportes.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/rutinas.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/search.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/sst.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/commands/update.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/lookup.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/output.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/record_completion.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/self_update.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/settings.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/shell.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/spreadsheet.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/updates.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/values.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli/welcome.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli.egg-info/SOURCES.txt +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli.egg-info/dependency_links.txt +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli.egg-info/entry_points.txt +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli.egg-info/requires.txt +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/operx_cli.egg-info/top_level.txt +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/setup.cfg +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_agentes.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_api_campos.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_app.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_articulos.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_campos.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_client.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_completar.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_daily_flows.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_inventario_lecturas.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_login.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_logout_and_routes.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_lookup.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_more_of_the_api.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_muestreo.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_output.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_personas.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_preventivos.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_record_completion.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_registros.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_rutinas.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_self_update.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_settings.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_shell.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_spreadsheet.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_updates.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_values.py +0 -0
- {operx_cli-0.7.7 → operx_cli-0.8.0}/tests/test_welcome.py +0 -0
|
@@ -79,7 +79,7 @@ llamada queda en el historial del token, marcada como hecha desde la CLI.
|
|
|
79
79
|
`rutinas crear`, `cambiar` y `quitar`; `campos crear`, `cambiar`,
|
|
80
80
|
`quitar` y `guardar`.
|
|
81
81
|
- SG-SST y Ambiental: `sst nueva-accion` y `accion-hecha`;
|
|
82
|
-
`ambiental nueva-accion`, `accion-hecha
|
|
82
|
+
`ambiental nueva-accion`, `accion-hecha` y `residuo`.
|
|
83
83
|
- `avisos --marcar-leidos`, y `operx api` con POST, PUT, PATCH o DELETE.
|
|
84
84
|
- Un error se corrige con su contrario, nunca borrando: `inventario revertir`
|
|
85
85
|
deshace un movimiento, `lecturas anular` una lectura. La excepción es
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""`operx ambiental`: Ambiental, per company: its action plan, permits and
|
|
2
|
-
obligations, and the
|
|
3
|
-
readings. A wrong record is voided in
|
|
2
|
+
obligations, and the record made most often, waste. Consumption is kept
|
|
3
|
+
by the month in the app (no meter readings). A wrong record is voided in
|
|
4
|
+
the app, never edited."""
|
|
4
5
|
|
|
5
6
|
from datetime import date
|
|
6
7
|
from typing import Annotated, Optional
|
|
@@ -158,23 +159,3 @@ def _stream(client, path, corriente, sede):
|
|
|
158
159
|
if not streams:
|
|
159
160
|
raise CliError("Esta empresa aún no tiene corrientes de residuos: créalas en la app, en Ambiental › Residuos.")
|
|
160
161
|
return find_in(streams, corriente, ["name"], "corriente")
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
@app.command()
|
|
164
|
-
def lectura(
|
|
165
|
-
ctx: typer.Context,
|
|
166
|
-
medidor: Annotated[str, typer.Option("--medidor", help="El nombre del medidor o de lo que mide: Acueducto.")],
|
|
167
|
-
valor: Annotated[str, typer.Option("--valor", help="Lo que marca: 1.234,5.")],
|
|
168
|
-
fecha: FechaOption = None,
|
|
169
|
-
empresa: EmpresaOption = None,
|
|
170
|
-
si: YesOption = False,
|
|
171
|
-
as_json: JsonOption = False,
|
|
172
|
-
):
|
|
173
|
-
"""Registra la lectura de un medidor (agua, energía, gas)."""
|
|
174
|
-
client = client_of(ctx)
|
|
175
|
-
path = company_path(client, ENVIRONMENT, empresa)
|
|
176
|
-
meter = find_in(client.get(f"{path}/consumption/meters"), medidor, ["name", "meter_name"], "medidor")
|
|
177
|
-
reading = {"source": meter["id"], "read_on": day(fecha) or date.today().isoformat(), "value": number(valor)}
|
|
178
|
-
confirm_write(f"Lectura {valor} en «{meter['name']}» el {shown_moment(reading['read_on'])}.", si)
|
|
179
|
-
recorded = answer_of(client.send("POST", f"{path}/consumption/readings", json=reading))
|
|
180
|
-
print_result(recorded, as_json, f"Lectura registrada: {valor} en {meter['name']}.")
|
|
@@ -72,11 +72,17 @@ PersonOption = Annotated[
|
|
|
72
72
|
]
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
def _assignee(order):
|
|
76
|
+
"""The person doing the order, or the linked contractor doing it instead."""
|
|
77
|
+
return order.get("assigned_to_name") or (order.get("assigned_company") or {}).get("name")
|
|
78
|
+
|
|
79
|
+
|
|
75
80
|
def _in_spanish(order):
|
|
76
81
|
return {
|
|
77
82
|
**order,
|
|
78
83
|
"status": STATUS_LABELS.get(order.get("status"), order.get("status")),
|
|
79
84
|
"priority": PRIORITY_LABELS.get(order.get("priority"), order.get("priority")),
|
|
85
|
+
"assigned_to_name": _assignee(order),
|
|
80
86
|
"due_date": shown_moment(order.get("due_date")),
|
|
81
87
|
}
|
|
82
88
|
|
|
@@ -127,7 +133,7 @@ def ver(ctx: typer.Context, orden: Annotated[int, typer.Argument(help="El númer
|
|
|
127
133
|
("Estado", STATUS_LABELS.get(order.get("status"), order.get("status"))),
|
|
128
134
|
("Prioridad", PRIORITY_LABELS.get(order.get("priority"), order.get("priority"))),
|
|
129
135
|
("Tipo", TYPE_LABELS.get(order.get("work_type"), order.get("work_type"))),
|
|
130
|
-
("Asignada a", order
|
|
136
|
+
("Asignada a", _assignee(order)),
|
|
131
137
|
("También", order.get("extra_assignee_names")),
|
|
132
138
|
("Equipo", order.get("asset_name")),
|
|
133
139
|
("Lugar", order.get("site_name")),
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "operx-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.8.0"
|
|
8
8
|
description = "La línea de comandos de OperX: tu empresa desde la terminal, con tu token de API."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -17,7 +17,7 @@ ORDER = {
|
|
|
17
17
|
"site_name": "Planta Norte",
|
|
18
18
|
"site_arrival": {"address": "", "city": "", "latitude": None, "longitude": None, "directions": ""},
|
|
19
19
|
"customer_id": None, "customer_name": None, "assigned_to_name": None, "assigned_to_avatar": None,
|
|
20
|
-
"extra_assignee_names": [], "location_description": "", "custom_fields": {}, "signature": None,
|
|
20
|
+
"assigned_company": None, "extra_assignee_names": [], "location_description": "", "custom_fields": {}, "signature": None,
|
|
21
21
|
"signed_by_name": "", "signed_at": None, "created_at": "2026-10-23T02:19:42.253068-05:00",
|
|
22
22
|
"updated_at": "2026-10-23T02:19:42.253068-05:00",
|
|
23
23
|
"comments": [{
|
|
@@ -12,7 +12,7 @@ from operx_cli.app import app
|
|
|
12
12
|
from .fake_api import FakeApi, page, run
|
|
13
13
|
from .test_more_of_the_api import FINDING, MOVEMENT, REASONS, STOP
|
|
14
14
|
from .test_preventivos import ASSET, NOTES, PLAN
|
|
15
|
-
from .test_sst_ambiental import EMPLOYEES, ENV_ACTION, ENV_COMPANIES,
|
|
15
|
+
from .test_sst_ambiental import EMPLOYEES, ENV_ACTION, ENV_COMPANIES, SST_ACTION, SST_COMPANIES, STREAMS
|
|
16
16
|
|
|
17
17
|
FILTER = {"id": 3, "code": "FIL-010", "name": "Filtro de aceite"}
|
|
18
18
|
STORE = {"id": 2, "name": "Bodega central"}
|
|
@@ -74,9 +74,6 @@ CASES = [
|
|
|
74
74
|
(["ambiental", "residuo", "--corriente", "aceite usado", "--kg", "12,5"],
|
|
75
75
|
{("GET", "environment/companies/"): ENV_COMPANIES, ("GET", "environment/companies/1/waste/streams/"): STREAMS},
|
|
76
76
|
("POST", "environment/companies/1/waste/entries/"), (201, {"id": 9, "kg": "12.500"})),
|
|
77
|
-
(["ambiental", "lectura", "--medidor", "Acueducto", "--valor", "1.234,5"],
|
|
78
|
-
{("GET", "environment/companies/"): ENV_COMPANIES, ("GET", "environment/companies/1/consumption/meters/"): METERS},
|
|
79
|
-
("POST", "environment/companies/1/consumption/readings/"), (201, {"id": 3})),
|
|
80
77
|
(["avisos", "--marcar-leidos"], {}, ("POST", "notifications/mark-read/"), {"marked": 4}),
|
|
81
78
|
]
|
|
82
79
|
|
|
@@ -6,7 +6,12 @@ from .fake_api import FakeApi, page, run
|
|
|
6
6
|
|
|
7
7
|
ORDER = {
|
|
8
8
|
"id": 15, "title": "Cambio de rodamiento", "status": "in_progress", "priority": "high",
|
|
9
|
-
"assigned_to_name": "Pedro Pérez", "asset_name": "Bomba 4", "site_name": "Planta",
|
|
9
|
+
"assigned_to_name": "Pedro Pérez", "assigned_company": None, "asset_name": "Bomba 4", "site_name": "Planta",
|
|
10
|
+
"due_date": "2026-10-30T00:00:00-05:00",
|
|
11
|
+
}
|
|
12
|
+
# The same order while a linked contractor does it: nobody of the company is on it.
|
|
13
|
+
HELD_BY_CONTRACTOR = {
|
|
14
|
+
**ORDER, "assigned_to_name": None, "assigned_company": {"name": "Climas del Valle", "kind": "contractor"},
|
|
10
15
|
}
|
|
11
16
|
PEOPLE = page({"id": 3, "user_id": 21, "user_name": "Pedro Pérez", "employee_code": "T-01"})
|
|
12
17
|
|
|
@@ -102,3 +107,11 @@ def test_ver_shows_one_order():
|
|
|
102
107
|
result = run(FakeApi({("GET", "work-orders/15/"): ORDER}), "ordenes", "ver", "15")
|
|
103
108
|
assert result.exit_code == 0, result.output
|
|
104
109
|
assert "Cambio de rodamiento" in result.output
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_an_order_a_contractor_has_names_the_contractor():
|
|
113
|
+
listed = run(FakeApi({("GET", "work-orders/"): page(HELD_BY_CONTRACTOR)}), "ordenes", "listar")
|
|
114
|
+
shown = run(FakeApi({("GET", "work-orders/15/"): HELD_BY_CONTRACTOR}), "ordenes", "ver", "15")
|
|
115
|
+
assert listed.exit_code == 0, listed.output
|
|
116
|
+
assert shown.exit_code == 0, shown.output
|
|
117
|
+
assert "Climas del Valle" in listed.output and "Climas del Valle" in shown.output
|
|
@@ -52,9 +52,6 @@ STREAMS = {
|
|
|
52
52
|
"moving_average_kg": None, "storage_alert_months": [], "certificate_days": 30,
|
|
53
53
|
"streams": [{"id": 11, "name": "Aceite usado", "kind": "hazardous", "hazardous": True}],
|
|
54
54
|
}
|
|
55
|
-
METERS = [{"id": 21, "name": "Acueducto", "meter_name": "Contador principal", "qr_code": "x", "establishment": "Planta Norte"}]
|
|
56
|
-
|
|
57
|
-
|
|
58
55
|
@pytest.fixture(autouse=True)
|
|
59
56
|
def signed_in(monkeypatch):
|
|
60
57
|
monkeypatch.setenv("OPERX_TOKEN", "opx_test")
|
|
@@ -224,20 +221,6 @@ def test_ambiental_residuo_finds_the_stream_by_its_name():
|
|
|
224
221
|
assert fake.sent("POST", "environment/companies/1/waste/entries/") == {"stream": 11, "date": "2026-10-22", "kg": "12.5"}
|
|
225
222
|
|
|
226
223
|
|
|
227
|
-
def test_ambiental_lectura_of_a_meter():
|
|
228
|
-
fake = FakeApi({
|
|
229
|
-
("GET", "environment/companies/"): ENV_COMPANIES,
|
|
230
|
-
("GET", "environment/companies/1/consumption/meters/"): METERS,
|
|
231
|
-
("POST", "environment/companies/1/consumption/readings/"): (201, {"id": 3}),
|
|
232
|
-
})
|
|
233
|
-
result = run(fake, "ambiental", "lectura", "--medidor", "Acueducto", "--valor", "1.234,5", "--fecha", "22/10/2026", "--si")
|
|
234
|
-
|
|
235
|
-
assert result.exit_code == 0, result.output
|
|
236
|
-
assert fake.sent("POST", "environment/companies/1/consumption/readings/") == {
|
|
237
|
-
"source": 21, "read_on": "2026-10-22", "value": "1234.5",
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
|
|
241
224
|
# ── Personas ──────────────────────────────────────────────────────────────
|
|
242
225
|
|
|
243
226
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|