跳转到主要内容

未提供项目描述

项目描述

Pyredox - 一个基于Pydantic的Redox数据库

PyPI Info Python Version GitHub Workflow Status Coverage Info Black Code Style PyPI - Downloads PyPI - License

Pyredox是一个库,用于生成、接收和验证来自Redox的数据,Redox是一个“旨在连接提供商、支付方和产品的数据平台”。

Pyredox是一组符合Redox数据模型规范的Pydantic模型,目的是使其易于将Redox格式的JSON转换为Python对象,反之亦然。由于pyredox继承了Pydantic的功能,它会在对象创建时自动验证JSON数据是否符合规范。

例如,如果你尝试创建一个数据不足的 NewPatient 模型,你会得到这样的错误

>>> from pyredox.patientadmin.newpatient import NewPatient
>>> NewPatient(Meta={})

ValidationError: 3 validation errors for NewPatient
Meta -> DataModel
  field required (type=value_error.missing)
Meta -> EventType
  field required (type=value_error.missing)
Patient
  field required (type=value_error.missing)

用法

创建pyredox对象有两种主要方法

  1. JSON字典扩展:

    • 优点
      • 如果你已经有一个JSON字符串或字典(或字典列表)并希望获取相应的pyredox对象,那么它很简单。
      • 如果你知道JSON负载的Redox类型,或者不知道。
    • 缺点
      • 如果你自己编写或创建完整的JSON负载,可能会非常冗长(与处理接收到的负载相比)。
  2. 通用对象:

    • 优点
      • 非常可组合;子对象的对象可以与构建的事件类型模型分开创建。
    • 缺点
      • 直到您调用 to_redox()dict()json() 中的任一方法,才完全执行字段值与原始 Redox 架构的验证。

有关如何序列化对象的说明,请参阅下面的 序列化为 JSON 或 dict 部分。

JSON字典扩展

从 JSON 有效负载创建 pyredox 模型的最简单方法是,在初始化对象时将其作为未打包的 dict 参数传递,如下所示:

payload_str = """
{
   "Meta": {
      "DataModel": "PatientAdmin",
      "EventType": "NewPatient"
   },
   "Patient": {
      "Identifiers": [
         {
            "ID": "e167267c-16c9-4fe3-96ae-9cff5703e90a",
            "IDType": "EHRID"
         }
      ]
   }
}
"""
data = json.loads(payload_str)
new_patient = NewPatient(**data)

如果您有一个有效负载,但不知道它是哪种对象类型,可以使用工厂辅助工具,该工具可以接受 JSON 字符串或已加载的 JSON 字典/列表。

from pyredox.factory import redox_object_factory

redox_object1 = redox_object_factory(payload_str)  # str input
redox_object2 = redox_object_factory(data)  # dict input

要从现有的 pyredox 对象创建发送到 Redox 的 JSON 有效负载,只需调用对象的 json() 方法。

new_patient.json()

在处理模型对象的各个字段时,您可以像这样遍历元素属性:

new_patient.patient.identifiers[0].id  # "e167267c-16c9-4fe3-96ae-9cff5703e90a"

使用泛型

Redox 架构重新定义了每个位置使用的所有事件类型的每个属性。无论属性定义是否完全相同还是略有差异,都是这种情况。为了确保库中每个事件类型类都按照架构中定义的结构验证执行,所有“正确的 Redox”类(我对不在 generic 文件夹中的所有 Redox 对象的术语)都有自己的属性类定义,这些定义与架构相匹配。这意味着存在具有相同字段、位于同一 Python 文件中并属于同一事件类型的类。

例如,在 pyredox/provider/new.py 中,NewProviderRoleLocationAddressNewProviderRoleOrganizationAddress 类具有完全相同的定义,因为它们都是地址。然而,因为一个代表提供者角色的位置地址,另一个代表提供者角色的组织地址,Redox 将它们视为不同。相比之下,大多数事件类型的 Meta 属性具有类似但不同的字段,尽管它们都具有必需的 DataModelEventType 字段。

因此,编写一个可以构建来自多个数据源的 Redox 消息而不与其紧密耦合的程序变得相当困难。即使如此,代码也可能因为庞大的 Python 字典而变得难以控制。

解决方案是使用在 generic 目录中定义的事件类型类和属性类。因此,而不是像这样创建一个新的提供者:

# THIS IS THE HARDER WAY TO DO THINGS!
from pyredox.provider import New
from pyredox.provider.new import (
    NewMeta,
    NewProvider,
    NewProviderIdentifier,
    NewProviderRole,
    NewProviderRoleLocation,
    NewProviderRoleLocationAddress,
    NewProviderRoleOrganization,
    NewProviderRoleOrganizationAddress,
)

provider_org = NewProviderRoleOrganization(
    Address=NewProviderRoleOrganizationAddress(
        StreetAddress="123 Cherry St",
        City="Green Bay",
        State="Wisconsin",
        ZIP="54321",
        Country="USA",
    )
)
provider_loc1 = NewProviderRoleLocation(
    Address=NewProviderRoleLocationAddress(
        StreetAddress="123 Cherry St",
        City="Green Bay",
        State="Wisconsin",
        ZIP="54321",
        Country="USA",
    ),
)
provider_loc2 = NewProviderRoleLocation(
    Address=NewProviderRoleLocationAddress(
        StreetAddress="567 Splenda Way",
        City="Green Bay",
        State="Wisconsin",
        ZIP="54321",
        Country="USA",
    )
)
provider = NewProvider(
    Identifiers=[NewProviderIdentifier(ID="FakeProviderID")],
    IsActive=True,
    Roles=[
        NewProviderRole(
            Organization=provider_org,
            Locations=[provider_loc1, provider_loc2],
        )
    ],
)

