indico-cli 0.1.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.
- indico_cli-0.1.0/.gitignore +11 -0
- indico_cli-0.1.0/.gitlab-ci.yml +15 -0
- indico_cli-0.1.0/CLAUDE.md +36 -0
- indico_cli-0.1.0/LICENSE +21 -0
- indico_cli-0.1.0/PKG-INFO +118 -0
- indico_cli-0.1.0/README.md +98 -0
- indico_cli-0.1.0/indico_cli/__init__.py +3 -0
- indico_cli-0.1.0/indico_cli/cli.py +326 -0
- indico_cli-0.1.0/indico_cli/indico_api.py +1766 -0
- indico_cli-0.1.0/indico_cli/utils.py +145 -0
- indico_cli-0.1.0/pyproject.toml +28 -0
- indico_cli-0.1.0/skill/.claude-plugin/plugin.json +11 -0
- indico_cli-0.1.0/skill/package.json +7 -0
- indico_cli-0.1.0/skill/skills/add-timetable-entry/SKILL.md +41 -0
- indico_cli-0.1.0/skill/skills/configure/SKILL.md +74 -0
- indico_cli-0.1.0/skill/skills/create-contribution/SKILL.md +48 -0
- indico_cli-0.1.0/skill/skills/create-event/SKILL.md +52 -0
- indico_cli-0.1.0/skill/skills/create-event-with-agenda/SKILL.md +74 -0
- indico_cli-0.1.0/skill/skills/create-session/SKILL.md +33 -0
- indico_cli-0.1.0/skill/skills/delete-timetable-entry/SKILL.md +32 -0
- indico_cli-0.1.0/skill/skills/download-attachment/SKILL.md +38 -0
- indico_cli-0.1.0/skill/skills/get-contributions/SKILL.md +39 -0
- indico_cli-0.1.0/skill/skills/get-event-details/SKILL.md +39 -0
- indico_cli-0.1.0/skill/skills/get-files/SKILL.md +54 -0
- indico_cli-0.1.0/skill/skills/get-user-info/SKILL.md +34 -0
- indico_cli-0.1.0/skill/skills/search-by-term/SKILL.md +38 -0
- indico_cli-0.1.0/skill/skills/search-categories/SKILL.md +38 -0
- indico_cli-0.1.0/skill/skills/search-events/SKILL.md +41 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
stages:
|
|
2
|
+
- publish
|
|
3
|
+
|
|
4
|
+
publish-pypi:
|
|
5
|
+
stage: publish
|
|
6
|
+
image: python:3.12-slim
|
|
7
|
+
rules:
|
|
8
|
+
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+/
|
|
9
|
+
script:
|
|
10
|
+
- pip install build twine
|
|
11
|
+
- python -m build
|
|
12
|
+
- twine upload dist/*
|
|
13
|
+
variables:
|
|
14
|
+
TWINE_USERNAME: __token__
|
|
15
|
+
TWINE_PASSWORD: $PYPI_API_TOKEN
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
|
|
5
|
+
Python CLI tool for interacting with Indico event management systems. Extracted from the `indico-mcp` MCP server. Run via `uvx indico-cli` or install with `pip install indico-cli`.
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
- `indico_cli/cli.py`: Click CLI entry point with 15 subcommands
|
|
10
|
+
- `indico_cli/indico_api.py`: HTTP API client (`IndicoAPI` for low-level requests, `IndicoClient` for high-level operations)
|
|
11
|
+
- `indico_cli/utils.py`: Utility functions for building Indico API request data
|
|
12
|
+
- `skill/`: Claude Code skill package (14 skills mapping to CLI commands)
|
|
13
|
+
|
|
14
|
+
## Development
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Install in dev mode
|
|
18
|
+
uv pip install -e .
|
|
19
|
+
|
|
20
|
+
# Run CLI
|
|
21
|
+
indico-cli --help
|
|
22
|
+
|
|
23
|
+
# Run with uvx from source
|
|
24
|
+
uvx --from . indico-cli --help
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Configuration
|
|
28
|
+
|
|
29
|
+
Requires env vars `INDICO_BASE_URL` and `INDICO_API_TOKEN`, or use `indico-cli configure`. A `.env` file is supported but must never be committed.
|
|
30
|
+
|
|
31
|
+
## Key Technical Details
|
|
32
|
+
|
|
33
|
+
- All API calls are async (httpx). The CLI wraps them with `asyncio.run()`.
|
|
34
|
+
- Bearer token auth only (tokens starting with `indp_`). No `Content-Type` header on GET requests (Indico returns 403 for file downloads if `Content-Type: application/json` is set).
|
|
35
|
+
- The Indico export API returns local sequential contribution IDs (e.g., 1, 2, 3) not database IDs. Always use `download_url` from API responses for file downloads, not reconstructed paths.
|
|
36
|
+
- Room booking is excluded (needs separate fix).
|
indico_cli-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 indico-cli contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: indico-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CLI tool for interacting with Indico event management systems
|
|
5
|
+
Project-URL: Repository, https://gitlab.cern.ch/cern-agent-skills/indico-cli
|
|
6
|
+
License: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: cern,cli,conferences,events,indico
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering
|
|
16
|
+
Requires-Python: >=3.12
|
|
17
|
+
Requires-Dist: click>=8.0
|
|
18
|
+
Requires-Dist: httpx>=0.25
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# indico-cli
|
|
22
|
+
|
|
23
|
+
Command line interface for interacting with [Indico](https://getindico.io/) event management systems.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Run directly with uvx (recommended)
|
|
29
|
+
uvx indico-cli --help
|
|
30
|
+
|
|
31
|
+
# Or install with pip
|
|
32
|
+
pip install indico-cli
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Configuration
|
|
36
|
+
|
|
37
|
+
Set environment variables:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
export INDICO_BASE_URL="https://indico.cern.ch"
|
|
41
|
+
export INDICO_API_TOKEN="indp_..." # Bearer token from Indico preferences
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Or use the `configure` command to save credentials:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
indico-cli configure --base-url https://indico.cern.ch --token indp_...
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
API tokens can be generated at `<your-indico-url>/user/preferences/api`. Required scopes: `read:legacy_api`, `write:legacy_api` (for create operations), `read:user` (optional).
|
|
51
|
+
|
|
52
|
+
## Usage
|
|
53
|
+
|
|
54
|
+
### Search
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Full-text search across all content types
|
|
58
|
+
indico-cli search-by-term --term "machine learning"
|
|
59
|
+
|
|
60
|
+
# Search events in a category
|
|
61
|
+
indico-cli search-events --category-id 4648 --from-date 2025-01-01
|
|
62
|
+
|
|
63
|
+
# Browse categories
|
|
64
|
+
indico-cli search-categories --category-id 0
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Event Details
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Get event info with contributions and sessions
|
|
71
|
+
indico-cli event-details --event-id 137346 --contributions --sessions
|
|
72
|
+
|
|
73
|
+
# List contributions/talks
|
|
74
|
+
indico-cli contributions --event-id 137346 --subcontributions
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Files
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# List and download all event files
|
|
81
|
+
indico-cli files --event-id 137346
|
|
82
|
+
|
|
83
|
+
# List files without downloading
|
|
84
|
+
indico-cli files --event-id 137346 --no-download
|
|
85
|
+
|
|
86
|
+
# Download a single attachment
|
|
87
|
+
indico-cli download --url "https://indico.cern.ch/event/.../file.pdf"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Create Events
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Create a simple meeting
|
|
94
|
+
indico-cli create-event --title "Team Meeting" --category-id 4648 --type meeting \
|
|
95
|
+
--start 2025-06-15T09:00:00 --end 2025-06-15T10:00:00
|
|
96
|
+
|
|
97
|
+
# Create event with full agenda from JSON file
|
|
98
|
+
indico-cli create-event-with-agenda --title "Workshop" --category-id 4648 \
|
|
99
|
+
--type meeting --start 2025-06-15T09:00:00 --end 2025-06-15T17:00:00 \
|
|
100
|
+
--agenda-file agenda.json
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### All Commands
|
|
104
|
+
|
|
105
|
+
Run `indico-cli --help` to see all available commands, or `indico-cli <command> --help` for command-specific options.
|
|
106
|
+
|
|
107
|
+
## Claude Code Skill
|
|
108
|
+
|
|
109
|
+
A Claude Code skill package is available in `skill/` for AI-assisted Indico workflows. Install it with:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
claude plugin install ./skill
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Documentation
|
|
116
|
+
|
|
117
|
+
- [Indico HTTP API docs](https://docs.getindico.io/en/stable/http-api/)
|
|
118
|
+
- [Indico platform](https://getindico.io/)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# indico-cli
|
|
2
|
+
|
|
3
|
+
Command line interface for interacting with [Indico](https://getindico.io/) event management systems.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Run directly with uvx (recommended)
|
|
9
|
+
uvx indico-cli --help
|
|
10
|
+
|
|
11
|
+
# Or install with pip
|
|
12
|
+
pip install indico-cli
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Configuration
|
|
16
|
+
|
|
17
|
+
Set environment variables:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
export INDICO_BASE_URL="https://indico.cern.ch"
|
|
21
|
+
export INDICO_API_TOKEN="indp_..." # Bearer token from Indico preferences
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or use the `configure` command to save credentials:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
indico-cli configure --base-url https://indico.cern.ch --token indp_...
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
API tokens can be generated at `<your-indico-url>/user/preferences/api`. Required scopes: `read:legacy_api`, `write:legacy_api` (for create operations), `read:user` (optional).
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
### Search
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Full-text search across all content types
|
|
38
|
+
indico-cli search-by-term --term "machine learning"
|
|
39
|
+
|
|
40
|
+
# Search events in a category
|
|
41
|
+
indico-cli search-events --category-id 4648 --from-date 2025-01-01
|
|
42
|
+
|
|
43
|
+
# Browse categories
|
|
44
|
+
indico-cli search-categories --category-id 0
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Event Details
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Get event info with contributions and sessions
|
|
51
|
+
indico-cli event-details --event-id 137346 --contributions --sessions
|
|
52
|
+
|
|
53
|
+
# List contributions/talks
|
|
54
|
+
indico-cli contributions --event-id 137346 --subcontributions
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Files
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# List and download all event files
|
|
61
|
+
indico-cli files --event-id 137346
|
|
62
|
+
|
|
63
|
+
# List files without downloading
|
|
64
|
+
indico-cli files --event-id 137346 --no-download
|
|
65
|
+
|
|
66
|
+
# Download a single attachment
|
|
67
|
+
indico-cli download --url "https://indico.cern.ch/event/.../file.pdf"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Create Events
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Create a simple meeting
|
|
74
|
+
indico-cli create-event --title "Team Meeting" --category-id 4648 --type meeting \
|
|
75
|
+
--start 2025-06-15T09:00:00 --end 2025-06-15T10:00:00
|
|
76
|
+
|
|
77
|
+
# Create event with full agenda from JSON file
|
|
78
|
+
indico-cli create-event-with-agenda --title "Workshop" --category-id 4648 \
|
|
79
|
+
--type meeting --start 2025-06-15T09:00:00 --end 2025-06-15T17:00:00 \
|
|
80
|
+
--agenda-file agenda.json
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### All Commands
|
|
84
|
+
|
|
85
|
+
Run `indico-cli --help` to see all available commands, or `indico-cli <command> --help` for command-specific options.
|
|
86
|
+
|
|
87
|
+
## Claude Code Skill
|
|
88
|
+
|
|
89
|
+
A Claude Code skill package is available in `skill/` for AI-assisted Indico workflows. Install it with:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
claude plugin install ./skill
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Documentation
|
|
96
|
+
|
|
97
|
+
- [Indico HTTP API docs](https://docs.getindico.io/en/stable/http-api/)
|
|
98
|
+
- [Indico platform](https://getindico.io/)
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""CLI entry point for interacting with Indico event management systems."""
|
|
3
|
+
|
|
4
|
+
import asyncio
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
import click
|
|
12
|
+
|
|
13
|
+
from indico_cli.indico_api import IndicoAPI, IndicoClient
|
|
14
|
+
|
|
15
|
+
CONFIG_DIR = Path.home() / ".config" / "indico-cli"
|
|
16
|
+
CONFIG_FILE = CONFIG_DIR / "config.json"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def load_config() -> dict:
|
|
20
|
+
"""Load saved configuration from disk, returning empty dict on failure."""
|
|
21
|
+
if CONFIG_FILE.exists():
|
|
22
|
+
try:
|
|
23
|
+
return json.loads(CONFIG_FILE.read_text())
|
|
24
|
+
except (json.JSONDecodeError, OSError):
|
|
25
|
+
return {}
|
|
26
|
+
return {}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def save_config(base_url: str, token: str) -> None:
|
|
30
|
+
"""Persist configuration to ~/.config/indico-cli/config.json."""
|
|
31
|
+
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
32
|
+
CONFIG_FILE.write_text(json.dumps({"base_url": base_url, "token": token}, indent=2))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_client(base_url: Optional[str], token: Optional[str]) -> IndicoClient:
|
|
36
|
+
"""Create an IndicoClient from CLI args, env vars, or saved config."""
|
|
37
|
+
saved_config = load_config()
|
|
38
|
+
resolved_url = base_url or os.getenv("INDICO_BASE_URL") or saved_config.get("base_url", "")
|
|
39
|
+
resolved_token = token or os.getenv("INDICO_API_TOKEN") or saved_config.get("token", "")
|
|
40
|
+
|
|
41
|
+
if not resolved_url:
|
|
42
|
+
click.echo("Error: --base-url, INDICO_BASE_URL, or saved config required", err=True)
|
|
43
|
+
sys.exit(1)
|
|
44
|
+
|
|
45
|
+
api = IndicoAPI(resolved_url, bearer_token=resolved_token if resolved_token else None)
|
|
46
|
+
return IndicoClient(api)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def run_async(coro):
|
|
50
|
+
"""Run an async coroutine and return its result."""
|
|
51
|
+
return asyncio.run(coro)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def parse_speakers_json(speakers_string: Optional[str]):
|
|
55
|
+
"""Parse a JSON string of speakers into a list of dicts."""
|
|
56
|
+
if not speakers_string:
|
|
57
|
+
return None
|
|
58
|
+
return json.loads(speakers_string)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@click.group()
|
|
62
|
+
@click.option("--base-url", envvar="INDICO_BASE_URL", help="Indico instance URL")
|
|
63
|
+
@click.option("--token", envvar="INDICO_API_TOKEN", help="API bearer token")
|
|
64
|
+
@click.pass_context
|
|
65
|
+
def cli(ctx, base_url, token):
|
|
66
|
+
"""CLI for interacting with Indico event management systems."""
|
|
67
|
+
ctx.ensure_object(dict)
|
|
68
|
+
ctx.obj["base_url"] = base_url
|
|
69
|
+
ctx.obj["token"] = token
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@cli.command()
|
|
73
|
+
@click.option("--base-url", required=True, help="Indico instance URL")
|
|
74
|
+
@click.option("--token", default="", help="API bearer token")
|
|
75
|
+
def configure(base_url, token):
|
|
76
|
+
"""Configure connection to an Indico instance and test it."""
|
|
77
|
+
api = IndicoAPI(base_url, bearer_token=token if token else None)
|
|
78
|
+
client = IndicoClient(api)
|
|
79
|
+
try:
|
|
80
|
+
result = run_async(client.get_user_info())
|
|
81
|
+
click.echo("Connection successful!")
|
|
82
|
+
click.echo(result)
|
|
83
|
+
save_config(base_url, token)
|
|
84
|
+
click.echo(f"Configuration saved to {CONFIG_FILE}")
|
|
85
|
+
except Exception as error:
|
|
86
|
+
click.echo(f"Connection test failed: {error}", err=True)
|
|
87
|
+
sys.exit(1)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@cli.command("user-info")
|
|
91
|
+
@click.pass_context
|
|
92
|
+
def user_info(ctx):
|
|
93
|
+
"""Get current user information."""
|
|
94
|
+
client = get_client(ctx.obj["base_url"], ctx.obj["token"])
|
|
95
|
+
result = run_async(client.get_user_info())
|
|
96
|
+
click.echo(result)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@cli.command("search-events")
|
|
100
|
+
@click.option("--category-id", required=True, help="Category ID to search in")
|
|
101
|
+
@click.option("--from-date", default=None, help="Start date filter (e.g. 2024-01-01)")
|
|
102
|
+
@click.option("--to-date", default=None, help="End date filter (e.g. 2024-12-31)")
|
|
103
|
+
@click.option("--limit", type=int, default=None, help="Maximum number of results")
|
|
104
|
+
@click.option("--only-public", is_flag=True, default=False, help="Only show public events")
|
|
105
|
+
@click.pass_context
|
|
106
|
+
def search_events(ctx, category_id, from_date, to_date, limit, only_public):
|
|
107
|
+
"""Search for events in a category."""
|
|
108
|
+
client = get_client(ctx.obj["base_url"], ctx.obj["token"])
|
|
109
|
+
result = run_async(client.search_events(category_id, from_date, to_date, limit, only_public))
|
|
110
|
+
click.echo(result)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@cli.command("search-by-term")
|
|
114
|
+
@click.option("--term", required=True, help="Search term")
|
|
115
|
+
@click.option("--limit", type=int, default=50, help="Maximum number of results")
|
|
116
|
+
@click.pass_context
|
|
117
|
+
def search_by_term(ctx, term, limit):
|
|
118
|
+
"""Search for events by keyword/term."""
|
|
119
|
+
client = get_client(ctx.obj["base_url"], ctx.obj["token"])
|
|
120
|
+
result = run_async(client.search_events_by_term(term, limit))
|
|
121
|
+
click.echo(result)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@cli.command("search-categories")
|
|
125
|
+
@click.option("--category-id", default="0", help="Parent category ID (default: root)")
|
|
126
|
+
@click.option("--limit", type=int, default=None, help="Maximum number of results")
|
|
127
|
+
@click.pass_context
|
|
128
|
+
def search_categories(ctx, category_id, limit):
|
|
129
|
+
"""Search for event categories."""
|
|
130
|
+
client = get_client(ctx.obj["base_url"], ctx.obj["token"])
|
|
131
|
+
result = run_async(client.search_categories(category_id, limit))
|
|
132
|
+
click.echo(result)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@cli.command("event-details")
|
|
136
|
+
@click.option("--event-id", required=True, help="Event ID")
|
|
137
|
+
@click.option("--contributions", is_flag=True, default=False, help="Include contributions")
|
|
138
|
+
@click.option("--sessions", is_flag=True, default=False, help="Include sessions")
|
|
139
|
+
@click.pass_context
|
|
140
|
+
def event_details(ctx, event_id, contributions, sessions):
|
|
141
|
+
"""Get detailed information about a specific event."""
|
|
142
|
+
client = get_client(ctx.obj["base_url"], ctx.obj["token"])
|
|
143
|
+
result = run_async(client.get_event_details(event_id, contributions, sessions))
|
|
144
|
+
click.echo(result)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@cli.command()
|
|
148
|
+
@click.option("--event-id", required=True, help="Event ID")
|
|
149
|
+
@click.option("--subcontributions", is_flag=True, default=False, help="Include subcontributions")
|
|
150
|
+
@click.option("--limit", type=int, default=None, help="Maximum number of results")
|
|
151
|
+
@click.pass_context
|
|
152
|
+
def contributions(ctx, event_id, subcontributions, limit):
|
|
153
|
+
"""Get contributions for a specific event."""
|
|
154
|
+
client = get_client(ctx.obj["base_url"], ctx.obj["token"])
|
|
155
|
+
result = run_async(client.get_event_contributions(event_id, subcontributions, limit))
|
|
156
|
+
click.echo(result)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@cli.command()
|
|
160
|
+
@click.option("--event-id", required=True, help="Event ID")
|
|
161
|
+
@click.option("--download/--no-download", default=True, help="Download files (default: yes)")
|
|
162
|
+
@click.option("--download-dir", default=None, help="Directory to save downloaded files")
|
|
163
|
+
@click.pass_context
|
|
164
|
+
def files(ctx, event_id, download, download_dir):
|
|
165
|
+
"""Get and optionally download files for an event."""
|
|
166
|
+
client = get_client(ctx.obj["base_url"], ctx.obj["token"])
|
|
167
|
+
result = run_async(client.get_files(event_id, download_files=download, download_dir=download_dir))
|
|
168
|
+
click.echo(result)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@cli.command()
|
|
172
|
+
@click.option("--url", required=True, help="Download URL for the attachment")
|
|
173
|
+
@click.option("--filename", default=None, help="Save as this filename")
|
|
174
|
+
@click.pass_context
|
|
175
|
+
def download(ctx, url, filename):
|
|
176
|
+
"""Download a single file attachment."""
|
|
177
|
+
client = get_client(ctx.obj["base_url"], ctx.obj["token"])
|
|
178
|
+
result = run_async(client.download_single_attachment(url, filename))
|
|
179
|
+
click.echo(result)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@cli.command("create-event")
|
|
183
|
+
@click.option("--title", required=True, help="Event title")
|
|
184
|
+
@click.option("--category-id", required=True, help="Category ID")
|
|
185
|
+
@click.option("--type", "event_type", required=True, type=click.Choice(["lecture", "meeting", "conference"]), help="Event type")
|
|
186
|
+
@click.option("--start", default=None, help="Start date/time (ISO format)")
|
|
187
|
+
@click.option("--end", default=None, help="End date/time (ISO format)")
|
|
188
|
+
@click.option("--timezone", default=None, help="Timezone (e.g. Europe/Zurich)")
|
|
189
|
+
@click.option("--description", default=None, help="Event description")
|
|
190
|
+
@click.option("--location", default=None, help="Event location")
|
|
191
|
+
@click.option("--room", default=None, help="Room name")
|
|
192
|
+
@click.option("--speakers", default=None, help="Speakers as JSON string")
|
|
193
|
+
@click.pass_context
|
|
194
|
+
def create_event(ctx, title, category_id, event_type, start, end, timezone, description, location, room, speakers):
|
|
195
|
+
"""Create a new event."""
|
|
196
|
+
client = get_client(ctx.obj["base_url"], ctx.obj["token"])
|
|
197
|
+
parsed_speakers = parse_speakers_json(speakers)
|
|
198
|
+
result = run_async(client.create_event(
|
|
199
|
+
title=title,
|
|
200
|
+
category_id=category_id,
|
|
201
|
+
event_type=event_type,
|
|
202
|
+
start_date=start,
|
|
203
|
+
end_date=end,
|
|
204
|
+
timezone=timezone,
|
|
205
|
+
description=description,
|
|
206
|
+
location=location,
|
|
207
|
+
room=room,
|
|
208
|
+
speakers=parsed_speakers,
|
|
209
|
+
))
|
|
210
|
+
click.echo(result)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@cli.command("create-session")
|
|
214
|
+
@click.option("--event-id", required=True, help="Event ID")
|
|
215
|
+
@click.option("--title", required=True, help="Session title")
|
|
216
|
+
@click.option("--description", default=None, help="Session description")
|
|
217
|
+
@click.pass_context
|
|
218
|
+
def create_session(ctx, event_id, title, description):
|
|
219
|
+
"""Create a session in an event."""
|
|
220
|
+
client = get_client(ctx.obj["base_url"], ctx.obj["token"])
|
|
221
|
+
result = run_async(client.create_session(
|
|
222
|
+
event_id=event_id,
|
|
223
|
+
title=title,
|
|
224
|
+
description=description,
|
|
225
|
+
))
|
|
226
|
+
click.echo(result)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@cli.command("create-contribution")
|
|
230
|
+
@click.option("--event-id", required=True, help="Event ID")
|
|
231
|
+
@click.option("--title", required=True, help="Contribution title")
|
|
232
|
+
@click.option("--description", default=None, help="Contribution description")
|
|
233
|
+
@click.option("--duration", type=int, default=None, help="Duration in minutes")
|
|
234
|
+
@click.option("--speakers", default=None, help="Speakers as JSON string")
|
|
235
|
+
@click.option("--session-id", default=None, help="Session ID to add contribution to")
|
|
236
|
+
@click.pass_context
|
|
237
|
+
def create_contribution(ctx, event_id, title, description, duration, speakers, session_id):
|
|
238
|
+
"""Create a contribution in an event."""
|
|
239
|
+
client = get_client(ctx.obj["base_url"], ctx.obj["token"])
|
|
240
|
+
parsed_speakers = parse_speakers_json(speakers)
|
|
241
|
+
result = run_async(client.create_contribution(
|
|
242
|
+
event_id=event_id,
|
|
243
|
+
title=title,
|
|
244
|
+
description=description,
|
|
245
|
+
duration_minutes=duration,
|
|
246
|
+
speakers=parsed_speakers,
|
|
247
|
+
session_id=session_id,
|
|
248
|
+
))
|
|
249
|
+
click.echo(result)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
@cli.command("add-timetable-entry")
|
|
253
|
+
@click.option("--event-id", required=True, help="Event ID")
|
|
254
|
+
@click.option("--type", "entry_type", required=True, type=click.Choice(["session_block", "contribution"]), help="Entry type")
|
|
255
|
+
@click.option("--object-id", required=True, help="Object ID (session or contribution)")
|
|
256
|
+
@click.option("--start", required=True, help="Start date/time (ISO format)")
|
|
257
|
+
@click.option("--duration", type=int, default=None, help="Duration in minutes")
|
|
258
|
+
@click.pass_context
|
|
259
|
+
def add_timetable_entry(ctx, event_id, entry_type, object_id, start, duration):
|
|
260
|
+
"""Add an entry to the event timetable."""
|
|
261
|
+
client = get_client(ctx.obj["base_url"], ctx.obj["token"])
|
|
262
|
+
result = run_async(client.add_timetable_entry(
|
|
263
|
+
event_id=event_id,
|
|
264
|
+
entry_type=entry_type,
|
|
265
|
+
object_id=object_id,
|
|
266
|
+
start_date=start,
|
|
267
|
+
duration_minutes=duration,
|
|
268
|
+
))
|
|
269
|
+
click.echo(result)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
@cli.command("delete-timetable-entry")
|
|
273
|
+
@click.option("--event-id", required=True, help="Event ID")
|
|
274
|
+
@click.option("--entry-id", required=True, help="Timetable entry ID")
|
|
275
|
+
@click.pass_context
|
|
276
|
+
def delete_timetable_entry(ctx, event_id, entry_id):
|
|
277
|
+
"""Delete a timetable entry from an event."""
|
|
278
|
+
client = get_client(ctx.obj["base_url"], ctx.obj["token"])
|
|
279
|
+
result = run_async(client.delete_timetable_entry(
|
|
280
|
+
event_id=event_id,
|
|
281
|
+
entry_id=entry_id,
|
|
282
|
+
))
|
|
283
|
+
click.echo(result)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
@cli.command("create-event-with-agenda")
|
|
287
|
+
@click.option("--title", required=True, help="Event title")
|
|
288
|
+
@click.option("--category-id", required=True, help="Category ID")
|
|
289
|
+
@click.option("--type", "event_type", required=True, type=click.Choice(["lecture", "meeting", "conference"]), help="Event type")
|
|
290
|
+
@click.option("--start", required=True, help="Start date/time (ISO format)")
|
|
291
|
+
@click.option("--end", required=True, help="End date/time (ISO format)")
|
|
292
|
+
@click.option("--timezone", default=None, help="Timezone (e.g. Europe/Zurich)")
|
|
293
|
+
@click.option("--description", default=None, help="Event description")
|
|
294
|
+
@click.option("--location", default=None, help="Event location")
|
|
295
|
+
@click.option("--room", default=None, help="Room name")
|
|
296
|
+
@click.option("--speakers", default=None, help="Speakers as JSON string")
|
|
297
|
+
@click.option("--agenda-file", type=click.Path(exists=True), default=None, help="Path to JSON file with agenda array")
|
|
298
|
+
@click.pass_context
|
|
299
|
+
def create_event_with_agenda(ctx, title, category_id, event_type, start, end, timezone, description, location, room, speakers, agenda_file):
|
|
300
|
+
"""Create an event with a full agenda from a JSON file."""
|
|
301
|
+
client = get_client(ctx.obj["base_url"], ctx.obj["token"])
|
|
302
|
+
parsed_speakers = parse_speakers_json(speakers)
|
|
303
|
+
|
|
304
|
+
agenda_data = None
|
|
305
|
+
if agenda_file:
|
|
306
|
+
with open(agenda_file) as agenda_fh:
|
|
307
|
+
agenda_data = json.load(agenda_fh)
|
|
308
|
+
|
|
309
|
+
result = run_async(client.create_event_with_agenda(
|
|
310
|
+
title=title,
|
|
311
|
+
category_id=category_id,
|
|
312
|
+
event_type=event_type,
|
|
313
|
+
start_date=start,
|
|
314
|
+
end_date=end,
|
|
315
|
+
timezone=timezone,
|
|
316
|
+
description=description,
|
|
317
|
+
location=location,
|
|
318
|
+
room=room,
|
|
319
|
+
speakers=parsed_speakers,
|
|
320
|
+
agenda=agenda_data,
|
|
321
|
+
))
|
|
322
|
+
click.echo(result)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
if __name__ == "__main__":
|
|
326
|
+
cli()
|