Skip to content

Commit 819dcde

Browse files
authored
Merge pull request #34 from sdhutchins/dev-master
Major updates (v0.2).
2 parents bc75fdc + 6c04a87 commit 819dcde

16 files changed

Lines changed: 971 additions & 198 deletions

.gitignore

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ __pycache__/
88

99
# Distribution / packaging
1010
.Python
11-
env/
1211
build/
1312
develop-eggs/
1413
dist/
@@ -21,9 +20,11 @@ parts/
2120
sdist/
2221
var/
2322
wheels/
23+
share/python-wheels/
2424
*.egg-info/
2525
.installed.cfg
2626
*.egg
27+
MANIFEST
2728

2829
# PyInstaller
2930
# Usually these files are written by a python script from a template
@@ -38,13 +39,17 @@ pip-delete-this-directory.txt
3839
# Unit test / coverage reports
3940
htmlcov/
4041
.tox/
42+
.nox/
4143
.coverage
4244
.coverage.*
4345
.cache
4446
nosetests.xml
4547
coverage.xml
4648
*.cover
49+
*.py,cover
4750
.hypothesis/
51+
.pytest_cache/
52+
cover/
4853

4954
# Translations
5055
*.mo
@@ -53,6 +58,8 @@ coverage.xml
5358
# Django stuff:
5459
*.log
5560
local_settings.py
61+
db.sqlite3
62+
db.sqlite3-journal
5663

5764
# Flask stuff:
5865
instance/
@@ -65,27 +72,64 @@ instance/
6572
docs/_build/
6673

6774
# PyBuilder
75+
.pybuilder/
6876
target/
6977

7078
# Jupyter Notebook
7179
.ipynb_checkpoints
7280

73-
# pyenv
74-
.python-version
81+
# IPython
82+
profile_default/
83+
ipython_config.py
7584

76-
# celery beat schedule file
85+
# pyenv
86+
# For a library or package, you might want to ignore these files since the code is
87+
# intended to run in multiple environments; otherwise, check them in:
88+
# .python-version
89+
90+
# pipenv
91+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
93+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
94+
# install all needed dependencies.
95+
#Pipfile.lock
96+
97+
# poetry
98+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99+
# This is especially recommended for binary packages to ensure reproducibility, and is more
100+
# commonly ignored for libraries.
101+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102+
#poetry.lock
103+
104+
# pdm
105+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106+
#pdm.lock
107+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108+
# in version control.
109+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
110+
.pdm.toml
111+
.pdm-python
112+
.pdm-build/
113+
114+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
115+
__pypackages__/
116+
117+
# Celery stuff
77118
celerybeat-schedule
119+
celerybeat.pid
78120

79121
# SageMath parsed files
80122
*.sage.py
81123

82-
# dotenv
124+
# Environments
83125
.env
84-
85-
# virtualenv
86126
.venv
127+
env/
87128
venv/
88129
ENV/
130+
env.bak/
131+
venv.bak/
132+
labrat-env/
89133

90134
# Spyder project settings
91135
.spyderproject
@@ -99,6 +143,24 @@ ENV/
99143

100144
# mypy
101145
.mypy_cache/
146+
.dmypy.json
147+
dmypy.json
148+
149+
# Pyre type checker
150+
.pyre/
151+
152+
# pytype static type analyzer
153+
.pytype/
154+
155+
# Cython debug symbols
156+
cython_debug/
157+
158+
# MacOSx
159+
.DS_Store
102160

103-
# vscode
104-
.vscode/
161+
# PyCharm
162+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
163+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
164+
# and can be added to the global gitignore or merged into this file. For a more nuclear
165+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
166+
#.idea/

