winidjango 1.0.4__py3-none-any.whl → 2.0.31__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.
winidjango/__init__.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """__init__ module."""
2
2
 
3
3
  import logging
4
+ from pathlib import Path
4
5
 
5
6
  import django
6
7
  import django_stubs_ext
@@ -11,8 +12,14 @@ logger = logging.getLogger(__name__)
11
12
  django_stubs_ext.monkeypatch()
12
13
  logger.info("Monkeypatched django-stubs")
13
14
 
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ # Configure Django settings for tests if not already configured
14
20
  if not settings.configured:
15
- logger.info("Configuring minimal django settings")
21
+ logger.info("Configuring minimal django settings for tests")
22
+ installed_apps = ["tests"] if Path("tests").exists() else []
16
23
  settings.configure(
17
24
  DATABASES={
18
25
  "default": {
@@ -20,6 +27,8 @@ if not settings.configured:
20
27
  "NAME": ":memory:",
21
28
  }
22
29
  },
23
- INSTALLED_APPS=["tests"],
30
+ INSTALLED_APPS=installed_apps,
31
+ USE_TZ=True,
24
32
  )
25
33
  django.setup()
34
+ logger.info("Django setup complete")
@@ -2,3 +2,17 @@
2
2
 
3
3
  All subclasses of ConfigFile in the configs package are automatically called.
4
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."""
winidjango/main.py CHANGED
@@ -1,18 +1,8 @@
1
1
  """Main entrypoint for the project."""
2
2
 
3
- import pyrig
4
-
5
3
 
6
4
  def main() -> None:
7
5
  """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
- `poetry run your-pkg-name main`
12
- or via
13
- `python -m your-pkg-name`.
14
- """
15
- raise NotImplementedError(msg)
16
6
 
17
7
 
18
8
  if __name__ == "__main__":
@@ -0,0 +1 @@
1
+ """__init__ module."""
@@ -1,3 +1,14 @@
1
- """src package."""
1
+ """src package.
2
2
 
3
- """__init__ module."""
3
+ This package exposes the project's internal modules used by the
4
+ command-line utilities and database helpers. It exists primarily so
5
+ that code under `winidjango/src` can be imported using the
6
+ `winidjango.src` package path in other modules and tests.
7
+
8
+ The package itself contains the following subpackages:
9
+ - `commands` - management command helpers and base classes
10
+ - `db` - database utilities and model helpers
11
+
12
+ Consumers should import the specific submodules they need rather than
13
+ relying on side effects from this package's import-time execution.
14
+ """
@@ -1 +1,8 @@
1
- """__init__ module."""
1
+ """Utilities and base classes for management commands.
2
+
3
+ The `commands` package contains base command classes and helpers used to
4
+ implement Django management commands in the project. Subpackages and
5
+ modules under `commands` provide reusable patterns for argument handling,
6
+ logging and common command behaviors so that individual commands can focus
7
+ on their business logic.
8
+ """
@@ -1 +1,7 @@
1
- """__init__ module."""
1
+ """Base helpers and abstractions for management commands.
2
+
3
+ This package provides a common abstract base class used by project
4
+ management commands. The base class centralizes argument handling,
5
+ standard options (dry-run, batching, timeouts, etc.) and integrates
6
+ logging behavior used throughout the commands package.
7
+ """
@@ -1,8 +1,11 @@
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.
1
+ """Utilities and an abstract base class for Django management commands.
2
+
3
+ This module defines :class:`ABCBaseCommand`, a reusable abstract base
4
+ that combines Django's ``BaseCommand`` with the project's logging
5
+ mixins and standard argument handling. The base class implements a
6
+ template method pattern so concrete commands only need to implement the
7
+ abstract extension points for providing command-specific arguments and
8
+ business logic.
6
9
  """
7
10
 
8
11
  import logging
@@ -17,41 +20,19 @@ logger = logging.getLogger(__name__)
17
20
 
18
21
 
19
22
  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
23
+ """Abstract base class for management commands with logging and standard options.
24
+
25
+ The class wires common behavior such as base arguments (dry-run,
26
+ batching, timeouts) and provides extension points that concrete
27
+ commands must implement: :meth:`add_command_arguments` and
28
+ :meth:`handle_command`.
29
+
30
+ Notes:
31
+ - Inheritance order matters: the logging mixin must precede
32
+ ``BaseCommand`` so mixin initialization occurs as expected.
33
+ - The base class follows the template method pattern; concrete
34
+ commands should not override :meth:`add_arguments` or
35
+ :meth:`handle` but implement the abstract hooks instead.
55
36
  """
56
37
 
57
38
  class Options:
@@ -67,25 +48,14 @@ class ABCBaseCommand(ABCLoggingMixin, BaseCommand):
67
48
  PROCESSES = "processes"
68
49
 
69
50
  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.
51
+ """Configure command-line arguments for the command.
75
52
 
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.
53
+ Adds common base arguments (dry-run, force, delete, timeout,
54
+ batching and concurrency options) and then delegates to
55
+ :meth:`add_command_arguments` for command-specific options.
79
56
 
