safe-file-example 0.1.1__tar.gz → 0.2.0__tar.gz
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.
- {safe_file_example-0.1.1 → safe_file_example-0.2.0}/.gitignore +1 -1
- safe_file_example-0.2.0/.pre-commit-config.yaml +28 -0
- {safe_file_example-0.1.1 → safe_file_example-0.2.0}/PKG-INFO +10 -10
- {safe_file_example-0.1.1 → safe_file_example-0.2.0}/README.md +9 -9
- {safe_file_example-0.1.1 → safe_file_example-0.2.0}/pyproject.toml +2 -2
- safe_file_example-0.2.0/safe/__init__.py +1 -0
- {safe_file_example-0.1.1 → safe_file_example-0.2.0}/safe/main.py +88 -37
- safe_file_example-0.2.0/safe/modules/__init__.py +1 -0
- {safe_file_example-0.1.1 → safe_file_example-0.2.0}/safe/modules/export_module.py +3 -5
- safe_file_example-0.2.0/safe/modules/helper_functions.py +52 -0
- {safe_file_example-0.1.1 → safe_file_example-0.2.0}/safe/modules/import_module.py +1 -2
- {safe_file_example-0.1.1 → safe_file_example-0.2.0}/safe/modules/list_export_module.py +15 -10
- safe_file_example-0.1.1/safe/__init__.py +0 -1
- safe_file_example-0.1.1/safe/modules/__init__.py +0 -1
- safe_file_example-0.1.1/safe/modules/select_helper.py +0 -22
- {safe_file_example-0.1.1 → safe_file_example-0.2.0}/LICENSE +0 -0
- {safe_file_example-0.1.1 → safe_file_example-0.2.0}/safe/assets/safe_logo.png +0 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Optimized for performance with parallel execution
|
|
2
|
+
default_stages: [pre-commit]
|
|
3
|
+
repos:
|
|
4
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
5
|
+
rev: v6.0.0
|
|
6
|
+
hooks:
|
|
7
|
+
- id: trailing-whitespace
|
|
8
|
+
- id: end-of-file-fixer
|
|
9
|
+
- id: check-toml # This is not necessary to have here
|
|
10
|
+
- id: check-yaml
|
|
11
|
+
- id: check-added-large-files
|
|
12
|
+
args: ['--maxkb=10000']
|
|
13
|
+
- id: check-merge-conflict
|
|
14
|
+
- id: detect-private-key
|
|
15
|
+
- id: check-symlinks
|
|
16
|
+
- id: pretty-format-json
|
|
17
|
+
args: ["--autofix"]
|
|
18
|
+
- id: fix-byte-order-marker
|
|
19
|
+
- id: check-case-conflict
|
|
20
|
+
- id: requirements-txt-fixer
|
|
21
|
+
- id: mixed-line-ending
|
|
22
|
+
|
|
23
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
24
|
+
rev: v0.14.14
|
|
25
|
+
hooks:
|
|
26
|
+
- id: ruff
|
|
27
|
+
args: [--fix, --diff]
|
|
28
|
+
- id: ruff-format
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: safe-file-example
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A python package for interacting with SAFE File transfer API
|
|
5
5
|
Project-URL: Homepage, https://git.app.uib.no/itpublic/python/safe-file-example
|
|
6
6
|
Author-email: Dip Paul <dip.paul@uib.no>, Eirik Halseth <eirik.halseth@uib.no>, Marco Antonio Corrales Cardenas <marco.corrales@uib.no>
|
|
@@ -25,24 +25,23 @@ Requires-Dist: urllib3==2.6.3
|
|
|
25
25
|
Description-Content-Type: text/markdown
|
|
26
26
|
|
|
27
27
|
# `safe-file-example`
|
|
28
|
-
|
|
29
28
|
## Description
|
|
30
29
|
|
|
31
30
|
A python package for interacting with SAFE File transfer API
|
|
32
31
|
|
|
33
32
|
## Current Functionalities
|
|
34
33
|
|
|
35
|
-
- Generate a JWT Token
|
|
34
|
+
- Generate a JWT Token
|
|
36
35
|
- Import a File
|
|
37
36
|
- Export a File
|
|
38
37
|
|
|
39
|
-
## Prerequisite
|
|
38
|
+
## Prerequisite
|
|
40
39
|
|
|
41
|
-
- Having minimum Python 3.12 installed in your system and setting up a Python Virtual Environment.
|
|
40
|
+
- Having minimum Python 3.12 installed in your system and setting up a Python Virtual Environment.
|
|
42
41
|
|
|
43
42
|
## Workflow
|
|
44
43
|
|
|
45
|
-
- Activate your Python Virtual Environment,
|
|
44
|
+
- Activate your Python Virtual Environment,
|
|
46
45
|
- Then run the following command.
|
|
47
46
|
|
|
48
47
|
```bash
|
|
@@ -54,6 +53,7 @@ pip install safe-file-example
|
|
|
54
53
|
safe-file-example
|
|
55
54
|
```
|
|
56
55
|
|
|
56
|
+
|
|
57
57
|
**Usage**:
|
|
58
58
|
|
|
59
59
|
```console
|
|
@@ -66,19 +66,19 @@ $ safe-file-example [OPTIONS] COMMAND [ARGS]...
|
|
|
66
66
|
|
|
67
67
|
**Commands**:
|
|
68
68
|
|
|
69
|
-
* `token`: Get and store access token
|
|
69
|
+
* `get-token`: Get and store access token
|
|
70
70
|
* `import-file`: Import a file
|
|
71
71
|
* `export-file`: Export a file
|
|
72
72
|
* `interactive`: Run interactive mode
|
|
73
73
|
|
|
74
|
-
## `safe-file-example token`
|
|
74
|
+
## `safe-file-example get-token`
|
|
75
75
|
|
|
76
76
|
Get and store access token
|
|
77
77
|
|
|
78
78
|
**Usage**:
|
|
79
79
|
|
|
80
80
|
```console
|
|
81
|
-
$ safe-file-example token [OPTIONS]
|
|
81
|
+
$ safe-file-example get-token [OPTIONS]
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
**Options**:
|
|
@@ -132,4 +132,4 @@ $ safe-file-example interactive [OPTIONS]
|
|
|
132
132
|
|
|
133
133
|
**Options**:
|
|
134
134
|
|
|
135
|
-
* `--help`: Show this message and exit.
|
|
135
|
+
* `--help`: Show this message and exit.
|
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
# `safe-file-example`
|
|
2
|
-
|
|
3
2
|
## Description
|
|
4
3
|
|
|
5
4
|
A python package for interacting with SAFE File transfer API
|
|
6
5
|
|
|
7
6
|
## Current Functionalities
|
|
8
7
|
|
|
9
|
-
- Generate a JWT Token
|
|
8
|
+
- Generate a JWT Token
|
|
10
9
|
- Import a File
|
|
11
10
|
- Export a File
|
|
12
11
|
|
|
13
|
-
## Prerequisite
|
|
12
|
+
## Prerequisite
|
|
14
13
|
|
|
15
|
-
- Having minimum Python 3.12 installed in your system and setting up a Python Virtual Environment.
|
|
14
|
+
- Having minimum Python 3.12 installed in your system and setting up a Python Virtual Environment.
|
|
16
15
|
|
|
17
16
|
## Workflow
|
|
18
17
|
|
|
19
|
-
- Activate your Python Virtual Environment,
|
|
18
|
+
- Activate your Python Virtual Environment,
|
|
20
19
|
- Then run the following command.
|
|
21
20
|
|
|
22
21
|
```bash
|
|
@@ -28,6 +27,7 @@ pip install safe-file-example
|
|
|
28
27
|
safe-file-example
|
|
29
28
|
```
|
|
30
29
|
|
|
30
|
+
|
|
31
31
|
**Usage**:
|
|
32
32
|
|
|
33
33
|
```console
|
|
@@ -40,19 +40,19 @@ $ safe-file-example [OPTIONS] COMMAND [ARGS]...
|
|
|
40
40
|
|
|
41
41
|
**Commands**:
|
|
42
42
|
|
|
43
|
-
* `token`: Get and store access token
|
|
43
|
+
* `get-token`: Get and store access token
|
|
44
44
|
* `import-file`: Import a file
|
|
45
45
|
* `export-file`: Export a file
|
|
46
46
|
* `interactive`: Run interactive mode
|
|
47
47
|
|
|
48
|
-
## `safe-file-example token`
|
|
48
|
+
## `safe-file-example get-token`
|
|
49
49
|
|
|
50
50
|
Get and store access token
|
|
51
51
|
|
|
52
52
|
**Usage**:
|
|
53
53
|
|
|
54
54
|
```console
|
|
55
|
-
$ safe-file-example token [OPTIONS]
|
|
55
|
+
$ safe-file-example get-token [OPTIONS]
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
**Options**:
|
|
@@ -106,4 +106,4 @@ $ safe-file-example interactive [OPTIONS]
|
|
|
106
106
|
|
|
107
107
|
**Options**:
|
|
108
108
|
|
|
109
|
-
* `--help`: Show this message and exit.
|
|
109
|
+
* `--help`: Show this message and exit.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "safe-file-example"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name="Dip Paul", email="dip.paul@uib.no" },
|
|
10
10
|
{ name="Eirik Halseth", email="eirik.halseth@uib.no" },
|
|
@@ -45,4 +45,4 @@ safe-file-example="safe.main:app"
|
|
|
45
45
|
packages = ["safe"]
|
|
46
46
|
include = [
|
|
47
47
|
"safe/assets/*.png"
|
|
48
|
-
]
|
|
48
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.0.0"
|
|
@@ -10,8 +10,10 @@ from pathlib import Path
|
|
|
10
10
|
import safe.modules.import_module as import_module
|
|
11
11
|
import safe.modules.export_module as export_module
|
|
12
12
|
import safe.modules.list_export_module as list_export_module
|
|
13
|
-
import safe.modules.select_helper as select_option
|
|
14
13
|
import importlib.resources
|
|
14
|
+
from safe.modules.helper_functions import HelperFunction
|
|
15
|
+
|
|
16
|
+
helperFunction = HelperFunction()
|
|
15
17
|
try:
|
|
16
18
|
import readline
|
|
17
19
|
except ImportError:
|
|
@@ -25,23 +27,26 @@ token_file = os.path.join(config_dir, "token.json")
|
|
|
25
27
|
base_url = "https://api.safe.uib.no/v1"
|
|
26
28
|
logo_path = importlib.resources.files("safe.assets").joinpath("safe_logo.png")
|
|
27
29
|
|
|
30
|
+
|
|
28
31
|
def get_access_token():
|
|
29
32
|
endpoint = "/auth"
|
|
30
33
|
auth_url = f"{base_url}{endpoint}"
|
|
31
34
|
webbrowser.open(auth_url)
|
|
32
|
-
|
|
35
|
+
|
|
33
36
|
Access_token = input("Paste You're JWT token here: ")
|
|
34
|
-
|
|
37
|
+
|
|
35
38
|
os.makedirs(config_dir, exist_ok=True)
|
|
36
39
|
with open(token_file, "w") as f:
|
|
37
40
|
f.write(Access_token)
|
|
38
|
-
if os.name !=
|
|
39
|
-
os.chmod(token_file, 0o600)
|
|
41
|
+
if os.name != "nt":
|
|
42
|
+
os.chmod(token_file, 0o600) # Change the ownership on Linux/MacOS
|
|
40
43
|
print("token succesfully stored")
|
|
41
44
|
|
|
45
|
+
|
|
42
46
|
def exit_cli():
|
|
43
47
|
sys.exit()
|
|
44
48
|
|
|
49
|
+
|
|
45
50
|
def importFile():
|
|
46
51
|
endpoint = "/files/import"
|
|
47
52
|
url = f"{base_url}{endpoint}"
|
|
@@ -64,11 +69,13 @@ def importFile():
|
|
|
64
69
|
root = tk.Tk()
|
|
65
70
|
app_icon = tk.PhotoImage(file=str(logo_path))
|
|
66
71
|
root.withdraw()
|
|
67
|
-
root.attributes(
|
|
72
|
+
root.attributes("-topmost", True)
|
|
68
73
|
root.iconphoto(True, app_icon)
|
|
69
74
|
root.update()
|
|
70
|
-
file_path_str = filedialog.askopenfilename(
|
|
71
|
-
|
|
75
|
+
file_path_str = filedialog.askopenfilename(
|
|
76
|
+
title="Select a file to import", initialdir=os.path.expanduser("~")
|
|
77
|
+
)
|
|
78
|
+
root.attributes("-topmost", False)
|
|
72
79
|
root.iconphoto(False, app_icon)
|
|
73
80
|
root.destroy()
|
|
74
81
|
|
|
@@ -77,7 +84,7 @@ def importFile():
|
|
|
77
84
|
continue
|
|
78
85
|
|
|
79
86
|
file_path = Path(file_path_str)
|
|
80
|
-
|
|
87
|
+
|
|
81
88
|
print("\n--- Import Summary ---")
|
|
82
89
|
print(f"\nServer: {server}")
|
|
83
90
|
print(f"File: {file_path.name}\n")
|
|
@@ -101,6 +108,7 @@ def importFile():
|
|
|
101
108
|
print("Import cancelled.")
|
|
102
109
|
break
|
|
103
110
|
|
|
111
|
+
|
|
104
112
|
def exportFile():
|
|
105
113
|
export_endpoint = "/files/export"
|
|
106
114
|
export_url = f"{base_url}{export_endpoint}"
|
|
@@ -131,14 +139,12 @@ def exportFile():
|
|
|
131
139
|
return
|
|
132
140
|
|
|
133
141
|
while True:
|
|
134
|
-
filename =
|
|
135
|
-
files,
|
|
136
|
-
"Select file by number or type filename: "
|
|
142
|
+
filename = helperFunction.select_from_list(
|
|
143
|
+
files, "Select file by number or type filename: "
|
|
137
144
|
)
|
|
138
145
|
if filename:
|
|
139
146
|
break
|
|
140
147
|
|
|
141
|
-
|
|
142
148
|
# ---- Directory picker ----
|
|
143
149
|
print("Select output directory")
|
|
144
150
|
root = tk.Tk()
|
|
@@ -147,7 +153,9 @@ def exportFile():
|
|
|
147
153
|
root.attributes("-topmost", True)
|
|
148
154
|
root.iconphoto(True, app_icon)
|
|
149
155
|
root.update()
|
|
150
|
-
output_dir = filedialog.askdirectory(
|
|
156
|
+
output_dir = filedialog.askdirectory(
|
|
157
|
+
title="Select output directory", initialdir=os.path.expanduser("~")
|
|
158
|
+
)
|
|
151
159
|
root.attributes("-topmost", False)
|
|
152
160
|
root.iconphoto(False, app_icon)
|
|
153
161
|
root.destroy()
|
|
@@ -185,6 +193,7 @@ def exportFile():
|
|
|
185
193
|
print("Export cancelled.")
|
|
186
194
|
return
|
|
187
195
|
|
|
196
|
+
|
|
188
197
|
def check_token():
|
|
189
198
|
if os.path.exists(token_file):
|
|
190
199
|
with open(token_file, "r") as f:
|
|
@@ -205,22 +214,42 @@ def check_token():
|
|
|
205
214
|
else:
|
|
206
215
|
print("No JWT token found in the token file.")
|
|
207
216
|
|
|
217
|
+
|
|
208
218
|
@app.callback()
|
|
209
|
-
def main(ctx:typer.Context):
|
|
219
|
+
def main(ctx: typer.Context):
|
|
220
|
+
print("Reminder: Please use SAFE-VPN when accessing the SAFE File Examples.")
|
|
221
|
+
print(
|
|
222
|
+
"Note: Do not share the information in the JWT-token with anyone as it is a personal access token"
|
|
223
|
+
)
|
|
224
|
+
version_check()
|
|
210
225
|
if ctx.invoked_subcommand is None:
|
|
211
226
|
typer.echo(ctx.get_help())
|
|
212
227
|
raise typer.Exit()
|
|
213
228
|
|
|
214
|
-
|
|
229
|
+
|
|
230
|
+
def version_check():
|
|
231
|
+
"""prints local version and the newest available version in pip"""
|
|
232
|
+
helperFunction.check_pip_version()
|
|
233
|
+
|
|
234
|
+
|
|
215
235
|
def token():
|
|
216
236
|
"""Get and store access token"""
|
|
217
237
|
get_access_token()
|
|
218
238
|
|
|
219
|
-
|
|
239
|
+
|
|
220
240
|
def import_file(
|
|
221
241
|
server: str = typer.Option(None, help="Server name"),
|
|
222
|
-
file_path: Path = typer.Option(
|
|
223
|
-
|
|
242
|
+
file_path: Path = typer.Option(
|
|
243
|
+
None,
|
|
244
|
+
exists=True,
|
|
245
|
+
file_okay=True,
|
|
246
|
+
dir_okay=False,
|
|
247
|
+
readable=True,
|
|
248
|
+
help="Path to file to import",
|
|
249
|
+
),
|
|
250
|
+
interactive: bool = typer.Option(
|
|
251
|
+
False, "-i", "--interactive", help="Run import interactively"
|
|
252
|
+
),
|
|
224
253
|
):
|
|
225
254
|
"""Import a file"""
|
|
226
255
|
if interactive or not (server and file_path):
|
|
@@ -249,12 +278,21 @@ def import_file(
|
|
|
249
278
|
)
|
|
250
279
|
typer.echo(f"Import result: {success}")
|
|
251
280
|
|
|
252
|
-
|
|
281
|
+
|
|
253
282
|
def export_file(
|
|
254
283
|
server: str = typer.Option(None, help="Server name"),
|
|
255
284
|
filename: str = typer.Option(None, help="Filename to export"),
|
|
256
|
-
output_dir: Path = typer.Option(
|
|
257
|
-
|
|
285
|
+
output_dir: Path = typer.Option(
|
|
286
|
+
None,
|
|
287
|
+
exists=True,
|
|
288
|
+
file_okay=False,
|
|
289
|
+
dir_okay=True,
|
|
290
|
+
writable=True,
|
|
291
|
+
help="Output directory",
|
|
292
|
+
),
|
|
293
|
+
interactive: bool = typer.Option(
|
|
294
|
+
False, "-i", "--interactive", help="Run export interactively"
|
|
295
|
+
),
|
|
258
296
|
):
|
|
259
297
|
"""Export a file"""
|
|
260
298
|
if interactive or not (server and filename and output_dir):
|
|
@@ -274,7 +312,9 @@ def export_file(
|
|
|
274
312
|
|
|
275
313
|
output_path = output_dir / filename
|
|
276
314
|
|
|
277
|
-
typer.echo(
|
|
315
|
+
typer.echo(
|
|
316
|
+
f"Exporting file {filename} from server {server} to {output_path}..."
|
|
317
|
+
)
|
|
278
318
|
|
|
279
319
|
success = export_module.write_file_to_disk(
|
|
280
320
|
download_path=output_path,
|
|
@@ -288,21 +328,30 @@ def export_file(
|
|
|
288
328
|
|
|
289
329
|
def interactive_menu():
|
|
290
330
|
check_token()
|
|
291
|
-
|
|
331
|
+
# Map functions to their user-friendly descriptions (matching --help output)
|
|
332
|
+
choices = [
|
|
333
|
+
(get_access_token, "get-token"),
|
|
334
|
+
(importFile, "import-file"),
|
|
335
|
+
(exportFile, "export-file"),
|
|
336
|
+
(exit_cli, "Exit the Prorgam"),
|
|
337
|
+
]
|
|
292
338
|
while True:
|
|
293
|
-
print("\n--- SAFE
|
|
294
|
-
for index,
|
|
295
|
-
print(f"{index}. {
|
|
296
|
-
|
|
297
|
-
menu_names = [
|
|
298
|
-
selection =
|
|
299
|
-
menu_names,
|
|
300
|
-
"Enter menu number or command name: "
|
|
339
|
+
print("\n--- SAFE File Examples ---")
|
|
340
|
+
for index, (_, description) in enumerate(choices, start=1):
|
|
341
|
+
print(f"{index}. {description}")
|
|
342
|
+
|
|
343
|
+
menu_names = [desc for _, desc in choices]
|
|
344
|
+
selection = helperFunction.select_from_list(
|
|
345
|
+
menu_names, "Enter menu number or command name: "
|
|
301
346
|
)
|
|
302
347
|
if not selection:
|
|
303
348
|
continue
|
|
304
349
|
|
|
305
|
-
selected_fn =
|
|
350
|
+
selected_fn = None
|
|
351
|
+
for fn, desc in choices:
|
|
352
|
+
if desc == selection:
|
|
353
|
+
selected_fn = fn
|
|
354
|
+
break
|
|
306
355
|
|
|
307
356
|
if selected_fn == exit_cli:
|
|
308
357
|
exit_cli()
|
|
@@ -310,15 +359,17 @@ def interactive_menu():
|
|
|
310
359
|
else:
|
|
311
360
|
selected_fn()
|
|
312
361
|
|
|
362
|
+
|
|
363
|
+
app.command(name="get-token")(token)
|
|
364
|
+
app.command(name="import-file")(import_file)
|
|
365
|
+
app.command(name="export-file")(export_file)
|
|
366
|
+
|
|
367
|
+
|
|
313
368
|
@app.command(name="interactive")
|
|
314
369
|
def interactive_command():
|
|
315
370
|
"""Run interactive mode"""
|
|
316
371
|
interactive_menu()
|
|
317
372
|
|
|
318
|
-
app.command()(token)
|
|
319
|
-
app.command()(import_file)
|
|
320
|
-
app.command()(export_file)
|
|
321
373
|
|
|
322
374
|
if __name__ == "__main__":
|
|
323
375
|
app()
|
|
324
|
-
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.0.0"
|
|
@@ -19,9 +19,7 @@ def write_file_to_disk(
|
|
|
19
19
|
temp_path = download_path.with_suffix(download_path.suffix + ".safetmp")
|
|
20
20
|
download_path.parent.mkdir(parents=True, exist_ok=True)
|
|
21
21
|
|
|
22
|
-
headers = {
|
|
23
|
-
"Authorization": f"Bearer {token}"
|
|
24
|
-
}
|
|
22
|
+
headers = {"Authorization": f"Bearer {token}"}
|
|
25
23
|
|
|
26
24
|
params = {
|
|
27
25
|
"filename": filename,
|
|
@@ -55,11 +53,11 @@ def write_file_to_disk(
|
|
|
55
53
|
print("download completed:", download_path)
|
|
56
54
|
return True
|
|
57
55
|
|
|
58
|
-
except Exception
|
|
56
|
+
except Exception:
|
|
59
57
|
if temp_path.exists():
|
|
60
58
|
try:
|
|
61
59
|
temp_path.unlink()
|
|
62
60
|
except Exception:
|
|
63
61
|
pass
|
|
64
62
|
print("Download failed")
|
|
65
|
-
return False
|
|
63
|
+
return False
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
2
|
+
import json
|
|
3
|
+
from urllib.request import urlopen
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class HelperFunction:
|
|
7
|
+
def select_from_list(self, options: list[str], prompt: str) -> str | None:
|
|
8
|
+
"""
|
|
9
|
+
Allow selection by number or exact name.
|
|
10
|
+
Returns the selected string or None if invalid.
|
|
11
|
+
"""
|
|
12
|
+
choice = input(prompt).strip()
|
|
13
|
+
|
|
14
|
+
# Number selection
|
|
15
|
+
if choice.isdigit():
|
|
16
|
+
idx = int(choice) - 1
|
|
17
|
+
if 0 <= idx < len(options):
|
|
18
|
+
return options[idx]
|
|
19
|
+
print("Invalid number.")
|
|
20
|
+
return None
|
|
21
|
+
|
|
22
|
+
# Name selection
|
|
23
|
+
if choice in options:
|
|
24
|
+
return choice
|
|
25
|
+
|
|
26
|
+
print("Invalid selection.")
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
def bytes_to_human_readable(self, size):
|
|
30
|
+
power = 2**10
|
|
31
|
+
n = 0
|
|
32
|
+
power_labels = {0: "", 1: "kilo", 2: "mega", 3: "giga", 4: "tera"}
|
|
33
|
+
while size > power:
|
|
34
|
+
size /= power
|
|
35
|
+
n += 1
|
|
36
|
+
return f"{size} {power_labels[n] + 'bytes'}"
|
|
37
|
+
|
|
38
|
+
def check_pip_version(self):
|
|
39
|
+
package_name = "safe-file-example"
|
|
40
|
+
try:
|
|
41
|
+
installed_version = version(package_name)
|
|
42
|
+
except PackageNotFoundError:
|
|
43
|
+
installed_version = "not installed"
|
|
44
|
+
|
|
45
|
+
with urlopen(
|
|
46
|
+
f"https://pypi.org/pypi/{package_name}/json", timeout=5
|
|
47
|
+
) as response:
|
|
48
|
+
pypi_version = json.load(response)["info"]["version"]
|
|
49
|
+
|
|
50
|
+
if pypi_version > installed_version:
|
|
51
|
+
print(f"{package_name} - {pypi_version} is available")
|
|
52
|
+
print(f"run pip install --upgrade {package_name} to upgrade")
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import requests
|
|
2
2
|
from typing import List
|
|
3
|
+
from .helper_functions import HelperFunction
|
|
4
|
+
|
|
5
|
+
helper_function = HelperFunction()
|
|
6
|
+
|
|
3
7
|
|
|
4
8
|
def list_export_files(
|
|
5
9
|
base_url: str,
|
|
@@ -9,12 +13,9 @@ def list_export_files(
|
|
|
9
13
|
pagination: bool = True,
|
|
10
14
|
timeout: int = 30,
|
|
11
15
|
) -> List[str]:
|
|
12
|
-
|
|
13
16
|
url = f"{base_url}/files/info/list/export"
|
|
14
17
|
|
|
15
|
-
headers = {
|
|
16
|
-
"Authorization": f"Bearer {token}"
|
|
17
|
-
}
|
|
18
|
+
headers = {"Authorization": f"Bearer {token}"}
|
|
18
19
|
|
|
19
20
|
params = {
|
|
20
21
|
"server": server,
|
|
@@ -37,18 +38,22 @@ def list_export_files(
|
|
|
37
38
|
|
|
38
39
|
# Extract filenames
|
|
39
40
|
content = data.get("content", {})
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
data = content.get("files_and_directories", [])
|
|
42
|
+
entries = {
|
|
43
|
+
item["name"]: helper_function.bytes_to_human_readable(item["size"])
|
|
44
|
+
for item in data
|
|
45
|
+
if item.get("type") == "file"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
files = list(entries.keys()) # List containing name of files
|
|
49
|
+
size_of_file = list(entries.values()) # List containing size of files
|
|
44
50
|
|
|
45
|
-
# Pretty print
|
|
46
51
|
print("\n--- Available export files ---")
|
|
47
52
|
if not files:
|
|
48
53
|
print("(No files found)")
|
|
49
54
|
else:
|
|
50
55
|
for i, name in enumerate((files), start=1):
|
|
51
|
-
print(f"{i}. {name}")
|
|
56
|
+
print(f"{i}. {name}" + f" -- {size_of_file[i - 1]}")
|
|
52
57
|
print("------------------------------\n")
|
|
53
58
|
|
|
54
59
|
return files
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.0.0"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.0.0"
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
def select_from_list(options: list[str], prompt: str) -> str | None:
|
|
3
|
-
"""
|
|
4
|
-
Allow selection by number or exact name.
|
|
5
|
-
Returns the selected string or None if invalid.
|
|
6
|
-
"""
|
|
7
|
-
choice = input(prompt).strip()
|
|
8
|
-
|
|
9
|
-
# Number selection
|
|
10
|
-
if choice.isdigit():
|
|
11
|
-
idx = int(choice) - 1
|
|
12
|
-
if 0 <= idx < len(options):
|
|
13
|
-
return options[idx]
|
|
14
|
-
print("Invalid number.")
|
|
15
|
-
return None
|
|
16
|
-
|
|
17
|
-
# Name selection
|
|
18
|
-
if choice in options:
|
|
19
|
-
return choice
|
|
20
|
-
|
|
21
|
-
print("Invalid selection.")
|
|
22
|
-
return None
|
|
File without changes
|
|
File without changes
|