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,127 @@
|
|
|
1
|
+
"""add document direction
|
|
2
|
+
|
|
3
|
+
Revision ID: d86c45d7aa8a
|
|
4
|
+
Revises: 43d6c240309d
|
|
5
|
+
Create Date: 2021-06-09 13:17:10.397016
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
import json
|
|
10
|
+
import sqlalchemy as sa
|
|
11
|
+
from sqlalchemy.dialects import postgresql
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# revision identifiers, used by Alembic.
|
|
15
|
+
revision = 'd86c45d7aa8a'
|
|
16
|
+
down_revision = '43d6c240309d'
|
|
17
|
+
branch_labels = None
|
|
18
|
+
depends_on = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
meta = sa.MetaData(naming_convention={
|
|
22
|
+
"ix": "ix_%(column_0_label)s",
|
|
23
|
+
"uq": "uq_%(table_name)s_%(column_0_name)s",
|
|
24
|
+
"ck": "ck_%(table_name)s_%(constraint_name)s",
|
|
25
|
+
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
|
|
26
|
+
"pk": "pk_%(table_name)s",
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
commands = sa.Table(
|
|
30
|
+
'commands',
|
|
31
|
+
meta,
|
|
32
|
+
sa.Column('id', sa.Integer, primary_key=True),
|
|
33
|
+
sa.Column('date', sa.DateTime, nullable=False),
|
|
34
|
+
sa.Column('user_login', sa.String(30), nullable=False),
|
|
35
|
+
sa.Column('project_id', sa.Integer, nullable=False),
|
|
36
|
+
sa.Column('document_id', sa.Integer, nullable=True),
|
|
37
|
+
sa.Column('payload', sa.Text, nullable=False),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _do_map(map_func):
|
|
42
|
+
"""Map JSON payloads through the given function.
|
|
43
|
+
"""
|
|
44
|
+
connection = op.get_bind()
|
|
45
|
+
assert connection is not None
|
|
46
|
+
|
|
47
|
+
payloads = connection.execute('''\
|
|
48
|
+
SELECT id, payload FROM commands;
|
|
49
|
+
''')
|
|
50
|
+
update = []
|
|
51
|
+
for id, payload in payloads:
|
|
52
|
+
payload = json.loads(payload)
|
|
53
|
+
payload = map_func(payload)
|
|
54
|
+
if payload is not None:
|
|
55
|
+
payload = json.dumps(payload, sort_keys=True)
|
|
56
|
+
update.append({'cmd_id': id, 'cmd_payload': payload})
|
|
57
|
+
|
|
58
|
+
if update:
|
|
59
|
+
with connection.begin():
|
|
60
|
+
connection.execute(
|
|
61
|
+
(
|
|
62
|
+
commands.update()
|
|
63
|
+
.where(commands.c.id == sa.bindparam('cmd_id'))
|
|
64
|
+
.values(payload=sa.bindparam('cmd_payload'))
|
|
65
|
+
),
|
|
66
|
+
update,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def upgrade():
|
|
71
|
+
# Add textdirection enum type
|
|
72
|
+
connection = op.get_bind()
|
|
73
|
+
if connection.dialect.name == 'postgresql':
|
|
74
|
+
direction_enum = postgresql.ENUM(
|
|
75
|
+
'LEFT_TO_RIGHT', 'RIGHT_TO_LEFT',
|
|
76
|
+
name='textdirection',
|
|
77
|
+
create_type=False,
|
|
78
|
+
)
|
|
79
|
+
direction_enum.create(connection)
|
|
80
|
+
else:
|
|
81
|
+
direction_enum = sa.Enum(
|
|
82
|
+
'LEFT_TO_RIGHT', 'RIGHT_TO_LEFT',
|
|
83
|
+
name='textdirection',
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Add column to 'documents'
|
|
87
|
+
op.add_column(
|
|
88
|
+
'documents',
|
|
89
|
+
sa.Column(
|
|
90
|
+
'text_direction',
|
|
91
|
+
direction_enum,
|
|
92
|
+
nullable=True,
|
|
93
|
+
),
|
|
94
|
+
)
|
|
95
|
+
op.execute(
|
|
96
|
+
'''\
|
|
97
|
+
UPDATE documents SET text_direction = 'LEFT_TO_RIGHT';
|
|
98
|
+
''',
|
|
99
|
+
)
|
|
100
|
+
with op.batch_alter_table('documents', schema=None) as batch_op:
|
|
101
|
+
batch_op.alter_column('text_direction', nullable=False)
|
|
102
|
+
|
|
103
|
+
# Add field to commands
|
|
104
|
+
def add_field(payload):
|
|
105
|
+
if payload['type'] == 'document_add':
|
|
106
|
+
payload['text_direction'] = 'LEFT_TO_RIGHT'
|
|
107
|
+
return payload
|
|
108
|
+
|
|
109
|
+
_do_map(add_field)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def downgrade():
|
|
113
|
+
# Remove field from commands
|
|
114
|
+
def remove_field(payload):
|
|
115
|
+
payload.pop('text_direction', None)
|
|
116
|
+
return payload
|
|
117
|
+
|
|
118
|
+
_do_map(remove_field)
|
|
119
|
+
|
|
120
|
+
# Remove column from 'documents'
|
|
121
|
+
with op.batch_alter_table('documents', schema=None) as batch_op:
|
|
122
|
+
batch_op.drop_column('text_direction')
|
|
123
|
+
|
|
124
|
+
# Remove textdirection enum type
|
|
125
|
+
connection = op.get_bind()
|
|
126
|
+
if connection.dialect.name == 'postgresql':
|
|
127
|
+
op.execute("DROP TYPE textdirection;")
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""remove blank highlights
|
|
2
|
+
|
|
3
|
+
Revision ID: db5e31a0233d
|
|
4
|
+
Revises: 382db36f20f4
|
|
5
|
+
Create Date: 2022-11-18 15:03:03.154401
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# revision identifiers, used by Alembic.
|
|
14
|
+
revision = 'db5e31a0233d'
|
|
15
|
+
down_revision = '382db36f20f4'
|
|
16
|
+
branch_labels = None
|
|
17
|
+
depends_on = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
meta = sa.MetaData(naming_convention={
|
|
21
|
+
"ix": "ix_%(column_0_label)s",
|
|
22
|
+
"uq": "uq_%(table_name)s_%(column_0_name)s",
|
|
23
|
+
"ck": "ck_%(table_name)s_%(constraint_name)s",
|
|
24
|
+
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
|
|
25
|
+
"pk": "pk_%(table_name)s",
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
highlights = sa.Table(
|
|
29
|
+
'highlights',
|
|
30
|
+
meta,
|
|
31
|
+
sa.Column('id', sa.Integer, primary_key=True),
|
|
32
|
+
sa.Column('document_id', sa.Integer, nullable=False),
|
|
33
|
+
sa.Column('start_offset', sa.Integer, nullable=False),
|
|
34
|
+
sa.Column('end_offset', sa.Integer, nullable=False),
|
|
35
|
+
sa.Column(
|
|
36
|
+
'snippet', sa.Text,
|
|
37
|
+
sa.ForeignKey('documents.id', ondelete='CASCADE'),
|
|
38
|
+
nullable=False,
|
|
39
|
+
),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
highlight_tags = sa.Table(
|
|
43
|
+
'highlight_tags',
|
|
44
|
+
meta,
|
|
45
|
+
sa.Column(
|
|
46
|
+
'highlight_id', sa.Integer,
|
|
47
|
+
sa.ForeignKey('highlights.id', ondelete='CASCADE'),
|
|
48
|
+
primary_key=True,
|
|
49
|
+
),
|
|
50
|
+
sa.Column(
|
|
51
|
+
'tag_id', sa.Integer,
|
|
52
|
+
sa.ForeignKey('tags.id', ondelete='CASCADE'),
|
|
53
|
+
primary_key=True,
|
|
54
|
+
),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def upgrade():
|
|
59
|
+
bind = op.get_bind()
|
|
60
|
+
|
|
61
|
+
# Find blank highlights
|
|
62
|
+
rows = bind.execute(
|
|
63
|
+
sa.select([highlights.c.id, highlights.c.snippet])
|
|
64
|
+
.select_from(highlights)
|
|
65
|
+
)
|
|
66
|
+
to_delete = []
|
|
67
|
+
for id, snippet in rows:
|
|
68
|
+
if all(c in '\r\n\t' for c in snippet):
|
|
69
|
+
to_delete.append(id)
|
|
70
|
+
|
|
71
|
+
# Delete them
|
|
72
|
+
if to_delete:
|
|
73
|
+
print(
|
|
74
|
+
"%d blank highlights to delete" % len(to_delete),
|
|
75
|
+
file=sys.stderr,
|
|
76
|
+
)
|
|
77
|
+
bind.execute(
|
|
78
|
+
highlight_tags.delete()
|
|
79
|
+
.where(highlight_tags.c.highlight_id.in_(to_delete))
|
|
80
|
+
)
|
|
81
|
+
bind.execute(
|
|
82
|
+
highlights.delete()
|
|
83
|
+
.where(highlights.c.id.in_(to_delete))
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def downgrade():
|
|
88
|
+
pass
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""unique_tags
|
|
2
|
+
|
|
3
|
+
Revision ID: dd37d9027a24
|
|
4
|
+
Revises: e4e090a5b511
|
|
5
|
+
Create Date: 2018-11-10 21:45:13.844011
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
from sqlalchemy.orm import Session
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = 'dd37d9027a24'
|
|
14
|
+
down_revision = 'e4e090a5b511'
|
|
15
|
+
branch_labels = None
|
|
16
|
+
depends_on = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade():
|
|
20
|
+
# Change the paths of tags that collide, before adding the unique index
|
|
21
|
+
bind = op.get_bind()
|
|
22
|
+
session = Session(bind=bind)
|
|
23
|
+
# https://stackoverflow.com/a/7416624
|
|
24
|
+
session.execute(
|
|
25
|
+
'''\
|
|
26
|
+
UPDATE tags
|
|
27
|
+
SET path = path || ' ' || id
|
|
28
|
+
WHERE
|
|
29
|
+
id NOT IN (
|
|
30
|
+
SELECT MIN(t3.id)
|
|
31
|
+
FROM tags t3
|
|
32
|
+
GROUP BY project_id, path
|
|
33
|
+
HAVING count(t3.id) > 1
|
|
34
|
+
) AND id IN (
|
|
35
|
+
SELECT t1.id
|
|
36
|
+
FROM tags AS t1
|
|
37
|
+
INNER JOIN tags AS t2
|
|
38
|
+
ON t1.path = t2.path AND
|
|
39
|
+
t1.project_id = t2.project_id AND
|
|
40
|
+
t1.id <> t2.id
|
|
41
|
+
);
|
|
42
|
+
''',
|
|
43
|
+
)
|
|
44
|
+
session.commit()
|
|
45
|
+
|
|
46
|
+
# Add the indexes
|
|
47
|
+
with op.batch_alter_table('tags') as batch_op:
|
|
48
|
+
batch_op.create_index(
|
|
49
|
+
batch_op.f('ix_tags_path'),
|
|
50
|
+
['path'],
|
|
51
|
+
unique=True,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# We assume no one created those, table is unused so far
|
|
55
|
+
with op.batch_alter_table('groups') as batch_op:
|
|
56
|
+
batch_op.create_index(
|
|
57
|
+
batch_op.f('ix_groups_path'),
|
|
58
|
+
['path'],
|
|
59
|
+
unique=True,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def downgrade():
|
|
64
|
+
with op.batch_alter_table('groups') as batch_op:
|
|
65
|
+
batch_op.drop_index(batch_op.f('ix_groups_path'))
|
|
66
|
+
|
|
67
|
+
with op.batch_alter_table('tags') as batch_op:
|
|
68
|
+
batch_op.drop_index(batch_op.f('ix_tags_path'))
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Add project_id to collections and relation to Project
|
|
2
|
+
|
|
3
|
+
Revision ID: de678cef2a94
|
|
4
|
+
Revises: 955db2e523d1
|
|
5
|
+
Create Date: 2025-07-08 14:22:33.272231
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = 'de678cef2a94'
|
|
14
|
+
down_revision = '955db2e523d1'
|
|
15
|
+
branch_labels = None
|
|
16
|
+
depends_on = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade():
|
|
20
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
21
|
+
with op.batch_alter_table('collections', schema=None) as batch_op:
|
|
22
|
+
batch_op.add_column(
|
|
23
|
+
sa.Column('project_id', sa.Integer(), nullable=False))
|
|
24
|
+
batch_op.create_index(batch_op.f('ix_collections_project_id'), [
|
|
25
|
+
'project_id'], unique=False)
|
|
26
|
+
batch_op.create_foreign_key(
|
|
27
|
+
batch_op.f('fk_collections_project_id_projects'),
|
|
28
|
+
'projects', ['project_id'], ['id'], ondelete='CASCADE')
|
|
29
|
+
|
|
30
|
+
# ### end Alembic commands ###
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def downgrade():
|
|
34
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
35
|
+
with op.batch_alter_table('collections', schema=None) as batch_op:
|
|
36
|
+
batch_op.drop_constraint(batch_op.f(
|
|
37
|
+
'fk_collections_project_id_projects'), type_='foreignkey')
|
|
38
|
+
batch_op.drop_index(batch_op.f('ix_collections_project_id'))
|
|
39
|
+
batch_op.drop_column('project_id')
|
|
40
|
+
|
|
41
|
+
# ### end Alembic commands ###
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""add table edugainidps
|
|
2
|
+
|
|
3
|
+
Revision ID: e459991d960e
|
|
4
|
+
Revises: 4c55b3b3ff30
|
|
5
|
+
Create Date: 2024-03-11 10:16:16.804208
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = 'e459991d960e'
|
|
14
|
+
down_revision = '4c55b3b3ff30'
|
|
15
|
+
branch_labels = None
|
|
16
|
+
depends_on = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade():
|
|
20
|
+
op.create_table(
|
|
21
|
+
'edigainidps',
|
|
22
|
+
sa.Column('id', sa.Integer(), nullable=False),
|
|
23
|
+
sa.Column('entityid', sa.String(length=255), nullable=True),
|
|
24
|
+
sa.Column('e_displayname', sa.String(length=255), nullable=True),
|
|
25
|
+
sa.Column('regauth', sa.String(length=255), nullable=True),
|
|
26
|
+
sa.Column('entity_cat', sa.String(length=255), nullable=True),
|
|
27
|
+
sa.Column('eccs_status', sa.Integer(), nullable=True),
|
|
28
|
+
sa.Column('validator_status', sa.BigInteger(), nullable=True),
|
|
29
|
+
sa.Column('coco_status', sa.String(length=255), nullable=True),
|
|
30
|
+
sa.Column('coco_id', sa.Integer(), nullable=True),
|
|
31
|
+
sa.Column('sirtfi_status', sa.Integer(), nullable=True),
|
|
32
|
+
sa.Column('code', sa.String(length=255), nullable=True),
|
|
33
|
+
sa.Column('scopes', sa.Text(), nullable=True),
|
|
34
|
+
sa.PrimaryKeyConstraint('id', name=op.f('pk_edigainidps'))
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def downgrade():
|
|
39
|
+
op.drop_table('edigainidps')
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""add index
|
|
2
|
+
|
|
3
|
+
Revision ID: e4cf92942271
|
|
4
|
+
Revises: 491de2dc7cd7
|
|
5
|
+
Create Date: 2021-11-18 10:38:47.124630
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# revision identifiers, used by Alembic.
|
|
12
|
+
revision = 'e4cf92942271'
|
|
13
|
+
down_revision = '491de2dc7cd7'
|
|
14
|
+
branch_labels = None
|
|
15
|
+
depends_on = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def upgrade():
|
|
19
|
+
with op.batch_alter_table('commands', schema=None) as batch_op:
|
|
20
|
+
batch_op.create_index(
|
|
21
|
+
'idx_project_id',
|
|
22
|
+
['project_id', 'id'],
|
|
23
|
+
unique=False,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def downgrade():
|
|
28
|
+
with op.batch_alter_table('commands', schema=None) as batch_op:
|
|
29
|
+
batch_op.drop_index('idx_project_id')
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""remove commands.document_id fk
|
|
2
|
+
|
|
3
|
+
Revision ID: e4e090a5b511
|
|
4
|
+
Revises:
|
|
5
|
+
Create Date: 2018-10-28 22:54:11.833009
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# revision identifiers, used by Alembic.
|
|
12
|
+
revision = 'e4e090a5b511'
|
|
13
|
+
down_revision = None
|
|
14
|
+
branch_labels = None
|
|
15
|
+
depends_on = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def upgrade():
|
|
19
|
+
naming_convention = {
|
|
20
|
+
"fk":
|
|
21
|
+
"fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
|
|
22
|
+
}
|
|
23
|
+
with op.batch_alter_table(
|
|
24
|
+
'commands',
|
|
25
|
+
naming_convention=naming_convention,
|
|
26
|
+
) as batch_op:
|
|
27
|
+
batch_op.drop_constraint(
|
|
28
|
+
'fk_commands_document_id_documents',
|
|
29
|
+
type_='foreignkey',
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def downgrade():
|
|
34
|
+
with op.batch_alter_table('commands') as batch_op:
|
|
35
|
+
batch_op.create_foreign_key(
|
|
36
|
+
None,
|
|
37
|
+
'documents', ['document_id'], ['id'],
|
|
38
|
+
)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Add favorite column to document
|
|
2
|
+
|
|
3
|
+
Revision ID: e9a9f1c77971
|
|
4
|
+
Revises: 7f56de97e3c8
|
|
5
|
+
Create Date: 2024-04-08 19:29:37.036133
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = 'e9a9f1c77971'
|
|
14
|
+
down_revision = '7f56de97e3c8'
|
|
15
|
+
branch_labels = None
|
|
16
|
+
depends_on = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade():
|
|
20
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
21
|
+
with op.batch_alter_table('documents', schema=None) as batch_op:
|
|
22
|
+
batch_op.add_column(sa.Column('favorite', sa.Boolean(), nullable=True))
|
|
23
|
+
|
|
24
|
+
# ### end Alembic commands ###
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def downgrade():
|
|
28
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
29
|
+
with op.batch_alter_table('documents', schema=None) as batch_op:
|
|
30
|
+
batch_op.drop_column('favorite')
|
|
31
|
+
|
|
32
|
+
# ### end Alembic commands ###
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Don't use server_default=now(), timezone issues
|
|
2
|
+
|
|
3
|
+
Revision ID: ecb4065de575
|
|
4
|
+
Revises: 807c0cc0ecf6
|
|
5
|
+
Create Date: 2021-02-27 11:12:36.221364
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
from sqlalchemy.sql import functions
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = 'ecb4065de575'
|
|
14
|
+
down_revision = '807c0cc0ecf6'
|
|
15
|
+
branch_labels = None
|
|
16
|
+
depends_on = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade():
|
|
20
|
+
with op.batch_alter_table('users') as batch_op:
|
|
21
|
+
batch_op.alter_column('created', server_default=None)
|
|
22
|
+
|
|
23
|
+
with op.batch_alter_table('projects') as batch_op:
|
|
24
|
+
batch_op.alter_column('created', server_default=None)
|
|
25
|
+
|
|
26
|
+
with op.batch_alter_table('documents') as batch_op:
|
|
27
|
+
batch_op.alter_column('created', server_default=None)
|
|
28
|
+
|
|
29
|
+
with op.batch_alter_table('commands') as batch_op:
|
|
30
|
+
batch_op.alter_column('date', server_default=None)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def downgrade():
|
|
34
|
+
with op.batch_alter_table('users') as batch_op:
|
|
35
|
+
batch_op.alter_column('created', server_default=functions.now())
|
|
36
|
+
|
|
37
|
+
with op.batch_alter_table('projects') as batch_op:
|
|
38
|
+
batch_op.alter_column('created', server_default=functions.now())
|
|
39
|
+
|
|
40
|
+
with op.batch_alter_table('documents') as batch_op:
|
|
41
|
+
batch_op.alter_column('created', server_default=functions.now())
|
|
42
|
+
|
|
43
|
+
with op.batch_alter_table('commands') as batch_op:
|
|
44
|
+
batch_op.alter_column('date', server_default=functions.now())
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""edugain : fix field type
|
|
2
|
+
|
|
3
|
+
Revision ID: fbdc29566f62
|
|
4
|
+
Revises: e459991d960e
|
|
5
|
+
Create Date: 2024-03-26 11:25:38.289515
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
import logging
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
# revision identifiers, used by Alembic.
|
|
15
|
+
revision = 'fbdc29566f62'
|
|
16
|
+
down_revision = 'e459991d960e'
|
|
17
|
+
branch_labels = None
|
|
18
|
+
depends_on = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def upgrade():
|
|
22
|
+
db_url = op.get_context().config.get_section('alembic')['sqlalchemy.url']
|
|
23
|
+
if db_url.startswith('sqlite:'):
|
|
24
|
+
# SQLite doesn't mind field length
|
|
25
|
+
logger.info(f"Skipping upgrade {revision} for SQLite")
|
|
26
|
+
else:
|
|
27
|
+
op.alter_column('edigainidps', 'scopes',
|
|
28
|
+
existing_type=sa.VARCHAR(length=255),
|
|
29
|
+
type_=sa.Text(),
|
|
30
|
+
existing_nullable=True)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def downgrade():
|
|
34
|
+
db_url = op.get_context().config.get_section('alembic')['sqlalchemy.url']
|
|
35
|
+
if db_url.startswith('sqlite:'):
|
|
36
|
+
# SQLite doesn't mind field length
|
|
37
|
+
logger.info(f"Skipping downgrade {down_revision} for SQLite")
|
|
38
|
+
else:
|
|
39
|
+
op.alter_column('edigainidps', 'scopes',
|
|
40
|
+
existing_type=sa.Text(),
|
|
41
|
+
type_=sa.VARCHAR(length=255),
|
|
42
|
+
existing_nullable=True)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Add parent_id column to tags table
|
|
2
|
+
|
|
3
|
+
Revision ID: fdc6eb54b034
|
|
4
|
+
Revises: db5e31a0233d
|
|
5
|
+
Create Date: 2024-01-09 13:46:17.464940
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = 'fdc6eb54b034'
|
|
14
|
+
down_revision = 'db5e31a0233d'
|
|
15
|
+
branch_labels = None
|
|
16
|
+
depends_on = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade():
|
|
20
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
21
|
+
with op.batch_alter_table('tags', schema=None) as batch_op:
|
|
22
|
+
batch_op.add_column(sa.Column('parent_id', sa.Integer(),
|
|
23
|
+
nullable=True))
|
|
24
|
+
batch_op.create_index(batch_op.f('ix_tags_parent_id'),
|
|
25
|
+
['parent_id'],
|
|
26
|
+
unique=False)
|
|
27
|
+
batch_op.create_foreign_key(batch_op.f('fk_tags_parent_id_tags'),
|
|
28
|
+
'tags', ['parent_id'], ['id'],
|
|
29
|
+
ondelete='CASCADE')
|
|
30
|
+
|
|
31
|
+
# ### end Alembic commands ###
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Customizing styles
|
|
2
|
+
LibreQDA uses SCSS instead of plain CSS for better modularity and theme management.
|
|
3
|
+
|
|
4
|
+
## Theme creation
|
|
5
|
+
Themes are stored under `taguette/static/scss/themes/<theme_name>.
|
|
6
|
+
|
|
7
|
+
You can copy the `default` or `example` theme and customize the main variables in `_variables.scss`. They are used throughout the partials in `components/` and `layout/`.
|
|
8
|
+
|
|
9
|
+
Note: do not edit `themed.css` directly — it’s overwritten on each build.
|
|
10
|
+
|
|
11
|
+
## Compilation
|
|
12
|
+
To compile a theme, run:
|
|
13
|
+
|
|
14
|
+
``` console
|
|
15
|
+
poetry run make-theme <theme_name>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Customizing the logo
|
|
19
|
+
|
|
20
|
+
Default LibreQDA logos are stored at the following path: `taguette/static/`.
|
|
21
|
+
|
|
22
|
+
To customize:
|
|
23
|
+
|
|
24
|
+
1. Add a new logo image under `taguette/static/assets/<theme_name>/`.
|
|
25
|
+
You need to create a directory for your theme inside `assets/`.
|
|
26
|
+
|
|
27
|
+
2. In your custom `_variables.scss`, define a new variable `$logo-main` and set it to the path for your new logo:
|
|
28
|
+
|
|
29
|
+
```scss
|
|
30
|
+
$logo-main: "/static/assets/<theme_name>/<your-logo.png>";
|
|
31
|
+
|
|
32
|
+
3. If the `$logo-main` variable is defined in your `_variables.scss`, it will overwrite the default logo. Otherwise, the default logo will be displayed.
|
|
33
|
+
|
|
34
|
+
4. You can repeat the same steps for the horizontal version of the logo using the `$logo-horizontal` variable.
|
|
File without changes
|
|
Binary file
|