-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from magicalpuffin/v0.1
V0.1 Release
- Loading branch information
Showing
17 changed files
with
939 additions
and
209 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
VERSION = "0.0.7" | ||
VERSION = "0.1.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,4 @@ | ||
from pandas_to_pydantic.annotation_utils import expand_annotation, get_base_fields, get_list_fields # noqa: F401 | ||
from pandas_to_pydantic.annotation_utils import ( # noqa: F401 | ||
get_model_columns, | ||
) | ||
from pandas_to_pydantic.to_pydantic import dataframe_to_pydantic, get_root_list, serialize_dataframe # noqa: F401 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,73 +1,109 @@ | ||
import types | ||
from typing import Optional | ||
|
||
from pydantic import BaseModel | ||
from pydantic._internal._model_construction import ModelMetaclass | ||
|
||
|
||
def expand_annotation(model: ModelMetaclass) -> dict: | ||
class ModelColumns(BaseModel): | ||
""" | ||
Expands a pydantic model annotations into basic types. Recursively expands nested models. | ||
Describes model fields. Used when mapping Dataframe columns to fields. | ||
Args: | ||
model (ModelMetaclass): pydantic model | ||
Raises: | ||
TypeError: error if not pydantic model | ||
Returns: | ||
dict: key as annotation name, value as type | ||
BaseModel (_type_): Pydantic BaseModel | ||
""" | ||
if not model.__base__ == BaseModel: | ||
error_message = f"{model} is not a BaseModel" | ||
raise TypeError(error_message) | ||
|
||
annotations = model.__annotations__.copy() | ||
|
||
for key, field_type in annotations.items(): | ||
if isinstance(field_type, types.GenericAlias): | ||
# Only expanding lists | ||
if field_type.__origin__ == list: | ||
# Using lists to indicate list structure | ||
annotations[key] = [expand_annotation(field_type.__args__[0])] | ||
|
||
return annotations | ||
name: str | ||
id_column: Optional[str] | ||
base_columns: list[str] | ||
list_columns: list["ModelColumns"] | ||
child_columns: list["ModelColumns"] | ||
|
||
|
||
# TODO | ||
# Combine functionality with list field | ||
def get_base_fields(annotation: dict) -> list[str]: | ||
def get_model_columns( | ||
model: ModelMetaclass, id_column_map: Optional[dict[str, str]] = None, name: Optional[str] = None | ||
) -> ModelColumns: | ||
""" | ||
Gets fields with basic types | ||
Creates ModelColumns for a Pydantic BaseModel | ||
Args: | ||
annotation (dict): key as annotation name, value as type | ||
model (ModelMetaclass): Pydantic BaseModel class | ||
id_column_map (Optional[dict[str, str]], optional): Map of field names and unique ID. Necessary for identifying | ||
and structuring nested objects. Defaults to None. | ||
name (Optional[str], optional): For name field in ModelColumns. If None, uses model.__name__. Defaults to None. | ||
Raises: | ||
TypeError: Error if model is not a Pydantic BaseModel | ||
Returns: | ||
list[str]: key names that are not list type | ||
ModelColumns: ModelColumns generated for the model. | ||
""" | ||
base_fields = [] | ||
# TODO consider returning field name | ||
if not model.__base__ == BaseModel: | ||
error_message = f"{model} is not a BaseModel" | ||
raise TypeError(error_message) | ||
|
||
for k, v in annotation.items(): | ||
if not isinstance(v, list): | ||
base_fields.append(k) | ||
if id_column_map is None: | ||
id_column_map = {} | ||
if name is None: | ||
name = model.__name__ | ||
|
||
return base_fields | ||
id_column = id_column_map.get(name) | ||
annotations = model.__annotations__ | ||
|
||
base_columns = [] | ||
list_columns = [] | ||
child_columns = [] | ||
|
||
def get_list_fields(annotation: dict) -> list[str]: | ||
for field_name, field_type in annotations.items(): | ||
if isinstance(field_type, types.GenericAlias): | ||
if field_type.__origin__ == list: | ||
# TODO reevaluate passed in field name | ||
list_columns.append(get_model_columns(field_type.__args__[0], id_column_map, field_name)) | ||
elif isinstance(field_type, ModelMetaclass): | ||
if field_type.__base__ == BaseModel: | ||
child_columns.append(get_model_columns(field_type, id_column_map, field_name)) | ||
else: | ||
base_columns.append(field_name) | ||
|
||
return ModelColumns( | ||
name=name, | ||
id_column=id_column, | ||
base_columns=base_columns, | ||
list_columns=list_columns, | ||
child_columns=child_columns, | ||
) | ||
|
||
|
||
# TODO deprecated? | ||
def expand_annotation(model: ModelMetaclass) -> dict: | ||
""" | ||
Gets fields with list types | ||
Expands a pydantic model annotations into basic types. Recursively expands nested models. | ||
Args: | ||
annotation (dict): key as annotation name, value as type | ||
model (ModelMetaclass): pydantic model | ||
Raises: | ||
TypeError: error if not pydantic model | ||
Returns: | ||
list[str]: key names that are list type | ||
dict: key as annotation name, value as type | ||
""" | ||
list_fields = [] | ||
if not model.__base__ == BaseModel: | ||
error_message = f"{model} is not a BaseModel" | ||
raise TypeError(error_message) | ||
|
||
annotations = model.__annotations__.copy() | ||
|
||
for k, v in annotation.items(): | ||
if isinstance(v, list): | ||
list_fields.append(k) | ||
for field_name, field_type in annotations.items(): | ||
if isinstance(field_type, types.GenericAlias): | ||
# Expanding lists | ||
if field_type.__origin__ == list: | ||
# Using lists to indicate list structure | ||
annotations[field_name] = [expand_annotation(field_type.__args__[0])] | ||
elif isinstance(field_type, ModelMetaclass): | ||
# Expanding pydantic models | ||
if field_type.__base__ == BaseModel: | ||
annotations[field_name] = expand_annotation(field_type) | ||
|
||
return list_fields | ||
return annotations |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.