dasmixer-cli 0.6.0__py3-none-any.whl

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.
@@ -0,0 +1,94 @@
1
+ """CLI commands for project management."""
2
+
3
+ import typer
4
+ from pathlib import Path
5
+ from typing import Annotated
6
+ import asyncio
7
+ from dasmixer.api.project.project import Project
8
+ from dasmixer.api.config import config
9
+
10
+ app = typer.Typer(help="Create new project")
11
+
12
+
13
+ @app.callback(invoke_without_command=True)
14
+ def create_project(
15
+ ctx: typer.Context,
16
+ project_path: Annotated[
17
+ str,
18
+ typer.Argument(help="Path to .dasmix project file to create"),
19
+ ],
20
+ ):
21
+ """
22
+ Create new empty project with default Control group.
23
+ """
24
+ path = Path(project_path)
25
+
26
+ # Check if file exists
27
+ if path.exists():
28
+ if not typer.confirm(f"File {path} exists. Overwrite?"):
29
+ typer.echo("Cancelled")
30
+ raise typer.Exit(0)
31
+ path.unlink()
32
+
33
+ # Create project
34
+ async def _create():
35
+ async with Project(path=path, create_if_not_exists=True) as project:
36
+ await project.add_subset(
37
+ "Control",
38
+ details="Default control group",
39
+ display_color="#3B82F6",
40
+ )
41
+ typer.echo(f"✓ Created project: {path}")
42
+ typer.echo("✓ Added default group: Control")
43
+
44
+ try:
45
+ asyncio.run(_create())
46
+ config.add_recent_project(str(path))
47
+ except Exception as e:
48
+ typer.echo(f"Error creating project: {e}", err=True)
49
+ raise typer.Exit(1)
50
+
51
+
52
+ @app.command()
53
+ def configure(
54
+ project_path: Annotated[str, typer.Argument(help="Path to .dasmix project file")],
55
+ key: Annotated[str, typer.Option("--key", "-k", help="Setting key")] = None,
56
+ value: Annotated[str, typer.Option("--value", "-v", help="Setting value")] = None,
57
+ list_settings: Annotated[bool, typer.Option("--list", "-l", help="List all settings")] = False,
58
+ ):
59
+ """
60
+ Configure project settings.
61
+ """
62
+ path = Path(project_path)
63
+ if not path.exists():
64
+ typer.echo(f"Error: Project file not found: {path}", err=True)
65
+ raise typer.Exit(1)
66
+
67
+ async def _run():
68
+ async with Project(path=path, create_if_not_exists=False) as project:
69
+ if list_settings:
70
+ rows = await project.execute_query(
71
+ "SELECT key, value FROM project_settings ORDER BY key"
72
+ )
73
+ if not rows:
74
+ typer.echo("No settings configured.")
75
+ return
76
+ typer.echo(f"{'Key':<35} {'Value'}")
77
+ typer.echo("-" * 70)
78
+ for row in rows:
79
+ typer.echo(f"{row['key']:<35} {row['value']}")
80
+ else:
81
+ if not key or value is None:
82
+ typer.echo("Error: --key and --value are required (or use --list)", err=True)
83
+ raise typer.Exit(1)
84
+ await project.set_setting(key, value)
85
+ await project.save()
86
+ typer.echo(f"✓ Setting saved: {key} = {value}")
87
+
88
+ try:
89
+ asyncio.run(_run())
90
+ except typer.Exit:
91
+ raise
92
+ except Exception as e:
93
+ typer.echo(f"Error: {e}", err=True)
94
+ raise typer.Exit(1)
@@ -0,0 +1,130 @@
1
+ """CLI commands for managing comparison groups (subsets)."""
2
+
3
+ import typer
4
+ from pathlib import Path
5
+ import asyncio
6
+ from dasmixer.api.project.project import Project
7
+ from typing import Annotated
8
+
9
+ app = typer.Typer(help="Manage comparison groups")
10
+
11
+
12
+ @app.command()
13
+ def add(
14
+ project_path: Annotated[str, typer.Argument(help="Path to .dasmix project file")],
15
+ name: Annotated[str, typer.Option("--name", "-n", help="Group name")] = ...,
16
+ details: Annotated[
17
+ str | None,
18
+ typer.Option("--details", "-d", help="Group description")
19
+ ] = None,
20
+ color: Annotated[
21
+ str | None,
22
+ typer.Option("--color", "-c", help="Display color (hex, e.g., #FF5733)")
23
+ ] = None
24
+ ):
25
+ """Add new comparison group to project."""
26
+ project_path = Path(project_path)
27
+
28
+ if not project_path.exists():
29
+ typer.echo(f"Error: Project file not found: {project_path}", err=True)
30
+ raise typer.Exit(1)
31
+
32
+ async def _add():
33
+ async with Project(path=project_path, create_if_not_exists=False) as project:
34
+ subset = await project.add_subset(name, details, color)
35
+ typer.echo(f"✓ Added group: {subset.name} (id={subset.id})")
36
+ if details:
37
+ typer.echo(f" Description: {details}")
38
+ if color:
39
+ typer.echo(f" Color: {color}")
40
+
41
+ try:
42
+ asyncio.run(_add())
43
+ except ValueError as e:
44
+ typer.echo(f"Error: {e}", err=True)
45
+ raise typer.Exit(1)
46
+ except Exception as e:
47
+ typer.echo(f"Error adding group: {e}", err=True)
48
+ raise typer.Exit(1)
49
+
50
+
51
+ @app.command()
52
+ def delete(
53
+ project_path: Annotated[str, typer.Argument(help="Path to .dasmix project file")],
54
+ name: Annotated[str, typer.Option("--name", "-n", help="Group name to delete")] = ...
55
+ ):
56
+ """Delete comparison group from project."""
57
+ project_path = Path(project_path)
58
+
59
+ if not project_path.exists():
60
+ typer.echo(f"Error: Project file not found: {project_path}", err=True)
61
+ raise typer.Exit(1)
62
+
63
+ async def _delete():
64
+ async with Project(path=project_path, create_if_not_exists=False) as project:
65
+ # Find subset by name
66
+ subsets = await project.get_subsets()
67
+ subset = next((s for s in subsets if s.name == name), None)
68
+
69
+ if not subset:
70
+ typer.echo(f"Error: Group '{name}' not found", err=True)
71
+ raise typer.Exit(1)
72
+
73
+ # Confirm deletion
74
+ if not typer.confirm(f"Delete group '{name}'?"):
75
+ typer.echo("Cancelled")
76
+ raise typer.Exit(0)
77
+
78
+ await project.delete_subset(subset.id)
79
+ typer.echo(f"✓ Deleted group: {name}")
80
+
81
+ try:
82
+ asyncio.run(_delete())
83
+ except ValueError as e:
84
+ typer.echo(f"Error: {e}", err=True)
85
+ raise typer.Exit(1)
86
+ except typer.Exit:
87
+ raise
88
+ except Exception as e:
89
+ typer.echo(f"Error deleting group: {e}", err=True)
90
+ raise typer.Exit(1)
91
+
92
+
93
+ @app.command("list")
94
+ def list_subsets(
95
+ project_path: Annotated[str, typer.Argument(help="Path to .dasmix project file")]
96
+ ):
97
+ """List all comparison groups in project."""
98
+ project_path = Path(project_path)
99
+
100
+ if not project_path.exists():
101
+ typer.echo(f"Error: Project file not found: {project_path}", err=True)
102
+ raise typer.Exit(1)
103
+
104
+ async def _list():
105
+ async with Project(path=project_path, create_if_not_exists=False) as project:
106
+ subsets = await project.get_subsets()
107
+
108
+ if not subsets:
109
+ typer.echo("No groups found in project")
110
+ return
111
+
112
+ typer.echo("\nComparison Groups:")
113
+ typer.echo("=" * 60)
114
+
115
+ for subset in subsets:
116
+ typer.echo(f"\n{subset.name} (ID: {subset.id})")
117
+ if subset.details:
118
+ typer.echo(f" Description: {subset.details}")
119
+ if subset.display_color:
120
+ typer.echo(f" Color: {subset.display_color}")
121
+
122
+ # Count samples in this group
123
+ samples = await project.get_samples(subset_id=subset.id)
124
+ typer.echo(f" Samples: {len(samples)}")
125
+
126
+ try:
127
+ asyncio.run(_list())
128
+ except Exception as e:
129
+ typer.echo(f"Error listing groups: {e}", err=True)
130
+ raise typer.Exit(1)
@@ -0,0 +1,147 @@
1
+ """CLI commands for managing project tools."""
2
+
3
+ import json
4
+ import typer
5
+ from pathlib import Path
6
+ from typing import Annotated
7
+ import asyncio
8
+ from dasmixer.api.project.project import Project
9
+
10
+ app = typer.Typer(help="Manage project tools")
11
+
12
+
13
+ @app.command()
14
+ def add(
15
+ project_path: Annotated[str, typer.Argument(help="Path to .dasmix project file")],
16
+ name: Annotated[str, typer.Option("--name", "-n", help="Tool name")] = ...,
17
+ parser: Annotated[str, typer.Option("--parser", "-p", help="Parser name (e.g., PowerNovo2, MGF)")] = ...,
18
+ type: Annotated[str, typer.Option("--type", "-t", help="Tool type: Library or De Novo")] = "Library",
19
+ color: Annotated[str, typer.Option("--color", help="Display color (hex, e.g. #4CAF50)")] = None,
20
+ ):
21
+ """
22
+ Add a new tool to the project.
23
+ """
24
+ if type not in ("Library", "De Novo"):
25
+ typer.echo("Error: --type must be 'Library' or 'De Novo'", err=True)
26
+ raise typer.Exit(1)
27
+
28
+ async def _run():
29
+ async with Project(path=Path(project_path), create_if_not_exists=False) as project:
30
+ tools = await project.get_tools()
31
+ if any(t.name == name for t in tools):
32
+ typer.echo(f"Error: Tool '{name}' already exists", err=True)
33
+ raise typer.Exit(1)
34
+ await project.add_tool(
35
+ name=name,
36
+ type=type,
37
+ parser=parser,
38
+ settings={},
39
+ display_color=color,
40
+ )
41
+ await project.save()
42
+ typer.echo(f"✓ Tool added: {name} (parser: {parser}, type: {type})")
43
+
44
+ try:
45
+ asyncio.run(_run())
46
+ except typer.Exit:
47
+ raise
48
+ except Exception as e:
49
+ typer.echo(f"Error: {e}", err=True)
50
+ raise typer.Exit(1)
51
+
52
+
53
+ @app.command()
54
+ def configure(
55
+ project_path: Annotated[str, typer.Argument(help="Path to .dasmix project file")],
56
+ name: Annotated[str, typer.Option("--name", "-n", help="Tool name")] = ...,
57
+ key: Annotated[str, typer.Option("--key", "-k", help="Setting key")] = ...,
58
+ value: Annotated[str, typer.Option("--value", "-v", help="Setting value")] = ...,
59
+ ):
60
+ """
61
+ Configure a tool setting.
62
+ """
63
+ async def _run():
64
+ async with Project(path=Path(project_path), create_if_not_exists=False) as project:
65
+ tools = await project.get_tools()
66
+ tool = next((t for t in tools if t.name == name), None)
67
+ if not tool:
68
+ typer.echo(f"Error: Tool '{name}' not found", err=True)
69
+ raise typer.Exit(1)
70
+ settings = dict(tool.settings or {})
71
+ settings[key] = value
72
+ await project._execute(
73
+ "UPDATE tool SET settings=? WHERE id=?",
74
+ [json.dumps(settings), tool.id],
75
+ )
76
+ await project.save()
77
+ typer.echo(f"✓ Tool '{name}' configured: {key} = {value}")
78
+
79
+ try:
80
+ asyncio.run(_run())
81
+ except typer.Exit:
82
+ raise
83
+ except Exception as e:
84
+ typer.echo(f"Error: {e}", err=True)
85
+ raise typer.Exit(1)
86
+
87
+
88
+ @app.command(name="param-list")
89
+ def param_list(
90
+ project_path: Annotated[str, typer.Argument(help="Path to .dasmix project file")],
91
+ name: Annotated[str, typer.Option("--name", "-n", help="Filter by tool name")] = None,
92
+ ):
93
+ """
94
+ List tool parameters/settings.
95
+ """
96
+ async def _run():
97
+ async with Project(path=Path(project_path), create_if_not_exists=False) as project:
98
+ tools = await project.get_tools()
99
+ if name:
100
+ tools = [t for t in tools if t.name == name]
101
+ if not tools:
102
+ typer.echo(f"Error: Tool '{name}' not found", err=True)
103
+ raise typer.Exit(1)
104
+ for tool in tools:
105
+ typer.echo(f"\nTool: {tool.name} ({tool.parser}, {tool.type})")
106
+ settings = tool.settings or {}
107
+ if settings:
108
+ for k, v in settings.items():
109
+ typer.echo(f" {k:<20} = {v}")
110
+ else:
111
+ typer.echo(" (no settings)")
112
+
113
+ try:
114
+ asyncio.run(_run())
115
+ except typer.Exit:
116
+ raise
117
+ except Exception as e:
118
+ typer.echo(f"Error: {e}", err=True)
119
+ raise typer.Exit(1)
120
+
121
+
122
+ @app.command()
123
+ def list(
124
+ project_path: Annotated[str, typer.Argument(help="Path to .dasmix project file")],
125
+ ):
126
+ """
127
+ List all tools in the project.
128
+ """
129
+ async def _run():
130
+ async with Project(path=Path(project_path), create_if_not_exists=False) as project:
131
+ tools = await project.get_tools()
132
+ if not tools:
133
+ typer.echo("No tools found.")
134
+ return
135
+ typer.echo(f"{'ID':<5} {'Name':<20} {'Type':<12} {'Parser':<20} {'Settings'}")
136
+ typer.echo("-" * 70)
137
+ for t in tools:
138
+ settings_count = len(t.settings or {})
139
+ typer.echo(f"{t.id:<5} {t.name:<20} {t.type:<12} {t.parser:<20} {settings_count} keys")
140
+
141
+ try:
142
+ asyncio.run(_run())
143
+ except typer.Exit:
144
+ raise
145
+ except Exception as e:
146
+ typer.echo(f"Error: {e}", err=True)
147
+ raise typer.Exit(1)
dasmixer/cli/main.py ADDED
@@ -0,0 +1,52 @@
1
+ """
2
+ DASMixer CLI — entry point for the `dasmixer-cli` command.
3
+
4
+ Provides command-line tools for project management without GUI.
5
+ """
6
+
7
+ import typer
8
+ from typing import Annotated
9
+ from pathlib import Path
10
+
11
+ from dasmixer.versions import APP_VERSION
12
+ from dasmixer.cli.commands import project, subset, import_data
13
+ from dasmixer.cli.commands import portable
14
+ from dasmixer.cli.commands import import_project as import_project_cmd
15
+ from dasmixer.cli.commands import tool as tool_cmd
16
+ from dasmixer.cli.commands import calculate as calculate_cmd
17
+
18
+ app = typer.Typer(
19
+ name="dasmixer-cli",
20
+ help="DASMixer CLI — manage projects without GUI",
21
+ add_completion=False,
22
+ )
23
+
24
+
25
+ @app.callback(invoke_without_command=True)
26
+ def main(
27
+ ctx: typer.Context,
28
+ version: Annotated[
29
+ bool,
30
+ typer.Option("--version", "-v", help="Show version and exit"),
31
+ ] = False,
32
+ ):
33
+ """DASMixer CLI — управление проектами без GUI."""
34
+ if version:
35
+ typer.echo(f"DASMixer CLI {APP_VERSION}")
36
+ raise typer.Exit(0)
37
+ if ctx.invoked_subcommand is None:
38
+ typer.echo(ctx.get_help())
39
+
40
+
41
+ app.add_typer(project.app, name="create", help="Create new project")
42
+ app.add_typer(project.app, name="project", help="Manage project settings (configure)")
43
+ app.add_typer(subset.app, name="subset", help="Manage comparison groups")
44
+ app.add_typer(import_data.app, name="import", help="Import data files")
45
+ app.add_typer(portable.app, name="checkpoint", help="Save WAL to database file")
46
+ app.add_typer(portable.app, name="vacuum", help="Compact database file")
47
+ app.add_typer(import_project_cmd.app, name="import-project", help="Merge another project into this one")
48
+ app.add_typer(tool_cmd.app, name="tool", help="Manage project tools")
49
+ app.add_typer(calculate_cmd.app, name="calculate", help="Run pipeline calculations")
50
+
51
+ if __name__ == "__main__":
52
+ app()
@@ -0,0 +1,51 @@
1
+ Metadata-Version: 2.4
2
+ Name: dasmixer-cli
3
+ Version: 0.6.0
4
+ Summary: DASMixer CLI — command-line tools for project management
5
+ Author: gluck
6
+ Author-email: glucksistemi@gmail.com
7
+ Requires-Python: <4.0, >=3.11
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Requires-Dist: dasmixer-core (==0.6.0)
14
+ Project-URL: Homepage, https://github.com/protdb/dasmixer
15
+ Description-Content-Type: text/markdown
16
+
17
+ # DASMixer CLI
18
+
19
+ Command-line tools for managing DASMixer projects without a graphical interface.
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ pip install dasmixer-cli
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ```bash
30
+ dasmixer-cli --help
31
+ dasmixer-cli create path/to/project.dasmix
32
+ dasmixer-cli subset list path/to/project.dasmix
33
+ dasmixer-cli subset add path/to/project.dasmix --name "Treatment" --color "#FF5733"
34
+ dasmixer-cli import mgf-file path/to/project.dasmix --sample "Sample1" --file spectra.mgf
35
+ dasmixer-cli import mgf-pattern path/to/project.dasmix --sample "Sample1" --folder ./spectra/ --pattern "*.mgf"
36
+ ```
37
+
38
+ ## Commands
39
+
40
+ | Command | Description |
41
+ |---|---|
42
+ | `create <project>` | Create a new empty project |
43
+ | `subset list <project>` | List comparison groups |
44
+ | `subset add <project> --name` | Add a comparison group |
45
+ | `subset delete <project> --name` | Delete a comparison group |
46
+ | `import mgf-file <project>` | Import a single MGF file |
47
+ | `import mgf-pattern <project>` | Batch import MGF files by pattern |
48
+ | `import ident-file <project>` | Import an identification file |
49
+ | `import ident-pattern <project>` | Batch import identification files |
50
+
51
+ Documentation: https://github.com/protdb/dasmixer
@@ -0,0 +1,14 @@
1
+ dasmixer/cli/__init__.py,sha256=LAxO9P8FGo1i5-bimxDHP3P9Wskh_Hs1Jf0Oq1Ne2ek,82
2
+ dasmixer/cli/commands/__init__.py,sha256=VGM6t1ow3-15tV5QtnEJov7qjluWtLBRP4tDCvcon_8,78
3
+ dasmixer/cli/commands/calculate.py,sha256=j6vsoo6mA9ngIvQoCc5zl6VKaYbFAz0yLm4mjvXkJz8,23325
4
+ dasmixer/cli/commands/import_data.py,sha256=c67xm_AivuWmHNhPTqPVTwdhs4eKXo3355XHdBNBU2s,19244
5
+ dasmixer/cli/commands/import_project.py,sha256=kDud5tJI6lMP0Xdk5LyfPSCKONWTDhdhWcEASOjYaJ4,2999
6
+ dasmixer/cli/commands/portable.py,sha256=5jcmREk7vHTocV-zgK0ijsIX6nWHWsQhrPj4dRmxklM,1719
7
+ dasmixer/cli/commands/project.py,sha256=4jmZOAFzoZ9MtQt2-wDbkQ5LGh2T2_DtQwwqeyAZ3Cs,3188
8
+ dasmixer/cli/commands/subset.py,sha256=M7uuaUG--HJVQ6fgyQgHCrIdxBAxDYBNO_-X6sDSxrA,4593
9
+ dasmixer/cli/commands/tool.py,sha256=cAO1c7hFpj-bMBS9mYFA3l1CdvResE8Qt6mcQWGXIUY,5324
10
+ dasmixer/cli/main.py,sha256=N8xmhmLpMlmwyiZQ5jriv_ICW7UfpeJDFCtr2tOfuO8,1923
11
+ dasmixer_cli-0.6.0.dist-info/entry_points.txt,sha256=a4KExsoM5TAa1-M7059iMIH3u9VerPOAyCF4mQz-so0,54
12
+ dasmixer_cli-0.6.0.dist-info/METADATA,sha256=gHDykCWY7rhQFGVKzU6IphkSWr3vuSayhCrMcohG0vA,1766
13
+ dasmixer_cli-0.6.0.dist-info/WHEEL,sha256=Vz2fHgx6HFtSwhs8KvkHLqH5Ea4w1_rner5uNVGCeIE,88
14
+ dasmixer_cli-0.6.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.3.2
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ dasmixer-cli=dasmixer.cli.main:app
3
+