kuhl-haus-mdp 0.1.9__py3-none-any.whl → 0.1.11__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.
- kuhl_haus/mdp/analyzers/massive_data_analyzer.py +8 -8
- kuhl_haus/mdp/analyzers/top_stocks.py +16 -15
- kuhl_haus/mdp/components/market_data_cache.py +27 -3
- kuhl_haus/mdp/models/constants.py +24 -0
- kuhl_haus/mdp/models/market_data_cache_ttl.py +24 -15
- {kuhl_haus_mdp-0.1.9.dist-info → kuhl_haus_mdp-0.1.11.dist-info}/METADATA +1 -1
- {kuhl_haus_mdp-0.1.9.dist-info → kuhl_haus_mdp-0.1.11.dist-info}/RECORD +10 -9
- {kuhl_haus_mdp-0.1.9.dist-info → kuhl_haus_mdp-0.1.11.dist-info}/WHEEL +0 -0
- {kuhl_haus_mdp-0.1.9.dist-info → kuhl_haus_mdp-0.1.11.dist-info}/entry_points.txt +0 -0
- {kuhl_haus_mdp-0.1.9.dist-info → kuhl_haus_mdp-0.1.11.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -50,7 +50,7 @@ class MassiveDataAnalyzer:
|
|
|
50
50
|
return [MarketDataAnalyzerResult(
|
|
51
51
|
data=data,
|
|
52
52
|
cache_key=f"{MarketDataCacheKeys.HALTS.value}:{symbol}",
|
|
53
|
-
cache_ttl=MarketDataCacheTTL.
|
|
53
|
+
cache_ttl=MarketDataCacheTTL.HALTS.value,
|
|
54
54
|
publish_key=f"{MarketDataCacheKeys.HALTS.value}:{symbol}",
|
|
55
55
|
)]
|
|
56
56
|
|
|
@@ -58,8 +58,8 @@ class MassiveDataAnalyzer:
|
|
|
58
58
|
def handle_equity_agg_event(data: dict, symbol: str) -> Optional[List[MarketDataAnalyzerResult]]:
|
|
59
59
|
return [MarketDataAnalyzerResult(
|
|
60
60
|
data=data,
|
|
61
|
-
cache_key=f"{MarketDataCacheKeys.AGGREGATE.value}:{symbol}",
|
|
62
|
-
cache_ttl=MarketDataCacheTTL.
|
|
61
|
+
# cache_key=f"{MarketDataCacheKeys.AGGREGATE.value}:{symbol}",
|
|
62
|
+
# cache_ttl=MarketDataCacheTTL.AGGREGATE.value,
|
|
63
63
|
publish_key=f"{MarketDataCacheKeys.AGGREGATE.value}:{symbol}",
|
|
64
64
|
)]
|
|
65
65
|
|
|
@@ -67,8 +67,8 @@ class MassiveDataAnalyzer:
|
|
|
67
67
|
def handle_equity_trade_event(data: dict, symbol: str) -> Optional[List[MarketDataAnalyzerResult]]:
|
|
68
68
|
return [MarketDataAnalyzerResult(
|
|
69
69
|
data=data,
|
|
70
|
-
cache_key=f"{MarketDataCacheKeys.TRADES.value}:{symbol}",
|
|
71
|
-
cache_ttl=MarketDataCacheTTL.
|
|
70
|
+
# cache_key=f"{MarketDataCacheKeys.TRADES.value}:{symbol}",
|
|
71
|
+
# cache_ttl=MarketDataCacheTTL.TRADES.value,
|
|
72
72
|
publish_key=f"{MarketDataCacheKeys.TRADES.value}:{symbol}",
|
|
73
73
|
)]
|
|
74
74
|
|
|
@@ -76,8 +76,8 @@ class MassiveDataAnalyzer:
|
|
|
76
76
|
def handle_equity_quote_event(data: dict, symbol: str) -> Optional[List[MarketDataAnalyzerResult]]:
|
|
77
77
|
return [MarketDataAnalyzerResult(
|
|
78
78
|
data=data,
|
|
79
|
-
cache_key=f"{MarketDataCacheKeys.QUOTES.value}:{symbol}",
|
|
80
|
-
cache_ttl=MarketDataCacheTTL.
|
|
79
|
+
# cache_key=f"{MarketDataCacheKeys.QUOTES.value}:{symbol}",
|
|
80
|
+
# cache_ttl=MarketDataCacheTTL.QUOTES.value,
|
|
81
81
|
publish_key=f"{MarketDataCacheKeys.QUOTES.value}:{symbol}",
|
|
82
82
|
)]
|
|
83
83
|
|
|
@@ -88,6 +88,6 @@ class MassiveDataAnalyzer:
|
|
|
88
88
|
return [MarketDataAnalyzerResult(
|
|
89
89
|
data=data,
|
|
90
90
|
cache_key=cache_key,
|
|
91
|
-
cache_ttl=MarketDataCacheTTL.
|
|
91
|
+
cache_ttl=MarketDataCacheTTL.UNKNOWN.value,
|
|
92
92
|
publish_key=f"{MarketDataCacheKeys.UNKNOWN.value}",
|
|
93
93
|
)]
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import time
|
|
3
|
-
from datetime import datetime, timezone
|
|
4
|
-
from typing import Optional, List
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from typing import Optional, List
|
|
5
5
|
from zoneinfo import ZoneInfo
|
|
6
6
|
|
|
7
|
-
from massive.exceptions import BadResponse
|
|
8
|
-
from massive.rest import RESTClient
|
|
9
|
-
from massive.rest.models import (
|
|
10
|
-
TickerSnapshot,
|
|
11
|
-
Agg,
|
|
12
|
-
)
|
|
13
7
|
from massive.websocket.models import (
|
|
14
8
|
EquityAgg,
|
|
15
9
|
EventType
|
|
@@ -19,6 +13,7 @@ from kuhl_haus.mdp.analyzers.analyzer import Analyzer
|
|
|
19
13
|
from kuhl_haus.mdp.components.market_data_cache import MarketDataCache
|
|
20
14
|
from kuhl_haus.mdp.models.market_data_analyzer_result import MarketDataAnalyzerResult
|
|
21
15
|
from kuhl_haus.mdp.models.market_data_cache_keys import MarketDataCacheKeys
|
|
16
|
+
from kuhl_haus.mdp.models.market_data_cache_ttl import MarketDataCacheTTL
|
|
22
17
|
from kuhl_haus.mdp.models.market_data_pubsub_keys import MarketDataPubSubKeys
|
|
23
18
|
from kuhl_haus.mdp.models.top_stocks_cache_item import TopStocksCacheItem
|
|
24
19
|
|
|
@@ -94,24 +89,24 @@ class TopStocksAnalyzer(Analyzer):
|
|
|
94
89
|
MarketDataAnalyzerResult(
|
|
95
90
|
data=self.cache_item.to_dict(),
|
|
96
91
|
cache_key=self.cache_key,
|
|
97
|
-
cache_ttl=
|
|
92
|
+
cache_ttl=MarketDataCacheTTL.TOP_STOCKS_SCANNER.value,
|
|
98
93
|
),
|
|
99
94
|
MarketDataAnalyzerResult(
|
|
100
95
|
data=self.cache_item.top_volume(100),
|
|
101
96
|
cache_key=MarketDataPubSubKeys.TOP_VOLUME_SCANNER.value,
|
|
102
|
-
cache_ttl=
|
|
97
|
+
cache_ttl=MarketDataCacheTTL.TOP_VOLUME_SCANNER.value,
|
|
103
98
|
publish_key=MarketDataPubSubKeys.TOP_VOLUME_SCANNER.value,
|
|
104
99
|
),
|
|
105
100
|
MarketDataAnalyzerResult(
|
|
106
101
|
data=self.cache_item.top_gainers(500),
|
|
107
102
|
cache_key=MarketDataPubSubKeys.TOP_GAINERS_SCANNER.value,
|
|
108
|
-
cache_ttl=
|
|
103
|
+
cache_ttl=MarketDataCacheTTL.TOP_GAINERS_SCANNER.value,
|
|
109
104
|
publish_key=MarketDataPubSubKeys.TOP_GAINERS_SCANNER.value,
|
|
110
105
|
),
|
|
111
106
|
MarketDataAnalyzerResult(
|
|
112
107
|
data=self.cache_item.top_gappers(500),
|
|
113
108
|
cache_key=MarketDataPubSubKeys.TOP_GAPPERS_SCANNER.value,
|
|
114
|
-
cache_ttl=
|
|
109
|
+
cache_ttl=MarketDataCacheTTL.TOP_GAPPERS_SCANNER.value,
|
|
115
110
|
publish_key=MarketDataPubSubKeys.TOP_GAPPERS_SCANNER.value,
|
|
116
111
|
)
|
|
117
112
|
]
|
|
@@ -141,7 +136,11 @@ class TopStocksAnalyzer(Analyzer):
|
|
|
141
136
|
prev_day_volume = snapshot.prev_day.volume
|
|
142
137
|
prev_day_vwap = snapshot.prev_day.vwap
|
|
143
138
|
break
|
|
144
|
-
except
|
|
139
|
+
except KeyError:
|
|
140
|
+
retry_count += 1
|
|
141
|
+
await self.cache.delete_ticker_snapshot(event.symbol)
|
|
142
|
+
except Exception as e:
|
|
143
|
+
self.logger.error(f"Failed to get snapshot for {event.symbol}: {e}")
|
|
145
144
|
retry_count += 1
|
|
146
145
|
if retry_count == max_tries and prev_day_close == 0:
|
|
147
146
|
self.logger.error(f"Failed to get snapshot for {event.symbol} after {max_tries} tries.")
|
|
@@ -155,7 +154,8 @@ class TopStocksAnalyzer(Analyzer):
|
|
|
155
154
|
try:
|
|
156
155
|
avg_volume = await self.cache.get_avg_volume(event.symbol)
|
|
157
156
|
break
|
|
158
|
-
except Exception:
|
|
157
|
+
except Exception as e:
|
|
158
|
+
self.logger.error(f"Failed to get average volume for {event.symbol}: {e}")
|
|
159
159
|
retry_count += 1
|
|
160
160
|
if retry_count == max_tries and avg_volume == 0:
|
|
161
161
|
self.logger.error(f"Failed to get average volume for {event.symbol} after {max_tries} tries.")
|
|
@@ -169,7 +169,8 @@ class TopStocksAnalyzer(Analyzer):
|
|
|
169
169
|
try:
|
|
170
170
|
free_float = await self.cache.get_free_float(event.symbol)
|
|
171
171
|
break
|
|
172
|
-
except Exception:
|
|
172
|
+
except Exception as e:
|
|
173
|
+
self.logger.error(f"Failed to get free float for {event.symbol}: {e}")
|
|
173
174
|
retry_count += 1
|
|
174
175
|
if retry_count == max_tries and free_float == 0:
|
|
175
176
|
self.logger.error(f"Failed to get free float for {event.symbol} after {max_tries} tries.")
|
|
@@ -26,6 +26,18 @@ class MarketDataCache:
|
|
|
26
26
|
self.redis_client = redis_client
|
|
27
27
|
self.http_session = None
|
|
28
28
|
|
|
29
|
+
async def delete_cache(self, cache_key: str):
|
|
30
|
+
"""
|
|
31
|
+
Delete cache entry.
|
|
32
|
+
|
|
33
|
+
:arg cache_key: Cache key to delete
|
|
34
|
+
"""
|
|
35
|
+
try:
|
|
36
|
+
await self.redis_client.delete(cache_key)
|
|
37
|
+
self.logger.info(f"Deleted cache entry: {cache_key}")
|
|
38
|
+
except Exception as e:
|
|
39
|
+
self.logger.error(f"Error deleting cache entry: {e}")
|
|
40
|
+
|
|
29
41
|
async def get_cache(self, cache_key: str) -> Optional[dict]:
|
|
30
42
|
"""Fetch current value from Redis cache (for snapshot requests)."""
|
|
31
43
|
value = await self.redis_client.get(cache_key)
|
|
@@ -44,6 +56,16 @@ class MarketDataCache:
|
|
|
44
56
|
await self.redis_client.publish(publish_key, json.dumps(data))
|
|
45
57
|
self.logger.info(f"Published data for {publish_key}")
|
|
46
58
|
|
|
59
|
+
async def delete_ticker_snapshot(self, ticker: str):
|
|
60
|
+
"""
|
|
61
|
+
Delete ticker snapshot from cache.
|
|
62
|
+
|
|
63
|
+
:param ticker: symbol of ticker to delete snapshot for
|
|
64
|
+
:return: None
|
|
65
|
+
"""
|
|
66
|
+
cache_key = f"{MarketDataCacheKeys.TICKER_SNAPSHOTS.value}:{ticker}"
|
|
67
|
+
await self.delete_cache(cache_key=cache_key)
|
|
68
|
+
|
|
47
69
|
async def get_ticker_snapshot(self, ticker: str) -> TickerSnapshot:
|
|
48
70
|
self.logger.info(f"Getting snapshot for {ticker}")
|
|
49
71
|
cache_key = f"{MarketDataCacheKeys.TICKER_SNAPSHOTS.value}:{ticker}"
|
|
@@ -61,7 +83,7 @@ class MarketDataCache:
|
|
|
61
83
|
await self.cache_data(
|
|
62
84
|
data=data,
|
|
63
85
|
cache_key=cache_key,
|
|
64
|
-
cache_ttl=MarketDataCacheTTL.
|
|
86
|
+
cache_ttl=MarketDataCacheTTL.TICKER_SNAPSHOTS.value
|
|
65
87
|
)
|
|
66
88
|
return snapshot
|
|
67
89
|
|
|
@@ -109,13 +131,15 @@ class MarketDataCache:
|
|
|
109
131
|
periods_calculated += 1
|
|
110
132
|
else:
|
|
111
133
|
break
|
|
134
|
+
if periods_calculated == 0:
|
|
135
|
+
raise Exception(f"No volume data returned for {ticker}")
|
|
112
136
|
avg_volume = total_volume / periods_calculated
|
|
113
137
|
|
|
114
138
|
self.logger.info(f"average volume {ticker}: {avg_volume}")
|
|
115
139
|
await self.cache_data(
|
|
116
140
|
data=avg_volume,
|
|
117
141
|
cache_key=cache_key,
|
|
118
|
-
cache_ttl=MarketDataCacheTTL.
|
|
142
|
+
cache_ttl=MarketDataCacheTTL.TICKER_AVG_VOLUME.value
|
|
119
143
|
)
|
|
120
144
|
return avg_volume
|
|
121
145
|
|
|
@@ -162,7 +186,7 @@ class MarketDataCache:
|
|
|
162
186
|
await self.cache_data(
|
|
163
187
|
data=free_float,
|
|
164
188
|
cache_key=cache_key,
|
|
165
|
-
cache_ttl=MarketDataCacheTTL.
|
|
189
|
+
cache_ttl=MarketDataCacheTTL.TICKER_FREE_FLOAT.value
|
|
166
190
|
)
|
|
167
191
|
return free_float
|
|
168
192
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Minutes in seconds
|
|
2
|
+
ONE_MINUTE = 60
|
|
3
|
+
FIVE_MINUTES = 300
|
|
4
|
+
TEN_MINUTES = 600
|
|
5
|
+
FIFTEEN_MINUTES = 900
|
|
6
|
+
TWENTY_MINUTES = 1200
|
|
7
|
+
THIRTY_MINUTES = 1800
|
|
8
|
+
|
|
9
|
+
# Hours in seconds
|
|
10
|
+
ONE_HOUR = 3600
|
|
11
|
+
TWO_HOURS = 7200
|
|
12
|
+
FOUR_HOURS = 14400
|
|
13
|
+
SIX_HOURS = 21600
|
|
14
|
+
EIGHT_HOURS = 28800
|
|
15
|
+
TWELVE_HOURS = 43200
|
|
16
|
+
|
|
17
|
+
# Days in seconds
|
|
18
|
+
ONE_DAY = 86400
|
|
19
|
+
TWO_DAYS = 172800
|
|
20
|
+
THREE_DAYS = 259200
|
|
21
|
+
FOUR_DAYS = 345600
|
|
22
|
+
FIVE_DAYS = 432000
|
|
23
|
+
SIX_DAYS = 518400
|
|
24
|
+
SEVEN_DAYS = 604800
|
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
|
+
from kuhl_haus.mdp.models.constants import (
|
|
3
|
+
EIGHT_HOURS,
|
|
4
|
+
FIVE_MINUTES,
|
|
5
|
+
ONE_DAY,
|
|
6
|
+
ONE_HOUR,
|
|
7
|
+
THREE_DAYS,
|
|
8
|
+
TWELVE_HOURS,
|
|
9
|
+
)
|
|
2
10
|
|
|
3
11
|
|
|
4
12
|
class MarketDataCacheTTL(Enum):
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
TWELVE_HOURS = 43200
|
|
13
|
+
# Raw market data caches
|
|
14
|
+
AGGREGATE = FIVE_MINUTES
|
|
15
|
+
HALTS = ONE_DAY
|
|
16
|
+
QUOTES = ONE_HOUR
|
|
17
|
+
TRADES = ONE_HOUR
|
|
18
|
+
UNKNOWN = ONE_DAY
|
|
12
19
|
|
|
13
|
-
#
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
# Ticker caches
|
|
21
|
+
TICKER_AVG_VOLUME = TWELVE_HOURS
|
|
22
|
+
TICKER_FREE_FLOAT = TWELVE_HOURS
|
|
23
|
+
TICKER_SNAPSHOTS = EIGHT_HOURS
|
|
24
|
+
|
|
25
|
+
# Scanner caches
|
|
26
|
+
TOP_STOCKS_SCANNER = EIGHT_HOURS
|
|
27
|
+
TOP_VOLUME_SCANNER = THREE_DAYS
|
|
28
|
+
TOP_GAINERS_SCANNER = THREE_DAYS
|
|
29
|
+
TOP_GAPPERS_SCANNER = THREE_DAYS
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
kuhl_haus/mdp/__init__.py,sha256=5dEpAdB3kypH8tCRECoXwbly1WV9kFU5kh8ldGSa0VI,349
|
|
2
2
|
kuhl_haus/mdp/analyzers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
kuhl_haus/mdp/analyzers/analyzer.py,sha256=rIU1lcHwP2IBai0QLt0y-4ySg_ibWsutNU8JUgSxa1U,471
|
|
4
|
-
kuhl_haus/mdp/analyzers/massive_data_analyzer.py,sha256=
|
|
5
|
-
kuhl_haus/mdp/analyzers/top_stocks.py,sha256=
|
|
4
|
+
kuhl_haus/mdp/analyzers/massive_data_analyzer.py,sha256=i3XFnornuGGhe6nbk6QqIupND0MYTxwKPjvyFw_UBDU,3863
|
|
5
|
+
kuhl_haus/mdp/analyzers/top_stocks.py,sha256=uKH7uRJNaPRcpBvFLFM_wJVqo8QyHZ8Obj8zjU3dx9E,10255
|
|
6
6
|
kuhl_haus/mdp/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
kuhl_haus/mdp/components/market_data_cache.py,sha256=
|
|
7
|
+
kuhl_haus/mdp/components/market_data_cache.py,sha256=Jp5bTycVlY1y5cY9wolqlhwFFqUgBIHnTJh8zPyqxCY,8064
|
|
8
8
|
kuhl_haus/mdp/components/market_data_scanner.py,sha256=45MgprFlq03MvmIRYXENsrc7UlTcBE_hIsPyOvNs1zc,9745
|
|
9
9
|
kuhl_haus/mdp/components/widget_data_service.py,sha256=ikygD9NRpidcXBEqft5Q11rHy_eUOwKGyOLEezo-Dd4,7439
|
|
10
10
|
kuhl_haus/mdp/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -17,15 +17,16 @@ kuhl_haus/mdp/integ/massive_data_processor.py,sha256=H1WlbGtuSF45n7qLTLleuNlG-Ol
|
|
|
17
17
|
kuhl_haus/mdp/integ/massive_data_queues.py,sha256=zC_uV2vwZCMyVerDQ18RAQwIMMF75iK4qUSqwuWqgwc,5050
|
|
18
18
|
kuhl_haus/mdp/integ/web_socket_message_serde.py,sha256=XdaoaByc7IhtzbPDXBtXKOTjyDzfPSDuZVCoHSIaTl4,5468
|
|
19
19
|
kuhl_haus/mdp/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
kuhl_haus/mdp/models/constants.py,sha256=t55gVHl3IVDSA5Y-mu7zdA8FXAuw4TbOEkI2r2U__1Y,420
|
|
20
21
|
kuhl_haus/mdp/models/market_data_analyzer_result.py,sha256=iICb5GVCtuqARNbR1JNCAfbxMijM3uppDNdL8_FB3eI,422
|
|
21
22
|
kuhl_haus/mdp/models/market_data_cache_keys.py,sha256=_CSPtwvZo5W6cuj9vb7lRTu6MMuRGASe_VigAaOQXwE,1067
|
|
22
|
-
kuhl_haus/mdp/models/market_data_cache_ttl.py,sha256=
|
|
23
|
+
kuhl_haus/mdp/models/market_data_cache_ttl.py,sha256=mfNNrt0mkdppQtjb6gvyd2toSPCcmK_s-EcY5nQ0xLE,642
|
|
23
24
|
kuhl_haus/mdp/models/market_data_pubsub_keys.py,sha256=PEIPXK9jBehJB7G4pqoSuQZcfMZgOQq8Yho1itqv-1A,1306
|
|
24
25
|
kuhl_haus/mdp/models/market_data_scanner_names.py,sha256=BYn1C0rYgGF1Sq583BkHADKUu-28ytNZQ-XgptuCH-Y,260
|
|
25
26
|
kuhl_haus/mdp/models/massive_data_queue.py,sha256=MfYBcjVc4Fi61DWIvvhhWLUOiLmRpE9egtW-2KH6FTE,188
|
|
26
27
|
kuhl_haus/mdp/models/top_stocks_cache_item.py,sha256=Zn9LociBatP2a1OUfjXDFlWM7tCudO-CR4oj6jYfEEM,7916
|
|
27
|
-
kuhl_haus_mdp-0.1.
|
|
28
|
-
kuhl_haus_mdp-0.1.
|
|
29
|
-
kuhl_haus_mdp-0.1.
|
|
30
|
-
kuhl_haus_mdp-0.1.
|
|
31
|
-
kuhl_haus_mdp-0.1.
|
|
28
|
+
kuhl_haus_mdp-0.1.11.dist-info/METADATA,sha256=U5ev3SWbfM1L1bIChjE8LYmjz_xqyHMg-9lpRQcponQ,8888
|
|
29
|
+
kuhl_haus_mdp-0.1.11.dist-info/WHEEL,sha256=tsUv_t7BDeJeRHaSrczbGeuK-TtDpGsWi_JfpzD255I,90
|
|
30
|
+
kuhl_haus_mdp-0.1.11.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
|
|
31
|
+
kuhl_haus_mdp-0.1.11.dist-info/licenses/LICENSE.txt,sha256=DRkJftAJcMqoTkQ_Y6-HtKj3nm4pZah_p8XBZiYnw-c,1079
|
|
32
|
+
kuhl_haus_mdp-0.1.11.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|