tg-prepare 2.2.2__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.
Files changed (82) hide show
  1. tg_prepare-2.2.2.dist-info/METADATA +20 -0
  2. tg_prepare-2.2.2.dist-info/RECORD +82 -0
  3. tg_prepare-2.2.2.dist-info/WHEEL +5 -0
  4. tg_prepare-2.2.2.dist-info/entry_points.txt +4 -0
  5. tg_prepare-2.2.2.dist-info/licenses/LICENSE +202 -0
  6. tg_prepare-2.2.2.dist-info/projects/.secret_key +1 -0
  7. tg_prepare-2.2.2.dist-info/top_level.txt +2 -0
  8. tgp_backend/__init__.py +17 -0
  9. tgp_backend/auth.py +71 -0
  10. tgp_backend/cli.py +95 -0
  11. tgp_backend/config.py +35 -0
  12. tgp_backend/directories.py +79 -0
  13. tgp_backend/interfaces.py +3 -0
  14. tgp_backend/nextcloud.py +146 -0
  15. tgp_backend/project.py +468 -0
  16. tgp_backend/session_manager.py +47 -0
  17. tgp_backend/tgclient.py +187 -0
  18. tgp_backend/user.py +137 -0
  19. tgp_backend/util.py +131 -0
  20. tgp_ui/__init__.py +0 -0
  21. tgp_ui/app.py +150 -0
  22. tgp_ui/routes/__init__.py +0 -0
  23. tgp_ui/routes/auth.py +72 -0
  24. tgp_ui/routes/collection.py +319 -0
  25. tgp_ui/routes/data.py +229 -0
  26. tgp_ui/routes/project.py +103 -0
  27. tgp_ui/routes/publication.py +229 -0
  28. tgp_ui/routes/tabs.py +34 -0
  29. tgp_ui/routes/views.py +66 -0
  30. tgp_ui/static/css/bootstrap-icons.min.css +5 -0
  31. tgp_ui/static/css/bootstrap.min.css +6 -0
  32. tgp_ui/static/css/bootstrap.min.css.map +1 -0
  33. tgp_ui/static/css/main.css +141 -0
  34. tgp_ui/static/css/navbar.css +92 -0
  35. tgp_ui/static/css/simpleXML.css +67 -0
  36. tgp_ui/static/img/favicon.ico +0 -0
  37. tgp_ui/static/img/textgrid-logo.svg +1 -0
  38. tgp_ui/static/js/bootstrap.bundle.min.js +7 -0
  39. tgp_ui/static/js/collectionManager.js +60 -0
  40. tgp_ui/static/js/fileManager.js +153 -0
  41. tgp_ui/static/js/jquery.min.js +2 -0
  42. tgp_ui/static/js/main.js +36 -0
  43. tgp_ui/static/js/modalManager.js +105 -0
  44. tgp_ui/static/js/navbarManager.js +151 -0
  45. tgp_ui/static/js/projectManager.js +60 -0
  46. tgp_ui/static/js/require.js +5 -0
  47. tgp_ui/static/js/sidebarManager.js +44 -0
  48. tgp_ui/static/js/simpleXML.js +193 -0
  49. tgp_ui/static/js/tabManager.js +83 -0
  50. tgp_ui/templates/auth/login.html +42 -0
  51. tgp_ui/templates/details/empty_container.html +16 -0
  52. tgp_ui/templates/details/manage_collection.html +209 -0
  53. tgp_ui/templates/file_tree.html +39 -0
  54. tgp_ui/templates/includes/get_sessionid.html +29 -0
  55. tgp_ui/templates/includes/publish_form.html +55 -0
  56. tgp_ui/templates/includes/set_sessionid.html +26 -0
  57. tgp_ui/templates/includes/upload_form.html +258 -0
  58. tgp_ui/templates/layout.html +74 -0
  59. tgp_ui/templates/macros.html +101 -0
  60. tgp_ui/templates/modal/delete_project.html +25 -0
  61. tgp_ui/templates/modal/empty_container.html +9 -0
  62. tgp_ui/templates/modal/file_explorer_content.html +34 -0
  63. tgp_ui/templates/modal/file_explorer_main.html +22 -0
  64. tgp_ui/templates/modal/file_explorer_nextcloud.html +27 -0
  65. tgp_ui/templates/modal/github_modal.html +29 -0
  66. tgp_ui/templates/modal/nextcloud_login.html +48 -0
  67. tgp_ui/templates/modal/tei_explorer.html +58 -0
  68. tgp_ui/templates/modal/xpath_parser.html +52 -0
  69. tgp_ui/templates/nxc_login.html +25 -0
  70. tgp_ui/templates/nxc_tab.html +15 -0
  71. tgp_ui/templates/project_main.html +36 -0
  72. tgp_ui/templates/project_navbar.html +81 -0
  73. tgp_ui/templates/projects_main.html +58 -0
  74. tgp_ui/templates/tabs/check_upload.html +87 -0
  75. tgp_ui/templates/tabs/edit_project.html +113 -0
  76. tgp_ui/templates/tabs/empty_container.html +12 -0
  77. tgp_ui/templates/tabs/import_data.html +122 -0
  78. tgp_ui/templates/tabs/manage_collections.html +107 -0
  79. tgp_ui/templates/tabs/publication.html +42 -0
  80. tgp_ui/templates/tabs/select_directories.html +68 -0
  81. tgp_ui/templates/tabs/upload.html +42 -0
  82. tgp_ui/templates/tabs/validate_metadata.html +227 -0
