Skip to content

Commit

Permalink
Implement custom XmlDateTime converter which keeps full precision of …
Browse files Browse the repository at this point in the history
…fractional seconds
  • Loading branch information
chrismostert committed Mar 11, 2024
1 parent 3e15fa3 commit f69ec19
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 30 deletions.
82 changes: 52 additions & 30 deletions pyeml_bindings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
Generator: DataclassGenerator
See: https://xsdata.readthedocs.io/
"""

from pyeml_bindings.converters.xml_date_time import FullPrecisionXmlDateTimeConverter
from pyeml_bindings.emlcore_kiesraad_strict import (
Accepted,
Affiliation,
Expand All @@ -23,8 +25,8 @@
BallotIdentifierRange,
BallotIdentifierRangeStructure,
BallotIdentifierStructure,
BinaryItemStructure,
BinaryFormat,
BinaryItemStructure,
Candidate,
CandidateIdentifier,
CandidateIdentifierStructure,
Expand All @@ -36,19 +38,19 @@
ContactDetailsStructure,
ContestIdentifier,
ContestIdentifierStructure,
CountingAlgorithm,
CountMetric,
CountMetricStructure,
CountQualifier,
CountQualifierStructure,
CountingAlgorithm,
DocumentIdentifier,
DocumentIdentifierStructure,
Emlstructure,
ElectionGroupStructure,
ElectionIdentifier,
ElectionIdentifierStructure,
ElectionStatement,
EmailStructure,
Emlstructure,
Endorsement,
EventIdentifier,
EventIdentifierStructure,
Expand All @@ -63,8 +65,8 @@
ManagingAuthority,
ManagingAuthorityStructure,
MaxVotes,
MessageType,
MessagesStructure,
MessageType,
MinVotes,
NominatingOfficer,
NominatingOfficerStructure,
Expand All @@ -74,7 +76,6 @@
Period,
PeriodStructure,
PeriodStructurePermanent,
PersonName as EmlcorePersonName,
PollingDistrict,
PollingDistrictStructure,
PollingPlace,
Expand Down Expand Up @@ -113,10 +114,6 @@
SupporterStructure,
TelephoneStructure,
TransactionId,
Vtoken,
VtokenQualified,
VtokenQualifiedStructure,
VtokenStructure,
VoterIdentificationStructure,
VoterInformationStructure,
VoterInformationStructureGender,
Expand All @@ -125,10 +122,17 @@
VotingChannelType,
VotingMethod,
VotingMethodType,
Vtoken,
VtokenQualified,
VtokenQualifiedStructure,
VtokenStructure,
WriteIn,
WriteInType,
YesNoType,
)
from pyeml_bindings.emlcore_kiesraad_strict import (
PersonName as EmlcorePersonName,
)
from pyeml_bindings.emlexternals_kiesraad_strict import (
AuthorityAddressStructure,
ElectoralAddressStructure,
Expand Down Expand Up @@ -182,8 +186,8 @@
CandidateStructureKr,
ContactDetailsStructureKr,
ContestIdentifierStructureKr,
EmlstructureKr,
ElectionIdentifierStructureKr,
EmlstructureKr,
GenericMailingAddressStructureKr,
GenericQualifyingAddressStructureKr,
MailingAddressStructureKr,
Expand All @@ -194,56 +198,66 @@
)
from pyeml_bindings.mod_110a_electionevent_kiesraad_strict import (
ContestIdentifierStructure110A,
Emlstructure110,
Eml as Eml110a,
ElectionEvent,
ElectionIdentifierStructure110A,
Emlstructure110,
PollingPlaceStructure110,
PollingPlaceStructure110Channel,
)
from pyeml_bindings.mod_110a_electionevent_kiesraad_strict import (
Eml as Eml110a,
)
from pyeml_bindings.mod_210_nomination_kiesraad_strict import (
AffiliationIdentifierStructure210,
AffiliationStructure210,
CandidateIdentifierStructure210,
CandidateStructure210,
ContestIdentifierStructure210,
Emlstructure210,
Eml as Eml210,
ElectionIdentifierStructure210,
Emlstructure210,
Nomination,
ProposerStructureKr,
ProposerStructureRestricted,
ProposerStructureRestrictedJobTitle,
)
from pyeml_bindings.mod_210_nomination_kiesraad_strict import (
Eml as Eml210,
)
from pyeml_bindings.mod_230_candidatelist_kiesraad_strict import (
CandidateList,
ElectionIdentifierStructure230,
Emlstructure230,
Eml as Eml230,
Emlstructure230Id,
ElectionIdentifierStructure230,
)
from pyeml_bindings.mod_230_candidatelist_kiesraad_strict import (
Eml as Eml230,
)
from pyeml_bindings.mod_510_count_kiesraad_strict import (
AffiliationIdentifierStructure510,
CandidateIdentifierStructure510,
CandidateStructure510,
Count,
Emlstructure510,
Eml as Eml510,
ElectionIdentifierStructure510,
Emlstructure510,
RejectedVotesReasonCode,
ReportingUnitVotes,
UncountedVotesReasonCode,
)
from pyeml_bindings.mod_510_count_kiesraad_strict import (
Eml as Eml510,
)
from pyeml_bindings.mod_520_result_kiesraad_strict import (
AffiliationIdentifierStructure520,
CandidateIdentifierStructure520,
CandidateStructure520,
Emlstructure520,
Eml as Eml520,
ElectionIdentifierStructure520,
Emlstructure520,
Result,
SelectionRanking,
)
from pyeml_bindings.mod_520_result_kiesraad_strict import (
Eml as Eml520,
)
from pyeml_bindings.x_al_kiesraad_strict import (
Address,
AddressDetails,
Expand Down Expand Up @@ -271,41 +285,41 @@
MailStopType,
MinimalCountryType,
MinimalLocalityType,
PostalCode,
PostalRouteType,
PostBox,
PostOffice,
PostOfficeNumberIndicatorOccurrence,
PostalCode,
PostalRouteType,
Premise,
PremiseNameTypeOccurrence,
PremiseNumber,
PremiseNumberIndicatorOccurrence,
PremiseNumberNumberType,
PremiseNumberNumberTypeOccurrence,
PremiseNumberPrefix,
PremiseNumberRangeIndicatorOccurence,
PremiseNumberRangeNumberRangeOccurence,
PremiseNumberSuffix,
PremiseNumberIndicatorOccurrence,
PremiseNumberNumberType,
PremiseNumberNumberTypeOccurrence,
SubPremiseNameTypeOccurrence,
SubPremiseNumberIndicatorOccurrence,
SubPremiseNumberNumberTypeOccurrence,
SubPremiseType,
Thoroughfare,
ThoroughfareDependentThoroughfares,
ThoroughfareLeadingTypeType,
ThoroughfareNameType,
ThoroughfareNumber,
ThoroughfareNumberIndicatorOccurrence,
ThoroughfareNumberNumberOccurrence,
ThoroughfareNumberNumberType,
ThoroughfareNumberPrefix,
ThoroughfareNumberRangeIndicatorOccurrence,
ThoroughfareNumberRangeNumberRangeOccurrence,
ThoroughfareNumberRangeRangeType,
ThoroughfareNumberSuffix,
ThoroughfareNumberIndicatorOccurrence,
ThoroughfareNumberNumberOccurrence,
ThoroughfareNumberNumberType,
ThoroughfarePostDirectionType,
ThoroughfarePreDirectionType,
ThoroughfareTrailingTypeType,
ThoroughfareDependentThoroughfares,
XAl,
)
from pyeml_bindings.x_nl_kiesraad_strict import (
Expand All @@ -316,9 +330,11 @@
NameLineType,
OrganisationNameDetails,
OrganisationNameDetails1,
PersonName as XNlPersonName,
XNl,
)
from pyeml_bindings.x_nl_kiesraad_strict import (
PersonName as XNlPersonName,
)

__all__ = [
"Accepted",
Expand Down Expand Up @@ -616,3 +632,9 @@
"PollingPlaceStructure110",
"PollingPlaceStructure110Channel",
]

# Register custom converters for serialization
from xsdata.formats.converter import converter
from xsdata.models.datatype import XmlDateTime

converter.register_converter(XmlDateTime, FullPrecisionXmlDateTimeConverter())
51 changes: 51 additions & 0 deletions pyeml_bindings/converters/xml_date_time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from typing import Any, Optional

from xsdata.formats.converter import Converter
from xsdata.models.datatype import XmlDateTime
from xsdata.utils.dates import format_date, format_offset


def custom_format_time(
hour: int, minute: int, second: int, fractional_second: int
) -> str:
"""Serializes a time according to ISO 8601.
Args:
hour (int): The hour to serialize
minute (int): The minute to serialize
second (int): The second to serialize
fractional_second (int): The fractional second to serialize. Can be either nano- micro- or milliseconds.
Returns:
str: The time formatted according to ISO 8601 (example: 14:14:33.000)
"""
microsecond, nano = divmod(fractional_second, 1000)
if nano:
return f"{hour:02d}:{minute:02d}:{second:02d}.{fractional_second:09d}"

milli, micro = divmod(microsecond, 1000)
if micro:
return f"{hour:02d}:{minute:02d}:{second:02d}.{microsecond:06d}"

return f"{hour:02d}:{minute:02d}:{second:02d}.{milli:03d}"


class FullPrecisionXmlDateTimeConverter(Converter):
"""Override the default XmlDateTimeConverter to preserve full precision when serializing datetimes
Args:
Converter (xsdata.formats.converter.Converter): Abstract converter class
"""

def deserialize(self, value: Any, **kwargs: Any) -> Any:
return XmlDateTime.from_string(value)

def serialize(self, value: Any, **kwargs: Any) -> Optional[str]:
if isinstance(value, XmlDateTime):
return "{}T{}{}".format(
format_date(value.year, value.month, value.day),
custom_format_time(
value.hour, value.minute, value.second, value.fractional_second
),
format_offset(value.offset),
)

0 comments on commit f69ec19

Please sign in to comment.