Skip to content

Commit 002e360

Browse files
committed
current state
1 parent a3d1439 commit 002e360

File tree

8 files changed

+307
-3
lines changed

8 files changed

+307
-3
lines changed

deep_code/tests/tools/test_new.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import unittest
2+
import shutil
3+
from pathlib import Path
4+
from unittest.mock import patch, MagicMock
5+
6+
from deep_code.tools.new import RepositoryInitializer
7+
8+
9+
class TestRepositoryInitializer(unittest.TestCase):
10+
def setUp(self):
11+
"""Set up a temporary directory for testing."""
12+
self.test_repo_name = "test_repo"
13+
self.test_repo_dir = Path(self.test_repo_name).absolute()
14+
self.templates_dir = Path(__file__).parent.parent / "deep_code" / "templates"
15+
16+
# Mock GitHub credentials
17+
self.github_username = "test_user"
18+
self.github_token = "test_token"
19+
20+
# Ensure the repository directory does not exist before each test
21+
if self.test_repo_dir.exists():
22+
shutil.rmtree(self.test_repo_dir)
23+
24+
def tearDown(self):
25+
"""Clean up the temporary directory after each test."""
26+
if self.test_repo_dir.exists():
27+
shutil.rmtree(self.test_repo_dir)
28+
29+
@patch("deep_code.tools.new.read_git_access_file")
30+
def test_missing_github_credentials(self, mock_read_git_access_file):
31+
"""Test that an error is raised if GitHub credentials are missing."""
32+
# Mock the Git access file with missing credentials
33+
mock_read_git_access_file.return_value = {}
34+
35+
# Verify that an error is raised
36+
with self.assertRaises(ValueError) as context:
37+
RepositoryInitializer(self.test_repo_name)
38+
self.assertIn("GitHub credentials are missing", str(context.exception))
39+
40+
@patch("deep_code.tools.new.read_git_access_file")
41+
def test_template_not_found(self, mock_read_git_access_file):
42+
"""Test that an error is raised if a template file is missing."""
43+
# Mock the Git access file
44+
mock_read_git_access_file.return_value = {
45+
"github-username": self.github_username,
46+
"github-token": self.github_token,
47+
}
48+
49+
# Initialize the repository with a non-existent template
50+
initializer = RepositoryInitializer(self.test_repo_name)
51+
initializer.templates_dir = Path("/non/existent/path")
52+
53+
# Verify that an error is raised
54+
with self.assertRaises(FileNotFoundError):
55+
initializer.initialize()
56+
57+
58+
if __name__ == "__main__":
59+
unittest.main()