80
57
  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
58
+ parser (ArgumentParser): The argument parser passed by Django.
89
59
  """
90
60
  # add base args that are used in most commands
91
61
  self.base_add_arguments(parser)
@@ -94,24 +64,10 @@ class ABCBaseCommand(ABCLoggingMixin, BaseCommand):
94
64
  self.add_command_arguments(parser)
95
65
 
96
66
  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.
67
+ """Add the project's standard command-line arguments to ``parser``.
106
68
 
107
69
  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()
70
+ parser (ArgumentParser): The argument parser passed by Django.
115
71
  """
116
72
  parser.add_argument(
117
73
  f"--{self.Options.DRY_RUN}",
@@ -168,138 +124,61 @@ class ABCBaseCommand(ABCLoggingMixin, BaseCommand):
168
124
 
169
125
  @abstractmethod
170
126
  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.
127
+ """Define command-specific arguments.
177
128
 
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.
129
+ Implement this hook to add options and positional arguments that are
130
+ specific to the concrete management command.
181
131
 
182
132
  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
133
+ parser (ArgumentParser): The argument parser passed by Django.
205
134
  """
206
135
 
207
136
  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().
137
+ """Orchestrate command execution.
213
138
 
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.
139
+ Performs shared pre-processing by calling :meth:`base_handle` and
140
+ then delegates to :meth:`handle_command` which must be implemented
141
+ by subclasses.
217
142
 
218
143
  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
144
+ *args: Positional arguments forwarded from Django.
145
+ **options: Parsed command-line options.
228
146
  """
229
147
  self.base_handle(*args, **options)
230
148
  self.handle_command()
231
149
 
232
150
  def base_handle(self, *args: Any, **options: Any) -> None:
233
- """Execute common handling logic shared across all commands.
151
+ """Perform common pre-processing for commands.
234
152
 
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.
153
+ Stores the incoming arguments and options on the instance for use
154
+ by :meth:`handle_command` and subclasses.
243
155
 
244
156
  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
157
+ *args: Positional arguments forwarded from Django.
158
+ **options: Parsed command-line options.
254
159
  """
255
160
  self.args = args
256
161
  self.options = options
257
162
 
258
163
  @abstractmethod
259
164
  def handle_command(self) -> None:
260
- """Execute command-specific logic and functionality.
165
+ """Run the command-specific behavior.
261
166
 
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.
167
+ This abstract hook should be implemented by concrete commands to
168
+ perform the command's main work. Implementations should read
169
+ ``self.args`` and ``self.options`` which were set in
170
+ :meth:`base_handle`.
171
+ """
266
172
 
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.
173
+ def get_option(self, option: str) -> Any:
174
+ """Retrieve a parsed command option by key.
270
175
 
271
176
  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
- """
177
+ option (str): The option key to retrieve from ``self.options``.
302
178
 
303
- def get_option(self, option: str) -> Any:
304
- """Get an option from the command options."""
179
+ Returns:
180
+ Any: The value for the requested option. If the option is not
181
+ present a ``KeyError`` will be raised (matching how Django
182
+ exposes options in management commands).
183
+ """
305
184
  return self.options[option]
@@ -1,6 +1,20 @@
1
- """Import command for Django.
2
-
3
- A base class for importing data from a source to the database.
1
+ """Import command base class and utilities.
2
+
3
+ This module defines a reusable base command for importing tabular data
4
+ into Django models. Implementations should provide a concrete source
5
+ ingestion (for example, reading from CSV or an external API), a
6
+ cleaning/normalization step implemented by a `CleaningDF` subclass, and
7
+ mapping logic that groups cleaned data into model instances that can be
8
+ bulk-created.
9
+
10
+ The base command centralizes the typical flow:
11
+ 1. Read raw data (``handle_import``)
12
+ 2. Wrap and clean the data using a `CleaningDF` subclass
13
+ 3. Convert the cleaned frame into per-model bulks
14
+ 4. Persist bulks using the project's bulk create helpers
15
+
16
+ Using this base class ensures a consistent import lifecycle and
17
+ reduces duplicated boilerplate across different import implementations.
4
18
  """
5
19
 
6
20
  import logging
@@ -18,49 +32,72 @@ logger = logging.getLogger(__name__)
18
32
 
19
33
 
20
34
  class ImportDataBaseCommand(ABCBaseCommand):
21
- """Base class for importing data from a source to the database.
35
+ """Abstract base for data-import Django management commands.
36
+
37
+ Subclasses must implement the ingestion, cleaning-class selection,
38
+ and mapping of cleaned rows to Django model instances. The base
39
+ implementation wires these pieces together and calls the project's
40
+ bulk creation helper to persist the data.
22
41
 
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.
42
+ Implementors typically only need to override the three abstract
43
+ methods documented below.
25
44
  """
