numerapi 2.22.0__py3-none-any.whl → 2.23.0.dev1__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.
- numerapi/base_api.py +350 -1
- {numerapi-2.22.0.dist-info → numerapi-2.23.0.dev1.dist-info}/METADATA +1 -1
- numerapi-2.23.0.dev1.dist-info/RECORD +14 -0
- numerapi-2.22.0.dist-info/RECORD +0 -14
- {numerapi-2.22.0.dist-info → numerapi-2.23.0.dev1.dist-info}/WHEEL +0 -0
- {numerapi-2.22.0.dist-info → numerapi-2.23.0.dev1.dist-info}/entry_points.txt +0 -0
- {numerapi-2.22.0.dist-info → numerapi-2.23.0.dev1.dist-info}/licenses/LICENSE +0 -0
- {numerapi-2.22.0.dist-info → numerapi-2.23.0.dev1.dist-info}/top_level.txt +0 -0
numerapi/base_api.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import datetime
|
|
4
4
|
import logging
|
|
5
5
|
import os
|
|
6
|
+
import warnings
|
|
6
7
|
from io import BytesIO
|
|
7
8
|
from typing import Dict, List, Tuple, Union
|
|
8
9
|
|
|
@@ -383,6 +384,211 @@ class Api:
|
|
|
383
384
|
}
|
|
384
385
|
return mapping
|
|
385
386
|
|
|
387
|
+
def v3_stake_auth(
|
|
388
|
+
self,
|
|
389
|
+
submission_id: str,
|
|
390
|
+
staker: str,
|
|
391
|
+
amount: float | str | None = None,
|
|
392
|
+
max_amount: float | str | None = None,
|
|
393
|
+
) -> Dict:
|
|
394
|
+
"""Issue a staking v3 authorization for a selected submission.
|
|
395
|
+
|
|
396
|
+
Args:
|
|
397
|
+
submission_id (str): submission id for the selected submission
|
|
398
|
+
staker (str): staker wallet address
|
|
399
|
+
amount (float or str, optional): max stake amount for the
|
|
400
|
+
authorization. Retained as a backwards-compatible alias for
|
|
401
|
+
`max_amount`.
|
|
402
|
+
max_amount (float or str, optional): max stake amount for the
|
|
403
|
+
authorization.
|
|
404
|
+
|
|
405
|
+
Returns:
|
|
406
|
+
dict: authorization payload with the following fields:
|
|
407
|
+
|
|
408
|
+
* authorizationSigner (`str`)
|
|
409
|
+
* authorizationDigest (`str`)
|
|
410
|
+
* chainId (`str`)
|
|
411
|
+
* deadline (`str`)
|
|
412
|
+
* maxAmount (`str`)
|
|
413
|
+
* amount (`str`) alias for `maxAmount`
|
|
414
|
+
* modelId (`str`)
|
|
415
|
+
* nmrAddress (`str`)
|
|
416
|
+
* nonce (`str`)
|
|
417
|
+
* roundId (`str`)
|
|
418
|
+
* signature (`str`)
|
|
419
|
+
* staker (`str`)
|
|
420
|
+
* stakingAddress (`str`)
|
|
421
|
+
* submissionId (`str`)
|
|
422
|
+
* submissionHash (`str`)
|
|
423
|
+
* tournamentId (`str`)
|
|
424
|
+
"""
|
|
425
|
+
if (amount is None) == (max_amount is None):
|
|
426
|
+
raise ValueError("Provide exactly one of amount or max_amount.")
|
|
427
|
+
|
|
428
|
+
max_amount = max_amount if max_amount is not None else amount
|
|
429
|
+
query = """
|
|
430
|
+
mutation($submissionId: ID!, $staker: String!, $maxAmount: String!) {
|
|
431
|
+
v3StakeAuth(
|
|
432
|
+
submissionId: $submissionId
|
|
433
|
+
staker: $staker
|
|
434
|
+
maxAmount: $maxAmount
|
|
435
|
+
) {
|
|
436
|
+
authorizationSigner
|
|
437
|
+
authorizationDigest
|
|
438
|
+
chainId
|
|
439
|
+
deadline
|
|
440
|
+
maxAmount
|
|
441
|
+
modelId
|
|
442
|
+
nmrAddress
|
|
443
|
+
nonce
|
|
444
|
+
roundId
|
|
445
|
+
signature
|
|
446
|
+
staker
|
|
447
|
+
stakingAddress
|
|
448
|
+
submissionId
|
|
449
|
+
submissionHash
|
|
450
|
+
tournamentId
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
"""
|
|
454
|
+
arguments = {
|
|
455
|
+
"submissionId": submission_id,
|
|
456
|
+
"staker": staker,
|
|
457
|
+
"maxAmount": str(max_amount),
|
|
458
|
+
}
|
|
459
|
+
authorization = self.raw_query(query, arguments, authorization=True)["data"][
|
|
460
|
+
"v3StakeAuth"
|
|
461
|
+
]
|
|
462
|
+
authorization["amount"] = authorization["maxAmount"]
|
|
463
|
+
return authorization
|
|
464
|
+
|
|
465
|
+
def v3_stake_config(self) -> Dict:
|
|
466
|
+
"""Fetch staking v3 contract configuration.
|
|
467
|
+
|
|
468
|
+
Returns:
|
|
469
|
+
dict: staking v3 configuration with the following fields:
|
|
470
|
+
|
|
471
|
+
* address (`str`)
|
|
472
|
+
* authorizationSigner (`str`)
|
|
473
|
+
* nmrAddress (`str`)
|
|
474
|
+
* owner (`str`)
|
|
475
|
+
* paused (`bool`)
|
|
476
|
+
* pendingOwner (`str`)
|
|
477
|
+
* serviceWallet (`str`)
|
|
478
|
+
"""
|
|
479
|
+
query = """
|
|
480
|
+
query {
|
|
481
|
+
v3StakeConfig {
|
|
482
|
+
address
|
|
483
|
+
authorizationSigner
|
|
484
|
+
nmrAddress
|
|
485
|
+
owner
|
|
486
|
+
paused
|
|
487
|
+
pendingOwner
|
|
488
|
+
serviceWallet
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
"""
|
|
492
|
+
return self.raw_query(query, authorization=True)["data"]["v3StakeConfig"]
|
|
493
|
+
|
|
494
|
+
def v3_stake_round(self, round_id: int | str) -> Dict:
|
|
495
|
+
"""Fetch staking v3 round status by round id.
|
|
496
|
+
|
|
497
|
+
Args:
|
|
498
|
+
round_id (int or str): round id
|
|
499
|
+
|
|
500
|
+
Returns:
|
|
501
|
+
dict: staking v3 round data with the following fields:
|
|
502
|
+
|
|
503
|
+
* closeTime (`str`)
|
|
504
|
+
* merkleRoot (`str`)
|
|
505
|
+
* openTime (`str`)
|
|
506
|
+
* payoutFactor (`str`)
|
|
507
|
+
* remainingBurn (`str`)
|
|
508
|
+
* remainingPayout (`str`)
|
|
509
|
+
* resolveTime (`str`)
|
|
510
|
+
* resolved (`bool`)
|
|
511
|
+
* roundId (`str`)
|
|
512
|
+
* stakeCap (`str`)
|
|
513
|
+
* stakeThreshold (`str`)
|
|
514
|
+
* state (`str`)
|
|
515
|
+
* totalPayout (`str`)
|
|
516
|
+
* totalStaked (`str`)
|
|
517
|
+
* tournamentId (`str`)
|
|
518
|
+
"""
|
|
519
|
+
query = """
|
|
520
|
+
query($roundId: String!) {
|
|
521
|
+
v3StakeRound(roundId: $roundId) {
|
|
522
|
+
closeTime
|
|
523
|
+
merkleRoot
|
|
524
|
+
openTime
|
|
525
|
+
payoutFactor
|
|
526
|
+
remainingBurn
|
|
527
|
+
remainingPayout
|
|
528
|
+
resolveTime
|
|
529
|
+
resolved
|
|
530
|
+
roundId
|
|
531
|
+
stakeCap
|
|
532
|
+
stakeThreshold
|
|
533
|
+
state
|
|
534
|
+
totalPayout
|
|
535
|
+
totalStaked
|
|
536
|
+
tournamentId
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
"""
|
|
540
|
+
arguments = {"roundId": str(round_id)}
|
|
541
|
+
return self.raw_query(query, arguments, authorization=True)["data"][
|
|
542
|
+
"v3StakeRound"
|
|
543
|
+
]
|
|
544
|
+
|
|
545
|
+
def v3_stake_claim(self, round_id: int | str, model_id: str, staker: str) -> Dict:
|
|
546
|
+
"""Fetch a staking v3 claim proof for a model and staker.
|
|
547
|
+
|
|
548
|
+
Args:
|
|
549
|
+
round_id (int or str): round id
|
|
550
|
+
model_id (str): model id
|
|
551
|
+
staker (str): staker wallet address
|
|
552
|
+
|
|
553
|
+
Returns:
|
|
554
|
+
dict: claim payload with the following fields:
|
|
555
|
+
|
|
556
|
+
* apiModelId (`str`)
|
|
557
|
+
* burnAmountWei (`str`)
|
|
558
|
+
* merkleRoot (`str`)
|
|
559
|
+
* modelId (`str`)
|
|
560
|
+
* payoutAmountWei (`str`)
|
|
561
|
+
* proof (`list` of `str`)
|
|
562
|
+
* roundId (`str`)
|
|
563
|
+
* staker (`str`)
|
|
564
|
+
* submissionId (`str`)
|
|
565
|
+
* tournamentId (`str`)
|
|
566
|
+
"""
|
|
567
|
+
query = """
|
|
568
|
+
query($roundId: String!, $modelId: ID!, $staker: String!) {
|
|
569
|
+
v3StakeClaim(roundId: $roundId, modelId: $modelId, staker: $staker) {
|
|
570
|
+
apiModelId
|
|
571
|
+
burnAmountWei
|
|
572
|
+
merkleRoot
|
|
573
|
+
modelId
|
|
574
|
+
payoutAmountWei
|
|
575
|
+
proof
|
|
576
|
+
roundId
|
|
577
|
+
staker
|
|
578
|
+
submissionId
|
|
579
|
+
tournamentId
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
"""
|
|
583
|
+
arguments = {
|
|
584
|
+
"roundId": str(round_id),
|
|
585
|
+
"modelId": model_id,
|
|
586
|
+
"staker": staker,
|
|
587
|
+
}
|
|
588
|
+
return self.raw_query(query, arguments, authorization=True)["data"][
|
|
589
|
+
"v3StakeClaim"
|
|
590
|
+
]
|
|
591
|
+
|
|
386
592
|
def get_current_round(self, tournament: int | None = None) -> int | None:
|
|
387
593
|
"""Get number of the current active round.
|
|
388
594
|
|
|
@@ -841,9 +1047,145 @@ class Api:
|
|
|
841
1047
|
utils.replace(results, "updatedAt", utils.parse_datetime_string)
|
|
842
1048
|
return results
|
|
843
1049
|
|
|
1050
|
+
def submission_scores(
|
|
1051
|
+
self,
|
|
1052
|
+
model_id: str,
|
|
1053
|
+
display_name: str | None = None,
|
|
1054
|
+
version: str | None = None,
|
|
1055
|
+
day: int | None = None,
|
|
1056
|
+
resolved: bool | None = None,
|
|
1057
|
+
tournament: int | None = None,
|
|
1058
|
+
last_n_rounds: int | None = None,
|
|
1059
|
+
distinct_on_round: bool | None = None,
|
|
1060
|
+
) -> List[Dict]:
|
|
1061
|
+
"""Fetch submission score history for a model.
|
|
1062
|
+
|
|
1063
|
+
Args:
|
|
1064
|
+
model_id (str): target model UUID
|
|
1065
|
+
display_name (str, optional): score metric name filter
|
|
1066
|
+
version (str, optional): score version filter
|
|
1067
|
+
day (int, optional): day filter
|
|
1068
|
+
resolved (bool, optional): resolved-state filter
|
|
1069
|
+
tournament (int, optional): tournament filter, defaults to the
|
|
1070
|
+
API instance tournament
|
|
1071
|
+
last_n_rounds (int, optional): limit by most recent rounds
|
|
1072
|
+
distinct_on_round (bool, optional): keep only the latest score per
|
|
1073
|
+
round after applying other filters
|
|
1074
|
+
|
|
1075
|
+
Returns:
|
|
1076
|
+
list of dicts: list of submission score entries
|
|
1077
|
+
"""
|
|
1078
|
+
|
|
1079
|
+
query = """
|
|
1080
|
+
query($modelId: ID!
|
|
1081
|
+
$displayName: String
|
|
1082
|
+
$version: String
|
|
1083
|
+
$day: Int
|
|
1084
|
+
$resolved: Boolean
|
|
1085
|
+
$tournament: Int
|
|
1086
|
+
$lastNRounds: Int
|
|
1087
|
+
$distinctOnRound: Boolean) {
|
|
1088
|
+
submissionScores(modelId: $modelId
|
|
1089
|
+
displayName: $displayName
|
|
1090
|
+
version: $version
|
|
1091
|
+
day: $day
|
|
1092
|
+
resolved: $resolved
|
|
1093
|
+
tournament: $tournament
|
|
1094
|
+
lastNRounds: $lastNRounds
|
|
1095
|
+
distinctOnRound: $distinctOnRound) {
|
|
1096
|
+
roundId
|
|
1097
|
+
submissionId
|
|
1098
|
+
roundNumber
|
|
1099
|
+
roundResolveTime
|
|
1100
|
+
roundScoreTime
|
|
1101
|
+
roundCloseStakingTime
|
|
1102
|
+
value
|
|
1103
|
+
percentile
|
|
1104
|
+
displayName
|
|
1105
|
+
version
|
|
1106
|
+
date
|
|
1107
|
+
day
|
|
1108
|
+
resolveDate
|
|
1109
|
+
resolved
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
"""
|
|
1113
|
+
arguments = {
|
|
1114
|
+
"modelId": model_id,
|
|
1115
|
+
"displayName": display_name,
|
|
1116
|
+
"version": version,
|
|
1117
|
+
"day": day,
|
|
1118
|
+
"resolved": resolved,
|
|
1119
|
+
"tournament": self.tournament_id if tournament is None else tournament,
|
|
1120
|
+
"lastNRounds": last_n_rounds,
|
|
1121
|
+
"distinctOnRound": distinct_on_round,
|
|
1122
|
+
}
|
|
1123
|
+
scores = self.raw_query(query, arguments)["data"]["submissionScores"]
|
|
1124
|
+
for score in scores:
|
|
1125
|
+
utils.replace(score, "roundResolveTime", utils.parse_datetime_string)
|
|
1126
|
+
utils.replace(score, "roundScoreTime", utils.parse_datetime_string)
|
|
1127
|
+
utils.replace(score, "roundCloseStakingTime", utils.parse_datetime_string)
|
|
1128
|
+
utils.replace(score, "date", utils.parse_datetime_string)
|
|
1129
|
+
utils.replace(score, "resolveDate", utils.parse_datetime_string)
|
|
1130
|
+
return scores
|
|
1131
|
+
|
|
1132
|
+
def pending_model_payouts(self, tournament: int | None = None) -> Dict:
|
|
1133
|
+
"""Fetch actual and pending payouts for the authenticated user's models.
|
|
1134
|
+
|
|
1135
|
+
Args:
|
|
1136
|
+
tournament (int, optional): tournament filter, defaults to the API
|
|
1137
|
+
instance tournament
|
|
1138
|
+
|
|
1139
|
+
Returns:
|
|
1140
|
+
dict: payout groups with `actual` and `pending` lists
|
|
1141
|
+
"""
|
|
1142
|
+
|
|
1143
|
+
query = """
|
|
1144
|
+
query($tournament: Int!) {
|
|
1145
|
+
pendingModelPayouts(tournament: $tournament) {
|
|
1146
|
+
actual {
|
|
1147
|
+
roundId
|
|
1148
|
+
roundNumber
|
|
1149
|
+
roundResolveTime
|
|
1150
|
+
modelId
|
|
1151
|
+
modelName
|
|
1152
|
+
modelDisplayName
|
|
1153
|
+
payoutNmr
|
|
1154
|
+
payoutValue
|
|
1155
|
+
currencySymbol
|
|
1156
|
+
}
|
|
1157
|
+
pending {
|
|
1158
|
+
roundId
|
|
1159
|
+
roundNumber
|
|
1160
|
+
roundResolveTime
|
|
1161
|
+
modelId
|
|
1162
|
+
modelName
|
|
1163
|
+
modelDisplayName
|
|
1164
|
+
payoutNmr
|
|
1165
|
+
payoutValue
|
|
1166
|
+
currencySymbol
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
"""
|
|
1171
|
+
arguments = {
|
|
1172
|
+
"tournament": self.tournament_id if tournament is None else tournament
|
|
1173
|
+
}
|
|
1174
|
+
payouts = self.raw_query(query, arguments, authorization=True)["data"][
|
|
1175
|
+
"pendingModelPayouts"
|
|
1176
|
+
]
|
|
1177
|
+
for payout_type in ["actual", "pending"]:
|
|
1178
|
+
for payout in payouts[payout_type]:
|
|
1179
|
+
utils.replace(payout, "roundResolveTime", utils.parse_datetime_string)
|
|
1180
|
+
utils.replace(payout, "payoutNmr", utils.parse_float_string)
|
|
1181
|
+
utils.replace(payout, "payoutValue", utils.parse_float_string)
|
|
1182
|
+
return payouts
|
|
1183
|
+
|
|
844
1184
|
def round_model_performances_v2(self, model_id: str):
|
|
845
1185
|
"""Fetch round model performance of a user.
|
|
846
1186
|
|
|
1187
|
+
DEPRECATED - please use `submission_scores` instead when possible.
|
|
1188
|
+
|
|
847
1189
|
Args:
|
|
848
1190
|
model_id (str)
|
|
849
1191
|
|
|
@@ -871,6 +1213,13 @@ class Api:
|
|
|
871
1213
|
* percentile (`float`)
|
|
872
1214
|
* value (`float`): value of the metric
|
|
873
1215
|
"""
|
|
1216
|
+
warnings.warn(
|
|
1217
|
+
"`round_model_performances_v2` is deprecated because it relies on "
|
|
1218
|
+
"`v2RoundModelPerformances`. Use `submission_scores` instead when "
|
|
1219
|
+
"possible.",
|
|
1220
|
+
DeprecationWarning,
|
|
1221
|
+
stacklevel=2,
|
|
1222
|
+
)
|
|
874
1223
|
|
|
875
1224
|
query = """
|
|
876
1225
|
query($modelId: String!
|
|
@@ -1200,7 +1549,7 @@ class Api:
|
|
|
1200
1549
|
return False
|
|
1201
1550
|
if raw is None:
|
|
1202
1551
|
return False
|
|
1203
|
-
open_time = utils.parse_datetime_string(raw[
|
|
1552
|
+
open_time = utils.parse_datetime_string(raw["openTime"])
|
|
1204
1553
|
now = datetime.datetime.now(tz=pytz.utc)
|
|
1205
1554
|
is_new_round = open_time > now - datetime.timedelta(hours=hours)
|
|
1206
1555
|
return is_new_round
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
numerapi/__init__.py,sha256=okDA4NGjUj1Q8bDJg4T1t0LTf_S3GPbXCdw8gla7_VU,449
|
|
2
|
+
numerapi/base_api.py,sha256=tdVIiy27RvKTpNTVZjdXVuCRjzzoOas_LO9N-MbGugo,71013
|
|
3
|
+
numerapi/cli.py,sha256=oaATypyxS0mlW2Uouby6Srq0DxWkouBg63uiXJY-QHM,8206
|
|
4
|
+
numerapi/cryptoapi.py,sha256=J9fAEhiaEfpvRCiDsSJz8rlazKD-4nkJRJ85f09jGp0,1502
|
|
5
|
+
numerapi/numerapi.py,sha256=cyTpNGRKr4BqK2jKA2Rj540tjyykpV3axz-6jLm1Flo,12323
|
|
6
|
+
numerapi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
numerapi/signalsapi.py,sha256=inKwMvuIWNGcDFzzdhqRO1RIms_qz1d7lkwA2dzvAoQ,10188
|
|
8
|
+
numerapi/utils.py,sha256=YgXujHyE1TLTf5v1pspcVAX89SQI3Zl3vMcbNlHZJDs,4688
|
|
9
|
+
numerapi-2.23.0.dev1.dist-info/licenses/LICENSE,sha256=BnOIJOdxTIxmbNPf_ZmCXqepXDTRWad1nQf9eYk-eSg,1056
|
|
10
|
+
numerapi-2.23.0.dev1.dist-info/METADATA,sha256=bgSq5xME7TXw3CnULv6QyN-3xkc5cBye5s39bcdym18,7041
|
|
11
|
+
numerapi-2.23.0.dev1.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
|
|
12
|
+
numerapi-2.23.0.dev1.dist-info/entry_points.txt,sha256=P7RHLytfftNPE14vRxml52-UEkyuAviRegWTID4a_ig,46
|
|
13
|
+
numerapi-2.23.0.dev1.dist-info/top_level.txt,sha256=7f4lKNQqRDEGaDXGIqRQCx-rU5pNl6ZgbXz864F1uXY,9
|
|
14
|
+
numerapi-2.23.0.dev1.dist-info/RECORD,,
|
numerapi-2.22.0.dist-info/RECORD
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
numerapi/__init__.py,sha256=okDA4NGjUj1Q8bDJg4T1t0LTf_S3GPbXCdw8gla7_VU,449
|
|
2
|
-
numerapi/base_api.py,sha256=vbR14JxijAeDGI5vTNa0TL1HKHO_hM0X9Ysq9W28emA,59280
|
|
3
|
-
numerapi/cli.py,sha256=oaATypyxS0mlW2Uouby6Srq0DxWkouBg63uiXJY-QHM,8206
|
|
4
|
-
numerapi/cryptoapi.py,sha256=J9fAEhiaEfpvRCiDsSJz8rlazKD-4nkJRJ85f09jGp0,1502
|
|
5
|
-
numerapi/numerapi.py,sha256=cyTpNGRKr4BqK2jKA2Rj540tjyykpV3axz-6jLm1Flo,12323
|
|
6
|
-
numerapi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
numerapi/signalsapi.py,sha256=inKwMvuIWNGcDFzzdhqRO1RIms_qz1d7lkwA2dzvAoQ,10188
|
|
8
|
-
numerapi/utils.py,sha256=YgXujHyE1TLTf5v1pspcVAX89SQI3Zl3vMcbNlHZJDs,4688
|
|
9
|
-
numerapi-2.22.0.dist-info/licenses/LICENSE,sha256=BnOIJOdxTIxmbNPf_ZmCXqepXDTRWad1nQf9eYk-eSg,1056
|
|
10
|
-
numerapi-2.22.0.dist-info/METADATA,sha256=UxdN8pX_RK1yl3oid8Jh4NJndoLwoHIXPeunkC1lSA0,7036
|
|
11
|
-
numerapi-2.22.0.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
|
|
12
|
-
numerapi-2.22.0.dist-info/entry_points.txt,sha256=P7RHLytfftNPE14vRxml52-UEkyuAviRegWTID4a_ig,46
|
|
13
|
-
numerapi-2.22.0.dist-info/top_level.txt,sha256=7f4lKNQqRDEGaDXGIqRQCx-rU5pNl6ZgbXz864F1uXY,9
|
|
14
|
-
numerapi-2.22.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|