winidjango 2.0.11__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.

Potentially problematic release.


This version of winidjango might be problematic. Click here for more details.

winidjango/__init__.py ADDED
@@ -0,0 +1,34 @@
1
+ """__init__ module."""
2
+
3
+ import logging
4
+ from pathlib import Path
5
+
6
+ import django
7
+ import django_stubs_ext
8
+ from django.conf import settings
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ django_stubs_ext.monkeypatch()
13
+ logger.info("Monkeypatched django-stubs")
14
+
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ # Configure Django settings for tests if not already configured
20
+ if not settings.configured:
21
+ logger.info("Configuring minimal django settings for tests")
22
+ installed_apps = ["tests"] if Path("tests").exists() else []
23
+ settings.configure(
24
+ DATABASES={
25
+ "default": {
26
+ "ENGINE": "django.db.backends.sqlite3",
27
+ "NAME": ":memory:",
28
+ }
29
+ },
30
+ INSTALLED_APPS=installed_apps,
31
+ USE_TZ=True,
32
+ )
33
+ django.setup()
34
+ logger.info("Django setup complete")
@@ -0,0 +1 @@
1
+ """__init__ module."""
@@ -0,0 +1 @@
1
+ """__init__ module."""
@@ -0,0 +1 @@
1
+ """__init__ module."""
@@ -0,0 +1 @@
1
+ """__init__ module."""
@@ -0,0 +1 @@
1
+ """__init__ module."""
@@ -0,0 +1,6 @@
1
+ """Subcommands for the CLI.
2
+
3
+ They will be automatically imported and added to the CLI
4
+ IMPORTANT: All funcs in this file will be added as subcommands.
5
+ So best to define the logic elsewhere and just call it here in a wrapper.
6
+ """
@@ -0,0 +1 @@
1
+ """__init__ module."""
@@ -0,0 +1,18 @@
1
+ """Configs for pyrig.
2
+
3
+ All subclasses of ConfigFile in the configs package are automatically called.
4
+ """
5
+
6
+ from pyrig.dev.configs.pyproject import PyprojectConfigFile as PyrigPyprojectConfigFile
7
+
8
+
9
+ class PyprojectConfigFile(PyrigPyprojectConfigFile):
10
+ """Pyproject.toml config file."""
11
+
12
+ @classmethod
13
+ def get_standard_dev_dependencies(cls) -> list[str]:
14
+ """Get the standard dev dependencies."""
15
+ dev_dependencies = super().get_standard_dev_dependencies()
16
+ dev_dependencies.extend(["django-stubs", "pytest-django"])
17
+
18
+ return sorted(dev_dependencies)
@@ -0,0 +1 @@
1
+ """__init__ module."""
@@ -0,0 +1 @@
1
+ """__init__ module."""
@@ -0,0 +1,7 @@
1
+ """Fixtures for testing.
2
+
3
+ This module provides custom fixtures for pytest that can be pluued into tests
4
+ across the entire test suite.
5
+ All fixtures defined under the fixtures package are auto plugged in automatically
6
+ by pyrig via the pytest_plugins mechanism.
7
+ """
@@ -0,0 +1 @@
1
+ """__init__ module."""
@@ -0,0 +1,8 @@
1
+ """Class-level test fixtures and utilities.
2
+
3
+ These fixtures in this module are automatically applied to all test classes
4
+ through pytest's autouse mechanism. Pyrig automatically adds this module to
5
+ pytest_plugins in conftest.py. However you still have decorate the fixture
6
+ with @autouse_class_fixture from pyrig.src.testing.fixtures or with pytest's
7
+ autouse mechanism @pytest.fixture(scope="class", autouse=True).
8
+ """
@@ -0,0 +1,8 @@
1
+ """Function-level test fixtures and utilities.
2
+
3
+ These fixtures in this module are automatically applied to all test functions
4
+ through pytest's autouse mechanism. Pyrig automatically adds this module to
5
+ pytest_plugins in conftest.py. However you still have decorate the fixture
6
+ with @autouse_function_fixture from pyrig.src.testing.fixtures or with pytest's
7
+ autouse mechanism @pytest.fixture(scope="function", autouse=True).
8
+ """
@@ -0,0 +1,8 @@
1
+ """Module-level test fixtures and utilities.
2
+
3
+ These fixtures in this module are automatically applied to all test modules
4
+ through pytest's autouse mechanism. Pyrig automatically adds this module to
5
+ pytest_plugins in conftest.py. However you still have decorate the fixture
6
+ with @autouse_module_fixture from pyrig.src.testing.fixtures or with pytest's
7
+ autouse mechanism @pytest.fixture(scope="module", autouse=True).
8
+ """
@@ -0,0 +1,8 @@
1
+ """Package-level test fixtures and utilities.
2
+
3
+ These fixtures in this module are automatically applied to all test packages
4
+ through pytest's autouse mechanism. Pyrig automatically adds this module to
5
+ pytest_plugins in conftest.py. However you still have decorate the fixture
6
+ with @autouse_package_fixture from pyrig.src.testing.fixtures or with pytest's
7
+ autouse mechanism @pytest.fixture(scope="package", autouse=True).
8
+ """
@@ -0,0 +1,8 @@
1
+ """Session-level test fixtures and utilities.
2
+
3
+ These fixtures in this module are automatically applied to the test session
4
+ through pytest's autouse mechanism. Pyrig automatically adds this module to
5
+ pytest_plugins in conftest.py. However you still have decorate the fixture
6
+ with @autouse_session_fixture from pyrig.src.testing.fixtures or with pytest's
7
+ autouse mechanism @pytest.fixture(scope="session", autouse=True).
8
+ """
winidjango/main.py ADDED
@@ -0,0 +1,19 @@
1
+ """Main entrypoint for the project."""
2
+
3
+ import pyrig
4
+
5
+
6
+ def main() -> None:
7
+ """Main entrypoint for the project."""
8
+ msg = f"""Add your projects entrypoint code to this function.
9
+ This function is automatically added to your cli by {pyrig.__name__}.
10
+ You can call it with
11
+ `your-pkg-name main`
12
+ or via
13
+ `python -m your-pkg-name`.
14
+ """
15
+ raise NotImplementedError(msg)
16
+
17
+
18
+ if __name__ == "__main__":
19
+ main()
winidjango/py.typed ADDED
File without changes
@@ -0,0 +1 @@
1
+ """src package."""
@@ -0,0 +1 @@
1
+ """__init__ module."""
@@ -0,0 +1 @@
1
+ """__init__ module."""
@@ -0,0 +1,305 @@
1
+ """Command utilities for Django.
2
+
3
+ This module provides utility functions for working with Django commands,
4
+ including command execution and output handling. These utilities help with
5
+ managing and automating Django command-line tasks.
6
+ """
7
+
8
+ import logging
9
+ from abc import abstractmethod
10
+ from argparse import ArgumentParser
11
+ from typing import Any
12
+
13
+ from django.core.management import BaseCommand
14
+ from winiutils.src.oop.mixins.mixin import ABCLoggingMixin
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class ABCBaseCommand(ABCLoggingMixin, BaseCommand):
20
+ """Abstract base class for Django management commands with logging and validation.
21
+
22
+ This class serves as a foundation for creating Django management commands that
23
+ require abstract method implementation enforcement and automatic logging.
24
+ It combines Django's BaseCommand with ABCImplementationLoggingMixin to provide
25
+ both command functionality and development-time validation.
26
+
27
+ The class implements a template method pattern where common argument handling
28
+ and execution flow are managed by final methods, while specific implementations
29
+ are defined through abstract methods that subclasses must implement.
30
+
31
+ Key Features:
32
+ - Automatic logging of method calls with performance tracking
33
+ - Compile-time validation that all abstract methods are implemented
34
+ - Structured argument handling with base and custom arguments
35
+ - Template method pattern for consistent command execution flow
36
+
37
+ Inheritance Order:
38
+ The order of inheritance is critical: ABCImplementationLoggingMixin must
39
+ come before BaseCommand because Django's BaseCommand doesn't call
40
+ super().__init__(), so the mixin's metaclass initialization must happen
41
+ first to ensure proper class construction.
42
+
43
+ Example:
44
+ >>> class MyCommand(ABCBaseCommand):
45
+ ... def add_command_arguments(self, parser):
46
+ ... parser.add_argument('--my-option', help='Custom option')
47
+ ...
48
+ ... def handle_command(self, *args, **options):
49
+ ... self.stdout.write('Executing my command')
50
+
51
+ Note:
52
+ - All methods are automatically logged with performance tracking
53
+ - Subclasses must implement add_command_arguments and handle_command
54
+ - The @final decorator prevents overriding of template methods
55
+ """
56
+
57
+ class Options:
58
+ """Just a container class for hard coding the option keys."""
59
+
60
+ DRY_RUN = "dry_run"
61
+ FORCE = "force"
62
+ DELETE = "delete"
63
+ YES = "yes"
64
+ TIMEOUT = "timeout"
65
+ BATCH_SIZE = "batch_size"
66
+ THREADS = "threads"
67
+ PROCESSES = "processes"
68
+
69
+ def add_arguments(self, parser: ArgumentParser) -> None:
70
+ """Configure command-line arguments for the Django management command.
71
+
72
+ This method implements the template method pattern by first adding common
73
+ base arguments that are used across multiple commands, then delegating
74
+ to the abstract add_command_arguments method for command-specific arguments.
75
+
76
+ The @final decorator prevents subclasses from overriding this method,
77
+ ensuring consistent argument handling across all commands while still
78
+ allowing customization through the abstract method.
79
+
80
+ Args:
81
+ parser (ArgumentParser): Django's argument parser instance used to
82
+ define command-line options and arguments for the command.
83
+
84
+ Note:
85
+ - This method is final and cannot be overridden by subclasses
86
+ - Common arguments are added first via _add_arguments()
87
+ - Custom arguments are added via the abstract add_command_arguments()
88
+ - Subclasses must implement add_command_arguments() for specific needs
89
+ """
90
+ # add base args that are used in most commands
91
+ self.base_add_arguments(parser)
92
+
93
+ # add additional args that are specific to the command
94
+ self.add_command_arguments(parser)
95
+
96
+ def base_add_arguments(self, parser: ArgumentParser) -> None:
97
+ """Add common command-line arguments used across multiple commands.
98
+
99
+ This method defines base arguments that are commonly used across different
100
+ Django management commands. These arguments provide standard functionality
101
+ like dry-run mode, verbosity control, and batch processing options.
102
+
103
+ The method is final to ensure consistent base argument handling, while
104
+ command-specific arguments are handled through the abstract
105
+ add_command_arguments method.
106
+
107
+ Args:
108
+ parser (ArgumentParser): Django's argument parser instance to which
109
+ common arguments should be added.
110
+
111
+ Note:
112
+ - Provides standard arguments for dry-run, verbosity, and batch processing
113
+ - The @final decorator prevents subclasses from overriding this method
114
+ - Command-specific arguments should be added via add_command_arguments()
115
+ """
116
+ parser.add_argument(
117
+ f"--{self.Options.DRY_RUN}",
118
+ action="store_true",
119
+ help="Show what would be done without actually executing the changes",
120
+ )
121
+
122
+ parser.add_argument(
123
+ f"--{self.Options.FORCE}",
124
+ action="store_true",
125
+ help="Force an action in a command",
126
+ )
127
+
128
+ parser.add_argument(
129
+ f"--{self.Options.DELETE}",
130
+ action="store_true",
131
+ help="Deleting smth in a command",
132
+ )
133
+
134
+ parser.add_argument(
135
+ f"--{self.Options.YES}",
136
+ action="store_true",
137
+ help="Answer yes to all prompts",
138
+ default=False,
139
+ )
140
+
141
+ parser.add_argument(
142
+ f"--{self.Options.TIMEOUT}",
143
+ type=int,
144
+ help="Timeout for a command",
145
+ default=None,
146
+ )
147
+
148
+ parser.add_argument(
149
+ f"--{self.Options.BATCH_SIZE}",
150
+ type=int,
151
+ default=None,
152
+ help="Number of items to process in each batch",
153
+ )
154
+
155
+ parser.add_argument(
156
+ f"--{self.Options.THREADS}",
157
+ type=int,
158
+ default=None,
159
+ help="Number of threads to use for processing",
160
+ )
161
+
162
+ parser.add_argument(
163
+ f"--{self.Options.PROCESSES}",
164
+ type=int,
165
+ default=None,
166
+ help="Number of processes to use for processing",
167
+ )
168
+
169
+ @abstractmethod
170
+ def add_command_arguments(self, parser: ArgumentParser) -> None:
171
+ """Add command-specific arguments to the argument parser.
172
+
173
+ This abstract method must be implemented by subclasses to define
174
+ command-specific command-line arguments. It is called after common
175
+ base arguments are added, allowing each command to customize its
176
+ argument interface while maintaining consistent base functionality.
177
+
178
+ Subclasses should use this method to add arguments specific to their
179
+ command's functionality, such as file paths, configuration options,
180
+ or operational flags.
181
+
182
+ Args:
183
+ parser (ArgumentParser): Django's argument parser instance to which
184
+ command-specific arguments should be added.
185
+
186
+ Example:
187
+ >>> def add_command_arguments(self, parser):
188
+ ... parser.add_argument(
189
+ ... '--input-file',
190
+ ... type=str,
191
+ ... required=True,
192
+ ... help='Path to input file'
193
+ ... )
194
+ ... parser.add_argument(
195
+ ... '--output-format',
196
+ ... choices=['json', 'csv', 'xml'],
197
+ ... default='json',
198
+ ... help='Output format for results'
199
+ ... )
200
+
201
+ Note:
202
+ - This method is abstract and must be implemented by subclasses
203
+ - Called after _add_arguments() adds common base arguments
204
+ - Should focus on command-specific functionality only
205
+ """
206
+
207
+ def handle(self, *args: Any, **options: Any) -> None:
208
+ """Execute the Django management command using template method pattern.
209
+
210
+ This method implements the main execution flow for the command by first
211
+ calling common handling logic through _handle(), then delegating to
212
+ the command-specific implementation via handle_command().
213
+
214
+ The @final decorator ensures this execution pattern cannot be overridden,
215
+ maintaining consistent command execution flow while allowing customization
216
+ through the abstract handle_command method.
217
+
218
+ Args:
219
+ *args: Positional arguments passed from Django's command execution.
220
+ **options: Keyword arguments containing parsed command-line options
221
+ and their values as defined by add_arguments().
222
+
223
+ Note:
224
+ - This method is final and cannot be overridden by subclasses
225
+ - Common handling logic is executed first via _handle()
226
+ - Command-specific logic is executed via abstract handle_command()
227
+ - All method calls are automatically logged with performance tracking
228
+ """
229
+ self.base_handle(*args, **options)
230
+ self.handle_command()
231
+
232
+ def base_handle(self, *args: Any, **options: Any) -> None:
233
+ """Execute common handling logic shared across all commands.
234
+
235
+ This method is intended to contain common processing logic that should
236
+ be executed before command-specific handling. Currently, it serves as
237
+ a placeholder for future common functionality such as logging setup,
238
+ validation, or shared initialization.
239
+
240
+ The method is final to ensure consistent common handling across all
241
+ commands, while command-specific logic is handled through the abstract
242
+ handle_command method.
243
+
244
+ Args:
245
+ *args: Positional arguments passed from Django's command execution.
246
+ Currently unused but reserved for future common processing.
247
+ **options: Keyword arguments containing parsed command-line options.
248
+ Currently unused but reserved for future common processing.
249
+
250
+ Note:
251
+ - Examples might include logging setup, database connection validation, etc.
252
+ - The @final decorator prevents subclasses from overriding this method
253
+ - Called before handle_command() in the template method pattern
254
+ """
255
+ self.args = args
256
+ self.options = options
257
+
258
+ @abstractmethod
259
+ def handle_command(self) -> None:
260
+ """Execute command-specific logic and functionality.
261
+
262
+ This abstract method must be implemented by subclasses to define the
263
+ core functionality of the Django management command. It is called after
264
+ common handling logic is executed, allowing each command to implement
265
+ its specific business logic while benefiting from shared infrastructure.
266
+
267
+ This method should contain the main logic that the command is designed
268
+ to perform, such as data processing, database operations, file manipulation,
269
+ or any other command-specific tasks.
270
+
271
+ Args:
272
+ None, args and options are stored in self.args and self.options
273
+
274
+ Example:
275
+ >>> def handle_command(self):
276
+ ... args, options = self.args, self.options
277
+ ... input_file = options['input_file']
278
+ ... dry_run = options['dry_run'] # Base argument
279
+ ... batch_size = options['batch_size'] # Base argument
280
+ ... quiet = options['quiet'] # Base argument
281
+ ...
282
+ ... if dry_run:
283
+ ... self.stdout.write('Dry run mode - no changes will be made')
284
+ ...
285
+ ... if not quiet:
286
+ ... msg = f'Processing {input_file} in batches of {batch_size}'
287
+ ... self.stdout.write(msg)
288
+ ...
289
+ ... # Perform command-specific operations
290
+ ... self.process_file(input_file, batch_size, dry_run)
291
+ ...
292
+ ... if not quiet:
293
+ ... self.stdout.write('Command completed successfully')
294
+
295
+ Note:
296
+ - This method is abstract and must be implemented by subclasses
297
+ - Called after _handle() executes common logic
298
+ - Should contain the main functionality of the command
299
+ - All method calls are automatically logged with performance tracking
300
+ - Use self.stdout.write() for output instead of print()
301
+ """
302
+
303
+ def get_option(self, option: str) -> Any:
304
+ """Get an option from the command options."""
305
+ return self.options[option]
@@ -0,0 +1,76 @@
1
+ """Import command for Django.
2
+
3
+ A base class for importing data from a source to the database.
4
+ """
5
+
6
+ import logging
7
+ from abc import abstractmethod
8
+ from collections.abc import Iterable
9
+
10
+ import polars as pl
11
+ from django.db.models import Model
12
+ from winiutils.src.data.dataframe.cleaning import CleaningDF
13
+
14
+ from winidjango.src.commands.base.base import ABCBaseCommand
15
+ from winidjango.src.db.bulk import bulk_create_bulks_in_steps
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class ImportDataBaseCommand(ABCBaseCommand):
21
+ """Base class for importing data from a source to the database.
22
+
23
+ This class provides a standardized way to import data from a source to the database.
24
+ It uses the cleaning df cls to clean the data and then imports it to the database.
25
+ """
26
+
27
+ @abstractmethod
28
+ def handle_import(self) -> pl.DataFrame:
29
+ """Handle importing the data from the source.
30
+
31
+ The data is possibly dirty and the job of this class to standardize the process
32
+ of importing the data.
33
+ """
34
+
35
+ @abstractmethod
36
+ def get_cleaning_df_cls(self) -> type[CleaningDF]:
37
+ """You will define a child of a cleaning df cls and return it.
38
+
39
+ The cleaning df cls is responsible for cleaning the data.
40
+ See: winiutils.src.data.dataframe.cleaning.CleaningDF
41
+ """
42
+
43
+ @abstractmethod
44
+ def get_bulks_by_model(
45
+ self, df: pl.DataFrame
46
+ ) -> dict[type[Model], Iterable[Model]]:
47
+ """Get the bulks of data to import by model.
48
+
49
+ The data is cleaned and ready to be imported to the database.
50
+ You need to return a dictionary mapping model classes to lists of instances
51
+ to create.
52
+
53
+ Args:
54
+ df (pl.DataFrame): The cleaned data to import.
55
+ Is passed from handle_command() automatically.
56
+ """
57
+
58
+ def handle_command(self) -> None:
59
+ """Execute the import command.
60
+
61
+ This method handles the import process from start to finish.
62
+ It imports the data with handle_import(), cleans it with the cleaning df cls,
63
+ and then imports it to the database.
64
+ """
65
+ data_df = self.handle_import()
66
+
67
+ cleaning_df_cls = self.get_cleaning_df_cls()
68
+ self.cleaning_df = cleaning_df_cls(data_df)
69
+
70
+ self.import_to_db()
71
+
72
+ def import_to_db(self) -> None:
73
+ """Import the cleaned data to the database."""
74
+ bulks_by_model = self.get_bulks_by_model(df=self.cleaning_df.df)
75
+
76
+ bulk_create_bulks_in_steps(bulks_by_model)
@@ -0,0 +1 @@
1
+ """__init__ module."""