mms-client 1.0.6__py3-none-any.whl → 1.2.0__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.
- mms_client/services/base.py +18 -6
- mms_client/services/market.py +26 -0
- mms_client/services/registration.py +64 -0
- mms_client/types/base.py +3 -1
- mms_client/types/enums.py +8 -0
- mms_client/types/fields.py +150 -2
- mms_client/types/offer.py +16 -4
- mms_client/types/registration.py +47 -0
- mms_client/types/resource.py +1064 -0
- mms_client/types/transport.py +1 -1
- mms_client/utils/serialization.py +60 -15
- {mms_client-1.0.6.dist-info → mms_client-1.2.0.dist-info}/METADATA +18 -2
- {mms_client-1.0.6.dist-info → mms_client-1.2.0.dist-info}/RECORD +15 -13
- {mms_client-1.0.6.dist-info → mms_client-1.2.0.dist-info}/LICENSE +0 -0
- {mms_client-1.0.6.dist-info → mms_client-1.2.0.dist-info}/WHEEL +0 -0
mms_client/services/base.py
CHANGED
|
@@ -10,6 +10,7 @@ from typing import Optional
|
|
|
10
10
|
from typing import Protocol
|
|
11
11
|
from typing import Tuple
|
|
12
12
|
from typing import Type
|
|
13
|
+
from typing import Union
|
|
13
14
|
|
|
14
15
|
from mms_client.security.crypto import Certificate
|
|
15
16
|
from mms_client.security.crypto import CryptoWrapper
|
|
@@ -119,7 +120,7 @@ class ClientProto(Protocol):
|
|
|
119
120
|
def request_many(
|
|
120
121
|
self,
|
|
121
122
|
envelope: E,
|
|
122
|
-
data: P,
|
|
123
|
+
data: Union[P, List[P]],
|
|
123
124
|
config: EndpointConfiguration,
|
|
124
125
|
) -> Tuple[MultiResponse[E, P], Dict[str, bytes]]:
|
|
125
126
|
"""Submit a request to the MMS server and return the multi-response.
|
|
@@ -361,7 +362,7 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
361
362
|
def request_many(
|
|
362
363
|
self,
|
|
363
364
|
envelope: E,
|
|
364
|
-
payload: P,
|
|
365
|
+
payload: Union[P, List[P]],
|
|
365
366
|
config: EndpointConfiguration[E, P],
|
|
366
367
|
) -> Tuple[MultiResponse[E, P], Dict[str, bytes]]:
|
|
367
368
|
"""Submit a request to the MMS server and return the multi-response.
|
|
@@ -374,13 +375,20 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
374
375
|
Returns: The multi-response from the MMS server.
|
|
375
376
|
"""
|
|
376
377
|
# First, create the MMS request from the payload and data.
|
|
378
|
+
is_list = isinstance(payload, list)
|
|
379
|
+
data_type = type(payload[0]) if is_list else type(payload) # type: ignore[index]
|
|
377
380
|
self._logger.debug(
|
|
378
381
|
(
|
|
379
382
|
f"{config.name}: Starting multi-request. Envelope: {type(envelope).__name__}, "
|
|
380
|
-
f"Data: {
|
|
383
|
+
f"Data: {data_type.__name__}"
|
|
381
384
|
),
|
|
382
385
|
)
|
|
383
|
-
|
|
386
|
+
serialized = (
|
|
387
|
+
config.service.serializer.serialize_multi(envelope, payload, data_type) # type: ignore[arg-type]
|
|
388
|
+
if is_list
|
|
389
|
+
else config.service.serializer.serialize(envelope, payload) # type: ignore[type-var]
|
|
390
|
+
)
|
|
391
|
+
request = self._to_mms_request(config.request_type, serialized)
|
|
384
392
|
|
|
385
393
|
# Next, submit the request to the MMS server and get and verify the response.
|
|
386
394
|
resp = self._get_wrapper(config.service).submit(request)
|
|
@@ -391,8 +399,12 @@ class BaseClient: # pylint: disable=too-many-instance-attributes
|
|
|
391
399
|
|
|
392
400
|
# Finally, deserialize and verify the response
|
|
393
401
|
envelope_type = config.response_envelope_type or type(envelope)
|
|
394
|
-
data_type = config.response_data_type or
|
|
395
|
-
data: MultiResponse[E, P] = config.service.serializer.deserialize_multi(
|
|
402
|
+
data_type = config.response_data_type or data_type
|
|
403
|
+
data: MultiResponse[E, P] = config.service.serializer.deserialize_multi(
|
|
404
|
+
resp.payload,
|
|
405
|
+
envelope_type,
|
|
406
|
+
data_type, # type: ignore[arg-type]
|
|
407
|
+
)
|
|
396
408
|
self._verify_multi_response(data, config)
|
|
397
409
|
|
|
398
410
|
# Return the response data and any attachments
|
mms_client/services/market.py
CHANGED
|
@@ -54,6 +54,32 @@ class MarketClientMixin: # pylint: disable=unused-argument
|
|
|
54
54
|
days=days,
|
|
55
55
|
)
|
|
56
56
|
|
|
57
|
+
@mms_multi_endpoint("MarketSubmit_OfferData", config, RequestType.INFO, ClientType.BSP)
|
|
58
|
+
def put_offers(
|
|
59
|
+
self: ClientProto, requests: List[OfferData], market_type: MarketType, days: int, date: Optional[Date] = None
|
|
60
|
+
) -> List[OfferData]:
|
|
61
|
+
"""Submit multiple offers to the MMS server.
|
|
62
|
+
|
|
63
|
+
This endpoint is only accessible to BSPs.
|
|
64
|
+
|
|
65
|
+
Arguments:
|
|
66
|
+
requests (List[OfferData]): The offers to submit to the MMS server.
|
|
67
|
+
market_type (MarketType): The type of market for which the offers are being submitted.
|
|
68
|
+
days (int): The number of days ahead for which the offers are being submitted.
|
|
69
|
+
date (Date): The date of the transaction in the format "YYYY-MM-DD". This value defaults to the
|
|
70
|
+
current date.
|
|
71
|
+
|
|
72
|
+
Returns: A list of offers that have been registered with the MMS server.
|
|
73
|
+
"""
|
|
74
|
+
# Note: the return type does not match the method definition but the decorator will return the correct type
|
|
75
|
+
return MarketSubmit( # type: ignore[return-value]
|
|
76
|
+
date=date or Date.today(),
|
|
77
|
+
participant=self.participant,
|
|
78
|
+
user=self.user,
|
|
79
|
+
market_type=market_type,
|
|
80
|
+
days=days,
|
|
81
|
+
)
|
|
82
|
+
|
|
57
83
|
@mms_multi_endpoint(
|
|
58
84
|
"MarketQuery_OfferQuery", config, RequestType.INFO, resp_envelope_type=MarketSubmit, resp_data_type=OfferData
|
|
59
85
|
)
|
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
"""Contains the client layer for making registration requests to the MMS server."""
|
|
2
2
|
|
|
3
|
+
from datetime import date as Date
|
|
4
|
+
from typing import List
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from mms_client.services.base import ClientProto
|
|
3
8
|
from mms_client.services.base import ServiceConfiguration
|
|
9
|
+
from mms_client.services.base import mms_endpoint
|
|
10
|
+
from mms_client.services.base import mms_multi_endpoint
|
|
11
|
+
from mms_client.types.registration import QueryAction
|
|
12
|
+
from mms_client.types.registration import QueryType
|
|
13
|
+
from mms_client.types.registration import RegistrationQuery
|
|
14
|
+
from mms_client.types.registration import RegistrationSubmit
|
|
15
|
+
from mms_client.types.resource import ResourceData
|
|
16
|
+
from mms_client.types.resource import ResourceQuery
|
|
17
|
+
from mms_client.types.transport import RequestType
|
|
4
18
|
from mms_client.utils.serialization import SchemaType
|
|
5
19
|
from mms_client.utils.serialization import Serializer
|
|
20
|
+
from mms_client.utils.web import ClientType
|
|
6
21
|
from mms_client.utils.web import Interface
|
|
7
22
|
|
|
8
23
|
|
|
@@ -11,3 +26,52 @@ class RegistrationClientMixin: # pylint: disable=unused-argument
|
|
|
11
26
|
|
|
12
27
|
# The configuration for the registration service
|
|
13
28
|
config = ServiceConfiguration(Interface.MI, Serializer(SchemaType.REGISTRATION, "RegistrationData"))
|
|
29
|
+
|
|
30
|
+
@mms_endpoint("RegistrationSubmit_Resource", config, RequestType.REGISTRATION, ClientType.BSP)
|
|
31
|
+
def put_resource(self: ClientProto, request: ResourceData) -> ResourceData:
|
|
32
|
+
"""Submit a new resource to the MMS server.
|
|
33
|
+
|
|
34
|
+
This endpoint is only accessible to BSPs.
|
|
35
|
+
|
|
36
|
+
Arguments:
|
|
37
|
+
request (ResourceData): The resource to register with the MMS server.
|
|
38
|
+
|
|
39
|
+
Returns: The resource that has been registered with the MMS server.
|
|
40
|
+
"""
|
|
41
|
+
# For some reason, the registration DTOs require that the participant ID exist on the payload rather than on
|
|
42
|
+
# the envelope so we need to set it before we return the envelope.
|
|
43
|
+
request.participant = self.participant
|
|
44
|
+
|
|
45
|
+
# Create and return the registration submit DTO.
|
|
46
|
+
return RegistrationSubmit() # type: ignore[return-value]
|
|
47
|
+
|
|
48
|
+
@mms_multi_endpoint(
|
|
49
|
+
"RegistrationQuery_Resource",
|
|
50
|
+
config,
|
|
51
|
+
RequestType.REGISTRATION,
|
|
52
|
+
resp_envelope_type=RegistrationSubmit,
|
|
53
|
+
resp_data_type=ResourceData,
|
|
54
|
+
)
|
|
55
|
+
def query_resources(
|
|
56
|
+
self: ClientProto, request: ResourceQuery, action: QueryAction, date: Optional[Date] = None
|
|
57
|
+
) -> List[ResourceData]:
|
|
58
|
+
"""Query resources from the MMS server.
|
|
59
|
+
|
|
60
|
+
Arguments:
|
|
61
|
+
request (ResourceQuery): The query to send to the MMS server.
|
|
62
|
+
action (QueryAction): The type of query being made. NORMAL for all records or LATEST for the most recent.
|
|
63
|
+
date (Date): The date of the transaction in the format "YYYY-MM-DD". This value defaults to the
|
|
64
|
+
current date.
|
|
65
|
+
|
|
66
|
+
Returns: A list of resources that match the query.
|
|
67
|
+
"""
|
|
68
|
+
# For some reason, the registration DTOs require that the participant ID exist on the payload rather than on
|
|
69
|
+
# the envelope so we need to set it before we return the envelope.
|
|
70
|
+
request.participant = self.participant
|
|
71
|
+
|
|
72
|
+
# Inject our parameters into the query and return it.
|
|
73
|
+
return RegistrationQuery( # type: ignore[return-value]
|
|
74
|
+
action=action,
|
|
75
|
+
query_type=QueryType.TRADE,
|
|
76
|
+
date=date or Date.today(),
|
|
77
|
+
)
|
mms_client/types/base.py
CHANGED
|
@@ -14,6 +14,8 @@ from pydantic_xml import BaseXmlModel
|
|
|
14
14
|
from pydantic_xml import attr
|
|
15
15
|
from pydantic_xml import element
|
|
16
16
|
|
|
17
|
+
from mms_client.types.fields import transaction_id
|
|
18
|
+
|
|
17
19
|
|
|
18
20
|
class ValidationStatus(Enum):
|
|
19
21
|
"""Represents the status of the validation check done on an element."""
|
|
@@ -67,7 +69,7 @@ class ProcessingStatistics(BaseXmlModel):
|
|
|
67
69
|
time_ms: Optional[int] = attr(default=None, name="ProcessingTimeMs")
|
|
68
70
|
|
|
69
71
|
# The transaction ID of the request
|
|
70
|
-
transaction_id: Optional[str] =
|
|
72
|
+
transaction_id: Optional[str] = transaction_id("TransactionId", True)
|
|
71
73
|
|
|
72
74
|
# When the request was received, in the format "DDD MMM DD HH:MM:SS TZ YYYY"
|
|
73
75
|
timestamp: str = attr(default="", name="TimeStamp")
|
mms_client/types/enums.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Contains enums common to all MMS types."""
|
|
2
2
|
|
|
3
3
|
from enum import Enum
|
|
4
|
+
from enum import IntEnum
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class AreaCode(Enum):
|
|
@@ -16,3 +17,10 @@ class AreaCode(Enum):
|
|
|
16
17
|
SHIKOKU = "08"
|
|
17
18
|
KYUSHU = "09"
|
|
18
19
|
OKINAWA = "10"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Frequency(IntEnum):
|
|
23
|
+
"""Represents the frequency of power sources."""
|
|
24
|
+
|
|
25
|
+
EAST = 50
|
|
26
|
+
WEST = 60
|
mms_client/types/fields.py
CHANGED
|
@@ -4,7 +4,13 @@ from pydantic_core import PydanticUndefined
|
|
|
4
4
|
from pydantic_xml import attr
|
|
5
5
|
|
|
6
6
|
# Describes the regular expression required by the MMS API for Japanese text
|
|
7
|
-
JAPANESE_TEXT = r"
|
|
7
|
+
JAPANESE_TEXT = r"[\u3000-\u30FF\uFF00-\uFF60\uFFA0-\uFFEF\u4E00-\u9FEA]*"
|
|
8
|
+
|
|
9
|
+
# Describes the regular expression required by the MMS API for ASCII text
|
|
10
|
+
ASCII_TEXT = r"[a-zA-Z0-9 ~!@#$*()_+}{:?>`='/.,%;\^\|\-\]\[\\<&"]*"
|
|
11
|
+
|
|
12
|
+
# Describes the regular expression required by the MMS API for Japanese or ASCII text
|
|
13
|
+
JAPANESE_ASCII_TEXT = f"{JAPANESE_TEXT}|{ASCII_TEXT}"
|
|
8
14
|
|
|
9
15
|
|
|
10
16
|
def participant(alias: str, optional: bool = False):
|
|
@@ -43,6 +49,41 @@ def operator_code(alias: str, optional: bool = False):
|
|
|
43
49
|
)
|
|
44
50
|
|
|
45
51
|
|
|
52
|
+
def transaction_id(alias: str, optional: bool = False):
|
|
53
|
+
"""Create a field for a transaction ID.
|
|
54
|
+
|
|
55
|
+
Arguments:
|
|
56
|
+
alias (str): The name of the alias to assign to the Pydanitc field. This value will be used to map the field
|
|
57
|
+
to the JSON/XML key.
|
|
58
|
+
optional (bool): If True, the field will be optional with a default of None. If False, the field will be
|
|
59
|
+
required, with no default.
|
|
60
|
+
|
|
61
|
+
Returns: A Pydantic Field object for the transaction ID.
|
|
62
|
+
"""
|
|
63
|
+
return attr(
|
|
64
|
+
default=None if optional else PydanticUndefined,
|
|
65
|
+
name=alias,
|
|
66
|
+
min_length=8,
|
|
67
|
+
max_length=10,
|
|
68
|
+
pattern=r"^[a-zA-Z0-9]{8,10}$",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def capacity(alias: str, minimum: int, optional: bool = False):
|
|
73
|
+
"""Create a field for a capacity value.
|
|
74
|
+
|
|
75
|
+
Arguments:
|
|
76
|
+
alias (str): The name of the alias to assign to the Pydanitc field. This value will be used to map the field
|
|
77
|
+
to the JSON/XML key.
|
|
78
|
+
minimum (int): The minimum value for the capacity field.
|
|
79
|
+
optional (bool): If True, the field will be optional with a default of None. If False, the field will be
|
|
80
|
+
required, with no default.
|
|
81
|
+
|
|
82
|
+
Returns: A Pydantic Field object for the capacity value.
|
|
83
|
+
"""
|
|
84
|
+
return attr(default=None if optional else PydanticUndefined, name=alias, ge=minimum, le=10000000)
|
|
85
|
+
|
|
86
|
+
|
|
46
87
|
def power_positive(alias: str, optional: bool = False):
|
|
47
88
|
"""Create a field for a positive power value.
|
|
48
89
|
|
|
@@ -68,7 +109,21 @@ def price(alias: str, optional: bool = False):
|
|
|
68
109
|
|
|
69
110
|
Returns: A Pydantic Field object for the price value.
|
|
70
111
|
"""
|
|
71
|
-
return attr(default=None if optional else PydanticUndefined, name=alias, ge=0.00,
|
|
112
|
+
return attr(default=None if optional else PydanticUndefined, name=alias, ge=0.00, lt=10000.00, decimal_places=2)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def percentage(alias: str, optional: bool = False):
|
|
116
|
+
"""Create a field for a percentage value.
|
|
117
|
+
|
|
118
|
+
Arguments:
|
|
119
|
+
alias (str): The name of the alias to assign to the Pydanitc field. This value will be used to map the field
|
|
120
|
+
to the JSON/XML key.
|
|
121
|
+
optional (bool): If True, the field will be optional with a default of None. If False, the field will be
|
|
122
|
+
required, with no default.
|
|
123
|
+
|
|
124
|
+
Returns: A Pydantic Field object for the percentage value.
|
|
125
|
+
"""
|
|
126
|
+
return attr(default=None if optional else PydanticUndefined, name=alias, ge=0.0, le=100.0, decimal_places=1)
|
|
72
127
|
|
|
73
128
|
|
|
74
129
|
def dr_patter_number(alias: str, optional: bool = False):
|
|
@@ -85,6 +140,22 @@ def dr_patter_number(alias: str, optional: bool = False):
|
|
|
85
140
|
return attr(default=None if optional else PydanticUndefined, name=alias, ge=1, le=20)
|
|
86
141
|
|
|
87
142
|
|
|
143
|
+
def pattern_name(alias: str, optional: bool = False):
|
|
144
|
+
"""Create a field for a pattern name.
|
|
145
|
+
|
|
146
|
+
Arguments:
|
|
147
|
+
alias (str): The name of the alias to assign to the Pydanitc field. This value will be used to map the field
|
|
148
|
+
to the JSON/XML key.
|
|
149
|
+
optional (bool): If True, the field will be optional with a default of None. If False, the field will be
|
|
150
|
+
required, with no default.
|
|
151
|
+
|
|
152
|
+
Returns: A Pydantic Field object for the pattern name.
|
|
153
|
+
"""
|
|
154
|
+
return attr(
|
|
155
|
+
default=None if optional else PydanticUndefined, name=alias, min_length=1, max_length=20, pattern=JAPANESE_TEXT
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
88
159
|
def company_short_name(alias: str, optional: bool = False):
|
|
89
160
|
"""Create a field for a company short name.
|
|
90
161
|
|
|
@@ -101,6 +172,44 @@ def company_short_name(alias: str, optional: bool = False):
|
|
|
101
172
|
)
|
|
102
173
|
|
|
103
174
|
|
|
175
|
+
def address(alias: str, optional: bool = False):
|
|
176
|
+
"""Create a field for an address.
|
|
177
|
+
|
|
178
|
+
Arguments:
|
|
179
|
+
alias (str): The name of the alias to assign to the Pydanitc field. This value will be used to map the field
|
|
180
|
+
to the JSON/XML key.
|
|
181
|
+
optional (bool): If True, the field will be optional with a default of None. If False, the field will be
|
|
182
|
+
required, with no default.
|
|
183
|
+
|
|
184
|
+
Returns: A Pydantic Field object for the address.
|
|
185
|
+
"""
|
|
186
|
+
return attr(
|
|
187
|
+
default=None if optional else PydanticUndefined, name=alias, min_length=1, max_length=50, pattern=JAPANESE_TEXT
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def phone(alias: str, first_part: bool, optional: bool = False):
|
|
192
|
+
"""Create a field for a phone number.
|
|
193
|
+
|
|
194
|
+
Arguments:
|
|
195
|
+
alias (str): The name of the alias to assign to the Pydanitc field. This value will be used to map the field
|
|
196
|
+
to the JSON/XML key.
|
|
197
|
+
first_part (bool): If True, the field will be the first part of the phone number. If False, the field will be the
|
|
198
|
+
second part of the phone number.
|
|
199
|
+
optional (bool): If True, the field will be optional with a default of None. If False, the field will be
|
|
200
|
+
required, with no default.
|
|
201
|
+
|
|
202
|
+
Returns: A Pydantic Field object for the phone number.
|
|
203
|
+
"""
|
|
204
|
+
return attr(
|
|
205
|
+
default=None if optional else PydanticUndefined,
|
|
206
|
+
name=alias,
|
|
207
|
+
min_length=1,
|
|
208
|
+
max_length=5 if first_part else 4,
|
|
209
|
+
pattern=r"^[0-9]*$",
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
|
|
104
213
|
def resource_name(alias: str, optional: bool = False):
|
|
105
214
|
"""Create a field for a resource name.
|
|
106
215
|
|
|
@@ -151,3 +260,42 @@ def system_code(alias: str, optional: bool = False):
|
|
|
151
260
|
return attr(
|
|
152
261
|
default=None if optional else PydanticUndefined, name=alias, min_length=5, max_length=5, pattern=r"^[A-Z0-9]*$"
|
|
153
262
|
)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def minute(alias: str, optional: bool = False):
|
|
266
|
+
"""Create a field for a minute value.
|
|
267
|
+
|
|
268
|
+
Arguments:
|
|
269
|
+
alias (str): The name of the alias to assign to the Pydanitc field. This value will be used to map the field
|
|
270
|
+
to the JSON/XML key.
|
|
271
|
+
optional (bool): If True, the field will be optional with a default of None. If False, the field will be
|
|
272
|
+
required, with no default.
|
|
273
|
+
|
|
274
|
+
Returns: A Pydantic Field object for the minute value.
|
|
275
|
+
"""
|
|
276
|
+
return attr(
|
|
277
|
+
default=None if optional else PydanticUndefined,
|
|
278
|
+
name=alias,
|
|
279
|
+
ge=0,
|
|
280
|
+
le=99,
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def hour(alias: str, optional: bool = False):
|
|
285
|
+
"""Create a field for an hour value.
|
|
286
|
+
|
|
287
|
+
Arguments:
|
|
288
|
+
alias (str): The name of the alias to assign to the Pydanitc field. This value will be used to map the field
|
|
289
|
+
to the JSON/XML key.
|
|
290
|
+
optional (bool): If True, the field will be optional with a default of None. If False, the field will be
|
|
291
|
+
required, with no default.
|
|
292
|
+
|
|
293
|
+
Returns: A Pydantic Field object for the hour value.
|
|
294
|
+
"""
|
|
295
|
+
return attr(
|
|
296
|
+
default=None if optional else PydanticUndefined,
|
|
297
|
+
name=alias,
|
|
298
|
+
ge=0.0,
|
|
299
|
+
lt=100.0,
|
|
300
|
+
decimal_places=1,
|
|
301
|
+
)
|
mms_client/types/offer.py
CHANGED
|
@@ -61,13 +61,24 @@ class OfferStack(Payload):
|
|
|
61
61
|
mandatory.
|
|
62
62
|
"""
|
|
63
63
|
|
|
64
|
-
# A number used to identify this PQ pair within the offer
|
|
64
|
+
# A number used to identify this PQ pair within the offer. Ensure that there are no duplicates of the combination
|
|
65
|
+
# of the same resource, pattern number, start date and time, and bid management number in the submitted data. In
|
|
66
|
+
# case of multiple bids in the same time slot and for the same resource, each bid must have a unique number. Enter
|
|
67
|
+
# '1' for a single bid.
|
|
65
68
|
number: int = attr(name="StackNumber", ge=1, le=20)
|
|
66
69
|
|
|
67
|
-
# The minimum quantity that must be provided before the offer can be awarded
|
|
70
|
+
# The minimum quantity that must be provided before the offer can be awarded. For resources with dedicated line
|
|
71
|
+
# control or monitoring methods, must be 5000kW or higher. For resources with simple command (online) control
|
|
72
|
+
# or monitoring methods, must be 1000kW or higher.
|
|
68
73
|
minimum_quantity_kw: int = power_positive("MinimumQuantityInKw")
|
|
69
74
|
|
|
70
|
-
# The primary bid quantity in kW
|
|
75
|
+
# The primary bid quantity in kW. Must be equal to or greater than minimum_quantity_kw. For non-VPP resources, the
|
|
76
|
+
# total bid volume of all records with the same resource and start date and time must be below the maximum supply
|
|
77
|
+
# capacity for the corresponding product category of the power source. However, in the case of tertiary adjustment
|
|
78
|
+
# power 2, it must be the value obtained by subtracting the total agreed capacity of effective tertiary adjustment
|
|
79
|
+
# power 1. For VPP power sources, the total bid volume of all records with the same power source code and start
|
|
80
|
+
# date and time must be below the maximum supply capacity registered for the corresponding product category in the
|
|
81
|
+
# pattern number.
|
|
71
82
|
primary_qty_kw: Optional[int] = power_positive("PrimaryOfferQuantityInKw", True)
|
|
72
83
|
|
|
73
84
|
# The first secondary bid quantity in kW
|
|
@@ -107,7 +118,8 @@ class OfferData(Payload):
|
|
|
107
118
|
# The direction of the offer (buy, sell)
|
|
108
119
|
direction: Direction = attr(name="Direction")
|
|
109
120
|
|
|
110
|
-
# The type of market for which the offer is being submitted
|
|
121
|
+
# The type of market for which the offer is being submitted. Must be a valid pattern number for the submission date
|
|
122
|
+
# Required for VPP resources. Ensure there are no duplicate pattern numbers for the same resource and start time.
|
|
111
123
|
pattern_number: Optional[int] = dr_patter_number("DrPatternNumber", True)
|
|
112
124
|
|
|
113
125
|
# The name of the BSP participant submitting the offer
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Contains objects for registrations."""
|
|
2
|
+
|
|
3
|
+
# Have to use this becasue pydantic doesn't like pendulum.Date. I've submitted a PR to address this but it hasn't been
|
|
4
|
+
# merged or released yet.
|
|
5
|
+
from datetime import date as Date
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
from pydantic_xml import attr
|
|
10
|
+
|
|
11
|
+
from mms_client.types.base import Envelope
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class QueryAction(Enum):
|
|
15
|
+
"""Represents the type of query being made."""
|
|
16
|
+
|
|
17
|
+
NORMAL = "NORMAL"
|
|
18
|
+
LATEST = "LATEST"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class QueryType(Enum):
|
|
22
|
+
"""Represents the type of data being queried."""
|
|
23
|
+
|
|
24
|
+
TRADE = "TRADE"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class RegistrationSubmit(Envelope):
|
|
28
|
+
"""Represents the base fields for a registration request."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class RegistrationQuery(Envelope):
|
|
32
|
+
"""Represents the base fields for a registration query."""
|
|
33
|
+
|
|
34
|
+
# The query type being made.
|
|
35
|
+
# NORMAL: Retrieve all records that match the specified conditions.
|
|
36
|
+
# LATEST: Retrieve only the latest record that matches the specified conditions.
|
|
37
|
+
action: QueryAction = attr(default=QueryAction.NORMAL, name="Action")
|
|
38
|
+
|
|
39
|
+
# The type of data being queried
|
|
40
|
+
query_type: QueryType = attr(default=QueryType.TRADE, name="DateType")
|
|
41
|
+
|
|
42
|
+
# Date of the transaction in the format "YYYY-MM-DD"
|
|
43
|
+
date: Optional[Date] = attr(default=None, name="Date")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class RegistrationApproval(Envelope):
|
|
47
|
+
"""Represents the base fields for a registration approval request."""
|