dsfolder 0.0.0.1__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.
Files changed (39) hide show
  1. dsfolder-0.0.0.1.dist-info/METADATA +49 -0
  2. dsfolder-0.0.0.1.dist-info/RECORD +39 -0
  3. dsfolder-0.0.0.1.dist-info/WHEEL +5 -0
  4. dsfolder-0.0.0.1.dist-info/entry_points.txt +3 -0
  5. dsfolder-0.0.0.1.dist-info/top_level.txt +1 -0
  6. dspackagefolder/__init__.py +1 -0
  7. dspackagefolder/install_precommit_and_create_env.py +184 -0
  8. dspackagefolder/main.py +46 -0
  9. dspackagefolder/template/.config/.gitkeep +0 -0
  10. dspackagefolder/template/.config/example.toml +1 -0
  11. dspackagefolder/template/.gitignore +18 -0
  12. dspackagefolder/template/.pre-commit-config.yaml +13 -0
  13. dspackagefolder/template/.python-version +1 -0
  14. dspackagefolder/template/Dockerfile +33 -0
  15. dspackagefolder/template/EXAMPLE/example.py +0 -0
  16. dspackagefolder/template/README.md +13 -0
  17. dspackagefolder/template/checklist/.gitignore +1 -0
  18. dspackagefolder/template/checklist/GENERIC_CHECKLIST.xlsx +0 -0
  19. dspackagefolder/template/checklist/ML_CHECKLIST.xlsx +0 -0
  20. dspackagefolder/template/checklist/STATISTICS_CHECKLIST.xlsx +0 -0
  21. dspackagefolder/template/data/.gitkeep +1 -0
  22. dspackagefolder/template/data/INPUT/.gitkeep +1 -0
  23. dspackagefolder/template/data/OUTPUT/.gitkeep +1 -0
  24. dspackagefolder/template/logs/.gitkeep +2 -0
  25. dspackagefolder/template/main.py +60 -0
  26. dspackagefolder/template/main_dashboard.py +0 -0
  27. dspackagefolder/template/models/.gitignore +1 -0
  28. dspackagefolder/template/requirements.txt +6 -0
  29. dspackagefolder/template/sql/.gitkeep +0 -0
  30. dspackagefolder/template/sql/query.sql +1 -0
  31. dspackagefolder/template/src/.gitkeep +0 -0
  32. dspackagefolder/template/src/__init__.py +0 -0
  33. dspackagefolder/template/src/sqlcrud.py +114 -0
  34. dspackagefolder/template/src/utils/config_loader.py +20 -0
  35. dspackagefolder/template/src/utils/env_loader.py +20 -0
  36. dspackagefolder/template/src/utils/logging_config.py +48 -0
  37. dspackagefolder/template/template_notebook.ipynb +384 -0
  38. dspackagefolder/template/unit_testing/.gitkeep +0 -0
  39. dspackagefolder/template/validation/.gitkeep +0 -0