@@ -0,0 +1,209 @@
1
+ {% from "macros.html" import classification_input, multi_input, xpath_or_value_input, required_fields_disclaimer with
2
+ context %}
3
+ <div class="container py-4">
4
+ <!-- Navigation -->
5
+ <ul class="nav nav-pills mb-4 d-flex align-items-center" id="collectionNav" role="tablist">
6
+ <li class="nav-item" role="presentation">
7
+ <button class="nav-link active" id="basic-attributes-tab" data-bs-toggle="pill"
8
+ data-bs-target="#basicAttributes" type="button" role="tab" aria-controls="basicAttributes"
9
+ aria-selected="true">Collection Attributes</button>
10
+ </li>
11
+ <li class="nav-item" role="presentation">
12
+ <button class="nav-link" id="work-attributes-tab" data-bs-toggle="pill" data-bs-target="#workAttributes"
13
+ type="button" role="tab" aria-controls="workAttributes" aria-selected="false">Work</button>
14
+ </li>
15
+ <li class="nav-item" role="presentation">
16
+ <button class="nav-link" id="edition-attributes-tab" data-bs-toggle="pill"
17
+ data-bs-target="#editionAttributes" type="button" role="tab" aria-controls="editionAttributes"
18
+ aria-selected="false">Edition</button>
19
+ </li>
20
+ <li class="nav-item" role="presentation">
21
+ <button class="nav-link" id="eltecAttributes-tab" data-bs-toggle="pill" data-bs-target="#eltecAttributes"
22
+ type="button" role="tab" aria-controls="eltecAttributes" aria-selected="false">ELTEC
23
+ Specifications</button>
24
+ </li>
25
+ <li class="nav-item" role="presentation">
26
+ <button class="nav-link" id="classificationAttributes-tab" data-bs-toggle="pill"
27
+ data-bs-target="#classificationAttributes" type="button" role="tab"
28
+ aria-controls="classificationAttributes" aria-selected="false">Classifications</button>
29
+ </li>
30
+ </ul>
31
+
32
+ <!-- Config Sections -->
33
+ <div class="tab-content" id="collectionTabContent">
34
+ {% set collection_parser = project.get_collection_parser(collection) %}
35
+
36
+ <!-- Tab: Collection Attributes -->
37
+ <div class="tab-pane fade show active" id="basicAttributes" role="tabpanel"
38
+ aria-labelledby="basic-attributes-tab">
39
+ <form class="js-tab-submit" method="POST" enctype="multipart/form-data"
40
+ data-reload-subtab="#basicAttributes" action=" {{ url_for('collection.save_collection_attributes',
41
+ projectname=project.path, collectionname=collectionname) }}">
42
+ <div class="mb-3 row text-center">
43
+ <label class="col-sm-12 col-form-label text-center"><i>Collection Attributes</i>
44
+ </label>
45
+ </div>
46
+ <div class="mb-3">
47
+ <label class="form-label">
48
+ Short title
49
+ <span class="text-danger">*</span>
50
+ </label>
51
+ <input type="text" class="form-control" name="short_title"
52
+ value="{{ collection.short_title or '' }}" required>
53
+ </div>
54
+ <div class="mb-3">
55
+ <label class="form-label">
56
+ Long title
57
+ <span class="text-danger">*</span>
58
+ </label>
59
+ <input type="text" class="form-control" name="long_title" value="{{ collection.long_title or '' }}"
60
+ required>
61
+ </div>
62
+
63
+ <div class="mb-3">
64
+ <label class="form-label">
65
+ Rights Holder
66
+ <span class="text-danger">*</span>
67
+ </label>
68
+ {{ multi_input(collection.rights_holder or [{}], 'rights_holder') }}
69
+ </div>
70
+
71
+ <div class="mb-3">
72
+ <label class="form-label">
73
+ Collector
74
+ <span class="text-danger">*</span>
75
+ </label>
76
+ {{ multi_input(collection.collector or [{}], 'collector') }}
77
+ </div>
78
+ <div class="mt-3 row text-center">
79
+ <button type="submit" class="btn btn-primary">Save</button>
80
+ </div>
81
+ </form>
82
+ </div>
83
+
84
+ <!-- Tab: Classifications -->
85
+ <div class="tab-pane fade" id="classificationAttributes" role="tabpanel"
86
+ aria-labelledby="classificationAttributes-tab">
87
+ <form class="js-tab-submit" method="POST" data-reload-subtab="#classificationAttributes"
88
+ action="{{ url_for('collection.save_collection_classifications', projectname=project.path, collectionname=collectionname) }}">
89
+ {{ classification_input('basic_classifications', 'Collection classification(s)',
90
+ collection.basic_classifications) }}
91
+
92
+ {{ classification_input('gnd_subjects', 'GND Subject Area(s)', collection.gnd_subjects) }}
93
+ <div class="mt-3 row text-center">
94
+ <button type="submit" class="btn btn-primary">Save</button>
95
+ </div>
96
+ </form>
97
+ </div>
98
+
99
+ <!-- Tab: Work -->
100
+ <div class="tab-pane fade" id="workAttributes" role="tabpanel" aria-labelledby="work-attributes-tab">
101
+ <form class="js-tab-submit" method="POST" data-reload-subtab="#workAttributes"
102
+ action="{{ url_for('collection.save_collection_metadata', projectname=project.path, collectionname=collectionname) }}">
103
+ <div class="mb-3 row text-center">
104
+ <label class="col-sm-12 col-form-label text-center"><i>General</i>
105
+ </label>
106
+ </div>
107
+ {{ xpath_or_value_input('ID', 'work_id',
108
+ collection.work_id, collection_parser) }}
109
+ {{ xpath_or_value_input('Title', 'work_title', collection.work_title, collection_parser) }}
110
+ {{ xpath_or_value_input('Genre', 'genre', collection.genre, collection_parser, required=True) }}
111
+ {{ xpath_or_value_input('Place of publication', 'work_publication_place',
112
+ collection.work_publication_place, collection_parser) }}
113
+
114
+ <div class="mb-3 row">
115
+ <label class="col-sm-12 col-form-label text-center"><i>Author</i>
116
+ </label>
117
+ </div>
118
+ {{ xpath_or_value_input('ID', 'author_id', collection.author_id, collection_parser)
119
+ }}
120
+ {{ xpath_or_value_input('Firstname', 'author_firstname', collection.author_firstname, collection_parser)
121
+ }}
122
+ {{ xpath_or_value_input('Lastname', 'author_lastname', collection.author_lastname, collection_parser) }}
123
+ {{ xpath_or_value_input('Fullname', 'author_fullname', collection.author_fullname, collection_parser) }}
124
+ <div class="mb-3 row">
125
+ <label class="col-sm-12 col-form-label text-center">
126
+ <i title="Set Date OR notBefore + notAfter!">
127
+ Date of Creation
128
+ <span class="text-danger">*</span>
129
+ </i>
130
+ </label>
131
+ </div>
132
+ {{ xpath_or_value_input('Date', 'work_publication_date',
133
+ collection.work_publication_date,
134
+ collection_parser) }}
135
+ {{ xpath_or_value_input('notBefore', 'work_publication_notBefore',
136
+ collection.work_publication_notBefore,
137
+ collection_parser) }}
138
+ {{ xpath_or_value_input('notAfter', 'work_publication_notAfter',
139
+ collection.work_publication_notAfter,
140
+ collection_parser) }}
141
+ <div class="mt-3 row text-center">
142
+ <button type="submit" class="btn btn-primary">Save</button>
143
+ </div>
144
+ </form>
145
+ </div>
146
+
147
+ <!-- Tab: Edition -->
148
+ <div class="tab-pane fade" id="editionAttributes" role="tabpanel" aria-labelledby="edition-attributes-tab">
149
+ <form class="js-tab-submit" method="POST" data-reload-subtab="#editionAttributes"
150
+ action="{{ url_for('collection.save_collection_metadata', projectname=project.path, collectionname=collectionname) }}">
151
+ <div class="mb-3 row text-center">
152
+ <label class="col-sm-12 col-form-label text-center"><i>General</i>
153
+ </label>
154
+ </div>
155
+ {{ xpath_or_value_input('Title', 'edition_title', collection.edition_title, collection_parser) }}
156
+ {{ xpath_or_value_input('Author ID', 'edition_author', collection.edition_author, collection_parser) }}
157
+ {{ xpath_or_value_input('Language', 'language', collection.language, collection_parser) }}
158
+ {{ xpath_or_value_input('Wordcount', 'wordcount', collection.wordcount, collection_parser) }}
159
+ <div class="mb-3 row text-center">
160
+ <label class="col-sm-12 col-form-label text-center"><i>Publication</i>
161
+ </label>
162
+ </div>
163
+ {{ xpath_or_value_input('Date', 'edition_publication_date',
164
+ collection.edition_publication_date, collection_parser) }}
165
+ {{ xpath_or_value_input('Place', 'edition_publication_place',
166
+ collection.edition_publication_place, collection_parser) }}
167
+ <div class="mb-3 row text-center">
168
+ <label class="col-sm-12 col-form-label text-center"><i>License</i>
169
+ </label>
170
+ </div>
171
+ {{ xpath_or_value_input('Title', 'edition_license_title',
172
+ collection.edition_license_title, collection_parser) }}
173
+ {{ xpath_or_value_input('URL', 'edition_license_url',
174
+ collection.edition_license_url, collection_parser) }}
175
+ <div class="mt-3 row text-center">
176
+ <button type="submit" class="btn btn-primary">Save</button>
177
+ </div>
178
+ </form>
179
+ </div>
180
+
181
+ <!-- Tab: ELTEC Specifications -->
182
+ <div class="tab-pane fade" id="eltecAttributes" role="tabpanel" aria-labelledby="eltecAttributes-tab">
183
+ <form class="js-tab-submit" method="POST" data-reload-subtab="#eltecAttributes"
184
+ action="{{ url_for('collection.save_collection_metadata', projectname=project.path, collectionname=collectionname) }}">
185
+ <div class="mb-3 row text-center">
186
+ <label class="col-sm-12 col-form-label text-center"><i>General</i>
187
+ </label>
188
+ </div>
189
+ {{ xpath_or_value_input('Author gender', 'eltec_author_gender', collection.eltec_author_gender,
190
+ collection_parser) }}
191
+ {{ xpath_or_value_input('Reprint count', 'eltec_reprintCount', collection.eltec_reprintCount,
192
+ collection_parser) }}
193
+ {{ xpath_or_value_input('Timeslot', 'eltec_time_slot', collection.eltec_time_slot,
194
+ collection_parser) }}
195
+ {{ xpath_or_value_input('Corpus collection', 'eltec_corpus', collection.eltec_corpus,
196
+ collection_parser) }}
197
+ {{ xpath_or_value_input('Size', 'eltec_size', collection.eltec_size,
198
+ collection_parser) }}
199
+ <div class="mt-3 row text-center">
200
+ <button type="submit" class="btn btn-primary">Save</button>
201
+ </div>
202
+ </form>
203
+ </div>
204
+
205
+ {{ required_fields_disclaimer() }}
206
+
207
+
208
+ </div>
209
+ </div>
@@ -0,0 +1,39 @@
1
+ {% set active_class = 'bg-warning' if project.main_config.get_subproject(inpath=item.path) else '' %}
2
+ {% if item.type == 'folder' %}
3
+ <li class="list-group-item {{ active_class }} folder">
4
+ <div class="d-flex justify-content-between align-items-center">
5
+ <span>
6
+ <a href="#" class="badge text-dark show-files" title="Show files">
7
+ <span class="bi bi-eye-slash-fill"></span>
8
+ </a>
9
+ <span class="bi-folder-fill"></span>
10
+ {{ item.name }}
11
+ <a href="#" class="badge text-dark select-folder" title="Select folder"
12
+ data-url="{{ url_for('set_configs', projectname=project.name) }}">
13
+ {% if project.main_config.get_subproject(inpath=item.path) %}
14
+ <span class="bi bi-circle-fill" data-path="{{ item.path }}"></span>
15
+ {% else %}
16
+ <span class="bi bi-circle" data-path="{{ item.path }}"></span>
17
+ {% endif %}
18
+ </a>
19
+ <span class="spinner-border spinner-border-sm" style="display:none;"></span>
20
+ </span>
21
+ <span>
22
+ <span class="badge bg-primary rounded-pill">{{ item.children.count }}</span>
23
+ </span>
24
+ </div>
25
+ {% for child in item.children.list %}
26
+ {% with item=child %}
27
+ <ul class="list-group list-group-flush">
28
+ {% include 'file_tree.html' %}
29
+ </ul>
30
+ {% endwith %}
31
+ {% endfor %}
32
+ </li>
33
+ {% else %}
34
+ <li class="list-group-item p-0 ps-3 m-0 small text-muted {{ active_class }} file" style="display: none;">
35
+ <span class="bi-file-earmark-text-fill"></span>
36
+ {{ item.name }}
37
+ </span>
38
+ </li>
39
+ {% endif %}
@@ -0,0 +1,29 @@
1
+ <!DOCTYPE html>
2
+ <div class="col-12 text-center">
3
+ {% if not tg_session_id %}
4
+ <!-- Show note when no session ID is set -->
5
+ <div class="alert alert-warning fst-italic">
6
+ <h6 class="fw-bold">Attention</h6>
7
+ <p>
8
+ <b>You need to provide a "TgAuth Session ID" in order to {{action}} your project to
9
+ TextGrid-{{ instance|capitalize }} Instance!</b>
10
+ </p>
11
+ <ol class="mb-0">
12
+ <li>Please first go to <a href="{{ get_textgrid_login_url(instance) }}" target="_blank">this
13
+ page</a>!
14
+ (You may need to log in via DARIAH-AAI)</li>
15
+ <li>Then click on "More Details" and copy the "TgAuth Session ID" to your clipboard.</li>
16
+ <li>Finally, paste it into the input field below and save it.</li>
17
+ </ol>
18
+ </div>
19
+ {% elif not is_valid_session %}
20
+ <!-- Show note when session ID is invalid -->
21
+ <div class="alert alert-warning fst-italic">
22
+ <h6 class="fw-bold">Attention</h6>
23
+ <p>
24
+ It looks like the given <b>Session ID is invalid</b>. Please try again and, if needed, check <a
25
+ href="{{ get_textgrid_login_url(instance) }}" target="_blank">here</a> again.
26
+ </p>
27
+ </div>
28
+ {% endif %}
29
+ </div>
@@ -0,0 +1,55 @@
1
+ <!DOCTYPE html>
2
+ <div class="row">
3
+ {% set tg_session_id = project.main_config.get_tg_session_id(instance) %}
4
+ {% set is_valid_session = project.get_tgp(instance).check_session() %}
5
+
6
+ <!-- Left column: Project management -->
7
+ {% set action = 'publish' %}
8
+ {% include 'includes/get_sessionid.html' %}
9
+
10
+ <div class="bg-light p-4 h-100 border border-1 w-100">
11
+
12
+ {% include 'includes/set_sessionid.html' %}
13
+
14
+ {% if is_valid_session %}
15
+ <div class="mb-4">
16
+ <h4 class="mb-3 border-bottom pb-2">Selected TextGrid Project</h4>
17
+ {% set tg_project_id = project.main_config.get_tg_project_id(instance) %}
18
+ {% set tgproject_handler = project.get_tgp(instance) %}
19
+ {% set tg_project = tgproject_handler.get_tg_project(tg_project_id) %}
20
+ <div class="d-flex align-items-center flex-wrap gap-3 py-2 px-3 bg-white border rounded">
21
+ <span class="fw-bold d-flex align-items-center">
22
+ <i class="bi bi-folder2-open me-2"></i>{{ tg_project.name }}
23
+ </span>
24
+ <span class="text-muted small" title="Project ID">
25
+ {{ tg_project.id }}
26
+ </span>
27
+ <div class="ms-auto d-flex align-items-center gap-2">
28
+ <span class="badge bg-primary" title="TEI Files">TEI: {{ tg_project.tei_count }}</span>
29
+ <span class="badge bg-success" title="Images">Img: {{ tg_project.img_count }}</span>
30
+ <span class="badge bg-secondary" title="Other Files">Other: {{ tg_project.other_count }}</span>
31
+ </div>
32
+ </div>
33
+ </div>
34
+ <div class="d-flex gap-3 justify-content-end">
35
+ <form class="js-tab-submit m-0" method="POST"
36
+ action="{{ url_for('publication.publish_project', projectname=project.path, action=action, instance=instance) }}">
37
+ <button type="submit" class="btn btn-outline-primary px-4">
38
+ Proof
39
+ <span class="spinner-border spinner-border-sm" style="display:none;" role="status"
40
+ aria-hidden="true"></span>
41
+ </button>
42
+ </form>
43
+ <form class="js-tab-submit m-0" method="POST"
44
+ action="{{ url_for('publication.publish_project', projectname=project.path, action=action, instance=instance) }}">
45
+ <button type="submit" class="btn btn-primary px-4" disabled>
46
+ Publish
47
+ <span class="spinner-border spinner-border-sm" style="display:none;" role="status"
48
+ aria-hidden="true"></span>
49
+ </button>
50
+ </form>
51
+ </div>
52
+ {% endif %}
53
+ </div>
54
+
55
+ </div>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <form class="row mb-1 js-tab-submit" method="POST"
3
+ action="{{ url_for('publication.save_session_id', projectname=project.path, action=action, instance=instance) }}">
4
+ <h5 class="mb-3">Set Session ID of TextGrid-{{ instance|capitalize }} Instance</h5>
5
+ <div class="{{tg_session_id and 'col-lg-8' or 'col-lg-10'}} mb-3">
6
+ <input type="text" class="form-control" name="tg_auth_session_id" id="tg_auth_session_id_{{instance}}"
7
+ placeholder="TgAuth Session ID" value="{{ tg_session_id if tg_session_id }}">
8
+ </div>
9
+ <div class="col-lg-2 mb-3">
10
+ <button type="submit" class="btn btn-primary w-100" style="min-width: 48px;">
11
+ <span class="bi bi-floppy2-fill"></span>
12
+ <span class="spinner-border spinner-border-sm" style="display:none;" role="status"
13
+ aria-hidden="true"></span>
14
+ </button>
15
+ </div>
16
+ {% if tg_session_id %}
17
+ <div class="col-lg-2">
18
+ <button type="button" class="btn btn-secondary w-100" style="min-width: 48px;" onclick="
19
+ this.closest('form').querySelector('input[type=text]').value = '';
20
+ this.closest('form').querySelector('button[type=submit]').click()
21
+ ">
22
+ <span class="bi bi-x-circle"></span>
23
+ </button>
24
+ </div>
25
+ {% endif %}
26
+ </form>
@@ -0,0 +1,258 @@
1
+ <!DOCTYPE html>
2
+ <div class="row">
3
+ {% set tg_session_id = project.main_config.get_tg_session_id(instance) %}
4
+ {% set is_valid_session = project.get_tgp(instance).check_session() %}
5
+
6
+ <!-- Left column: Project management -->
7
+ {% set action = 'upload' %}
8
+ {% include 'includes/get_sessionid.html' %}
9
+
10
+ <div class="col-lg-7 mb-2 mb-lg-0">
11
+ <div class="bg-light rounded p-4 h-100">
12
+
13
+ {% include 'includes/set_sessionid.html' %}
14
+
15
+ {% if tg_session_id %}
16
+ <h5 class="border-top pt-3">New project:</h5>
17
+ <div class="row pb-1">
18
+ <form class="row tab-submit js-tab-submit" method="POST"
19
+ action="{{ url_for('publication.create_tg_project', projectname=project.path, action=action, instance=instance) }}">
20
+ <div class="col-8">
21
+ <input type="text" class="form-control" name="tg_projectname" placeholder="TG Projectname"
22
+ required>
23
+ </div>
24
+ <div class="col-2">
25
+ <button type="submit" class="btn btn-primary w-100">
26
+ <span class="bi bi-pencil-square"></span>
27
+ <span class="spinner-border spinner-border-sm" style="display:none;" role="status"
28
+ aria-hidden="true"></span>
29
+ </button>
30
+ </div>
31
+ </form>
32
+ </div>
33
+
34
+ <h5 class="my-3 border-top pt-3">Select from existing projects</h5>
35
+ <div class="row pb-1">
36
+ {% set tg_project_id = project.main_config.get_tg_project_id(instance) %}
37
+ {% for tg_project in project.get_tgp(instance).get_tg_projects() %}
38
+ <div class="col-md-6 mb-3">
39
+ <div
40
+ class="card h-100 {{'border-primary' if tg_project_id == tg_project.id else 'border-secondary'}}">
41
+ <div class="card-body p-3">
42
+ <div class="d-flex align-items-center flex-wrap gap-3">
43
+ <span class="fw-bold d-flex align-items-center">
44
+ <i class="bi bi-folder2-open me-2"></i>{{ tg_project.name }}
45
+ </span>
46
+ <span class="" title="Project ID">{{ tg_project.id }}</span>
47
+ <span class="badge bg-primary ms-auto" title="TEI Files">TEI: {{ tg_project.tei_count
48
+ }}</span>
49
+ <span class="badge bg-success" title="Images">Img: {{ tg_project.img_count }}</span>
50
+ <span class="badge bg-secondary" title="Other Files">Other: {{ tg_project.other_count
51
+ }}</span>
52
+ </div>
53
+ </div>
54
+ <div class="card-footer d-flex justify-content-between align-items-center">
55
+ <form method="POST" class="js-tab-submit"
56
+ action="{{ url_for('publication.save_tg_project_id', projectname=project.path, action=action, instance=instance, tg_project_id=tg_project.id) }}">
57
+ <button type="submit" class="btn btn-sm btn-outline-primary">
58
+ {% if tg_project_id == tg_project.id %}
59
+ <span class="bi bi-check-circle-fill text-primary"></span> Selected
60
+ {% else %}
61
+ <span class="bi bi-circle"></span> Select
62
+ {% endif %}
63
+ </button>
64
+ </form>
65
+ <form method="POST" class="js-tab-submit"
66
+ action="{{ url_for('publication.clear_tg_project', projectname=project.path, action=action, instance=instance, tg_project_id=tg_project.id) }}">
67
+ <button type="submit" class="btn btn-sm btn-outline-warning text-secondary"
68
+ title="Clear project">
69
+ <span class="bi bi-eraser"></span> Clear
70
+ </button>
71
+ </form>
72
+ <form method="POST" class="js-tab-submit"
73
+ action="{{ url_for('publication.delete_tg_project_id', projectname=project.path, action=action, instance=instance, tg_project_id=tg_project.id) }}">
74
+ <button type="submit" class="btn btn-sm btn-outline-danger" title="Delete project">
75
+ <span class="bi bi-trash"></span> Delete
76
+ </button>
77
+ </form>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ {% endfor %}
82
+ </div>
83
+ </div>
84
+ {% endif %}
85
+ </div>
86
+
87
+ <!-- Rechte Spalte: Upload -->
88
+ <div class="col-lg-5 border-start">
89
+ {% if is_valid_session %}
90
+ <div class="bg-light rounded p-4 h-100">
91
+ <div class="text-center mb-4">
92
+ <h4 class="mb-2"><i
93
+ class="bi bi-cloud-upload {% if instance == 'test' %}text-primary{% else %}text-danger{% endif %} me-2"></i>Upload
94
+ to {{ instance|capitalize }} Instance</h4>
95
+ {% if instance == 'test' %}
96
+ <span class="badge bg-warning text-dark mb-2">
97
+ This upload is for <b>testing purposes</b>.
98
+ </span>
99
+ {% else %}
100
+ <span class="badge bg-danger text-light mb-2">
101
+ This starts a <b>real publication</b>.
102
+ <br />
103
+ Please ensure all data is correct.
104
+ </span>
105
+ {% endif %}
106
+ </div>
107
+ {% if not tg_project_id %}
108
+ <div class="alert alert-warning mt-3 text-center" role="alert">
109
+ <p>
110
+ <i class="bi bi-exclamation-triangle-fill me-2"></i>
111
+ <strong>No TG Project selected!</strong>
112
+ </p>
113
+ <p>
114
+ Please select a project from the list above or create a new one.
115
+ </p>
116
+ </div>
117
+ {% else %}
118
+ {% set p_validation = project.get_validation_results(refresh=False, file_attributes=True) %}
119
+ {% set tg_project = tg_project if tg_project else project.get_tgp(instance) %}
120
+ {% set all_tgp_contents = tg_project.get_project_contents() %}
121
+ {% for c_validation in p_validation["subprojects"] %}
122
+ <div class="card mb-3">
123
+ <div class="card-header fw-bold">
124
+ {{c_validation.title}}
125
+ </div>
126
+ <div class="card-body">
127
+ <div class="table-responsive mb-3">
128
+ <table class="table table-sm align-middle mb-0">
129
+ <thead>
130
+ <tr class="text-center">
131
+ <th class="w-25">
132
+ </th>
133
+ <th class="w-25">
134
+ <span class="bi bi-clipboard-check" title="Ready for upload"></span>
135
+ </th>
136
+ <th class="w-25">
137
+ <span class="bi bi-cloud-arrow-up" title="Uploaded"></span>
138
+ </th>
139
+ <th class="w-25">
140
+ <span class="bi bi-cloud-check" title="Published"></span>
141
+ </th>
142
+ </tr>
143
+ </thead>
144
+ <tbody>
145
+ {% for file_name, file_errors in c_validation.files.items() %}
146
+ {% if 'file_attributes' in c_validation %}
147
+ {% set title = c_validation.file_attributes[file_name]['work_title'] %}
148
+ {% else %}
149
+ {% set title = file_name %}
150
+ {% endif %}
151
+ <tr>
152
+ <td class="small text-truncate" style="max-width: 12em;"
153
+ title="{{title}} ({{file_name}})">
154
+ {{title}} ({{file_name}})
155
+ </td>
156
+ <td class="text-center">
157
+ {% if len(file_errors) == 0 %}
158
+ <span class="bi bi-check-circle-fill text-success" title="No errors"></span>
159
+ {% else %}
160
+ <span class="bi bi-x-circle-fill text-danger" title="Errors detected"></span>
161
+ {% endif %}
162
+ </td>
163
+ <td class="text-center">
164
+ {% if all_tgp_contents[title] %}
165
+ <span class="bi bi-check-circle-fill text-success" title="Uploaded"></span>
166
+ {% else %}
167
+ <span class="bi bi-dash-circle" title="Not uploaded"></span>
168
+ {% endif %}
169
+ </td>
170
+ <td class="text-center">
171
+ {% if all_tgp_contents[title] and all_tgp_contents[title]['published'] %}
172
+ <span class="bi bi-check-circle-fill text-success" title="Published"></span>
173
+ {% else %}
174
+ <span class="bi bi-dash-circle" title="Not published"></span>
175
+ {% endif %}
176
+ </td>
177
+ </tr>
178
+ {% endfor %}
179
+ </tbody>
180
+ </table>
181
+ </div>
182
+ {% if c_validation.stats.error == 0 %}
183
+ <form method="POST"
184
+ action="{{ url_for('publication.upload_collection', projectname=project.path, action=action, collection=c_validation.title, instance=instance) }}"
185
+ class="w-100 js-tab-submit">
186
+ <button type="submit" class="btn btn-success w-100 py-3" style="font-size:1.25rem;">
187
+ <i class="bi bi-rocket-takeoff me-2"></i>Upload
188
+ </button>
189
+ </form>
190
+ {% else %}
191
+ <div class="alert alert-danger mt-2 mb-0 p-2 small">
192
+ <i class="bi bi-exclamation-triangle me-1"></i>
193
+ Validation Errors detected – please fix before uploading.
194
+ </div>
195
+ {% endif %}
196
+ </div>
197
+ </div>
198
+ {% endfor %}
199
+ <div class="card mb-3">
200
+ <div class="card-header fw-bold">
201
+ Other Files
202
+ </div>
203
+ <div class="card-body">
204
+ <div class="table-responsive mb-3">
205
+ <table class="table table-sm align-middle mb-0">
206
+ <thead>
207
+ <tr class="text-center">
208
+ <th class="w-25">
209
+ </th>
210
+ <th class="w-25">&nbsp;</th>
211
+ <th class="w-25">
212
+ <span class="bi bi-cloud-arrow-up" title="Uploaded"></span>
213
+ </th>
214
+ <th class="w-25">
215
+ <span class="bi bi-cloud-check" title="Published"></span>
216
+ </th>
217
+ </tr>
218
+ </thead>
219
+ <tbody>
220
+ {% for filename in project.project_config.other_files.get_all_files() %}
221
+ <tr>
222
+ <td class="small text-truncate" style="max-width: 12em;" title="{{file_name}}">
223
+ {{filename}}
224
+ </td>
225
+ <td class="text-center">&nbsp;</td>
226
+ <td class="text-center">
227
+ {% if all_tgp_contents[filename] %}
228
+ <span class="bi bi-check-circle-fill text-success" title="Uploaded"></span>
229
+ {% else %}
230
+ <span class="bi bi-dash-circle" title="Not uploaded"></span>
231
+ {% endif %}
232
+ </td>
233
+ <td class="text-center">
234
+ {% if all_tgp_contents[filename] and all_tgp_contents[filename]['published'] %}
235
+ <span class="bi bi-check-circle-fill text-success" title="Published"></span>
236
+ {% else %}
237
+ <span class="bi bi-dash-circle" title="Not published"></span>
238
+ {% endif %}
239
+ </td>
240
+ </tr>
241
+ {% endfor %}
242
+ </tbody>
243
+ </table>
244
+ </div>
245
+ <form method="POST"
246
+ action="{{ url_for('publication.upload_other_files', projectname=project.path, action=action, instance=instance) }}"
247
+ class="w-100 js-tab-submit">
248
+ <button type="submit" class="btn btn-success w-100 py-3" style="font-size:1.25rem;">
249
+ <i class="bi bi-rocket-takeoff me-2"></i>Upload
250
+ </button>
251
+ </form>
252
+ </div>
253
+ </div>
254
+ {% endif %}
255
+ </div>
256
+ {% endif %}
257
+ </div>
258
+ </div>