new_provider_msg = New(
    Meta=NewMeta(DataModel="Provider", EventType="New", Test=True),
    Providers=[provider],
)

以下内容更易于组合,并且稍微简单一些:

# Simpler way to create a new Provider
from pyredox.generic import types as pyredox_types
from pyredox.generic.Provider import New as NewProvider

# Because office_address is a generic Address type, we can reuse it for both
# the Organization and the Location for this Provider.
office_address = pyredox_types.Address(
    StreetAddress="123 Cherry St",
    City="Green Bay",
    State="Wisconsin",
    ZIP="54321",
    Country="USA",
)
clinic_address = pyredox_types.Address(
    StreetAddress="567 Splenda Way",
    City="Green Bay",
    State="Wisconsin",
    ZIP="54321",
    Country="USA",
)
provider_org = pyredox_types.Organization(Address=office_address)
provider_loc1 = pyredox_types.Location(Address=office_address)
provider_loc2 = pyredox_types.Location(Address=clinic_address)
provider = pyredox_types.Provider(
    Identifiers=[pyredox_types.Identifier(ID="FakeProviderID")],
    IsActive=True,
    Roles=[
        pyredox_types.Role(
            Organization=provider_org,
            Locations=[provider_loc1, provider_loc2],
        )
    ],
)

new_provider_msg = NewProvider(
    Meta=pyredox_types.Meta(DataModel="Provider", EventType="New", Test=True),
    Providers=[provider],
).to_redox()  # This converts the object to a "proper Redox" model

这里需要注意的是,泛型事件类型类的 .dict().json() 方法会自动将数据转换为“正确的 Redox”形式,因此最后一句话也可以写成这样:

new_provider_json = NewProvider(
    Meta=pyredox_types.Meta(DataModel="Provider", EventType="New", Test=True),
    Providers=[provider],
).json()  # This converts the object to a "proper Redox" model, then gets the JSON string

通过使用泛型类型以组合方式构建 Redox 消息,您可能会引入在对象的泛型版本中可用但不在“正确的 Redox”模型中定义的字段。库的默认行为是静默地删除这些字段,目前没有计划使其可配置。

还有可能,您构建的“正确的 Redox”对象指定了字段的特定数据类型,这与使用该数据类型的其他模型不同,这是由架构的指定方式造成的。例如,泛型 Demographics 类具有以下字段定义:

EmailAddresses: Union[List["EmailAddress"], List[str]]

一些事件类型模型指定字符串列表,而另一些则需要 EmailAddress 对象。目前,检测此类类型不匹配的唯一方法是捕获 pydantic.ValidationError 异常,如下所示:

from pydantic import ValidationError

try:
    new_provider_json = NewProvider(
        Meta=pyredox_types.Meta(DataModel="Provider", EventType="New", Test=True),
        Providers=[provider],
    ).json()  # This converts the object to a "proper Redox" model
except ValidationError:
    # TODO: Handle the validation error here
    pass

序列化为 JSON 或 dict

所有 pyredox 对象都拥有允许轻松序列化的方法。

  • 对于对象的 dict 版本,调用 dict() 方法。
  • 对于对象的 JSON str 版本,调用 json() 方法。

为了自定义 pyredox 从模型导出数据的方式,您可以使用来自底层 Pydantic 模型的任何可用参数。有关这些参数的详细信息,请参阅此处。注意,在调用 json() 方法时,您还可以包含传递给 json.dumps() 的关键字参数。

在序列化泛型类型时,请注意,在返回序列化数据之前,pyredox 将对象转换为相应的“正确 Redox”。有关更多信息,请参见上面内容。

类型转换

每个 pyredox 对象都有一个 cast_from() 方法,用于在您需要将相同值分配给多个对象并避免任何类型检查错误时使用。例如,在泛型 Visit 对象中,有多个提供者字段,这些字段仅在提供者为访问提供哪种角色方面有所不同。如果同一提供者为多个角色提供服务,则在多个对象实例中指定相同的提供者信息是多余的。

使用这个 cast_from() 类方法,您只需要创建一个具有所有提供者信息的泛型对象,然后将它转换为不同类型。

provider = AdmittingProvider(...)
visit = Visit(
    AdmittingProvider=provider,
    AttendingProvider=AttendingProvider.cast_from(provider),
    VisitProvider=VisitProvider.cast_from(provider),
)

如果将多个对象传递给 cast_from,则优先考虑第一个对象的字段,然后是第二个对象的字段,依此类推。这类似于多重继承中的 MRO(请参阅 https://docs.pythonlang.cn/3/tutorial/classes.html#multiple-inheritance 以获取更多信息)。

项目详情


下载文件

下载适合您平台的应用程序。如果您不确定要选择哪一个,请了解更多有关 安装包 的信息。

源代码分发

pyredox-1.0.4.tar.gz (112.0 kB 查看哈希值)

上传时间 源代码

构建分发

pyredox-1.0.4-py3-none-any.whl (214.6 kB 查看哈希值)

上传时间 Python 3

由以下支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面