tg-prepare 1.0.0__py3-none-any.whl → 2.1.0b1__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.
Potentially problematic release.
This version of tg-prepare might be problematic. Click here for more details.
- {tg_prepare-1.0.0.dist-info → tg_prepare-2.1.0b1.dist-info}/METADATA +3 -3
- tg_prepare-2.1.0b1.dist-info/RECORD +54 -0
- {tg_prepare-1.0.0.dist-info → tg_prepare-2.1.0b1.dist-info}/WHEEL +1 -1
- tg_prepare-2.1.0b1.dist-info/projects/.secret_key +1 -0
- tgp_backend/__init__.py +3 -4
- tgp_backend/config.py +31 -0
- tgp_backend/directories.py +6 -12
- tgp_backend/nextcloud.py +40 -19
- tgp_backend/project.py +179 -52
- tgp_backend/session_manager.py +47 -0
- tgp_backend/tgclient.py +0 -1
- tgp_backend/util.py +73 -25
- tgp_ui/app.py +43 -337
- tgp_ui/routes/__init__.py +0 -0
- tgp_ui/routes/collection.py +272 -0
- tgp_ui/routes/data.py +228 -0
- tgp_ui/routes/project.py +102 -0
- tgp_ui/routes/publication.py +129 -0
- tgp_ui/routes/tabs.py +34 -0
- tgp_ui/routes/views.py +62 -0
- tgp_ui/static/css/navbar.css +92 -0
- tgp_ui/static/js/collectionManager.js +60 -0
- tgp_ui/static/js/fileManager.js +186 -0
- tgp_ui/static/js/main.js +32 -450
- tgp_ui/static/js/modalManager.js +105 -0
- tgp_ui/static/js/navbarManager.js +151 -0
- tgp_ui/static/js/projectManager.js +60 -0
- tgp_ui/static/js/require.js +5 -0
- tgp_ui/static/js/sidebarManager.js +32 -0
- tgp_ui/static/js/tabManager.js +79 -0
- tgp_ui/templates/layout.html +39 -37
- tgp_ui/templates/macros.html +84 -74
- tgp_ui/templates/project_main.html +36 -0
- tgp_ui/templates/project_navbar.html +81 -0
- tgp_ui/templates/{projects.html → projects_main.html} +14 -29
- tgp_ui/templates/publication.html +156 -0
- tgp_ui/templates/tab_final_upload.html +29 -0
- tg_prepare-1.0.0.dist-info/RECORD +0 -46
- tgp_ui/static/js/jstree.min.js +0 -3
- tgp_ui/templates/collection.html +0 -194
- tgp_ui/templates/empty.html +0 -4
- tgp_ui/templates/file_upload.html +0 -24
- tgp_ui/templates/jsfiler.html +0 -6
- tgp_ui/templates/layout2.html +0 -99
- tgp_ui/templates/main.html +0 -5
- tgp_ui/templates/nxc_file_tree.html +0 -33
- tgp_ui/templates/project.html +0 -25
- tgp_ui/templates/project_nxc.html +0 -37
- tgp_ui/templates/publish.html +0 -120
- tgp_ui/templates/storage.html +0 -49
- tgp_ui/templates/tei_browser.html +0 -14
- tgp_ui/templates/tei_explorer.html +0 -48
- tgp_ui/templates/xpath_parser_modal_content.html +0 -37
- {tg_prepare-1.0.0.dist-info → tg_prepare-2.1.0b1.dist-info}/entry_points.txt +0 -0
- {tg_prepare-1.0.0.dist-info → tg_prepare-2.1.0b1.dist-info}/licenses/LICENSE +0 -0
- {tg_prepare-1.0.0.dist-info → tg_prepare-2.1.0b1.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tg_prepare
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 2.1.0b1
|
|
4
4
|
Summary: Simple UI to handle TextGrid imports visually.
|
|
5
5
|
Author: Ralf Klammer, Moritz Wilhelm
|
|
6
6
|
Author-email: ralf.klammer@tu-dresden.de, moritz.wilhelm@tu-dresden.de
|
|
@@ -8,8 +8,8 @@ License-File: LICENSE
|
|
|
8
8
|
Requires-Dist: click
|
|
9
9
|
Requires-Dist: flask
|
|
10
10
|
Requires-Dist: flask_json
|
|
11
|
-
Requires-Dist: tg_model
|
|
12
|
-
Requires-Dist: tgclients
|
|
11
|
+
Requires-Dist: tg_model==3.8.0.b2
|
|
12
|
+
Requires-Dist: tgclients
|
|
13
13
|
Requires-Dist: tgadmin
|
|
14
14
|
Requires-Dist: nextcloud-api-wrapper
|
|
15
15
|
Dynamic: author
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
tg_prepare-2.1.0b1.dist-info/licenses/LICENSE,sha256=deYu6g1OGKm0VUwhM4Octoh8qlzjsiHHxoI-KkCBVBE,11351
|
|
2
|
+
tg_prepare-2.1.0b1.dist-info/projects/.secret_key,sha256=PF3sI74fctNP5J8NoHmvl6nRsvyTeGMU82UTY_E3re4,66
|
|
3
|
+
tgp_backend/__init__.py,sha256=Hsy1huPlOgpNffJVJpxGZzUKdHU8X837K-cOW_DkEy0,373
|
|
4
|
+
tgp_backend/auth.py,sha256=hUPd0rc01BTT003xpkdmhNc6o0JztbAdimyDb0B9oTQ,2293
|
|
5
|
+
tgp_backend/cli.py,sha256=iw_gknn-_m_UFZZUKfkV9aJixG3sVcHIONybNtOG40I,551
|
|
6
|
+
tgp_backend/config.py,sha256=9rFr7bxZN4r1OOKaxs8eBYUnZ0_ccVHU6oqoEsNTKBE,922
|
|
7
|
+
tgp_backend/directories.py,sha256=wNmwc2Uhd9ivZYIM9UkfCv2HkQY8pmDQk2y39wOgBQo,2366
|
|
8
|
+
tgp_backend/interfaces.py,sha256=SO7mOdhkd1f7irUBrwFhp5aKx3UKkkUzU9QBYDl0Tvc,91
|
|
9
|
+
tgp_backend/nextcloud.py,sha256=JNhyJfuR26o33r5QV37ClTALQm_fMQn451SMV5lixNc,5133
|
|
10
|
+
tgp_backend/project.py,sha256=_qebCEkC_wRfSknOIL-c4BAPG830EUZ5m4HW_gkHRn4,12707
|
|
11
|
+
tgp_backend/session_manager.py,sha256=pQGhnXtGPqwdw6xQhZItjGgurx9JBUsIW8mzfSKk_WM,1395
|
|
12
|
+
tgp_backend/tgclient.py,sha256=QG2iFPrv2qLL4FRXoEfXzEMJriroiIaNL9rkreGtzuQ,2961
|
|
13
|
+
tgp_backend/util.py,sha256=4lTaC3liik2ixRcx5wDQS4-FW3QpPhRdXQm32_64J_E,3648
|
|
14
|
+
tgp_ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
tgp_ui/app.py,sha256=6elwUZ_hSM08_FstTerrLX2rB4QurQUSqu359rkhxrI,3704
|
|
16
|
+
tgp_ui/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
tgp_ui/routes/collection.py,sha256=NzfLnF-ZDlZI23ulDhvLXJXELlTlcJw0Z_LONHcU2bA,7861
|
|
18
|
+
tgp_ui/routes/data.py,sha256=xnw-0v72SK8Z2zjwCYNAHp8omkfsZKRtvUKr3YZSSFg,6891
|
|
19
|
+
tgp_ui/routes/project.py,sha256=AyCYI76V4vkEfZQPjSgXmPOIWClpryEEBTQoSjxSXkM,2794
|
|
20
|
+
tgp_ui/routes/publication.py,sha256=K_wDy3t_iwL9TMLLFXR7P2PdDWyHNQUMY9FArJBsmN4,3791
|
|
21
|
+
tgp_ui/routes/tabs.py,sha256=JaZpoY03S_p8uz_sVRCR-fIOfl-ml0M6CJ7_KaYN9aI,908
|
|
22
|
+
tgp_ui/routes/views.py,sha256=ZgxcfEZoyE_4ABrnRtHc8FFR11Rufs8HVaUr4cOBshA,1814
|
|
23
|
+
tgp_ui/static/css/bootstrap-icons.min.css,sha256=XxUvYRPQvG4RTCkukUmXwvf1N0YF9KQ_ocuWV6O1dk8,85797
|
|
24
|
+
tgp_ui/static/css/bootstrap.min.css,sha256=djO3wMl9GeaC_u6K-ic4Uj_LKhRUSlUFcsruzS7v5ms,155845
|
|
25
|
+
tgp_ui/static/css/main.css,sha256=EK8euwgxX8I3qsBiDmIvj8UBIxkSKQZz1mo_OX4Q1FQ,2641
|
|
26
|
+
tgp_ui/static/css/navbar.css,sha256=D68mfVJ88zruV1sRH1D6zjwQ2MoHRy2HQ7BAn_8OSkc,1609
|
|
27
|
+
tgp_ui/static/css/simpleXML.css,sha256=dfY7UZCiC79BkNOT1hM0GQ6v_yLSxhKCnmV0RZIT9S8,961
|
|
28
|
+
tgp_ui/static/js/bootstrap.bundle.min.js,sha256=fh8VA992XMpeCZiRuU4xii75UIG6KvHrbUF8yIS_2_4,78743
|
|
29
|
+
tgp_ui/static/js/collectionManager.js,sha256=gbeXe7CtEdCdka3WLfBx7cVRcT8cveUP_t3TDwRdgvo,2291
|
|
30
|
+
tgp_ui/static/js/fileManager.js,sha256=_plx7zrC039-3y0ZqLd77ymDhTtAiJC3XOLSeAooYAc,7095
|
|
31
|
+
tgp_ui/static/js/jquery.min.js,sha256=_xUj-3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej_m4,89501
|
|
32
|
+
tgp_ui/static/js/main.js,sha256=VCaLVlGFml8ObO0mbyV_kT1aDVH-OB4jqVlzsfT42xM,1107
|
|
33
|
+
tgp_ui/static/js/modalManager.js,sha256=8q4Dtx88aO4zbG5EgZ6Nw1NJa36-RKoRcVzy4DVHEVg,4030
|
|
34
|
+
tgp_ui/static/js/navbarManager.js,sha256=E1_SF262uU4I405vxVCrEXhAE7aEZt9ASixG7huL1As,6713
|
|
35
|
+
tgp_ui/static/js/projectManager.js,sha256=LoT8lZnyvrgquSD7aSNkYaAdggH5kUZYRXVFD-d3dzk,2296
|
|
36
|
+
tgp_ui/static/js/require.js,sha256=DCwpJYECNmnwjgb2xsUmuvSrkNPXRYgYADz7nlUc_-Y,20781
|
|
37
|
+
tgp_ui/static/js/sidebarManager.js,sha256=sfYPwLvKUQA8vMhyVKsA7CJe5cUJliCao8ccwfLKAV0,928
|
|
38
|
+
tgp_ui/static/js/simpleXML.js,sha256=jEkZe3TuiHuEt6V7If_1EmqYIgTywssJ3OwOQzOFYNg,8065
|
|
39
|
+
tgp_ui/static/js/tabManager.js,sha256=spS-Hr-LylqpW0nQz7ontsPfX64b3whilr6038VMq18,3304
|
|
40
|
+
tgp_ui/templates/file_tree.html,sha256=IKWp5MLUvnFF65cIUIxU4Jf5wi9Fg8koSXD96Mk328E,1640
|
|
41
|
+
tgp_ui/templates/layout.html,sha256=ClI6xHMBy6auir72i3Sm6SCJI97XUXhpPrzJSgf_7wM,2727
|
|
42
|
+
tgp_ui/templates/macros.html,sha256=3qetLd7wC__iAoWZ6g4--82Zd2t36XOZcwiLbi7DEAA,4463
|
|
43
|
+
tgp_ui/templates/nxc_login.html,sha256=tDxqHYPZqsY2ZNnkn4a5bMOYiD8ybcNRpzSq7MvMWCo,1269
|
|
44
|
+
tgp_ui/templates/nxc_tab.html,sha256=9HWCa7UZKxL0fZasT_g6uozI64L-497Y2LppPPTHF2M,517
|
|
45
|
+
tgp_ui/templates/project_main.html,sha256=Vdvr9QDfpnc9EU38uTm0H4pT0pzLdi7DckL3d9DC_u4,1878
|
|
46
|
+
tgp_ui/templates/project_navbar.html,sha256=WyS8WNI7tgNDdYOkuFaXeu9A58ER3OK7puVNO-Sfz-0,4412
|
|
47
|
+
tgp_ui/templates/projects_main.html,sha256=2c6PNdomNV1w6j6e-FH27bvdwVNfrzTf5DY1pNzHuMM,2530
|
|
48
|
+
tgp_ui/templates/publication.html,sha256=TXCHCE2198cHFmN4_l4JNmq0nQoy8GwnESePjRfjliU,9357
|
|
49
|
+
tgp_ui/templates/tab_final_upload.html,sha256=Nn1pwxI_Y_XBB81N5l2G8P_pSsVOyS2AQLsp8IE9a4s,1663
|
|
50
|
+
tg_prepare-2.1.0b1.dist-info/METADATA,sha256=sZRgS2wsd54fQOGcyShPWkEBMqpj0C08w8xXTzhfLrs,529
|
|
51
|
+
tg_prepare-2.1.0b1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
52
|
+
tg_prepare-2.1.0b1.dist-info/entry_points.txt,sha256=mbsfiEA8_fQdrFUE_oabDi-Syw8jjli37Ms7tawDDhs,78
|
|
53
|
+
tg_prepare-2.1.0b1.dist-info/top_level.txt,sha256=ueOyX_KdozreQJD_HRs6kAsvGNO4vfM9B_QqqhdyRPI,19
|
|
54
|
+
tg_prepare-2.1.0b1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"94c9d50a8949b627be9608827a66d0025919e013d89236b092de90dd0b79fd12"
|
tgp_backend/__init__.py
CHANGED
|
@@ -4,15 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
6
|
|
|
7
|
-
from .
|
|
7
|
+
from .config import LOG_LEVEL
|
|
8
8
|
|
|
9
9
|
log = logging.getLogger(__name__)
|
|
10
10
|
|
|
11
|
-
log_level = config("log", "level", default="INFO")
|
|
12
11
|
log_config = dict(
|
|
13
|
-
level=
|
|
12
|
+
level=LOG_LEVEL,
|
|
14
13
|
format="%(asctime)s %(name)-10s %(levelname)-4s %(message)s",
|
|
15
14
|
)
|
|
16
15
|
|
|
17
16
|
logging.basicConfig(**log_config)
|
|
18
|
-
logging.getLogger("").setLevel(
|
|
17
|
+
logging.getLogger("").setLevel(LOG_LEVEL)
|
tgp_backend/config.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (C) 2023-2024 TU-Dresden (ZIH)
|
|
3
|
+
# ralf.klammer@tu-dresden.de
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
from configparser import ConfigParser
|
|
7
|
+
|
|
8
|
+
log = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_config_value(section, parameter, default=None):
|
|
12
|
+
_config = ConfigParser()
|
|
13
|
+
_config.read("config.ini")
|
|
14
|
+
|
|
15
|
+
if section not in _config:
|
|
16
|
+
log.warn("Section: %s not in *.ini -> using default" % section)
|
|
17
|
+
return default
|
|
18
|
+
config_val = _config[section].get(parameter)
|
|
19
|
+
if not config_val:
|
|
20
|
+
log.info(
|
|
21
|
+
"Parameter: %s not in section: (%s) of *.ini -> using default: %s"
|
|
22
|
+
% (parameter, section, default)
|
|
23
|
+
)
|
|
24
|
+
return default
|
|
25
|
+
else:
|
|
26
|
+
return config_val
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
LOG_LEVEL = get_config_value("log", "level", default="INFO")
|
|
30
|
+
MAIN_PATH = get_config_value("main", "path", default="./projects")
|
|
31
|
+
MAX_FILENAME_LENGTH = get_config_value("main", "maxFileNameLength", default=15)
|
tgp_backend/directories.py
CHANGED
|
@@ -6,13 +6,11 @@ import logging
|
|
|
6
6
|
import os
|
|
7
7
|
import re
|
|
8
8
|
|
|
9
|
-
from tgp_backend.
|
|
10
|
-
|
|
9
|
+
from tgp_backend.config import MAX_FILENAME_LENGTH
|
|
10
|
+
from tgp_backend.util import get_file_extension
|
|
11
11
|
|
|
12
12
|
log = logging.getLogger(__name__)
|
|
13
13
|
|
|
14
|
-
maxFileNameLength = config("main", "maxFileNameLength", default=15)
|
|
15
|
-
|
|
16
14
|
|
|
17
15
|
def generateList(path=None):
|
|
18
16
|
# path = path if path else config("main", "path", default="/tmp")
|
|
@@ -28,7 +26,6 @@ def generateList(path=None):
|
|
|
28
26
|
for name in os.listdir(path):
|
|
29
27
|
fullpath = "%s/%s" % (path, name)
|
|
30
28
|
if not name.startswith("."):
|
|
31
|
-
print(name)
|
|
32
29
|
if os.path.isdir(fullpath):
|
|
33
30
|
dList.append({"fullpath": fullpath, "name": name})
|
|
34
31
|
else:
|
|
@@ -39,12 +36,12 @@ def generateList(path=None):
|
|
|
39
36
|
curDir = path
|
|
40
37
|
|
|
41
38
|
for d in dList:
|
|
42
|
-
if len(d["name"]) >
|
|
39
|
+
if len(d["name"]) > MAX_FILENAME_LENGTH:
|
|
43
40
|
dots = "..."
|
|
44
41
|
else:
|
|
45
42
|
dots = ""
|
|
46
43
|
temp_dir = {}
|
|
47
|
-
temp_dir["f"] = d["name"][0:
|
|
44
|
+
temp_dir["f"] = d["name"][0:MAX_FILENAME_LENGTH] + dots
|
|
48
45
|
temp_dir["name"] = d["name"]
|
|
49
46
|
temp_dir["f_url"] = re.sub("#", "|HASHTAG|", d["fullpath"])
|
|
50
47
|
temp_dir["currentDir"] = curDir
|
|
@@ -62,12 +59,12 @@ def generateList(path=None):
|
|
|
62
59
|
pass
|
|
63
60
|
if not image:
|
|
64
61
|
image = "files_icon/unknown-icon.png"
|
|
65
|
-
if len(f["name"]) >
|
|
62
|
+
if len(f["name"]) > MAX_FILENAME_LENGTH:
|
|
66
63
|
dots = "..."
|
|
67
64
|
else:
|
|
68
65
|
dots = ""
|
|
69
66
|
temp_file = {}
|
|
70
|
-
temp_file["f"] = f["name"][0:
|
|
67
|
+
temp_file["f"] = f["name"][0:MAX_FILENAME_LENGTH] + dots
|
|
71
68
|
temp_file["name"] = f["name"]
|
|
72
69
|
temp_file["name_stripped"] = f["name"].replace(f".{tp}", "")
|
|
73
70
|
temp_file["f_url"] = re.sub("#", "|HASHTAG|", f["fullpath"])
|
|
@@ -79,7 +76,4 @@ def generateList(path=None):
|
|
|
79
76
|
|
|
80
77
|
file_list_dict.append(temp_file)
|
|
81
78
|
|
|
82
|
-
# log.debug("generateList - dir_list_dict: %s" % dir_list_dict)
|
|
83
|
-
# print("generateList - file_list_dict: %s" % file_list_dict)
|
|
84
|
-
# print(tp_dict)
|
|
85
79
|
return dir_list_dict, file_list_dict
|
tgp_backend/nextcloud.py
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
# moritz.wilhelm@tu-dresden.de
|
|
5
5
|
|
|
6
6
|
import logging
|
|
7
|
-
|
|
8
7
|
import nextcloud
|
|
9
8
|
import os
|
|
10
9
|
|
|
@@ -14,21 +13,21 @@ log = logging.getLogger(__name__)
|
|
|
14
13
|
class Nextcloud:
|
|
15
14
|
def __init__(
|
|
16
15
|
self,
|
|
17
|
-
|
|
16
|
+
url=None,
|
|
18
17
|
username=None,
|
|
19
18
|
password=None,
|
|
20
|
-
|
|
19
|
+
folder=None,
|
|
21
20
|
**kwargs,
|
|
22
21
|
):
|
|
23
22
|
self._nxc = None
|
|
24
23
|
self._root = None
|
|
25
|
-
self.
|
|
24
|
+
self.url = url
|
|
26
25
|
self.username = username
|
|
27
26
|
self.password = password
|
|
28
|
-
self.root_dir =
|
|
27
|
+
self.root_dir = folder
|
|
29
28
|
|
|
30
29
|
def test_connection(self):
|
|
31
|
-
if all([self.
|
|
30
|
+
if all([self.url, self.username, self.password]):
|
|
32
31
|
return str(self.nxc.list_folders().status_code).startswith("2")
|
|
33
32
|
return False
|
|
34
33
|
|
|
@@ -36,7 +35,7 @@ class Nextcloud:
|
|
|
36
35
|
def nxc(self):
|
|
37
36
|
if self._nxc is None:
|
|
38
37
|
self._nxc = nextcloud.NextCloud(
|
|
39
|
-
endpoint=self.
|
|
38
|
+
endpoint=self.url,
|
|
40
39
|
user=self.username,
|
|
41
40
|
password=self.password,
|
|
42
41
|
)
|
|
@@ -51,29 +50,51 @@ class Nextcloud:
|
|
|
51
50
|
raise "Could not find the Root Directory"
|
|
52
51
|
return self._root
|
|
53
52
|
|
|
54
|
-
def nxc_list_files_and_folders(self):
|
|
53
|
+
def nxc_list_files_and_folders(self, get_selectable=False):
|
|
54
|
+
|
|
55
|
+
selectable_folders = []
|
|
55
56
|
|
|
56
|
-
def
|
|
57
|
+
def recursive_list(item, depth=0):
|
|
57
58
|
items = []
|
|
58
59
|
for d in item.list():
|
|
59
60
|
if d.isdir():
|
|
60
|
-
children =
|
|
61
|
+
children = recursive_list(d, depth=depth + 1)
|
|
62
|
+
contains_xml = any(
|
|
63
|
+
f.get("name", "").endswith(".xml") for f in children
|
|
64
|
+
)
|
|
65
|
+
item = {
|
|
66
|
+
"type": "folder",
|
|
67
|
+
"name": d.basename(),
|
|
68
|
+
"depth": depth,
|
|
69
|
+
"path": d.get_relative_path(),
|
|
70
|
+
"contains_xml": contains_xml,
|
|
71
|
+
"children": {
|
|
72
|
+
"count": len(children),
|
|
73
|
+
"list": children,
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
items.append(item)
|
|
77
|
+
if contains_xml:
|
|
78
|
+
selectable_folders.append(item)
|
|
79
|
+
else:
|
|
61
80
|
items.append(
|
|
62
81
|
{
|
|
63
|
-
"type": "
|
|
82
|
+
"type": "file",
|
|
64
83
|
"name": d.basename(),
|
|
65
|
-
"
|
|
66
|
-
"children": {
|
|
67
|
-
"count": len(children),
|
|
68
|
-
"list": children,
|
|
69
|
-
},
|
|
84
|
+
"depth": depth,
|
|
70
85
|
}
|
|
71
86
|
)
|
|
72
|
-
else:
|
|
73
|
-
items.append({"type": "file", "name": d.basename()})
|
|
74
87
|
return items
|
|
75
88
|
|
|
76
|
-
|
|
89
|
+
result = recursive_list(self.root)
|
|
90
|
+
if get_selectable:
|
|
91
|
+
return selectable_folders
|
|
92
|
+
else:
|
|
93
|
+
return result
|
|
94
|
+
|
|
95
|
+
def get_selectable_folders(self):
|
|
96
|
+
|
|
97
|
+
return self.nxc_list_files_and_folders(get_selectable=True)
|
|
77
98
|
|
|
78
99
|
def download_nxc_files(self, file_paths, projectname):
|
|
79
100
|
for path in file_paths:
|
tgp_backend/project.py
CHANGED
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
# ralf.klammer@tu-dresden.de
|
|
4
4
|
# moritz.wilhelm@tu-dresden.de
|
|
5
5
|
import logging
|
|
6
|
+
|
|
7
|
+
import json
|
|
6
8
|
import os
|
|
7
9
|
import subprocess
|
|
8
10
|
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
|
|
9
13
|
from tg_model.collection import CollectionModeler # type: ignore
|
|
14
|
+
from tg_model.project import Project as ProjectHandler # type: ignore
|
|
10
15
|
from tg_model.tei import TEIParser # type: ignore
|
|
11
16
|
|
|
12
17
|
from tg_model.yaml import ( # type: ignore
|
|
@@ -16,91 +21,172 @@ from tg_model.yaml import ( # type: ignore
|
|
|
16
21
|
ProjectConfig,
|
|
17
22
|
)
|
|
18
23
|
|
|
24
|
+
from tgp_backend.config import MAIN_PATH
|
|
19
25
|
from tgp_backend.tgclient import TGclient
|
|
20
|
-
from tgp_backend.util import
|
|
26
|
+
from tgp_backend.util import (
|
|
27
|
+
list_files_and_folders,
|
|
28
|
+
get_selectable_folders,
|
|
29
|
+
)
|
|
21
30
|
|
|
22
31
|
log = logging.getLogger(__name__)
|
|
23
32
|
|
|
24
|
-
MAIN_PATH = config("main", "path", default="./projects")
|
|
25
|
-
|
|
26
33
|
|
|
27
34
|
class Project(object):
|
|
28
|
-
def __init__(self,
|
|
29
|
-
self.
|
|
30
|
-
self.
|
|
31
|
-
self.datapath = f"{self.fullpath}/data"
|
|
32
|
-
self.metadatapath = f"{self.fullpath}/metadata"
|
|
33
|
-
self._main_config = None
|
|
35
|
+
def __init__(self, path):
|
|
36
|
+
self.path = path
|
|
37
|
+
self._project_config = None
|
|
34
38
|
self._collections = None
|
|
35
39
|
self._nextcloud = None
|
|
40
|
+
self.tgm_project = TGMProject(self.metadatapath)
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def name(self):
|
|
44
|
+
log.warning("Project.name is deprecated, use Project.path instead")
|
|
45
|
+
return self.path
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def fullpath(self):
|
|
49
|
+
return f"{MAIN_PATH}/{self.path}"
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def title(self):
|
|
53
|
+
return self.project_config.title
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def description(self):
|
|
57
|
+
return self.project_config.description
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def collectors(self):
|
|
61
|
+
return self.project_config.collectors
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def path_data(self):
|
|
65
|
+
return os.path.join(self.fullpath, "data")
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def path_metadata(self):
|
|
69
|
+
return os.path.join(self.fullpath, "metadata")
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def path_otherfiles(self):
|
|
73
|
+
return os.path.join(self.path_metadata, "other_files")
|
|
36
74
|
|
|
37
75
|
def create(self):
|
|
38
76
|
if not os.path.exists(self.fullpath):
|
|
39
77
|
os.makedirs(self.fullpath)
|
|
40
|
-
os.makedirs(self.
|
|
41
|
-
os.makedirs(self.
|
|
78
|
+
os.makedirs(self.path_data)
|
|
79
|
+
os.makedirs(self.path_metadata)
|
|
42
80
|
else:
|
|
43
81
|
log.warning("Project already exists!")
|
|
44
82
|
|
|
83
|
+
@property
|
|
84
|
+
def avatar(self):
|
|
85
|
+
if self.project_config.avatar:
|
|
86
|
+
return os.path.join(
|
|
87
|
+
self.path_otherfiles, self.project_config.avatar
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def xslt(self):
|
|
92
|
+
if self.project_config.xslt:
|
|
93
|
+
return os.path.join(self.path_otherfiles, self.project_config.xslt)
|
|
94
|
+
|
|
95
|
+
def save_avatar(self, file):
|
|
96
|
+
if file.filename and file.filename != "":
|
|
97
|
+
file.save(os.path.join(self.path_otherfiles, file.filename))
|
|
98
|
+
self.project_config.avatar = file.filename
|
|
99
|
+
|
|
100
|
+
def save_xslt(self, file):
|
|
101
|
+
if file.filename and file.filename != "":
|
|
102
|
+
file.save(os.path.join(self.path_otherfiles, file.filename))
|
|
103
|
+
self.project_config.xslt = file.filename
|
|
104
|
+
|
|
105
|
+
def clear_xslt(self):
|
|
106
|
+
self.project_config.xslt = None
|
|
107
|
+
self.project_config.save()
|
|
108
|
+
|
|
109
|
+
def update(self, title, description, collectors, avatar, xslt):
|
|
110
|
+
self.project_config.title = title
|
|
111
|
+
self.project_config.description = description
|
|
112
|
+
self.project_config.collectors = collectors
|
|
113
|
+
|
|
114
|
+
if avatar:
|
|
115
|
+
self.save_avatar(avatar)
|
|
116
|
+
self.save_xslt(xslt)
|
|
117
|
+
|
|
118
|
+
self.project_config.save()
|
|
119
|
+
|
|
45
120
|
def clone_git_project(self, url):
|
|
46
121
|
repo_name = url.split("/")[-1].replace(".git", "")
|
|
47
|
-
repo_path = os.path.join(self.
|
|
122
|
+
repo_path = os.path.join(self.path_data, repo_name)
|
|
48
123
|
|
|
49
124
|
if not os.path.exists(repo_path):
|
|
50
125
|
subprocess.run(["git", "clone", url, repo_path])
|
|
51
126
|
else:
|
|
52
|
-
log.
|
|
127
|
+
log.info("Repository already exists!")
|
|
53
128
|
|
|
54
129
|
def file_upload(self, files):
|
|
55
130
|
for file in files:
|
|
56
131
|
if file:
|
|
57
132
|
filename = file.filename
|
|
58
|
-
filepath = os.path.join(self.
|
|
133
|
+
filepath = os.path.join(self.path_data, filename)
|
|
59
134
|
directory = os.path.dirname(filepath)
|
|
60
135
|
if not os.path.exists(directory):
|
|
61
136
|
os.makedirs(directory)
|
|
62
137
|
file.save(filepath)
|
|
63
138
|
|
|
64
139
|
def list_files_and_folders(self):
|
|
65
|
-
|
|
66
|
-
|
|
140
|
+
return list_files_and_folders(self.path_data)
|
|
141
|
+
|
|
142
|
+
def get_selectable_folders(self):
|
|
143
|
+
selectable_folders = get_selectable_folders(self.path_data)
|
|
144
|
+
for folder in selectable_folders:
|
|
145
|
+
folder["relative_path"] = folder["path"].replace(
|
|
146
|
+
self.path_data, ""
|
|
147
|
+
)
|
|
148
|
+
return selectable_folders
|
|
67
149
|
|
|
68
150
|
@property
|
|
69
151
|
def main_config(self):
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return self._main_config
|
|
73
|
-
|
|
74
|
-
@main_config.setter
|
|
75
|
-
def main_config(self, tei_directories):
|
|
76
|
-
self._main_config = ProjectConfigTemplate(self.metadatapath).render(
|
|
77
|
-
tei_directories=tei_directories
|
|
152
|
+
log.warning(
|
|
153
|
+
"Project.main_config is deprecated, use Project.project_config instead"
|
|
78
154
|
)
|
|
155
|
+
return self.project_config
|
|
79
156
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
157
|
+
@property
|
|
158
|
+
def project_config(self):
|
|
159
|
+
if not self._project_config:
|
|
160
|
+
self._project_config = ProjectConfig(
|
|
161
|
+
projectpath=self.path_metadata
|
|
162
|
+
)
|
|
163
|
+
if not self._project_config.exists():
|
|
164
|
+
self._project_config = ProjectConfigTemplate(
|
|
165
|
+
self.path_metadata
|
|
166
|
+
).render()
|
|
167
|
+
self._project_config.title = self.path
|
|
168
|
+
self._project_config.save()
|
|
169
|
+
|
|
170
|
+
return self._project_config
|
|
171
|
+
|
|
172
|
+
@property
|
|
173
|
+
def project_handler(self):
|
|
174
|
+
return ProjectHandler(self.path_metadata)
|
|
89
175
|
|
|
90
176
|
def get_subproject_inpath(self, name):
|
|
91
|
-
for subproject in self.
|
|
177
|
+
for subproject in self.project_config.content["subprojects"]:
|
|
92
178
|
if subproject["name"] == name:
|
|
93
179
|
return subproject["inpath"]
|
|
94
180
|
return None
|
|
95
181
|
|
|
96
182
|
def _set_collections(self):
|
|
97
183
|
self._collections = {}
|
|
98
|
-
if self.
|
|
99
|
-
for subproject in self.
|
|
184
|
+
if self.project_config.exists():
|
|
185
|
+
for subproject in self.project_config.content["subprojects"]:
|
|
100
186
|
collection_config = CollectionConfig(subproject["basepath"])
|
|
101
187
|
if not collection_config.exists():
|
|
102
188
|
collection_config = CollectionConfigTemplate(
|
|
103
|
-
projectpath=self.
|
|
189
|
+
projectpath=self.path_metadata,
|
|
104
190
|
subproject=subproject,
|
|
105
191
|
files=[
|
|
106
192
|
TEIParser(fullpath=file)
|
|
@@ -113,7 +199,7 @@ class Project(object):
|
|
|
113
199
|
"paths": subproject,
|
|
114
200
|
"parser": CollectionParser(collection_config),
|
|
115
201
|
"modeler": CollectionModeler(
|
|
116
|
-
subproject, self.
|
|
202
|
+
subproject, self.path_metadata
|
|
117
203
|
),
|
|
118
204
|
}
|
|
119
205
|
|
|
@@ -132,6 +218,49 @@ class Project(object):
|
|
|
132
218
|
def get_tgp(self, instance):
|
|
133
219
|
return TGProject(self, instance)
|
|
134
220
|
|
|
221
|
+
def _save_validation_results(self, results, filepath):
|
|
222
|
+
"""
|
|
223
|
+
Saves the validation results to a JSON file.
|
|
224
|
+
Adds the date of the last execution.
|
|
225
|
+
"""
|
|
226
|
+
results["last_run"] = datetime.now().strftime("%Y-%m-%d %H:%M")
|
|
227
|
+
|
|
228
|
+
with open(filepath, "w", encoding="utf-8") as f:
|
|
229
|
+
json.dump(results, f, indent=4, ensure_ascii=False)
|
|
230
|
+
|
|
231
|
+
log.info(f"Validation results saved to {filepath}")
|
|
232
|
+
|
|
233
|
+
def _run_validation(self):
|
|
234
|
+
validation_results = self.project_handler.validate()
|
|
235
|
+
self._save_validation_results(validation_results, self.validation_file)
|
|
236
|
+
return validation_results
|
|
237
|
+
|
|
238
|
+
@property
|
|
239
|
+
def validation_file(self):
|
|
240
|
+
"""
|
|
241
|
+
Returns the path to the validation results file.
|
|
242
|
+
"""
|
|
243
|
+
return os.path.join(self.path_metadata, "validation_results.json")
|
|
244
|
+
|
|
245
|
+
def get_validation_results(self, refresh=False):
|
|
246
|
+
"""
|
|
247
|
+
Returns stored validation results.
|
|
248
|
+
If no results are saved or `refresh=True`, a new validation will
|
|
249
|
+
be performed.
|
|
250
|
+
"""
|
|
251
|
+
# If `refresh` is set or file doesn't exist, perform new validation
|
|
252
|
+
if refresh or not os.path.exists(self.validation_file):
|
|
253
|
+
validation_results = self._run_validation()
|
|
254
|
+
else:
|
|
255
|
+
# Load results from file
|
|
256
|
+
with open(self.validation_file, "r", encoding="utf-8") as f:
|
|
257
|
+
validation_results = json.load(f)
|
|
258
|
+
|
|
259
|
+
if "ready_for_publication" not in validation_results:
|
|
260
|
+
validation_results = self._run_validation()
|
|
261
|
+
|
|
262
|
+
return validation_results
|
|
263
|
+
|
|
135
264
|
|
|
136
265
|
class TGProject(object):
|
|
137
266
|
|
|
@@ -141,7 +270,7 @@ class TGProject(object):
|
|
|
141
270
|
else:
|
|
142
271
|
self.project = Project(project)
|
|
143
272
|
self.instance = instance
|
|
144
|
-
self.
|
|
273
|
+
self.project_config = self.project.project_config
|
|
145
274
|
self.collections = self.project.collections
|
|
146
275
|
self._tg_client = None
|
|
147
276
|
self._tg_session_id = None
|
|
@@ -150,7 +279,7 @@ class TGProject(object):
|
|
|
150
279
|
@property
|
|
151
280
|
def tg_session_id(self):
|
|
152
281
|
if not self._tg_session_id:
|
|
153
|
-
self._tg_session_id = self.
|
|
282
|
+
self._tg_session_id = self.project_config.get_tg_session_id(
|
|
154
283
|
self.instance
|
|
155
284
|
)
|
|
156
285
|
return self._tg_session_id
|
|
@@ -158,12 +287,12 @@ class TGProject(object):
|
|
|
158
287
|
@tg_session_id.setter
|
|
159
288
|
def tg_session_id(self, session_id):
|
|
160
289
|
self._tg_session_id = session_id
|
|
161
|
-
self.
|
|
290
|
+
self.project_config.set_tg_session_id(session_id, self.instance)
|
|
162
291
|
|
|
163
292
|
@property
|
|
164
293
|
def tg_project_id(self):
|
|
165
294
|
if not self._tg_project_id:
|
|
166
|
-
self._tg_project_id = self.
|
|
295
|
+
self._tg_project_id = self.project_config.get_tg_project_id(
|
|
167
296
|
self.instance
|
|
168
297
|
)
|
|
169
298
|
return self._tg_project_id
|
|
@@ -171,7 +300,7 @@ class TGProject(object):
|
|
|
171
300
|
@tg_project_id.setter
|
|
172
301
|
def tg_project_id(self, project_id):
|
|
173
302
|
self._tg_project_id = project_id
|
|
174
|
-
self.
|
|
303
|
+
self.project_config.set_tg_project_id(project_id, self.instance)
|
|
175
304
|
|
|
176
305
|
@property
|
|
177
306
|
def tg_client(self):
|
|
@@ -206,22 +335,21 @@ class TGProject(object):
|
|
|
206
335
|
if self.tg_session_id:
|
|
207
336
|
return self.tg_client.get_project_content(project_id).hits
|
|
208
337
|
|
|
209
|
-
def
|
|
338
|
+
def upload_tg_project(self, instance="test"):
|
|
210
339
|
if not self.tg_session_id:
|
|
211
340
|
return False
|
|
212
341
|
|
|
213
342
|
# step 1: create required metadata
|
|
214
|
-
|
|
215
|
-
collection = self.collections[c_name]
|
|
216
|
-
collection["modeler"].render_collection()
|
|
343
|
+
self.project.tgm_project.render_project()
|
|
217
344
|
|
|
218
345
|
# step 2: push project to textgrid server
|
|
219
346
|
for collection_name in self.collections:
|
|
220
347
|
collection = self.collections[collection_name]
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
)
|
|
348
|
+
print(collection["modeler"].get_collection_path())
|
|
349
|
+
# self.tg_client.put_aggregation(
|
|
350
|
+
# self.tg_project_id,
|
|
351
|
+
# collection["modeler"].get_collection_path(),
|
|
352
|
+
# )
|
|
225
353
|
|
|
226
354
|
|
|
227
355
|
class CollectionParser(object):
|
|
@@ -247,7 +375,6 @@ class CollectionParser(object):
|
|
|
247
375
|
results = []
|
|
248
376
|
for element in self.elements:
|
|
249
377
|
result = element["tei_parser"].find(xpath)
|
|
250
|
-
print(result)
|
|
251
378
|
if result is not None:
|
|
252
379
|
results.append(
|
|
253
380
|
{
|