deep_code/tools/new.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,135 @@
1010
pyproject.toml), and a template setup for documentation (e.g., using mkdocs),
1111
setup of the build pipeline"""
1212

13+
import subprocess
14+
from pathlib import Path
15+
import logging
1316

17+
import requests
18+
19+
from deep_code.utils.helper import read_git_access_file
20+
21+
logging.basicConfig(level=logging.INFO)
22+
logger = logging.getLogger(__name__)
23+
24+
25+
class RepositoryInitializer:
26+
"""
27+
A utility class to initialize a GitHub repository with configuration files,
28+
a workflow notebook template, and a Python package template for DeepESDL experiment
29+
"""
30+
31+
def __init__(self, repo_name: str):
32+
"""
33+
Initialize the RepositoryInitializer.
34+
"""
35+
self.repo_name = repo_name
36+
self.repo_dir = Path(repo_name).absolute()
37+
self.templates_dir = Path(__file__).parent / "templates"
38+
git_config = read_git_access_file()
39+
self.github_username = git_config.get("github-username")
40+
self.github_token = git_config.get("github-token")
41+
if not self.github_username or not self.github_token:
42+
raise ValueError("GitHub credentials are missing in the `.gitaccess` file.")
43+
44+
def create_local_repo(self) -> None:
45+
"""Create a local directory for the repository and initialize it as a Git repository."""
46+
logger.info(f"Creating local repository: {self.repo_dir}")
47+
self.repo_dir.mkdir(parents=True, exist_ok=True)
48+
subprocess.run(["git", "init"], cwd=self.repo_dir, check=True)
49+
50+
def _load_template(self, template_name: str) -> str:
51+
"""Load a template file from the templates directory."""
52+
template_path = self.templates_dir / template_name
53+
if not template_path.exists():
54+
raise FileNotFoundError(f"Template not found: {template_path}")
55+
with open(template_path, "r") as file:
56+
return file.read()
57+
58+
def create_config_files(self) -> None:
59+
"""Create configuration files in the repository."""
60+
logger.info("Creating configuration files...")
61+
62+
# Create README.md
63+
readme_content = (f"# {self.repo_name}\n\nThis repository contains workflows "
64+
f"and Python code for a DeepESDL Experiment.")
65+
(self.repo_dir / "README.md").write_text(readme_content)
66+
67+
# Create .gitignore
68+
gitignore_content = self._load_template(".gitignore")
69+
(self.repo_dir / ".gitignore").write_text(gitignore_content)
70+
71+
def create_jupyter_notebook_template(self) -> None:
72+
"""Create a workflow notebook template (workflow.ipynb)."""
73+
logger.info("Creating workflow notebook template...")
74+
workflow_content = self._load_template("workflow.ipynb")
75+
(self.repo_dir / "workflow.ipynb").write_text(workflow_content)
76+
77+
def create_python_package(self) -> None:
78+
"""Create a Python package template with a pyproject.toml file."""
79+
logger.info("Creating Python package template...")
80+
81+
# Create package directory
82+
package_dir = self.repo_dir / self.repo_name
83+
package_dir.mkdir(exist_ok=True)
84+
85+
# Create __init__.py
86+
(package_dir / "__init__.py").write_text("# Package initialization\n")
87+
88+
# Create pyproject.toml
89+
pyproject_content = self._load_template("pyproject.toml")
90+
pyproject_content = pyproject_content.replace("{repo_name}", self.repo_name)
91+
(self.repo_dir / "pyproject.toml").write_text(pyproject_content)
92+
93+
def create_github_repo(self) -> None:
94+
"""Create a remote GitHub repository and push the local repository."""
95+
if not self.github_username or not self.github_token:
96+
logger.warning("GitHub credentials not provided. Skipping remote repository creation.")
97+
return
98+
99+
logger.info("Creating remote GitHub repository...")
100+
101+
repo_url = "https://api.github.com/user/repos"
102+
repo_data = {
103+
"name": self.repo_name,
104+
"description": "Repository for DeepESDL workflows and Python code.",
105+
"private": True,
106+
}
107+
headers = {
108+
"Authorization": f"token {self.github_token}",
109+
"Accept": "application/vnd.github.v3+json",
110+
}
111+
response = requests.post(repo_url, json=repo_data, headers=headers)
112+
response.raise_for_status()
113+
114+
remote_url = f"https://github.com/{self.github_username}/{self.repo_name}.git"
115+
subprocess.run(["git", "remote", "add", "origin", remote_url], cwd=self.repo_dir, check=True)
116+
subprocess.run(["git", "add", "."], cwd=self.repo_dir, check=True)
117+
subprocess.run(["git", "commit", "-m", "Initial commit"], cwd=self.repo_dir, check=True)
118+
subprocess.run(["git", "push", "-u", "origin", "main"], cwd=self.repo_dir, check=True)
119+
120+
logger.info(f"Remote repository created: {remote_url}")
121+
122+
def create_github_actions_workflow(self) -> None:
123+
"""Create a GitHub Actions workflow for running unit tests."""
124+
logger.info("Creating GitHub Actions workflow...")
125+
126+
workflows_dir = self.repo_dir / ".github" / "workflows"
127+
workflows_dir.mkdir(parents=True, exist_ok=True)
128+
129+
workflow_content = self._load_template("unit-tests.yml")
130+
(workflows_dir / "unit-tests.yml").write_text(workflow_content)
131+
132+
def initialize(self) -> None:
133+
"""Initialize the repository with all templates and configurations."""
134+
self.create_local_repo()
135+
self.create_config_files()
136+
self.create_jupyter_notebook_template()
137+
self.create_python_package()
138+
self.create_github_repo()
139+
logger.info(f"Repository '{self.repo_name}' initialized successfully!")
140+
141+
142+
if __name__ == '__main__':
143+
r = RepositoryInitializer("deepesdl-test-experiment")
144+
r.initialize()

deep_code/tools/publish.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
)
2323
from deep_code.utils.dataset_stac_generator import OscDatasetStacGenerator
2424
from deep_code.utils.github_automation import GitHubAutomation
25-
from deep_code.utils.helper import serialize
25+
from deep_code.utils.helper import serialize, read_git_access_file
2626
from deep_code.utils.ogc_api_record import (
2727
ExperimentAsOgcRecord,
2828
LinksBuilder,
@@ -42,8 +42,7 @@ class GitHubPublisher:
4242
"""
4343

