subscriptions-to-csv 1.0.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.
@@ -0,0 +1,26 @@
1
+ """Convert subscription lists to CSV with EUR conversion.
2
+
3
+ This package provides both a command-line tool and a Python library
4
+ for converting subscription data to CSV format with EUR conversion.
5
+ """
6
+
7
+ from .converter import (
8
+ convert_subscriptions,
9
+ fetch_exchange_rate,
10
+ parse_subscription_data,
11
+ write_csv_file,
12
+ SubscriptionConverter,
13
+ SubscriptionParseError,
14
+ ExchangeRateError,
15
+ )
16
+
17
+ __version__ = "1.0.0"
18
+ __all__ = [
19
+ "convert_subscriptions",
20
+ "fetch_exchange_rate",
21
+ "parse_subscription_data",
22
+ "write_csv_file",
23
+ "SubscriptionConverter",
24
+ "SubscriptionParseError",
25
+ "ExchangeRateError",
26
+ ]
@@ -0,0 +1,100 @@
1
+ """Command-line interface for subscription-to-csv converter."""
2
+
3
+ import sys
4
+ import argparse
5
+ from pathlib import Path
6
+ from .converter import convert_subscriptions, ExchangeRateError, SubscriptionParseError
7
+
8
+
9
+ def parse_arguments():
10
+ """Parse command line arguments."""
11
+ parser = argparse.ArgumentParser(
12
+ description='Convert subscription list to CSV with EUR conversion',
13
+ formatter_class=argparse.RawDescriptionHelpFormatter,
14
+ epilog="""
15
+ Input File Format:
16
+ Each subscription consists of 2 lines:
17
+ Line 1: Service name (e.g., "Netflix")
18
+ Line 2: Price with currency, optionally preceded by tabs/spaces (e.g., "$15.99 USD" or "€9.99")
19
+
20
+ Example input file format:
21
+ Netflix
22
+ \t12.99 €
23
+ Spotify
24
+ \t9.99 €
25
+
26
+ Supported currencies: USD ($), EUR (€)
27
+ All prices are converted to EUR in the output CSV.
28
+
29
+ Examples:
30
+ subscriptions-to-csv subscriptions.txt output.csv
31
+ subscriptions-to-csv --input subscriptions.txt --output output.csv
32
+ subscriptions-to-csv # uses default files
33
+ """
34
+ )
35
+ parser.add_argument('input_pos', nargs='?', help='Input file containing subscriptions')
36
+ parser.add_argument('output_pos', nargs='?', help='Output CSV file')
37
+ parser.add_argument('--input', '-i', help='Input file containing subscriptions')
38
+ parser.add_argument('--output', '-o', help='Output CSV file')
39
+ args = parser.parse_args()
40
+
41
+ # Use optional args if provided, otherwise positional, otherwise defaults
42
+ args.input = args.input or args.input_pos or 'subscriptions.txt'
43
+ args.output = args.output or args.output_pos or 'subscriptions.csv'
44
+
45
+ return args
46
+
47
+
48
+ def print_summary(output_file: str, subscriptions: list, total_eur: float):
49
+ """Print summary of the conversion."""
50
+ print(f'Created {output_file}')
51
+ print('First few lines:')
52
+
53
+ # Show header and first few rows
54
+ if subscriptions:
55
+ print('Service,Price,Currency,PriceEUR')
56
+ for sub in subscriptions[:3]: # Show first 3 subscriptions
57
+ print(f"{sub['Service']},{sub['Price']},{sub['Currency']},{sub['PriceEUR']}")
58
+ if len(subscriptions) > 3:
59
+ print('...')
60
+
61
+ print(f'Total in EUR: {total_eur:.2f}')
62
+
63
+
64
+ def main():
65
+ """Main function to run the subscription converter CLI."""
66
+ args = parse_arguments()
67
+
68
+ try:
69
+ # Check if input file exists
70
+ input_path = Path(args.input)
71
+ if not input_path.exists():
72
+ print(f"Error: Input file '{args.input}' does not exist", file=sys.stderr)
73
+ sys.exit(1)
74
+
75
+ # Read input file
76
+ with open(args.input, 'r', encoding='utf-8') as f:
77
+ content = f.read()
78
+
79
+ # Convert subscriptions
80
+ subscriptions, total_eur = convert_subscriptions(content, args.output)
81
+
82
+ # Print summary
83
+ print_summary(args.output, subscriptions, total_eur)
84
+
85
+ except ExchangeRateError as e:
86
+ print(f"Error: {e}", file=sys.stderr)
87
+ sys.exit(1)
88
+ except SubscriptionParseError as e:
89
+ print(f"Error: {e}", file=sys.stderr)
90
+ sys.exit(1)
91
+ except FileNotFoundError as e:
92
+ print(f"Error: {e}", file=sys.stderr)
93
+ sys.exit(1)
94
+ except Exception as e:
95
+ print(f"Unexpected error: {e}", file=sys.stderr)
96
+ sys.exit(1)
97
+
98
+
99
+ if __name__ == '__main__':
100
+ main()
@@ -0,0 +1,209 @@
1
+ """Convert subscription lists to CSV with EUR conversion."""
2
+
3
+ import json
4
+ import urllib.request
5
+ import csv
6
+ from typing import List, Dict, Union, Optional, Tuple
7
+
8
+
9
+ class SubscriptionParseError(Exception):
10
+ """Exception raised when subscription data cannot be parsed."""
11
+ pass
12
+
13
+
14
+ class ExchangeRateError(Exception):
15
+ """Exception raised when exchange rate cannot be fetched."""
16
+ pass
17
+
18
+
19
+ def fetch_exchange_rate() -> float:
20
+ """Fetch USD to EUR exchange rate from API.
21
+
22
+ Returns:
23
+ Exchange rate as float
24
+
25
+ Note:
26
+ Falls back to 1.0 if the API request fails
27
+ """
28
+ try:
29
+ with urllib.request.urlopen('https://api.exchangerate-api.com/v4/latest/USD') as f:
30
+ data = json.load(f)
31
+ return data['rates']['EUR']
32
+ except Exception:
33
+ return 1.0 # fallback
34
+
35
+
36
+ def parse_subscription_data(content: Union[str, List[str]], rate: float) -> List[Dict[str, str]]:
37
+ """Parse subscription data from string or list of lines into list of dictionaries.
38
+
39
+ Args:
40
+ content: Subscription data as string or list of lines
41
+ rate: USD to EUR exchange rate
42
+
43
+ Returns:
44
+ List of subscription dictionaries with Service, Price, Currency, and PriceEUR keys
45
+
46
+ Raises:
47
+ SubscriptionParseError: If the content format is invalid
48
+ """
49
+ if isinstance(content, str):
50
+ lines = [line.strip() for line in content.strip().split('\n') if line.strip()]
51
+ else:
52
+ lines = [line.strip() for line in content if line.strip()]
53
+
54
+ subscriptions = []
55
+ for i in range(0, len(lines), 2):
56
+ if i + 1 >= len(lines):
57
+ break
58
+
59
+ service = lines[i]
60
+ price_line = lines[i + 1].lstrip('\t')
61
+ parts = price_line.split()
62
+
63
+ if not parts:
64
+ continue
65
+
66
+ price_str = parts[0].lstrip('$').lstrip('€')
67
+ try:
68
+ price = float(price_str)
69
+ except ValueError:
70
+ continue # Skip invalid prices
71
+
72
+ # Determine currency from the price string
73
+ if parts[0].startswith('€') or (len(parts) > 1 and parts[1].upper() in ('EUR', '€')):
74
+ currency = 'EUR'
75
+ elif parts[0].startswith('$') or (len(parts) > 1 and parts[1].upper() == 'USD'):
76
+ currency = 'USD'
77
+ else:
78
+ currency = parts[1] if len(parts) > 1 else 'EUR'
79
+
80
+ if currency.upper() == 'USD':
81
+ eur_price = price * rate
82
+ elif currency.upper() in ('EUR', '€'):
83
+ eur_price = price
84
+ else:
85
+ raise SubscriptionParseError(f"Unsupported currency '{currency}' for service '{service}'")
86
+
87
+ subscriptions.append({
88
+ 'Service': service,
89
+ 'Price': f'{price:.2f}',
90
+ 'Currency': currency.upper(),
91
+ 'PriceEUR': f'{eur_price:.2f}'
92
+ })
93
+
94
+ return subscriptions
95
+
96
+
97
+ def write_csv_file(subscriptions: List[Dict[str, str]], output_file: str) -> float:
98
+ """Write subscriptions to CSV file.
99
+
100
+ Args:
101
+ subscriptions: List of subscription dictionaries
102
+ output_file: Path to output CSV file
103
+
104
+ Returns:
105
+ Total EUR amount
106
+ """
107
+ total_eur = sum(float(sub['PriceEUR']) for sub in subscriptions)
108
+ with open(output_file, 'w', newline='') as csvfile:
109
+ fieldnames = ['Service', 'Price', 'Currency', 'PriceEUR']
110
+ writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
111
+ writer.writeheader()
112
+ writer.writerows(subscriptions)
113
+ return total_eur
114
+
115
+
116
+ def convert_subscriptions(
117
+ content: Union[str, List[str]],
118
+ output_file: Optional[str] = None,
119
+ exchange_rate: Optional[float] = None
120
+ ) -> Tuple[List[Dict[str, str]], float]:
121
+ """Convert subscription data to CSV format with EUR conversion.
122
+
123
+ This is the main library function for converting subscription data.
124
+
125
+ Args:
126
+ content: Subscription data as string or list of lines
127
+ output_file: Optional path to write CSV file to
128
+ exchange_rate: Optional exchange rate (will fetch if not provided)
129
+
130
+ Returns:
131
+ Tuple of (subscriptions_list, total_eur_amount)
132
+
133
+ Raises:
134
+ ExchangeRateError: If exchange rate cannot be fetched
135
+ SubscriptionParseError: If subscription data is malformed
136
+ """
137
+ rate = exchange_rate if exchange_rate is not None else fetch_exchange_rate()
138
+ subscriptions = parse_subscription_data(content, rate)
139
+
140
+ if output_file:
141
+ total_eur = write_csv_file(subscriptions, output_file)
142
+ else:
143
+ total_eur = sum(float(sub['PriceEUR']) for sub in subscriptions)
144
+
145
+ return subscriptions, total_eur
146
+
147
+
148
+ class SubscriptionConverter:
149
+ """Advanced converter class for subscription data processing.
150
+
151
+ This class provides more control over the conversion process,
152
+ allowing you to reuse exchange rates and customize behavior.
153
+ """
154
+
155
+ def __init__(self, exchange_rate: Optional[float] = None):
156
+ """Initialize the converter.
157
+
158
+ Args:
159
+ exchange_rate: Optional exchange rate to use (will fetch if not provided)
160
+ """
161
+ self.exchange_rate = exchange_rate
162
+
163
+ def set_exchange_rate(self, rate: float):
164
+ """Set a custom exchange rate."""
165
+ self.exchange_rate = rate
166
+
167
+ def get_exchange_rate(self) -> float:
168
+ """Get the current exchange rate, fetching if necessary."""
169
+ if self.exchange_rate is None:
170
+ self.exchange_rate = fetch_exchange_rate()
171
+ return self.exchange_rate
172
+
173
+ def convert(self, content: Union[str, List[str]]) -> List[Dict[str, str]]:
174
+ """Convert subscription content to list of dictionaries.
175
+
176
+ Args:
177
+ content: Subscription data as string or list of lines
178
+
179
+ Returns:
180
+ List of subscription dictionaries
181
+ """
182
+ rate = self.get_exchange_rate()
183
+ return parse_subscription_data(content, rate)
184
+
185
+ def convert_to_csv(self, content: Union[str, List[str]], output_file: str) -> float:
186
+ """Convert subscription content and write to CSV file.
187
+
188
+ Args:
189
+ content: Subscription data as string or list of lines
190
+ output_file: Path to output CSV file
191
+
192
+ Returns:
193
+ Total EUR amount
194
+ """
195
+ subscriptions = self.convert(content)
196
+ return write_csv_file(subscriptions, output_file)
197
+
198
+ def convert_with_total(self, content: Union[str, List[str]]) -> Tuple[List[Dict[str, str]], float]:
199
+ """Convert subscription content and return data with total.
200
+
201
+ Args:
202
+ content: Subscription data as string or list of lines
203
+
204
+ Returns:
205
+ Tuple of (subscriptions_list, total_eur_amount)
206
+ """
207
+ subscriptions = self.convert(content)
208
+ total_eur = sum(float(sub['PriceEUR']) for sub in subscriptions)
209
+ return subscriptions, total_eur
@@ -0,0 +1,331 @@
1
+ Metadata-Version: 2.4
2
+ Name: subscriptions-to-csv
3
+ Version: 1.0.0
4
+ Summary: Convert subscription lists to CSV with EUR conversion
5
+ Author: Subscription Converter Team
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/MBanucu/subscriptions-to-csv
8
+ Keywords: csv,subscriptions,eur,conversion,finance
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.6
11
+ Classifier: Programming Language :: Python :: 3.7
12
+ Classifier: Programming Language :: Python :: 3.8
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Topic :: Utilities
20
+ Classifier: Topic :: Office/Business :: Financial
21
+ Classifier: Intended Audience :: Developers
22
+ Classifier: Intended Audience :: End Users/Desktop
23
+ Requires-Python: >=3.6
24
+ Description-Content-Type: text/markdown
25
+
26
+ # Subscriptions to CSV
27
+
28
+ A Python package built as a Nix flake utility that provides both CLI and library functionality to convert subscription lists into CSV files with EUR conversions and totals. Includes comprehensive type hints, error handling, and a full test suite.
29
+
30
+ ## Description
31
+
32
+ This tool processes subscription data (from files or strings) containing service names and prices, generates CSV output with columns for Service, Price, Currency, and Price in EUR (with automatic USD to EUR conversion), and calculates total sums in EUR.
33
+
34
+ Available as both:
35
+ - **Command-line tool**: Process files directly from the terminal
36
+ - **Python library**: Import and use programmatically in your applications
37
+
38
+ The project includes comprehensive unit tests covering all major functionality and supports PyPI distribution.
39
+
40
+ ## Installation
41
+
42
+ ### Option 1: PyPI (Library + CLI)
43
+
44
+ ```bash
45
+ pip install subscriptions-to-csv
46
+ ```
47
+
48
+ This installs both the command-line tool and Python library.
49
+
50
+ ### Option 2: Nix Flake (Development/Direct Usage)
51
+
52
+ Ensure you have Nix installed with flakes enabled.
53
+
54
+ #### Clone the Repository
55
+
56
+ ```bash
57
+ git clone https://github.com/MBanucu/subscriptions-to-csv.git
58
+ cd subscriptions-to-csv
59
+
60
+ # Allow direnv to load the .envrc file (one-time setup)
61
+ direnv allow
62
+ ```
63
+
64
+ The project uses [direnv](https://direnv.net/) for automatic development environment loading. After running `direnv allow`, the Nix devShell will be automatically activated whenever you enter the directory.
65
+
66
+ #### Direct from GitHub
67
+
68
+ You can also use this flake directly from GitHub without cloning:
69
+
70
+ ```bash
71
+ # Run with default files
72
+ nix run github:MBanucu/subscriptions-to-csv#subscriptions-to-csv
73
+
74
+ # Specify input and output files
75
+ nix run github:MBanucu/subscriptions-to-csv#subscriptions-to-csv path/to/input.txt path/to/output.csv
76
+
77
+ # Show help
78
+ nix run github:MBanucu/subscriptions-to-csv#subscriptions-to-csv -- --help
79
+ ```
80
+
81
+ This approach allows you to use the tool immediately without downloading the source code.
82
+
83
+ **Note**: When using `nix run` directly from GitHub, use positional arguments for input/output files or the `--` separator before option flags. Both approaches work the same way. Options work normally when running locally after cloning.
84
+
85
+ ## Usage
86
+
87
+ ### CLI Usage
88
+
89
+ #### Basic Usage
90
+
91
+ ```bash
92
+ # Enter the development shell (or use direnv for automatic loading)
93
+ nix develop
94
+
95
+ # Run the converter
96
+ subscriptions-to-csv
97
+ ```
98
+
99
+ This will read `subscriptions.txt` and output `subscriptions.csv`.
100
+
101
+ **Note**: If you have direnv installed, the development shell will be automatically activated when you enter the directory, making the `nix develop` step unnecessary.
102
+
103
+ ### Custom Files
104
+
105
+ ```bash
106
+ # Specify input and output files (positional)
107
+ nix run .#subscriptions-to-csv path/to/input.txt path/to/output.csv
108
+
109
+ # Or using options
110
+ nix run .#subscriptions-to-csv --input path/to/input.txt --output path/to/output.csv
111
+ ```
112
+
113
+ ### Direct Run
114
+
115
+ ```bash
116
+ nix run .#subscriptions-to-csv
117
+ ```
118
+
119
+ ### Help
120
+
121
+ ```bash
122
+ # Show usage information
123
+ nix run .#subscriptions-to-csv -- --help
124
+ ```
125
+
126
+ Note: The `--` separates nix arguments from application arguments.
127
+
128
+ ## Library Usage
129
+
130
+ When installed via pip, you can use the package as a Python library:
131
+
132
+ ### Basic Usage
133
+
134
+ ```python
135
+ from subscriptions_to_csv import convert_subscriptions
136
+
137
+ # Convert from string data
138
+ data = """Netflix
139
+ $15.99 USD
140
+ Spotify
141
+ €9.99"""
142
+
143
+ subscriptions, total = convert_subscriptions(data)
144
+ print(f"Total: €{total:.2f}")
145
+ for sub in subscriptions:
146
+ print(f"{sub['Service']}: {sub['Price']} {sub['Currency']} = €{sub['PriceEUR']}")
147
+ ```
148
+
149
+ ### Advanced Usage
150
+
151
+ ```python
152
+ from subscriptions_to_csv import SubscriptionConverter, fetch_exchange_rate
153
+
154
+ # Manual control over exchange rates
155
+ converter = SubscriptionConverter()
156
+ converter.set_exchange_rate(0.85) # Set custom rate
157
+
158
+ # Convert and get data
159
+ subscriptions = converter.convert("Netflix\n$15.99 USD")
160
+ total, count = converter.convert_with_total("Netflix\n$15.99 USD")
161
+
162
+ # Write to CSV file
163
+ converter.convert_to_csv("Netflix\n$15.99 USD", "output.csv")
164
+
165
+ # Individual functions
166
+ rate = fetch_exchange_rate()
167
+ ```
168
+
169
+ ## Input Format
170
+
171
+ The input file should contain subscription data in the following format:
172
+
173
+ ```
174
+ Service Name
175
+ Price Currency
176
+ Service Name
177
+ Price Currency
178
+ ```
179
+
180
+ Example:
181
+
182
+ ```
183
+ Spotify
184
+ 12.99 €
185
+ Netflix
186
+ 19.99 €
187
+ GutHub Copilot Pro
188
+ $10.00 USD
189
+ ```
190
+
191
+ Supported currencies: € (Euro), USD (automatically converted to EUR).
192
+
193
+ ## Output
194
+
195
+ The output CSV contains:
196
+
197
+ - **Service**: The subscription name
198
+ - **Price**: The original price
199
+ - **Currency**: The original currency
200
+ - **PriceEUR**: The price in EUR (converted if necessary)
201
+
202
+ Plus a total sum in EUR printed to the console.
203
+
204
+ Example output:
205
+
206
+ ```
207
+ Service,Price,Currency,PriceEUR
208
+ Spotify,12.99,€,12.99
209
+ Netflix,19.99,€,19.99
210
+ GutHub Copilot Pro,10.00,USD,8.62
211
+ Total in EUR: 41.60
212
+ ```
213
+
214
+ ## Configuration
215
+
216
+ - **Input file**: Default `subscriptions.txt`, can be overridden with `--input` or positional argument
217
+ - **Output file**: Default `subscriptions.csv`, can be overridden with `--output` or positional argument
218
+ - **Exchange rate**: Automatically fetched from exchangerate-api.com
219
+ - **Fallback**: If API fails, uses rate 1.0
220
+
221
+ ## Test Coverage
222
+
223
+ The project includes comprehensive unit tests covering:
224
+ - Command-line argument parsing (default, positional, optional)
225
+ - Exchange rate API fetching with fallback behavior
226
+ - Subscription data parsing and currency conversion
227
+ - CSV file generation and total calculations
228
+ - Integration testing of the full workflow
229
+
230
+ ## Requirements
231
+
232
+ ### CLI Usage
233
+ - Nix with flakes support (for nix-based installation)
234
+ - Internet connection for exchange rate fetching
235
+
236
+ ### Library Usage
237
+ - Python 3.6+ (3.13 recommended)
238
+ - pip for installation
239
+ - Internet connection for exchange rate fetching
240
+
241
+ ## Development
242
+
243
+ ### Project Structure
244
+
245
+ The project is structured as a proper Python package:
246
+
247
+ - `flake.nix`: Nix flake configuration for multi-platform builds
248
+ - `flake.lock`: Nix flake lock file
249
+ - `.envrc`: Direnv configuration for automatic devShell loading
250
+ - `pyproject.toml`: Python package configuration and build system
251
+ - `subscriptions_to_csv/`: Main Python package
252
+ - `__init__.py`: Package initialization and exports
253
+ - `converter.py`: Core conversion functions and classes
254
+ - `cli.py`: Command-line interface
255
+ - `tests/test_main.py`: Comprehensive unit test suite
256
+
257
+ ### Building
258
+
259
+ Build the Python package:
260
+
261
+ ```bash
262
+ nix build
263
+ ```
264
+
265
+ This creates a proper Python package using `buildPythonPackage` that can be installed and distributed.
266
+
267
+ ### Testing
268
+
269
+ Run the comprehensive test suite including CLI integration tests:
270
+
271
+ ```bash
272
+ # Run unit tests (direnv automatically loads environment)
273
+ pytest
274
+
275
+ # Or manually enter devShell and run tests
276
+ nix develop --command pytest
277
+
278
+ # Run flake checks (includes CLI functionality tests)
279
+ nix flake check
280
+
281
+ # Run specific flake checks
282
+ nix build .#checks.x86_64-linux.help-test
283
+ nix build .#checks.x86_64-linux.basic-test
284
+ nix build .#checks.x86_64-linux.named-args-test
285
+ ```
286
+
287
+ The flake checks verify that:
288
+ - The `--help` command works correctly
289
+ - Basic functionality with sample data works
290
+ - Positional and named arguments function properly
291
+
292
+ ### Testing
293
+
294
+ ```bash
295
+ # Run the test suite (environment loads automatically with direnv)
296
+ pytest
297
+
298
+ # Or enter devShell manually
299
+ nix develop
300
+ pytest
301
+
302
+ # Run specific tests
303
+ pytest tests/test_main.py
304
+ pytest -k "parse" # Run tests matching pattern
305
+
306
+ # Manual testing - Run with defaults
307
+ nix run .#subscriptions-to-csv
308
+
309
+ # Test CLI options
310
+ nix run .#subscriptions-to-csv -- --help
311
+
312
+ # Check the output CSV and total
313
+ ```
314
+
315
+ ### Code Style
316
+
317
+ See AGENTS.md for detailed coding guidelines.
318
+
319
+ ## Contributing
320
+
321
+ 1. Fork the repository
322
+ 2. Create a feature branch
323
+ 3. Make changes
324
+ 4. Run tests: `pytest` (direnv automatically loads the environment)
325
+ 5. Test CLI: `nix run .#subscriptions-to-csv -- --help`
326
+ 6. Test library: `python3 -c "from subscriptions_to_csv import convert_subscriptions; print('Library works')"`
327
+ 7. Submit a pull request
328
+
329
+ ## License
330
+
331
+ This project is open source. Please check the license file if present.
@@ -0,0 +1,8 @@
1
+ subscriptions_to_csv/__init__.py,sha256=aZiEm8F7jC9z_0KN3hJdhsH6lgA2Y58aermSenJJEY0,635
2
+ subscriptions_to_csv/cli.py,sha256=etEm9R9BXlywSKD8YMRIMKGpOOroVF6SPbkhNRQOjbk,3267
3
+ subscriptions_to_csv/converter.py,sha256=EZ8ziBxGFkJYyzOvtQEFLP5uP-SevPYa34-6X--x9JU,6739
4
+ subscriptions_to_csv-1.0.0.dist-info/METADATA,sha256=kimiPYwOK1dRK3datpRgNY_5v7_2QgaI4XwwpDRyW8Y,9068
5
+ subscriptions_to_csv-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
+ subscriptions_to_csv-1.0.0.dist-info/entry_points.txt,sha256=euesE9jMcLXw3mocwe4eGn0EVLoIurCg_nPc6lNaXkA,71
7
+ subscriptions_to_csv-1.0.0.dist-info/top_level.txt,sha256=AxP-dG7UH18VV3vIIXuwPM9VWz3Lwxy4rhUNhsrEqN8,21
8
+ subscriptions_to_csv-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ subscriptions-to-csv = subscriptions_to_csv.cli:main
@@ -0,0 +1 @@
1
+ subscriptions_to_csv