26
45
 
27
46
  @abstractmethod
28
47
  def handle_import(self) -> pl.DataFrame:
29
- """Handle importing the data from the source.
48
+ """Read raw data from the import source.
30
49
 
31
- The data is possibly dirty and the job of this class to standardize the process
32
- of importing the data.
50
+ This method should read data from whatever source the concrete
51
+ command targets (files, remote APIs, etc.) and return it as a
52
+ ``polars.DataFrame``. No cleaning should be performed here;
53
+ cleaning is handled by the cleaning `CleaningDF` returned from
54
+ ``get_cleaning_df_cls``.
55
+
56
+ Returns:
57
+ pl.DataFrame: Raw (uncleaned) tabular data to be cleaned and
58
+ mapped to model instances.
33
59
  """
34
60
 
35
61
  @abstractmethod
36
62
  def get_cleaning_df_cls(self) -> type[CleaningDF]:
37
- """You will define a child of a cleaning df cls and return it.
63
+ """Return the `CleaningDF` subclass used to normalize the data.
64
+
65
+ The returned class will be instantiated with the raw DataFrame
66
+ returned from :meth:`handle_import` and must provide the
67
+ transformations required to prepare data for mapping into model
68
+ instances.
38
69
 
39
- The cleaning df cls is responsible for cleaning the data.
40
- See: winiutils.src.data.dataframe.cleaning.CleaningDF
70
+ Returns:
71
+ type[CleaningDF]: A subclass of ``CleaningDF`` that performs
72
+ the necessary normalization and validation.
41
73
  """
42
74
 
43
75
  @abstractmethod
44
76
  def get_bulks_by_model(
45
77
  self, df: pl.DataFrame
46
78
  ) -> dict[type[Model], Iterable[Model]]:
47
- """Get the bulks of data to import by model.
79
+ """Map the cleaned DataFrame to model-instance bulks.
48
80
 
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.
81
+ The implementation should inspect the cleaned DataFrame and
82
+ return a mapping where keys are Django model classes and values
83
+ are iterables of unsaved model instances (or dataclass-like
84
+ objects accepted by the project's bulk-creation utility).
52
85
 
53
86
  Args:
54
- df (pl.DataFrame): The cleaned data to import.
55
- Is passed from handle_command() automatically.
87
+ df (pl.DataFrame): The cleaned and normalized DataFrame.
88
+
89
+ Returns:
90
+ dict[type[Model], Iterable[Model]]: Mapping from model classes
91
+ to iterables of instances that should be created.
56
92
  """
57
93
 
58
94
  def handle_command(self) -> None:
59
- """Execute the import command.
95
+ """Execute the full import lifecycle.
60
96
 
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.
97
+ This template method reads raw data via :meth:`handle_import`,
98
+ wraps it with the cleaning class returned by
99
+ :meth:`get_cleaning_df_cls` and then persists the resulting
100
+ model bulks returned by :meth:`get_bulks_by_model`.
64
101
  """
65
102
  data_df = self.handle_import()
66
103
 
@@ -70,7 +107,11 @@ class ImportDataBaseCommand(ABCBaseCommand):
70
107
  self.import_to_db()
71
108
 
72
109
  def import_to_db(self) -> None:
73
- """Import the cleaned data to the database."""
110
+ """Persist prepared model bulks to the database.
111
+
112
+ Calls the project's `bulk_create_bulks_in_steps` helper with the
113
+ mapping returned from :meth:`get_bulks_by_model`.
114
+ """
74
115
  bulks_by_model = self.get_bulks_by_model(df=self.cleaning_df.df)
75
116
 
76
117
  bulk_create_bulks_in_steps(bulks_by_model)
@@ -1 +1,7 @@
1
- """__init__ module."""
1
+ """Database utilities and common model helpers used in the project.
2
+
3
+ This package contains helpers for working with Django models, such as
4
+ topological sorting of model classes according to foreign-key
5
+ dependencies, a lightweight model hashing helper, and a project-wide
6
+ ``BaseModel`` that adds common timestamp fields.
7
+ """