4444
def __init__(self):
45-
with fsspec.open(".gitaccess", "r") as file:
46-
git_config = yaml.safe_load(file) or {}
45+
git_config = read_git_access_file()
4746
self.github_username = git_config.get("github-username")
4847
self.github_token = git_config.get("github-token")
4948
if not self.github_username or not self.github_token:
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Ignore Python compiled files
2+
__pycache__/
3+
*.pyc
4+
*.pyo
5+
*.pyd
6+
7+
# Ignore Jupyter notebook checkpoints
8+
.ipynb_checkpoints/
9+
10+
# Ignore virtual environments
11+
venv/
12+
env/
13+
14+
.gitaccess
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[build-system]
2+
requires = ['setuptools>=61.2.0', 'wheel', 'build']
3+
build-backend = 'setuptools.build_meta'
4+
5+
[project]
6+
name = '{repo_name}'
7+
version = '0.1.0'
8+
description = 'A Python package for DeepESDL workflows.'
9+
authors = [
10+
{name = 'Your Name', email = '[email protected]'}
11+
]
12+
dependencies = [
13+
'numpy',
14+
'pandas',
15+
]
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Unit Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
11+
jobs:
12+
test:
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- name: Checkout repository
17+
uses: actions/checkout@v4
18+
19+
- name: Set up Python
20+
uses: actions/setup-python@v4
21+
with:
22+
python-version: '3.12'
23+
24+
- name: Install dependencies
25+
run: |
26+
python -m pip install --upgrade pip
27+
pip install .[dev]
28+
29+
- name: Run unit tests
30+
run: |
31+
pytest tests/
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"# Workflow Notebook\n\n",
8+
"This notebook provides a template for running DeepESDL workflows.\n",
9+
"Modify this notebook to implement your specific workflow."
10+
]
11+
},
12+
{
13+
"cell_type": "code",
14+
"execution_count": null,
15+
"metadata": {},
16+
"outputs": [],
17+
"source": [
18+
"# Import required libraries\n",
19+
"import numpy as np\n",
20+
"import pandas as pd\n",
21+
"\n",
22+
"# Add your workflow code here\n"
23+
]
24+
}
25+
],
26+
"metadata": {
27+
"kernelspec": {
28+
"display_name": "Python 3",
29+
"language": "python",
30+
"name": "python3"
31+
},
32+
"language_info": {
33+
"codemirror_mode": {
34+
"name": "ipython",
35+
"version": 3
36+
},
37+
"file_extension": ".py",
38+
"mimetype": "text/x-python",
39+
"name": "python",
40+
"nbconvert_exporter": "python",
41+
"pygments_lexer": "ipython3",
42+
"version": "3.10"
43+
}
44+
},
45+
"nbformat": 4,
46+
"nbformat_minor": 4
47+
}

deep_code/utils/helper.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import fsspec
2+
import yaml
3+
4+
15
def serialize(obj):
26
"""Convert non-serializable objects to JSON-compatible formats.
37
Args:
@@ -12,3 +16,7 @@ def serialize(obj):
1216
if hasattr(obj, "__dict__"):
1317
return obj.__dict__
1418
raise TypeError(f"Object of type {type(obj).__name__} is not JSON serializable")
19+
20+
def read_git_access_file():
21+
with fsspec.open(".gitaccess", "r") as file:
22+
return yaml.safe_load(file) or {}

0 commit comments

Comments
 (0)