Showing preview only (444K chars total). Download the full file or copy to clipboard to get everything.
Repository: okfn/opendataeditor
Branch: main
Commit: 957c79351781
Files: 104
Total size: 415.0 KB
Directory structure:
gitextract_cjudfkyp/
├── .github/
│ ├── issue_template.md
│ ├── pull_request_template.md
│ ├── stale.yaml
│ └── workflows/
│ └── general.yaml
├── .gitignore
├── .python-version
├── LICENSE.md
├── README.md
├── build.py
├── create-deb.sh
├── create-dmg.sh
├── docs/
│ ├── Makefile
│ ├── make.bat
│ ├── public/
│ │ └── .gitkeep
│ ├── requirements.txt
│ └── source/
│ ├── conf.py
│ ├── contributing/
│ │ ├── contribution-guidelines.md
│ │ └── translations.md
│ ├── index.rst
│ ├── introduction/
│ │ ├── acknowledgements.md
│ │ ├── fair-data.md
│ │ ├── free-data-literacy-course.md
│ │ ├── latest-updates.md
│ │ ├── responsible-ai-integration.md
│ │ ├── similar-tools-and-differentiators.md
│ │ └── what-is-open-data-editor.md
│ ├── technical-documentation/
│ │ ├── building-the-application.md
│ │ ├── documentation.md
│ │ ├── environment.md
│ │ ├── making-a-release.md
│ │ ├── prerequisites.md
│ │ ├── running-tests.md
│ │ └── start-the-application.md
│ ├── use-cases/
│ │ ├── agricultural-data-ghana.md
│ │ ├── climate-data-kenya.md
│ │ ├── context.md
│ │ ├── data-journalism-mexico.md
│ │ ├── defence-data-france.md
│ │ ├── financial-data-south-africa.md
│ │ ├── government-data-croatia.md
│ │ ├── heritage-data-cambodia.md
│ │ └── library-data-india.md
│ └── user-guide/
│ ├── assets/
│ │ └── table-error-list/
│ │ ├── column-name-missing.csv
│ │ ├── duplicate-column-name.csv
│ │ ├── empty-row.csv
│ │ ├── extra-cell.csv
│ │ ├── header-missing.csv
│ │ └── wrong-data-type.csv
│ ├── deleting-files-or-folders.md
│ ├── downloading-ode.md
│ ├── editing-errors-in-tables.md
│ ├── exporting-your-data.md
│ ├── full-list-of-table-errors-detected.md
│ ├── how-to-explore-and-edit-metadata.md
│ ├── how-to-explore-table-errors.md
│ ├── how-to-use-the-ai-component.md
│ ├── installing-ode.md
│ └── uploading-data.md
├── packaging/
│ ├── linux/
│ │ └── opendataeditor.desktop
│ ├── macos/
│ │ ├── entitlements.mac.plist
│ │ └── icon.icns
│ └── windows/
│ └── installer.nsi
├── pyproject.sublime-workspace
├── pyproject.toml
├── src/
│ └── ode/
│ ├── __init__.py
│ ├── assets/
│ │ ├── __init__.py
│ │ ├── licenses.json
│ │ ├── style.qss
│ │ └── translations/
│ │ ├── de.qm
│ │ ├── de.ts
│ │ ├── es.qm
│ │ ├── es.ts
│ │ ├── fr.qm
│ │ ├── fr.ts
│ │ ├── it.qm
│ │ ├── it.ts
│ │ ├── pt.qm
│ │ └── pt.ts
│ ├── dialogs/
│ │ ├── __init__.py
│ │ ├── delete.py
│ │ ├── download.py
│ │ ├── llm_dialog_warning.py
│ │ ├── loading.py
│ │ ├── metadata.py
│ │ ├── rename.py
│ │ └── upload.py
│ ├── file.py
│ ├── llama.py
│ ├── log_setup.py
│ ├── main.py
│ ├── panels/
│ │ ├── __init__.py
│ │ ├── data.py
│ │ ├── errors.py
│ │ └── source.py
│ ├── paths.py
│ ├── shared.py
│ └── utils.py
└── tests/
├── __init__.py
├── conftest.py
└── ode/
├── __init__.py
├── test_application.py
├── test_files.py
├── test_frictionless_errors.py
└── test_paths.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/issue_template.md
================================================
# Overview
Please replace this line with full information about your idea or problem. If it's a bug share as much as possible to reproduce it
================================================
FILE: .github/pull_request_template.md
================================================
- fixes #<issue-number>
---
Please make sure that all the checks pass. Please add here any additional information regarding this pull request. It's highly recommended that you link this PR to an issue (please create one if it doesn't exist for this PR)
================================================
FILE: .github/stale.yaml
================================================
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 90
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 30
# Issues with these labels will never be considered stale
exemptLabels:
- feature
- enhancement
- bug
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
================================================
FILE: .github/workflows/general.yaml
================================================
name: general
on:
push:
branches:
- main
tags:
- v*.*.*
pull_request:
branches:
- main
env:
# Mandatory when using uv pip workflow.
UV_SYSTEM_PYTHON: 1
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
version: "0.9.9"
- name: Set up Python 3.13
uses: actions/setup-python@v3
with:
python-version: "3.13"
- name: Check pep8
run: |
uvx ruff check
tests:
needs: lint
runs-on: ubuntu-latest
env:
QT_QPA_PLATFORM: 'offscreen'
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
version: "0.9.9"
- name: Set up Python 3.13
uses: actions/setup-python@v3
with:
python-version: "3.13"
- name: Install QT Libs and Dependencies
uses: tlambert03/setup-qt-libs@v1
- name: Install python requirements
run: |
uv sync
- name: Check pep8
run: |
uvx ruff check
- name: Run tests
run: |
uv run pytest
macos-packaging:
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')
needs: tests
runs-on: macos-13
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
version: "0.9.9"
- name: Set up Python 3.13
uses: actions/setup-python@v3
with:
python-version: "3.13"
- name: Install Dependencies
run: |
uv sync
brew install create-dmg
- name: Build and notarize the dmg file
env:
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
run: |
chmod +x create-dmg.sh
./create-dmg.sh
- name: Archive build artifacts
uses: actions/upload-artifact@v4
with:
name: distribution-files-macos
path: |
*.dmg
retention-days: 14
linux-packaging:
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')
needs: tests
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
version: "0.9.9"
- name: Set up Python 3.13
uses: actions/setup-python@v3
with:
python-version: "3.13"
- name: Install OS Dependencies
run: |
sudo apt-get update
sudo gem install fpm
fpm --version
- name: Install Qt Dependencies
run: |
# https://forum.qt.io/post/769050
# Fix PyInstaller warnings of Qt Dependencies missing
sudo apt-get install synaptic
sudo apt-get install libxcb-icccm4 libxcb-image0-dev libxcb-keysyms1 libxcb-render-util0 libxcb-xkb1 libxcb-xinerama0 libxkbcommon-x11-0 libxcb-cursor0 libxcb-shape0-dev
- name: Install Dependencies
run: |
uv sync
- name: Build the deb package
run: |
chmod +x create-deb.sh
./create-deb.sh
- name: Archive build artifacts
uses: actions/upload-artifact@v4
with:
name: distribution-files-deb
path: |
dist/*.deb
retention-days: 14
windows-packaging:
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')
needs: tests
runs-on: windows-2022
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
version: "0.9.9"
- name: Set up Python 3.13
uses: actions/setup-python@v3
with:
python-version: "3.13"
- name: Install Dependencies
run: |
uv sync
- name: Install NSIS
run: choco install nsis
- name: Build with PyInstaller
run: uv run build.py build
- name: Compile installer
shell: bash # Force bash shell for VERSION command
run: |
VERSION=$(uv run python -c "from importlib.metadata import version; print(version('opendataeditor'))")
makensis -DAPP_VERSION="$VERSION" ./packaging/windows/installer.nsi
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: distribution-files-win
path: |
.\packaging\windows\*.exe
retention-days: 14
linux-app-image:
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')
needs: tests
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
version: "0.9.9"
- name: Set up Python 3.13
uses: actions/setup-python@v3
with:
python-version: "3.13"
- name: Install Qt Dependencies
run: |
# https://forum.qt.io/post/769050
# Fix PyInstaller warnings of Qt Dependencies missing
sudo apt-get update
sudo apt-get install synaptic
sudo apt-get install libxcb-icccm4 libxcb-image0-dev libxcb-keysyms1 libxcb-render-util0 libxcb-xkb1 libxcb-xinerama0 libxkbcommon-x11-0
- name: Install Dependencies
run: |
uv sync
- name: Build the deb package
run: |
VERSION=$(uv run python -c "from importlib.metadata import version; print(version('opendataeditor'))")
uv run build.py build --onefile --name "opendataeditor-$VERSION.AppImage"
- name: Archive build artifacts
uses: actions/upload-artifact@v4
with:
name: distribution-files-app-image
path: |
dist/*.AppImage
retention-days: 14
================================================
FILE: .gitignore
================================================
venv
data/
__pycache__
build/
dist/
tmp/
# Astro
node_modules/
.astro
package-lock.json
/.venv
.DS_Store
================================================
FILE: .python-version
================================================
3.13
================================================
FILE: LICENSE.md
================================================
The MIT License (MIT)
Copyright (c) 2022 Open Knowledge Foundation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
[](https://github.com/frictionlessdata/application/actions)
[](https://github.com/frictionlessdata/application)

# Open Data Editor (beta)
### Welcome to our Readme!
The Open Data Editor (ODE) is a **no-code application** to **explore, validate and publish data** in a simple way. Forever free and **open source project** powered by the **Frictionless Framework**.
📩 [Send us feedback/Report a problem (email)](mailto:info@okfn.org)
🪲 [Create an issue on GitHub](https://github.com/okfn/opendataeditor/issues)
🤔 [Suggest a new feature](https://github.com/okfn/opendataeditor/issues)
# Useful links
🔵 [Development guide](https://opendataeditor.okfn.org/contributing/development/)
🔵 [Open Data Editor User Guide and Project Documentation](https://opendataeditor.okfn.org/)
🔵 [Frictionless Framework](https://framework.frictionlessdata.io/)
🔵 [Frictionless Data](https://frictionlessdata.io/)
🔵 [Contributing Guidelines](https://opendataeditor.okfn.org/contributing/contribution-guidelines)
🔵 [Open Data Editor Concept Note](https://opendataeditor.okfn.org/ode-concept-note.pdf)
🔵 For all contributions: [Code of conduct](https://frictionlessdata.io/code-of-conduct/)
# How to download the ODE
You can download the latest version from the [ODE website](https://okfn.org/en/projects/open-data-editor/)
For previous releases, you can download them from Github [RELEASE](https://github.com/okfn/opendataeditor/releases)
* For **Windows**:Download the most recent **EXE** file.
* For **MacOS**:Download the most recent **DMG** file.
* For **Linux**:Download the most recent **AppImage** or **DEB** file.
================================================
FILE: build.py
================================================
import os
import platform
import PyInstaller.__main__
import subprocess
import sys
def run(cmd: list[str], cwd: str = "."):
"""Run a subprocess command."""
subprocess.run(cmd, check=True, cwd=cwd)
def docs():
"""Build the documentation and run a local server."""
run(["make", "html"], cwd="docs")
run(["python", "-m", "http.server", "-d", "build/html"], cwd="docs")
def update_translations():
"""Generate/update .ts files from the source code."""
run(["pyside6-lupdate", "-extensions", "py", "-recursive", "ode", "-ts", "ode/assets/translations/de.ts", "-target-language", "de_DE"])
run(["pyside6-lupdate", "-extensions", "py", "-recursive", "ode", "-ts", "ode/assets/translations/es.ts", "-target-language", "es_ES"])
run(["pyside6-lupdate", "-extensions", "py", "-recursive", "ode", "-ts", "ode/assets/translations/fr.ts", "-target-language", "fr_FR"])
run(["pyside6-lupdate", "-extensions", "py", "-recursive", "ode", "-ts", "ode/assets/translations/pt.ts", "-target-language", "pt_PT"])
run(["pyside6-lupdate", "-extensions", "py", "-recursive", "ode", "-ts", "ode/assets/translations/it.ts", "-target-language", "it_IT"])
def compile_translations():
"""Compile .ts to .qm files."""
run(["pyside6-lrelease", "ode/assets/translations/de.ts", "-qm", "ode/assets/translations/de.qm"])
run(["pyside6-lrelease", "ode/assets/translations/es.ts", "-qm", "ode/assets/translations/es.qm"])
run(["pyside6-lrelease", "ode/assets/translations/fr.ts", "-qm", "ode/assets/translations/fr.qm"])
run(["pyside6-lrelease", "ode/assets/translations/pt.ts", "-qm", "ode/assets/translations/pt.qm"])
run(["pyside6-lrelease", "ode/assets/translations/it.ts", "-qm", "ode/assets/translations/it.qm"])
def build_application():
"""Build an executable file for the Application."""
system = platform.system()
# Linux Defaults
icon_path = "packaging/linux/icon.svg"
app_name = "opendataeditor"
if system == "Darwin": # macOS
icon_path = "packaging/macos/icon.icns"
app_name = "OpenDataEditor"
elif system == "Windows":
icon_path = "packaging/windows/icon.ico"
app_name = "opendataeditor"
print("Creating executable file for Open Data Editor")
params = [
"src/ode/main.py",
"--windowed", # Required for Windows install to not open a console.
"--collect-all",
"frictionless", # Frictionless depends on data files
"--collect-all",
"ode", # Collect all assets from Open Data Editor
"--collect-all",
"llama_cpp", # Collect all assets from llama_cpp
"--collect-all",
"numpy", # Collect all assets from numpy (llama_cpp dependency)
"--log-level",
"WARN",
"--name",
app_name,
"--noconfirm",
"--icon",
icon_path,
]
if system == "Darwin":
params.extend(["--osx-bundle-identifier", "org.okfn.opendataeditor"])
if system == "Windows":
# llama_cpp depends on vcomp140.dll and it is not properly collected by PyInstaller as it
# is a dependency of shiboken6 as well. This library is only present in Windows if C++ Redistributable
# is installed which might not be the case for all of our users.
params.extend(["--add-binary", "C:\\Windows\\system32\\vcomp140.dll:."])
# Allow calling `python build.py build` with extra arguments
# e.g. when building AppImage `python build.py build --onefile --name "opendataeditor-X.Y.Z.AppImage"`
cli_args = sys.argv[2:]
if cli_args:
params.extend(cli_args)
PyInstaller.__main__.run(params)
# Clean the spec file generated by PyInstaller
if os.path.exists(f"{app_name}.spec"):
os.remove(f"{app_name}.spec")
def main():
if len(sys.argv) < 2:
print("Usage:")
print(" python build.py update-translations")
print(" python build.py compile-translations")
print(" python build.py build")
print(" python build.py docs")
sys.exit(1)
command = sys.argv[1].lower()
if command == "update-translations":
update_translations()
elif command == "compile-translations":
compile_translations()
elif command == "build":
build_application()
elif command == "docs":
docs()
else:
print(f"Unknown command: {command}")
sys.exit(1)
if __name__ == "__main__":
main()
================================================
FILE: create-deb.sh
================================================
#!/bin/sh
# Script to create a PyQT deb package using fpm
#
# This script will create a folder structure that will be used as input
# for the fpm command and copy into it all the distributable files generated by
# pyinstaller.
# We will create a folder with the same structure Linux systems expects:
# - /opt for our executable and associated files (a.k.a our dist/ folder)
# - /usr/share/applications (for the .desktop file)
# - /usr/share/icons/hicolor/scalable/apps for our svg icons (only svg in scalable folder)
#
# More info:
# - https://www.pythonguis.com/tutorials/packaging-pyqt5-applications-linux-pyinstaller/
# - https://fpm.readthedocs.io/en/latest/packages/dir.html#dir-local-files
#
# Create folders
[ -e tmp ] && rm -r tmp
mkdir -p tmp/opt
mkdir -p tmp/usr/share/applications
mkdir -p tmp/usr/share/icons/hicolor/scalable/apps
# Build the project
[ -e build ] && rm -r build
[ -e dist ] && rm -r dist
uv run build.py build
# Copy files
cp -r dist/opendataeditor tmp/opt/opendataeditor
cp ./packaging/linux/icon.svg tmp/usr/share/icons/hicolor/scalable/apps/org.okfn.opendataeditor.svg
cp ./packaging/linux/opendataeditor.desktop tmp/usr/share/applications
# Change permissions
# Packages retain the permissions of installed files from when they were packaged,
# but will be installed by root. In order for ordinary users to be able to run the
# application, we need to change the permissions.
find tmp/opt/opendataeditor -type f -exec chmod 644 -- {} +
find tmp/opt/opendataeditor -type d -exec chmod 755 -- {} +
find tmp/usr/share -type f -exec chmod 644 -- {} +
chmod +x tmp/opt/opendataeditor/opendataeditor
# Create the deb package
VERSION=$(uv run python -c "from importlib.metadata import version; print(version('opendataeditor'))")
FILENAME=opendataeditor-linux-$VERSION.deb
[ -e dist/$FILENAME ] && rm dist/$FILENAME
fpm -C tmp -s dir -t deb -n "opendataeditor" -v $VERSION -p dist/$FILENAME
================================================
FILE: create-dmg.sh
================================================
#!/bin/sh
# File to create the DMG file using create-dmg tool and notarize it.
#
# It is intended to work on Github Actions and so it might not be the most optimized
# workflow for executing it locally (because of the secrets required for code sign
# and notarizing)
#
# This script expects 6 secrets:
# - CSC_LINK: A base64 encoded p12 certificate.
# - CSC_KEY_PASSWORD: The password used to encrypt the p12 certificate
# - APPLE_TEAM_ID: This is the ID of the team of your Apple Developer Account (Something like S1235Q75WSA)
# - APPLE_APPLE_ID: This is the ID of your Apple Developer Account (usually your email)
# - APPLE_APP_SPECIFIC_PASSWORD: The Application Specific Password created in your Developer Account.
#
# How to generate an APPLE_APP_SPECIFIC_PASSWORD
# 1) Visit the Apple ID website: https://appleid.apple.com/
# 2) Sign in with your Apple ID credentials
# 3) Navigate to the "Security" section
# 4) Look for "App-Specific Passwords"
# 5) Click "Generate Password..."
# 6) Enter a descriptive label (e.g., "macOS App Notarization")
# 7) Apple will generate a 16-character app-specific password
# 8) Copy this password and use it as the value for the $APPLE_APP_SPECIFIC_PASSWORD environment variable in your notarization workflow
#
# Context and materials that inspired this script:
# - https://www.pythonguis.com/tutorials/packaging-pyqt6-applications-pyinstaller-macos-dmg/
# - https://medium.com/flutter-community/build-sign-and-deliver-flutter-macos-desktop-applications-on-github-actions-5d9b69b0469c
# - https://defn.io/2023/09/22/distributing-mac-apps-with-github-actions/
# - https://gist.github.com/txoof/0636835d3cc65245c6288b2374799c43
#
# Issues we had with the notarization process:
# - https://github.com/pyinstaller/pyinstaller/issues/8927
# Build the project
[ -e build ] && rm -r build
[ -e dist ] && rm -r dist
uv run build.py build
mv "dist/OpenDataEditor.app" "dist/Open Data Editor.app"
# # Codesign the executable created by pyinstaller
echo "Codesigning the executable created by PyInstaller"
echo $CSC_LINK | base64 --decode > certificate.p12
security create-keychain -p thisisatemporarypass build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p thisisatemporarypass build.keychain
security import certificate.p12 -k build.keychain -P $CSC_KEY_PASSWORD -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codedign: -s -k thisisatemporarypass build.keychain
echo "Signing complete application bundle..."
/usr/bin/codesign --force --deep --options=runtime --entitlements ./packaging/macos/entitlements.mac.plist -s $APPLE_TEAM_ID --timestamp "dist/Open Data Editor.app"
# Verify signature
echo "Verifying signature..."
codesign -vvv --deep --strict "dist/Open Data Editor.app"
echo "Signing process completed."
# Create dmg folder and copy our signed executable
mkdir -p dist/dmg
# We need to use -R to copy the app bundle recursively instead of -r because it doesn't preserver the symlinks otherwise
# https://github.com/pyinstaller/pyinstaller/issues/8927
# https://pyinstaller.org/en/stable/common-issues-and-pitfalls.html#requirements-imposed-by-symbolic-links-in-frozen-application
cp -R "dist/Open Data Editor.app" "dist/dmg"
# We need to detach the volume if it is already mounted
# and remove the dmg file if it exists
echo "Unmounting any existing volume..."
hdiutil detach /Volumes/"Open Data Editor" &>/dev/null || true
sleep 5
rm -f *.dmg
# Create the dmg file
VERSION=$(uv run python -c "from importlib.metadata import version; print(version('opendataeditor'))")
FILENAME=opendataeditor-macos-$VERSION.dmg
[ -e $FILENAME ] && rm $FILENAME
MAX_RETRIES=3
RETRY_COUNT=0
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
echo "Creating the DMG file"
if create-dmg \
--volname "Open Data Editor" \
--volicon "./packaging/macos/icon.icns" \
--window-pos 200 120 \
--window-size 800 400 \
--icon-size 100 \
--icon "Open Data Editor.app" 200 190 \
--hide-extension "Open Data Editor.app" \
--app-drop-link 600 185 \
$FILENAME \
"dist/dmg/";then
echo "DMG created: $FILENAME"
break
else
RETRY_COUNT=$((RETRY_COUNT + 1))
echo "Failed to create DMG. Retrying... ($RETRY_COUNT/$MAX_RETRIES)"
hdiutil detach "/Volumes/Open Data Editor" -force &>/dev/null || true
killall Finder &>/dev/null || true
sleep 20
fi
done
if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then
echo "Failed to create DMG after $MAX_RETRIES attempts. Exiting."
exit 1
fi
if [ ! -f "$FILENAME" ]; then
echo "DMG file not found. Exiting."
exit 1
fi
# Notarize the DMG File
# If an error occurs, we can check the logs using
# xcrun notarytool log $REPLACE-WITH-RUNNING-HASH --team-id $APPLE_TEAM_ID --apple-id $APPLE_ID --password $APPLE_APP_SPECIFIC_PASSWORD notarization_log.json
echo "Notarizing the DMG file"
xcrun notarytool submit --verbose --team-id $APPLE_TEAM_ID --apple-id $APPLE_ID --password $APPLE_APP_SPECIFIC_PASSWORD --wait $FILENAME > notarization_output.txt
# Staple the file
# We check if the notarization was successful
if grep -q "status: Accepted" notarization_output.txt; then
echo "Notarization successful!"
# We wait for 30 seconds to make sure the notarization ticket is available
echo "Waiting 30 seconds for notarization ticket to be available..."
sleep 30
echo "Stapling the file"
xcrun stapler staple $FILENAME
else
echo "Notarization failed. Check notarization_output.txt for details."
cat notarization_output.txt
exit 1
fi
================================================
FILE: docs/Makefile
================================================
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
================================================
FILE: docs/make.bat
================================================
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
if "%1" == "" goto help
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd
================================================
FILE: docs/public/.gitkeep
================================================
================================================
FILE: docs/requirements.txt
================================================
sphinx
myst-parser
sphinx_rtd_theme
================================================
FILE: docs/source/conf.py
================================================
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = 'Open Data Editor'
copyright = '2025, Open Knowledge Foundation'
author = 'Open Knowledge Foundation'
release = '1.5.1'
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = [
'myst_parser',
'sphinx_rtd_theme',
]
myst_enable_extensions = [
"colon_fence", # Admonitions
]
templates_path = ['_templates']
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
html_theme = 'sphinx_rtd_theme'
html_static_path = ['_static']
================================================
FILE: docs/source/contributing/contribution-guidelines.md
================================================
# Contributing
## Contribution Guidelines
You don’t need to know how to code to contribute to Open Data Editor, you just need time and attitude. There are many possible ways to contribute to the project:
* Use the Open Data Editor and give us feedback
* Spread the word about it\!
* Improve the documentation
* Add a translation
* Report issues
* Contribute to the code
Please read this guide for more details on the contribution process.
### How can you help?
#### Giving us feedback
Use the Open Data Editor and give us feedback. You can try out all the features and report any issues you might encounter. You can also suggest improvements to the Open Data Editor’s look and layout. For example, *if something feels confusing or hard to find in the tool’s layout, you can suggest changes to make it easier to use.*
Feedback from a beginner’s perspective is particularly welcome as it helps us improve usability.
To give us feedback, you can either [open an issue in the GitHub repository](https://github.com/okfn/opendataeditor) or reach out by email at info@okfn.org.
#### Spread the word
If you like the application, tell others about it\! Spreading the word about the Open Data Editor is also a valuable contribution to the project. Whether you share it on social media, write a blog post, create a tutorial video, or simply tell a friend, you’re helping more people discover and use the tool. Every bit of awareness and engagement makes a difference\!
#### Documentation
##### Improving documentation
Documentation is always the best place to start contributing. If you spot a typo or you’ve ever thought, “I wish the documentation explained this more clearly,” you can help make it easier to understand. You can either flag that, or propose a modification. You can also add missing steps or explanations.
##### Adding examples
You can also contribute by adding examples\! If you notice something is not covered in the documentation, try creating a helpful example and sharing it. You can also provide real-world use cases to show how Open Data Editor can be used. Every improvement makes Open Data Editor easier for everyone to use\!
All documentation files are contained [in this folder](https://github.com/okfn/opendataeditor/tree/main/portal/content/docs/documentation). You can just propose a change or add examples by opening a PR. Please **always include a short description of what you changed and why**, so it is easier to review. If you don’t feel comfortable using GitHub, you can also send us an email with the proposed changes at info\[at\]okfn.org.
##### Use cases
If you have started using Open Data Editor in your daily work and would like to share with the world how the tool is helping you, you can always write a blog about it. You can find a few examples [here](https://blog.okfn.org/tag/ode-use-cases/).
If possible, try to include photos and screenshots. If you need guidance, you can always reach out to us at info@okfn.org. You can send us your blog at that same email address.
#### Reporting a bug
We use GitHub as a code and issues hosting platform. To report a bug or propose a new feature, please open an issue [in the repository](https://github.com/okfn/opendataeditor/issues). Please provide a detailed description of the bug and include screenshots. There are a few predefined issue templates to help you get started.
If you don’t feel comfortable using GitHub, you can also send us an email at info@okfn.org.
#### Code contributions
##### Pull requests
First, please check the [issue tracker](https://github.com/okfn/opendataeditor/issues). Look for issues with “help wanted” or “good first issue.” **If you want to submit a PR, there needs to be a corresponding issue in the issue tracker.** Please link the issue in your PR, and provide a brief explanation of the changes you are pushing.
### What is the review process for your contribution?
Your contribution will be **reviewed by the Open Data Editor core team at Open Knowledge Foundation**.
In case of documentation and code contributions, a member of the core team will check if it is relevant, helpful, and easy to understand. If small changes are needed, they suggest edits. Once everything looks good, the contribution is approved and added to the project, and the contributor is acknowledged (because every contribution matters\!).
In case of bugs and feedback contributions, the core team will discuss if and how they can be implemented. We can include you in the discussion if you wish. Once we have a clear pathway, we will notify you.
### Do you need help?
If at any point you need help, feel free to contact us via email at [info@okfn.org](mailto:info@okfn.org).
================================================
FILE: docs/source/contributing/translations.md
================================================
# Translations
ODE supports several languages following the [Qt Framework practices for](https://doc.qt.io/qt-6/internationalization.html) internationalisation.
## Internationalisation workflow
:::{note}
ODE provides a `python build.py update-translations` and `python build.py compile-translations` command for all the supported languages. Check the project’s `build.py` file for reference.
:::
1. Create or update translation files by running `python build.py update-translations` (or the `pyside6-lupdate` command directly if you are on macOS)
2. Update the translation files:
1. Complete `unfinished` translations (this is the actual addition of translated text).
2. Clean `vanished` translations.
3. Compile the translation files by running `python build.py compile-translations` (or the `pyside6-lrelease` command directly if you are on macOS)
4. Commit both the `.ts` and `.qm` files.
5. Create a PR with the changes.
All translation files are located in the `ode/assets/translations/` folder.
## Translation Tools
For updating translations, you can use either:
1. A text editor to directly update the translation files (\*.ts)
2. Install Qt and use [Qt Linguist](https://doc.qt.io/qt-6/qtlinguist-index.html) application to do the translation using a UI.
## Adding new languages
When adding a new language, two extra changes are required:
1. Add the new language to both `python build.py update-translations` and `python build.py compile-translations` commands of the `build.py` file. (after doing this, you can run the `python build.py update-translations` and it will create the `.ts` file for you.)
2. Update the `language` QComboBox of the `main.py` file so the new language appears as an option to the user.
Here is a reference Pull Request of what is expected when adding a new language: [https://github.com/okfn/opendataeditor/pull/750](https://github.com/okfn/opendataeditor/pull/750)
================================================
FILE: docs/source/index.rst
================================================
.. Open Data Editor documentation master file, created by
sphinx-quickstart on Fri Jul 4 13:22:06 2025.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Open Data Editor Docs
========================================
.. image:: /assets/ODE-logo.png
:alt: Open Data Editor logo
:width: 200px
**Your no-code app for error-free spreadsheets, and guaranteed privacy and FAIR data**
This website contains user guides and technical documentation for the Open Data Editor (ODE), as well as information on contributing code and use cases.
For more information, please visit the official project page at the Open Knowledge Foundation (OKFN) website: `https://okfn.org/en/projects/open-data-editor/ <https://okfn.org/en/projects/open-data-editor/>`_
.. toctree::
:maxdepth: 3
:caption: Introduction
introduction/what-is-open-data-editor.md
introduction/fair-data.md
introduction/responsible-ai-integration.md
introduction/free-data-literacy-course.md
introduction/similar-tools-and-differentiators.md
introduction/acknowledgements.md
introduction/latest-updates.md
.. toctree::
:maxdepth: 3
:caption: Use Cases
use-cases/context.md
use-cases/agricultural-data-ghana.md
use-cases/climate-data-kenya.md
use-cases/data-journalism-mexico.md
use-cases/defence-data-france.md
use-cases/financial-data-south-africa.md
use-cases/government-data-croatia.md
use-cases/heritage-data-cambodia.md
use-cases/library-data-india.md
.. toctree::
:maxdepth: 3
:caption: User Guide
user-guide/downloading-ode.md
user-guide/installing-ode.md
user-guide/uploading-data.md
user-guide/how-to-explore-table-errors.md
user-guide/editing-errors-in-tables.md
user-guide/deleting-files-or-folders.md
user-guide/full-list-of-table-errors-detected.md
user-guide/how-to-use-the-ai-component.md
user-guide/how-to-explore-and-edit-metadata.md
user-guide/exporting-your-data.md
.. toctree::
:maxdepth: 3
:caption: Technical Documentation
technical-documentation/prerequisites.md
technical-documentation/environment.md
technical-documentation/start-the-application.md
technical-documentation/running-tests.md
technical-documentation/building-the-application.md
technical-documentation/documentation.md
technical-documentation/making-a-release.md
.. toctree::
:maxdepth: 3
:caption: Contributing
contributing/contribution-guidelines.md
contributing/translations.md
================================================
FILE: docs/source/introduction/acknowledgements.md
================================================
## Acknowledgements
We are grateful for the support and partnership of the [Patrick J. McGovern Foundation (PJMF)](https://www.mcgovern.org/), without which the development of the Open Data Editor would not have been possible. Learn more about its funding programmes [here](https://www.mcgovern.org/grants/).
================================================
FILE: docs/source/introduction/fair-data.md
================================================
## FAIR data
The Open Data Editor (ODE) improves data quality based on the [FAIR principles](https://www.go-fair.org/fair-principles/).
As described by the [GO FAIR initiative](https://www.go-fair.org/), FAIR data refers to data that adheres to the **Findable**, **Accessible**, **Interoperable**, and **Reusable** principles, designed to make research data more discoverable and usable by people and machines, enhancing its value and reuse.
**Findable**
The first step in (re)using data is to find it. Metadata and data should be easy to find for both humans and computers.
**Accessible**
Once the user finds the required data, they need to know how it can be accessed, possibly including authentication and authorisation.
**Interoperable**
The data usually need to be integrated with other data. In addition, the data needs to interoperate with applications or workflows for analysis, storage, and processing.
**Reusable**
The ultimate goal of FAIR is to optimise the reuse of data. To achieve this, metadata and data should be well-described so that they can be replicated and/or combined in different settings.
Learn all about the FAIR principles in [Module 4](https://schoolofdata.org/courses/quality-and-consistent-data-with-open-data-editor/lessons/introduction-6/) of the ‘Quality and Consistent Data with ODE’ course, available on [School of Data](https://schoolofdata.org/).
================================================
FILE: docs/source/introduction/free-data-literacy-course.md
================================================
## Free data literacy course
Open Data Editor's focus on improving digital literacy and preparing non-technical users to work with data, led us to develop a free course available on School of Data. [‘Quality and Consistent Data with the Open Data Editor’](https://schoolofdata.org/courses/quality-and-consistent-data-with-open-data-editor/) is an essential open educational resource for anyone who wants to generate knowledge from data. The course is now available in English and Portuguese, and will soon be translated into Spanish and French.
It is specially designed for non-technical users working with tabular data (Excel, Google Sheets, CSV) but without advanced technical knowledge. It's 100% online, free, and gamified.
You can check the contents and enrol here: [https://schoolofdata.org/courses/quality-and-consistent-data-with-open-data-editor/](https://schoolofdata.org/courses/quality-and-consistent-data-with-open-data-editor/)
================================================
FILE: docs/source/introduction/latest-updates.md
================================================
## Latest updates
Open Data Editor is being built in the open. Follow the progress, from feature updates to community stories on the Open Knowledge Blog: [https://blog.okfn.org/category/open-data-editor/](https://blog.okfn.org/category/open-data-editor/)
================================================
FILE: docs/source/introduction/responsible-ai-integration.md
================================================
## Responsible AI integration
The Open Data Editor (ODE) has an AI component to help users better understand their data. The AI component is powered by local models. **No data is sent to the cloud, and all operations take place on the user’s computer.**
The introduction of these AI-assisted data quality features significantly expands the tool’s capabilities, enabling faster identification of anomalies, smarter suggestions for corrections, and improved handling of large, dense datasets while retaining a privacy-preserving, local-first design.
This deepens Open Knowledge Foundation's commitment to developing simple, sustainable and long-lasting technologies that solve people’s real problems, in line with [The Tech We Want](https://okfn.org/en/projects/the-tech-we-want/) initiative.
In order to use the AI feature, ODE will guide users to download the model onto their machine first.
:::{note}
As the model is local and all computing will be happening in the user’s computer, performance will be affected by the machine’s hardware, and the response quality sometimes will be minor than typical cloud LLMs like ChatGPT, Deepseek or Claude.
:::
ODE is trying to balance user experience for non-technical audiences and performance and privacy.
================================================
FILE: docs/source/introduction/similar-tools-and-differentiators.md
================================================
## Similar tools and differentiators
The tools currently available with functions similar to those of the Open Data Editor were created for a specific purpose and have a more technical profile. This makes them difficult for people unfamiliar with code, standards or programming languages to access.
The main differences in relation to ODE are listed in each subsection below:
### Data Check
Available at: [https://data.humdata.org/tools/datacheck/import](https://data.humdata.org/tools/datacheck/import)
Main differences:
* Maximum file size: 20 MB.
* Works only with the HXL standard.
* The table view after the error check is limited, and the user needs to navigate through several tabs if the file has many lines.
* Does not include a publication feature.
### IATI Validator
Available at: [https://validator.iatistandard.org/](https://validator.iatistandard.org/)
Main differences:
* Works only with the IATI standard.
* Targets a specific sector, the international aid community.
* The tool offers five levels of qualification regarding data quality (Success, Success with Advisories, Warning, Error, and Critical), rather than a list of all errors and how to correct them.
### CSV Lint.io
Available at: [https://csvlint.io/](https://csvlint.io/)
Main differences:
* Works only with CSV files, informing the user if the file “is readable” or not.
* Agnostic tool; the schema can also be ingested.
### 360Giving Data Quality Checker
Available at: [https://dataquality.threesixtygiving.org/](https://dataquality.threesixtygiving.org/)
Main differences:
* Works only with the 360Giving standard.
================================================
FILE: docs/source/introduction/what-is-open-data-editor.md
================================================
## What is Open Data Editor
[Open Data Editor (ODE)](https://okfn.org/opendataeditor), developed by the [Open Knowledge Foundation (OKFN)](https://okfn.org/en/), is a free, open-source tool designed to help nonprofits, data journalists, activists, and public servants detect errors in their datasets. It is designed for people working with tabular data (Excel, Google Sheets, CSV) who don't know how to code or don't have the programming skills to automatise the data exploration process.
The technical mission of ODE is to provide a free, open-source, no-code, cross-platform desktop application that empowers non-technical users working with tabular data to quickly detect and correct errors, enforce data-validation and metadata standards (notably the [FAIR principles](https://www.go-fair.org/fair-principles/)), and output clean, interoperable datasets ready for publication – while preserving privacy (local-first architecture), remaining lightweight (suitable for low-resource or offline contexts), and employing open standards (e.g., the [Frictionless framework](https://framework.frictionlessdata.io/)) for maximum reuse and integration.
Since 2025, the Digital Public Goods Alliance (DPGA) has recognised Open Data Editor as a [digital public good](https://blog.okfn.org/2025/10/22/open-data-editor-recognised-as-a-digital-public-good/) (DPG), which means it meets high standards of openness and supports sustainable development globally.
================================================
FILE: docs/source/technical-documentation/building-the-application.md
================================================
## Building the application
```bash
uv run build.py build
```
or
```bash
# With the virtual environment activated
python build.py build
```
This will create a distributable file for the application in the ‘dist/’ folder.
================================================
FILE: docs/source/technical-documentation/documentation.md
================================================
## Documentation
Documentation is written with [Sphinx](https://www.sphinx-doc.org/en/master/) (in the `docs` directory). The source files are in the `docs/source/` directory. To locally build the documentation, you can execute:
```bash
uv run build.py docs
```
or
```bash
# With the virtual environment activated
python build.py docs
```
It will be automatically published on CloudFlare when merged to the `main`branch, with previews available for pull requests.
================================================
FILE: docs/source/technical-documentation/environment.md
================================================
## Environment
Use `uv` to create a virtualenv and activate it:
```bash
uv sync
source venv/bin/activate
```
================================================
FILE: docs/source/technical-documentation/making-a-release.md
================================================
## Making a release
To make a release, follow the following checklist:
* Check with the Product Owner that the `main` branch is code complete.
* Check that the distributables built on `main` are working by installing them on your machine.
* Sometimes PyInstaller cannot compile new dependencies, and the application will fail at runtime.
* Create a new PR bumping the version of the application in the `pyproject.toml` file and merge it to main.
* Create a New Github Release with a new tag matching the new version number of the application.
* Fill in the Release notes.
* Create the Release.
* Wait until the GitHub Action for the new tag finishes, and then upload the distributable files to the new Release.
* Notify the Communications Team to make the announcement and changes to the [OKFN’s Website](https://okfn.org/opendataeditor/).
================================================
FILE: docs/source/technical-documentation/prerequisites.md
================================================
## Prerequisites
We are using 3.13. To start working on the project, you need the following dependencies on your machine:
* Python 3.13
* python3.13-dev (For PyInstaller)
We are using [uv](https://docs.astral.sh/uv/) as a package manager, so make sure you have it installed.
================================================
FILE: docs/source/technical-documentation/running-tests.md
================================================
## Running tests
```bash
uv run pytest tests/
```
or
```bash
# With the virtual environment activated
pytest tests/
```
================================================
FILE: docs/source/technical-documentation/start-the-application.md
================================================
## Start the application
```bash
uv run ode
```
or
```bash
# With the virtual environment activated
python src/ode/main.py
```
================================================
FILE: docs/source/use-cases/agricultural-data-ghana.md
================================================
## Agricultural data (Ghana)
Open Science Community Ghana (OSCG) used ODE to streamline the work with manually-collected data and reduce the time to identify and correct errors.
The ODE interface enabled the team to easily define and enforce a standard data structure, ensuring that every dataset adheres to a consistent format. This directly solved their core problem of harmonising the data that they receive from different sources.

An example of errors detected by ODE in a research dataset: blank cells and wrong formats.
Learn more: [https://blog.okfn.org/2025/11/10/open-data-editor-in-action-bridging-the-gap-between-field-data-and-research-insights-in-ghana/](https://blog.okfn.org/2025/11/10/open-data-editor-in-action-bridging-the-gap-between-field-data-and-research-insights-in-ghana/)
================================================
FILE: docs/source/use-cases/climate-data-kenya.md
================================================
## Climate data (Kenya)
The Demography Project used ODE to check and correct errors in a giant spreadsheet of air quality data, enabling accurate analysis.
These tables, which compile data from different sources (including two portable air quality monitors, GPS devices, smartphones), had many inconsistencies that were detected in seconds. Once the errors have been identified, they could then correct the missing or incorrect data, increasing the dataset’s quality.

In this spreadsheet, ODE identified 29 inconsistencies and errors in the dataset.
Learn more: [https://blog.okfn.org/2025/04/28/open-data-editor-use-case-a-giant-spreadsheet-of-environmental-data-now-accurate-for-analysis/](https://blog.okfn.org/2025/04/28/open-data-editor-use-case-a-giant-spreadsheet-of-environmental-data-now-accurate-for-analysis/)
================================================
FILE: docs/source/use-cases/context.md
================================================
# Context
The Open Data Editor was developed in collaboration with user communities worldwide. Between 2024 and 2025, three formal testing and feedback phases were conducted with civil society organisations working in different fields of knowledge. This work has given rise to several use cases, all of which are compiled here: [https://blog.okfn.org/tag/ode-use-cases/](https://blog.okfn.org/tag/ode-use-cases/)
Below, we highlight some of them to give you an idea of the types of tasks that ODE helps to perform to improve data workflow and data quality.
You can also contribute by documenting a use case. Learn how at **Documentation \> Use cases** in this guide.
================================================
FILE: docs/source/use-cases/data-journalism-mexico.md
================================================
## Data journalism (Mexico)
Data Crítica used ODE to identify reliable variables for journalistic investigations and ensure stories are built on a solid foundation.
They used it as a central part of its “data interrogation” methodology in workshops and its own research. ODE instantly flagged missing values, providing a clear, visual overview of a dataset’s completeness by highlighting empty cells.

In the same dataset as the picture above, ODE now identifies errors in columns with the same name and unexpected data types
Learn more: [https://blog.okfn.org/2025/11/28/open-data-editor-in-action-interrogating-data-for-investigative-journalism-in-mexico/](https://blog.okfn.org/2025/11/28/open-data-editor-in-action-interrogating-data-for-investigative-journalism-in-mexico/)
================================================
FILE: docs/source/use-cases/defence-data-france.md
================================================
## Defence data (France)
The Observatoire des armements used ODE to turn multiple public spending data sources (arms purchases and sales) into a single quality spreadsheet.
They reduced error resolution time from days to seconds, eliminated 95% of manual review work, and enabled the team to focus on what matters.

In this spreadsheet, ODE flagged 48 inconsistencies in seconds.
Learn more: [https://blog.okfn.org/2025/04/14/open-data-editor-use-case-multiple-defence-spending-data-sources-in-a-single-quality-spreadsheet/](https://blog.okfn.org/2025/04/14/open-data-editor-use-case-multiple-defence-spending-data-sources-in-a-single-quality-spreadsheet/)
================================================
FILE: docs/source/use-cases/financial-data-south-africa.md
================================================
## Financial data (South Africa)
The Public Affairs Research Institute (PARI) used ODE to restructure messy public financial datasets into a clean, consistent format, making them ready for reliable analysis.
ODE automatically profiled the data, instantly highlighting empty cells, type mismatches, and structural inconsistencies that would take days to find manually. This shifted their role from manual detectives to efficient data supervisors.

Examples of errors detected by ODE in a municipal dataset: blank cells and wrong formats.
Learn more: [https://blog.okfn.org/2025/11/10/open-data-editor-in-action-enhancing-fiscal-governance-and-transparency-in-south-african-municipalities/](https://blog.okfn.org/2025/11/10/open-data-editor-in-action-enhancing-fiscal-governance-and-transparency-in-south-african-municipalities/)
================================================
FILE: docs/source/use-cases/government-data-croatia.md
================================================
## Government data (Croatia)
The City of Zagreb used ODE to comply with open data standards and foster a culture of data literacy across different city offices in Zagreb.
ODE flagged inconsistencies (e.g., missing values, formatting errors) in seconds, such as a public buildings dataset with 11 columns of unlabeled energy metrics. The team was able to add critical context (e.g., data owners, sourcing methods) directly in ODE’s metadata panel, aligning with FAIR principles.

ODE’s metadata panel was central to understanding the importance of interoperability and creating a culture of data literacy in the public administration.
Learn more: [https://blog.okfn.org/2025/05/20/open-data-editor-in-action-streamlining-data-governance-and-unlocking-the-potential-value-of-urban-data-in-croatia/](https://blog.okfn.org/2025/05/20/open-data-editor-in-action-streamlining-data-governance-and-unlocking-the-potential-value-of-urban-data-in-croatia/)
================================================
FILE: docs/source/use-cases/heritage-data-cambodia.md
================================================
## Heritage data (Cambodia)
An AI of Our Own (AAOO) used ODE to create AI models that are built on respectful and ethically sourced data from the Global South.
ODE allowed the team to identify and rectify formatting inconsistencies. They could standardise date formats and other variables, ensuring that data from different collection methods could be seamlessly unified. A critical feature for AAOO was the ability to add detailed descriptions to each column. This process of adding context and meaning to each data point is fundamental to building a high-quality, culturally nuanced AI dataset.

Errors flagged showing data inconsistency from the converted unstructured data into a structured format without considering the standard format and time stamps (Data on Indigenous Knowledge Systems on Plant use)
Learn more: [https://blog.okfn.org/2025/11/05/open-data-editor-in-action-building-culturally-accurate-and-global-south-sensitive-ai-datasets/](https://blog.okfn.org/2025/11/05/open-data-editor-in-action-building-culturally-accurate-and-global-south-sensitive-ai-datasets/)
================================================
FILE: docs/source/use-cases/library-data-india.md
================================================
## Library data (India)
The Indian Institute of Technology (IIT) Delhi used ODE to check errors in large spreadsheets of publications catalogue and educate colleagues about data quality.
ODE automatically flagged a range of issues in complex datasets, including bibliographic information from major indexes like Scopus and Web of Science. By identifying formatting inconsistencies, ODE provided a foundation for the team to clean and standardise fields, creating more reliable datasets for their reports and website.

Open Data Editor helps automate error detection; above, problems with blank cells and the data types expected for a given column.
Learn more: [https://blog.okfn.org/2025/12/09/open-data-editor-in-action-advancing-data-quality-in-academic-library-services-in-india/](https://blog.okfn.org/2025/12/09/open-data-editor-in-action-advancing-data-quality-in-academic-library-services-in-india/)
================================================
FILE: docs/source/user-guide/assets/table-error-list/column-name-missing.csv
================================================
col1,
1,2
3,4
================================================
FILE: docs/source/user-guide/assets/table-error-list/duplicate-column-name.csv
================================================
col1,col1
1,2
3,4
================================================
FILE: docs/source/user-guide/assets/table-error-list/empty-row.csv
================================================
col1,col2
1,2
3,4
================================================
FILE: docs/source/user-guide/assets/table-error-list/extra-cell.csv
================================================
col1,col2
1,2
3,4
5,6,7
================================================
FILE: docs/source/user-guide/assets/table-error-list/header-missing.csv
================================================
,
1,2
3,4
================================================
FILE: docs/source/user-guide/assets/table-error-list/wrong-data-type.csv
================================================
col1,col2
1,2
3,4
5,6
7,8
9,10
11,12
13,14
15,16
17,18
19,20
21,bad
================================================
FILE: docs/source/user-guide/deleting-files-or-folders.md
================================================
## Deleting files or folders
To delete a file or folder, click on the three dots next to the file/folder name and select **Delete**.

================================================
FILE: docs/source/user-guide/downloading-ode.md
================================================
## Downloading ODE
Open Data Editor is available on all major platforms:
* **For Windows:** Download the most recent **EXE file**.
* **For MacOS:** Download the most recent **DMG file**.
* **For Debian-based Linux:** Download the most recent **AppImage or DEB file**.
* **For other Linux:** Download the most recent **AppImage**.
### From our website
You can download ODE from our website: [https://okfn.org/opendataeditor/](https://okfn.org/opendataeditor/)
### From GitHub Releases
You can download ODE from the repository: [https://github.com/okfn/opendataeditor/releases](https://github.com/okfn/opendataeditor/releases)
================================================
FILE: docs/source/user-guide/editing-errors-in-tables.md
================================================
## Editing errors in tables
To fix cell errors, you can directly edit the data cells in the viewer/editor.
**Step 1:** Locate the cell with the error. For example:

**Step 2:** Double-click on the cell to start editing content:

**Step 3:** To save changes, click on another part of the table to accept the change in the cell, and when the **Save changes** button is activated, click on it. The button will get activated if there are unsaved changes.

After clicking the **Save changes** button, ODE will update the Errors Report.
================================================
FILE: docs/source/user-guide/exporting-your-data.md
================================================
## Exporting your data
You can export your data using the **Export** feature located at the top right of the datagrid:

Once you click the **Export** button, ODE will display the following dialogue:

### Download file
This option will download the file in CSV format.
### Download file with errors
This option will export an Excel file with three sheets:
* **Data:** This sheet contains the original table with all the errors painted in red.
* **Errors Description:** This sheet contains the description of the errors detected by ODE with the corresponding cell (row and column).
* **Blank Rows:** This sheet contains the rows on the original table that did not contain any values.
================================================
FILE: docs/source/user-guide/full-list-of-table-errors-detected.md
================================================
## Full list of table errors detected
Here we describe the list of errors that ODE can detect after users upload tables to the app. All the examples are based on CSV files.
:::{note}
It is possible to reproduce a subset of these errors using other formats like Excel, but some errors might not be applicable to other formats.
:::
To explain and understand errors, we need to illustrate some key elements that are part of tables:
* A regular table contains one **header row** (where the names of columns are listed), **rows** and **cells**.
* **Cells describing names of columns** are also called **labels**.
* Rows contain **cells** called **values**.
The table example is represented as follows:
```
[1] [header row] label 1 | label 2
[2] [data row] value 1 | value 2
[3] [data row] value 3 | value 4
```
### Errors detected automatically
**This type of error occurs when the structure of the data is not as expected.** For example, the number of columns in a row is different from the number of columns in the header.
#### Header missing (Blank Label)
This error occurs when the **header row is empty**. The header row should contain the names of the columns:
```
,
1,2
3,4
```
* [Reproduce the error using this file](https://opendataeditor.okfn.org/_downloads/bb8d52a4fb7cb7ce135cda4ac8156173/header-missing.csv)
This is how ODE will show the error:

#### Column name missing
This error occurs when **one or more column names are missing**:
```
col1,
1,2
3,4
```
* [Reproduce the error using this file](https://opendataeditor.okfn.org/_downloads/45ba64a900f79509f76ef9865da818fe/column-name-missing.csv)
This is how ODE will show the error:

#### Duplicate column name
This error occurs when there are **two or more columns with the same name**. Each column should have a unique name.
```
col1,col1
1,2
3,4
```
* [Reproduce the error using this file](https://opendataeditor.okfn.org/_downloads/c352eb003acbc2caf2f45df0d37631c9/duplicate-column-name.csv)
This is how ODE will show the error:

#### Empty row
This error occurs when an **empty row is present in the data**.
```
col1,col2
1,2
3,4
```
* [Reproduce the error using this file](https://opendataeditor.okfn.org/_downloads/4cd0ff415190ada70a6cf765842f7aff/empty-row.csv)
This is how ODE will show the error:

#### Missing cell
This error occurs when **a row has fewer cells than the header**.
```
col1,col2
1,2
3,4
5
```
This is how ODE will show the error:

#### Extra cell
This error occurs when a row has more cells than the header. Each row should have the same number of cells as the header.
```
col1,col2
1,2
3,4
5,6,7
```
* [Reproduce the error using this file](https://opendataeditor.okfn.org/_downloads/1c7d6743ad084bf1dbe1de05b961e158/extra-cell.csv)
This is how ODE will show the error:

#### Wrong data type
This error occurs when **a cell contains a value that is not of the expected type**. For example, a cell in a column that should contain numbers contains a string.
```
col1,col2
1,2
3,4
5,6
7,8
9,10
11,12
13,14
15,16
17,18
19,20
21,bad
```
* [Reproduce the error using this file](https://opendataeditor.okfn.org/_downloads/508223106a8c55b55430211419875f01/wrong-data-type.csv)
This is how ODE will show the error:

:::{note}
This error can be identified without providing a Table Schema, but only if the data has enough cells of the correct type in the column to infer the intended type. :::
### Errors Requiring Metadata
These errors can only be identified if a Table Dialect or Table Schema is provided by editing the table’s metadata. The Table Schema defines the structure of the data, including the type of each column. Table Schema adds additional constraints to the data, which are used to validate the data.
#### Extra column name
This error occurs when **a header label in the data is not defined in the Table Schema**. The Table Schema should define all the columns in the data.
```
fields:
- name: col1
- name: col2
col1,col2,col3
1,2
3,4
Cell col3 is an extra label
```
#### Missing column name
This error occurs when **a column defined in the Table Schema is not present in the header row**. The data should contain all the columns defined in the Table Schema.
```
fields:
- name: col1
- name: col2
- name: col3
col1,col2
1,2,3
4,5,6
Missing cell col3 is a missing label.
```
#### Incorrect column name
This error occurs when **the header label in the data does not match the label defined in the Table Schema**. The header row should contain the same labels as defined in the Table Schema.
```
fields:
- name: col1
- name: col2
col1,col3
1,2
3,4
Cell col3 is an incorrect label.
```
#### Primary Key Error
This error occurs when the **primary key constraint defined in the Table Schema is not satisfied**. The primary key constraint ensures that the values in the specified columns are unique.
```
fields:
- name: col1
- name: col2
primaryKey: col1
col1,col2
1,2
1,4
Cell 1 in the second data row is not unique.
```
#### Foreign Key Error
This error occurs when the **foreign key constraint defined in the Table Schema is not satisfied**. The foreign key constraint ensures that the values in the specified columns are present in another table or satisfies self-referencing constraint.
```
fields:
- name: col1
- name: col2
foreignKeys:
- fields: col2
reference:
fields: col1
col1,col2
1,2
2,4
Cell 4 in the second data row is not present in the col1 column.
```
#### Unique constraint error
This error occurs when the **unique constraint defined in the Table Schema is not satisfied**. The unique constraint ensures that the values in the specified columns are unique.
```
fields:
- name: col1
- name: col2
unique: true
col1,col2
1,2
3,2
Cell 2 in the second data row is not unique.
```
#### Constraint Error
This error occurs when **a field constraint defined in the Table Schema is not satisfied**.
Read more about Table Schema constraints: [https://datapackage.org/standard/table-schema/\#field-constraints](https://datapackage.org/standard/table-schema/#field-constraints)
```
fields:
- name: col1
- name: col2
constraints:
- required: true
col1,col2
1,2
3
Missing cell 4 in the second data row is required.
```
The following constraints can be defined in the Table Schema and are currently supported by Open Data Editor (please read the section above about unique constraints):
- required
- enum
- minimum
- maximum
- minLength
- maxLength
- pattern
================================================
FILE: docs/source/user-guide/how-to-explore-and-edit-metadata.md
================================================
## How to explore and edit metadata
To explore or edit the metadata, select a file from the menu on the left and click on any cell of the header row (first row).

ODE will then display the **Metadata** window:

You can click on any of the options to start editing the metadata linked to your file.
Once you have finished editing the metadata, click on the **Save changes** button to save the changes.
:::{note}
Saving changes will trigger a validation of the file.
:::
================================================
FILE: docs/source/user-guide/how-to-explore-table-errors.md
================================================
## How to explore table errors
As mentioned in the **Uploading data** section in this guide, if a file has errors, ODE will show a red dot next to the file name on the sidebar. However, if you want to review errors in the table, you can use the datagrid to explore problematic data.
ODE will highlight the cell in red if it has a problem. For instance, if it contains text instead of a number.
This is how a cell with an error is shown on ODE:

You can also explore errors by clicking on the **Errors Report** button, located at the top left of the datagrid:

After clicking the button, ODE will display a panel with the full list of errors:

================================================
FILE: docs/source/user-guide/how-to-use-the-ai-component.md
================================================
## How to use the AI component
To use the AI assistant, select a file from the sidebar, and then click on the **AI** button located in the top right corner of the app:

ODE will show a dialogue to assist the user in downloading the model:

Once the model is downloaded, users will be able to click on the **Next** button to continue.
### AI Use Cases
ODE has two use cases for the AI component:
1. Assist users in understanding the columns of a table.
2. Suggest analysis and questions that users can use to query the data.
To choose the use case, select it from the dropdown menu.

Once selected, click on the **Execute** button to get a response from the AI model.
:::{note}
Depending on the hardware of the users’ machines, the response time can vary. Usually, it will take around 10 seconds to get a response. :::
#### Assist users in understanding the columns of a table
The AI model will analyse the columns’ names, types and some sample data and will generate a description of each column. This is useful to understand the data better, clarify technical or complex names, expand acronyms, etc.

#### Suggest analysis and questions that users can use to query the data
The AI model will analyse the columns’ names, types and some sample data and will generate a list of questions that the user can use to query the data.

================================================
FILE: docs/source/user-guide/installing-ode.md
================================================
## Installing ODE
### Windows
Download the most recent **EXE** file as per the above instructions.
1\. If you receive the following message, click ‘Continue download’.

2\. After downloading, double-click to run the app. You may encounter the security message window, click ‘More info’ and proceed.

3\. Click ‘Run anyway’ to run the application.

### MacOS
Download the most recent **DMG** file as per the above instructions.
1\. If you encounter a security message, click on the question mark and then click the link in the first section.

2\. Change settings to allow the app to execute.

### Linux
For Linux, there are two options available:
* AppImage (for any distributions)
* deb (for Ubuntu/Debian)
#### Any Distribution
Download the most recent **AppImage** file as per the above instructions.
After downloading, you have to make it executable:

Then double-click on the file to start the application.
#### Ubuntu/Debian
Download the most recent **DEB** file as per the above instructions.
Double-click on the file, and it will initiate the installation process.

After installation, you can use it.

Optionally, in Debian, you can install it by running the following command:
*\# Replace \<version\> with the version you downloaded*
sudo dpkg \-i opendataeditor-linux-\<version\>.deb
================================================
FILE: docs/source/user-guide/uploading-data.md
================================================
## Uploading data
This section explains how to upload tabular files, folders with tables and online data to ODE. The tool also ingests other types of format files like PDF, JPEG, etc. However, please note, ODE's main objective is to detect errors on tabular files, the application will **ONLY** show previews for tables.
**Uploading data to ODE is easy\!** After installing the app and once you open the application on your laptop, you will see this screen:

You can click on the **Upload your data** button, located in the centre of the screen or at the top left of the sidebar, to start adding files/folders to the app.
:::{note}
Each time you upload a file or folder to ODE, the application will not ingest the original file/folder from your computer. Instead, it will make a copy of it and add it to the application folder on your laptop. After the ingestion process ends, you can right-click on the file/folder and select the **Open Location** option. By doing so, ODE will redirect you to the exact location where the copy was saved.
:::

### Excel, CSV files and folders
When clicking on the **Upload your data** button, ODE will display the following dialogue. If you want to upload files or folders, you can do so from the **From Your Computer** section. If you want to add tables that are online, you can do it from **Add External Data**:

You will see there are two options available in the **From your computer** section. To add file/s, click **Select** on the **Add one or more Excel or csv files** feature. If you want to ingest one or many folders, click Select on the **Add one or more folders** box.
Once the ingestion process concludes, ODE will add your data to the sidebar of the app:

Note that while uploading your data, the tool checks your files or folders to find errors according to the validation rules provided by [Frictionless](https://framework.frictionlessdata.io/). Please, check the **Full list of table errors detected** in this guide to learn more.
Please keep in mind that since the preview section (datagrid) can only show one tabular file at a time, when uploading folders, and after the ingestion process is done, you will need to click on the folder and select the file you want to visualise on the screen. As soon as you click on a specific file, ODE will start validating your data (looking for possible errors), and the table will be shown on the app.
#### Tables published online
ODE also allows users to upload online tables. You can upload files from open data portals, Google Sheets or tables from your GitHub repository.
To upload online tables, first click the **Upload your data** button and then select the **Add External Data** section:

Now, write or paste the URL to the table and click the **Add** button:

After that, ODE will start reviewing your file in the background to detect possible errors, and data will be displayed on the main screen.
:::{note}
Before you upload your online table…
👉🏼 If you are uploading a Google Sheets file, check that the file is published online. If you don’t know how to do it, please visit [this page](https://support.google.com/docs/answer/183965?hl=en&co=GENIE.Platform%3DDesktop) and follow the steps listed there.
👉🏼 For Google Sheets, please make sure you are adding the public version of your file without the HTML term at the end. For example:
✅ https://docs.google.com/spreadsheets/d/1dFVoF6f9VU5pjaGhyyvQaBN0n6ae-iLCtlvsO1N2jhA/edit?gid=0\#gid=0
❌ https://docs.google.com/spreadsheets/d/e/2PACX-1vQ8w9yb7D-iYEbImb0WD4Kh53\_Yp7H1VOi1bIMcicphWbkrrH9PobXCJhXt9frqyQ/pubhtml
👉🏼 When exporting a file from Google Sheets in CSV and you have columns with numbers, please make sure to use “.” for decimals, instead of commas. Otherwise, Frictionless, the code working behind ODE will interpret the content of your cells with numbers as text.
For all tables…
👉🏼 Make sure your file is well-organised (well-formatted: each column must contain a name, there should be no extra rows before the table, or additional elements (like borders or lines) next to the space where the tabular data is located.
👉🏼 Check that the tabular data does not contain cells that are merged. Data producers from Governments, international organizations and internal reports usually add tiles, descriptions, and graphs within sheets, like in this case. If there are extra elements in your file, ODE will ingest your file and show you multiple errors.
:::
================================================
FILE: packaging/linux/opendataeditor.desktop
================================================
[Desktop Entry]
# The type of the thing this desktop file refers to
Type=Application
# The Application Name
Name=Open Data Editor
# Tooltip comment to show in menus
Comment=Data management for humans.
# The path (folder) in which the executable is run
Path=/opt/opendataeditor
# The executable (can include arguments)
Exec=/opt/opendataeditor/opendataeditor
# The icon we install for the application, use the target filesystem path
Icon=org.okfn.opendataeditor
================================================
FILE: packaging/macos/entitlements.mac.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
</dict>
</plist>
================================================
FILE: packaging/windows/installer.nsi
================================================
; NSIS Script to create a Windows Installer.
;
; To create a windows installer:
; 1. Install NSIS from https://nsis.sourceforge.io/Download
; 2. Build your application with PyInstaller first: python build.py
; 3. Create the installer: 'C:\Program Files (x86)\NSIS\makensis.exe' .\packaging\windows\installer.nsi
;
; The APP_ID was originaly set by electron-builder in the first versions of the application and we are maintaining it.
; https://www.electron.build/nsis.html#guid-vs-application-name
!define APP_ID "42c092cd-67f7-566d-b9a4-980d3103f082"
!define APP_NAME "Open Data Editor"
!define PUBLISHER "Open Knowledge Foundation"
!define INSTALL_DIR "$LOCALAPPDATA\Programs\opendataeditor"
; Required version parameter
!ifndef APP_VERSION
!error "APP_VERSION must be defined via -DAPP_VERSION=x.y.z command parameter."
!endif
; Modern UI setup
!include "MUI2.nsh"
!include "LogicLib.nsh"
!include "FileFunc.nsh" ; To calculate Estimated Size
Name "${APP_NAME}"
OutFile "opendataeditor-win-${APP_VERSION}.exe"
InstallDir "${INSTALL_DIR}"
RequestExecutionLevel user ; No admin privileges needed for user-level install
; Registry key for uninstaller
!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_ID}"
!define MUI_ICON "icon.ico"
!define MUI_UNICON "icon.ico"
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"
Section "Install"
; Remove previous installation
RMDir /r "$INSTDIR"
; Create installation directory
SetOutPath "$INSTDIR"
; Copy application files from PyInstaller output
File /r "..\..\dist\opendataeditor\*.*"
; Calculate installed size
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
IntFmt $0 "0x%08X" $0
; Create shortcuts
CreateDirectory "$SMPROGRAMS\${APP_NAME}"
CreateShortcut "$SMPROGRAMS\${APP_NAME}\${APP_NAME}.lnk" "$INSTDIR\opendataeditor.exe"
CreateShortcut "$SMPROGRAMS\${APP_NAME}\Uninstall.lnk" "$INSTDIR\Uninstall opendataeditor.exe"
; Write uninstaller
WriteUninstaller "$INSTDIR\Uninstall opendataeditor.exe"
; Write registry entries
WriteRegStr HKCU "${UNINST_KEY}" "DisplayName" "${APP_NAME}"
WriteRegStr HKCU "${UNINST_KEY}" "DisplayVersion" "${APP_VERSION}"
WriteRegStr HKCU "${UNINST_KEY}" "Publisher" "${PUBLISHER}"
WriteRegStr HKCU "${UNINST_KEY}" "UninstallString" '"$INSTDIR\Uninstall opendataeditor.exe" /currentuser'
WriteRegStr HKCU "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\opendataeditor.exe"
WriteRegStr HKCU "${UNINST_KEY}" "InstallLocation" "$INSTDIR"
WriteRegStr HKCU "${UNINST_KEY}" "NoModify" 1
WriteRegStr HKCU "${UNINST_KEY}" "NoRepair" 1
WriteRegDWORD HKCU "${UNINST_KEY}" "EstimatedSize" "$0"
SectionEnd
Section "Uninstall"
; Remove application files
RMDir /r "$INSTDIR"
; Remove shortcuts
RMDir /r "$SMPROGRAMS\${APP_NAME}"
; Remove registry entries
DeleteRegKey HKCU "${UNINST_KEY}"
SectionEnd
================================================
FILE: pyproject.sublime-workspace
================================================
{}
================================================
FILE: pyproject.toml
================================================
[project]
name = "opendataeditor"
version = "1.7.1" # Update this version number also in ode/__init__.py
description = "A no-code application to explore and validate tabular data in a simple way. "
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"frictionless[excel,github]>=5.18.1",
"llama-cpp-python>=0.3.16",
"openpyxl>=3.1.5",
"pyside6>=6.10.0",
"xlrd>=2.0.2",
"xlutils>=2.0.0",
"xlwt>=1.3.0",
]
[project.scripts]
ode = "ode.main:main"
[build-system]
requires = ["uv_build>=0.9.10,<0.10.0"]
build-backend = "uv_build"
[tool.uv.build-backend]
module-name = "ode"
[tool.ruff]
line-length = 140
[dependency-groups]
dev = [
"ipdb>=0.13.13",
"mypy>=1.18.2",
"myst-parser>=4.0.1",
"pyinstaller==6.11.1",
"pytest>=9.0.0",
"pytest-qt>=4.5.0",
"ruff>=0.14.4",
"sphinx>=8.2.3",
"sphinx-rtd-theme>=3.0.2",
]
================================================
FILE: src/ode/__init__.py
================================================
================================================
FILE: src/ode/assets/__init__.py
================================================
# Required for PyInstaller to collect all assets
================================================
FILE: src/ode/assets/licenses.json
================================================
[
{
"name": "AAL",
"path": "https://opensource.org/licenses/AAL",
"title": "Attribution Assurance Licenses"
},
{
"name": "AFL-3.0",
"path": "https://opensource.org/licenses/AFL-3.0",
"title": "Academic Free License 3.0"
},
{
"name": "AGPL-3.0",
"path": "https://opensource.org/licenses/AGPL-3.0",
"title": "GNU Affero General Public License v3"
},
{
"name": "APL-1.0",
"path": "https://opensource.org/licenses/APL-1.0",
"title": "Adaptive Public License 1.0"
},
{
"name": "APSL-2.0",
"path": "https://opensource.org/licenses/APSL-2.0",
"title": "Apple Public Source License 2.0"
},
{
"name": "Against-DRM",
"path": "https://opendefinition.org/licenses/against-drm",
"title": "Against DRM"
},
{
"name": "Apache-1.1",
"path": "https://opensource.org/licenses/Apache-1.1",
"title": "Apache Software License 1.1"
},
{
"name": "Apache-2.0",
"path": "https://opensource.org/licenses/Apache-2.0",
"title": "Apache Software License 2.0"
},
{
"name": "Artistic-2.0",
"path": "https://opensource.org/licenses/Artistic-2.0",
"title": "Artistic License 2.0"
},
{
"name": "BSD-2-Clause",
"path": "https://opensource.org/licenses/BSD-2-Clause",
"title": "BSD 2-Clause \"Simplified\" or \"FreeBSD\" License (BSD-2-Clause)"
},
{
"name": "BSD-3-Clause",
"path": "https://opensource.org/licenses/BSD-3-Clause",
"title": "BSD 3-Clause \"New\" or \"Revised\" License (BSD-3-Clause)"
},
{
"name": "BSL-1.0",
"path": "https://opensource.org/licenses/BSL-1.0",
"title": "Boost Software License 1.0"
},
{
"name": "BitTorrent-1.1",
"path": "https://spdx.org/licenses/BitTorrent-1.1",
"title": "BitTorrent Open Source License 1.1"
},
{
"name": "CATOSL-1.1",
"path": "https://opensource.org/licenses/CATOSL-1.1",
"title": "Computer Associates Trusted Open Source License 1.1 (CATOSL-1.1)"
},
{
"name": "CC-BY-4.0",
"path": "https://creativecommons.org/licenses/by/4.0/",
"title": "Creative Commons Attribution 4.0"
},
{
"name": "CC-BY-NC-4.0",
"path": "https://creativecommons.org/licenses/by-nc/4.0/",
"title": "Creative Commons Attribution-NonCommercial 4.0"
},
{
"name": "CC-BY-NC-ND-4.0",
"path": "https://creativecommons.org/licenses/by-nc-nd/4.0/",
"title": "Attribution-NonCommercial-NoDerivatives 4.0"
},
{
"name": "CC-BY-NC-SA-4.0",
"path": "https://creativecommons.org/licenses/by-nc-sa/4.0/",
"title": "Attribution-NonCommercial-ShareAlike 4.0"
},
{
"name": "CC-BY-ND-4.0",
"path": "https://creativecommons.org/licenses/by-nd/4.0/",
"title": "Attribution-NoDerivatives 4.0"
},
{
"name": "CC-BY-SA-4.0",
"path": "https://creativecommons.org/licenses/by-sa/4.0/",
"title": "Creative Commons Attribution Share-Alike 4.0"
},
{
"name": "CC0-1.0",
"path": "https://creativecommons.org/publicdomain/zero/1.0/",
"title": "CC0 1.0"
},
{
"name": "CDDL-1.0",
"path": "https://opensource.org/licenses/CDDL-1.0",
"title": "Common Development and Distribution License 1.0"
},
{
"name": "CECILL-2.1",
"path": "https://opensource.org/licenses/CECILL-2.1",
"title": "CeCILL License 2.1"
},
{
"name": "CNRI-Python",
"path": "https://opensource.org/licenses/CNRI-Python",
"title": "CNRI Python License"
},
{
"name": "CPAL-1.0",
"path": "https://opensource.org/licenses/CPAL-1.0",
"title": "Common Public Attribution License 1.0"
},
{
"name": "CUA-OPL-1.0",
"path": "https://opensource.org/licenses/CUA-OPL-1.0",
"title": "CUA Office Public License 1.0"
},
{
"name": "DSL",
"path": "https://opendefinition.org/licenses/dsl",
"title": "Design Science License"
},
{
"name": "ECL-2.0",
"path": "https://opensource.org/licenses/ECL-2.0",
"title": "Educational Community License 2.0"
},
{
"name": "EFL-2.0",
"path": "https://opensource.org/licenses/EFL-2.0",
"title": "Eiffel Forum License 2.0"
},
{
"name": "EPL-1.0",
"path": "https://opensource.org/licenses/EPL-1.0",
"title": "Eclipse Public License 1.0"
},
{
"name": "EPL-2.0",
"path": "https://opensource.org/licenses/EPL-2.0",
"title": "Eclipse Public License 2.0"
},
{
"name": "EUDatagrid",
"path": "https://opensource.org/licenses/EUDatagrid",
"title": "EU DataGrid Software License"
},
{
"name": "EUPL-1.1",
"path": "https://opensource.org/licenses/EUPL-1.1",
"title": "European Union Public License 1.1"
},
{
"name": "Entessa",
"path": "https://opensource.org/licenses/Entessa",
"title": "Entessa Public License"
},
{
"name": "FAL-1.3",
"path": "https://opendefinition.org/licenses/fal",
"title": "Free Art License 1.3"
},
{
"name": "Fair",
"path": "https://opensource.org/licenses/Fair",
"title": "Fair License"
},
{
"name": "Frameworx-1.0",
"path": "https://opensource.org/licenses/Frameworx-1.0",
"title": "Frameworx License 1.0"
},
{
"name": "GFDL-1.3-no-cover-texts-no-invariant-sections",
"path": "https://opendefinition.org/licenses/gfdl",
"title": "GNU Free Documentation License 1.3 with no cover texts and no invariant sections"
},
{
"name": "GPL-2.0",
"path": "https://opensource.org/licenses/GPL-2.0",
"title": "GNU General Public License 2.0"
},
{
"name": "GPL-3.0",
"path": "https://opensource.org/licenses/GPL-3.0",
"title": "GNU General Public License 3.0"
},
{
"name": "HPND",
"path": "https://opensource.org/licenses/HPND",
"title": "Historical Permission Notice and Disclaimer"
},
{
"name": "IPA",
"path": "https://opensource.org/licenses/IPA",
"title": "IPA Font License"
},
{
"name": "IPL-1.0",
"path": "https://opensource.org/licenses/IPL-1.0",
"title": "IBM Public License 1.0"
},
{
"name": "ISC",
"path": "https://opensource.org/licenses/ISC",
"title": "ISC License"
},
{
"name": "Intel",
"path": "https://opensource.org/licenses/Intel",
"title": "Intel Open Source License"
},
{
"name": "LGPL-2.1",
"path": "https://opensource.org/licenses/LGPL-2.1",
"title": "GNU Lesser General Public License 2.1"
},
{
"name": "LGPL-3.0",
"path": "https://opensource.org/licenses/LGPL-3.0",
"title": "GNU Lesser General Public License 3.0"
},
{
"name": "LO-FR-2.0",
"path": "https://www.etalab.gouv.fr/licence-ouverte-open-licence",
"title": "Open License 2.0"
},
{
"name": "LPL-1.0",
"path": "https://opensource.org/licenses/LPL-1.0",
"title": "Lucent Public License (\"Plan9\") 1.0"
},
{
"name": "LPL-1.02",
"path": "https://opensource.org/licenses/LPL-1.02",
"title": "Lucent Public License 1.02"
},
{
"name": "LPPL-1.3c",
"path": "https://opensource.org/licenses/LPPL-1.3c",
"title": "LaTeX Project Public License 1.3c"
},
{
"name": "MIT",
"path": "https://opensource.org/licenses/MIT",
"title": "MIT License"
},
{
"name": "MPL-1.0",
"path": "https://opensource.org/licenses/MPL-1.0",
"title": "Mozilla Public License 1.0"
},
{
"name": "MPL-1.1",
"path": "https://opensource.org/licenses/MPL-1.1",
"title": "Mozilla Public License 1.1"
},
{
"name": "MPL-2.0",
"path": "https://opensource.org/licenses/MPL-2.0",
"title": "Mozilla Public License 2.0"
},
{
"name": "MS-PL",
"path": "https://opensource.org/licenses/MS-PL",
"title": "Microsoft Public License"
},
{
"name": "MS-RL",
"path": "https://opensource.org/licenses/MS-RL",
"title": "Microsoft Reciprocal License"
},
{
"name": "MirOS",
"path": "https://opensource.org/licenses/MirOS",
"title": "MirOS Licence"
},
{
"name": "Motosoto",
"path": "https://opensource.org/licenses/Motosoto",
"title": "Motosoto License"
},
{
"name": "Multics",
"path": "https://opensource.org/licenses/Multics",
"title": "Multics License"
},
{
"name": "NASA-1.3",
"path": "https://opensource.org/licenses/NASA-1.3",
"title": "NASA Open Source Agreement 1.3"
},
{
"name": "NCSA",
"path": "https://opensource.org/licenses/NCSA",
"title": "University of Illinois/NCSA Open Source License"
},
{
"name": "NGPL",
"path": "https://opensource.org/licenses/NGPL",
"title": "Nethack General Public License"
},
{
"name": "NPOSL-3.0",
"path": "https://opensource.org/licenses/NPOSL-3.0",
"title": "Non-Profit Open Software License 3.0"
},
{
"name": "NTP",
"path": "https://opensource.org/licenses/NTP",
"title": "NTP License"
},
{
"name": "Naumen",
"path": "https://opensource.org/licenses/Naumen",
"title": "Naumen Public License"
},
{
"name": "Nokia",
"path": "https://opensource.org/licenses/Nokia",
"title": "Nokia Open Source License"
},
{
"name": "OCLC-2.0",
"path": "https://opensource.org/licenses/OCLC-2.0",
"title": "OCLC Research Public License 2.0"
},
{
"name": "ODC-BY-1.0",
"path": "https://opendefinition.org/licenses/odc-by",
"title": "Open Data Commons Attribution License 1.0"
},
{
"name": "ODbL-1.0",
"path": "https://opendefinition.org/licenses/odc-odbl",
"title": "Open Data Commons Open Database License 1.0"
},
{
"name": "OFL-1.1",
"path": "https://opensource.org/licenses/OFL-1.1",
"title": "Open Font License 1.1"
},
{
"name": "OGL-Canada-2.0",
"path": "https://open.canada.ca/en/open-government-licence-canada",
"title": "Open Government License 2.0 (Canada)"
},
{
"name": "OGL-UK-1.0",
"path": "https://www.nationalarchives.gov.uk/doc/open-government-licence/version/1/",
"title": "Open Government Licence 1.0 (United Kingdom)"
},
{
"name": "OGL-UK-2.0",
"path": "https://www.nationalarchives.gov.uk/doc/open-government-licence/version/2/",
"title": "Open Government Licence 2.0 (United Kingdom)"
},
{
"name": "OGL-UK-3.0",
"path": "https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/",
"title": "Open Government Licence 3.0 (United Kingdom)"
},
{
"name": "OGTSL",
"path": "https://opensource.org/licenses/OGTSL",
"title": "Open Group Test Suite License"
},
{
"name": "OSL-3.0",
"path": "https://opensource.org/licenses/OSL-3.0",
"title": "Open Software License 3.0"
},
{
"name": "PDDL-1.0",
"path": "https://opendefinition.org/licenses/odc-pddl",
"title": "Open Data Commons Public Domain Dedication and Licence 1.0"
},
{
"name": "PHP-3.0",
"path": "https://opensource.org/licenses/PHP-3.0",
"title": "PHP License 3.0"
},
{
"name": "PostgreSQL",
"path": "https://opensource.org/licenses/PostgreSQL",
"title": "PostgreSQL License"
},
{
"name": "Python-2.0",
"path": "https://opensource.org/licenses/Python-2.0",
"title": "Python License 2.0"
},
{
"name": "QPL-1.0",
"path": "https://opensource.org/licenses/QPL-1.0",
"title": "Q Public License 1.0"
},
{
"name": "RPL-1.5",
"path": "https://opensource.org/licenses/RPL-1.5",
"title": "Reciprocal Public License 1.5"
},
{
"name": "RPSL-1.0",
"path": "https://opensource.org/licenses/RPSL-1.0",
"title": "RealNetworks Public Source License 1.0"
},
{
"name": "RSCPL",
"path": "https://opensource.org/licenses/RSCPL",
"title": "Ricoh Source Code Public License"
},
{
"name": "SISSL",
"path": "https://opensource.org/licenses/SISSL",
"title": "Sun Industry Standards Source License 1.1"
},
{
"name": "SPL-1.0",
"path": "https://opensource.org/licenses/SPL-1.0",
"title": "Sun Public License 1.0"
},
{
"name": "SimPL-2.0",
"path": "https://opensource.org/licenses/SimPL-2.0",
"title": "Simple Public License 2.0"
},
{
"name": "Sleepycat",
"path": "https://opensource.org/licenses/Sleepycat",
"title": "Sleepycat License"
},
{
"name": "Talis",
"path": "https://opendefinition.org/licenses/tcl",
"title": "Talis Community License"
},
{
"name": "Unlicense",
"path": "https://unlicense.org/",
"title": "Unlicense"
},
{
"name": "VSL-1.0",
"path": "https://opensource.org/licenses/VSL-1.0",
"title": "Vovida Software License 1.0"
},
{
"name": "W3C",
"path": "https://opensource.org/licenses/W3C",
"title": "W3C License"
},
{
"name": "WXwindows",
"path": "https://opensource.org/licenses/WXwindows",
"title": "wxWindows Library License"
},
{
"name": "Watcom-1.0",
"path": "https://opensource.org/licenses/Watcom-1.0",
"title": "Sybase Open Watcom Public License 1.0"
},
{
"name": "Xnet",
"path": "https://opensource.org/licenses/Xnet",
"title": "X.Net License"
},
{
"name": "ZPL-2.0",
"path": "https://opensource.org/licenses/ZPL-2.0",
"title": "Zope Public License 2.0"
},
{
"name": "Zlib",
"path": "https://opensource.org/licenses/Zlib",
"title": "zlib/libpng license"
},
{
"name": "dli-model-use",
"path": "http://data.library.ubc.ca/datalib/geographic/DMTI/license.html",
"title": "Statistics Canada: Data Liberation Initiative (DLI) - Model Data Use Licence"
},
{
"name": "geogratis",
"path": "http://geogratis.gc.ca/geogratis/licenceGG",
"title": "Geogratis"
},
{
"name": "hesa-withrights",
"path": "https://web.archive.org/web/20131009082944/http://www.hesa.ac.uk/index.php?option=com_content&task=view&id=2619&Itemid=209",
"title": "Higher Education Statistics Agency Copyright with data.gov.uk rights"
},
{
"name": "localauth-withrights",
"path": "",
"title": "Local Authority Copyright with data.gov.uk rights"
},
{
"name": "met-office-cp",
"path": "https://www.metoffice.gov.uk/climatechange/science/monitoring/ukcp09/UKCIP08_license_agreement_130709.pdf",
"title": "Met Office UK Climate Projections Licence Agreement"
},
{
"name": "mitre",
"path": "https://opensource.org/licenses/CVW",
"title": "MITRE Collaborative Virtual Workspace License (CVW License)"
},
{
"name": "notspecified",
"path": "",
"title": "License Not Specified"
},
{
"name": "other-at",
"path": "",
"title": "Other (Attribution)"
},
{
"name": "other-closed",
"path": "",
"title": "Other (Not Open)"
},
{
"name": "other-nc",
"path": "",
"title": "Other (Non-Commercial)"
},
{
"name": "other-open",
"path": "",
"title": "Other (Open)"
},
{
"name": "other-pd",
"path": "",
"title": "Other (Public Domain)"
},
{
"name": "ukclickusepsi",
"path": "",
"title": "UK Click Use PSI"
},
{
"name": "ukcrown",
"path": "",
"title": "UK Crown Copyright"
},
{
"name": "ukcrown-withrights",
"path": "",
"title": "UK Crown Copyright with data.gov.uk rights"
},
{
"name": "ukpsi",
"path": "https://opendefinition.org/licenses/ukpsi",
"title": "UK PSI Public Sector Information"
}
]
================================================
FILE: src/ode/assets/style.qss
================================================
/* Open Data Editor main Style Sheet.
The application uses a fusion style set when creating the QMainWindow object. This stylesheets
complements the fusion style with colors and branding. Open Data Editor is design for light-mode
so we are enforcing it by explicitly setting the main widgets and components background to white.
*/
/* Enforcing Light Mode of main elements. */
Content, FrictionlessResourceMetadataWidget, SelectWidget, Toolbar,
FieldsForm, SingleFieldForm, SchemaForm, QWidget#fields_form_container,
QTableView, QTableView QHeaderView, QTableView QTableCornerButton,
QTreeView, QDialog, QPushButton, QComboBox, QComboBox QAbstractItemView, QLineEdit,
QLabel, QTabWidget, QTabBar, QScrollBar, QSpinBox, QListWidget, QTextEdit, QSrollArea,
QPlainTextEdit, QWidget#central_widget, QGroupBox {
background: #FFF;
color: #404040;
font-size: 19px; /* multi-os support */
}
* { gridline-color: lightgrey; } /* multi-os support */
.QComboBox:disabled, QLineEdit:disabled {
background: #f0f0f0;
}
ErrorsReportButton {
border: 0;
}
ErrorsReportButton:hover {
background-color: #F0F0F0;
}
ErrorsReportButton QLabel {
font-size: 16px;
font-weight: 600;
color: #4C5564;
background: transparent;
}
ErrorsReportButton QLabel:disabled {
color: grey;
}
ErrorsReportButton QLabel[error="true"] {
border: 1px solid #FECBCA;
color: #D32F2F;
padding: 0px;
}
Toolbar QPushButton {
font-size: 16px;
font-weight: 600;
border: 0px solid;
color: #4C5564;
background: #FFFFFF;
padding: 6px 8px;
}
Toolbar QPushButton:hover {
background-color: #F0F0F0;
}
Toolbar QPushButton:pressed {
background-color: #F0F0F0;
}
Toolbar QPushButton[active="true"]{
border: 2px solid #0078D7;
background-color: #F0F0F0;
outline: none;
}
Toolbar QComboBox#excelSheetCombo,
Toolbar QLabel#excelSheetLabel {
font-size: 16px;
font-weight: 600;
color: #4C5564;
}
QPushButton#button_save, QPushButton#button_export, QPushButton#button_ai {
font-size: 14px;
font-weight: 500;
color: #FFF;
background-color: #000;
border-style: outset;
border-width: 1px;
border-radius: 4px;
border-color: #000;
padding: 6px 8px;
}
QPushButton#button_save:hover, QPushButton#button_export:hover, QPushButton#button_ai:hover {
color: #FFF;
background: #0288D1;
border-color: #0288d1;
}
QPushButton#button_save:pressed, QPushButton#button_export:pressed, QPushButton#button_ai:pressed {
color: #FFF;
background-color: #000;
border-color: #FFF;
}
QPushButton#button_save:disabled, QPushButton#button_export:disabled, QPushButton#button_ai:disabled {
background-color: #F0F0F0;
border-color: #F0F0F0;
color: #4C5564;
}
Sidebar {
border-right: 1px solid #000;
}
Sidebar QPushButton#button_upload {
font-size: 14px;
font-weight: 500;
text-align: center;
color: #0288D1;
border-style: outset;
border-width: 1px;
border-radius: 4px;
border-color: #0288d1;
padding-top: 10px;
padding-bottom: 10px;
padding-left: 15px;
padding-right: 15px;
margin-top: 22px;
margin-bottom: 22px;
}
Sidebar QPushButton#button_upload:hover {
color: #FFF;
background: #0288D1;
}
Sidebar QPushButton#button_upload:pressed {
color: #0288D1;
background: #FFF;
}
Sidebar QTreeView {
border: 1px solid #d0d0d0;
}
Sidebar QTreeView::item:hover {
color: #FFF;
background: black;
}
Sidebar QTreeView::item:selected {
color: #FFF;
background: gray;
}
Sidebar QPushButton {
border: 0px;
padding: 3px;
text-align: left;
}
Sidebar QPushButton:hover {
background: #D0D0D0;
}
Sidebar QPushButton:pressed {
background: #FFFFFF;
}
Welcome QPushButton {
font-size: 14px;
font-weight: 500;
color: #FFFFFF;
background: #000000;
border-style: outset;
border-width: 1px;
border-radius: 4px;
padding: 10px 15px;
}
Welcome QPushButton:hover {
color: #FFFFFF;
background: #0288D1;
border-color: #0288D1;
}
Welcome QPushButton:pressed {
color: #FFFFFF;
background: #000000;
border-color: #000000;
}
FieldsForm QScrollArea {
border: none;
}
QHeaderView::section {
background-color: rgb(240, 240, 240);
border: 1px solid rgb(200, 200, 200);
}
QTableCornerButton::section {
background-color: rgb(240, 240, 240);
border: 1px solid rgb(200, 200, 200);
}
DataUploadDialog,
DeleteDialog,
RenameDialog,
DownloadDialog,
LlamaDialog,
LLMWarningDialog,
ColumnMetadataDialog,
LlamaDownloadingDialog {
border: 2px solid #000000;
}
DataUploadDialog QPushButton,
DeleteDialog QPushButton,
RenameDialog QPushButton,
DownloadDialog QPushButton,
LLMWarningDialog QPushButton,
ColumnMetadataDialog QPushButton,
LlamaDialog QPushButton,
LlamaDownloadDialog QPushButton {
border: 2px solid #000000;
background-color: #F0F0F0;
font-size: 16px;
font-weight: 500;
color: #FFFFFF;
background: #000000;
border-style: outset;
border-width: 1px;
border-radius: 4px;
padding: 10px 15px;
text-align: center;
}
DeleteDialog QPushButton,
RenameDialog QPushButton,
DownloadDialog QPushButton,
LLMWarningDialog QPushButton,
ColumnMetadataDialog QPushButton,
LlamaDialog QPushButton,
LlamaDownloadDialog QPushButton {
padding: 5px;
}
DataUploadDialog QPushButton:hover,
DeleteDialog QPushButton:hover,
RenameDialog QPushButton:hover,
DownloadDialog QPushButton:hover,
ColumnMetadataDialog QPushButton:hover,
LLMWarningDialog QPushButton:hover,
LlamaDialog QPushButton:hover,
LlamaDownloadDialog QPushButton:hover {
background: #0288D1;
border-color: #0288d1;
}
DataUploadDialog QPushButton:pressed,
DeleteDialog QPushButton:pressed,
RenameDialog QPushButton:pressed,
DownloadDialog QPushButton:pressed,
ColumnMetadataDialog QPushButton:pressed,
LLMWarningDialog QPushButton:pressed,
LlamaDialog QPushButton:pressed,
LlamaDownloadDialog QPushButton:pressed {
color: #FFFFFF;
background: #000000;
border-color: #000000;
}
DataUploadDialog QPushButton:disabled,
DeleteDialog QPushButton:disabled,
RenameDialog QPushButton:disabled,
DownloadDialog QPushButton:disabled,
ColumnMetadataDialog QPushButton:disabled,
LLMWarningDialog QPushButton:disabled,
LlamaDialog QPushButton:disabled,
LlamaDownloadDialog QPushButton:disabled {
background-color: #F0F0F0;
border-color: #F0F0F0;
color: #4C5564;
}
QTableView::item:selected {
background: white;
color: black;
border: 2px solid #646464;
}
/* Keyboard Focus Styles for Accessibility */
/* General focus styles for primary UI components */
QPushButton:focus {
border: 2px solid #0078D7;
outline: none;
}
QLineEdit:focus {
border: 2px solid #0078D7;
outline: none;
}
QComboBox:focus {
border: 2px solid #0078D7;
outline: none;
}
QTreeView:focus {
border: 2px solid #0078D7;
outline: none;
}
QTableView:focus {
border: 2px solid #0078D7;
outline: none;
}
/* Focus styles for save/publish buttons */
QPushButton#button_save:focus, QPushButton#button_export:focus {
border: 3px solid #0078D7;
outline: none;
}
/* Focus styles for sidebar elements */
Sidebar QPushButton#button_upload:focus {
border: 2px solid #0078D7;
outline: none;
}
Sidebar QPushButton:focus {
border: 2px solid #0078D7;
background: #D0D0D0;
outline: none;
}
Sidebar QTreeView:focus {
border: 2px solid #0078D7;
outline: none;
}
/* Focus styles for welcome screen buttons */
Welcome QPushButton:focus {
border: 2px solid #0078D7;
outline: none;
}
/* Focus styles for dialog buttons */
DataUploadDialog QPushButton:focus,
DeleteDialog QPushButton:focus,
RenameDialog QPushButton:focus,
DownloadDialog QPushButton:focus,
ColumnMetadataDialog QPushButton:focus,
LLMWarningDialog QPushButton:focus,
LlamaDialog QPushButton:focus,
LlamaDownloadDialog QPushButton:focus {
border: 2px solid #0078D7;
outline: none;
}
/* Focus styles for table items */
QTableView::item:focus {
border: 2px solid #0078D7;
outline: none;
}
LLMWarningDialog QCheckBox {
color: #404040;
font-size: 18px;
background-color: white;
}
QPushButton#deleteButton {
background-color: black;
color: white;
}
QPushButton#deleteButton:hover {
background-color: #0078D7;
}
QPushButton#deleteButton:disabled {
color: gray;
background-color: #f0f0f0;
}
/* Download Button */
QPushButton#downloadButton {
background-color: #black;
color: white;
}
QPushButton#downloadButton:hover {
background-color: #0078D7;
}
QPushButton#downloadButton:disabled {
color: gray;
background-color: #f0f0f0;
}
QWidget#llamaDownloadModelRow {
border: 1px solid #ccc;
border-radius: 4px;
background-color: #f9f9f9;
margin: 2px;
}
================================================
FILE: src/ode/assets/translations/de.ts
================================================
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="de_DE">
<context>
<name>ColumnMetadataDialog</name>
<message>
<location filename="../../dialogs/metadata.py" line="276"/>
<source>Column name cannot be empty</source>
<translation>Spaltenname darf nicht leer sein</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="281"/>
<source>There is another column in the table with the same name. Please choose a different one</source>
<translation>Es gibt bereits eine Spalte in der Tabelle mit demselben Namen. Bitte wählen Sie einen anderen</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="306"/>
<source>Save</source>
<translation>Speichern</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="307"/>
<source>Cancel</source>
<translation>Abbrechen</translation>
</message>
</context>
<context>
<name>DataUploadDialog</name>
<message>
<location filename="../../dialogs/upload.py" line="163"/>
<source>Please paste a valid URL.</source>
<translation>Bitte fügen Sie eine gültige URL ein.</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="166"/>
<source>Please paste a valid URL starting with http:// or https://.</source>
<translation>Bitte fügen Sie eine gültige URL ein, die mit http:// oder https:// beginnt.</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="176"/>
<source>Error: The Google Sheets URL is not valid or the table is not publicly available.</source>
<translation>Fehler: Die Google Sheets URL ist nicht gültig oder die Tabelle ist nicht öffentlich verfügbar.</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="187"/>
<source>Error: The URL is not associated with a table</source>
<translation>Fehler: Die URL ist nicht mit einer Tabelle verknüpft</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="216"/>
<source>Upload your data</source>
<translation>Laden Sie Ihre Daten hoch</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="217"/>
<source>Add one or more Excel or csv files</source>
<translation>Fügen Sie eine oder mehrere Excel- oder CSV-Dateien hinzu</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="218"/>
<source>Add a folder</source>
<translation>Einen Ordner hinzufügen</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="226"/>
<source>Paste your Google Sheet or csv link to create a local copy in the Open Data Editor</source>
<translation>Fügen Sie Ihren Google Sheet oder CSV-Link ein, um eine lokale Kopie im Open Data Editor zu erstellen</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="228"/>
<source>Add Local Files</source>
<translation>Lokale Dateien hinzufügen</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="219"/>
<location filename="../../dialogs/upload.py" line="220"/>
<source>Select</source>
<translation>Auswählen</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="221"/>
<source>Link to the external table: </source>
<translation>Link zur externen Tabelle: </translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="222"/>
<source>Enter or paste URL</source>
<translation>URL eingeben oder einfügen</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="227"/>
<source>Add</source>
<translation>Hinzufügen</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="229"/>
<source>Add External Data</source>
<translation>Externe Daten hinzufügen</translation>
</message>
</context>
<context>
<name>DataViewer</name>
<message>
<location filename="../../panels/data.py" line="557"/>
<source>Preview not available for this item.</source>
<translation>Vorschau für dieses Element nicht verfügbar.</translation>
</message>
</context>
<context>
<name>DataWorker</name>
<message>
<location filename="../../panels/data.py" line="67"/>
<source>Reading file...</source>
<translation>Datei wird gelesen...</translation>
</message>
<message>
<location filename="../../panels/data.py" line="69"/>
<source>Checking errors...</source>
<translation>Fehler werden überprüft...</translation>
</message>
<message>
<location filename="../../panels/data.py" line="72"/>
<source>Drawing table...</source>
<translation>Tabelle wird gezeichnet...</translation>
</message>
<message>
<location filename="../../panels/data.py" line="82"/>
<source>Read and error checking finished.</source>
<translation>Lesen und Fehlerüberprüfung abgeschlossen.</translation>
</message>
</context>
<context>
<name>DeleteDialog</name>
<message>
<location filename="../../dialogs/delete.py" line="43"/>
<source>Cancel</source>
<translation>Abbrechen</translation>
</message>
<message>
<location filename="../../dialogs/delete.py" line="44"/>
<source>Ok</source>
<translation>OK</translation>
</message>
<message>
<location filename="../../dialogs/delete.py" line="45"/>
<source>Are you sure you want to delete this item?</source>
<translation>Sind Sie sicher, dass Sie dieses Element löschen möchten?</translation>
</message>
<message>
<location filename="../../dialogs/delete.py" line="46"/>
<source>Delete file</source>
<translation>Datei löschen</translation>
</message>
</context>
<context>
<name>DownloadDialog</name>
<message>
<location filename="../../dialogs/download.py" line="48"/>
<source>Download</source>
<translation>Herunterladen</translation>
</message>
<message>
<location filename="../../dialogs/download.py" line="49"/>
<source>Please, select one of the following options:</source>
<translation>Bitte wählen Sie eine der folgenden Optionen:</translation>
</message>
<message>
<location filename="../../dialogs/download.py" line="50"/>
<source>Download file</source>
<translation>Datei herunterladen</translation>
</message>
<message>
<location filename="../../dialogs/download.py" line="51"/>
<source>Download file with errors</source>
<translation>Datei mit Fehlern herunterladen</translation>
</message>
<message>
<location filename="../../dialogs/download.py" line="63"/>
<source>File downloaded successfully to:
{}</source>
<translation>Datei erfolgreich heruntergeladen nach:
{}</translation>
</message>
<message>
<location filename="../../dialogs/download.py" line="64"/>
<source>Success</source>
<translation>Erfolg</translation>
</message>
<message>
<location filename="../../dialogs/download.py" line="67"/>
<source>Error downloading file:
{}</source>
<translation>Fehler beim Herunterladen der Datei:
{}</translation>
</message>
</context>
<context>
<name>ErrorsMessages</name>
<message>
<location filename="../../utils.py" line="121"/>
<location filename="../../utils.py" line="127"/>
<source>Missing header</source>
<translation>Fehlende Kopfzeile</translation>
</message>
<message>
<location filename="../../utils.py" line="122"/>
<source>Duplicated header</source>
<translation>Doppelte Kopfzeile</translation>
</message>
<message>
<location filename="../../utils.py" line="123"/>
<source>Empty row</source>
<translation>Leere Zeile</translation>
</message>
<message>
<location filename="../../utils.py" line="124"/>
<source>Type mismatch</source>
<translation>Typkonflikt</translation>
</message>
<message>
<location filename="../../utils.py" line="125"/>
<source>Missing value</source>
<translation>Fehlender Wert</translation>
</message>
<message>
<location filename="../../utils.py" line="126"/>
<source>Extra cell</source>
<translation>Zusätzliche Zelle</translation>
</message>
<message>
<location filename="../../utils.py" line="128"/>
<source>Blank Label</source>
<translation>Leeres Label</translation>
</message>
<message>
<location filename="../../utils.py" line="137"/>
<location filename="../../utils.py" line="143"/>
<source>A column in the header row has no name. Every column should have a unique, non-empty header.</source>
<translation>Eine Spalte in der Kopfzeile hat keinen Namen. Jede Spalte sollte einen eindeutigen, nicht-leeren Kopfzeilennamen haben.</translation>
</message>
<message>
<location filename="../../utils.py" line="138"/>
<source>Two or more columns share the same name. Column names must be unique.</source>
<translation>Zwei oder mehr Spalten teilen sich denselben Namen. Spaltennamen müssen eindeutig sein.</translation>
</message>
<message>
<location filename="../../utils.py" line="139"/>
<source>This row has no data. Rows should contain at least one cell with data.</source>
<translation>Diese Zeile enthält keine Daten. Zeilen sollten mindestens eine Zelle mit Daten enthalten.</translation>
</message>
<message>
<location filename="../../utils.py" line="140"/>
<source>A cell value doesn't match the expected data type or format for the column.</source>
<translation>Ein Zellenwert entspricht nicht dem erwarteten Datentyp oder Format für die Spalte.</translation>
</message>
<message>
<location filename="../../utils.py" line="141"/>
<source>This cell is missing data</source>
<translation>Diese Zelle enthält keine Daten</translation>
</message>
<message>
<location filename="../../utils.py" line="142"/>
<source>This row has more values compared to the header row.</source>
<translation>Diese Zeile hat mehr Werte im Vergleich zur Kopfzeile.</translation>
</message>
<message>
<location filename="../../utils.py" line="144"/>
<source>A label in the header row is missing a value. Label should be provided and not be blank.</source>
<translation>Ein Label in der Kopfzeile hat keinen Wert. Das Label sollte angegeben werden und nicht leer sein.</translation>
</message>
</context>
<context>
<name>ErrorsWidget</name>
<message>
<location filename="../../panels/errors.py" line="220"/>
<source>Please note that the ODE currently detects errors in tables, with a maximum of </source>
<translation>Bitte beachten Sie, dass der ODE derzeit Fehler in Tabellen erkennt, mit einem Maximum von </translation>
</message>
</context>
<context>
<name>FrictionlessTableModel</name>
<message>
<location filename="../../panels/data.py" line="249"/>
<source>Data</source>
<translation>Daten</translation>
</message>
</context>
<context>
<name>LLMWarningDialog</name>
<message>
<location filename="../../dialogs/llm_dialog_warning.py" line="13"/>
<source>AI assistant</source>
<translation>KI-Assistent</translation>
</message>
<message>
<location filename="../../dialogs/llm_dialog_warning.py" line="25"/>
<source>Welcome to the ODE's AI assistant! This feature will help you generating better descriptions for the columns of your table and also questions for data analysis.
To get started, you will need to install the AI file in your computer. Once installed, everything will run locally, meaning your data always stays private and secure. Learn more</source>
<translation>Willkommen beim KI-Assistenten des ODE! Diese Funktion hilft Ihnen dabei, bessere Beschreibungen für die Spalten Ihrer Tabelle zu generieren und auch Fragen zur Datenanalyse.
Um zu beginnen, müssen Sie die KI-Datei auf Ihrem Computer installieren. Nach der Installation läuft alles lokal, was bedeutet, dass Ihre Daten immer privat und sicher bleiben. Mehr erfahren</translation>
</message>
<message>
<location filename="../../dialogs/llm_dialog_warning.py" line="28"/>
<source>Don't show again</source>
<translation>Nicht mehr anzeigen</translation>
</message>
<message>
<location filename="../../dialogs/llm_dialog_warning.py" line="33"/>
<source>Cancel</source>
<translation>Abbrechen</translation>
</message>
<message>
<location filename="../../dialogs/llm_dialog_warning.py" line="40"/>
<source>Ok</source>
<translation>OK</translation>
</message>
</context>
<context>
<name>LlamaDialog</name>
<message>
<location filename="../../llama.py" line="282"/>
<source>Generating response...</source>
<translation>Antwort wird generiert...</translation>
</message>
<message>
<location filename="../../llama.py" line="288"/>
<location filename="../../llama.py" line="298"/>
<location filename="../../llama.py" line="314"/>
<source>Execute</source>
<translation>Ausführen</translation>
</message>
<message>
<location filename="../../llama.py" line="299"/>
<source>Error</source>
<translation>Fehler</translation>
</message>
<message>
<location filename="../../llama.py" line="313"/>
<source>AI assistant</source>
<translation>KI-Assistent</translation>
</message>
<message>
<location filename="../../llama.py" line="315"/>
<source>Stop execution</source>
<translation>Ausführung stoppen</translation>
</message>
<message>
<location filename="../../llama.py" line="316"/>
<source>Results will be displayed here...</source>
<translation>Ergebnisse werden hier angezeigt...</translation>
</message>
</context>
<context>
<name>LlamaDownloadDialog</name>
<message>
<location filename="../../llama.py" line="348"/>
<source>AI assistant</source>
<translation>KI-Assistent</translation>
</message>
<message>
<location filename="../../llama.py" line="352"/>
<source>To start using the AI assistant, please download the following model.</source>
<translation>Um den KI-Assistenten zu verwenden, laden Sie bitte das folgende Modell herunter.</translation>
</message>
<message>
<location filename="../../llama.py" line="359"/>
<source>The ODE will save the file in this location: <i><a href="file://{AI_MODELS_PATH}">{AI_MODELS_PATH}</a></i></source>
<translation>Der ODE wird die Datei an diesem Speicherort speichern: <i><a href="file://{AI_MODELS_PATH}">{AI_MODELS_PATH}</a></i></translation>
</message>
<message>
<location filename="../../llama.py" line="373"/>
<source>Next</source>
<translation>Weiter</translation>
</message>
<message>
<location filename="../../llama.py" line="398"/>
<source>Delete</source>
<translation>Löschen</translation>
</message>
<message>
<location filename="../../llama.py" line="404"/>
<source>Download</source>
<translation>Herunterladen</translation>
</message>
<message>
<location filename="../../llama.py" line="457"/>
<source>File exists</source>
<translation>Datei existiert</translation>
</message>
<message>
<location filename="../../llama.py" line="458"/>
<source>Do you want to delete it and download it again?</source>
<translation>Möchten Sie sie löschen und erneut herunterladen?</translation>
</message>
<message>
<location filename="../../llama.py" line="474"/>
<source>Downloading model</source>
<translation>Modell wird heruntergeladen</translation>
</message>
<message>
<location filename="../../llama.py" line="474"/>
<source>Cancel</source>
<translation>Abbrechen</translation>
</message>
<message>
<location filename="../../llama.py" line="475"/>
<source>LLM Model Download Progress</source>
<translation>LLM-Modell Download-Fortschritt</translation>
</message>
<message>
<location filename="../../llama.py" line="486"/>
<location filename="../../llama.py" line="553"/>
<source>Error Occurred</source>
<translation>Fehler ist aufgetreten</translation>
</message>
<message>
<location filename="../../llama.py" line="494"/>
<source>Confirm Deletion</source>
<translation>Löschung bestätigen</translation>
</message>
<message>
<location filename="../../llama.py" line="495"/>
<source>Are you sure you want to delete {AI_MODEL.name}?</source>
<translation>Sind Sie sicher, dass Sie {AI_MODEL.name} löschen möchten?</translation>
</message>
</context>
<context>
<name>LoadingDialog</name>
<message>
<location filename="../../dialogs/loading.py" line="30"/>
<source>Loading</source>
<translation>Laden</translation>
</message>
<message>
<location filename="../../dialogs/loading.py" line="31"/>
<source>Loading...</source>
<translation>Laden...</translation>
</message>
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../../main.py" line="817"/>
<source>Error</source>
<translation>Fehler</translation>
</message>
<message>
<location filename="../../main.py" line="864"/>
<source>File</source>
<translation>Datei</translation>
</message>
<message>
<location filename="../../main.py" line="684"/>
<source>Ready.</source>
<translation>Bereit.</translation>
</message>
<message>
<location filename="../../main.py" line="817"/>
<source>Error initializing the LLM:
</source>
<translation>Fehler beim Initialisieren des LLM:
</translation>
</message>
<message>
<location filename="../../main.py" line="865"/>
<source>Add</source>
<translation>Hinzufügen</translation>
</message>
<message>
<location filename="../../main.py" line="866"/>
<source>File/Folder</source>
<translation>Datei/Ordner</translation>
</message>
<message>
<location filename="../../main.py" line="867"/>
<source>External URL</source>
<translation>Externe URL</translation>
</message>
<message>
<location filename="../../main.py" line="870"/>
<source>View</source>
<translation>Ansicht</translation>
</message>
<message>
<location filename="../../main.py" line="871"/>
<source>Errors panel</source>
<translation>Fehlerpanel</translation>
</message>
<message>
<location filename="../../main.py" line="872"/>
<source>Source panel</source>
<translation>Quellpanel</translation>
</message>
<message>
<location filename="../../main.py" line="875"/>
<source>Help</source>
<translation>Hilfe</translation>
</message>
<message>
<location filename="../../main.py" line="876"/>
<source>User Guide</source>
<translation>Benutzerhandbuch</translation>
</message>
<message>
<location filename="../../main.py" line="877"/>
<source>Report an Issue</source>
<translation>Ein Problem melden</translation>
</message>
<message>
<location filename="../../main.py" line="878"/>
<source>View logs</source>
<translation>Logs anzeigen</translation>
</message>
<message>
<location filename="../../main.py" line="879"/>
<source>About</source>
<translation>Über</translation>
</message>
<message>
<location filename="../../main.py" line="915"/>
<source>Language changed.</source>
<translation>Sprache geändert.</translation>
</message>
<message>
<location filename="../../main.py" line="942"/>
<source>File and Metadata changes saved.</source>
<translation>Datei- und Metadatenänderungen gespeichert.</translation>
</message>
<message>
<location filename="../../main.py" line="1130"/>
<source>Last 100 Lines</source>
<translation>Letzte 100 Zeilen</translation>
</message>
<message>
<location filename="../../main.py" line="1150"/>
<source>Close</source>
<translation>Schließen</translation>
</message>
<message>
<location filename="../../main.py" line="1155"/>
<source>Copy to Clipboard</source>
<translation>In Zwischenablage kopieren</translation>
</message>
<message>
<location filename="../../main.py" line="1173"/>
<source>Downloading data with errors...</source>
<translation>Daten mit Fehlern werden heruntergeladen...</translation>
</message>
<message>
<location filename="../../main.py" line="1191"/>
<source>File downloaded successfully to:
{}</source>
<translation>Datei erfolgreich heruntergeladen nach:
{}</translation>
</message>
<message>
<location filename="../../main.py" line="1192"/>
<source>Success</source>
<translation>Erfolg</translation>
</message>
</context>
<context>
<name>MetadataForm</name>
<message>
<location filename="../../dialogs/metadata.py" line="189"/>
<source>Column Name:</source>
<translation>Spaltenname:</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="190"/>
<source>Data Type:</source>
<translation>Datentyp:</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="191"/>
<source>Description:</source>
<translation>Beschreibung:</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="192"/>
<source>Flag empty cells as errors?:</source>
<translation>Leere Zellen als Fehler markieren?:</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="193"/>
<source>Min. Characters in cells:</source>
<translation>Min. Zeichen in Zellen:</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="194"/>
<source>Max. Characters in cell</source>
<translation>Max. Zeichen in Zelle</translation>
</message>
</context>
<context>
<name>RenameDialog</name>
<message>
<location filename="../../dialogs/rename.py" line="50"/>
<source>Rename file</source>
<translation>Datei umbenennen</translation>
</message>
<message>
<location filename="../../dialogs/rename.py" line="51"/>
<source>Rename item to:</source>
<translation>Element umbenennen zu:</translation>
</message>
<message>
<location filename="../../dialogs/rename.py" line="52"/>
<source>Cancel</source>
<translation>Abbrechen</translation>
</message>
<message>
<location filename="../../dialogs/rename.py" line="53"/>
<source>OK</source>
<translation>OK</translation>
</message>
</context>
<context>
<name>Sidebar</name>
<message>
<location filename="../../main.py" line="257"/>
<source>Upload your data</source>
<translation>Laden Sie Ihre Daten hoch</translation>
</message>
<message>
<location filename="../../main.py" line="258"/>
<source>User guide</source>
<translation>Benutzerhandbuch</translation>
</message>
<message>
<location filename="../../main.py" line="259"/>
<source>Report an issue</source>
<translation>Ein Problem melden</translation>
</message>
<message>
<location filename="../../main.py" line="260"/>
<source>Rename</source>
<translation>Umbenennen</translation>
</message>
<message>
<location filename="../../main.py" line="261"/>
<source>Open File in Location</source>
<translation>Datei im Speicherort öffnen</translation>
</message>
<message>
<location filename="../../main.py" line="262"/>
<source>Delete</source>
<translation>Löschen</translation>
</message>
<message>
<location filename="../../main.py" line="310"/>
<source>Operation not permitted.</source>
<translation>Vorgang nicht zulässig.</translation>
</message>
<message>
<location filename="../../main.py" line="312"/>
<source>File with this name already exists.</source>
<translation>Datei mit diesem Namen existiert bereits.</translation>
</message>
<message>
<location filename="../../main.py" line="314"/>
<source>Item renamed successfuly.</source>
<translation>Element erfolgreich umbenannt.</translation>
</message>
<message>
<location filename="../../main.py" line="344"/>
<source>Item deleted successfuly.</source>
<translation>Element erfolgreich gelöscht.</translation>
</message>
<message>
<location filename="../../main.py" line="302"/>
<location filename="../../main.py" line="306"/>
<location filename="../../main.py" line="310"/>
<location filename="../../main.py" line="312"/>
<location filename="../../main.py" line="340"/>
<source>Error</source>
<translation>Fehler</translation>
</message>
<message>
<location filename="../../main.py" line="303"/>
<source>Source is a file but destination a directory.</source>
<translation>Die Quelle ist eine Datei, das Ziel jedoch ein Verzeichnis.</translation>
</message>
<message>
<location filename="../../main.py" line="307"/>
<source>Source is a directory but destination a file.</source>
<translation>Die Quelle ist ein Verzeichnis, das Ziel jedoch eine Datei.</translation>
</message>
</context>
<context>
<name>SourceViewer</name>
<message>
<location filename="../../panels/source.py" line="82"/>
<source>This view is only available for CSV files.</source>
<translation>Diese Ansicht ist nur für CSV-Dateien verfügbar.</translation>
</message>
</context>
<context>
<name>Toolbar</name>
<message>
<location filename="../../main.py" line="502"/>
<source>Data</source>
<translation>Daten</translation>
</message>
<message>
<location filename="../../main.py" line="503"/>
<source>Errors Report</source>
<translation>Fehlerbericht</translation>
</message>
<message>
<location filename="../../main.py" line="504"/>
<source>Source code</source>
<translation>Quellcode</translation>
</message>
<message>
<location filename="../../main.py" line="505"/>
<source>Export</source>
<translation>Exportieren</translation>
</message>
<message>
<location filename="../../main.py" line="506"/>
<source>Save changes</source>
<translation>Änderungen speichern</translation>
</message>
<message>
<location filename="../../main.py" line="507"/>
<source>AI</source>
<translation>KI</translation>
</message>
<message>
<location filename="../../main.py" line="508"/>
<source>Sheet:</source>
<translation>Blatt:</translation>
</message>
</context>
<context>
<name>Welcome</name>
<message>
<location filename="../../main.py" line="573"/>
<source>The ODE supports Excel & csv files</source>
<translation>Der ODE unterstützt Excel- & CSV-Dateien</translation>
</message>
<message>
<location filename="../../main.py" line="574"/>
<source>You can also add links to online tables</source>
<translation>Sie können auch Links zu Online-Tabellen hinzufügen</translation>
</message>
<message>
<location filename="../../main.py" line="575"/>
<source>Upload your data</source>
<translation>Laden Sie Ihre Daten hoch</translation>
</message>
</context>
</TS>
================================================
FILE: src/ode/assets/translations/es.ts
================================================
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="es_ES">
<context>
<name>ColumnMetadataDialog</name>
<message>
<location filename="../../dialogs/metadata.py" line="276"/>
<source>Column name cannot be empty</source>
<translation>El nombre de la columna no puede estar vacío</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="281"/>
<source>There is another column in the table with the same name. Please choose a different one</source>
<translation>Hay otra columna en la tabla con el mismo nombre. Por favor elige otro</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="306"/>
<source>Save</source>
<translation>Guardar</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="307"/>
<source>Cancel</source>
<translation>Cancelar</translation>
</message>
</context>
<context>
<name>DataUploadDialog</name>
<message>
<location filename="../../dialogs/upload.py" line="163"/>
<source>Please paste a valid URL.</source>
<translation>Por favor ingresa una URL válida.</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="166"/>
<source>Please paste a valid URL starting with http:// or https://.</source>
<translation>Por favor ingresa una URL válida que empiece con http:// o https://.</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="176"/>
<source>Error: The Google Sheets URL is not valid or the table is not publicly available.</source>
<translation>Error: La URL de la Planilla de Google no es válida o su contenido no es público.</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="187"/>
<source>Error: The URL is not associated with a table</source>
<translation>Error: La URL no está asociada a una tabla</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="216"/>
<source>Upload your data</source>
<translation>Carga tus archivos</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="217"/>
<source>Add one or more Excel or csv files</source>
<translation>Agrega uno o más archivos Excel of CSV</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="218"/>
<source>Add a folder</source>
<translation>Agrega una carpeta</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="219"/>
<location filename="../../dialogs/upload.py" line="220"/>
<source>Select</source>
<translation>Seleccionar</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="221"/>
<source>Link to the external table: </source>
<translation>Enlace a la tabla externa: </translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="222"/>
<source>Enter or paste URL</source>
<translation>Introduce o pega la URL</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="226"/>
<source>Paste your Google Sheet or csv link to create a local copy in the Open Data Editor</source>
<translation>Pega el enlace de tu hoja de cálculo de Google o CSV para crear una copia local en el Editor de Datos Abiertos</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="227"/>
<source>Add</source>
<translation>Agregar</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="228"/>
<source>Add Local Files</source>
<translation>Agregar archivos locales</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="229"/>
<source>Add External Data</source>
<translation>Agregar Datos Externos</translation>
</message>
</context>
<context>
<name>DataViewer</name>
<message>
<location filename="../../panels/data.py" line="557"/>
<source>Preview not available for this item.</source>
<translation>Vista previa no disponible para este ítem.</translation>
</message>
</context>
<context>
<name>DataWorker</name>
<message>
<location filename="../../panels/data.py" line="67"/>
<source>Reading file...</source>
<translation>Leyendo el archivo...</translation>
</message>
<message>
<location filename="../../panels/data.py" line="69"/>
<source>Checking errors...</source>
<translation>Comprobando errores...</translation>
</message>
<message>
<location filename="../../panels/data.py" line="82"/>
<source>Read and error checking finished.</source>
<translation>Lectura y comprobación de errores finalizada.</translation>
</message>
<message>
<location filename="../../panels/data.py" line="72"/>
<source>Drawing table...</source>
<translation>Renderizando la tabla...</translation>
</message>
</context>
<context>
<name>DeleteDialog</name>
<message>
<location filename="../../dialogs/delete.py" line="43"/>
<source>Cancel</source>
<translation>Cancelar</translation>
</message>
<message>
<location filename="../../dialogs/delete.py" line="44"/>
<source>Ok</source>
<translation>Ok</translation>
</message>
<message>
<location filename="../../dialogs/delete.py" line="45"/>
<source>Are you sure you want to delete this item?</source>
<translation>¿Está seguro de que desea eliminar este elemento?</translation>
</message>
<message>
<location filename="../../dialogs/delete.py" line="46"/>
<source>Delete file</source>
<translation>Eliminar archivo</translation>
</message>
</context>
<context>
<name>DownloadDialog</name>
<message>
<location filename="../../dialogs/download.py" line="48"/>
<source>Download</source>
<translation>Descargar</translation>
</message>
<message>
<location filename="../../dialogs/download.py" line="49"/>
<source>Please, select one of the following options:</source>
<translation>Por favor, selecciona una de las siguientes opciones:</translation>
</message>
<message>
<location filename="../../dialogs/download.py" line="50"/>
<source>Download file</source>
<translation>Descargar archivo</translation>
</message>
<message>
<location filename="../../dialogs/download.py" line="51"/>
<source>Download file with errors</source>
<translation>Descargar archivo con errores</translation>
</message>
<message>
<location filename="../../dialogs/download.py" line="63"/>
<source>File downloaded successfully to:
{}</source>
<translation>Archivo descargado exitosamente en:
{}</translation>
</message>
<message>
<location filename="../../dialogs/download.py" line="67"/>
<source>Error downloading file:
{}</source>
<translation>Error descargando el archivo:
{}</translation>
</message>
<message>
<location filename="../../dialogs/download.py" line="64"/>
<source>Success</source>
<translation>Éxito</translation>
</message>
</context>
<context>
<name>ErrorsMessages</name>
<message>
<location filename="../../utils.py" line="121"/>
<location filename="../../utils.py" line="127"/>
<source>Missing header</source>
<translation>Falta el encabezado</translation>
</message>
<message>
<location filename="../../utils.py" line="122"/>
<source>Duplicated header</source>
<translation>Encabezado duplicado</translation>
</message>
<message>
<location filename="../../utils.py" line="123"/>
<source>Empty row</source>
<translation>Fila vacía</translation>
</message>
<message>
<location filename="../../utils.py" line="124"/>
<source>Type mismatch</source>
<translation>Error de tipo</translation>
</message>
<message>
<location filename="../../utils.py" line="125"/>
<source>Missing value</source>
<translation>Valor faltante</translation>
</message>
<message>
<location filename="../../utils.py" line="126"/>
<source>Extra cell</source>
<translation>Celda adicional</translation>
</message>
<message>
<location filename="../../utils.py" line="128"/>
<source>Blank Label</source>
<translation>Etiqueta en blanco</translation>
</message>
<message>
<location filename="../../utils.py" line="137"/>
<location filename="../../utils.py" line="143"/>
<source>A column in the header row has no name. Every column should have a unique, non-empty header.</source>
<translation>Una columna en la fila de encabezado no tiene nombre. Cada columna debe tener un encabezado único y no vacío.</translation>
</message>
<message>
<location filename="../../utils.py" line="138"/>
<source>Two or more columns share the same name. Column names must be unique.</source>
<translation>Dos o más columnas comparten el mismo nombre. Los nombres de columna deben ser únicos.</translation>
</message>
<message>
<location filename="../../utils.py" line="139"/>
<source>This row has no data. Rows should contain at least one cell with data.</source>
<translation>Esta fila no tiene datos. Las filas deben contener al menos una celda con datos.</translation>
</message>
<message>
<location filename="../../utils.py" line="140"/>
<source>A cell value doesn't match the expected data type or format for the column.</source>
<translation>Un valor de celda no coincide con el tipo de dato o formato esperado para la columna.</translation>
</message>
<message>
<location filename="../../utils.py" line="141"/>
<source>This cell is missing data</source>
<translation>Esta celda carece de datos</translation>
</message>
<message>
<location filename="../../utils.py" line="142"/>
<source>This row has more values compared to the header row.</source>
<translation>Esta fila tiene más valores en comparación con la fila de encabezado.</translation>
</message>
<message>
<location filename="../../utils.py" line="144"/>
<source>A label in the header row is missing a value. Label should be provided and not be blank.</source>
<translation>Falta un valor en una etiqueta de la fila de encabezado. La etiqueta debe proporcionarse y no estar en blanco.</translation>
</message>
</context>
<context>
<name>ErrorsWidget</name>
<message>
<location filename="../../panels/errors.py" line="220"/>
<source>Please note that the ODE currently detects errors in tables, with a maximum of </source>
<translation>Por favor, ten en cuenta que la ODE detecta actualmente errores en las tablas, con un máximo de </translation>
</message>
</context>
<context>
<name>FrictionlessTableModel</name>
<message>
<location filename="../../panels/data.py" line="249"/>
<source>Data</source>
<translation>Datos</translation>
</message>
</context>
<context>
<name>LLMWarningDialog</name>
<message>
<location filename="../../dialogs/llm_dialog_warning.py" line="13"/>
<source>AI assistant</source>
<translation>Asistente de IA</translation>
</message>
<message>
<location filename="../../dialogs/llm_dialog_warning.py" line="25"/>
<source>Welcome to the ODE's AI assistant! This feature will help you generating better descriptions for the columns of your table and also questions for data analysis.
To get started, you will need to install the AI file in your computer. Once installed, everything will run locally, meaning your data always stays private and secure. Learn more</source>
<translation>¡Bienvenido al asistente de IA de ODE! Esta función te ayudará a generar mejores descripciones para las columnas de tu tabla y también preguntas para el análisis de datos.
Para comenzar, necesitarás instalar el archivo de IA en tu computadora. Una vez instalado, todo se ejecutará localmente, lo que significa que tus datos siempre permanecerán privados y seguros. Más información</translation>
</message>
<message>
<location filename="../../dialogs/llm_dialog_warning.py" line="28"/>
<source>Don't show again</source>
<translation>No mostrar de nuevo</translation>
</message>
<message>
<location filename="../../dialogs/llm_dialog_warning.py" line="33"/>
<source>Cancel</source>
<translation>Cancelar</translation>
</message>
<message>
<location filename="../../dialogs/llm_dialog_warning.py" line="40"/>
<source>Ok</source>
<translation>Ok</translation>
</message>
</context>
<context>
<name>LlamaDialog</name>
<message>
<location filename="../../llama.py" line="288"/>
<location filename="../../llama.py" line="298"/>
<location filename="../../llama.py" line="314"/>
<source>Execute</source>
<translation>Ejecutar</translation>
</message>
<message>
<location filename="../../llama.py" line="282"/>
<source>Generating response...</source>
<translation>Generando respuesta...</translation>
</message>
<message>
<location filename="../../llama.py" line="299"/>
<source>Error</source>
<translation>Error</translation>
</message>
<message>
<location filename="../../llama.py" line="313"/>
<source>AI assistant</source>
<translation>Asistente de IA</translation>
</message>
<message>
<location filename="../../llama.py" line="315"/>
<source>Stop execution</source>
<translation>Detener ejecución</translation>
</message>
<message>
<location filename="../../llama.py" line="316"/>
<source>Results will be displayed here...</source>
<translation>Los resultados se mostrarán aquí...</translation>
</message>
</context>
<context>
<name>LlamaDownloadDialog</name>
<message>
<location filename="../../llama.py" line="398"/>
<source>Delete</source>
<translation>Eliminar</translation>
</message>
<message>
<location filename="../../llama.py" line="404"/>
<source>Download</source>
<translation>Descargar</translation>
</message>
<message>
<location filename="../../llama.py" line="348"/>
<source>AI assistant</source>
<translation>Asistente de IA</translation>
</message>
<message>
<location filename="../../llama.py" line="352"/>
<source>To start using the AI assistant, please download the following model.</source>
<translation>Para comenzar a usar el asistente de IA, por favor descarga el siguiente modelo.</translation>
</message>
<message>
<location filename="../../llama.py" line="359"/>
<source>The ODE will save the file in this location: <i><a href="file://{AI_MODELS_PATH}">{AI_MODELS_PATH}</a></i></source>
<translation>El ODE guardará el archivo en esta carpeta: <i><a href="file://{AI_MODELS_PATH}">{AI_MODELS_PATH}</a></i></translation>
</message>
<message>
<location filename="../../llama.py" line="373"/>
<source>Next</source>
<translation>Siguiente</translation>
</message>
<message>
<location filename="../../llama.py" line="457"/>
<source>File exists</source>
<translation>El archivo ya existe</translation>
</message>
<message>
<location filename="../../llama.py" line="458"/>
<source>Do you want to delete it and download it again?</source>
<translation>¿Quieres eliminarlo y volver a descargarlo?</translation>
</message>
<message>
<location filename="../../llama.py" line="474"/>
<source>Downloading model</source>
<translation>Descargando modelo</translation>
</message>
<message>
<location filename="../../llama.py" line="474"/>
<source>Cancel</source>
<translation>Cancelar</translation>
</message>
<message>
<location filename="../../llama.py" line="475"/>
<source>LLM Model Download Progress</source>
<translation>Progreso de descarga del modelo LLM</translation>
</message>
<message>
<location filename="../../llama.py" line="486"/>
<location filename="../../llama.py" line="553"/>
<source>Error Occurred</source>
<translation>Ocurrió un error</translation>
</message>
<message>
<location filename="../../llama.py" line="494"/>
<source>Confirm Deletion</source>
<translation>Confirmar eliminación</translation>
</message>
<message>
<location filename="../../llama.py" line="495"/>
<source>Are you sure you want to delete {AI_MODEL.name}?</source>
<translation>¿Estás seguro de que quieres eliminar {AI_MODEL.name}?</translation>
</message>
</context>
<context>
<name>LoadingDialog</name>
<message>
<location filename="../../dialogs/loading.py" line="30"/>
<source>Loading</source>
<translation>Cargando</translation>
</message>
<message>
<location filename="../../dialogs/loading.py" line="31"/>
<source>Loading...</source>
<translation>Cargando...</translation>
</message>
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../../main.py" line="684"/>
<source>Ready.</source>
<translation>Listo.</translation>
</message>
<message>
<location filename="../../main.py" line="817"/>
<source>Error</source>
<translation>Error</translation>
</message>
<message>
<location filename="../../main.py" line="817"/>
<source>Error initializing the LLM:
</source>
<translation>Error al inicializar el LLM:
</translation>
</message>
<message>
<location filename="../../main.py" line="864"/>
<source>File</source>
<translation>Archivo</translation>
</message>
<message>
<location filename="../../main.py" line="865"/>
<source>Add</source>
<translation>Agregar</translation>
</message>
<message>
<location filename="../../main.py" line="866"/>
<source>File/Folder</source>
<translation>Archivo/Carpeta</translation>
</message>
<message>
<location filename="../../main.py" line="867"/>
<source>External URL</source>
<translation>URL Externa</translation>
</message>
<message>
<location filename="../../main.py" line="870"/>
<source>View</source>
<translation>Ver</translation>
</message>
<message>
<location filename="../../main.py" line="1173"/>
<source>Downloading data with errors...</source>
<translation>Descargando datos con errores...</translation>
</message>
<message>
<location filename="../../main.py" line="1191"/>
<source>File downloaded successfully to:
{}</source>
<translation>Archivo descargado exitosamente en:
{}</translation>
</message>
<message>
<location filename="../../main.py" line="1192"/>
<source>Success</source>
<translation>Éxito</translation>
</message>
<message>
<location filename="../../main.py" line="871"/>
<source>Errors panel</source>
<translation>Panel de Errores</translation>
</message>
<message>
<location filename="../../main.py" line="872"/>
<source>Source panel</source>
<translation>Panel de Fuente</translation>
</message>
<message>
<location filename="../../main.py" line="875"/>
<source>Help</source>
<translation>Ayuda</translation>
</message>
<message>
<location filename="../../main.py" line="876"/>
<source>User Guide</source>
<translation>Guía de Usuario</translation>
</message>
<message>
<location filename="../../main.py" line="877"/>
<source>Report an Issue</source>
<translation>Reportar un problema</translation>
</message>
<message>
<location filename="../../main.py" line="879"/>
<source>About</source>
<translation>Acerca de</translation>
</message>
<message>
<location filename="../../main.py" line="878"/>
<source>View logs</source>
<translation>Ver logs</translation>
</message>
<message>
<location filename="../../main.py" line="915"/>
<source>Language changed.</source>
<translation>Lenguaje cambiado.</translation>
</message>
<message>
<location filename="../../main.py" line="942"/>
<source>File and Metadata changes saved.</source>
<translation>Archivo y Metadatos guardados.</translation>
</message>
<message>
<location filename="../../main.py" line="1130"/>
<source>Last 100 Lines</source>
<translation>Últimas 100 líneas</translation>
</message>
<message>
<location filename="../../main.py" line="1150"/>
<source>Close</source>
<translation>Cerrar</translation>
</message>
<message>
<location filename="../../main.py" line="1155"/>
<source>Copy to Clipboard</source>
<translation>Copiar al portapapeles</translation>
</message>
</context>
<context>
<name>MetadataForm</name>
<message>
<location filename="../../dialogs/metadata.py" line="189"/>
<source>Column Name:</source>
<translation>Nombre de la columna:</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="190"/>
<source>Data Type:</source>
<translation>Tipo de dato:</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="191"/>
<source>Description:</source>
<translation>Descripción:</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="192"/>
<source>Flag empty cells as errors?:</source>
<translation>¿Marcar celdas vacías como errores?:</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="193"/>
<source>Min. Characters in cells:</source>
<translation>Caracteres mínimos en las celdas:</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="194"/>
<source>Max. Characters in cell</source>
<translation>Caracteres máximos en la celda</translation>
</message>
</context>
<context>
<name>RenameDialog</name>
<message>
<location filename="../../dialogs/rename.py" line="50"/>
<source>Rename file</source>
<translation>Renombrar archivo</translation>
</message>
<message>
<location filename="../../dialogs/rename.py" line="51"/>
<source>Rename item to:</source>
<translation>Renombrar archivo a:</translation>
</message>
<message>
<location filename="../../dialogs/rename.py" line="52"/>
<source>Cancel</source>
<translation>Cancelar</translation>
</message>
<message>
<location filename="../../dialogs/rename.py" line="53"/>
<source>OK</source>
<translation>OK</translation>
</message>
</context>
<context>
<name>Sidebar</name>
<message>
<location filename="../../main.py" line="257"/>
<source>Upload your data</source>
<translation>Carga tus archivos</translation>
</message>
<message>
<location filename="../../main.py" line="258"/>
<source>User guide</source>
<translation>Guía de Usuario</translation>
</message>
<message>
<location filename="../../main.py" line="259"/>
<source>Report an issue</source>
<translation>Reportar un problema</translation>
</message>
<message>
<location filename="../../main.py" line="260"/>
<source>Rename</source>
<translation>Renombrar</translation>
</message>
<message>
<location filename="../../main.py" line="261"/>
<source>Open File in Location</source>
<translation>Abrir la ubicacion de archivo</translation>
</message>
<message>
<location filename="../../main.py" line="262"/>
<source>Delete</source>
<translation>Eliminar</translation>
</message>
<message>
<location filename="../../main.py" line="302"/>
<location filename="../../main.py" line="306"/>
<location filename="../../main.py" line="310"/>
<location filename="../../main.py" line="312"/>
<location filename="../../main.py" line="340"/>
<source>Error</source>
<translation>Error</translation>
</message>
<message>
<location filename="../../main.py" line="303"/>
<source>Source is a file but destination a directory.</source>
<translation>El origen es un archivo pero el destino es un directorio.</translation>
</message>
<message>
<location filename="../../main.py" line="307"/>
<source>Source is a directory but destination a file.</source>
<translation>El origen es un directorio pero el destino es un archivo.</translation>
</message>
<message>
<location filename="../../main.py" line="310"/>
<source>Operation not permitted.</source>
<translation>Operación no permitida.</translation>
</message>
<message>
<location filename="../../main.py" line="312"/>
<source>File with this name already exists.</source>
<translation>Ya existe un archivo con este nombre.</translation>
</message>
<message>
<location filename="../../main.py" line="314"/>
<source>Item renamed successfuly.</source>
<translation>Item renombrado exitosamente.</translation>
</message>
<message>
<location filename="../../main.py" line="344"/>
<source>Item deleted successfuly.</source>
<translation>Item eliminado exitosamente.</translation>
</message>
</context>
<context>
<name>SourceViewer</name>
<message>
<location filename="../../panels/source.py" line="82"/>
<source>This view is only available for CSV files.</source>
<translation>Esta vista sólo está disponible para archivos CSV.</translation>
</message>
</context>
<context>
<name>Toolbar</name>
<message>
<location filename="../../main.py" line="508"/>
<source>Sheet:</source>
<translation>Hoja:</translation>
</message>
<message>
<location filename="../../main.py" line="502"/>
<source>Data</source>
<translation>Datos</translation>
</message>
<message>
<location filename="../../main.py" line="503"/>
<source>Errors Report</source>
<translation>Reporte de Errores</translation>
</message>
<message>
<location filename="../../main.py" line="504"/>
<source>Source code</source>
<translation>Codigo fuente</translation>
</message>
<message>
<location filename="../../main.py" line="505"/>
<source>Export</source>
<translation>Exportar</translation>
</message>
<message>
<location filename="../../main.py" line="506"/>
<source>Save changes</source>
<translation>Guardar cambios</translation>
</message>
<message>
<location filename="../../main.py" line="507"/>
<source>AI</source>
<translation>IA</translation>
</message>
</context>
<context>
<name>Welcome</name>
<message>
<location filename="../../main.py" line="573"/>
<source>The ODE supports Excel & csv files</source>
<translation>El ODE admite archivos Excel y CSV</translation>
</message>
<message>
<location filename="../../main.py" line="574"/>
<source>You can also add links to online tables</source>
<translation>También puedes agregar enlaces a tablas en línea</translation>
</message>
<message>
<location filename="../../main.py" line="575"/>
<source>Upload your data</source>
<translation>Carga tus archivos</translation>
</message>
</context>
</TS>
================================================
FILE: src/ode/assets/translations/fr.ts
================================================
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="fr_FR">
<context>
<name>ColumnMetadataDialog</name>
<message>
<location filename="../../dialogs/metadata.py" line="276"/>
<source>Column name cannot be empty</source>
<translation>Le nom de la colonne ne peut pas être vide</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="281"/>
<source>There is another column in the table with the same name. Please choose a different one</source>
<translation>Il y a une autre colonne dans le tableau avec le même nom. Veuillez en choisir un autre</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="306"/>
<source>Save</source>
<translation>Enregistrer</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="307"/>
<source>Cancel</source>
<translation>Annuler</translation>
</message>
</context>
<context>
<name>DataUploadDialog</name>
<message>
<location filename="../../dialogs/upload.py" line="163"/>
<source>Please paste a valid URL.</source>
<translation>Veuillez coller une URL valide.</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="166"/>
<source>Please paste a valid URL starting with http:// or https://.</source>
<translation>Veuillez coller une URL valide commençant par http:// ou https://.</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="176"/>
<source>Error: The Google Sheets URL is not valid or the table is not publicly available.</source>
<translation>Erreur: L'URL Google Sheets n'est pas valide ou le tableau n'est pas accessible publiquement.</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="187"/>
<source>Error: The URL is not associated with a table</source>
<translation>Erreur: L'URL n'est pas associée à un tableau</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="216"/>
<source>Upload your data</source>
<translation>Télécharger vos données</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="217"/>
<source>Add one or more Excel or csv files</source>
<translation>Ajouter un ou plusieurs fichiers Excel ou csv</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="218"/>
<source>Add a folder</source>
<translation>Ajouter un dossier</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="219"/>
<location filename="../../dialogs/upload.py" line="220"/>
<source>Select</source>
<translation>Sélectionner</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="221"/>
<source>Link to the external table: </source>
<translation>Lien vers le tableau externe: </translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="222"/>
<source>Enter or paste URL</source>
<translation>Entrez ou collez l'URL</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="226"/>
<source>Paste your Google Sheet or csv link to create a local copy in the Open Data Editor</source>
<translation>Collez le lien de votre feuille de calcul Google ou fichier CSV pour créer une copie locale dans l'Éditeur de Données Ouvertes.</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="227"/>
<source>Add</source>
<translation>Ajouter</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="228"/>
<source>Add Local Files</source>
<translation>Ajouter fichiers</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line="229"/>
<source>Add External Data</source>
<translation>Ajouter des données externes</translation>
</message>
</context>
<context>
<name>DataViewer</name>
<message>
<location filename="../../panels/data.py" line="557"/>
<source>Preview not available for this item.</source>
<translation>Aperçu non disponible pour cet article.</translation>
</message>
</context>
<context>
<name>DataWorker</name>
<message>
<location filename="../../panels/data.py" line="67"/>
<source>Reading file...</source>
<translation>Lecture du fichier...</translation>
</message>
<message>
<location filename="../../panels/data.py" line="69"/>
<source>Checking errors...</source>
<translation>Vérification des erreurs...</translation>
</message>
<message>
<location filename="../../panels/data.py" line="82"/>
<source>Read and error checking finished.</source>
<translation>Lecture et vérification des erreurs terminées.</translation>
</message>
<message>
<location filename="../../panels/data.py" line="72"/>
<source>Drawing table...</source>
<translation>Rendu du tableau...</translation>
</message>
</context>
<context>
<name>DeleteDialog</name>
<message>
<location filename="../../dialogs/delete.py" line="43"/>
<source>Cancel</source>
<translation>Annuler</translation>
</message>
<message>
<location filename="../../dialogs/delete.py" line="44"/>
<source>Ok</source>
<translation>Ok</translation>
</message>
<message>
<location filename="../../dialogs/delete.py" line="45"/>
<source>Are you sure you want to delete this item?</source>
<translation>Êtes-vous sûr de vouloir supprimer cet élément?</translation>
</message>
<message>
<location filename="../../dialogs/delete.py" line="46"/>
<source>Delete file</source>
<translation>Supprimer le fichier</translation>
</message>
</context>
<context>
<name>DownloadDialog</name>
<message>
<location filename="../../dialogs/download.py" line="48"/>
<source>Download</source>
<translation>Télécharger</translation>
</message>
<message>
<location filename="../../dialogs/download.py" line="49"/>
<source>Please, select one of the following options:</source>
<translation>Veuillez sélectionner l'une des options suivantes :</translation>
</message>
<message>
<location filename="../../dialogs/download.py" line="50"/>
<source>Download file</source>
<translation>Télécharger le fichier</translation>
</message>
<message>
<location filename="../../dialogs/download.py" line="51"/>
<source>Download file with errors</source>
<translation>Télécharger le fichier avec erreurs</translation>
</message>
<message>
<location filename="../../dialogs/download.py" line="63"/>
<source>File downloaded successfully to:
{}</source>
<translation>Fichier téléchargé avec succès dans :
{}</translation>
</message>
<message>
<location filename="../../dialogs/download.py" line="67"/>
<source>Error downloading file:
{}</source>
<translation>Erreur lors du téléchargement du fichier :
{}</translation>
</message>
<message>
<location filename="../../dialogs/download.py" line="64"/>
<source>Success</source>
<translation>Succès</translation>
</message>
</context>
<context>
<name>ErrorsMessages</name>
<message>
<location filename="../../utils.py" line="121"/>
<location filename="../../utils.py" line="127"/>
<source>Missing header</source>
<translation>En-tête manquant</translation>
</message>
<message>
<location filename="../../utils.py" line="122"/>
<source>Duplicated header</source>
<translation>En-tête dupliqué</translation>
</message>
<message>
<location filename="../../utils.py" line="123"/>
<source>Empty row</source>
<translation>Ligne vide</translation>
</message>
<message>
<location filename="../../utils.py" line="124"/>
<source>Type mismatch</source>
<translation>Type incompatible</translation>
</message>
<message>
<location filename="../../utils.py" line="125"/>
<source>Missing value</source>
<translation>Valeur manquante</translation>
</message>
<message>
<location filename="../../utils.py" line="126"/>
<source>Extra cell</source>
<translation>Cellule supplémentaire</translation>
</message>
<message>
<location filename="../../utils.py" line="128"/>
<source>Blank Label</source>
<translation>Étiquette vide</translation>
</message>
<message>
<location filename="../../utils.py" line="137"/>
<location filename="../../utils.py" line="143"/>
<source>A column in the header row has no name. Every column should have a unique, non-empty header.</source>
<translation>Une colonne dans la ligne d'en-tête n'a pas de nom. Chaque colonne doit avoir un en-tête unique et non vide.</translation>
</message>
<message>
<location filename="../../utils.py" line="138"/>
<source>Two or more columns share the same name. Column names must be unique.</source>
<translation>Deux colonnes ou plus partagent le même nom. Les noms de colonnes doivent être uniques.</translation>
</message>
<message>
<location filename="../../utils.py" line="139"/>
<source>This row has no data. Rows should contain at least one cell with data.</source>
<translation>Cette ligne ne contient aucune donnée. Les lignes doivent contenir au moins une cellule avec des données.</translation>
</message>
<message>
<location filename="../../utils.py" line="140"/>
<source>A cell value doesn't match the expected data type or format for the column.</source>
<translation>Une valeur de cellule ne correspond pas au type de données ou au format attendu pour la colonne.</translation>
</message>
<message>
<location filename="../../utils.py" line="141"/>
<source>This cell is missing data</source>
<translation>Cette cellule ne contient pas de données</translation>
</message>
<message>
<location filename="../../utils.py" line="142"/>
<source>This row has more values compared to the header row.</source>
<translation>Cette ligne a plus de valeurs que la ligne d'en-tête.</translation>
</message>
<message>
<location filename="../../utils.py" line="144"/>
<source>A label in the header row is missing a value. Label should be provided and not be blank.</source>
<translation>Une étiquette dans la ligne d'en-tête n'a pas de valeur. L'étiquette doit être fournie et ne pas être vide.</translation>
</message>
</context>
<context>
<name>ErrorsWidget</name>
<message>
<location filename="../../panels/errors.py" line="220"/>
<source>Please note that the ODE currently detects errors in tables, with a maximum of </source>
<translation>Veuillez noter que l'ODE détecte actuellement des erreurs dans les tableaux, avec un maximum de </translation>
</message>
</context>
<context>
<name>FrictionlessTableModel</name>
<message>
<location filename="../../panels/data.py" line="249"/>
<source>Data</source>
<translation>Données</translation>
</message>
</context>
<context>
<name>LLMWarningDialog</name>
<message>
<location filename="../../dialogs/llm_dialog_warning.py" line="13"/>
<source>AI assistant</source>
<translation>Assistant IA</translation>
</message>
<message>
<location filename="../../dialogs/llm_dialog_warning.py" line="25"/>
<source>Welcome to the ODE's AI assistant! This feature will help you generating better descriptions for the columns of your table and also questions for data analysis.
To get started, you will need to install the AI file in your computer. Once installed, everything will run locally, meaning your data always stays private and secure. Learn more</source>
<translation>Bienvenue dans l'assistant IA de l'ODE ! Cette fonctionnalité vous aidera à générer de meilleures descriptions pour les colonnes de votre tableau ainsi que des questions pour l'analyse des données.
Pour commencer, vous devrez installer le fichier IA sur votre ordinateur. Une fois installé, tout fonctionnera localement, ce qui signifie que vos données restent toujours privées et sécurisées. En savoir plus</translation>
</message>
<message>
<location filename="../../dialogs/llm_dialog_warning.py" line="28"/>
<source>Don't show again</source>
<translation>Ne plus afficher</translation>
</message>
<message>
<location filename="../../dialogs/llm_dialog_warning.py" line="33"/>
<source>Cancel</source>
<translation>Annuler</translation>
</message>
<message>
<location filename="../../dialogs/llm_dialog_warning.py" line="40"/>
<source>Ok</source>
<translation>Ok</translation>
</message>
</context>
<context>
<name>LlamaDialog</name>
<message>
<location filename="../../llama.py" line="288"/>
<location filename="../../llama.py" line="298"/>
<location filename="../../llama.py" line="314"/>
<source>Execute</source>
<translation>Exécuter</translation>
</message>
<message>
<location filename="../../llama.py" line="282"/>
<source>Generating response...</source>
<translation>Génération de la réponse...</translation>
</message>
<message>
<location filename="../../llama.py" line="299"/>
<source>Error</source>
<translation>Erreur</translation>
</message>
<message>
<location filename="../../llama.py" line="313"/>
<source>AI assistant</source>
<translation>Assistant IA</translation>
</message>
<message>
<location filename="../../llama.py" line="315"/>
<source>Stop execution</source>
<translation>Arrêter l'exécution</translation>
</message>
<message>
<location filename="../../llama.py" line="316"/>
<source>Results will be displayed here...</source>
<translation>Les résultats seront affichés ici...</translation>
</message>
</context>
<context>
<name>LlamaDownloadDialog</name>
<message>
<location filename="../../llama.py" line="348"/>
<source>AI assistant</source>
<translation>Assistant IA</translation>
</message>
<message>
<location filename="../../llama.py" line="352"/>
<source>To start using the AI assistant, please download the following model.</source>
<translation>Pour commencer à utiliser l'assistant IA, veuillez télécharger le modèle suivant.</translation>
</message>
<message>
<location filename="../../llama.py" line="359"/>
<source>The ODE will save the file in this location: <i><a href="file://{AI_MODELS_PATH}">{AI_MODELS_PATH}</a></i></source>
<translation>L'ODE enregistrera le fichier à cet emplacement : <i><a href="file://{AI_MODELS_PATH}">{AI_MODELS_PATH}</a></i></translation>
</message>
<message>
<location filename="../../llama.py" line="373"/>
<source>Next</source>
<translation>Suivant</translation>
</message>
<message>
<location filename="../../llama.py" line="398"/>
<source>Delete</source>
<translation>Supprimer</translation>
</message>
<message>
<location filename="../../llama.py" line="404"/>
<source>Download</source>
<translation>Télécharger</translation>
</message>
<message>
<location filename="../../llama.py" line="457"/>
<source>File exists</source>
<translation>Fichier existant</translation>
</message>
<message>
<location filename="../../llama.py" line="458"/>
<source>Do you want to delete it and download it again?</source>
<translation>Voulez-vous le supprimer et le télécharger à nouveau ?</translation>
</message>
<message>
<location filename="../../llama.py" line="474"/>
<source>Downloading model</source>
<translation>Téléchargement du modèle</translation>
</message>
<message>
<location filename="../../llama.py" line="474"/>
<source>Cancel</source>
<translation>Annuler</translation>
</message>
<message>
<location filename="../../llama.py" line="475"/>
<source>LLM Model Download Progress</source>
<translation>Progression du téléchargement du modèle LLM</translation>
</message>
<message>
<location filename="../../llama.py" line="486"/>
<location filename="../../llama.py" line="553"/>
<source>Error Occurred</source>
<translation>Une erreur s'est produite</translation>
</message>
<message>
<location filename="../../llama.py" line="494"/>
<source>Confirm Deletion</source>
<translation>Confirmer la suppression</translation>
</message>
<message>
<location filename="../../llama.py" line="495"/>
<source>Are you sure you want to delete {AI_MODEL.name}?</source>
<translation>Êtes-vous sûr de vouloir supprimer {AI_MODEL.name} ?</translation>
</message>
</context>
<context>
<name>LoadingDialog</name>
<message>
<location filename="../../dialogs/loading.py" line="30"/>
<source>Loading</source>
<translation>Chargement...</translation>
</message>
<message>
<location filename="../../dialogs/loading.py" line="31"/>
<source>Loading...</source>
<translation>Chargement...</translation>
</message>
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../../main.py" line="684"/>
<source>Ready.</source>
<translation>Prêt.</translation>
</message>
<message>
<location filename="../../main.py" line="817"/>
<source>Error</source>
<translation>Erreur</translation>
</message>
<message>
<location filename="../../main.py" line="817"/>
<source>Error initializing the LLM:
</source>
<translation>Erreur lors de l'initialisation du LLM :
</translation>
</message>
<message>
<location filename="../../main.py" line="864"/>
<source>File</source>
<translation>Fichier</translation>
</message>
<message>
<location filename="../../main.py" line="865"/>
<source>Add</source>
<translation>Ajouter</translation>
</message>
<message>
<location filename="../../main.py" line="866"/>
<source>File/Folder</source>
<translation>Fichier/Dossier</translation>
</message>
<message>
<location filename="../../main.py" line="867"/>
<source>External URL</source>
<translation>URL externe</translation>
</message>
<message>
<location filename="../../main.py" line="870"/>
<source>View</source>
<translation>Affichage</translation>
</message>
<message>
<location filename="../../main.py" line="1173"/>
<source>Downloading data with errors...</source>
<translation>Téléchargement des données avec erreurs...</translation>
</message>
<message>
<location filename="../../main.py" line="1191"/>
<source>File downloaded successfully to:
{}</source>
<translation>Fichier téléchargé avec succès dans :
{}</translation>
</message>
<message>
<location filename="../../main.py" line="1192"/>
<source>Success</source>
<translation>Succès</translation>
</message>
<message>
<location filename="../../main.py" line="871"/>
<source>Errors panel</source>
<translation>Panneau d'erreurs</translation>
</message>
<message>
<location filename="../../main.py" line="872"/>
<source>Source panel</source>
<translation>Panneau source</translation>
</message>
<message>
<location filename="../../main.py" line="875"/>
<source>Help</source>
<translation>Aide</translation>
</message>
<message>
<location filename="../../main.py" line="876"/>
<source>User Guide</source>
<translation>Guide d'utilisateur</translation>
</message>
<message>
<location filename="../../main.py" line="877"/>
<source>Report an Issue</source>
<translation>Signaler un problème</translation>
</message>
<message>
<location filename="../../main.py" line="879"/>
<source>About</source>
<translation>À propos</translation>
</message>
<message>
<location filename="../../main.py" line="878"/>
<source>View logs</source>
<translation>Voir les logs</translation>
</message>
<message>
<location filename="../../main.py" line="915"/>
<source>Language changed.</source>
<translation>Langue modifiée.</translation>
</message>
<message>
<location filename="../../main.py" line="942"/>
<source>File and Metadata changes saved.</source>
<translation>Modifications du fichier et des métadonnées enregistrées.</translation>
</message>
<message>
<location filename="../../main.py" line="1130"/>
<source>Last 100 Lines</source>
<translation>100 dernières lignes</translation>
</message>
<message>
<location filename="../../main.py" line="1150"/>
<source>Close</source>
<translation>Fermer</translation>
</message>
<message>
<location filename="../../main.py" line="1155"/>
<source>Copy to Clipboard</source>
<translation>Copier dans le presse-papiers</translation>
</message>
</context>
<context>
<name>MetadataForm</name>
<message>
<location filename="../../dialogs/metadata.py" line="189"/>
<source>Column Name:</source>
<translation>Nom de la colonne:</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="190"/>
<source>Data Type:</source>
<translation>Type de données:</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="191"/>
<source>Description:</source>
<translation>Description:</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="192"/>
<source>Flag empty cells as errors?:</source>
<translation>Marquer les cellules vides comme des erreurs ?</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="193"/>
<source>Min. Characters in cells:</source>
<translation>Caractères min. dans les cellules:</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="194"/>
<source>Max. Characters in cell</source>
<translation>Caractères max. dans la cellule:</translation>
</message>
</context>
<context>
<name>RenameDialog</name>
<message>
<location filename="../../dialogs/rename.py" line="50"/>
<source>Rename file</source>
<translation>Renommer le fichier</translation>
</message>
<message>
<location filename="../../dialogs/rename.py" line="51"/>
<source>Rename item to:</source>
<translation>Renommer l'élément en:</translation>
</message>
<message>
<location filename="../../dialogs/rename.py" line="52"/>
<source>Cancel</source>
<translation>Annuler</translation>
</message>
<message>
<location filename="../../dialogs/rename.py" line="53"/>
<source>OK</source>
<translation>OK</translation>
</message>
</context>
<context>
<name>Sidebar</name>
<message>
<location filename="../../main.py" line="257"/>
<source>Upload your data</source>
<translation>Téléchargez vos données</translation>
</message>
<message>
<location filename="../../main.py" line="258"/>
<source>User guide</source>
<translation>Guide d'utilisateur</translation>
</message>
<message>
<location filename="../../main.py" line="259"/>
<source>Report an issue</source>
<translation>Signaler un problème</translation>
</message>
<message>
<location filename="../../main.py" line="260"/>
<source>Rename</source>
<translation>Renommer</translation>
</message>
<message>
<location filename="../../main.py" line="261"/>
<source>Open File in Location</source>
<translation>Ouvrir le fichier dans l'emplacement</translation>
</message>
<message>
<location filename="../../main.py" line="262"/>
<source>Delete</source>
<translation>Supprimer</translation>
</message>
<message>
<location filename="../../main.py" line="302"/>
<location filename="../../main.py" line="306"/>
<location filename="../../main.py" line="310"/>
<location filename="../../main.py" line="312"/>
<location filename="../../main.py" line="340"/>
<source>Error</source>
<translation>Erreur</translation>
</message>
<message>
<location filename="../../main.py" line="303"/>
<source>Source is a file but destination a directory.</source>
<translation>La source est un fichier mais la destination est un répertoire.</translation>
</message>
<message>
<location filename="../../main.py" line="307"/>
<source>Source is a directory but destination a file.</source>
<translation>La source est un répertoire mais la destination est un fichier.</translation>
</message>
<message>
<location filename="../../main.py" line="310"/>
<source>Operation not permitted.</source>
<translation>Opération non autorisée.</translation>
</message>
<message>
<location filename="../../main.py" line="312"/>
<source>File with this name already exists.</source>
<translation>Un fichier portant ce nom existe déjà.</translation>
</message>
<message>
<location filename="../../main.py" line="314"/>
<source>Item renamed successfuly.</source>
<translation>Élément renommé avec succès.</translation>
</message>
<message>
<location filename="../../main.py" line="344"/>
<source>Item deleted successfuly.</source>
<translation>Élément supprimé avec succès.</translation>
</message>
</context>
<context>
<name>SourceViewer</name>
<message>
<location filename="../../panels/source.py" line="82"/>
<source>This view is only available for CSV files.</source>
<translation>Cette vue est uniquement disponible pour les fichiers CSV.</translation>
</message>
</context>
<context>
<name>Toolbar</name>
<message>
<location filename="../../main.py" line="502"/>
<source>Data</source>
<translation>Données</translation>
</message>
<message>
<location filename="../../main.py" line="508"/>
<source>Sheet:</source>
<translation>Feuille :</translation>
</message>
<message>
<location filename="../../main.py" line="503"/>
<source>Errors Report</source>
<translation>Rapport d'erreurs</translation>
</message>
<message>
<location filename="../../main.py" line="504"/>
<source>Source code</source>
<translation>Code source</translation>
</message>
<message>
<location filename="../../main.py" line="505"/>
<source>Export</source>
<translation>Exporter</translation>
</message>
<message>
<location filename="../../main.py" line="506"/>
<source>Save changes</source>
<translation>Enregistrer les modifications</translation>
</message>
<message>
<location filename="../../main.py" line="507"/>
<source>AI</source>
<translation>IA</translation>
</message>
</context>
<context>
<name>Welcome</name>
<message>
<location filename="../../main.py" line="573"/>
<source>The ODE supports Excel & csv files</source>
<translation>L'ODE prend en charge les fichiers Excel et csv</translation>
</message>
<message>
<location filename="../../main.py" line="574"/>
<source>You can also add links to online tables</source>
<translation>Vous pouvez également ajouter des liens vers des tableaux en ligne</translation>
</message>
<message>
<location filename="../../main.py" line="575"/>
<source>Upload your data</source>
<translation>Téléchargez vos données</translation>
</message>
</context>
</TS>
================================================
FILE: src/ode/assets/translations/it.ts
================================================
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="it_IT">
<context>
<name>ColumnMetadataDialog</name>
<message>
<location filename="../../dialogs/metadata.py" line="276"/>
<source>Column name cannot be empty</source>
<translation>Il nome della colonna non può essere vuoto</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="281"/>
<source>There is another column in the table with the same name. Please choose a different one</source>
<translation>C'è un altra colonna nella tabella con lo stesso nome. Scegli un nome differente</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="306"/>
<source>Save</source>
<translation>Salva</translation>
</message>
<message>
<location filename="../../dialogs/metadata.py" line="307"/>
<source>Cancel</source>
<translation>Annulla</translation>
</message>
</context>
<context>
<name>DataUploadDialog</name>
<message>
<location filename="../../dialogs/upload.py" line="163"/>
<source>Please paste a valid URL.</source>
<translation>Incolla una URL valida.</translation>
</message>
<message>
<location filename="../../dialogs/upload.py" line=
gitextract_cjudfkyp/
├── .github/
│ ├── issue_template.md
│ ├── pull_request_template.md
│ ├── stale.yaml
│ └── workflows/
│ └── general.yaml
├── .gitignore
├── .python-version
├── LICENSE.md
├── README.md
├── build.py
├── create-deb.sh
├── create-dmg.sh
├── docs/
│ ├── Makefile
│ ├── make.bat
│ ├── public/
│ │ └── .gitkeep
│ ├── requirements.txt
│ └── source/
│ ├── conf.py
│ ├── contributing/
│ │ ├── contribution-guidelines.md
│ │ └── translations.md
│ ├── index.rst
│ ├── introduction/
│ │ ├── acknowledgements.md
│ │ ├── fair-data.md
│ │ ├── free-data-literacy-course.md
│ │ ├── latest-updates.md
│ │ ├── responsible-ai-integration.md
│ │ ├── similar-tools-and-differentiators.md
│ │ └── what-is-open-data-editor.md
│ ├── technical-documentation/
│ │ ├── building-the-application.md
│ │ ├── documentation.md
│ │ ├── environment.md
│ │ ├── making-a-release.md
│ │ ├── prerequisites.md
│ │ ├── running-tests.md
│ │ └── start-the-application.md
│ ├── use-cases/
│ │ ├── agricultural-data-ghana.md
│ │ ├── climate-data-kenya.md
│ │ ├── context.md
│ │ ├── data-journalism-mexico.md
│ │ ├── defence-data-france.md
│ │ ├── financial-data-south-africa.md
│ │ ├── government-data-croatia.md
│ │ ├── heritage-data-cambodia.md
│ │ └── library-data-india.md
│ └── user-guide/
│ ├── assets/
│ │ └── table-error-list/
│ │ ├── column-name-missing.csv
│ │ ├── duplicate-column-name.csv
│ │ ├── empty-row.csv
│ │ ├── extra-cell.csv
│ │ ├── header-missing.csv
│ │ └── wrong-data-type.csv
│ ├── deleting-files-or-folders.md
│ ├── downloading-ode.md
│ ├── editing-errors-in-tables.md
│ ├── exporting-your-data.md
│ ├── full-list-of-table-errors-detected.md
│ ├── how-to-explore-and-edit-metadata.md
│ ├── how-to-explore-table-errors.md
│ ├── how-to-use-the-ai-component.md
│ ├── installing-ode.md
│ └── uploading-data.md
├── packaging/
│ ├── linux/
│ │ └── opendataeditor.desktop
│ ├── macos/
│ │ ├── entitlements.mac.plist
│ │ └── icon.icns
│ └── windows/
│ └── installer.nsi
├── pyproject.sublime-workspace
├── pyproject.toml
├── src/
│ └── ode/
│ ├── __init__.py
│ ├── assets/
│ │ ├── __init__.py
│ │ ├── licenses.json
│ │ ├── style.qss
│ │ └── translations/
│ │ ├── de.qm
│ │ ├── de.ts
│ │ ├── es.qm
│ │ ├── es.ts
│ │ ├── fr.qm
│ │ ├── fr.ts
│ │ ├── it.qm
│ │ ├── it.ts
│ │ ├── pt.qm
│ │ └── pt.ts
│ ├── dialogs/
│ │ ├── __init__.py
│ │ ├── delete.py
│ │ ├── download.py
│ │ ├── llm_dialog_warning.py
│ │ ├── loading.py
│ │ ├── metadata.py
│ │ ├── rename.py
│ │ └── upload.py
│ ├── file.py
│ ├── llama.py
│ ├── log_setup.py
│ ├── main.py
│ ├── panels/
│ │ ├── __init__.py
│ │ ├── data.py
│ │ ├── errors.py
│ │ └── source.py
│ ├── paths.py
│ ├── shared.py
│ └── utils.py
└── tests/
├── __init__.py
├── conftest.py
└── ode/
├── __init__.py
├── test_application.py
├── test_files.py
├── test_frictionless_errors.py
└── test_paths.py
SYMBOL INDEX (283 symbols across 22 files)
FILE: build.py
function run (line 8) | def run(cmd: list[str], cwd: str = "."):
function docs (line 13) | def docs():
function update_translations (line 19) | def update_translations():
function compile_translations (line 28) | def compile_translations():
function build_application (line 37) | def build_application():
function main (line 96) | def main():
FILE: src/ode/dialogs/delete.py
class DeleteDialog (line 4) | class DeleteDialog(QDialog):
method __init__ (line 9) | def __init__(self, parent: QWidget, filename: str):
method confirm (line 35) | def confirm(parent, filename):
method retranslateUI (line 41) | def retranslateUI(self):
FILE: src/ode/dialogs/download.py
class DownloadDialog (line 9) | class DownloadDialog(QDialog):
method __init__ (line 15) | def __init__(self, parent, filepath: Path, has_errors:bool) -> None:
method retranslateUI (line 46) | def retranslateUI(self) -> None:
method download_file (line 53) | def download_file(self):
method download_error_file (line 70) | def download_error_file(self):
FILE: src/ode/dialogs/llm_dialog_warning.py
class LLMWarningDialog (line 5) | class LLMWarningDialog(QDialog):
method __init__ (line 11) | def __init__(self, parent: QWidget):
method accept (line 47) | def accept(self):
method confirm (line 56) | def confirm(parent):
FILE: src/ode/dialogs/loading.py
class LoadingDialog (line 5) | class LoadingDialog(QDialog):
method __init__ (line 10) | def __init__(self, parent=None):
method retranslateUI (line 29) | def retranslateUI(self):
method cancel_loading_timer (line 33) | def cancel_loading_timer(self):
method show (line 37) | def show(self, millis: int = 300):
method show_immediately (line 40) | def show_immediately(self):
method show_message (line 44) | def show_message(self, message):
FILE: src/ode/dialogs/metadata.py
class ColumnMetadataField (line 18) | class ColumnMetadataField(NamedTuple):
class DataTypeMapper (line 29) | class DataTypeMapper:
method __init__ (line 69) | def __init__(self):
method get_display_type (line 73) | def get_display_type(self, internal_type):
method get_internal_type (line 77) | def get_internal_type(self, display_type):
method get_all_display_types (line 81) | def get_all_display_types(self):
method get_all_internal_types (line 85) | def get_all_internal_types(self):
class NoWheelComboBox (line 90) | class NoWheelComboBox(QComboBox):
method wheelEvent (line 98) | def wheelEvent(self, event):
class MetadataForm (line 102) | class MetadataForm(QWidget):
method __init__ (line 107) | def __init__(self, *args, **kwargs):
method on_type_changed (line 164) | def on_type_changed(self, text):
method retranslateUI (line 185) | def retranslateUI(self):
class ColumnMetadataDialog (line 197) | class ColumnMetadataDialog(QDialog):
method __init__ (line 204) | def __init__(self, parent: QWidget, field: ColumnMetadataField, field_...
method setup_layout (line 250) | def setup_layout(self):
method save_and_close (line 266) | def save_and_close(self):
method retranslateUI (line 302) | def retranslateUI(self):
FILE: src/ode/dialogs/rename.py
class RenameDialog (line 4) | class RenameDialog(QDialog):
method __init__ (line 12) | def __init__(self, parent: QWidget, filename: str):
method accept (line 42) | def accept(self):
method retranslateUI (line 48) | def retranslateUI(self):
FILE: src/ode/dialogs/upload.py
class SelectWidget (line 24) | class SelectWidget(QWidget):
method __init__ (line 27) | def __init__(self, icon_path, parent=None):
method connect_select_action (line 47) | def connect_select_action(self, action):
class DataUploadDialog (line 51) | class DataUploadDialog(QDialog):
method __init__ (line 64) | def __init__(self, parent, external_first=False):
method add_files (line 127) | def add_files(self):
method add_folders (line 144) | def add_folders(self) -> None:
method load_table_from_url (line 155) | def load_table_from_url(self):
method upload_dialog (line 189) | def upload_dialog(self) -> tuple[int, Path]:
method _read_url_html_title (line 199) | def _read_url_html_title(self, url):
method retranslateUI (line 214) | def retranslateUI(self):
FILE: src/ode/file.py
class File (line 17) | class File:
method __init__ (line 34) | def __init__(self, path: str | Path, sheet_name: str | None = None) ->...
method get_metadata_dict (line 38) | def get_metadata_dict(self, metadata_path) -> dict:
method set_metadata_dict (line 48) | def set_metadata_dict(self, metadata_path: Path, metadata: dict) -> None:
method _get_path_to_metadata_file (line 52) | def _get_path_to_metadata_file(self, path: Path, sheet_name: str | Non...
method get_or_create_metadata (line 81) | def get_or_create_metadata(self, sheet_name: str | None = None):
method _setup_metadata_first_time (line 107) | def _setup_metadata_first_time(self, sheet_name: str | None = None):
method rename (line 130) | def rename(self, new_name, sheet_names: list[str] | None = None):
method rename_metadata_file (line 164) | def rename_metadata_file(self, old_metadata_path: Path, new_metadata_p...
method remove (line 183) | def remove(self, sheet_names: list[str] | None = None):
method get_sheets_names (line 205) | def get_sheets_names(filepath: Path) -> list[str]:
FILE: src/ode/llama.py
class PromptKeys (line 32) | class PromptKeys(Enum):
class AIModel (line 38) | class AIModel(NamedTuple):
class LlamaWorkerSignals (line 53) | class LlamaWorkerSignals(QObject):
class LlamaWorker (line 63) | class LlamaWorker(QThread):
method __init__ (line 68) | def __init__(self, llm, prompt):
method run (line 74) | def run(self):
class LlamaDialog (line 103) | class LlamaDialog(QDialog):
method __init__ (line 108) | def __init__(self, parent=None):
method closeEvent (line 119) | def closeEvent(self, event):
method init_ui (line 130) | def init_ui(self):
method set_data (line 177) | def set_data(self, data):
method _get_columns_prompt (line 181) | def _get_columns_prompt(self):
method _get_analysis_prompt (line 200) | def _get_analysis_prompt(self):
method set_prompt (line 225) | def set_prompt(self, index):
method init_llm (line 243) | def init_llm(self, model_path):
method run (line 256) | def run(self):
method stop (line 273) | def stop(self):
method on_execution_started (line 280) | def on_execution_started(self):
method on_execution_finished (line 286) | def on_execution_finished(self):
method on_execution_error (line 295) | def on_execution_error(self, error_msg):
method on_stream_token (line 301) | def on_stream_token(self, token):
method on_stream_token_first_received (line 306) | def on_stream_token_first_received(self):
method retranslateUI (line 311) | def retranslateUI(self):
method _calculate_half_cpu_count (line 318) | def _calculate_half_cpu_count(self) -> int:
class LlamaDownloadDialog (line 330) | class LlamaDownloadDialog(QDialog):
method __init__ (line 336) | def __init__(self, parent=None):
method init_ui (line 346) | def init_ui(self):
method create_model_row (line 383) | def create_model_row(self, parent_layout):
method update_ui_state (line 413) | def update_ui_state(self):
method is_model_downloaded (line 426) | def is_model_downloaded(self):
method on_next (line 431) | def on_next(self):
method open_download_directory (line 437) | def open_download_directory(self):
method on_download_model (line 450) | def on_download_model(self):
method on_delete_model (line 489) | def on_delete_model(self):
method on_download_abort (line 507) | def on_download_abort(self):
method on_download_finished (line 520) | def on_download_finished(self):
method on_download_ready_read (line 531) | def on_download_ready_read(self):
method on_download_progress (line 538) | def on_download_progress(self, bytesReceived: int, bytesTotal: int):
method on_download_error (line 550) | def on_download_error(self, code: QNetworkReply.NetworkError):
method format_size (line 557) | def format_size(bytes_size: int) -> str:
class LlamaInitWorker (line 566) | class LlamaInitWorker(QObject):
method __init__ (line 575) | def __init__(self, ai_llama, model_path):
method init_llm (line 581) | def init_llm(self):
FILE: src/ode/log_setup.py
function configure_logging (line 11) | def configure_logging():
function configure_exception_handling (line 36) | def configure_exception_handling(logger):
function get_module_logger (line 62) | def get_module_logger(module_name):
FILE: src/ode/main.py
class ContentIndex (line 85) | class ContentIndex(IntEnum):
class CustomTreeView (line 95) | class CustomTreeView(QTreeView):
method __init__ (line 104) | def __init__(self, parent=None):
method keyPressEvent (line 109) | def keyPressEvent(self, event: QKeyEvent):
method mousePressEvent (line 122) | def mousePressEvent(self, event):
method viewportEvent (line 129) | def viewportEvent(self, event):
method item_clicked (line 158) | def item_clicked(self, index: QModelIndex):
class ClickableLabel (line 171) | class ClickableLabel(QLabel):
method __init__ (line 179) | def __init__(self, *args, **kwargs):
method mousePressEvent (line 183) | def mousePressEvent(self, event):
class Sidebar (line 188) | class Sidebar(QWidget):
method __init__ (line 196) | def __init__(self):
method retranslateUI (line 253) | def retranslateUI(self):
method _setup_file_navigator_context_menu (line 262) | def _setup_file_navigator_context_menu(self):
method _show_context_menu (line 278) | def _show_context_menu(self, position):
method _rename_file_navigator_item (line 285) | def _rename_file_navigator_item(self):
method _open_file_navigator_location (line 317) | def _open_file_navigator_location(self):
method _delete_file_navitagor_item (line 331) | def _delete_file_navitagor_item(self):
method _show_only_name_column_in_file_navigator (line 350) | def _show_only_name_column_in_file_navigator(self, file_model, file_na...
class ErrorsReportButton (line 357) | class ErrorsReportButton(QPushButton):
method __init__ (line 365) | def __init__(self, parent=None):
method setText (line 387) | def setText(self, text):
method setIcon (line 391) | def setIcon(self, icon):
method enable (line 404) | def enable(self, number):
method disable (line 420) | def disable(self):
class Toolbar (line 434) | class Toolbar(QWidget):
method __init__ (line 443) | def __init__(self):
method retranslateUI (line 504) | def retranslateUI(self):
class Content (line 515) | class Content(QWidget):
method __init__ (line 523) | def __init__(self):
class Welcome (line 549) | class Welcome(QWidget):
method __init__ (line 552) | def __init__(self):
method retranslateUI (line 575) | def retranslateUI(self):
class MainWindow (line 582) | class MainWindow(QMainWindow):
method __init__ (line 596) | def __init__(self):
method _create_status_bar (line 687) | def _create_status_bar(self):
method _menu_bar (line 690) | def _menu_bar(self):
method apply_stylesheet (line 747) | def apply_stylesheet(self):
method show_welcome_screen (line 758) | def show_welcome_screen(self):
method on_export_click (line 766) | def on_export_click(self):
method on_data_changed (line 775) | def on_data_changed(self):
method on_ai_click (line 779) | def on_ai_click(self):
method on_llm_init_finished (line 811) | def on_llm_init_finished(self):
method on_llm_init_error (line 817) | def on_llm_init_error(self, error_message):
method change_active_panel (line 823) | def change_active_panel(self, panel_index: ContentIndex):
method retranslateUI (line 852) | def retranslateUI(self):
method on_language_change (line 898) | def on_language_change(self, index):
method upload_data (line 921) | def upload_data(self, external_first=False):
method on_button_upload_click (line 942) | def on_button_upload_click(self):
method on_save_click (line 945) | def on_save_click(self):
method on_data_view_save (line 952) | def on_data_view_save(self, save_data):
method update_views (line 962) | def update_views(self, worker_data):
method update_excel_sheet_dropdown (line 982) | def update_excel_sheet_dropdown(self, filepath):
method on_excel_sheet_selection_changed (line 1007) | def on_excel_sheet_selection_changed(self, sheet_name: str):
method update_toolbar (line 1019) | def update_toolbar(self, worker_data):
method update_menu_bar (line 1039) | def update_menu_bar(self, worker_data):
method read_validate_and_display_file (line 1056) | def read_validate_and_display_file(self, file_path, fn_callback: Calla...
method on_tree_click (line 1089) | def on_tree_click(self, index):
method open_about_dialog (line 1104) | def open_about_dialog(self):
method open_user_guide (line 1108) | def open_user_guide(self):
method open_report_issue (line 1111) | def open_report_issue(self):
method show_logs_content (line 1115) | def show_logs_content(self):
method on_download_error_file (line 1167) | def on_download_error_file(self):
method _perform_download (line 1180) | def _perform_download(self):
function main (line 1195) | def main():
FILE: src/ode/panels/data.py
class DataWorkerSignals (line 31) | class DataWorkerSignals(QObject):
class DataWorker (line 38) | class DataWorker(QRunnable):
method __init__ (line 47) | def __init__(self, filepath, sheet_name=None):
method run (line 55) | def run(self):
class ColumnMetadataIconDelegate (line 86) | class ColumnMetadataIconDelegate(QStyledItemDelegate):
method __init__ (line 93) | def __init__(self, icon_path, parent=None):
method _get_icon_rect (line 98) | def _get_icon_rect(self, option):
method paint (line 107) | def paint(self, painter, option, index):
method editorEvent (line 126) | def editorEvent(self, event, model, option, index):
class FrictionlessTableModel (line 135) | class FrictionlessTableModel(QAbstractTableModel):
method __init__ (line 138) | def __init__(
method write_data (line 149) | def write_data(self, filepath: Path, sheet_name=None):
method write_data_csv (line 163) | def write_data_csv(self, filepath: Path):
method write_data_xlsx (line 186) | def write_data_xlsx(self, filepath: Path, sheet_name=None):
method write_data_xls (line 213) | def write_data_xls(self, filepath: str, sheet_name=None):
method write_error_xlsx (line 241) | def write_error_xlsx(self, filepath: Path):
method _get_errors (line 308) | def _get_errors(self, errors):
method _get_row_count (line 347) | def _get_row_count(self):
method _get_column_count (line 350) | def _get_column_count(self):
method get_header_data (line 361) | def get_header_data(self):
method rowCount (line 365) | def rowCount(self, parent=None):
method columnCount (line 369) | def columnCount(self, parent=None):
method data (line 373) | def data(self, index, role):
method flags (line 411) | def flags(self, index):
method setData (line 421) | def setData(self, index, value, role):
class CustomTableView (line 444) | class CustomTableView(QTableView):
method __init__ (line 449) | def __init__(self, parent=None):
method keyPressEvent (line 452) | def keyPressEvent(self, event: QKeyEvent):
method mouseMoveEvent (line 465) | def mouseMoveEvent(self, event):
class DataViewer (line 475) | class DataViewer(QWidget):
method __init__ (line 481) | def __init__(self):
method display_data (line 508) | def display_data(self, model, filepath, sheet_name=None):
method show_column_metadata_dialog (line 528) | def show_column_metadata_dialog(self, field_index):
method clear (line 548) | def clear(self, model):
method retranslateUI (line 558) | def retranslateUI(self):
method save_metadata_to_descriptor_file (line 562) | def save_metadata_to_descriptor_file(self, field_form: dict):
FILE: src/ode/panels/errors.py
class ErrorFilterProxyModel (line 12) | class ErrorFilterProxyModel(QSortFilterProxyModel):
method __init__ (line 20) | def __init__(self, error_type):
method filterAcceptsRow (line 24) | def filterAcceptsRow(self, source_row, source_parent):
method data (line 41) | def data(self, index, role):
class ErrorReport (line 71) | class ErrorReport(QWidget):
method __init__ (line 82) | def __init__(self, errors, model, *args, **kwargs):
method retranslateUI (line 139) | def retranslateUI(self):
class ErrorsWidget (line 150) | class ErrorsWidget(QWidget):
method __init__ (line 153) | def __init__(self, *args, **kwargs):
method display_errors (line 174) | def display_errors(self, errors, model):
method clear (line 198) | def clear(self):
method _sort_frictionless_errors (line 205) | def _sort_frictionless_errors(self, errors):
method retranslateUI (line 217) | def retranslateUI(self):
FILE: src/ode/panels/source.py
class SourceViewer (line 10) | class SourceViewer(QWidget):
method __init__ (line 18) | def __init__(self):
method _read_file (line 38) | def _read_file(self, filepath):
method open_file (line 62) | def open_file(self, filepath):
method clear (line 76) | def clear(self):
method retranslateUI (line 81) | def retranslateUI(self):
FILE: src/ode/paths.py
class Paths (line 13) | class Paths:
method asset (line 20) | def asset(cls, filename):
method translation (line 24) | def translation(cls, filename):
method get_unique_destination_filepath (line 28) | def get_unique_destination_filepath(cls, src_filepath) -> Path:
FILE: src/ode/utils.py
function setup_ode_internal_folders (line 15) | def setup_ode_internal_folders():
function migrate_metadata_store (line 26) | def migrate_metadata_store():
function set_common_style (line 98) | def set_common_style(widget):
function show_error_dialog (line 102) | def show_error_dialog(message=None, title="Error"):
class ErrorTexts (line 116) | class ErrorTexts:
method get_error_title (line 118) | def get_error_title(cls, error_type):
method get_error_description (line 134) | def get_error_description(cls, error_type):
FILE: tests/conftest.py
function project_folder (line 6) | def project_folder(tmp_path):
function window (line 14) | def window(qtbot, project_folder):
FILE: tests/ode/test_application.py
function test_file_is_displayed (line 6) | def test_file_is_displayed(qtbot, window, project_folder):
function test_button_errors_displays_error_count (line 27) | def test_button_errors_displays_error_count(qtbot, window, project_folder):
function test_error_reports_show_two_blank_lines_in_red (line 43) | def test_error_reports_show_two_blank_lines_in_red(qtbot, window, projec...
function test_error_reports_show_two_errors_in_same_row (line 75) | def test_error_reports_show_two_errors_in_same_row(qtbot, window, projec...
FILE: tests/ode/test_files.py
class TestFiles (line 7) | class TestFiles:
method test_constructor (line 8) | def test_constructor(self, project_folder):
method test_path_to_metadata_file (line 15) | def test_path_to_metadata_file(self, project_folder):
method test_path_to_metadata_subfolder (line 20) | def test_path_to_metadata_subfolder(self, project_folder):
method test_path_to_metadata_folder (line 25) | def test_path_to_metadata_folder(self, project_folder):
method test_get_create_metadata (line 31) | def test_get_create_metadata(self, project_folder):
method test_get_metadata_dict (line 41) | def test_get_metadata_dict(self, project_folder):
method test_rename_file (line 53) | def test_rename_file(self, project_folder):
method test_rename_folder (line 62) | def test_rename_folder(self, project_folder):
method test_rename_raises_error_if_target_exist (line 75) | def test_rename_raises_error_if_target_exist(self, project_folder):
method test_rename_also_updates_object_attributes (line 85) | def test_rename_also_updates_object_attributes(self, project_folder):
method test_rename_file_metadata (line 94) | def test_rename_file_metadata(self, project_folder):
method test_rename_folder_metadata (line 106) | def test_rename_folder_metadata(self, project_folder):
method test_remove_file_and_metadata (line 133) | def test_remove_file_and_metadata(self, project_folder):
method test_delete_folder_and_metadata (line 143) | def test_delete_folder_and_metadata(self, project_folder):
FILE: tests/ode/test_frictionless_errors.py
class TestFrictionlessErrors (line 10) | class TestFrictionlessErrors:
method test_blank_header_error (line 19) | def test_blank_header_error(self, qtbot, window, project_folder):
method test_duplicate_label_error (line 40) | def test_duplicate_label_error(self, qtbot, window, project_folder):
method test_blank_row_error (line 61) | def test_blank_row_error(self, qtbot, window, project_folder):
method test_blank_row_and_duplicated_label_error (line 83) | def test_blank_row_and_duplicated_label_error(self, qtbot, window, pro...
method test_missing_cell_error (line 107) | def test_missing_cell_error(self, qtbot, window, project_folder):
method test_extra_cell_error (line 132) | def test_extra_cell_error(self, qtbot, window, project_folder):
method test_custom_errors_descriptions_are_shown (line 155) | def test_custom_errors_descriptions_are_shown(self, qtbot, window, pro...
method test_default_frictionless_errors_if_missing_custom (line 178) | def test_default_frictionless_errors_if_missing_custom(self, qtbot, wi...
method test_changing_column_header_name_fixes_error_with_dialog (line 203) | def test_changing_column_header_name_fixes_error_with_dialog(self, qtb...
method test_changing_column_type_with_metadata_dialog (line 259) | def test_changing_column_type_with_metadata_dialog(self, qtbot, window...
FILE: tests/ode/test_paths.py
class TestPaths (line 4) | class TestPaths:
method test_no_conflict (line 5) | def test_no_conflict(self, project_folder):
method test_single_conflict (line 10) | def test_single_conflict(self, project_folder):
method test_multiple_conflicts (line 17) | def test_multiple_conflicts(self, project_folder):
Condensed preview — 104 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (451K chars).
[
{
"path": ".github/issue_template.md",
"chars": 143,
"preview": "# Overview\n\nPlease replace this line with full information about your idea or problem. If it's a bug share as much as po"
},
{
"path": ".github/pull_request_template.md",
"chars": 255,
"preview": "- fixes #<issue-number>\n\n---\n\nPlease make sure that all the checks pass. Please add here any additional information rega"
},
{
"path": ".github/stale.yaml",
"chars": 702,
"preview": "# Number of days of inactivity before an issue becomes stale\ndaysUntilStale: 90\n\n# Number of days of inactivity before a"
},
{
"path": ".github/workflows/general.yaml",
"chars": 6051,
"preview": "name: general\n\non:\n push:\n branches:\n - main\n tags:\n - v*.*.*\n pull_request:\n branches:\n - mai"
},
{
"path": ".gitignore",
"chars": 107,
"preview": "venv\ndata/\n__pycache__\nbuild/\ndist/\ntmp/\n\n# Astro \nnode_modules/\n.astro\npackage-lock.json\n/.venv\n.DS_Store\n"
},
{
"path": ".python-version",
"chars": 5,
"preview": "3.13\n"
},
{
"path": "LICENSE.md",
"chars": 1092,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2022 Open Knowledge Foundation\n\nPermission is hereby granted, free of charge, to an"
},
{
"path": "README.md",
"chars": 1928,
"preview": "\n[]"
},
{
"path": "build.py",
"chars": 4471,
"preview": "import os\nimport platform\nimport PyInstaller.__main__\nimport subprocess\nimport sys\n\n\ndef run(cmd: list[str], cwd: str = "
},
{
"path": "create-deb.sh",
"chars": 1926,
"preview": "#!/bin/sh\n# Script to create a PyQT deb package using fpm\n#\n# This script will create a folder structure that will be us"
},
{
"path": "create-dmg.sh",
"chars": 5559,
"preview": "#!/bin/sh\n# File to create the DMG file using create-dmg tool and notarize it.\n#\n# It is intended to work on Github Acti"
},
{
"path": "docs/Makefile",
"chars": 638,
"preview": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line, and also\n# from the "
},
{
"path": "docs/make.bat",
"chars": 804,
"preview": "@ECHO OFF\r\n\r\npushd %~dp0\r\n\r\nREM Command file for Sphinx documentation\r\n\r\nif \"%SPHINXBUILD%\" == \"\" (\r\n\tset SPHINXBUILD=sp"
},
{
"path": "docs/public/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "docs/requirements.txt",
"chars": 36,
"preview": "sphinx\nmyst-parser\nsphinx_rtd_theme\n"
},
{
"path": "docs/source/conf.py",
"chars": 1076,
"preview": "# Configuration file for the Sphinx documentation builder.\n#\n# For the full list of built-in configuration values, see t"
},
{
"path": "docs/source/contributing/contribution-guidelines.md",
"chars": 4712,
"preview": "# Contributing\n\n## Contribution Guidelines\n\nYou don’t need to know how to code to contribute to Open Data Editor, you ju"
},
{
"path": "docs/source/contributing/translations.md",
"chars": 1937,
"preview": "# Translations\n\nODE supports several languages following the [Qt Framework practices for](https://doc.qt.io/qt-6/interna"
},
{
"path": "docs/source/index.rst",
"chars": 2548,
"preview": ".. Open Data Editor documentation master file, created by\n sphinx-quickstart on Fri Jul 4 13:22:06 2025.\n You can a"
},
{
"path": "docs/source/introduction/acknowledgements.md",
"chars": 309,
"preview": "## Acknowledgements\n\nWe are grateful for the support and partnership of the [Patrick J. McGovern Foundation (PJMF)](http"
},
{
"path": "docs/source/introduction/fair-data.md",
"chars": 1400,
"preview": "## FAIR data \n\nThe Open Data Editor (ODE) improves data quality based on the [FAIR principles](https://www.go-fair.org/f"
},
{
"path": "docs/source/introduction/free-data-literacy-course.md",
"chars": 946,
"preview": "## Free data literacy course \n\nOpen Data Editor's focus on improving digital literacy and preparing non-technical users "
},
{
"path": "docs/source/introduction/latest-updates.md",
"chars": 256,
"preview": "## Latest updates\n\nOpen Data Editor is being built in the open. Follow the progress, from feature updates to community s"
},
{
"path": "docs/source/introduction/responsible-ai-integration.md",
"chars": 1256,
"preview": "## Responsible AI integration\n\nThe Open Data Editor (ODE) has an AI component to help users better understand their data"
},
{
"path": "docs/source/introduction/similar-tools-and-differentiators.md",
"chars": 1627,
"preview": "## Similar tools and differentiators\n\nThe tools currently available with functions similar to those of the Open Data Edi"
},
{
"path": "docs/source/introduction/what-is-open-data-editor.md",
"chars": 1451,
"preview": "## What is Open Data Editor\n\n[Open Data Editor (ODE)](https://okfn.org/opendataeditor), developed by the [Open Knowledge"
},
{
"path": "docs/source/technical-documentation/building-the-application.md",
"chars": 225,
"preview": "## Building the application\n\n```bash\nuv run build.py build\n```\n\nor\n\n```bash\n# With the virtual environment activated\npyt"
},
{
"path": "docs/source/technical-documentation/documentation.md",
"chars": 469,
"preview": "## Documentation\n\nDocumentation is written with [Sphinx](https://www.sphinx-doc.org/en/master/) (in the `docs` directory"
},
{
"path": "docs/source/technical-documentation/environment.md",
"chars": 111,
"preview": "## Environment\n\nUse `uv` to create a virtualenv and activate it:\n\n```bash\nuv sync\nsource venv/bin/activate\n```\n"
},
{
"path": "docs/source/technical-documentation/making-a-release.md",
"chars": 857,
"preview": "## Making a release\n\nTo make a release, follow the following checklist:\n\n* Check with the Product Owner that the `main` "
},
{
"path": "docs/source/technical-documentation/prerequisites.md",
"chars": 279,
"preview": "## Prerequisites\n\nWe are using 3.13. To start working on the project, you need the following dependencies on your machin"
},
{
"path": "docs/source/technical-documentation/running-tests.md",
"chars": 123,
"preview": "## Running tests\n\n```bash\nuv run pytest tests/\n```\n\nor\n\n```bash\n# With the virtual environment activated\npytest tests/\n`"
},
{
"path": "docs/source/technical-documentation/start-the-application.md",
"chars": 130,
"preview": "## Start the application\n\n```bash\nuv run ode\n```\n\nor\n\n```bash\n# With the virtual environment activated\npython src/ode/ma"
},
{
"path": "docs/source/use-cases/agricultural-data-ghana.md",
"chars": 842,
"preview": "## Agricultural data (Ghana)\n\nOpen Science Community Ghana (OSCG) used ODE to streamline the work with manually-collecte"
},
{
"path": "docs/source/use-cases/climate-data-kenya.md",
"chars": 864,
"preview": "## Climate data (Kenya)\n\nThe Demography Project used ODE to check and correct errors in a giant spreadsheet of air quali"
},
{
"path": "docs/source/use-cases/context.md",
"chars": 670,
"preview": "# Context\n\nThe Open Data Editor was developed in collaboration with user communities worldwide. Between 2024 and 2025, t"
},
{
"path": "docs/source/use-cases/data-journalism-mexico.md",
"chars": 823,
"preview": "## Data journalism (Mexico)\n\nData Crítica used ODE to identify reliable variables for journalistic investigations and en"
},
{
"path": "docs/source/use-cases/defence-data-france.md",
"chars": 702,
"preview": "## Defence data (France)\n\nThe Observatoire des armements used ODE to turn multiple public spending data sources (arms pu"
},
{
"path": "docs/source/use-cases/financial-data-south-africa.md",
"chars": 885,
"preview": "## Financial data (South Africa)\n\nThe Public Affairs Research Institute (PARI) used ODE to restructure messy public fina"
},
{
"path": "docs/source/use-cases/government-data-croatia.md",
"chars": 994,
"preview": "## Government data (Croatia)\n\nThe City of Zagreb used ODE to comply with open data standards and foster a culture of dat"
},
{
"path": "docs/source/use-cases/heritage-data-cambodia.md",
"chars": 1132,
"preview": "## Heritage data (Cambodia)\n\nAn AI of Our Own (AAOO) used ODE to create AI models that are built on respectful and ethic"
},
{
"path": "docs/source/use-cases/library-data-india.md",
"chars": 950,
"preview": "## Library data (India)\n\nThe Indian Institute of Technology (IIT) Delhi used ODE to check errors in large spreadsheets o"
},
{
"path": "docs/source/user-guide/assets/table-error-list/column-name-missing.csv",
"chars": 14,
"preview": "col1,\n1,2\n3,4\n"
},
{
"path": "docs/source/user-guide/assets/table-error-list/duplicate-column-name.csv",
"chars": 18,
"preview": "col1,col1\n1,2\n3,4\n"
},
{
"path": "docs/source/user-guide/assets/table-error-list/empty-row.csv",
"chars": 19,
"preview": "col1,col2\n1,2\n\n3,4\n"
},
{
"path": "docs/source/user-guide/assets/table-error-list/extra-cell.csv",
"chars": 24,
"preview": "col1,col2\n1,2\n3,4\n5,6,7\n"
},
{
"path": "docs/source/user-guide/assets/table-error-list/header-missing.csv",
"chars": 10,
"preview": ",\n1,2\n3,4\n"
},
{
"path": "docs/source/user-guide/assets/table-error-list/wrong-data-type.csv",
"chars": 68,
"preview": "col1,col2\n1,2\n3,4\n5,6\n7,8\n9,10\n11,12\n13,14\n15,16\n17,18\n19,20\n21,bad\n"
},
{
"path": "docs/source/user-guide/deleting-files-or-folders.md",
"chars": 223,
"preview": "## Deleting files or folders\n\nTo delete a file or folder, click on the three dots next to the file/folder name and selec"
},
{
"path": "docs/source/user-guide/downloading-ode.md",
"chars": 639,
"preview": "## Downloading ODE\n\nOpen Data Editor is available on all major platforms:\n\n* **For Windows:** Download the most recent *"
},
{
"path": "docs/source/user-guide/editing-errors-in-tables.md",
"chars": 774,
"preview": "## Editing errors in tables\n\nTo fix cell errors, you can directly edit the data cells in the viewer/editor.\n\n**Step 1:**"
},
{
"path": "docs/source/user-guide/exporting-your-data.md",
"chars": 812,
"preview": "## Exporting your data\n\nYou can export your data using the **Export** feature located at the top right of the datagrid:\n"
},
{
"path": "docs/source/user-guide/full-list-of-table-errors-detected.md",
"chars": 6992,
"preview": "## Full list of table errors detected\n\nHere we describe the list of errors that ODE can detect after users upload tables"
},
{
"path": "docs/source/user-guide/how-to-explore-and-edit-metadata.md",
"chars": 615,
"preview": "## How to explore and edit metadata\n\nTo explore or edit the metadata, select a file from the menu on the left and click "
},
{
"path": "docs/source/user-guide/how-to-explore-table-errors.md",
"chars": 865,
"preview": "## How to explore table errors\n\nAs mentioned in the **Uploading data** section in this guide, if a file has errors, ODE "
},
{
"path": "docs/source/user-guide/how-to-use-the-ai-component.md",
"chars": 1797,
"preview": "## How to use the AI component\n\nTo use the AI assistant, select a file from the sidebar, and then click on the **AI** bu"
},
{
"path": "docs/source/user-guide/installing-ode.md",
"chars": 1883,
"preview": "## Installing ODE\n\n### Windows\n\nDownload the most recent **EXE** file as per the above instructions.\n\n1\\. If you receive"
},
{
"path": "docs/source/user-guide/uploading-data.md",
"chars": 4902,
"preview": "## Uploading data\n\nThis section explains how to upload tabular files, folders with tables and online data to ODE. The to"
},
{
"path": "packaging/linux/opendataeditor.desktop",
"chars": 467,
"preview": "[Desktop Entry]\n\n# The type of the thing this desktop file refers to\nType=Application\n\n# The Application Name\nName=Open "
},
{
"path": "packaging/macos/entitlements.mac.plist",
"chars": 333,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "packaging/windows/installer.nsi",
"chars": 3041,
"preview": "; NSIS Script to create a Windows Installer.\n;\n; To create a windows installer:\n; 1. Install NSIS from https://nsis.so"
},
{
"path": "pyproject.sublime-workspace",
"chars": 2,
"preview": "{}"
},
{
"path": "pyproject.toml",
"chars": 887,
"preview": "[project]\nname = \"opendataeditor\"\nversion = \"1.7.1\" # Update this version number also in ode/__init__.py\ndescription = \""
},
{
"path": "src/ode/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "src/ode/assets/__init__.py",
"chars": 49,
"preview": "# Required for PyInstaller to collect all assets\n"
},
{
"path": "src/ode/assets/licenses.json",
"chars": 15309,
"preview": "[\n {\n \"name\": \"AAL\",\n \"path\": \"https://opensource.org/licenses/AAL\",\n \"title\": \"Attribution Assurance Licenses"
},
{
"path": "src/ode/assets/style.qss",
"chars": 8540,
"preview": "/* Open Data Editor main Style Sheet.\n\nThe application uses a fusion style set when creating the QMainWindow object. Thi"
},
{
"path": "src/ode/assets/translations/de.ts",
"chars": 29483,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"de_DE\">\n<context>\n <name>ColumnMetad"
},
{
"path": "src/ode/assets/translations/es.ts",
"chars": 29352,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"es_ES\">\n<context>\n <name>ColumnMetad"
},
{
"path": "src/ode/assets/translations/fr.ts",
"chars": 29855,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"fr_FR\">\n<context>\n <name>ColumnMetad"
},
{
"path": "src/ode/assets/translations/it.ts",
"chars": 29241,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"it_IT\">\n<context>\n <name>ColumnMetad"
},
{
"path": "src/ode/assets/translations/pt.ts",
"chars": 29441,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"pt_PT\">\n<context>\n <name>ColumnMetad"
},
{
"path": "src/ode/dialogs/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "src/ode/dialogs/delete.py",
"chars": 1578,
"preview": "from PySide6.QtWidgets import QDialog, QVBoxLayout, QPushButton, QHBoxLayout, QLabel, QWidget\n\n\nclass DeleteDialog(QDial"
},
{
"path": "src/ode/dialogs/download.py",
"chars": 2526,
"preview": "import os\nimport shutil\n\nfrom PySide6.QtWidgets import QVBoxLayout, QPushButton, QDialog, QMessageBox, QLabel, QHBoxLayo"
},
{
"path": "src/ode/dialogs/llm_dialog_warning.py",
"chars": 2505,
"preview": "from PySide6.QtWidgets import QDialog, QVBoxLayout, QPushButton, QHBoxLayout, QLabel, QWidget, QCheckBox\nfrom PySide6.Qt"
},
{
"path": "src/ode/dialogs/loading.py",
"chars": 1211,
"preview": "from PySide6.QtWidgets import QDialog, QProgressBar, QVBoxLayout, QLabel\nfrom PySide6.QtCore import QTimer, Slot\n\n\nclass"
},
{
"path": "src/ode/dialogs/metadata.py",
"chars": 9745,
"preview": "from typing import NamedTuple\nfrom PySide6.QtWidgets import (\n QDialog,\n QVBoxLayout,\n QHBoxLayout,\n QPushBu"
},
{
"path": "src/ode/dialogs/rename.py",
"chars": 1797,
"preview": "from PySide6.QtWidgets import QDialog, QVBoxLayout, QLineEdit, QPushButton, QHBoxLayout, QLabel, QWidget\n\n\nclass RenameD"
},
{
"path": "src/ode/dialogs/upload.py",
"chars": 8390,
"preview": "import re\nimport shutil\n\nfrom frictionless.resources import FileResource, TableResource, FrictionlessException\nfrom path"
},
{
"path": "src/ode/file.py",
"chars": 9385,
"preview": "import json\nimport shutil\nimport logging\nimport xlrd\nimport openpyxl\n\nfrom frictionless import system\nfrom frictionless."
},
{
"path": "src/ode/llama.py",
"chars": 21200,
"preview": "import sys\nimport os\nimport logging\nfrom pathlib import Path\nfrom typing import NamedTuple\nfrom enum import Enum\n\nfrom l"
},
{
"path": "src/ode/log_setup.py",
"chars": 2289,
"preview": "import logging\nimport sys\nfrom logging.handlers import RotatingFileHandler\n\nfrom PySide6.QtCore import QtMsgType, qInsta"
},
{
"path": "src/ode/main.py",
"chars": 49113,
"preview": "import logging\nimport os\nimport sys\n\nfrom enum import IntEnum\nfrom importlib.metadata import version\nfrom pathlib import"
},
{
"path": "src/ode/panels/__init__.py",
"chars": 498,
"preview": "\"\"\" Open Data Editor main widgets module.\n\nThis module contains the widgets that implements the main functionalities of "
},
{
"path": "src/ode/panels/data.py",
"chars": 22781,
"preview": "import json\nimport csv\nimport logging\nfrom pathlib import Path\nfrom frictionless import system\n\nfrom PySide6.QtCore impo"
},
{
"path": "src/ode/panels/errors.py",
"chars": 7739,
"preview": "import collections\n\nfrom PySide6.QtCore import Qt, QSortFilterProxyModel\nfrom PySide6.QtWidgets import QWidget, QLabel, "
},
{
"path": "src/ode/panels/source.py",
"chars": 2760,
"preview": "import sys\n\nfrom PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QPlainTextEdit, QLabel\nfrom PySide6.QtGui "
},
{
"path": "src/ode/paths.py",
"chars": 1852,
"preview": "import os\nfrom pathlib import Path\n\n# This is the Project path where all files are stored and\n# it will be hardcoded to "
},
{
"path": "src/ode/shared.py",
"chars": 95,
"preview": "from PySide6.QtGui import QColor\n\nCOLOR_RED = QColor(\"#D32F2F\")\nCOLOR_BLUE = QColor(\"#0288D1\")\n"
},
{
"path": "src/ode/utils.py",
"chars": 6881,
"preview": "import json\nimport platform\nimport subprocess\n\nfrom pathlib import Path\n\nfrom frictionless.resources import TableResourc"
},
{
"path": "tests/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/conftest.py",
"chars": 413,
"preview": "import pytest\nfrom ode import paths, main\n\n\n@pytest.fixture(autouse=True)\ndef project_folder(tmp_path):\n # Patch the "
},
{
"path": "tests/ode/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/ode/test_application.py",
"chars": 4080,
"preview": "from PySide6.QtCore import Qt\n\nfrom ode.shared import COLOR_RED\n\n\ndef test_file_is_displayed(qtbot, window, project_fold"
},
{
"path": "tests/ode/test_files.py",
"chars": 5937,
"preview": "import json\nimport pytest\n\nfrom ode.file import File\n\n\nclass TestFiles:\n def test_constructor(self, project_folder):\n"
},
{
"path": "tests/ode/test_frictionless_errors.py",
"chars": 14307,
"preview": "from PySide6.QtCore import Qt\n\nfrom PySide6.QtWidgets import QDialog\n\nfrom ode.dialogs.metadata import ColumnMetadataDia"
},
{
"path": "tests/ode/test_paths.py",
"chars": 934,
"preview": "from ode.paths import Paths\n\n\nclass TestPaths:\n def test_no_conflict(self, project_folder):\n test_file = proje"
}
]
// ... and 6 more files (download for full content)
About this extraction
This page contains the full source code of the okfn/opendataeditor GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 104 files (415.0 KB), approximately 100.0k tokens, and a symbol index with 283 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.