libreqda 1.2.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.
- CHANGELOG.md +481 -0
- libreqda-1.2.0.dist-info/LICENSE.txt +28 -0
- libreqda-1.2.0.dist-info/METADATA +131 -0
- libreqda-1.2.0.dist-info/RECORD +255 -0
- libreqda-1.2.0.dist-info/WHEEL +4 -0
- libreqda-1.2.0.dist-info/entry_points.txt +4 -0
- scripts/babelrc +8 -0
- scripts/dist/LICENSE.txt +33 -0
- scripts/dist/RELEASE.txt +23 -0
- scripts/dist/macos/libreqda.icns +0 -0
- scripts/dist/macos/macos.spec +43 -0
- scripts/dist/macos/plist.patch +10 -0
- scripts/dist/macos/taguette.icns +0 -0
- scripts/dist/macos/taguette_console_wrapper +4 -0
- scripts/dist/macos.sh +32 -0
- scripts/dist/pyinstaller_entrypoint.py +19 -0
- scripts/dist/windows/windows-installer.iss +51 -0
- scripts/dist/windows/windows.spec +39 -0
- scripts/edugain/worker_update_edugain_idps.py +89 -0
- scripts/edugain/worker_update_edugain_idps.sh +9 -0
- scripts/make_password_reset_link.py +56 -0
- scripts/make_theme.py +99 -0
- scripts/new_db_revision.sh +8 -0
- scripts/register_user.py +76 -0
- scripts/update_pot.sh +33 -0
- scripts/update_translations.sh +17 -0
- scripts/validate.py +140 -0
- scripts/wipe_and_disable_user.py +90 -0
- taguette/__init__.py +13 -0
- taguette/access_control.py +145 -0
- taguette/convert.py +623 -0
- taguette/database/__init__.py +177 -0
- taguette/database/base.py +9 -0
- taguette/database/copy.py +433 -0
- taguette/database/models.py +920 -0
- taguette/demodata.py +688 -0
- taguette/export.py +500 -0
- taguette/extract.py +287 -0
- taguette/import_codebook.py +110 -0
- taguette/l10n/de_DE/LC_MESSAGES/taguette_javascript.mo +0 -0
- taguette/l10n/de_DE/LC_MESSAGES/taguette_main.mo +0 -0
- taguette/l10n/el_EL/LC_MESSAGES/taguette_javascript.mo +0 -0
- taguette/l10n/el_GR/LC_MESSAGES/taguette_javascript.mo +0 -0
- taguette/l10n/el_GR/LC_MESSAGES/taguette_main.mo +0 -0
- taguette/l10n/en_US/LC_MESSAGES/taguette_javascript.mo +0 -0
- taguette/l10n/en_US/LC_MESSAGES/taguette_main.mo +0 -0
- taguette/l10n/es_ES/LC_MESSAGES/taguette_javascript.mo +0 -0
- taguette/l10n/es_ES/LC_MESSAGES/taguette_main.mo +0 -0
- taguette/l10n/fr_FR/LC_MESSAGES/taguette_javascript.mo +0 -0
- taguette/l10n/fr_FR/LC_MESSAGES/taguette_main.mo +0 -0
- taguette/l10n/id_ID/LC_MESSAGES/taguette_javascript.mo +0 -0
- taguette/l10n/id_ID/LC_MESSAGES/taguette_main.mo +0 -0
- taguette/l10n/it_IT/LC_MESSAGES/taguette_javascript.mo +0 -0
- taguette/l10n/it_IT/LC_MESSAGES/taguette_main.mo +0 -0
- taguette/l10n/nl_NL/LC_MESSAGES/taguette_javascript.mo +0 -0
- taguette/l10n/nl_NL/LC_MESSAGES/taguette_main.mo +0 -0
- taguette/l10n/pt_PT/LC_MESSAGES/taguette_javascript.mo +0 -0
- taguette/l10n/pt_PT/LC_MESSAGES/taguette_main.mo +0 -0
- taguette/l10n/sv_SE/LC_MESSAGES/taguette_javascript.mo +0 -0
- taguette/l10n/sv_SE/LC_MESSAGES/taguette_main.mo +0 -0
- taguette/loki_handler.py +107 -0
- taguette/main.py +649 -0
- taguette/migrations/env.py +72 -0
- taguette/migrations/script.py.mako +24 -0
- taguette/migrations/versions/09c662cd9483_make_documents_filename_not_null.py +39 -0
- taguette/migrations/versions/1b29e68900c1_add_user_roles.py +55 -0
- taguette/migrations/versions/1b96a606551d_update_user_to_have_a_display_name.py +34 -0
- taguette/migrations/versions/2467bab602b7_add_color_column_to_tags_table.py +33 -0
- taguette/migrations/versions/382db36f20f4_add_user_disabled.py +66 -0
- taguette/migrations/versions/43d6c240309d_remove_document_groups.py +81 -0
- taguette/migrations/versions/447d636f72c5_cascades.py +40 -0
- taguette/migrations/versions/491de2dc7cd7_add_indexes.py +51 -0
- taguette/migrations/versions/4c55b3b3ff30_user_login_length.py +58 -0
- taguette/migrations/versions/61a4abcc8769_user_with_parent_for_permissions.py +40 -0
- taguette/migrations/versions/6489b5f9cfb5_increase_password_hash_size.py +34 -0
- taguette/migrations/versions/679f625e6e6a_fix_unique_tags.py +59 -0
- taguette/migrations/versions/77c4543a5b9b_add_parent_id_column_to_collection_table.py +41 -0
- taguette/migrations/versions/7c94b7a31e69_add_is_archived_column_to_projets.py +39 -0
- taguette/migrations/versions/7f56de97e3c8_change_edugain_displayname_length.py +42 -0
- taguette/migrations/versions/807c0cc0ecf6_remove_invalid_hltags.py +43 -0
- taguette/migrations/versions/80b1cc9d4c22_add_user_email.py +35 -0
- taguette/migrations/versions/91ade71ccf4d_add_user_language.py +28 -0
- taguette/migrations/versions/955db2e523d1_add_collections_tables_and_associations_.py +60 -0
- taguette/migrations/versions/a1b2c3d4e5f6_add_user_hide_code_counts.py +28 -0
- taguette/migrations/versions/b23f3b7a638e_add_user_password_change_date.py +36 -0
- taguette/migrations/versions/b7c1e2d3f4a5_add_project_compact_layout.py +28 -0
- taguette/migrations/versions/bc8e0e0677e9_rename_command_fields.py +126 -0
- taguette/migrations/versions/bce44849c2f2_lowercase_logins.py +96 -0
- taguette/migrations/versions/ca076140922f_update_user_for_external_logins.py +32 -0
- taguette/migrations/versions/d86c45d7aa8a_add_document_direction.py +127 -0
- taguette/migrations/versions/db5e31a0233d_remove_blank_highlights.py +88 -0
- taguette/migrations/versions/dd37d9027a24_unique_tags.py +68 -0
- taguette/migrations/versions/de678cef2a94_add_project_id_to_collections_and_.py +41 -0
- taguette/migrations/versions/e459991d960e_add_table_edugainidps.py +39 -0
- taguette/migrations/versions/e4cf92942271_add_index.py +29 -0
- taguette/migrations/versions/e4e090a5b511_remove_commands_document_id_fk.py +38 -0
- taguette/migrations/versions/e9a9f1c77971_add_favorite_column_to_document.py +32 -0
- taguette/migrations/versions/ecb4065de575_don_t_use_server_default_now_timezone_.py +44 -0
- taguette/migrations/versions/fbdc29566f62_edugain_fix_field_type.py +42 -0
- taguette/migrations/versions/fdc6eb54b034_add_parent_id_column_to_tags_table.py +31 -0
- taguette/static/README.md +34 -0
- taguette/static/alt_templates/.gitkeep +0 -0
- taguette/static/assets/test/logos/monsters-university-logo.png +0 -0
- taguette/static/css/custom.css +1696 -0
- taguette/static/css/fa-all.min.css +5 -0
- taguette/static/css/themed.css +3 -0
- taguette/static/css/themed.css.map +25 -0
- taguette/static/css/vendor/bootstrap-treeview.min.css +1 -0
- taguette/static/css/vendor/bootstrap.min.css +6 -0
- taguette/static/css/vendor/font/context-menu-icons.eot +0 -0
- taguette/static/css/vendor/font/context-menu-icons.ttf +0 -0
- taguette/static/css/vendor/font/context-menu-icons.woff +0 -0
- taguette/static/css/vendor/font/context-menu-icons.woff2 +0 -0
- taguette/static/css/vendor/hierarchy-select.min.css +1 -0
- taguette/static/css/vendor/introjs.min.css +2 -0
- taguette/static/css/vendor/jquery.contextMenu.min.css +16 -0
- taguette/static/favicon.ico +0 -0
- taguette/static/js/codebook-viz.js +428 -0
- taguette/static/js/collection/context-menu.js +180 -0
- taguette/static/js/collection/hierarchy-select.js +71 -0
- taguette/static/js/collection/manager.js +217 -0
- taguette/static/js/document/context-menu.js +92 -0
- taguette/static/js/document/custom-select.js +82 -0
- taguette/static/js/document/manager.js +943 -0
- taguette/static/js/document/treeview.js +593 -0
- taguette/static/js/highlight/README.md +18 -0
- taguette/static/js/highlight/crud.js +119 -0
- taguette/static/js/highlight/extracts.js +497 -0
- taguette/static/js/highlight/loader.js +109 -0
- taguette/static/js/highlight/renderer.js +405 -0
- taguette/static/js/login.js +15 -0
- taguette/static/js/permissions.js +153 -0
- taguette/static/js/polling-event.js +145 -0
- taguette/static/js/project/intro.js +524 -0
- taguette/static/js/project/manager.js +0 -0
- taguette/static/js/project/search.js +124 -0
- taguette/static/js/tag/context-menu.js +133 -0
- taguette/static/js/tag/explore-codes.js +400 -0
- taguette/static/js/tag/hierarchy-select.js +69 -0
- taguette/static/js/tag/manager.js +545 -0
- taguette/static/js/tag/treeview.js +613 -0
- taguette/static/js/taguette.js +1790 -0
- taguette/static/js/utilities.js +714 -0
- taguette/static/js/vendor/bootstrap-treeview-custum.js +421 -0
- taguette/static/js/vendor/bootstrap-treeview-custum.min.js +1 -0
- taguette/static/js/vendor/bootstrap-treeview-default.min.js +1 -0
- taguette/static/js/vendor/bootstrap.bundle.min.js +7 -0
- taguette/static/js/vendor/bootstrap.bundle.min.js.map +1 -0
- taguette/static/js/vendor/hierarchy-select.min.js +1 -0
- taguette/static/js/vendor/introjs.min.js +17 -0
- taguette/static/js/vendor/jquery-3.7.1.js +2 -0
- taguette/static/js/vendor/jquery.contextMenu.min.js +2 -0
- taguette/static/libreqda.ico +0 -0
- taguette/static/logo-h.png +0 -0
- taguette/static/logo.png +0 -0
- taguette/static/logo_blanc.png +0 -0
- taguette/static/missing.png +0 -0
- taguette/static/robots.txt +2 -0
- taguette/static/scss/components/_badge.scss +42 -0
- taguette/static/scss/components/_button.scss +169 -0
- taguette/static/scss/components/_checkbox.scss +40 -0
- taguette/static/scss/components/_context_menu.scss +28 -0
- taguette/static/scss/components/_custom_select.scss +150 -0
- taguette/static/scss/components/_dropdown.scss +43 -0
- taguette/static/scss/components/_form.scss +54 -0
- taguette/static/scss/components/_modal.scss +57 -0
- taguette/static/scss/components/_project_card.scss +163 -0
- taguette/static/scss/components/_tag.scss +308 -0
- taguette/static/scss/components/_treeview.scss +76 -0
- taguette/static/scss/custom.scss +809 -0
- taguette/static/scss/layout/_credits.scss +62 -0
- taguette/static/scss/layout/_footer.scss +6 -0
- taguette/static/scss/layout/_navigation.scss +165 -0
- taguette/static/scss/layout/_sidebar.scss +37 -0
- taguette/static/scss/themes/default/_variables.scss +103 -0
- taguette/static/scss/themes/default/theme.css +3 -0
- taguette/static/scss/themes/default/theme.css.map +26 -0
- taguette/static/scss/themes/default/theme.scss +4 -0
- taguette/static/webfonts/BadScript-OFL.txt +94 -0
- taguette/static/webfonts/BadScript.ttf +0 -0
- taguette/static/webfonts/Montserrat-Light.ttf +0 -0
- taguette/static/webfonts/Montserrat-OFL.txt +93 -0
- taguette/static/webfonts/fa-brands-400.eot +0 -0
- taguette/static/webfonts/fa-brands-400.svg +1184 -0
- taguette/static/webfonts/fa-brands-400.ttf +0 -0
- taguette/static/webfonts/fa-brands-400.woff +0 -0
- taguette/static/webfonts/fa-brands-400.woff2 +0 -0
- taguette/static/webfonts/fa-regular-400.eot +0 -0
- taguette/static/webfonts/fa-regular-400.svg +467 -0
- taguette/static/webfonts/fa-regular-400.ttf +0 -0
- taguette/static/webfonts/fa-regular-400.woff +0 -0
- taguette/static/webfonts/fa-regular-400.woff2 +0 -0
- taguette/static/webfonts/fa-solid-900.eot +0 -0
- taguette/static/webfonts/fa-solid-900.svg +2618 -0
- taguette/static/webfonts/fa-solid-900.ttf +0 -0
- taguette/static/webfonts/fa-solid-900.woff +0 -0
- taguette/static/webfonts/fa-solid-900.woff2 +0 -0
- taguette/templates/alt_templates/.gitkeep +0 -0
- taguette/templates/archived_projects.html +50 -0
- taguette/templates/auth/account.html +74 -0
- taguette/templates/auth/cilogon.login.html +89 -0
- taguette/templates/auth/email_reset_password.html +5 -0
- taguette/templates/auth/external_account.html +60 -0
- taguette/templates/auth/login.html +144 -0
- taguette/templates/auth/reset_password.html +27 -0
- taguette/templates/auth/token_needed.html +7 -0
- taguette/templates/base.html +209 -0
- taguette/templates/bug_report.html +158 -0
- taguette/templates/cookies_prompt.html +15 -0
- taguette/templates/downloads.html +127 -0
- taguette/templates/email_bug_report.txt +9 -0
- taguette/templates/email_reset_password.txt +5 -0
- taguette/templates/error.html +8 -0
- taguette/templates/export_codebook.html +20 -0
- taguette/templates/export_document.html +20 -0
- taguette/templates/export_highlights.html +47 -0
- taguette/templates/index.html +347 -0
- taguette/templates/maintenance.html +8 -0
- taguette/templates/messages.js +74 -0
- taguette/templates/modal/change_document.html +68 -0
- taguette/templates/modal/create_collection.html +100 -0
- taguette/templates/modal/create_document.html +84 -0
- taguette/templates/modal/create_highlight.html +38 -0
- taguette/templates/modal/create_tag.html +73 -0
- taguette/templates/modal/explore_codes.html +58 -0
- taguette/templates/modal/manage_collaborators.html +44 -0
- taguette/templates/modal/merge_tag.html +49 -0
- taguette/templates/new_password.html +32 -0
- taguette/templates/partials/alert.html +16 -0
- taguette/templates/partials/codebook_viz_modal.html +34 -0
- taguette/templates/partials/confirm.html +17 -0
- taguette/templates/partials/document_toolbar.html +37 -0
- taguette/templates/partials/hlinfo.html +13 -0
- taguette/templates/partials/project_card.html +44 -0
- taguette/templates/partials/project_card_archived.html +41 -0
- taguette/templates/partials/project_sidebar.html +99 -0
- taguette/templates/partials/spinner.html +11 -0
- taguette/templates/permissions.html +54 -0
- taguette/templates/project.html +288 -0
- taguette/templates/project_delete.html +17 -0
- taguette/templates/project_import.html +122 -0
- taguette/templates/project_import_codebook.html +34 -0
- taguette/templates/project_import_codebook_confirm.html +49 -0
- taguette/templates/project_new.html +31 -0
- taguette/templates/tos.html +8 -0
- taguette/templates/trans.js +7 -0
- taguette/templates/welcome.html +15 -0
- taguette/utils.py +177 -0
- taguette/validate.py +141 -0
- taguette/version_check.py +102 -0
- taguette/web/__init__.py +252 -0
- taguette/web/api.py +1835 -0
- taguette/web/base.py +864 -0
- taguette/web/export.py +338 -0
- taguette/web/views.py +1618 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const tag_hierarchy_select = $('#tag-hierarchy-select');
|
|
2
|
+
const tag_hierarchy_merge_select = $('#tag-hierarchy-merge-select');
|
|
3
|
+
const hs_menu_inner = $('.hs-menu-inner');
|
|
4
|
+
const hs_menu_inner_merge = $('.hs-menu-inner');
|
|
5
|
+
|
|
6
|
+
const DEFAULT_LEVEL = 1;
|
|
7
|
+
|
|
8
|
+
// Creates a default 'None' option
|
|
9
|
+
function addDefaultValue(parentContainer) {
|
|
10
|
+
const li = $("<li>");
|
|
11
|
+
const a = $("<a>").addClass("dropdown-item")
|
|
12
|
+
.attr("data-value", '')
|
|
13
|
+
.attr("data-default-selected", '')
|
|
14
|
+
.attr("data-level", DEFAULT_LEVEL)
|
|
15
|
+
.text(gettext('None'));
|
|
16
|
+
li.append(a);
|
|
17
|
+
|
|
18
|
+
parentContainer.append(li);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
// Recursively generates items from tag hierarchy data
|
|
23
|
+
function generateTagHierarchyItem(data, parentContainer, level) {
|
|
24
|
+
|
|
25
|
+
$.each(data, function (index, item) {
|
|
26
|
+
const li = $("<li>");
|
|
27
|
+
const a = $("<a>").addClass("dropdown-item")
|
|
28
|
+
.attr("data-value", item.id)
|
|
29
|
+
.attr("data-level", level)
|
|
30
|
+
.attr("href", "#")
|
|
31
|
+
.text(item.path);
|
|
32
|
+
|
|
33
|
+
li.append(a);
|
|
34
|
+
|
|
35
|
+
parentContainer.append(li);
|
|
36
|
+
|
|
37
|
+
if (item.children && item.children.length > 0) {
|
|
38
|
+
generateTagHierarchyItem(item.children, parentContainer, level + DEFAULT_LEVEL);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Init tag hierarchy select dropdown for create tag
|
|
44
|
+
function initTagHierarchySelect() {
|
|
45
|
+
|
|
46
|
+
initHierarchySelect(hs_menu_inner, tag_hierarchy_select, true);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Init tag hierarchy select dropdown for the merge form
|
|
50
|
+
function initMergeTagHierarchySelect() {
|
|
51
|
+
|
|
52
|
+
initHierarchySelect(hs_menu_inner_merge, tag_hierarchy_merge_select, true);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Sets up a hierarchy select dropdown
|
|
56
|
+
function initHierarchySelect(menu, select, withDefault = true) {
|
|
57
|
+
|
|
58
|
+
menu.html('');
|
|
59
|
+
|
|
60
|
+
if(withDefault) {
|
|
61
|
+
addDefaultValue(menu)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
generateTagHierarchyItem(tags, menu, DEFAULT_LEVEL);
|
|
65
|
+
|
|
66
|
+
select.hierarchySelect({
|
|
67
|
+
width: 'auto'
|
|
68
|
+
});
|
|
69
|
+
}
|
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
const SPLICE_DEFAULT_INDEX = 1;
|
|
2
|
+
const DEFAULT_COUNT = 0;
|
|
3
|
+
|
|
4
|
+
// Find a tag by its ID or path in the tag tree
|
|
5
|
+
const findTag = (id) => {
|
|
6
|
+
// Recursive function to search for a tag
|
|
7
|
+
const recursiveSearch = (tag) => {
|
|
8
|
+
// Check if current tag matches the search criteria
|
|
9
|
+
if (tag.id === parseInt(id) || tag.path === id) {
|
|
10
|
+
return tag;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Search in children if present
|
|
14
|
+
if (tag.children) {
|
|
15
|
+
for (const child of tag.children) {
|
|
16
|
+
const found = recursiveSearch(child);
|
|
17
|
+
if (found) {
|
|
18
|
+
return found;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return null;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Iterate through all top-level tags
|
|
27
|
+
for (const tag of tags) {
|
|
28
|
+
const found = recursiveSearch(tag);
|
|
29
|
+
if (found) {
|
|
30
|
+
return found;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Find the root parent of tag
|
|
38
|
+
function findRootParent(tagId) {
|
|
39
|
+
const tag = findTag(tagId);
|
|
40
|
+
if (tag && tag.parent) {
|
|
41
|
+
return findRootParent(tag.parent);
|
|
42
|
+
}
|
|
43
|
+
return tagId;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function highlightTag(tagId) {
|
|
47
|
+
const nodeId = getNodeIdFromTreeviewByTagId(treeview_tags, tagId);
|
|
48
|
+
const nodeElement = treeview_tags.find(`li[data-nodeid="${nodeId}"]`);
|
|
49
|
+
|
|
50
|
+
nodeElement.addClass('highlight-tag');
|
|
51
|
+
setTimeout(() => {
|
|
52
|
+
nodeElement.addClass('fade-out');
|
|
53
|
+
}, 3000);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
const findTagAndDelete = (id) => {
|
|
58
|
+
|
|
59
|
+
const tag = findTag(parseInt(id));
|
|
60
|
+
|
|
61
|
+
if (tag.parent) {
|
|
62
|
+
const parent = findTag(tag.parent);
|
|
63
|
+
const indexToDelete = parent.children.findIndex(item => item.id === tag.id);
|
|
64
|
+
parent.children.splice(indexToDelete, SPLICE_DEFAULT_INDEX);
|
|
65
|
+
} else {
|
|
66
|
+
const indexToDelete = tags.findIndex(item => item.id === tag.id);
|
|
67
|
+
tags.splice(indexToDelete, SPLICE_DEFAULT_INDEX);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Main function to add or update a tag
|
|
72
|
+
function addTag(tag) {
|
|
73
|
+
// Check if the tag with the same id already exists
|
|
74
|
+
let existingTag = findTag(tag.id);
|
|
75
|
+
const existingTagColor = existingTag?.color ?? '';
|
|
76
|
+
|
|
77
|
+
if (existingTag) {
|
|
78
|
+
updateExistingTag(existingTag, tag);
|
|
79
|
+
} else {
|
|
80
|
+
addNewTag(tag);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
checkColorChangeAndUpdate(existingTagColor, tag.color);
|
|
84
|
+
|
|
85
|
+
if(!createTagFromHighlight) {
|
|
86
|
+
updateTagsListWithCallback(function() {
|
|
87
|
+
highlightTag(tag.id);
|
|
88
|
+
});
|
|
89
|
+
} else {
|
|
90
|
+
updateTagsList();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Function to add a new tag to the tag tree
|
|
96
|
+
function addNewTag(tag) {
|
|
97
|
+
const newTag = {
|
|
98
|
+
...tag,
|
|
99
|
+
count: DEFAULT_COUNT,
|
|
100
|
+
count_document: DEFAULT_COUNT,
|
|
101
|
+
children: []
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
if (tag.parent) {
|
|
105
|
+
const parentTag = findTag(tag.parent);
|
|
106
|
+
if (!parentTag) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
parentTag.children = parentTag.children || [];
|
|
110
|
+
parentTag.children.push(newTag);
|
|
111
|
+
} else {
|
|
112
|
+
tags.push(newTag);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Function to update an existing tag
|
|
117
|
+
function updateExistingTag(existingTag, newTag) {
|
|
118
|
+
if (newTag.parent) {
|
|
119
|
+
handleExistingTagWithParent(existingTag, newTag);
|
|
120
|
+
} else {
|
|
121
|
+
handleExistingTagWithoutParent(existingTag, newTag);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Handle updating an existing a tag that has a parent
|
|
126
|
+
function handleExistingTagWithParent(existingTag, newTag) {
|
|
127
|
+
const newParent = findTag(newTag.parent);
|
|
128
|
+
if (!newParent) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!existingTag.parent) {
|
|
133
|
+
// If the existing tag had no parent, move it to the new parent
|
|
134
|
+
findTagAndDelete(newTag.id);
|
|
135
|
+
moveTagToNewParent(existingTag, newParent);
|
|
136
|
+
} else if (newTag.parent !== existingTag.parent) {
|
|
137
|
+
// If the tag is moving to a different parent
|
|
138
|
+
const oldParent = findTag(existingTag.parent);
|
|
139
|
+
removeTagFromParent(existingTag, oldParent);
|
|
140
|
+
moveTagToNewParent(existingTag, newParent);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Update the existing tag with new properties
|
|
144
|
+
Object.assign(existingTag, newTag);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Handle updating an existing tag that has no parent
|
|
148
|
+
function handleExistingTagWithoutParent(existingTag, newTag) {
|
|
149
|
+
if (existingTag.parent) {
|
|
150
|
+
// If the existing tag had a parent, remove it from the parent and add to top level
|
|
151
|
+
const oldParent = findTag(existingTag.parent);
|
|
152
|
+
removeTagFromParent(existingTag, oldParent);
|
|
153
|
+
tags.push(existingTag);
|
|
154
|
+
}
|
|
155
|
+
// Update the existing tag with new properties
|
|
156
|
+
Object.assign(existingTag, newTag);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Move a tag to a new parent
|
|
160
|
+
function moveTagToNewParent(tag, newParent) {
|
|
161
|
+
newParent.children = newParent.children || [];
|
|
162
|
+
newParent.children.push(tag);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Remove a tag from its parent
|
|
166
|
+
function removeTagFromParent(tag, parent) {
|
|
167
|
+
if (!parent || !parent.children) return;
|
|
168
|
+
const index = parent.children.findIndex(child => child.id === tag.id);
|
|
169
|
+
if (index !== -1) {
|
|
170
|
+
parent.children.splice(index, 1);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function checkColorChangeAndUpdate(oldColor, newColor) {
|
|
175
|
+
if (oldColor !== '' && oldColor !== newColor) {
|
|
176
|
+
updateAndLoadDocument();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function updateAndLoadDocument() {
|
|
181
|
+
const url = window.location.href;
|
|
182
|
+
const documentPattern = /\/document\/(\d+)/;
|
|
183
|
+
const match = url.match(documentPattern);
|
|
184
|
+
|
|
185
|
+
if (match) {
|
|
186
|
+
const doc_id = match[1];
|
|
187
|
+
window.history.pushState({document_id: doc_id}, "Document " + doc_id, url);
|
|
188
|
+
loadDocument(doc_id);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function removeTag(id) {
|
|
193
|
+
const tagToDelete = findTag(id);
|
|
194
|
+
findTagAndDelete(id);
|
|
195
|
+
updateTagsList();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/*
|
|
199
|
+
mergeTags
|
|
200
|
+
*/
|
|
201
|
+
function mergeTags(tag_src, tag_dest) {
|
|
202
|
+
const tagToRemove = findTag(tag_src);
|
|
203
|
+
const tagToUpdate = findTag(tag_dest);
|
|
204
|
+
|
|
205
|
+
if (!tagToRemove || !tagToUpdate) return;
|
|
206
|
+
|
|
207
|
+
tagToUpdate.count = parseInt(tagToUpdate.count) + parseInt(tagToRemove.count);
|
|
208
|
+
|
|
209
|
+
if (tagToRemove.children && tagToRemove.children.length > 0) {
|
|
210
|
+
for (const child of tagToRemove.children) {
|
|
211
|
+
child.parent = tag_dest;
|
|
212
|
+
}
|
|
213
|
+
tagToUpdate.children = tagToUpdate.children || [];
|
|
214
|
+
tagToUpdate.children.push(...tagToRemove.children);
|
|
215
|
+
tagToRemove.children = [];
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
findTagAndDelete(tag_src);
|
|
219
|
+
updateTagsList();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function selectAllTag() {
|
|
223
|
+
expandAllNodes(treeview_tags);
|
|
224
|
+
const allNodes = treeview_tags.treeview('getEnabled');
|
|
225
|
+
for(const node of allNodes) {
|
|
226
|
+
const nodeId = node.nodeId;
|
|
227
|
+
const nodeSelector = $('li[data-nodeid="' + nodeId + '"]');
|
|
228
|
+
nodeSelector.addClass("highlight-tag")
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/*
|
|
233
|
+
updateTagCount
|
|
234
|
+
*/
|
|
235
|
+
function updateTagCount(tagsId) {
|
|
236
|
+
|
|
237
|
+
let updateTagList = [];
|
|
238
|
+
for (const key in tagsId) {
|
|
239
|
+
const tagId = parseInt(key);
|
|
240
|
+
const tagData = tagsId[key];
|
|
241
|
+
const tagToUpdate = findTag(tagId);
|
|
242
|
+
updateTagList.push(tagToUpdate);
|
|
243
|
+
|
|
244
|
+
if (tagToUpdate) {
|
|
245
|
+
tagToUpdate.count = tagData.highlights_count;
|
|
246
|
+
tagToUpdate.count_document = tagData.documents_count;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
initTagsTreeview();
|
|
251
|
+
|
|
252
|
+
for (const tag of updateTagList) {
|
|
253
|
+
if(!tag.parent) {
|
|
254
|
+
highlightTag(tag.id);
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
const parentRoot = findRootParent(tag.id);
|
|
258
|
+
expandNodeParent(parentRoot);
|
|
259
|
+
highlightTag(tag.id);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function updateTagsList() {
|
|
264
|
+
const isReverse = sortTags[1] === 'desc';
|
|
265
|
+
sortTagsByKey(tags, sortTags[0], isReverse);
|
|
266
|
+
let selectedHighLightTags = [];
|
|
267
|
+
|
|
268
|
+
if(createTagFromHighlight) {
|
|
269
|
+
selectedHighLightTags = getAllSelectedNodes(treeview_tags_highlight).map(node => node.id)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
initTagsTreeview();
|
|
273
|
+
initTagsHighlightTreeview();
|
|
274
|
+
initTagsExploreTreeview();
|
|
275
|
+
initTagHierarchySelect();
|
|
276
|
+
initMergeTagHierarchySelect();
|
|
277
|
+
hideSearchErrorMessage();
|
|
278
|
+
|
|
279
|
+
if(createTagFromHighlight) {
|
|
280
|
+
selectedHighLightTags.forEach(id => {
|
|
281
|
+
const nodeId = getNodeIdFromTreeviewByTagId(treeview_tags_highlight, id)
|
|
282
|
+
selectNode(treeview_tags_highlight, nodeId);
|
|
283
|
+
});
|
|
284
|
+
const nodeId = getNodeIdFromTreeviewByTagId(treeview_tags_highlight, last_added_tag)
|
|
285
|
+
selectNode(treeview_tags_highlight, nodeId);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function updateTagsListWithCallback(callback) {
|
|
290
|
+
const isReverse = sortTags[1] === 'desc';
|
|
291
|
+
sortTagsByKey(tags, sortTags[0], isReverse);
|
|
292
|
+
|
|
293
|
+
initTagsTreeview(null, callback);
|
|
294
|
+
initTagsHighlightTreeview();
|
|
295
|
+
initTagsExploreTreeview();
|
|
296
|
+
initTagHierarchySelect();
|
|
297
|
+
initMergeTagHierarchySelect();
|
|
298
|
+
hideSearchErrorMessage();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function sortTagsByKey(array, key, reverse) {
|
|
302
|
+
const orderByX = reverse ? -1 : 1;
|
|
303
|
+
|
|
304
|
+
array.sort((a, b) => {
|
|
305
|
+
const keyA = typeof key === 'function' ? key(a) : a[key];
|
|
306
|
+
const keyB = typeof key === 'function' ? key(b) : b[key];
|
|
307
|
+
|
|
308
|
+
if (keyA < keyB) {
|
|
309
|
+
return -1 * orderByX;
|
|
310
|
+
} else if (keyA > keyB) {
|
|
311
|
+
return 1 * orderByX;
|
|
312
|
+
} else {
|
|
313
|
+
return 0;
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// sort each level of "children"
|
|
318
|
+
array.forEach(item => {
|
|
319
|
+
if (item.children && item.children.length > 0) {
|
|
320
|
+
sortTagsByKey(item.children, key, reverse);
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function handleSearch(filterText) {
|
|
326
|
+
function searchInChildren(item) {
|
|
327
|
+
const filteredChildren = [];
|
|
328
|
+
|
|
329
|
+
item.children.forEach(child => {
|
|
330
|
+
const childMatches = child.path.toLowerCase().includes(filterText);
|
|
331
|
+
const matchingChildren = searchInChildren(child);
|
|
332
|
+
|
|
333
|
+
if (childMatches || matchingChildren.length > 0) {
|
|
334
|
+
filteredChildren.push({
|
|
335
|
+
...child,
|
|
336
|
+
children: matchingChildren
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
return filteredChildren;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return tags
|
|
345
|
+
.map(item => {
|
|
346
|
+
const matches = item.path.toLowerCase().includes(filterText);
|
|
347
|
+
const filteredChildren = searchInChildren(item);
|
|
348
|
+
|
|
349
|
+
if (matches || filteredChildren.length > 0) {
|
|
350
|
+
return {
|
|
351
|
+
...item,
|
|
352
|
+
children: filteredChildren
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return null;
|
|
357
|
+
})
|
|
358
|
+
.filter(item => item !== null);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function submitTagCreation(tag, callbackSuccess, callbackFail) {
|
|
362
|
+
|
|
363
|
+
const { id } = tag;
|
|
364
|
+
let newTag = { ...tag };
|
|
365
|
+
delete newTag.id;
|
|
366
|
+
|
|
367
|
+
let request;
|
|
368
|
+
if (id) {
|
|
369
|
+
request = postJSON(`/api/project/${project_id}/tag/${id}`, newTag);
|
|
370
|
+
} else {
|
|
371
|
+
request = postJSON(`/api/project/${project_id}/tag/new`, newTag);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
showSpinner();
|
|
375
|
+
request.then(function (reply) {
|
|
376
|
+
callbackSuccess(reply);
|
|
377
|
+
}).catch(function (error) {
|
|
378
|
+
callbackFail(error);
|
|
379
|
+
}).finally(hideSpinner);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function submitTagDelete(tag_id, callbackSuccess, callbackFail) {
|
|
383
|
+
|
|
384
|
+
const request = deleteURL(`/api/project/${project_id}/tag/${tag_id}`)
|
|
385
|
+
|
|
386
|
+
request.then(function (result) {
|
|
387
|
+
callbackSuccess(result);
|
|
388
|
+
}).catch(function (error) {
|
|
389
|
+
callbackFail(error);
|
|
390
|
+
}).finally(hideSpinner);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function submitTagMerge(tag_src, tag_dest, callbackSuccess, callbackFail) {
|
|
394
|
+
|
|
395
|
+
const request = postJSON(`/api/project/${project_id}/tag/merge`,
|
|
396
|
+
{src: tag_src, dest: tag_dest})
|
|
397
|
+
|
|
398
|
+
showSpinner();
|
|
399
|
+
request.then(function (result) {
|
|
400
|
+
callbackSuccess(result);
|
|
401
|
+
}).catch(function (error) {
|
|
402
|
+
callbackFail(error);
|
|
403
|
+
}).finally(hideSpinner);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// update tag parent
|
|
407
|
+
function updateTagParent(child_tag_1, new_parent_tag_id) {
|
|
408
|
+
const tag = findTag(child_tag_1);
|
|
409
|
+
if(tag.parent === new_parent_tag_id) return;
|
|
410
|
+
const new_tag = {
|
|
411
|
+
id: tag.id,
|
|
412
|
+
path: tag.path,
|
|
413
|
+
description: tag.description,
|
|
414
|
+
color: tag.color,
|
|
415
|
+
parent_id: new_parent_tag_id
|
|
416
|
+
};
|
|
417
|
+
submitTagCreation(new_tag, () => {}, () => {});
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// update tag parent
|
|
421
|
+
function removeTagParent(tagId) {
|
|
422
|
+
const tag = findTag(tagId);
|
|
423
|
+
tag.parent_id = null;
|
|
424
|
+
submitTagCreation(tag,
|
|
425
|
+
() => {},
|
|
426
|
+
() => {}
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function generateTagBadgeHtml(tag, index, condition) {
|
|
431
|
+
const abbreviation = abbreviateTag(tag.path);
|
|
432
|
+
let conditionTextHtml = "";
|
|
433
|
+
if (index > 0) {
|
|
434
|
+
conditionTextHtml = getConditionText(condition);
|
|
435
|
+
}
|
|
436
|
+
return `${conditionTextHtml}<span title="${tag.path}" class="tag-badge" style="background-color: ${tag.color};">
|
|
437
|
+
${abbreviation}
|
|
438
|
+
<button onclick="removeTagBadge(${tag.id})" class="tag-badge-button">x</button>
|
|
439
|
+
</span>`;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function generateTagBadgeForHighlight(tag) {
|
|
443
|
+
const abbreviation = abbreviateTag(tag.path);
|
|
444
|
+
return `<span class="tag-badge me-2 my-2" title="${tag.path}" style="background-color: ${tag.color};">
|
|
445
|
+
${abbreviation}
|
|
446
|
+
<button onclick="removeHighLightSelectedTagBadge(${tag.id})" class="tag-badge-button">x</button>
|
|
447
|
+
</span>`;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function getConditionText(condition) {
|
|
451
|
+
switch (condition) {
|
|
452
|
+
case 'and':
|
|
453
|
+
return `<span style="margin: 5px;">et</span>`;
|
|
454
|
+
case 'or':
|
|
455
|
+
return `<span style="margin: 5px;">ou</span>`;
|
|
456
|
+
case 'not':
|
|
457
|
+
return `<span style="margin: 5px;">sans</span>`;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function showTagSelection() {
|
|
462
|
+
const tagsCriteria = $('#tag-selection-criteria-area');
|
|
463
|
+
|
|
464
|
+
let content = '';
|
|
465
|
+
if (selectedTags.length > 1) {
|
|
466
|
+
const yourSelectionLabel = gettext("Your selection");
|
|
467
|
+
const clearSelectionLabel = gettext("Clear selection");
|
|
468
|
+
content += `<div class="d-flex justify-content-between">`;
|
|
469
|
+
content += `<span style='font-size: 0.9rem'>${yourSelectionLabel} :</span>`;
|
|
470
|
+
content += `<span id="clear-selection" onclick="emptyTagSelection(true)" style="cursor:pointer;">` +
|
|
471
|
+
`<i class="fa fa-times" aria-hidden="true" title="${clearSelectionLabel}"></i></span>`;
|
|
472
|
+
content += `</div>`;
|
|
473
|
+
|
|
474
|
+
tagsCriteria.show();
|
|
475
|
+
selectedTags.forEach((item, index) => {
|
|
476
|
+
const tag = findTag(item.id);
|
|
477
|
+
content += generateTagBadgeHtml(tag, index, item.condition);
|
|
478
|
+
});
|
|
479
|
+
} else if (typeof appliedExploreTags !== 'undefined' && appliedExploreTags.length) {
|
|
480
|
+
showExploreQuerySummary(appliedExploreTags);
|
|
481
|
+
return;
|
|
482
|
+
} else {
|
|
483
|
+
tagsCriteria.hide();
|
|
484
|
+
}
|
|
485
|
+
tagsCriteria.html(content);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function showHighlightTagSelection() {
|
|
489
|
+
|
|
490
|
+
const HighlightSelectedTagsArea = $('#highlight-selected-tag-area');
|
|
491
|
+
|
|
492
|
+
let content = '';
|
|
493
|
+
|
|
494
|
+
if (highlightSelectedTags.length > 0) {
|
|
495
|
+
const yourSelectionLabel = gettext("Your selection");
|
|
496
|
+
content += `<div class="d-flex justify-content-between">`;
|
|
497
|
+
content += `<span style='font-size: 0.9rem'>${yourSelectionLabel} :</span>`;
|
|
498
|
+
content += `</div>`;
|
|
499
|
+
|
|
500
|
+
HighlightSelectedTagsArea.show();
|
|
501
|
+
highlightSelectedTags.forEach((item, index) => {
|
|
502
|
+
const tag = findTag(item);
|
|
503
|
+
content += generateTagBadgeForHighlight(tag, index, item.condition);
|
|
504
|
+
});
|
|
505
|
+
} else {
|
|
506
|
+
HighlightSelectedTagsArea.hide();
|
|
507
|
+
}
|
|
508
|
+
HighlightSelectedTagsArea.html(content);
|
|
509
|
+
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function emptyTagSelection(unSelectNode = false, collapse = true) {
|
|
513
|
+
selectedTags = [];
|
|
514
|
+
showTagSelection();
|
|
515
|
+
hideTagReturnLink();
|
|
516
|
+
if (unSelectNode) {
|
|
517
|
+
deselectAllNodes(treeview_tags);
|
|
518
|
+
isExplorationMode = false;
|
|
519
|
+
initTagsTreeview();
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function hideTagReturnLink() {
|
|
524
|
+
const tags_return = $('#tag-selection-return'),
|
|
525
|
+
tags_return_link = $('a', tags_return);
|
|
526
|
+
tags_return_link.hide();
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function removeTagBadge(tagId) {
|
|
530
|
+
selectedTags = selectedTags.filter(tag => tag.id !== tagId);
|
|
531
|
+
showTagSelection();
|
|
532
|
+
const nodeId = getNodeIdFromTreeviewByTagId(treeview_tags, tagId);
|
|
533
|
+
unselectNode(treeview_tags, nodeId);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function removeHighLightSelectedTagBadge(tagId) {
|
|
537
|
+
highlightSelectedTags = highlightSelectedTags.filter(item => item !== tagId);
|
|
538
|
+
showHighlightTagSelection();
|
|
539
|
+
const nodeId = getNodeIdFromTreeviewByTagId(treeview_tags_highlight, tagId);
|
|
540
|
+
unselectNode(treeview_tags_highlight, nodeId);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function abbreviateTag(tagPath) {
|
|
544
|
+
return tagPath;
|
|
545
|
+
}
|