.travis.yml

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
1-
sudo: false
21
language: python
2+
dist: focal # Use Ubuntu 20.04 for Python 3.10+ support
33
cache: pip
4+
45
python:
5-
- "3.5"
6-
- "3.6"
6+
- "3.9"
7+
- "3.10"
8+
- "3.11"
9+
710
notifications:
811
email: false
9-
# command to install dependencies
12+
13+
# Install dependencies
1014
install:
11-
- "pip install --upgrade pip setuptools"
12-
- "pip install -r requirements.txt"
13-
# command to run nosetests
14-
script: nosetests tests/ --verbosity=3
15+
- pip install --upgrade pip setuptools wheel
16+
- pip install -e . # Install the package in editable mode
17+
- pip install -r requirements.txt
18+
- pip install pytest pytest-cov # Ensure pytest and coverage are installed
19+
20+
# Run tests with pytest and generate a coverage report
21+
script:
22+
- pytest --cov=labrat tests/

README.md

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# labrat
2-
[![Build Status](https://travis-ci.com/sdhutchins/labrat.svg?branch=master)](https://travis-ci.com/sdhutchins/labrat)
2+
3+
[![Build Status](https://app.travis-ci.com/sdhutchins/labrat.svg?token=xfnbNTQhjNbir5xACn8R&branch=master)](https://app.travis-ci.com/sdhutchins/labrat)
34

45
A basic science lab framework aimed at reproducibility and lab management. This package is in the very early stages of development.
56

@@ -18,8 +19,6 @@ A basic science lab framework aimed at reproducibility and lab management. This
1819

1920
If you want to develop or contribute to this package, install with `pip install -e .`
2021

21-
*This package is compatible with python 3.4 and up.*
22-
2322
## Examples
2423

2524
### Dilute a stock concentration
@@ -30,16 +29,50 @@ from labrat.math import dilute_stock
3029
# Get the final concentration
3130
dilute_stock(100, 2, **{'vF': 4})
3231
```
33-
<br>
3432

35-
### Create a new computational biology project
33+
### Project Management
34+
35+
Create a new project using the below code. This will create a `.labrat` file that contains the project information for
36+
any project created.
3637

3738
```python
39+
# Create a new project
40+
import os
3841
from labrat.project import ProjectManager
39-
projectmanager = ProjectManager('Dr. Jane Doe')
40-
projectmanager.new_project(project_type='computational-biology',
41-
project_name='KARG Analysis',
42-
project_path=os.getcwd())
42+
43+
# Initialize the ProjectManager with a username
44+
project_manager = ProjectManager('Dr. Jane Doe')
45+
46+
# Create a new project
47+
project_manager.new_project(
48+
project_type='computational-biology',
49+
project_name='KARG Analysis',
50+
project_path=os.getcwd(),
51+
description="Analyze the KARG data."
52+
)
53+
```
54+
55+
Archive a project:
56+
57+
```python
58+
from projectmanager import ProjectManager
59+
60+
project_manager = ProjectManager()
61+
project_path = "/Users/shutchens/Documents/Git-Repos/labrat/karg_analysis"
62+
archive_base_dir = "/Users/shutchens/Archive"
63+
64+
archive_dir = project_manager.archive_project(project_path=project_path, archive_base_dir=archive_base_dir)
65+
```
66+
67+
Delete a project:
68+
69+
```python
70+
# Path to the project to delete
71+
project_path = "/Users/shutchens/Documents/Git-Repos/labrat/karg_analysis"
72+
archive_base_dir = "/Users/shutchens/Archive"
73+
74+
# Delete the project
75+
archived_path = project_manager.delete_project(project_path, archive_base_dir)
4376
```
4477

4578
## ToDo
@@ -52,7 +85,7 @@ projectmanager.new_project(project_type='computational-biology',
5285
## Author
5386

5487
Shaurita Hutchins · [@sdhutchins](https://github.com/sdhutchins)
55-
· [:email:](mailto:sdhutchins@outlook.com)
88+
· [:email:](mailto:shaurita.d.hutchins@gmail.com)
5689

5790
## Contributing
5891

labrat/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
# Package Global Variables
32
_DATEFMT1 = '%a %b %d %I:%M:%S %p %Y' # Used to add as a date
43
_DATEFMT2 = '%m-%d-%Y_%I-%M-%S-%p' # Used to append to archives

labrat/filemanager/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
from .backup import Backup
1+
from .archive import Archiver
2+
from .organize import FileOrganizer

labrat/filemanager/archive.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
from shutil import copytree, SameFileError, make_archive
2+
from datetime import datetime
3+
import os
4+
import contextlib
5+
from pathlib import Path
6+
import logzero
7+
from logzero import logger
8+
9+
10+
class Archiver:
11+
"""Archive folders and files."""
12+
13+
def __init__(self, source_dir, archive_dir):
14+
"""
15+
Initialize logger and archive parameters.
16+
17+
Args:
18+
source_dir (str or Path): The directory to copy or archive.
19+
archive_dir (str or Path): The destination directory for the archive.
20+
"""
21+
self.source_dir = Path(source_dir).resolve()
22+
self.archive_dir = Path(archive_dir).resolve()
23+
24+
if not self.source_dir.exists() or not self.source_dir.is_dir():
25+
raise ValueError(f"Source directory '{self.source_dir}' does not exist or is not a directory.")
26+
logger.debug(f"Source directory: {self.source_dir}")
27+
28+
if not self.archive_dir.parent.exists():
29+
raise ValueError(f"Archive directory parent '{self.archive_dir.parent}' does not exist.")
30+
logger.debug(f"Archive directory: {self.archive_dir}")
31+
32+
# Configure log file
33+
logzero.logfile(f"archive_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log")
34+
logger.info("Archive initialized.")
35+
36+
def archive(self):
37+
"""
38+
Perform the archive by copying the source directory to the archive directory
39+
and then creating a zip file of the archived folder.
40+
41+
Raises:
42+
SameFileError: If the source and destination are the same.
43+
OSError: For other filesystem-related errors.
44+
"""
45+
logger.info("Starting archive process...")
46+
logger.info(f"Source: {self.source_dir}")
47+
logger.info(f"Destination: {self.archive_dir}")
48+
49+
# Step 1: Copy the source directory to the archive directory
50+
try:
51+
copytree(self.source_dir, self.archive_dir)
52+
logger.info(f"Archive folder created successfully: {self.source_dir} -> {self.archive_dir}")
53+
except SameFileError as e:
54+
logger.error(f"Source and destination are the same: {e}")
55+
raise
56+
except FileExistsError:
57+
logger.warning(f"Archive destination already exists: {self.archive_dir}")
58+
except OSError as e:
59+
logger.error(f"Failed to complete archive: {e}")
60+
raise
61+
62+
# Create a zip file for the archived folder
63+
try:
64+
zip_path = make_archive(
65+
base_name=str(self.archive_dir), # The base name of the archive
66+
format="zip", # Archive format
67+
root_dir=str(self.archive_dir), # Root directory to archive
68+
)
69+
logger.info(f"Archive zipped successfully: {zip_path}")
70+
except OSError as e:
71+
logger.error(f"Failed to zip the archive: {e}")
72+
raise
73+
74+
return zip_path
75+
76+
@staticmethod
77+
def get_archive_dir(base_dir, project_name):
78+
"""
79+
Generate a timestamped archive directory path.
80+
81+
Args:
82+
base_dir (str or Path): The base directory where archives are stored.
83+
project_name (str): The name of the project being archived.
84+
85+
Returns:
86+
Path: A path to the timestamped archive directory.
87+
"""
88+
base_dir = Path(base_dir).resolve()
89+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
90+
archive_dir = base_dir / f"{project_name}_archive_{timestamp}"
91+
logger.debug(f"Generated archive directory: {archive_dir}")
92+
return archive_dir

0 commit comments

Comments
 (0)