@@ -0,0 +1,49 @@
1
+ Metadata-Version: 2.1
2
+ Name: dsfolder
3
+ Version: 0.0.0.1
4
+ Summary: Add your description here
5
+ Requires-Python: >=3.11
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: path>=17.1.1
8
+ Requires-Dist: python-dotenv>=1.2.1
9
+ Requires-Dist: sqlalchemy>=2.0.48
10
+ Requires-Dist: toml>=0.10.2
11
+
12
+ ## Data Science Folder Structure Setup
13
+
14
+ 1. This was developed for creating a Python folder to start a data science project.
15
+
16
+ 2. It includes preconfigured connections to different systems:
17
+ - uv
18
+ - git
19
+ - GitHub workflow
20
+ - .gitignore
21
+
22
+ 3. It also provides small code snippets for connecting to other systems.
23
+
24
+ 4. Below are some of the systems included, along with additional setup steps to get them working:
25
+ - logging
26
+ - SQL
27
+ - src
28
+ - config
29
+ - pre-commit hook
30
+ - .gitignore
31
+ - git
32
+ - GitHub workflow
33
+ - uv
34
+ - env
35
+ - validation
36
+ - testing
37
+ - requirements.txt
38
+ - Install dependencies with: `uv install requirements.txt`
39
+
40
+ 5. Install the package:
41
+ ```bash
42
+ pip install datascience-folder-structure
43
+ 6. Create a folder structure:
44
+ ```bash
45
+ createdatasciencefolder NameOfProject
46
+
47
+ or
48
+ ```bash
49
+ dsf NameOfProject
@@ -0,0 +1,39 @@
1
+ dspackagefolder/__init__.py,sha256=T4SQ9iIEoHqgs3vcujfIj14638recwUibHxZJMp_x0g,36
2
+ dspackagefolder/install_precommit_and_create_env.py,sha256=WBHSDf6V4Dinelrx4zPOnKn1txgJxj13GmBOcXT850E,5899
3
+ dspackagefolder/main.py,sha256=2uT3xXbruN9RKaCW4AZU3TSYMtsYeolJjqH2O3cThoo,1651
4
+ dspackagefolder/template/.gitignore,sha256=z31xAJAzlQXZa7OPSmKu-AvzRLiy8x2l5z1hmuGFzg4,198
5
+ dspackagefolder/template/.pre-commit-config.yaml,sha256=KqIkDWcZz_1SyNx2SbGpMjN81kpLlHUgTU_dqY_Mkt4,288
6
+ dspackagefolder/template/.python-version,sha256=p7naXjVc4upFCMxTdWhJ9EyWDYejjlp20Mc011EsU3E,6
7
+ dspackagefolder/template/Dockerfile,sha256=kOWmv8tJOcWrMTYAu2O3sB-lSSH-APlSRDk_qNC0CzQ,867
8
+ dspackagefolder/template/README.md,sha256=PhYLaYwDOaxnCPcGC8IprTABLM00z3I-fUeby0OsXBg,376
9
+ dspackagefolder/template/main.py,sha256=bPmQKOveew9aL9nolZgxic4RbwJII-oekxugIa5DVrU,1613
10
+ dspackagefolder/template/main_dashboard.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ dspackagefolder/template/requirements.txt,sha256=TuUEcQyEvKvPEmmZNW351SJsVtWjE4KTvLZ2HCK1PBQ,74
12
+ dspackagefolder/template/template_notebook.ipynb,sha256=Cw_zuCJ0UGpqkCAqU0EtSf3wd5if9-8zIiz8Qha3d2s,8474
13
+ dspackagefolder/template/.config/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ dspackagefolder/template/.config/example.toml,sha256=O1CFW5XwAaj9TltHIyOVLGrA0_V97YsIu60m41mtqKM,10
15
+ dspackagefolder/template/EXAMPLE/example.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ dspackagefolder/template/checklist/.gitignore,sha256=L4FYmilJi5_FIJCHOdbeBC35uyrz6Lb0QpIYTHDrd3Y,3
17
+ dspackagefolder/template/checklist/GENERIC_CHECKLIST.xlsx,sha256=JZsJaItwx6YgCkDsmn01kmOid19LhKSLzT4Kl0SBzrI,10080
18
+ dspackagefolder/template/checklist/ML_CHECKLIST.xlsx,sha256=ezIJjW6xSojmFL-PcY7xvyHgtqinqK0GtXxtBvwkvMQ,11157
19
+ dspackagefolder/template/checklist/STATISTICS_CHECKLIST.xlsx,sha256=_oVHef7dDJoNs21Vic1ez5T4_pmEckGpB0kgAaes3sk,9557
20
+ dspackagefolder/template/data/.gitkeep,sha256=L4FYmilJi5_FIJCHOdbeBC35uyrz6Lb0QpIYTHDrd3Y,3
21
+ dspackagefolder/template/data/INPUT/.gitkeep,sha256=L4FYmilJi5_FIJCHOdbeBC35uyrz6Lb0QpIYTHDrd3Y,3
22
+ dspackagefolder/template/data/OUTPUT/.gitkeep,sha256=L4FYmilJi5_FIJCHOdbeBC35uyrz6Lb0QpIYTHDrd3Y,3
23
+ dspackagefolder/template/logs/.gitkeep,sha256=yJX96YmS0LvV0np6RZeW2d1q8B9Do1MqExKH5iIhUAY,10
24
+ dspackagefolder/template/models/.gitignore,sha256=L4FYmilJi5_FIJCHOdbeBC35uyrz6Lb0QpIYTHDrd3Y,3
25
+ dspackagefolder/template/sql/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ dspackagefolder/template/sql/query.sql,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
27
+ dspackagefolder/template/src/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
+ dspackagefolder/template/src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
+ dspackagefolder/template/src/sqlcrud.py,sha256=UZfWfdCwK1nnu1WJShMSWDYOvWStq0iHlx6z8c24h5Y,2858
30
+ dspackagefolder/template/src/utils/config_loader.py,sha256=rjUFGcdbEIDxelUpIHp1YX6HMGjJCcUkWOzAsHQYayw,629
31
+ dspackagefolder/template/src/utils/env_loader.py,sha256=0ajuMmv3BLhsRgIt5qOLlJaZxNXZBZ3NGcfJaZf5bkM,589
32
+ dspackagefolder/template/src/utils/logging_config.py,sha256=hhCcIAHjc5E_QIyYixewI-jQgAJwpJiJRaM7wv9ARvM,1394
33
+ dspackagefolder/template/unit_testing/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
+ dspackagefolder/template/validation/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
+ dsfolder-0.0.0.1.dist-info/METADATA,sha256=jwA8w01F49D0eKOr-Nce-mikg_DfQNS1K_1wyvIqzs8,1222
36
+ dsfolder-0.0.0.1.dist-info/WHEEL,sha256=EWGgdj_eH-g9dwsWZOTG0kmGucEuKPxGzOKD-jAVUHs,92
37
+ dsfolder-0.0.0.1.dist-info/entry_points.txt,sha256=kHH2Hc8lEfWczBRr8UXUWid_KHU6MHsikh3RWHTHgy4,134
38
+ dsfolder-0.0.0.1.dist-info/top_level.txt,sha256=XKesjMHDkbB-GfN3xELx0YajPXIKPGEJVVZ1c42KL_Q,16
39
+ dsfolder-0.0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.48.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ createdatasciencefolder = dspackagefolder.main:create_ds_folder_cli
3
+ dsf = dspackagefolder.main:create_ds_folder_cli
@@ -0,0 +1 @@
1
+ dspackagefolder
@@ -0,0 +1 @@
1
+ from .main import create_ds_folder
@@ -0,0 +1,184 @@
1
+ ## install uv
2
+ ## uv add requirements.txt
3
+ ## install pre-commit
4
+ ## pre-commit install
5
+ ## creeate env file into the new folder
6
+
7
+
8
+ import subprocess
9
+ import sys
10
+ from pathlib import Path
11
+
12
+
13
+ def setup_project_environment(project_dir: str | Path = "."):
14
+ project_dir = Path(project_dir).resolve()
15
+
16
+ try:
17
+ if not project_dir.exists():
18
+ raise FileNotFoundError(f"{project_dir} does not exist")
19
+ except Exception as e:
20
+ print(f"❌ Invalid project directory: {e}")
21
+ return
22
+
23
+ def run(cmd: list[str]):
24
+ try:
25
+ subprocess.check_call(cmd, cwd=project_dir)
26
+ except subprocess.CalledProcessError as e:
27
+ print(f"❌ Command failed: {' '.join(cmd)}\n{e}")
28
+ except FileNotFoundError as e:
29
+ print(f"❌ Command not found: {cmd[0]}\n{e}")
30
+ except Exception as e:
31
+ print(f"❌ Unexpected error running {' '.join(cmd)}: {e}")
32
+
33
+ # 1️⃣ Install uv if missing
34
+ try:
35
+ subprocess.check_call([sys.executable, "-m", "uv", "--version"])
36
+ except (subprocess.CalledProcessError, FileNotFoundError):
37
+ try:
38
+ print("Installing uv...")
39
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "uv"])
40
+ except Exception as e:
41
+ print(f"❌ Failed to install uv: {e}")
42
+
43
+ # 2️⃣ Initialise uv project if pyproject.toml is missing
44
+ try:
45
+ pyproject = project_dir / "pyproject.toml"
46
+ if not pyproject.exists():
47
+ print("Initialising uv project...")
48
+ run(["uv", "init"])
49
+ except Exception as e:
50
+ print(f"❌ Error during uv init: {e}")
51
+
52
+ # 3️⃣ Add requirements.txt using uv
53
+ try:
54
+ requirements = project_dir / "requirements.txt"
55
+ if requirements.exists():
56
+ run(["uv", "add", "-r", "requirements.txt"])
57
+ else:
58
+ print("requirements.txt not found — skipping uv add")
59
+ except Exception as e:
60
+ print(f"❌ Error adding requirements: {e}")
61
+
62
+ # 4️⃣ Install pre-commit if missing
63
+ try:
64
+ subprocess.check_call(["pre-commit", "--version"])
65
+ except (subprocess.CalledProcessError, FileNotFoundError):
66
+ try:
67
+ print("Installing pre-commit...")
68
+ subprocess.check_call(
69
+ [sys.executable, "-m", "pip", "install", "pre-commit"]
70
+ )
71
+ except Exception as e:
72
+ print(f"❌ Failed to install pre-commit: {e}")
73
+
74
+ # 5️⃣ Install pre-commit hooks
75
+ try:
76
+ run(["git", "init"])
77
+ except FileNotFoundError:
78
+ print("❌ Git is not installed or is not in your PATH.")
79
+ print("Please install Git from https://git-scm.com/downloads")
80
+ print("After installing Git, restart your terminal and try again.")
81
+ except Exception as e:
82
+ print(f"❌ Error initializing git repository: {e}")
83
+ try:
84
+ run(["pre-commit", "install"])
85
+ except Exception as e:
86
+ print(f"❌ Error setting up pre-commit hooks: {e}")
87
+
88
+ # 6️⃣ Create ENVIRONMENT folder and .env file
89
+ try:
90
+ env_folder = project_dir / "ENVIRONMENT"
91
+ env_folder.mkdir(parents=True, exist_ok=True)
92
+
93
+ env_file = env_folder / ".env"
94
+ if not env_file.exists():
95
+ env_file.write_text("# Environment variables\n", encoding="utf-8")
96
+ print(f"Created {env_file}")
97
+ else:
98
+ print(f"{env_file} already exists")
99
+
100
+ gitignore_file = env_folder / ".gitignore"
101
+ if not gitignore_file.exists():
102
+ gitignore_file.write_text("*\n", encoding="utf-8")
103
+ print(f"Created {gitignore_file}")
104
+ else:
105
+ print(f"{gitignore_file} already exists")
106
+
107
+ except Exception as e:
108
+ print(f"❌ Error creating environment files: {e}")
109
+
110
+ # 7️⃣ Create GitHub Actions workflow
111
+ try:
112
+ github_workflows = project_dir / ".github" / "workflows"
113
+ github_workflows.mkdir(parents=True, exist_ok=True)
114
+
115
+ precommit_workflow = github_workflows / "pre-commit.yml"
116
+
117
+ if not precommit_workflow.exists():
118
+ precommit_workflow.write_text(
119
+ """name: Pre-commit
120
+
121
+ on:
122
+ push:
123
+ pull_request:
124
+
125
+ jobs:
126
+ pre-commit:
127
+ runs-on: ubuntu-latest
128
+
129
+ steps:
130
+ - name: Checkout code
131
+ uses: actions/checkout@v4
132
+
133
+ - name: Set up Python
134
+ uses: actions/setup-python@v5
135
+ with:
136
+ python-version: "3.11"
137
+
138
+ - name: Install dependencies
139
+ run: |
140
+ pip install pre-commit
141
+
142
+ - name: Run pre-commit
143
+ run: |
144
+ pre-commit run --all-files
145
+ """,
146
+ encoding="utf-8",
147
+ )
148
+ print(f"Created GitHub Actions workflow at {precommit_workflow}")
149
+ else:
150
+ print(f"{precommit_workflow} already exists")
151
+
152
+ except Exception as e:
153
+ print(f"❌ Error creating GitHub workflow: {e}")
154
+
155
+ # 7️⃣ Create GitHub Actions workflow
156
+ try:
157
+ precommit_workflow = project_dir / ".pre-commit-config.yaml"
158
+
159
+ if not precommit_workflow.exists():
160
+ precommit_workflow.write_text(
161
+ """repos:
162
+ - repo: https://github.com/kynan/nbstripout
163
+ rev: 0.6.1
164
+ hooks:
165
+ - id: nbstripout
166
+ - repo: https://github.com/Yelp/detect-secrets
167
+ rev: v1.5.0
168
+ hooks:
169
+ - id: detect-secrets
170
+ - repo: https://github.com/psf/black
171
+ rev: 24.4.2
172
+ hooks:
173
+ - id: black
174
+ """,
175
+ encoding="utf-8",
176
+ )
177
+ print(f"Created pre-commit configuration at {precommit_workflow}")
178
+ else:
179
+ print(f"{precommit_workflow} already exists")
180
+
181
+ except Exception as e:
182
+ print(f"❌ Error creating pre-commit configuration: {e}")
183
+
184
+ print("✅ Project environment setup complete")
@@ -0,0 +1,46 @@
1
+ from pathlib import Path
2
+ import shutil
3
+ import importlib.resources as resources
4
+ import sys
5
+ from dspackagefolder.install_precommit_and_create_env import setup_project_environment
6
+
7
+
8
+ def create_ds_folder(folder_name: str = "dsframework"):
9
+ # 1️⃣ Create destination folder
10
+ # 2️⃣ Locate the template folder INSIDE the package
11
+ with resources.path("dspackagefolder", "template") as template_path:
12
+ target = Path(folder_name)
13
+
14
+ if target.exists():
15
+ print(f"Folder already exists: {target}")
16
+ return
17
+
18
+ # 3️⃣ Copy template into destination
19
+ shutil.copytree(template_path, target)
20
+
21
+ print(f"Project created at: {target.resolve()}")
22
+ # 4️⃣ Setup project environment
23
+ setup_project_environment(target)
24
+ print("🎉 Setup complete! Happy coding!")
25
+
26
+
27
+ def create_ds_folder_cli():
28
+ # 1️⃣ Create destination folder
29
+ # dest_root = Path("dspackagefolder")
30
+ # dest_root.mkdir(exist_ok=True)
31
+ project_name = sys.argv[1] if len(sys.argv) > 1 else "data_science_project"
32
+ # 2️⃣ Locate the template folder INSIDE the package
33
+ with resources.path("dspackagefolder", "template") as template_path:
34
+ target = Path(project_name)
35
+
36
+ if target.exists():
37
+ print(f"Folder already exists: {target}")
38
+ return
39
+
40
+ # 3️⃣ Copy template into destination
41
+ shutil.copytree(template_path, target)
42
+
43
+ print(f"Project created at: {target.resolve()}")
44
+ # 4️⃣ Setup project environment
45
+ setup_project_environment(project_name)
46
+ print("🎉 Setup complete! Happy coding!")
File without changes
@@ -0,0 +1 @@
1
+ "data" = 3
@@ -0,0 +1,18 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+
13
+ # JUPYTER NOTEBOOKS
14
+ .ipynb_checkpoints/
15
+
16
+
17
+ # Environment variables
18
+ .env
@@ -0,0 +1,13 @@
1
+ repos:
2
+ - repo: https://github.com/kynan/nbstripout
3
+ rev: 0.6.1
4
+ hooks:
5
+ - id: nbstripout
6
+ - repo: https://github.com/Yelp/detect-secrets
7
+ rev: v1.5.0
8
+ hooks:
9
+ - id: detect-secrets
10
+ - repo: https://github.com/psf/black
11
+ rev: 24.4.2
12
+ hooks:
13
+ - id: black
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,33 @@
1
+ # Dockerfile
2
+ FROM python:3.12-slim
3
+
4
+ WORKDIR /app
5
+
6
+ # Install uv and Jupyter
7
+ RUN pip install --no-cache-dir uv jupyterlab notebook
8
+
9
+ CMD ["bash"]
10
+
11
+
12
+ ## Build the Docker image
13
+ # Run the following command from the folder containing this Dockerfile:
14
+ # docker build -t my-dev-env .
15
+
16
+ ## Run the container (mount your local project directory)
17
+ # Linux/macOS:
18
+ # docker run -it -p 8888:8888 -v $(pwd):/app my-dev-env bash
19
+ #
20
+ # Windows (Command Prompt):
21
+ # docker run -it -p 8888:8888 -v %cd%:/app my-dev-env cmd
22
+
23
+ ## Start Jupyter Notebook inside the container
24
+ # jupyter notebook --ip=0.0.0.0 --port=8888 --no-browser --allow-root
25
+ #
26
+ # Then open your browser and go to:
27
+ # http://localhost:8888
28
+
29
+ ## Restart an existing container
30
+ # docker restart <container_name_or_id>
31
+
32
+ ## Reconnect to a running container
33
+ # docker exec -it <container_name_or_id> bash
File without changes
@@ -0,0 +1,13 @@
1
+ ## TO USE THIS FOLDER:
2
+ run the install_precommit_and_create_env.py first
3
+ run uv add requirements.txt
4
+
5
+
6
+
7
+ ## main_dashboard use and running
8
+ main_dashboard.py is used for visualizing the dashboard of the data.
9
+ you run it by uv run streamlit run main_dashboard.py
10
+
11
+ # WHAT TO DO NEXT
12
+ I need to show examples of how to connect the data/sql/validation/logs/config/src
13
+
@@ -0,0 +1 @@
1
+ !*/
@@ -0,0 +1 @@
1
+ !*/
@@ -0,0 +1 @@
1
+ !*/
@@ -0,0 +1 @@
1
+ !*/
@@ -0,0 +1,2 @@
1
+ !*/
2
+ *.log
@@ -0,0 +1,60 @@
1
+ ##### This is a template for making src available in any folder.
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ sys.path.append(str(Path(__file__).resolve().parents[1]))
6
+ from src.utils.logging_config import setup_logger
7
+ from src.utils.config_loader import load_config
8
+
9
+ ###### Set up logger
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ sys.path.append(str(Path(__file__).resolve().parents[1]))
14
+ from src.utils.logging_config import setup_logger
15
+
16
+ logger = setup_logger(__name__, base_filename="pipeline")
17
+
18
+ logger.info("Started run")
19
+ logger.error("Failure occurred")
20
+
21
+ # logging = setup_logging()
22
+
23
+
24
+ ### Load config
25
+ import sys
26
+ from pathlib import Path
27
+
28
+ sys.path.append(str(Path(__file__).resolve().parents[1]))
29
+ from src.utils.config_loader import load_config
30
+
31
+ config = load_config()
32
+ print(config)
33
+
34
+
35
+ ### Load env variables
36
+ import sys
37
+ from pathlib import Path
38
+
39
+ sys.path.append(str(Path(__file__).resolve().parents[1]))
40
+ from src.utils.env_loader import load_env_from_root
41
+
42
+ env_path = load_env_from_root()
43
+ print(f"Loaded env variables from: {env_path}")
44
+ ##import os
45
+ ##os.getenv("YOUR_ENV_VAR")
46
+
47
+ ### Example usage of SQLCRUD
48
+ import sys
49
+ from pathlib import Path
50
+
51
+ sys.path.append(str(Path(__file__).resolve().parents[1]))
52
+ from src.sqlcrud import SQLServerCRUD
53
+
54
+ connection_string = "DRIVER={ODBC Driver 17 for SQL Server};SERVER=your_server;DATABASE=your_db;UID=your_user;PWD=your_password"
55
+ sql_crud = SQLServerCRUD(connection_string)
56
+ sql_crud.connect()
57
+ sql_crud.build_query("sql/query.sql", params={"param1": "value1"})
58
+ df = sql_crud.read_data()
59
+ print(df)
60
+ sql_crud.close_connection()
File without changes
@@ -0,0 +1 @@
1
+ !*/
@@ -0,0 +1,6 @@
1
+ # toml
2
+ # streamlit
3
+ # pydantic
4
+ # python-dotenv
5
+ # sqlalchemy
6
+ # pandas
File without changes
@@ -0,0 +1 @@
1
+
File without changes
File without changes
@@ -0,0 +1,114 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Optional, Dict, Any
3
+
4
+
5
+ class SQLCRUD(ABC):
6
+ """
7
+ Abstract base class for SQL CRUD operations.
8
+ """
9
+
10
+ def _init_(self, connection_params: dict):
11
+ self.connection_params = connection_params
12
+ self.connection = None
13
+ self.query = None
14
+
15
+ @abstractmethod
16
+ def connect(self):
17
+ """
18
+ Establish a database connection.
19
+ """
20
+ pass
21
+
22
+ @abstractmethod
23
+ def build_query(self, sql_file_path: str, params: Optional[Dict[str, Any]] = None):
24
+ """
25
+ Build SQL query from a .sql file and optional parameters.
26
+ """
27
+ pass
28
+
29
+ @abstractmethod
30
+ def read_data(self):
31
+ """
32
+ Execute the built query and return the results.
33
+ """
34
+ pass
35
+
36
+ def close_connection(self):
37
+ """
38
+ Close the database connection if open.
39
+ """
40
+ if self.connection:
41
+ self.connection.close()
42
+ self.connection = None
43
+
44
+
45
+ from sqlalchemy import create_engine, text
46
+ from sqlalchemy.engine import URL
47
+ from typing import Optional, Dict, Any
48
+
49
+
50
+ class SQLServerCRUD(SQLCRUD):
51
+ """
52
+ Concrete implementation of SQLCRUD for SQL Server using SQLAlchemy.
53
+ Designed for NMTR training and imputation queries.
54
+ """
55
+
56
+ def _init_(self, connection_string: str):
57
+ super()._init_({"connection_string": connection_string})
58
+ self.engine = None
59
+ self.params: Dict[str, Any] = {}
60
+
61
+
62
+ def connect(self):
63
+ connection_url = URL.create(
64
+ "mssql+pyodbc",
65
+ query={"odbc_connect": self.connection_params["connection_string"]},
66
+ )
67
+ self.engine = create_engine(connection_url)
68
+
69
+
70
+ def build_query(
71
+ self,
72
+ sql: Optional[str] = None,
73
+ sql_file_path: Optional[str] = None,
74
+ params: Optional[Dict[str, Any]] = None,
75
+ ):
76
+ """
77
+ Build query from either:
78
+ - raw SQL string
79
+ - .sql file path
80
+ """
81
+
82
+ if sql_file_path:
83
+ with open(sql_file_path, "r") as file:
84
+ sql = file.read()
85
+
86
+ if not sql:
87
+ raise ValueError("Either sql or sql_file_path must be provided")
88
+
89
+ self.query = sql
90
+ self.params = params or {}
91
+
92
+
93
+ def read_data(self):
94
+ import pandas as pd
95
+
96
+ """
97
+ Executes the query. For SELECT returns results,
98
+ for DDL (like SELECT INTO) just executes.
99
+ """
100
+ if not self.engine:
101
+ raise ValueError("Connection not established")
102
+
103
+ if not self.query:
104
+ raise ValueError("Query not built")
105
+
106
+ with self.engine.begin() as conn:
107
+ result = conn.execute(text(self.query), self.params)
108
+
109
+ # Try fetching (will fail for DDL, so handle gracefully)
110
+ try:
111
+ df = pd.DataFrame(result.fetchall(), columns=result.keys())
112
+ return df
113
+ except:
114
+ return None
@@ -0,0 +1,20 @@
1
+ from pathlib import Path
2
+ import tomllib # Python 3.11+
3
+
4
+ # use: import tomli as tomllib # if Python <= 3.10
5
+
6
+
7
+ def load_config(config_name: str = ".config/example.toml") -> dict:
8
+ """
9
+ Load config.toml from project root regardless of where this is imported from.
10
+ """
11
+
12
+ current = Path(__file__).resolve()
13
+
14
+ for parent in [current] + list(current.parents):
15
+ candidate = parent / config_name
16
+ if candidate.exists():
17
+ with open(candidate, "rb") as f:
18
+ return tomllib.load(f)
19
+
20
+ raise FileNotFoundError(f"{config_name} not found in any parent directory")
@@ -0,0 +1,20 @@
1
+ from pathlib import Path
2
+ from dotenv import load_dotenv
3
+
4
+
5
+ def load_env_from_root(env_subpath: str = "ENVIRONMENT/.env") -> Path:
6
+ """
7
+ Search upward from this file to find ENVIRONMENT/.env and load it.
8
+
9
+ Returns the path of the loaded env file.
10
+ """
11
+
12
+ current = Path(__file__).resolve()
13
+
14
+ for parent in [current] + list(current.parents):
15
+ env_path = parent / env_subpath
16
+ if env_path.exists():
17
+ load_dotenv(env_path)
18
+ return env_path
19
+
20
+ raise FileNotFoundError(f"{env_subpath} not found in any parent directory")
@@ -0,0 +1,48 @@
1
+ import logging
2
+ from pathlib import Path
3
+ from datetime import datetime
4
+
5
+
6
+ def setup_logger(
7
+ name: str = "app_logger",
8
+ log_dir: str = "logs",
9
+ base_filename: str = "app",
10
+ level: int = logging.INFO,
11
+ ) -> logging.Logger:
12
+ """
13
+ Configure logger with timestamped log filename and datetime in messages.
14
+ """
15
+
16
+ logger = logging.getLogger(name)
17
+ logger.setLevel(level)
18
+
19
+ # Prevent duplicate handlers if called multiple times
20
+ if logger.handlers:
21
+ return logger
22
+
23
+ # ---- create timestamped filename ----
24
+ timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
25
+ project_root = Path(__file__).resolve().parents[2]
26
+ log_path = project_root / "logs"
27
+ print(f"Log file will be saved to: {log_path / f'{base_filename}_{timestamp}.log'}")
28
+ log_path.mkdir(parents=True, exist_ok=True)
29
+
30
+ log_file = log_path / f"{base_filename}_{timestamp}.log"
31
+
32
+ # ---- formatter with datetime ----
33
+ formatter = logging.Formatter(
34
+ fmt="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
35
+ datefmt="%Y-%m-%d %H:%M:%S",
36
+ )
37
+
38
+ # ---- console handler ----
39
+ ch = logging.StreamHandler()
40
+ ch.setFormatter(formatter)
41
+ logger.addHandler(ch)
42
+
43
+ # ---- file handler ----
44
+ fh = logging.FileHandler(log_file)
45
+ fh.setFormatter(formatter)
46
+ logger.addHandler(fh)
47
+
48
+ return logger
@@ -0,0 +1,384 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": null,
6
+ "id": "530631d3",
7
+ "metadata": {},
8
+ "outputs": [],
9
+ "source": [
10
+ "#uv add ipykernel\n",
11
+ "#uv run -m ipykernel install --user --name=.venv --display-name \"Python (.venv)\"\n",
12
+ "\n",
13
+ "## The code above is used to add the current virtual environment as a kernel in Jupyter Notebook.\n",
14
+ "## Run the first command in a terminal or command prompt to add ipykernel to your virtual environment, and then run the second command to install the kernel with a specific name and display name. This allows you to select this virtual environment as a kernel when working in Jupyter Notebook."
15
+ ]
16
+ },
17
+ {
18
+ "cell_type": "code",
19
+ "execution_count": null,
20
+ "id": "a585775f",
21
+ "metadata": {},
22
+ "outputs": [],
23
+ "source": [
24
+ "%load_ext autoreload\n",
25
+ "%autoreload 2\n",
26
+ "\n",
27
+ "### the above code is used to automatically reload modules in Jupyter Notebook when they are modified. This is useful for development and testing, as it allows you to see changes in your code without having to restart the kernel or manually reload the modules. The `%load_ext autoreload` command loads the autoreload extension, and `%autoreload 2` sets the autoreload mode to automatically reload all modules (except those excluded by `%aimport`) every time before executing a new line of code."
28
+ ]
29
+ },
30
+ {
31
+ "cell_type": "markdown",
32
+ "id": "abd53d26-89fa-4cfc-94fd-a93f30d73151",
33
+ "metadata": {},
34
+ "source": [
35
+ "# INTRODUCTION ANALYSIS OF DATA"
36
+ ]
37
+ },
38
+ {
39
+ "cell_type": "code",
40
+ "execution_count": null,
41
+ "id": "5fedeefb-c662-42a3-8a5f-9e29376685ba",
42
+ "metadata": {},
43
+ "outputs": [],
44
+ "source": []
45
+ },
46
+ {
47
+ "cell_type": "markdown",
48
+ "id": "58680534-ea5d-4ebb-803d-1ee09c6a0e1b",
49
+ "metadata": {},
50
+ "source": [
51
+ "# IMPORT LIBRARIES AND CODES FROM SRC"
52
+ ]
53
+ },
54
+ {
55
+ "cell_type": "code",
56
+ "execution_count": null,
57
+ "id": "883ba69b-2973-48ea-b7e2-70b342e2c42d",
58
+ "metadata": {},
59
+ "outputs": [],
60
+ "source": []
61
+ },
62
+ {
63
+ "cell_type": "markdown",
64
+ "id": "1a58ac2e-aa62-4963-a695-a0fa5302d3bd",
65
+ "metadata": {
66
+ "jp-MarkdownHeadingCollapsed": true
67
+ },
68
+ "source": [
69
+ "# IMPORT THE DATA"
70
+ ]
71
+ },
72
+ {
73
+ "cell_type": "code",
74
+ "execution_count": null,
75
+ "id": "1a95314a-23ed-4663-8a6a-f8cbe46e47d6",
76
+ "metadata": {},
77
+ "outputs": [],
78
+ "source": []
79
+ },
80
+ {
81
+ "cell_type": "markdown",
82
+ "id": "fcfbfeb5-cd8e-4be9-aa24-5155a5a5ebeb",
83
+ "metadata": {},
84
+ "source": [
85
+ "# VALIDATE/EDA OF THE DATA"
86
+ ]
87
+ },
88
+ {
89
+ "cell_type": "code",
90
+ "execution_count": null,
91
+ "id": "9c453db0-fc2c-485c-a448-79bcc35620ce",
92
+ "metadata": {},
93
+ "outputs": [],
94
+ "source": []
95
+ },
96
+ {
97
+ "cell_type": "markdown",
98
+ "id": "899ef0f2-18ce-4fdc-8e38-0a12c8b22464",
99
+ "metadata": {},
100
+ "source": [
101
+ "# CLEANING"
102
+ ]
103
+ },
104
+ {
105
+ "cell_type": "code",
106
+ "execution_count": null,
107
+ "id": "369813bf-5d91-4ed2-ad02-a00a9ffdfa68",
108
+ "metadata": {},
109
+ "outputs": [],
110
+ "source": []
111
+ },
112
+ {
113
+ "cell_type": "markdown",
114
+ "id": "73d24e6d-503c-4cfd-8686-7548bbcd0401",
115
+ "metadata": {},
116
+ "source": [
117
+ "# VISUALIZATION"
118
+ ]
119
+ },
120
+ {
121
+ "cell_type": "code",
122
+ "execution_count": null,
123
+ "id": "6e7d2c3d-de56-4f28-aeb6-93d489051fe9",
124
+ "metadata": {},
125
+ "outputs": [],
126
+ "source": []
127
+ },
128
+ {
129
+ "cell_type": "markdown",
130
+ "id": "0c082268-f0b8-496c-85f0-1b0c4d06f8c8",
131
+ "metadata": {
132
+ "jp-MarkdownHeadingCollapsed": true
133
+ },
134
+ "source": [
135
+ "## STATISTICAL ANALYSIS"
136
+ ]
137
+ },
138
+ {
139
+ "cell_type": "code",
140
+ "execution_count": null,
141
+ "id": "e689d695-eaa0-4697-b278-be8f9a250120",
142
+ "metadata": {},
143
+ "outputs": [],
144
+ "source": []
145
+ },
146
+ {
147
+ "cell_type": "code",
148
+ "execution_count": null,
149
+ "id": "aa4f7188-1903-4276-ae43-36fb65b911bd",
150
+ "metadata": {},
151
+ "outputs": [],
152
+ "source": []
153
+ },
154
+ {
155
+ "cell_type": "code",
156
+ "execution_count": null,
157
+ "id": "35e9808b-0fdb-45f6-a3df-45778f684a91",
158
+ "metadata": {},
159
+ "outputs": [],
160
+ "source": []
161
+ },
162
+ {
163
+ "cell_type": "code",
164
+ "execution_count": null,
165
+ "id": "be07857e-8425-4bdb-80d8-b6d53e23c2cb",
166
+ "metadata": {},
167
+ "outputs": [],
168
+ "source": []
169
+ },
170
+ {
171
+ "cell_type": "markdown",
172
+ "id": "1c4d04c1-e619-464a-8781-c764a403e5e5",
173
+ "metadata": {},
174
+ "source": [
175
+ "## MODELLING AND/OR FORECASTING"
176
+ ]
177
+ },
178
+ {
179
+ "cell_type": "markdown",
180
+ "id": "06415f8a-e1ad-4faf-ab24-1708915d7cd5",
181
+ "metadata": {},
182
+ "source": [
183
+ "### PREPROCESSING"
184
+ ]
185
+ },
186
+ {
187
+ "cell_type": "code",
188
+ "execution_count": null,
189
+ "id": "db6408f5-e3db-4441-a5bd-5256c07fdf48",
190
+ "metadata": {},
191
+ "outputs": [],
192
+ "source": []
193
+ },
194
+ {
195
+ "cell_type": "markdown",
196
+ "id": "8ce3a1ba-98e2-4957-9689-83cfa627b599",
197
+ "metadata": {},
198
+ "source": [
199
+ "### FEATURE ENGINEERING"
200
+ ]
201
+ },
202
+ {
203
+ "cell_type": "code",
204
+ "execution_count": null,
205
+ "id": "cb311c49-2c67-47ce-9146-89d84cd569c2",
206
+ "metadata": {},
207
+ "outputs": [],
208
+ "source": []
209
+ },
210
+ {
211
+ "cell_type": "markdown",
212
+ "id": "1495385c-9fc1-4175-8006-d58189db5db2",
213
+ "metadata": {},
214
+ "source": [
215
+ "### MODEL TRAINING"
216
+ ]
217
+ },
218
+ {
219
+ "cell_type": "code",
220
+ "execution_count": null,
221
+ "id": "de9b8db8-57b9-4370-bb25-943221485c6c",
222
+ "metadata": {},
223
+ "outputs": [],
224
+ "source": []
225
+ },
226
+ {
227
+ "cell_type": "code",
228
+ "execution_count": null,
229
+ "id": "50f3cb28-d654-48ac-b529-f7e41e37bd73",
230
+ "metadata": {},
231
+ "outputs": [],
232
+ "source": []
233
+ },
234
+ {
235
+ "cell_type": "markdown",
236
+ "id": "b9e435c9-42a3-4f50-ab59-09b99b6cf759",
237
+ "metadata": {},
238
+ "source": [
239
+ "### PERFORMANCE EVALUATION"
240
+ ]
241
+ },
242
+ {
243
+ "cell_type": "code",
244
+ "execution_count": null,
245
+ "id": "1c16a623-ac1b-454b-95b5-258e93f1c860",
246
+ "metadata": {},
247
+ "outputs": [],
248
+ "source": []
249
+ },
250
+ {
251
+ "cell_type": "code",
252
+ "execution_count": null,
253
+ "id": "579b2748-0cfb-4eec-8ce0-9ef3771f2c74",
254
+ "metadata": {},
255
+ "outputs": [],
256
+ "source": []
257
+ },
258
+ {
259
+ "cell_type": "markdown",
260
+ "id": "e806bf62-16d5-498e-b2b1-ddf85abc233c",
261
+ "metadata": {},
262
+ "source": [
263
+ "### PREDICTION"
264
+ ]
265
+ },
266
+ {
267
+ "cell_type": "code",
268
+ "execution_count": null,
269
+ "id": "3a855cee-98ab-4b7a-9b75-7f5819e06bb1",
270
+ "metadata": {},
271
+ "outputs": [],
272
+ "source": []
273
+ },
274
+ {
275
+ "cell_type": "code",
276
+ "execution_count": null,
277
+ "id": "cd0badb5-511f-4864-8f09-191e20aeac51",
278
+ "metadata": {},
279
+ "outputs": [],
280
+ "source": []
281
+ },
282
+ {
283
+ "cell_type": "markdown",
284
+ "id": "97f94129-2514-443f-bc0b-4f0e0c39f8db",
285
+ "metadata": {},
286
+ "source": [
287
+ "### SAVE THE MODEL"
288
+ ]
289
+ },
290
+ {
291
+ "cell_type": "code",
292
+ "execution_count": null,
293
+ "id": "5ef3ab9e-2d76-41d4-9243-e32aa8d97a61",
294
+ "metadata": {},
295
+ "outputs": [],
296
+ "source": []
297
+ },
298
+ {
299
+ "cell_type": "code",
300
+ "execution_count": null,
301
+ "id": "bed9b348-1185-4be7-a51d-b2f100ca0bf4",
302
+ "metadata": {},
303
+ "outputs": [],
304
+ "source": []
305
+ },
306
+ {
307
+ "cell_type": "code",
308
+ "execution_count": null,
309
+ "id": "87c3ab8d-be4d-454b-ae0b-0856dec7b327",
310
+ "metadata": {},
311
+ "outputs": [],
312
+ "source": []
313
+ },
314
+ {
315
+ "cell_type": "code",
316
+ "execution_count": null,
317
+ "id": "1bed1e08-6a8e-4bcc-a333-af7aa6e909df",
318
+ "metadata": {},
319
+ "outputs": [],
320
+ "source": []
321
+ },
322
+ {
323
+ "cell_type": "code",
324
+ "execution_count": null,
325
+ "id": "f3d2aea7-5e4e-4c6d-8a71-e11c454e25c3",
326
+ "metadata": {},
327
+ "outputs": [],
328
+ "source": []
329
+ },
330
+ {
331
+ "cell_type": "code",
332
+ "execution_count": null,
333
+ "id": "a9810bee-c9af-4de9-a617-a73ad651c170",
334
+ "metadata": {},
335
+ "outputs": [],
336
+ "source": []
337
+ },
338
+ {
339
+ "cell_type": "code",
340
+ "execution_count": null,
341
+ "id": "02a8d509-6651-4fbc-b75f-83c3588c9493",
342
+ "metadata": {},
343
+ "outputs": [],
344
+ "source": []
345
+ },
346
+ {
347
+ "cell_type": "code",
348
+ "execution_count": null,
349
+ "id": "67e5117d-e4c8-4b70-b4b6-902bbae5bc45",
350
+ "metadata": {},
351
+ "outputs": [],
352
+ "source": []
353
+ },
354
+ {
355
+ "cell_type": "code",
356
+ "execution_count": null,
357
+ "id": "92dc84de-7353-4e62-9a86-69e553f52fe0",
358
+ "metadata": {},
359
+ "outputs": [],
360
+ "source": []
361
+ }
362
+ ],
363
+ "metadata": {
364
+ "kernelspec": {
365
+ "display_name": "Python 3 (ipykernel)",
366
+ "language": "python",
367
+ "name": "python3"
368
+ },
369
+ "language_info": {
370
+ "codemirror_mode": {
371
+ "name": "ipython",
372
+ "version": 3
373
+ },
374
+ "file_extension": ".py",
375
+ "mimetype": "text/x-python",
376
+ "name": "python",
377
+ "nbconvert_exporter": "python",
378
+ "pygments_lexer": "ipython3",
379
+ "version": "3.12.7"
380
+ }
381
+ },
382
+ "nbformat": 4,
383
+ "nbformat_minor": 5
384
+ }
File without changes
File without changes