pylabrat 0.1__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.
- pylabrat-0.1/LICENSE +21 -0
- pylabrat-0.1/MANIFEST.in +3 -0
- pylabrat-0.1/PKG-INFO +178 -0
- pylabrat-0.1/README.md +142 -0
- pylabrat-0.1/labrat/__init__.py +3 -0
- pylabrat-0.1/labrat/cli.py +187 -0
- pylabrat-0.1/labrat/filemanager/__init__.py +2 -0
- pylabrat-0.1/labrat/filemanager/archive.py +94 -0
- pylabrat-0.1/labrat/filemanager/organize.py +235 -0
- pylabrat-0.1/labrat/genetics/__init__.py +0 -0
- pylabrat-0.1/labrat/genetics/codons.json +68 -0
- pylabrat-0.1/labrat/genetics/dna_analysis.py +65 -0
- pylabrat-0.1/labrat/genetics/protein_analysis.py +92 -0
- pylabrat-0.1/labrat/graphpad/__init__.py +0 -0
- pylabrat-0.1/labrat/graphpad/scripts.py +0 -0
- pylabrat-0.1/labrat/guis/__init__.py +0 -0
- pylabrat-0.1/labrat/math/__init__.py +2 -0
- pylabrat-0.1/labrat/math/functions.py +127 -0
- pylabrat-0.1/labrat/notebook/__init__.py +0 -0
- pylabrat-0.1/labrat/project/__init__.py +1 -0
- pylabrat-0.1/labrat/project/projectmanager.py +445 -0
- pylabrat-0.1/labrat/utils.py +31 -0
- pylabrat-0.1/labrat-env/bin/jp.py +54 -0
- pylabrat-0.1/pylabrat.egg-info/PKG-INFO +178 -0
- pylabrat-0.1/pylabrat.egg-info/SOURCES.txt +38 -0
- pylabrat-0.1/pylabrat.egg-info/dependency_links.txt +1 -0
- pylabrat-0.1/pylabrat.egg-info/entry_points.txt +2 -0
- pylabrat-0.1/pylabrat.egg-info/not-zip-safe +1 -0
- pylabrat-0.1/pylabrat.egg-info/requires.txt +12 -0
- pylabrat-0.1/pylabrat.egg-info/top_level.txt +6 -0
- pylabrat-0.1/pyproject.toml +61 -0
- pylabrat-0.1/setup.cfg +16 -0
- pylabrat-0.1/setup.py +11 -0
- pylabrat-0.1/tests/test_archiver.py +43 -0
- pylabrat-0.1/tests/test_cli.py +313 -0
- pylabrat-0.1/tests/test_file_organizer.py +72 -0
- pylabrat-0.1/tests/test_math_functions.py +189 -0
- pylabrat-0.1/tests/test_project_manager.py +87 -0
- pylabrat-0.1/tests/test_protein_analysis.py +187 -0
pylabrat-0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Shaurita D. Hutchins
|
|
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.
|
pylabrat-0.1/MANIFEST.in
ADDED
pylabrat-0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pylabrat
|
|
3
|
+
Version: 0.1
|
|
4
|
+
Summary: A package of helpful guis and functions to improve reproducibility for genetics/psychiatry related labs.
|
|
5
|
+
Author-email: Shaurita Hutchins <shaurita.d.hutchins@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/sdhutchins/labrat
|
|
8
|
+
Project-URL: Documentation, https://labrat.readthedocs.io/en/latest/
|
|
9
|
+
Project-URL: Releases, https://github.com/sdhutchins/labrat/releases
|
|
10
|
+
Project-URL: Issues, https://github.com/sdhutchins/labrat/issues
|
|
11
|
+
Keywords: science,lab,genetics,math,filemanagement
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
+
Classifier: Operating System :: Unix
|
|
16
|
+
Classifier: Natural Language :: English
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Requires-Python: >=3.7
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: cookiecutter>=1.5.1
|
|
25
|
+
Requires-Dist: logzero>=1.3.1
|
|
26
|
+
Requires-Dist: exmemo>=0.1.0
|
|
27
|
+
Requires-Dist: click>=6.7
|
|
28
|
+
Requires-Dist: jinja2-time>=0.2.0
|
|
29
|
+
Provides-Extra: docs
|
|
30
|
+
Requires-Dist: mkdocs>=1.5.0; extra == "docs"
|
|
31
|
+
Requires-Dist: mkdocs-material>=9.0.0; extra == "docs"
|
|
32
|
+
Requires-Dist: mkdocs-click>=0.8.0; extra == "docs"
|
|
33
|
+
Requires-Dist: mkdocstrings[python]>=0.23.0; extra == "docs"
|
|
34
|
+
Requires-Dist: pymdown-extensions>=10.0.0; extra == "docs"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# labrat
|
|
38
|
+
|
|
39
|
+
[](https://app.travis-ci.com/sdhutchins/labrat)
|
|
40
|
+
[](https://codecov.io/gh/sdhutchins/labrat)
|
|
41
|
+
[](https://doi.org/10.5281/zenodo.17705600)
|
|
42
|
+
|
|
43
|
+
A basic science lab framework aimed at reproducibility and lab management. This package is in the very early stages of development.
|
|
44
|
+
|
|
45
|
+
## Features
|
|
46
|
+
|
|
47
|
+
- Create, list, and track/manage computational biology projects with structured templates
|
|
48
|
+
- Calculate solution dilutions, molarity, transmittance/absorbance conversions, and more
|
|
49
|
+
- Automatically organize scientific data files (FASTQ, FASTA, SAM, BAM, VCF, etc.) and others files like pictures, videos, and archives
|
|
50
|
+
- Archive projects and directories with timestamped backups
|
|
51
|
+
- Convert DNA sequences to amino acids and analyze genetic data
|
|
52
|
+
- Full-featured CLI for all major operations
|
|
53
|
+
|
|
54
|
+
## Install
|
|
55
|
+
|
|
56
|
+
Install from PyPI:
|
|
57
|
+
```bash
|
|
58
|
+
pip install pylabrat
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Or install from source:
|
|
62
|
+
```bash
|
|
63
|
+
git clone https://github.com/sdhutchins/labrat.git
|
|
64
|
+
cd labrat
|
|
65
|
+
pip install .
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
For development, install in editable mode:
|
|
69
|
+
```bash
|
|
70
|
+
pip install -e .
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Examples
|
|
74
|
+
|
|
75
|
+
### Command-Line Interface
|
|
76
|
+
|
|
77
|
+
Create a new project:
|
|
78
|
+
```bash
|
|
79
|
+
labrat project new --type computational-biology --name "KARG Analysis" \
|
|
80
|
+
--path ./karg_analysis --description "Analyze the KARG data"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
List all projects:
|
|
84
|
+
```bash
|
|
85
|
+
labrat project list
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Archive files or directories:
|
|
89
|
+
```bash
|
|
90
|
+
labrat archive --source ./my_project --destination ~/Archive --name "project_backup"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Organize scientific data files:
|
|
94
|
+
```bash
|
|
95
|
+
labrat organize --science
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Python API
|
|
99
|
+
|
|
100
|
+
Calculate solution dilutions:
|
|
101
|
+
```python
|
|
102
|
+
from labrat.math import dilute_stock
|
|
103
|
+
|
|
104
|
+
# Calculate final concentration
|
|
105
|
+
final_conc = dilute_stock(100, 2, vF=4) # Returns 50.0
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Manage projects programmatically:
|
|
109
|
+
```python
|
|
110
|
+
from labrat.project import ProjectManager
|
|
111
|
+
|
|
112
|
+
# Create a new project
|
|
113
|
+
manager = ProjectManager('Dr. Jane Doe')
|
|
114
|
+
manager.new_project(
|
|
115
|
+
project_type='computational-biology',
|
|
116
|
+
project_name='KARG Analysis',
|
|
117
|
+
project_path='./karg_analysis',
|
|
118
|
+
description="Analyze the KARG data."
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# List all projects
|
|
122
|
+
projects = manager.list_projects()
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Tests
|
|
126
|
+
|
|
127
|
+
Before running tests, ensure all dependencies are installed:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
pip install -r requirements.txt
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Or if installing the package:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
pip install .
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Run all tests using unittest:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
python -m unittest discover -s tests
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Or run tests with pytest (if installed):
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
pytest tests/
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
To run a specific test file:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
python -m unittest tests.test_archiver
|
|
155
|
+
python -m unittest tests.test_file_organizer
|
|
156
|
+
python -m unittest tests.test_project_manager
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## ToDo
|
|
160
|
+
|
|
161
|
+
- [ ] Add a lab inventory app
|
|
162
|
+
- [ ] Add project report template
|
|
163
|
+
- [ ] Integrate [exmemo](https://github.com/kalekundert/exmemo)
|
|
164
|
+
|
|
165
|
+
## Author
|
|
166
|
+
|
|
167
|
+
Shaurita Hutchins · [@sdhutchins](https://github.com/sdhutchins)
|
|
168
|
+
· [:email:](mailto:shaurita.d.hutchins@gmail.com)
|
|
169
|
+
|
|
170
|
+
## Contributing
|
|
171
|
+
|
|
172
|
+
If you would like to contribute to this package, install the package in
|
|
173
|
+
development mode, and check out our [contributing
|
|
174
|
+
guidelines](https://github.com/sdhutchins/labrat/blob/master/CONTRIBUTING.md).
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
[MIT](https://github.com/sdhutchins/labrat/blob/master/LICENSE)
|
pylabrat-0.1/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# labrat
|
|
2
|
+
|
|
3
|
+
[](https://app.travis-ci.com/sdhutchins/labrat)
|
|
4
|
+
[](https://codecov.io/gh/sdhutchins/labrat)
|
|
5
|
+
[](https://doi.org/10.5281/zenodo.17705600)
|
|
6
|
+
|
|
7
|
+
A basic science lab framework aimed at reproducibility and lab management. This package is in the very early stages of development.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- Create, list, and track/manage computational biology projects with structured templates
|
|
12
|
+
- Calculate solution dilutions, molarity, transmittance/absorbance conversions, and more
|
|
13
|
+
- Automatically organize scientific data files (FASTQ, FASTA, SAM, BAM, VCF, etc.) and others files like pictures, videos, and archives
|
|
14
|
+
- Archive projects and directories with timestamped backups
|
|
15
|
+
- Convert DNA sequences to amino acids and analyze genetic data
|
|
16
|
+
- Full-featured CLI for all major operations
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
Install from PyPI:
|
|
21
|
+
```bash
|
|
22
|
+
pip install pylabrat
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or install from source:
|
|
26
|
+
```bash
|
|
27
|
+
git clone https://github.com/sdhutchins/labrat.git
|
|
28
|
+
cd labrat
|
|
29
|
+
pip install .
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
For development, install in editable mode:
|
|
33
|
+
```bash
|
|
34
|
+
pip install -e .
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Examples
|
|
38
|
+
|
|
39
|
+
### Command-Line Interface
|
|
40
|
+
|
|
41
|
+
Create a new project:
|
|
42
|
+
```bash
|
|
43
|
+
labrat project new --type computational-biology --name "KARG Analysis" \
|
|
44
|
+
--path ./karg_analysis --description "Analyze the KARG data"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
List all projects:
|
|
48
|
+
```bash
|
|
49
|
+
labrat project list
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Archive files or directories:
|
|
53
|
+
```bash
|
|
54
|
+
labrat archive --source ./my_project --destination ~/Archive --name "project_backup"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Organize scientific data files:
|
|
58
|
+
```bash
|
|
59
|
+
labrat organize --science
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Python API
|
|
63
|
+
|
|
64
|
+
Calculate solution dilutions:
|
|
65
|
+
```python
|
|
66
|
+
from labrat.math import dilute_stock
|
|
67
|
+
|
|
68
|
+
# Calculate final concentration
|
|
69
|
+
final_conc = dilute_stock(100, 2, vF=4) # Returns 50.0
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Manage projects programmatically:
|
|
73
|
+
```python
|
|
74
|
+
from labrat.project import ProjectManager
|
|
75
|
+
|
|
76
|
+
# Create a new project
|
|
77
|
+
manager = ProjectManager('Dr. Jane Doe')
|
|
78
|
+
manager.new_project(
|
|
79
|
+
project_type='computational-biology',
|
|
80
|
+
project_name='KARG Analysis',
|
|
81
|
+
project_path='./karg_analysis',
|
|
82
|
+
description="Analyze the KARG data."
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# List all projects
|
|
86
|
+
projects = manager.list_projects()
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Tests
|
|
90
|
+
|
|
91
|
+
Before running tests, ensure all dependencies are installed:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
pip install -r requirements.txt
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Or if installing the package:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
pip install .
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Run all tests using unittest:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
python -m unittest discover -s tests
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Or run tests with pytest (if installed):
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
pytest tests/
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
To run a specific test file:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
python -m unittest tests.test_archiver
|
|
119
|
+
python -m unittest tests.test_file_organizer
|
|
120
|
+
python -m unittest tests.test_project_manager
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## ToDo
|
|
124
|
+
|
|
125
|
+
- [ ] Add a lab inventory app
|
|
126
|
+
- [ ] Add project report template
|
|
127
|
+
- [ ] Integrate [exmemo](https://github.com/kalekundert/exmemo)
|
|
128
|
+
|
|
129
|
+
## Author
|
|
130
|
+
|
|
131
|
+
Shaurita Hutchins · [@sdhutchins](https://github.com/sdhutchins)
|
|
132
|
+
· [:email:](mailto:shaurita.d.hutchins@gmail.com)
|
|
133
|
+
|
|
134
|
+
## Contributing
|
|
135
|
+
|
|
136
|
+
If you would like to contribute to this package, install the package in
|
|
137
|
+
development mode, and check out our [contributing
|
|
138
|
+
guidelines](https://github.com/sdhutchins/labrat/blob/master/CONTRIBUTING.md).
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
[MIT](https://github.com/sdhutchins/labrat/blob/master/LICENSE)
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Command-line interface for labrat."""
|
|
3
|
+
import click
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from labrat.project import ProjectManager
|
|
6
|
+
from labrat.filemanager import Archiver, FileOrganizer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.group()
|
|
10
|
+
def main():
|
|
11
|
+
"""Labrat - A basic science lab framework for reproducibility and lab management."""
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@main.group()
|
|
16
|
+
def project():
|
|
17
|
+
"""Manage projects."""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@project.command('new')
|
|
22
|
+
@click.option('--type', 'project_type', required=True,
|
|
23
|
+
help='Type of project (e.g., computational-biology, data-science)')
|
|
24
|
+
@click.option('--name', 'project_name', required=True,
|
|
25
|
+
help='Name of the project')
|
|
26
|
+
@click.option('--path', 'project_path', required=True, type=click.Path(),
|
|
27
|
+
help='Path where the project will be created')
|
|
28
|
+
@click.option('--description', required=True,
|
|
29
|
+
help='Description of the project')
|
|
30
|
+
@click.option('--username', default=None,
|
|
31
|
+
help='Username for project manager (defaults to system default)')
|
|
32
|
+
def new_project(project_type, project_name, project_path, description, username):
|
|
33
|
+
"""Create a new project."""
|
|
34
|
+
try:
|
|
35
|
+
manager = ProjectManager(username=username)
|
|
36
|
+
manager.new_project(
|
|
37
|
+
project_type=project_type,
|
|
38
|
+
project_name=project_name,
|
|
39
|
+
project_path=project_path,
|
|
40
|
+
description=description
|
|
41
|
+
)
|
|
42
|
+
click.echo(f"✓ Project '{project_name}' created successfully at {project_path}")
|
|
43
|
+
except Exception as e:
|
|
44
|
+
click.echo(f"✗ Error creating project: {e}", err=True)
|
|
45
|
+
raise click.Abort()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@project.command('list')
|
|
49
|
+
@click.option('--username', default=None,
|
|
50
|
+
help='Username for project manager (defaults to system default)')
|
|
51
|
+
def list_projects(username):
|
|
52
|
+
"""List all projects."""
|
|
53
|
+
try:
|
|
54
|
+
manager = ProjectManager(username=username)
|
|
55
|
+
projects = manager.list_projects()
|
|
56
|
+
|
|
57
|
+
if not projects:
|
|
58
|
+
click.echo("No projects found.")
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
click.echo(f"\nFound {len(projects)} project(s):\n")
|
|
62
|
+
for idx, proj in enumerate(projects, 1):
|
|
63
|
+
click.echo(f"{idx}. {proj.get('name', 'Unknown')}")
|
|
64
|
+
click.echo(f" Path: {proj.get('path', 'Unknown')}")
|
|
65
|
+
click.echo(f" Type: {proj.get('project_type', 'Unknown')}")
|
|
66
|
+
click.echo(f" Created: {proj.get('created_at', 'Unknown')}")
|
|
67
|
+
click.echo()
|
|
68
|
+
except Exception as e:
|
|
69
|
+
click.echo(f"✗ Error listing projects: {e}", err=True)
|
|
70
|
+
raise click.Abort()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@project.command('delete')
|
|
74
|
+
@click.option('--path', 'project_path', required=True, type=click.Path(exists=True),
|
|
75
|
+
help='Path to the project to delete')
|
|
76
|
+
@click.option('--archive-dir', required=True, type=click.Path(),
|
|
77
|
+
help='Directory where the archived project will be stored')
|
|
78
|
+
@click.option('--username', default=None,
|
|
79
|
+
help='Username for project manager (defaults to system default)')
|
|
80
|
+
@click.confirmation_option(prompt='Are you sure you want to delete this project?')
|
|
81
|
+
def delete_project(project_path, archive_dir, username):
|
|
82
|
+
"""Delete a project (archives it first)."""
|
|
83
|
+
try:
|
|
84
|
+
manager = ProjectManager(username=username)
|
|
85
|
+
archive_path = manager.delete_project(project_path, archive_dir)
|
|
86
|
+
click.echo(f"✓ Project deleted and archived to: {archive_path}")
|
|
87
|
+
except Exception as e:
|
|
88
|
+
click.echo(f"✗ Error deleting project: {e}", err=True)
|
|
89
|
+
raise click.Abort()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@main.command('archive')
|
|
93
|
+
@click.option('--source', required=True, type=click.Path(exists=True, dir_okay=True),
|
|
94
|
+
help='Source directory to archive')
|
|
95
|
+
@click.option('--destination', required=True, type=click.Path(),
|
|
96
|
+
help='Base directory for storing archives')
|
|
97
|
+
@click.option('--name', 'project_name', required=True,
|
|
98
|
+
help='Name for the archive')
|
|
99
|
+
def archive(source, destination, project_name):
|
|
100
|
+
"""Archive a directory."""
|
|
101
|
+
try:
|
|
102
|
+
archive_dir = Archiver.get_archive_dir(destination, project_name)
|
|
103
|
+
archiver = Archiver(source_dir=source, archive_dir=archive_dir)
|
|
104
|
+
zip_path = archiver.archive()
|
|
105
|
+
click.echo(f"✓ Archive created successfully: {zip_path}")
|
|
106
|
+
except Exception as e:
|
|
107
|
+
click.echo(f"✗ Error creating archive: {e}", err=True)
|
|
108
|
+
raise click.Abort()
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@main.command('organize')
|
|
112
|
+
@click.option('--science', 'organize_science', is_flag=True,
|
|
113
|
+
help='Organize scientific data files (fastq, fasta, sam, bam, vcf, fits, hdf5, etc.) to Documents/Research_Data')
|
|
114
|
+
@click.option('--science-dir', type=click.Path(),
|
|
115
|
+
help='Custom directory for scientific data files (default: Documents/Research_Data)')
|
|
116
|
+
@click.option('--keyword', default=None,
|
|
117
|
+
help='Move files containing this keyword to a specific folder')
|
|
118
|
+
@click.option('--pictures', 'organize_pictures', is_flag=True,
|
|
119
|
+
help='Organize picture files to Pictures folder')
|
|
120
|
+
@click.option('--videos', 'organize_videos', is_flag=True,
|
|
121
|
+
help='Organize video files to Videos folder')
|
|
122
|
+
@click.option('--archives', 'organize_archives', is_flag=True,
|
|
123
|
+
help='Organize archive files by compression type')
|
|
124
|
+
@click.option('--all', 'organize_all', is_flag=True,
|
|
125
|
+
help='Organize all file types')
|
|
126
|
+
def organize(organize_science, science_dir, keyword, organize_pictures,
|
|
127
|
+
organize_videos, organize_archives, organize_all):
|
|
128
|
+
"""
|
|
129
|
+
Organize files in Downloads and Documents directories.
|
|
130
|
+
|
|
131
|
+
By default, scientific data files (fastq, fasta, sam, bam, vcf, fits, hdf5, nc, etc.)
|
|
132
|
+
are moved to Documents/Research_Data. Use --science-dir to specify a custom location.
|
|
133
|
+
|
|
134
|
+
Examples:
|
|
135
|
+
labrat organize --science
|
|
136
|
+
labrat organize --science --science-dir ~/Research
|
|
137
|
+
labrat organize --keyword "project_alpha"
|
|
138
|
+
labrat organize --all
|
|
139
|
+
"""
|
|
140
|
+
if not any([organize_science, keyword, organize_pictures, organize_videos,
|
|
141
|
+
organize_archives, organize_all]):
|
|
142
|
+
click.echo("Error: Specify at least one organization option", err=True)
|
|
143
|
+
click.echo("Use --science to organize science files, or --all for everything", err=True)
|
|
144
|
+
raise click.Abort()
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
organizer = FileOrganizer()
|
|
148
|
+
actions_taken = []
|
|
149
|
+
|
|
150
|
+
if organize_all:
|
|
151
|
+
organizer.organize_all()
|
|
152
|
+
actions_taken.append("all files")
|
|
153
|
+
else:
|
|
154
|
+
# Organize science files (default behavior for scientists)
|
|
155
|
+
if organize_science:
|
|
156
|
+
organizer.organize_science_files(science_dir=science_dir)
|
|
157
|
+
location = science_dir if science_dir else "Documents/Research_Data"
|
|
158
|
+
actions_taken.append(f"science files to {location}")
|
|
159
|
+
|
|
160
|
+
# Organize media files
|
|
161
|
+
if organize_pictures or organize_videos:
|
|
162
|
+
organizer.organize_files()
|
|
163
|
+
media = []
|
|
164
|
+
if organize_pictures:
|
|
165
|
+
media.append("pictures")
|
|
166
|
+
if organize_videos:
|
|
167
|
+
media.append("videos")
|
|
168
|
+
actions_taken.append(f"{' and '.join(media)}")
|
|
169
|
+
|
|
170
|
+
# Organize archives
|
|
171
|
+
if organize_archives:
|
|
172
|
+
organizer.organize_archives()
|
|
173
|
+
actions_taken.append("archives")
|
|
174
|
+
|
|
175
|
+
# Handle keyword-based organization
|
|
176
|
+
if keyword:
|
|
177
|
+
organizer.move_specific_files(keyword=keyword)
|
|
178
|
+
actions_taken.append(f"files with keyword '{keyword}'")
|
|
179
|
+
|
|
180
|
+
click.echo(f"✓ Organized {', '.join(actions_taken)} successfully")
|
|
181
|
+
except Exception as e:
|
|
182
|
+
click.echo(f"✗ Error organizing files: {e}", err=True)
|
|
183
|
+
raise click.Abort()
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
if __name__ == "__main__":
|
|
187
|
+
main()
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from shutil import copytree, SameFileError, make_archive
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import logzero
|
|
5
|
+
from logzero import logger
|
|
6
|
+
from labrat.utils import get_labrat_dir
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Archiver:
|
|
10
|
+
"""Archive folders and files."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, source_dir, archive_dir):
|
|
13
|
+
"""
|
|
14
|
+
Initialize logger and archive parameters.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
source_dir (str or Path): The directory to copy or archive.
|
|
18
|
+
archive_dir (str or Path): The destination directory for the archive.
|
|
19
|
+
"""
|
|
20
|
+
self.source_dir = Path(source_dir).resolve()
|
|
21
|
+
self.archive_dir = Path(archive_dir).resolve()
|
|
22
|
+
|
|
23
|
+
if not self.source_dir.exists() or not self.source_dir.is_dir():
|
|
24
|
+
raise ValueError(f"Source directory '{self.source_dir}' does not exist or is not a directory.")
|
|
25
|
+
logger.debug(f"Source directory: {self.source_dir}")
|
|
26
|
+
|
|
27
|
+
if not self.archive_dir.parent.exists():
|
|
28
|
+
raise ValueError(f"Archive directory parent '{self.archive_dir.parent}' does not exist.")
|
|
29
|
+
logger.debug(f"Archive directory: {self.archive_dir}")
|
|
30
|
+
|
|
31
|
+
# Configure log file in .labrat directory
|
|
32
|
+
# Store logs in user's home directory under .labrat folder
|
|
33
|
+
labrat_dir = get_labrat_dir()
|
|
34
|
+
log_file = labrat_dir / f"archive_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
|
|
35
|
+
logzero.logfile(str(log_file))
|
|
36
|
+
logger.info("Archive initialized.")
|
|
37
|
+
|
|
38
|
+
def archive(self):
|
|
39
|
+
"""
|
|
40
|
+
Perform the archive by copying the source directory to the archive directory
|
|
41
|
+
and then creating a zip file of the archived folder.
|
|
42
|
+
|
|
43
|
+
Raises:
|
|
44
|
+
SameFileError: If the source and destination are the same.
|
|
45
|
+
OSError: For other filesystem-related errors.
|
|
46
|
+
"""
|
|
47
|
+
logger.info("Starting archive process...")
|
|
48
|
+
logger.info(f"Source: {self.source_dir}")
|
|
49
|
+
logger.info(f"Destination: {self.archive_dir}")
|
|
50
|
+
|
|
51
|
+
# Step 1: Copy the source directory to the archive directory
|
|
52
|
+
try:
|
|
53
|
+
copytree(self.source_dir, self.archive_dir)
|
|
54
|
+
logger.info(f"Archive folder created successfully: {self.source_dir} -> {self.archive_dir}")
|
|
55
|
+
except SameFileError as e:
|
|
56
|
+
logger.error(f"Source and destination are the same: {e}")
|
|
57
|
+
raise
|
|
58
|
+
except FileExistsError:
|
|
59
|
+
logger.warning(f"Archive destination already exists: {self.archive_dir}")
|
|
60
|
+
except OSError as e:
|
|
61
|
+
logger.error(f"Failed to complete archive: {e}")
|
|
62
|
+
raise
|
|
63
|
+
|
|
64
|
+
# Create a zip file for the archived folder
|
|
65
|
+
try:
|
|
66
|
+
zip_path = make_archive(
|
|
67
|
+
base_name=str(self.archive_dir), # The base name of the archive
|
|
68
|
+
format="zip", # Archive format
|
|
69
|
+
root_dir=str(self.archive_dir), # Root directory to archive
|
|
70
|
+
)
|
|
71
|
+
logger.info(f"Archive zipped successfully: {zip_path}")
|
|
72
|
+
except OSError as e:
|
|
73
|
+
logger.error(f"Failed to zip the archive: {e}")
|
|
74
|
+
raise
|
|
75
|
+
|
|
76
|
+
return zip_path
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def get_archive_dir(base_dir, project_name):
|
|
80
|
+
"""
|
|
81
|
+
Generate a timestamped archive directory path.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
base_dir (str or Path): The base directory where archives are stored.
|
|
85
|
+
project_name (str): The name of the project being archived.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Path: A path to the timestamped archive directory.
|
|
89
|
+
"""
|
|
90
|
+
base_dir = Path(base_dir).resolve()
|
|
91
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
92
|
+
archive_dir = base_dir / f"{project_name}_archive_{timestamp}"
|
|
93
|
+
logger.debug(f"Generated archive directory: {archive_dir}")
|
|
94
|
+
return archive_dir
|