feat(dataqualityrule): added data quality rule support - #66
feat(dataqualityrule): added data quality rule support#66jacopocinaark wants to merge 18 commits into
Conversation
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
…Klab/Artesian.SDK-Python into feature/22668-DataQualityRule
There was a problem hiding this comment.
Pull request overview
Adds Data Quality Rule (DQR) support to the Python SDK’s MarketData surface, including DTOs/enums, MarketDataService endpoints, tests, and usage docs/samples.
Changes:
- Added
MarketDataServiceCRUD APIs for data quality rules and rule assignments, plus an assignment events feed endpoint. - Introduced Data Quality DTOs/enums (rule types, schedules, outlier models, paged results, assignments, events, status summary).
- Extended tests, README documentation, and added runnable samples for rule/assignment flows.
Reviewed changes
Copilot reviewed 31 out of 31 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/TestMarketDataService.py | Adds unit tests for DQR CRUD and assignment CRUD. |
| src/Artesian/MarketData/MarketDataService.py | Adds DQR and assignment endpoints to the service, including an events feed method. |
| src/Artesian/MarketData/_Enum/ScheduleDefinitionType.py | Adds schedule definition discriminator enum. |
| src/Artesian/MarketData/_Enum/RuleType.py | Adds DQ rule type enum (CompletenessAndFreshness/Outlier). |
| src/Artesian/MarketData/_Enum/PeriodPrecision.py | Adds precision enum used by period-based configs. |
| src/Artesian/MarketData/_Enum/OutlierModel.py | Adds outlier model discriminator enum. |
| src/Artesian/MarketData/_Enum/MarketDataTypeV2.py | Adds “v2” market data type enum for DQ configs. |
| src/Artesian/MarketData/_Enum/CheckAggregatedStatus.py | Adds aggregated status enum (OK/KO). |
| src/Artesian/MarketData/_Enum/init.py | Updates enum package exports (currently incomplete for new public enums). |
| src/Artesian/MarketData/_Dto/VersionedCompletenessAndFreshnessConfigDto.py | Adds versioned completeness/freshness config DTO. |
| src/Artesian/MarketData/_Dto/ScheduleDefinitionDto.py | Adds base schedule definition DTO abstraction. |
| src/Artesian/MarketData/_Dto/ScheduleConfigDto.py | Adds schedule config DTO (definition + maxDelay). |
| src/Artesian/MarketData/_Dto/RecordValidationConfigDto.py | Adds record validation window DTO. |
| src/Artesian/MarketData/_Dto/PagedResult.py | Adds paged result wrappers for DQRs and assignments. |
| src/Artesian/MarketData/_Dto/OutlierRefCurveConfigDto.py | Adds reference-curve outlier model config DTO. |
| src/Artesian/MarketData/_Dto/OutlierModelConfigDto.py | Adds base outlier model config DTO (currently has constructor issue). |
| src/Artesian/MarketData/_Dto/OutlierConfigDto.py | Adds outlier rule configuration DTO. |
| src/Artesian/MarketData/_Dto/OutlierAbsoluteBoundConfigDto.py | Adds absolute-bounds outlier model config DTO. |
| src/Artesian/MarketData/_Dto/MarketDataQualityRuleAssignmentDto.py | Adds rule assignment DTOs (input/output). |
| src/Artesian/MarketData/_Dto/DqCheckChangeEventDto.py | Adds DQ change-event DTOs for assignment event feed. |
| src/Artesian/MarketData/_Dto/DataQualityStatusSummaryDto.py | Adds status summary DTO (currently has keyword/serialization mapping risk). |
| src/Artesian/MarketData/_Dto/DataQualityRuleDtoOutput.py | Adds rule output DTO (adds aggregatedStatus). |
| src/Artesian/MarketData/_Dto/DataQualityRuleDtoInput.py | Adds rule input DTO. |
| src/Artesian/MarketData/_Dto/DataQualityRuleConfigDto.py | Adds base config DTO with type discriminator. |
| src/Artesian/MarketData/_Dto/CronScheduleDefinitionDto.py | Adds cron-based schedule definition DTO. |
| src/Artesian/MarketData/_Dto/CompletenessAndFreshnessConfigDto.py | Adds completeness/freshness base config DTO. |
| src/Artesian/MarketData/_Dto/ActualCompletenessAndFreshnessConfigDto.py | Adds “actual time series” completeness/freshness config DTO. |
| src/Artesian/MarketData/_Dto/init.py | Exposes new DTOs via the DTO package exports. |
| samples/TestDataQualityAssignment.py | Adds a manual end-to-end sample for rule assignment lifecycle. |
| samples/TestDataQuality.py | Adds a manual sample for rule CRUD lifecycle. |
| README.md | Documents Data Quality Rules usage and updates formatting in other sections. |
Comments suppressed due to low confidence (2)
src/Artesian/MarketData/MarketDataService.py:802
marketDataIdandruleIdare always added to query params even when None. This risks sendingmarketDataId=None/ruleId=None; omit them when not provided.
params["page"] = page
params["pageSize"] = pageSize
params["marketDataId"] = marketDataId
params["ruleId"] = ruleId
if ruleName:
src/Artesian/MarketData/MarketDataService.py:795
- Pagination validation error messages contain grammatical errors ("must to be") and report constraints inconsistently (code enforces 1-based pages). Consider clearer, structured messages.
if pageSize < 1:
raise ValueError(
"PageSize must to be greater than 0. Page Size:" + str(pageSize)
)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 31 out of 31 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (4)
src/Artesian/MarketData/MarketDataService.py:527
marketDataIdis always added to query params, even when it is None. Withrequests, this can result inmarketDataId=Nonebeing sent, which changes the meaning of the request. Only include this filter when a value is provided.
if type is not None:
params["type"] = type.name
params["marketDataId"] = marketDataId
if name:
src/Artesian/MarketData/MarketDataService.py:793
- The validation error messages here are ungrammatical/inconsistent ("must to be") and differ from the style used elsewhere in this file. Prefer the same
page must be >= 1 (got X)format used inreadDataQualityRuleAsync.
if page < 1:
raise ValueError("Page must to be greater than 0. Page:" + str(page))
if pageSize < 1:
raise ValueError(
"PageSize must to be greater than 0. Page Size:" + str(pageSize)
)
src/Artesian/MarketData/MarketDataService.py:953
- New
readDataQualityRuleAssignmentEventsFeedAsyncbehavior is not covered by unit tests (endpoint path andafterTimestampquery serialization). This file already has extensive request-matching tests, so this looks like an accidental gap.
async def readDataQualityRuleAssignmentEventsFeedAsync(
self: MarketDataService,
id: int,
afterTimestamp: Optional[datetime] = None,
) -> List[DqCheckChangeEventDtoOutput]:
src/Artesian/MarketData/_Dto/DataQualityStatusSummaryDto.py:35
- Field name
from_will serialize to JSON keyFrom_with the current global key transformer (__camelToPascalonly uppercases the first letter). The docstring says the API field name isFrom, so this DTO likely won't round-trip correctly unless the serializer strips the trailing underscore or a per-field rename is configured.
from_: Optional[date] = None
to: Optional[date] = None
…Klab/Artesian.SDK-Python into feature/22668-DataQualityRule
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 31 out of 31 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (4)
src/Artesian/MarketData/MarketDataService.py:496
typeis documented as an optional filter, but it's a required positional argument in the signature. This forces callers to always pass a value (or explicitly passNone), which is inconsistent with the docstring and other optional query filters.
self: MarketDataService,
page: int,
pageSize: int,
type: Optional[RuleType],
marketDataId: Optional[int] = None,
src/Artesian/MarketData/MarketDataService.py:800
marketDataIdandruleIdare optional filters, but they are always added to the query params even whenNone. Withrequests, this can end up sendingmarketDataId=None/ruleId=Noneon the wire, which changes server-side filtering semantics.
params = {}
params["page"] = page
params["pageSize"] = pageSize
params["marketDataId"] = marketDataId
params["ruleId"] = ruleId
src/Artesian/MarketData/MarketDataService.py:794
- The validation error messages have grammatical issues ("must to be") and are inconsistent with the clearer f-string format used elsewhere in this file (e.g.,
readDataQualityRuleAsync). This is a public-facing exception message.
raise ValueError("Page must to be greater than 0. Page:" + str(page))
if pageSize < 1:
raise ValueError(
"PageSize must to be greater than 0. Page Size:" + str(pageSize)
)
src/Artesian/MarketData/MarketDataService.py:954
- This new API surface (
readDataQualityRuleAssignmentEventsFeed*) has no unit test coverage intests/TestMarketDataService.py, unlike the other newly added Data Quality Rule endpoints. Add aresponses-based test to lock down the query param serialization (especiallyafterTimestamp) and the list deserialization behavior.
async def readDataQualityRuleAssignmentEventsFeedAsync(
self: MarketDataService,
id: int,
afterTimestamp: Optional[datetime] = None,
) -> List[DqCheckChangeEventDtoOutput]:
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 31 out of 31 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (8)
src/Artesian/MarketData/MarketDataService.py:796
- Error messages for page/pageSize validation are inconsistent with other pagination methods in this file and contain grammatical errors ("must to be"). Prefer the same >= 1 (got X) format used elsewhere for clearer API errors.
if page < 1:
raise ValueError("Page must to be greater than 0. Page:" + str(page))
if pageSize < 1:
raise ValueError(
"PageSize must to be greater than 0. Page Size:" + str(pageSize)
)
src/Artesian/MarketData/MarketDataService.py:802
- readDataQualityRuleAssignmentAsync currently always includes marketDataId/ruleId in query params even when they are None. That can send
marketDataId=None/ruleId=Noneto the API and change server-side filtering behavior. Only include these params when a value is provided (same pattern as readDataQualityRuleAsync).
params = {}
params["page"] = page
params["pageSize"] = pageSize
params["marketDataId"] = marketDataId
params["ruleId"] = ruleId
src/Artesian/MarketData/MarketDataService.py:984
- New API surface readDataQualityRuleAssignmentEventsFeedAsync/readDataQualityRuleAssignmentEventsFeed is not covered by tests, while this module has extensive response-mocking coverage for other MarketDataService endpoints.
async def readDataQualityRuleAssignmentEventsFeedAsync(
self: MarketDataService,
id: int,
afterTimestamp: Optional[datetime] = None,
) -> List[DqCheckChangeEventDtoOutput]:
"""
Retrieves the raw event feed for a specific rule assignment.
Args:
id: rule assignment identifier.
afterTimestamp: optional lower bound, returns events after instant.
Returns:
List of DqCheckChangeEventDtoOutput (Async).
"""
url = "/dataquality/dqruleassignment/" + str(id) + "/events"
params = {}
if afterTimestamp is not None:
params["afterTimestamp"] = afterTimestamp.isoformat()
with self.__client as c:
res = await asyncio.gather(
*[
self.__executor.exec(
c.exec,
"GET",
url,
None,
retcls=List[DqCheckChangeEventDtoOutput],
params=params,
)
]
)
return cast(List[DqCheckChangeEventDtoOutput], res[0])
src/Artesian/MarketData/_Dto/ScheduleDefinitionDto.py:16
- ScheduleDefinitionDto defines
typeas a @Property. jsons/dataclass serialization typically only serializes dataclass fields, so the discriminator may be omitted from JSON. Maketypea dataclass field (init=False) and let subclasses provide the default so the discriminator is reliably serialized.
@dataclass
class ScheduleDefinitionDto:
"""
Base class for schedule definition DTOs.
"""
@property
def type(self: "ScheduleDefinitionDto") -> ScheduleDefinitionType:
raise NotImplementedError(
"ScheduleDefinitionDto.type must be implemented by subclasses"
)
src/Artesian/MarketData/_Dto/CronScheduleDefinitionDto.py:24
- CronScheduleDefinitionDto exposes the schedule discriminator via a @Property. If the API expects a
typefield in the payload, this may not be serialized. Prefer a dataclass field (init=False) with a default so it is always present in JSON.
@dataclass
class CronScheduleDefinitionDto(ScheduleDefinitionDto):
"""
A schedule definition based on a cron expression, specifying recurring
check times in a given time zone.
Attributes:
cronExpression: cron expression defining the schedule pattern
timeZone: IANA time zone identifier used to evaluate cronExpression
"""
cronExpression: Optional[str] = None
timeZone: Optional[str] = None
@property
def type(self: "CronScheduleDefinitionDto") -> ScheduleDefinitionType:
return ScheduleDefinitionType.Cron
src/Artesian/MarketData/_Dto/OutlierModelConfigDto.py:17
- OutlierModelConfigDto defines
modelas a @Property. Ifmodelis a required discriminator for outlier configs, it may be omitted from serialized JSON. Prefer a dataclass field (init=False) and let subclasses set the default discriminator value.
@dataclass
class OutlierModelConfigDto(DataQualityRuleConfigDto):
"""
Base configuration for outlier detection rules.
"""
@property
def model(self: "OutlierModelConfigDto") -> OutlierModel:
raise NotImplementedError(
"OutlierModelConfigDto.model must be implemented by subclasses"
)
src/Artesian/MarketData/_Dto/OutlierAbsoluteBoundConfigDto.py:25
- OutlierAbsoluteBoundConfigDto exposes
modelvia a @Property. Ifmodelmust be part of the JSON payload for polymorphic deserialization server-side, this likely won’t be serialized. Use a dataclass field (init=False) with a default discriminator value instead.
@dataclass
class OutlierAbsoluteBoundConfigDto(OutlierModelConfigDto):
"""
Outlier detection model using fixed absolute bounds.
A data point is flagged as an outlier if its value falls below
lowerBound or above upperBound.
Attributes:
upperBound: maximum acceptable value
lowerBound: minimum acceptable value
"""
upperBound: float
lowerBound: float
@property
def model(self: "OutlierAbsoluteBoundConfigDto") -> OutlierModel:
return OutlierModel.AbsoluteBound
src/Artesian/MarketData/_Dto/OutlierRefCurveConfigDto.py:25
- OutlierRefCurveConfigDto exposes
modelvia a @Property. Ifmodelmust be present in JSON to discriminate between outlier model subtypes, it may be omitted from serialization. Use a dataclass field (init=False) with a default discriminator value instead.
@dataclass
class OutlierRefCurveConfigDto(OutlierModelConfigDto):
"""
Outlier detection model based on a reference Market Data curve.
A data point is flagged as an outlier if it deviates from the
reference value by more than tolerancePerc.
Attributes:
referenceMarketDataId: id of the reference Market Data entity
tolerancePerc: maximum allowed percentage deviation from reference
"""
referenceMarketDataId: int
tolerancePerc: float
@property
def model(self: "OutlierRefCurveConfigDto") -> OutlierModel:
return OutlierModel.RefCurve
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 46 out of 46 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (5)
src/Artesian/MarketData/MarketDataService.py:802
readDataQualityRuleAssignmentAsyncalways includesmarketDataIdandruleIdin query params even when they areNone. Unlike other methods in this file, this can emit unwanted query parameters (e.g.ruleId=None) and change server-side filtering behavior.
params["marketDataId"] = marketDataId
params["ruleId"] = ruleId
src/Artesian/MarketData/_Dto/OutlierModelConfigDto.py:12
OutlierModelConfigDtoinheritsDataQualityRuleConfigDto, so its subclasses (e.g.OutlierAbsoluteBoundConfigDto) require callers to passtype=...even though this is alwaysRuleType.Outlier. That’s error-prone (callers can pass the wrong discriminator) and inconsistent with other config DTOs that fixtypeviafield(init=False, default=...).
class OutlierModelConfigDto(DataQualityRuleConfigDto):
"""
Base configuration for outlier detection rules.
"""
src/Artesian/MarketData/_Enum/MarketDataTypeV2.py:4
- This PR is titled/linked as adding Data Quality Rule support, but it also renames/removes the public
MarketDataTypeenum (nowMarketDataTypeV2) and updates exports. That is a potentially breaking API change unrelated to data quality rules; consider either restoring backwards compatibility (alias/stub module + re-export) or calling out the breaking change explicitly in the PR description/release notes.
src/Artesian/MarketData/_Enum/init.py:17 __all__containsMarketDataTypeV2.__name__twice, which can lead to duplicate exports and is likely unintended.
src/Artesian/MarketData/MarketDataService.py:796- Validation error messages for
page/pageSizeinreadDataQualityRuleAssignmentAsynchave grammar issues ("must to be") and are inconsistent with the clearer f-string style used elsewhere in this file (e.g.readDataQualityRuleAsync).
raise ValueError("Page must to be greater than 0. Page:" + str(page))
if pageSize < 1:
raise ValueError(
"PageSize must to be greater than 0. Page Size:" + str(pageSize)
)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 46 out of 46 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (7)
README.md:531
- The outlier rule example passes
type=RuleType.OutlierintoOutlierAbsoluteBoundConfigDto(...), buttypeis defined withfield(init=False, ...)in the base class and will raiseTypeError: __init__() got an unexpected keyword argument 'type'.
model=OutlierAbsoluteBoundConfigDto(
lowerBound=-10.0,
upperBound=45.0,
type=RuleType.Outlier
)
src/Artesian/MarketData/MarketDataService.py:795
- The new validation errors in
readDataQualityRuleAssignmentAsynchave grammatical issues ("must to be") and are inconsistent with the rest of the file (f-string + lowercase parameter names).
"""
if page < 1:
raise ValueError("Page must to be greater than 0. Page:" + str(page))
if pageSize < 1:
raise ValueError(
src/Artesian/MarketData/init.py:5
- Renaming the public enum from
MarketDataTypetoMarketDataTypeV2is a breaking change for consumers importingMarketDataTypefromArtesian.MarketData. Consider providing a backward-compatible alias.
src/Artesian/MarketData/_Dto/OutlierModelConfigDto.py:12 OutlierModelConfigDto.pyhas duplicated imports (including duplicateddataclassimport blocks). This is noisy and can trigger lint failures.
from dataclasses import dataclass, field
from .._Enum.OutlierModel import OutlierModel
from .._Enum.RuleType import RuleType
from .DataQualityRuleConfigDto import DataQualityRuleConfigDto
src/Artesian/MarketData/_Enum/init.py:17
MarketDataTypeV2.__name__is duplicated in__all__, and the rename fromMarketDataTypetoMarketDataTypeV2is a breaking change for consumers usingfrom Artesian.MarketData._Enum import MarketDataType. Consider exporting a backward-compatible alias.
README.md:508- The "Completeness and Freshness Rule for Versioned Time Series" example is syntactically invalid (indentation/parentheses) and is missing
recordRangeToforRecordValidationConfigDto, so users can't copy/paste it successfully.
This issue also appears on line 527 of the same file.
recordValidationConfig=RecordValidationConfigDto(
recordRangeFrom="P0D",
versionToleranceFrom="-PT1H",
versionToleranceTo="PT1H",
versionPrecision=PeriodPrecision.Hour,
tests/TestMarketDataService.py:645
- The service passes
sorttorequestsas a list, so the encoded query param will parse as a list (e.g.{'sort': ['Id asc']}); the mock currently expects a string ("Id asc") and may not match, causing this test to fail.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 46 out of 46 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (6)
src/Artesian/MarketData/MarketDataService.py:986
- No tests cover the new
readDataQualityRuleAssignmentEventsFeed*APIs. The rest ofMarketDataServicehas request/response contract tests intests/TestMarketDataService.py, so this endpoint should also have a mocked-HTTP test to catch URL/params/serialization regressions.
async def readDataQualityRuleAssignmentEventsFeedAsync(
self: MarketDataService,
id: int,
afterTimestamp: Optional[datetime] = None,
) -> List[DqCheckChangeEventDtoOutput]:
"""
Retrieves the raw event feed for a specific rule assignment.
Args:
id: rule assignment identifier.
afterTimestamp: optional lower bound, returns events after instant.
Returns:
List of DqCheckChangeEventDtoOutput (Async).
"""
url = "/dataquality/dqruleassignment/" + str(id) + "/events"
params = {}
if afterTimestamp is not None:
params["afterTimestamp"] = afterTimestamp.isoformat()
with self.__client as c:
res = await asyncio.gather(
*[
self.__executor.exec(
c.exec,
"GET",
url,
None,
retcls=List[DqCheckChangeEventDtoOutput],
params=params,
)
]
)
return cast(List[DqCheckChangeEventDtoOutput], res[0])
src/Artesian/MarketData/_Enum/init.py:18
__all__exportsMarketDataTypeV2twice, which is redundant and can confuse wildcard imports / docs generation.
src/Artesian/MarketData/MarketDataService.py:796- The pagination validation error messages have grammar issues ("must to be") and use inconsistent casing compared to other methods in this file (e.g.
readDataQualityRuleAsync). These messages are user-facing when consumers pass invalid parameters.
if page < 1:
raise ValueError("Page must to be greater than 0. Page:" + str(page))
if pageSize < 1:
raise ValueError(
"PageSize must to be greater than 0. Page Size:" + str(pageSize)
)
README.md:531
- The Outlier example passes
type=RuleType.OutlierintoOutlierAbsoluteBoundConfigDto(...), but that class inheritstypeasfield(init=False, ...)so this call will raiseTypeError: __init__() got an unexpected keyword argument 'type'.
model=OutlierAbsoluteBoundConfigDto(
lowerBound=-10.0,
upperBound=45.0,
type=RuleType.Outlier
)
README.md:1021
- The DerivedTransformQueryValidation example imports
MarketDataTypebut usesMarketDataTypeV2in the request payload, so the sample will fail withNameErrorunlessMarketDataTypeV2is imported.
(datetime(2018, 10, 1, 1, 0), 100)
],
type=MarketDataTypeV2.ActualTimeSerie,
),
README.md:506
- The Versioned Completeness & Freshness example is not valid Python:
RecordValidationConfigDtois missing the requiredrecordRangeToargument, and theversionTolerance*fields are mis-indented (they should be arguments ofVersionedCompletenessAndFreshnessConfigDto, notRecordValidationConfigDto).
recordValidationConfig=RecordValidationConfigDto(
recordRangeFrom="P0D",
versionToleranceFrom="-PT1H",
versionToleranceTo="PT1H",
versionPrecision=PeriodPrecision.Hour,
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 53 out of 54 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (8)
README.md:531
- In this README snippet,
OutlierAbsoluteBoundConfigDtoinheritstypefromOutlierModelConfigDtoasfield(init=False, ...), so passingtype=...will raiseTypeError: __init__() got an unexpected keyword argument 'type'.
model=OutlierAbsoluteBoundConfigDto(
lowerBound=-10.0,
upperBound=45.0,
type=RuleType.Outlier
)
README.md:1021
- This sample imports
MarketDataTypebut the snippet usesMarketDataTypeV2. Copy/paste will fail withNameError: MarketDataTypeV2 is not definedunless the import is corrected.
rows=[
(datetime(2018, 10, 1, 0, 0), 100),
(datetime(2018, 10, 1, 1, 0), 100)
],
type=MarketDataTypeV2.ActualTimeSerie,
),
src/Artesian/MarketData/_Dto/init.py:96
DataQualityStatusSummaryDtois included twice in__all__, which is redundant and can lead to duplicate exports in documentation tooling.
src/Artesian/MarketData/_Dto/MarketDataDqStatusSummaryDto.py:25assignmentsis typed asOptional[List], which loses the element type information and makes this DTO harder to use correctly. It should reference the assignment DTO type.
src/Artesian/MarketData/_Enum/CheckAggregatedStatus.py:6- There are two different
CheckAggregatedStatusenums in the SDK (Artesian.CheckAggregatedStatuswith string values andArtesian.MarketData._Enum.CheckAggregatedStatuswith numeric values). This creates ambiguous APIs and makes it easy to pass the wrong enum type between DTOs and service methods.
src/Artesian/MarketData/_Enum/init.py:17 __all__containsMarketDataTypeV2twice, which is redundant and can confuse wildcard imports and generated docs.
src/Artesian/MarketData/MarketDataService.py:800- Validation error messages here contain grammatical issues ("must to be") and are inconsistent with the newer f-string messages used elsewhere in this file (e.g.
readDataQualityRuleAsync).
if page < 1:
raise ValueError("Page must to be greater than 0. Page:" + str(page))
if pageSize < 1:
raise ValueError(
"PageSize must to be greater than 0. Page Size:" + str(pageSize)
)
README.md:508
- This README code sample for
VersionedCompletenessAndFreshnessConfigDtois syntactically invalid:versionToleranceFrom/versionToleranceToare mis-indented (they appear insideRecordValidationConfigDto(...)),recordRangeTois missing, and parentheses don’t balance. As written, users can’t copy/paste this example successfully.
This issue also appears in the following locations of the same file:
- line 527
- line 1016
recordValidationConfig=RecordValidationConfigDto(
recordRangeFrom="P0D",
versionToleranceFrom="-PT1H",
versionToleranceTo="PT1H",
versionPrecision=PeriodPrecision.Hour,
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 53 out of 54 changed files in this pull request and generated 1 comment.
Suppressed comments (8)
src/Artesian/MarketData/_Dto/CheckResultCheckSummaryDto.py:32
aggregatedStatusshould be typed asCheckAggregatedStatus(matching other DQ DTOs) rather thanstr.
aggregatedStatus: str
src/Artesian/MarketData/_Enum/init.py:17
__all__includesMarketDataTypeV2twice; this is redundant and can confuse static tooling.
src/Artesian/MarketData/_Dto/init.py:97__all__includesDataQualityStatusSummaryDtotwice; remove the duplicate export to avoid redundant public surface.
src/Artesian/MarketData/MarketDataService.py:34MarketDataServiceimportsCheckAggregatedStatusfromArtesian.CheckAggregatedStatus, while the rest of the MarketData API surface exportsCheckAggregatedStatusfromArtesian.MarketData._Enum. This split makes it easy for users to pass the wrong enum type (and therefore send the wrongdqStatus.value). Use the MarketData enum consistently here.
from ._Dto.DqRuleDqStatusSummaryDto import DqRuleDqStatusSummaryDto
from ..CheckAggregatedStatus import CheckAggregatedStatus
src/Artesian/MarketData/MarketDataService.py:800
- The validation error messages here are ungrammatical and inconsistent with the rest of this file (which uses
page must be >= 1 (got x)style).
if page < 1:
raise ValueError("Page must to be greater than 0. Page:" + str(page))
if pageSize < 1:
raise ValueError(
"PageSize must to be greater than 0. Page Size:" + str(pageSize)
src/Artesian/MarketData/_Dto/CheckResultCheckSummaryDto.py:6
- This DTO uses
aggregatedStatus: str, but other DQ DTOs model the same concept as aCheckAggregatedStatusenum. If the API returns "OK"/"KO", using the enum here keeps the SDK consistent and avoids stringly-typed status handling.
This issue also appears on line 32 of the same file.
from typing import Optional
import datetime
from Artesian.MarketData._Dto.MarketDataQualityRuleAssignmentDto import MarketDataQualityRuleAssignmentDtoOutput
README.md:508
- The Versioned Completeness/Freshness example is currently syntactically invalid (indentation/parentheses) and missing required fields for
RecordValidationConfigDto(e.g.,recordRangeTo).
recordValidationConfig=RecordValidationConfigDto(
recordRangeFrom="P0D",
versionToleranceFrom="-PT1H",
versionToleranceTo="PT1H",
versionPrecision=PeriodPrecision.Hour,
README.md:531
OutlierAbsoluteBoundConfigDtoinheritstypefromOutlierModelConfigDtoasinit=False, so passingtype=...in the README example will raiseTypeError. Remove thetypeargument from the example.
model=OutlierAbsoluteBoundConfigDto(
lowerBound=-10.0,
upperBound=45.0,
type=RuleType.Outlier
)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 59 out of 60 changed files in this pull request and generated no new comments.
Suppressed comments (8)
src/Artesian/MarketData/_Enum/CheckAggregatedStatus.py:6
- There are now two different
CheckAggregatedStatusenums in the SDK (Artesian.CheckAggregatedStatususes string values, while this one uses ints). DTOs import the MarketData one, butMarketDataServiceuses the root one, which risks failed (de)serialization and inconsistent comparisons across the API surface. Consolidate to a single enum definition and update imports accordingly.
src/Artesian/MarketData/_Enum/init.py:19 __all__contains a duplicateMarketDataTypeV2.__name__entry, which is redundant and can cause confusing introspection/export behavior.
src/Artesian/MarketData/_Dto/init.py:109__all__includesDataQualityStatusSummaryDto.__name__twice; keep a single entry to avoid redundant exports.
src/Artesian/MarketData/MarketDataService.py:1029- Validation error messages here are inconsistent (and grammatically incorrect) compared to the other paging methods added in this PR (which use
page must be >= 1 (got ...)). This makes the public API harder to use and test.
if page < 1:
raise ValueError("Page must to be greater than 0. Page:" + str(page))
if pageSize < 1:
raise ValueError(
"PageSize must to be greater than 0. Page Size:" + str(pageSize)
README.md:508
- The Versioned Completeness/Freshness example has broken indentation/parentheses and places
versionTolerance*fields insideRecordValidationConfigDto(they belong toVersionedCompletenessAndFreshnessConfigDto). As-is, the snippet won’t run and misdocuments the API.
recordValidationConfig=RecordValidationConfigDto(
recordRangeFrom="P0D",
versionToleranceFrom="-PT1H",
versionToleranceTo="PT1H",
versionPrecision=PeriodPrecision.Hour,
README.md:531
- The Outlier rule example passes
type=RuleType.OutlierintoOutlierAbsoluteBoundConfigDto, buttypeis defined asinit=FalseinOutlierModelConfigDto, so this snippet will raiseTypeError: __init__() got an unexpected keyword argument 'type'.
model=OutlierAbsoluteBoundConfigDto(
lowerBound=-10.0,
upperBound=45.0,
type=RuleType.Outlier
)
README.md:693
- The README imports
CheckAggregatedStatusfromArtesian.MarketData._Enum, but the service APIs in this PR useArtesian.CheckAggregatedStatus. This mismatch will confuse users and can break comparisons if both enums exist.
from Artesian.MarketData._Enum.CheckAggregatedStatus import CheckAggregatedStatus
src/Artesian/MarketData/_Dto/MarketDataDqStatusSummaryDto.py:25
assignmentsis documented as a list of DQ rule assignments but is typed asOptional[List], losing element type information for consumers and for JSON deserialization hints. Prefer a concrete element type.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 59 out of 60 changed files in this pull request and generated no new comments.
Suppressed comments (4)
src/Artesian/MarketData/_Enum/init.py:19
__all__includesMarketDataTypeV2twice; this is redundant and can confuse generated docs/autocomplete.
src/Artesian/MarketData/MarketDataService.py:1029- The
ValueErrormessages here are grammatically incorrect ("must to be") and inconsistent with the rest of the file (which uses lowercase parameter names and f-strings).
if page < 1:
raise ValueError("Page must to be greater than 0. Page:" + str(page))
if pageSize < 1:
raise ValueError(
"PageSize must to be greater than 0. Page Size:" + str(pageSize)
)
README.md:508
- This Versioned Completeness/Freshness example is syntactically broken (indentation/parentheses) and omits required
RecordValidationConfigDto.recordRangeTo, so copy/pasting it will fail.
versionedCompletenessRule = DataQualityRuleDtoInput(
name="Hourly forecast version check",
type=RuleType.CompletenessAndFreshness,
configuration=VersionedCompletenessAndFreshnessConfigDto(
marketDataType=MarketDataTypeV2.VersionedTimeSerie,
scheduleConfig=ScheduleConfigDto(
scheduleDefinition=CronScheduleDefinitionDto(
cronExpression="15 * * * *",
timeZone="UTC",
),
maxDelay="PT30M",
),
recordValidationConfig=RecordValidationConfigDto(
recordRangeFrom="P0D",
versionToleranceFrom="-PT1H",
versionToleranceTo="PT1H",
versionPrecision=PeriodPrecision.Hour,
),
version=0,
)
README.md:534
- The Outlier rule example is missing imports (
DataQualityRuleDtoInput,RuleType) and passestype=...intoOutlierAbsoluteBoundConfigDto, buttypeis declared withinit=False(so this will raiseTypeError: __init__() got an unexpected keyword argument 'type').
from Artesian.MarketData._Dto.OutlierAbsoluteBoundConfigDto import (
OutlierAbsoluteBoundConfigDto,
)
from Artesian.MarketData._Dto.OutlierConfigDto import OutlierConfigDto
outlierRule = DataQualityRuleDtoInput(
name="Temperature outlier detection",
type=RuleType.Outlier,
configuration=OutlierConfigDto(
model=OutlierAbsoluteBoundConfigDto(
lowerBound=-10.0,
upperBound=45.0,
type=RuleType.Outlier
)
),
version=0,
)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 62 out of 63 changed files in this pull request and generated no new comments.
Suppressed comments (10)
src/Artesian/MarketData/_Dto/init.py:114
DataQualityStatusSummaryDtois included twice in__all__; this is redundant and makes the export list harder to audit.
src/Artesian/MarketData/MarketDataService.py:1163- These validation error messages are ungrammatical ("must to be") and inconsistent with the rest of the service (other methods use
>= 1 (got X)style).
if page < 1:
raise ValueError("Page must to be greater than 0. Page:" + str(page))
if pageSize < 1:
raise ValueError(
"PageSize must to be greater than 0. Page Size:" + str(pageSize)
src/Artesian/MarketData/MarketDataService.py:1872
- Query params should use enum names, not
.value, to avoid coupling correctness to the enum’s underlying value type.
if dqStatus is not None:
params["dqStatus"] = dqStatus.value
src/Artesian/MarketData/MarketDataService.py:1947
- Query params should use enum names, not
.value, to avoid incorrectdqStatusvalues when differentCheckAggregatedStatusenums are in play.
if dqStatus is not None:
params["dqStatus"] = dqStatus.value
src/Artesian/MarketData/_Enum/init.py:18
__all__containsMarketDataTypeV2twice, which is redundant and can confuse consumers relying on exported names.
src/Artesian/MarketData/_Dto/init.py:14- The multi-import from
.PagedResultuses leading commas on new lines, which is hard to read and inconsistent with typical formatting; it’s easy to accidentally break in future edits.
This issue also appears on line 112 of the same file.
src/Artesian/MarketData/MarketDataService.py:1765
- Query params should use enum names, not
.value. Using.valuemakes behavior depend on whether callers passArtesian.CheckAggregatedStatus(string values) vsArtesian.MarketData._Enum.CheckAggregatedStatus(int values).
This issue also appears in the following locations of the same file:
- line 1871
- line 1946
if dqStatus is not None:
params["dqStatus"] = dqStatus.value
src/Artesian/MarketData/_Dto/CheckResultCheckSummaryDto.py:33
aggregatedStatusis typed asstr, but other DQ DTOs model this asCheckAggregatedStatus. Keeping it as a string loses type-safety and makes client code inconsistent.
lastCheckTime: datetime.datetime
rangeStart: datetime.date
rangeEnd: datetime.date
aggregatedStatus: str
assignment: Optional[MarketDataQualityRuleAssignmentDtoOutput] = None
README.md:506
- The versioned completeness rule example is syntactically invalid (mis-indented fields and missing required
recordRangeTo), so users copying it will get runtime errors.
recordValidationConfig=RecordValidationConfigDto(
recordRangeFrom="P0D",
versionToleranceFrom="-PT1H",
versionToleranceTo="PT1H",
versionPrecision=PeriodPrecision.Hour,
README.md:531
OutlierAbsoluteBoundConfigDtoinheritstypefromOutlierModelConfigDtowithinit=False, so passingtype=...in this example will raiseTypeError: __init__() got an unexpected keyword argument 'type'.
model=OutlierAbsoluteBoundConfigDto(
lowerBound=-10.0,
upperBound=45.0,
type=RuleType.Outlier
)
ref: #22668