diff --git a/.dockerignore b/.dockerignore index 6526b0abec..e14c75cfda 100644 --- a/.dockerignore +++ b/.dockerignore @@ -11,9 +11,10 @@ !/TRADEMARK # Exclude -src/dashboard/frontend/node_modules -src/dashboard/src/media/js/build +src/archivematica/dashboard/frontend/node_modules +src/archivematica/dashboard/media/js/build **/*~ **/*.log **/node_modules **/Thumbs.db +**/*.egg-info diff --git a/.gitattributes b/.gitattributes index 2db620324f..6b8b006b59 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,4 @@ -*.py ident -*.sh ident -src/dashboard/frontend/app/locale/*.po linguist-generated=true -src/dashboard/frontend/app/locale/*.pot linguist-generated=true -src/dashboard/frontend/app/locale/translations.json linguist-generated=true -src/dashboard/frontend/package-lock.json linguist-generated=true +src/archivematica/dashboard/frontend/app/locale/*.po linguist-generated=true +src/archivematica/dashboard/frontend/app/locale/*.pot linguist-generated=true +src/archivematica/dashboard/frontend/app/locale/translations.json linguist-generated=true +src/archivematica/dashboard/frontend/package-lock.json linguist-generated=true diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..de29d0f7bd --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,29 @@ +version: 2 +updates: + - package-ecosystem: "docker-compose" + directory: "/hack" + schedule: + interval: "daily" + open-pull-requests-limit: 1 + groups: + docker-compose-dev: + patterns: + - "*" + - package-ecosystem: "docker-compose" + directory: "/tests/integration" + schedule: + interval: "daily" + open-pull-requests-limit: 1 + groups: + docker-compose-tests: + patterns: + - "*" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 1 + groups: + github-actions: + patterns: + - "*" diff --git a/.github/workflows/acceptance-test.yml b/.github/workflows/acceptance-test.yml index e7ec2da7dc..3572845d5c 100644 --- a/.github/workflows/acceptance-test.yml +++ b/.github/workflows/acceptance-test.yml @@ -14,7 +14,7 @@ jobs: test: if: "${{ github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.event.label.name == 'AMAUAT') }}" name: "Test ${{ matrix.tag }} on ${{ matrix.browser }}" - runs-on: "ubuntu-22.04" + runs-on: "ubuntu-24.04" strategy: fail-fast: false matrix: @@ -35,12 +35,12 @@ jobs: steps: - name: "Check out repository" if: "${{ github.event_name != 'workflow_dispatch' }}" - uses: "actions/checkout@v4" + uses: "actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955" # v4.3.0 with: submodules: true - name: "Check out repository (manually triggered)" if: "${{ github.event_name == 'workflow_dispatch' }}" - uses: "actions/checkout@v4" + uses: "actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955" # v4.3.0 with: ref: "${{ inputs.am_version || 'qa/1.x' }}" submodules: true @@ -83,7 +83,7 @@ jobs: working-directory: ./hack - name: "Upload docker logs on failure" if: "${{ (failure() && steps.amauat-run.outcome == 'failure') || (cancelled() && steps.amauat-run.outcome == 'cancelled') }}" - uses: "actions/upload-artifact@v4" + uses: "actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02" # v4.6.2 with: name: "docker-logs-${{ matrix.tag }}-${{ matrix.browser }}" path: "./hack/docker-logs" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 50e5c8f1d5..ae48d5603c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,7 +34,7 @@ jobs: ARCHIVEMATICA_MCP_SERVER_IMAGE_TAG: ${{ steps.generate_mcp_server_image_tag.outputs.ARCHIVEMATICA_MCP_SERVER_IMAGE_TAG}} ARCHIVEMATICA_MCP_CLIENT_IMAGE_TAG: ${{ steps.generate_mcp_client_image_tag.outputs.ARCHIVEMATICA_MCP_CLIENT_IMAGE_TAG}} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd - name: Set ECR domain env var id: set_ecr_domain run: echo "ECR_DOMAIN=364159549467.dkr.ecr.$AWS_REGION.amazonaws.com" >> "$GITHUB_ENV" @@ -62,7 +62,7 @@ jobs: env: ARCHIVEMATICA_DASHBOARD_IMAGE_TAG: ${{ needs.generate_image_tags.outputs.ARCHIVEMATICA_DASHBOARD_IMAGE_TAG}} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd with: repository: ${{ inputs.target_repo }} - run: docker build -t $ARCHIVEMATICA_DASHBOARD_IMAGE_TAG --target archivematica-dashboard -f hack/Dockerfile . @@ -81,7 +81,7 @@ jobs: env: ARCHIVEMATICA_MCP_SERVER_IMAGE_TAG: ${{ needs.generate_image_tags.outputs.ARCHIVEMATICA_MCP_SERVER_IMAGE_TAG}} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd with: repository: ${{ inputs.target_repo }} - run: docker build -t $ARCHIVEMATICA_MCP_SERVER_IMAGE_TAG --target archivematica-mcp-server -f hack/Dockerfile . @@ -100,7 +100,7 @@ jobs: env: ARCHIVEMATICA_MCP_CLIENT_IMAGE_TAG: ${{ needs.generate_image_tags.outputs.ARCHIVEMATICA_MCP_CLIENT_IMAGE_TAG}} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd with: repository: ${{ inputs.target_repo }} - run: docker build -t $ARCHIVEMATICA_MCP_CLIENT_IMAGE_TAG --target archivematica-mcp-client -f hack/Dockerfile . diff --git a/.github/workflows/generate-sbom.yml b/.github/workflows/generate-sbom.yml index 68d6ceb108..45a8a36bef 100644 --- a/.github/workflows/generate-sbom.yml +++ b/.github/workflows/generate-sbom.yml @@ -14,13 +14,13 @@ jobs: options: --entrypoint "" steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - name: Set up cache - uses: actions/cache@v4 + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 with: path: .trivycache/ - key: ${{ runner.os }}-trivy-${{ hashFiles('requirements*.txt', 'src/dashboard/frontend/package-lock.json') }} + key: ${{ runner.os }}-trivy-${{ hashFiles('requirements*.txt', 'src/archivematica/dashboard/frontend/package-lock.json') }} restore-keys: | ${{ runner.os }}-trivy- @@ -30,7 +30,7 @@ jobs: TRIVY_NO_PROGRESS: "true" - name: Upload SBOM artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: sbom path: sbom.xml @@ -40,7 +40,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download SBOM artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: sbom diff --git a/.github/workflows/oidc-integration-test.yml b/.github/workflows/oidc-integration-test.yml index 9de9b1bf5f..bc963a56d2 100644 --- a/.github/workflows/oidc-integration-test.yml +++ b/.github/workflows/oidc-integration-test.yml @@ -8,7 +8,7 @@ jobs: runs-on: "ubuntu-24.04" steps: - name: "Check out repository" - uses: "actions/checkout@v4" + uses: "actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955" # v4.3.0 - name: "Save user id" id: user_id run: | @@ -18,7 +18,7 @@ jobs: run: | echo "group_id=$(id -g)" >> $GITHUB_OUTPUT - name: "Set up buildx" - uses: "docker/setup-buildx-action@v3" + uses: "docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435" # v3.11.1 - name: "Run tests" run: | ./run.sh @@ -27,7 +27,7 @@ jobs: env: USER_ID: ${{ steps.user_id.outputs.user_id }} GROUP_ID: ${{ steps.group_id.outputs.group_id }} - UBUNTU_VERSION: "22.04" + UBUNTU_VERSION: "24.04" PYTHON_VERSION: "3.9" COMPOSE_DOCKER_CLI_BUILD: 1 DOCKER_BUILDKIT: 1 diff --git a/.github/workflows/push-images.yml b/.github/workflows/push-images.yml new file mode 100644 index 0000000000..62a37a7c35 --- /dev/null +++ b/.github/workflows/push-images.yml @@ -0,0 +1,32 @@ +name: "Push images to Docker Hub" +on: workflow_dispatch +jobs: + build: + name: "Build and push images" + runs-on: "ubuntu-24.04" + strategy: + fail-fast: false + matrix: + component: + - archivematica-dashboard + - archivematica-mcp-client + - archivematica-mcp-server + steps: + - name: "Check out repository" + uses: "actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955" # v4.3.0 + - name: "Set up buildx" + uses: "docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435" # v3.11.1 + - name: "Log in to Docker Hub" + uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_TOKEN }} + - name: "Build and push ${{ matrix.component }}" + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 + with: + context: . + push: true + load: true + file: ./hack/Dockerfile + target: ${{ matrix.component }} + tags: artefactual/${{ matrix.component }}:latest diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ec8b2e2ee8..c55fc45bc9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,35 +2,62 @@ name: "Test" on: workflow_dispatch: - inputs: - am_version: - description: "Archivematica ref (branch, tag or SHA to checkout)" - default: "qa/1.x" - required: true - type: "string" pull_request: push: branches: - "qa/**" - "stable/**" jobs: + cache-pyenv-builder: + name: "Cache layers of the pyenv-builder stage" + runs-on: "ubuntu-24.04" + strategy: + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12"] + ubuntu-version: ["24.04"] + steps: + - name: "Check out repository" + uses: "actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955" # v4.3.0 + - name: "Set up Docker Buildx" + uses: "docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435" # v3.11.1 + with: + driver-opts: | + network=host + - name: "Generate cache key from requirements" + id: cache_key + run: | + echo "requirements_hash=${{ hashFiles('requirements*.txt', 'requirements*.in', 'pyproject.toml') }}" >> $GITHUB_OUTPUT + - name: "Build base layers" + uses: "docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83" # v6.18.0 + with: + context: . + file: ./hack/Dockerfile + target: pyenv-builder + build-args: | + UBUNTU_VERSION=${{ matrix.ubuntu-version }} + PYTHON_VERSION=${{ matrix.python-version }} + BUILDKIT_INLINE_CACHE=1 + push: false + cache-from: | + type=gha,scope=archivematica-tests-base-${{ matrix.ubuntu-version }}-${{ matrix.python-version }} + type=gha,scope=archivematica-tests-${{ matrix.ubuntu-version }}-${{ matrix.python-version }}-${{ steps.cache_key.outputs.requirements_hash }} + cache-to: | + type=gha,scope=archivematica-tests-base-${{ matrix.ubuntu-version }}-${{ matrix.python-version }},mode=max + type=gha,scope=archivematica-tests-${{ matrix.ubuntu-version }}-${{ matrix.python-version }}-${{ steps.cache_key.outputs.requirements_hash }},mode=max test: + needs: [cache-pyenv-builder] name: "Test ${{ matrix.rule }} on ${{ matrix.python-version }}" - runs-on: "ubuntu-22.04" + runs-on: "ubuntu-24.04" strategy: fail-fast: false matrix: rule: [ - "mcp-server", - "mcp-client", - "dashboard", - "archivematica-common", + "archivematica", ] ubuntu-version: [ - "22.04", + "24.04", ] python-version: [ - "3.8", "3.9", "3.10", "3.11", @@ -38,20 +65,14 @@ jobs: ] include: - rule: "storage-service" - ubuntu-version: "22.04" + ubuntu-version: "24.04" python-version: "3.9" - rule: "migrations" - ubuntu-version: "22.04" + ubuntu-version: "24.04" python-version: "3.9" steps: - name: "Check out repository" - if: "${{ github.event_name != 'workflow_dispatch' }}" - uses: "actions/checkout@v4" - - name: "Check out repository (manually triggered)" - if: "${{ github.event_name == 'workflow_dispatch' }}" - uses: "actions/checkout@v4" - with: - ref: "${{ inputs.am_version || 'qa/1.x' }}" + uses: "actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955" # v4.3.0 - name: "Check out the archivematica-storage-service submodule" run: | git submodule update --init hack/submodules/archivematica-storage-service/ @@ -63,10 +84,17 @@ jobs: id: group_id run: | echo "group_id=$(id -g)" >> $GITHUB_OUTPUT + - name: "Generate cache key from requirements" + id: cache_key + run: | + echo "requirements_hash=${{ hashFiles('requirements*.txt', 'requirements*.in', 'pyproject.toml') }}" >> $GITHUB_OUTPUT - name: "Set up buildx" - uses: "docker/setup-buildx-action@v3" + uses: "docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435" # v3.11.1 + with: + driver-opts: | + network=host - name: "Build archivematica-tests image" - uses: "docker/build-push-action@v6" + uses: "docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83" # v6.18.0 with: context: . file: ./hack/Dockerfile @@ -76,14 +104,28 @@ jobs: GROUP_ID=${{ steps.group_id.outputs.group_id }} UBUNTU_VERSION=${{ matrix.ubuntu-version }} PYTHON_VERSION=${{ matrix.python-version }} + BUILDKIT_INLINE_CACHE=1 tags: archivematica-tests:latest push: false load: true - cache-from: "type=gha,scope=archivematica-tests-${{ matrix.ubuntu-version }}-${{ matrix.python-version }}-${{ matrix.rule }}" - cache-to: "type=gha,scope=archivematica-tests-${{ matrix.ubuntu-version }}-${{ matrix.python-version }}-${{ matrix.rule }},mode=max" + cache-from: | + type=gha,scope=archivematica-tests-base-${{ matrix.ubuntu-version }}-${{ matrix.python-version }} + type=gha,scope=archivematica-tests-${{ matrix.ubuntu-version }}-${{ matrix.python-version }}-${{ matrix.rule }} + type=gha,scope=archivematica-tests-${{ matrix.ubuntu-version }}-${{ matrix.python-version }}-${{ steps.cache_key.outputs.requirements_hash }} + cache-to: | + type=gha,scope=archivematica-tests-base-${{ matrix.ubuntu-version }}-${{ matrix.python-version }},mode=max + type=gha,scope=archivematica-tests-${{ matrix.ubuntu-version }}-${{ matrix.python-version }}-${{ matrix.rule }},mode=max + type=gha,scope=archivematica-tests-${{ matrix.ubuntu-version }}-${{ matrix.python-version }}-${{ steps.cache_key.outputs.requirements_hash }},mode=max - name: "Create external volumes" run: | make -C hack/ create-volumes + - name: "Restore .tox cache" + uses: "actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809" # v4.2.4 + with: + path: ./.tox + key: tox-${{ matrix.python-version }}-${{ steps.cache_key.outputs.requirements_hash }} + restore-keys: | + tox-${{ matrix.python-version }}- - name: "Run make rule" run: | docker buildx install @@ -94,7 +136,7 @@ jobs: PYTHON_VERSION: ${{ matrix.python-version }} - name: "Upload coverage report" if: matrix.rule != 'storage-service' && matrix.rule != 'migrations' && github.repository == 'artefactual/archivematica' - uses: "codecov/codecov-action@v4" + uses: "codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238" # v4.6.0 with: files: ./coverage.xml fail_ci_if_error: false @@ -104,21 +146,21 @@ jobs: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} frontend: name: "Test frontend" - runs-on: "ubuntu-22.04" + runs-on: "ubuntu-24.04" defaults: run: - working-directory: "./src/dashboard/frontend/" + working-directory: "./src/archivematica/dashboard/frontend/" steps: - name: "Check out repository" if: "${{ github.event_name != 'workflow_dispatch' }}" - uses: "actions/checkout@v4" + uses: "actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955" # v4.3.0 - name: "Check out repository (manually triggered)" if: "${{ github.event_name == 'workflow_dispatch' }}" - uses: "actions/checkout@v4" + uses: "actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955" # v4.3.0 with: ref: "${{ inputs.am_version || 'qa/1.x' }}" - name: "Set up Node JS" - uses: "actions/setup-node@v4" + uses: "actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020" # v4.4.0 with: node-version: "20" cache: "npm" @@ -132,18 +174,18 @@ jobs: npm run "test-single-run" linting: name: "Lint" - runs-on: "ubuntu-22.04" + runs-on: "ubuntu-24.04" steps: - name: "Check out repository" if: "${{ github.event_name != 'workflow_dispatch' }}" - uses: "actions/checkout@v4" + uses: "actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955" # v4.3.0 - name: "Check out repository (manually triggered)" if: "${{ github.event_name == 'workflow_dispatch' }}" - uses: "actions/checkout@v4" + uses: "actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955" # v4.3.0 with: ref: "${{ inputs.am_version || 'qa/1.x' }}" - name: "Set up Python 3.9" - uses: "actions/setup-python@v5" + uses: "actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065" # v5.6.0 with: python-version: "3.9" cache: "pip" diff --git a/.github/workflows/validate-action-pinning.yml b/.github/workflows/validate-action-pinning.yml new file mode 100644 index 0000000000..2b44adad0a --- /dev/null +++ b/.github/workflows/validate-action-pinning.yml @@ -0,0 +1,25 @@ +name: Validate GitHub Action pinning + +on: + pull_request: + paths: + - ".github/workflows/**" + push: + branches: + - "qa/**" + - "stable/**" + paths: + - ".github/workflows/**" + +permissions: + contents: read + +jobs: + enforce-pinning: + name: Enforce commit SHA pinning + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + - name: Ensure SHA-pinned Actions + uses: zgosalvez/github-actions-ensure-sha-pinned-actions@fc87bb5b5a97953d987372e74478de634726b3e5 # v3.0.25 diff --git a/.gitignore b/.gitignore index 5bd2242d36..9e68de73ba 100644 --- a/.gitignore +++ b/.gitignore @@ -23,5 +23,8 @@ .tox/ # frontend builds -src/dashboard/src/static/ -src/dashboard/src/media/js/build +src/archivematica/dashboard/static/ +src/archivematica/dashboard/media/js/build + +# setuptools build +**/*.egg-info diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cc9073a7d6..e7e2605ebc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,16 +1,16 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: pretty-format-json args: [--no-ensure-ascii, --autofix] files: | (?x)^( - src/MCPServer/lib/assets/.*\.json| - src/(MCPClient/MCPServer|dashboard)/install/.*\.json + src/archivematica/MCPServer/assets/.*\.json| + src/archivematica/(MCPClient|MCPServer|dashboard)/install/.*\.json ) - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.0 + rev: v0.12.12 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] @@ -21,13 +21,13 @@ repos: - id: django-upgrade args: [--target-version, "4.2"] - repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.42.0 + rev: v0.45.0 hooks: - id: markdownlint exclude: | (?x)^( ISSUE_TEMPLATE\.md| - src/dashboard/(frontend/app|src/media)/vendor/.* + src/archivematica/dashboard/(frontend/app|media)/vendor/.* ) - repo: https://github.com/thlorenz/doctoc rev: v2.2.0 @@ -37,7 +37,7 @@ repos: (?x)^( (CONTRIBUTING|SECURITY).*\.md| hack/README\.md| - src/(MCPServer|MCPClient|dashboard)/install/README\.md + src/archivematica/(MCPServer|MCPClient|dashboard)/install/README\.md ) - repo: https://github.com/citation-file-format/cffconvert # Current "2.0.0" git tag does not work because of @@ -46,7 +46,7 @@ repos: hooks: - id: validate-cff - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.12.1 + rev: v1.17.1 hooks: - id: mypy additional_dependencies: @@ -54,12 +54,12 @@ repos: - types-python-dateutil - pytest - repo: https://github.com/tcort/markdown-link-check - rev: v3.12.2 + rev: v3.13.7 hooks: - id: markdown-link-check stages: [manual] args: [--quiet, --config, .markdown-link-check.json] exclude: | (?x)^( - src/dashboard/src/media/vendor/.*\.md + src/archivematica/dashboard/media/vendor/.*\.md ) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3cfc172360..9f54edce5c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -122,6 +122,17 @@ submitted for code review should contain commits covering a related section of code. Try not to bundle unrelated changes together in one branch; it makes review harder. +When naming branches, Artefactual uses the following naming format: +`dev/issue-####-short-description` + +- `dev/` marks it as being a development branch, not for QA integration or + a stable release + +- `issue-####` is the issue that the work is mostly related to + +- `short-description` is a description of the branch in a few words, to make it + easier to remember what the topic is. Example: `dev/issue-8161-dashboard-i18n` + If you're not familiar with forking repositories and creating branches in GitHub, consult their [guide]. @@ -215,8 +226,8 @@ Alternatively, you may send a printed, signed agreement to: ```text Artefactual Systems Inc. -201 - 301 Sixth Street -New Westminster BC V3L 3A7 +#2 - 10138 Whalley Blvd. +Surrey BC V3T 4H4 Canada ``` @@ -429,7 +440,7 @@ Further content comes after a blank line. [Line comment]: https://i.imgur.com/FsWppGN.png [code review guidelines]: https://github.com/artefactual/archivematica/blob/qa/1.x/code_review.md [interactive rebase feature]: https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History -[Contributor's Agreement]: https://wiki.archivematica.org/images/e/e6/Archivematica-CLA-firstname-lastname-YYYY.pdf +[Contributor's Agreement]: https://drive.google.com/file/d/1z7D2pK7ts82qLNcqUDENA8_ETNuaSADR/view?usp=sharing [Apache Foundation]: http://apache.org [contributor license]: http://www.apache.org/licenses/icla.txt [Artefactual Systems]: http://artefactual.com diff --git a/Makefile b/Makefile index a06623c6ed..e0ca9a5b16 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,12 @@ .PHONY: pip-compile pip-compile: # Compile pip requirements - pip-compile --allow-unsafe --output-file requirements.txt requirements.in - pip-compile --allow-unsafe --output-file requirements-dev.txt requirements-dev.in + pip-compile --allow-unsafe --output-file requirements.txt pyproject.toml + pip-compile --allow-unsafe --extra dev --output-file requirements-dev.txt pyproject.toml .PHONY: pip-upgrade pip-upgrade: # Upgrade pip requirements - pip-compile --allow-unsafe --upgrade --output-file requirements.txt requirements.in - pip-compile --allow-unsafe --upgrade --output-file requirements-dev.txt requirements-dev.in + pip-compile --allow-unsafe --upgrade --output-file requirements.txt pyproject.toml + pip-compile --allow-unsafe --upgrade --extra dev --output-file requirements-dev.txt pyproject.toml .PHONY: pip-sync pip-sync: # Sync virtualenv diff --git a/README.md b/README.md index e66f3ed493..4b11e12372 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ See the [LICENSE] file for details. * [Website] for the user and administrator documentation * [Presentations repo] and [YouTube channel] for previous trainings and talks +* [Case studies repo] for a collection of resources about working on and with +Archivematica * [User Group] is a forum/mailing list for user questions (both technical and end-user) * [Paid support] is for paid support, hosting, training, consulting @@ -36,6 +38,8 @@ and software development contracts from Artefactual analysis and community resources * [Issues] is the Git repository used for tracking Archivematica issues and feature/enhancement ideas +* The [Archivematica roadmap] contains current release information and potential +future works ## Contributing @@ -43,21 +47,26 @@ Thank you for your interest in Archivematica! For more details, see the [contributing guidelines]. -Read about our [merging process], including branch naming conventions, and -make any [documentation update] reflecting the changes introduced by your -contribution. +Read about our guidelines for making [pull requests], including [contribution +standards], and make any [documentation updates] reflecting the changes +introduced by your contribution. -You might have questions about the history of developement decisions: find +Our [Contributors Portal] contains useful background information and +instructions on translations in Archivematica. + +You might have questions about the history of development decisions: find answers in the [Architectural Decisions Record]. -The [Wiki] currently holds the release notes and previous developer facing -documentation. +Our current release notes are available on GitHub. Release notes since version +0.6 are available in the [RELEASENOTES file]. ## Reporting an issue Issues related to Archivematica, the Storage Service, or any related repository can be filed in the [Issues] repository. +Read our guide about [issue reporting]. + ### Security If you have a security concern about Archivematica or any related repository, @@ -77,33 +86,38 @@ Archivematica consists of several projects working together, including: the Format Policy Registry (FPR) server that displays and updates FPR rules and commands -For more projects in the Archivematica ecosystem, -see the [getting started] page. +For more projects in the Archivematica ecosystem, see the list on the +[dependencies] page of the [Contributors Portal]. [Architectural Decisions Record]: https://adr.archivematica.org/ [Archivematica API documentation]: https://archivematica.org/docs/latest/dev-manual/api/api-reference-archivematica/#api-reference-archivematica [Archivematica Codecov]: https://codecov.io/gh/artefactual/archivematica [Archivematica GitHub]: https://github.com/artefactual/archivematica [Archivematica]: https://www.archivematica.org/ +[Archivematica roadmap]: https://github.com/archivematica/Issues/wiki/Archivematica-Roadmap [Artefactual]: https://www.artefactual.com/ +[Case studies repo]: https://github.com/archivematica/archivematica-case-studies [codecov]: https://codecov.io/gh/artefactual/archivematica/branch/qa/1.x/graph/badge.svg?token=tKlfjhmrlC [contributing guidelines]: https://github.com/artefactual/archivematica/blob/qa/1.x/CONTRIBUTING.md +[contribution standards]: https://github.com/artefactual/archivematica/blob/qa/1.x/CONTRIBUTING.md#contribution-standards +[Contributors Portal]: https://contributors.artefactual.com/ +[dependencies]: https://contributors.artefactual.com/dependencies.html#archivematica [Developer guide]: https://github.com/artefactual/archivematica/blob/qa/1.x/hack/README.md [Development installation]: https://github.com/artefactual/archivematica/tree/qa/1.x/hack -[documentation update]: https://github.com/artefactual/archivematica-docs/wiki -[Format Policy Registry]: https://github.com/artefactual/archivematica/tree/qa/1.x/src/dashboard/src/fpr -[getting started]: https://wiki.archivematica.org/Getting_started#Projects +[documentation updates]: https://github.com/artefactual/archivematica-docs/wiki +[Format Policy Registry]: https://github.com/artefactual/archivematica/tree/qa/1.x/src/archivematica/dashboard/fpr [GitHub CI]: https://github.com/artefactual/archivematica/actions/workflows/test.yml/badge.svg +[issue reporting]: https://github.com/archivematica/Issues/blob/main/CONTRIBUTING.md [Issues]: https://github.com/archivematica/Issues [LICENSE]: LICENSE -[merging process]: https://wiki.archivematica.org/Merging [Paid support]: https://www.artefactual.com/services/ [Production installation]: https://www.archivematica.org/docs/latest/admin-manual/installation-setup/installation/installation/ +[pull requests]: https://github.com/artefactual/archivematica/blob/qa/1.x/CONTRIBUTING.md#getting-started +[RELEASENOTES file]: RELEASENOTES.md [SECURITY file]: SECURITY.md [Presentations repo]: https://slideshare.net/Archivematica/presentations [Storage Service]: https://github.com/artefactual/archivematica-storage-service [Test workflow]: https://github.com/artefactual/archivematica/actions/workflows/test.yml [User Group]: https://groups.google.com/forum/#!forum/archivematica [Website]: https://www.archivematica.org/ -[Wiki]: https://wiki.archivematica.org/Development [YouTube channel]: https://www.youtube.com/@ArtefactualSystems diff --git a/RELEASENOTES.md b/RELEASENOTES.md index ab2ee070e4..c268e34e9c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -18,7 +18,141 @@ contributors - thank you! Questions about a release or the release notes? Ask on the [Archivematica User Group](https://groups.google.com/g/archivematica)! -## (Current Release) Archivematica 1.17.1 +## (Current Release) Archivematica 1.18.0 and Storage Service 0.24.0 + +*Release date*: September 26, 2025 + +[Original release notes][1.18.0 release notes]. + +[1.18.0 release notes]: https://wiki.archivematica.org/Archivematica_1.18.0_and_Storage_Service_0.24.0_release_notes + +### Environments for 1.18.0 + +Please see the [installation instructions][1.18.0 installation instructions]. + +[1.18.0 installation instructions]: https://www.archivematica.org/en/docs/archivematica-1.18/admin-manual/installation-setup/installation/installation/#installation + +Archivematica 1.18.0 and Storage Service 0.24.0 have been tested in the +following environments: + +- Ubuntu 24.04 64-bit Server Edition +- Rocky Linux 9 x86_64 + +For development purposes, most of our developers prefer to use Docker +containers. These and all above environments are linked from the installation +instructions above. + +### Changed in 1.18.0 + +#### Elasticsearch 8.x + +- Issue: + +Archivematica 1.18.0 no longer supports Elasticsearch 6.x and is now compatible +with version 8.x. For instructions on migrating your Elasticsearch data, refer +to the Upgrade Elasticsearch from 6.x to 8.x section of the Upgrade page in the documentation. + +#### Ubuntu 24.04 + +- Issue: + +Archivematica 1.18.0 supports Ubuntu 24.04 and drops support for Ubuntu 20.04. + +#### JHOVE validation commands + +- Issue: + +New JHOVE validation commands have been implemented to prevent false positives +that occur when JHOVE encounters the built-in BYTESTREAM module following +validation errors. The existing validation rules have been updated to use these +new commands. + +The new commands also capture errors from the command output and include them in +the outcome detail note of the validation PREMIS event within the METS file. + +Special thanks to Felicitas Günther for sharing their error-handling implementation. + +#### JHOVE 1.34.0 + +- Issue: + +#### PRONOM v120 + +- Issue: + +#### Cookie configuration improvements + +New application variables have been added to control cookie flags (Secure, +HttpOnly, SameSite) in the Archivematica Dashboard and Storage Service. This +change enhances session protection and reduces risks such as session hijacking +and cross-site request forgery, particularly in HTTPS deployments. + +Because default values for several cookie-related settings have changed, this +update is backward incompatible with previous deployments. In particular, if +your deployment is not using HTTPS, you must explicitly set the +`*_SESSION_COOKIE_SECURE` and `*_CSRF_COOKIE_SECURE` application variables to +`false` to restore the previous behavior. Deployments already configured with +HTTPS should not be affected by these changes. Administrators should review +their settings to take advantage of these options ensuring deployment +configurations are updated to align with these stricter defaults. + +**Note on OIDC authentication**: If your deployment uses OIDC authentication +across different domains, you may need to adjust the `*_SESSION_COOKIE_SAMESITE` +settings. By default, they are set to `Strict`, which may prevent cross-domain +login flows. In such cases, relax the settings to `Lax` to allow OIDC +authentication to function correctly across domains. + +##### Dashboard application variables + +| Variable Name | Previous Value | New Value | +|-----------------------------------------------------------|----------------|-----------| +| ARCHIVEMATICA_DASHBOARD_DASHBOARD_SESSION_COOKIE_SECURE | false | true | +| ARCHIVEMATICA_DASHBOARD_DASHBOARD_SESSION_COOKIE_HTTPONLY | false | true | +| ARCHIVEMATICA_DASHBOARD_DASHBOARD_SESSION_COOKIE_SAMESITE | Lax | Strict | +| ARCHIVEMATICA_DASHBOARD_DASHBOARD_CSRF_COOKIE_SECURE | false | true | +| ARCHIVEMATICA_DASHBOARD_DASHBOARD_CSRF_COOKIE_HTTPONLY | false | false | +| ARCHIVEMATICA_DASHBOARD_DASHBOARD_CSRF_COOKIE_SAMESITE | Lax | Strict | + +##### Storage Service application-specific environment variables + +| Variable Name | Previous Value | New Value | +|-------------------------|----------------|-----------| +| SESSION_COOKIE_SECURE | false | true | +| SESSION_COOKIE_HTTPONLY | false | true | +| SESSION_COOKIE_SAMESITE | Lax | Strict | +| CSRF_COOKIE_SECURE | false | true | +| CSRF_COOKIE_HTTPONLY | false | true | +| CSRF_COOKIE_SAMESITE | Lax | Strict | + +### Fixed in 1.18.0 + +#### Users must re-authenticate when changing their passwords + +- Issue: + +#### User API keys are not displayed anymore + +- Issue: +- Issue: + +#### METS file generation works with bags with no metadata + +- Issue: + +#### Transfer status check has been optimized + +- Issue: + +Special thanks to [Liam Lloyd-Tucker](https://github.com/liam-lloyd) for this contribution. + +#### Transcribe job handles invalid arguments + +- Issue: + +Please see the 1.18.0 milestone in GitHub for all issues addressed in this +release: . + +## Archivematica 1.17.1 *Release date*: May 20, 2025 @@ -797,7 +931,7 @@ which should no longer be relevant since resolving [Issue 1446](https://github.com/archivematica/Issues/issues/1446)- active transfers or SIPs can no longer be hidden. However as a precaution, we have made that command available within Archivematica -(). +(). #### DIP storage locations now allowed in S3 spaces @@ -2239,6 +2373,7 @@ uses RPMs. There are no changes to supported environments in this release. Please continue to follow the installation instructions + [here](https://www.archivematica.org/en/docs/archivematica-1.8/admin-manual/installation-setup/installation/installation/#installation). ### Fixed for 1.8.1 @@ -2814,6 +2949,7 @@ documentation](https://www.archivematica.org/docs/archivematica-1.7/). ### Supported environments for 1.7 Installation instructions are available + [here](https://www.archivematica.org/en/docs/archivematica-1.7/admin-manual/installation-setup/installation/installation/#installation). Archivematica can be installed using packages or Ansible scripts in @@ -3025,6 +3161,7 @@ Libraries](http://library.columbia.edu/). Thank you! A README file in html format has been added to the AIP containing a brief explanation of what the AIP is, how it was created and how it is structured. See the contents of the README + [here](https://github.com/artefactual/archivematica/blob/f1e04ffe528156f83a137c6c4dbd228e6b53f5b2/src/MCPClient/lib/assets/README/README.html). This feature was sponsored by the [Denver Art Museum](https://denverartmuseum.org/). Thank you! @@ -3434,6 +3571,7 @@ archives." #### Bug fixes for 1.6 Please see bug fixes + [here](https://projects.artefactual.com/projects/archivematica/issues?query_id=79) ## Archivematica 1.5.1 Release Notes diff --git a/hack/.gitignore b/hack/.gitignore new file mode 100644 index 0000000000..f10862a655 --- /dev/null +++ b/hack/.gitignore @@ -0,0 +1 @@ +/.env diff --git a/hack/Dockerfile b/hack/Dockerfile index 348c7d018c..39ac0bd4fe 100644 --- a/hack/Dockerfile +++ b/hack/Dockerfile @@ -1,5 +1,5 @@ ARG TARGET=archivematica-mcp-server -ARG UBUNTU_VERSION=22.04 +ARG UBUNTU_VERSION=24.04 ARG USER_ID=1000 ARG GROUP_ID=1000 ARG PYTHON_VERSION=3.9 @@ -7,6 +7,7 @@ ARG NODE_VERSION=20 ARG PYENV_DIR=/pyenv ARG SELENIUM_DIR=/selenium ARG MEDIAAREA_VERSION=1.0-25 +ARG JHOVE_VERSION=1.34.0-1~22.04 # ----------------------------------------------------------------------------- @@ -17,7 +18,9 @@ ARG PYENV_DIR=/pyenv ENV DEBIAN_FRONTEND=noninteractive ENV PYTHONUNBUFFERED=1 -RUN set -ex \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + set -ex \ && apt-get update \ && apt-get install -y --no-install-recommends \ ca-certificates \ @@ -31,8 +34,7 @@ RUN set -ex \ locales \ make \ pkg-config \ - tzdata \ - && rm -rf /var/lib/apt/lists/* /var/cache/apt/* + tzdata RUN locale-gen en_US.UTF-8 ENV LANG=en_US.UTF-8 @@ -48,7 +50,9 @@ FROM base-builder AS pyenv-builder ARG PYTHON_VERSION=3.9 -RUN set -ex \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + set -ex \ && apt-get update \ && apt-get install -y --no-install-recommends \ build-essential \ @@ -63,8 +67,7 @@ RUN set -ex \ libxmlsec1-dev \ tk-dev \ xz-utils \ - zlib1g-dev \ - && rm -rf /var/lib/apt/lists/* /var/cache/apt/* + zlib1g-dev RUN set -ex \ && curl --retry 3 -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash \ @@ -73,7 +76,8 @@ RUN set -ex \ COPY --link requirements-dev.txt /src/requirements-dev.txt -RUN set -ex \ +RUN --mount=type=cache,target=/root/.cache/pip,sharing=locked \ + set -ex \ && pyenv exec python3 -m pip install --upgrade pip setuptools \ && pyenv exec python3 -m pip install --requirement /src/requirements-dev.txt \ && pyenv rehash @@ -84,17 +88,19 @@ FROM base-builder AS browsers-builder ARG SELENIUM_DIR=/selenium -RUN set -ex \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + set -ex \ && apt-get update \ && apt-get install -y --no-install-recommends \ - jq \ - && rm -rf /var/lib/apt/lists/* /var/cache/apt/* + jq RUN set -ex \ && SELENIUM_CACHE=${SELENIUM_DIR}/cache \ && SELENIUM_BIN=${SELENIUM_DIR}/bin \ && mkdir -p $SELENIUM_CACHE $SELENIUM_BIN \ - && curl -o $SELENIUM_BIN/selenium-manager -L https://github.com/SeleniumHQ/selenium/raw/selenium-4.16.0/common/manager/linux/selenium-manager \ + && SELENIUM_MANAGER_DOWNLOAD_URL=$(curl -sL -o /dev/null -w '%{url_effective}' https://github.com/SeleniumHQ/selenium_manager_artifacts/releases/latest | sed -e 's|/tag/\(.*\)|/download/\1/selenium-manager-linux|g') \ + && curl -o $SELENIUM_BIN/selenium-manager -L $SELENIUM_MANAGER_DOWNLOAD_URL \ && chmod +x $SELENIUM_BIN/selenium-manager \ && CHROME_OUTPUT=$($SELENIUM_BIN/selenium-manager --cache-path $SELENIUM_CACHE --browser chrome --output JSON) \ && FIREFOX_OUTPUT=$($SELENIUM_BIN/selenium-manager --cache-path $SELENIUM_CACHE --browser firefox --output JSON) \ @@ -107,9 +113,9 @@ RUN set -ex \ FROM node:${NODE_VERSION} AS archivematica-dashboard-frontend-builder -COPY --link src/dashboard/frontend /src/src/dashboard/frontend +COPY --link src/archivematica/dashboard/frontend /src/src/archivematica/dashboard/frontend -WORKDIR /src/src/dashboard/frontend +WORKDIR /src/src/archivematica/dashboard/frontend RUN set ex \ && npm ci @@ -118,12 +124,14 @@ RUN set ex \ FROM node:${NODE_VERSION} AS archivematica-dashboard-testing -ARG USER_ID=1000 -ARG GROUP_ID=1000 +ARG USER_ID +ARG GROUP_ID ARG SELENIUM_DIR=/selenium ENV PATH=${SELENIUM_DIR}/bin:$PATH -RUN set -ex \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + set -ex \ && apt-get update \ && apt-get -y --no-install-recommends install \ libasound2 \ @@ -135,14 +143,13 @@ RUN set -ex \ libnss3 \ libx11-xcb1 \ libxcb1 \ - libxtst6 \ - && rm -rf /var/lib/apt/lists/* /var/cache/apt/* + libxtst6 COPY --chown=${USER_ID}:${GROUP_ID} --from=browsers-builder --link ${SELENIUM_DIR} ${SELENIUM_DIR} -COPY --chown=${USER_ID}:${GROUP_ID} --from=archivematica-dashboard-frontend-builder --link /src/src/dashboard/frontend/node_modules /src/src/dashboard/frontend/node_modules -COPY --link src/dashboard/frontend /src/src/dashboard/frontend +COPY --chown=${USER_ID}:${GROUP_ID} --from=archivematica-dashboard-frontend-builder --link /src/src/archivematica/dashboard/frontend/node_modules /src/src/archivematica/dashboard/frontend/node_modules +COPY --link src/archivematica/dashboard/frontend /src/src/archivematica/dashboard/frontend -WORKDIR /src/src/dashboard/frontend +WORKDIR /src/src/archivematica/dashboard/frontend ENTRYPOINT ["npm", "run", "test-single-run"] @@ -150,17 +157,22 @@ ENTRYPOINT ["npm", "run", "test-single-run"] FROM base-builder AS base -ARG USER_ID=1000 -ARG GROUP_ID=1000 +ARG USER_ID +ARG GROUP_ID ARG PYENV_DIR=/pyenv ARG MEDIAAREA_VERSION +ARG JHOVE_VERSION RUN set -ex \ - && curl --retry 3 -fsSL https://packages.archivematica.org/1.17.x/key.asc | gpg --dearmor -o /etc/apt/keyrings/archivematica-1.17.x.gpg \ - && echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/archivematica-1.17.x.gpg] http://packages.archivematica.org/1.17.x/ubuntu-externals jammy main" > /etc/apt/sources.list.d/archivematica-external.list \ + && curl --retry 3 -fsSL https://packages.archivematica.org/1.18.x/key.asc | gpg --dearmor -o /etc/apt/keyrings/archivematica-1.18.x.gpg \ + && echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/archivematica-1.18.x.gpg] http://packages.archivematica.org/1.18.x/ubuntu-externals jammy main" > /etc/apt/sources.list.d/archivematica-external.list \ && curl --retry 3 -so /tmp/repo-mediaarea.deb -L https://mediaarea.net/repo/deb/repo-mediaarea_${MEDIAAREA_VERSION}_all.deb \ && dpkg -i /tmp/repo-mediaarea.deb \ - && rm /tmp/repo-mediaarea.deb \ + && rm /tmp/repo-mediaarea.deb + +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + set -ex \ && apt-get update \ && apt-get install -y --no-install-recommends \ atool \ @@ -176,7 +188,7 @@ RUN set -ex \ hashdeep \ imagemagick \ inkscape \ - jhove \ + jhove=${JHOVE_VERSION} \ libffi-dev \ libimage-exiftool-perl \ libldap2-dev \ @@ -204,10 +216,18 @@ RUN set -ex \ unar \ unrar-free \ uuid \ - wkhtmltopdf \ - && rm -rf /var/lib/apt/lists/* /var/cache/apt/* + wkhtmltopdf +# Ensure the requested UID/GID do not clash with defaults in the Ubuntu base +# image. We look up any existing user/group that already uses such identifiers +# and remove them to avoid conflicts before creating the archivematica account. RUN set -ex \ + && { \ + grp=$(getent group "${GROUP_ID}" | cut -d: -f1 || true); \ + if [ -n "${grp}" ]; then groupdel --force "${grp}"; fi; \ + usr=$(getent passwd "${USER_ID}" | cut -d: -f1 || true); \ + if [ -n "${usr}" ]; then userdel --remove "${usr}"; fi; \ + } \ && groupadd --gid ${GROUP_ID} --system archivematica \ && useradd --uid ${USER_ID} --gid ${GROUP_ID} --home-dir /var/archivematica --system archivematica \ && mkdir -p /var/archivematica/sharedDirectory \ @@ -221,72 +241,60 @@ USER archivematica COPY --chown=${USER_ID}:${GROUP_ID} --from=pyenv-builder --link ${PYENV_DIR} ${PYENV_DIR} COPY --chown=${USER_ID}:${GROUP_ID} --link . /src +ENV PYTHONPATH=/src/src + # ----------------------------------------------------------------------------- FROM base AS archivematica-mcp-client -ENV DJANGO_SETTINGS_MODULE=settings.common -ENV PYTHONPATH=/src/src/MCPClient/lib/:/src/src/MCPClient/lib/clientScripts:/src/src/archivematicaCommon/lib/:/src/src/dashboard/src/ -ENV ARCHIVEMATICA_MCPCLIENT_ARCHIVEMATICACLIENTMODULES=/src/src/MCPClient/lib/archivematicaClientModules -ENV ARCHIVEMATICA_MCPCLIENT_CLIENTASSETSDIRECTORY=/src/src/MCPClient/lib/assets/ -ENV ARCHIVEMATICA_MCPCLIENT_CLIENTSCRIPTSDIRECTORY=/src/src/MCPClient/lib/clientScripts/ +ENV DJANGO_SETTINGS_MODULE=archivematica.MCPClient.settings.common -# Some scripts in archivematica-fpr-admin executed by MCPClient rely on certain -# files being available in this image (e.g. see https://git.io/vA1wF). -COPY --link src/archivematicaCommon/lib/externals/fido/ /usr/lib/archivematica/archivematicaCommon/externals/fido/ -COPY --link src/archivematicaCommon/lib/externals/fiwalk_plugins/ /usr/lib/archivematica/archivematicaCommon/externals/fiwalk_plugins/ - -ENTRYPOINT ["pyenv", "exec", "python3", "/src/src/MCPClient/lib/archivematicaClient.py"] +ENTRYPOINT ["pyenv", "exec", "python3", "-m", "archivematica.MCPClient.archivematicaClient"] # ----------------------------------------------------------------------------- FROM base AS archivematica-mcp-server -ENV DJANGO_SETTINGS_MODULE=settings.common -ENV PYTHONPATH=/src/src/MCPServer/lib/:/src/src/archivematicaCommon/lib/:/src/src/dashboard/src/ +ENV DJANGO_SETTINGS_MODULE=archivematica.MCPServer.settings.common -ENTRYPOINT ["pyenv", "exec", "python3", "/src/src/MCPServer/lib/archivematicaMCP.py"] +ENTRYPOINT ["pyenv", "exec", "python3", "-m", "archivematica.MCPServer.archivematicaMCP"] # ----------------------------------------------------------------------------- FROM base AS archivematica-dashboard -ARG USER_ID=1000 -ARG GROUP_ID=1000 +ARG USER_ID +ARG GROUP_ID ARG PYTHON_VERSION=3.9 USER root RUN set -ex \ && internalDirs=' \ - /src/src/dashboard/frontend \ - /src/src/dashboard/src/static \ - /src/src/dashboard/src/media \ + /src/src/archivematica/dashboard/frontend \ + /src/src/archivematica/dashboard/static \ + /src/src/archivematica/dashboard/media \ ' \ && mkdir -p $internalDirs \ && chown -R archivematica $internalDirs USER archivematica -COPY --chown=${USER_ID}:${GROUP_ID} --from=archivematica-dashboard-frontend-builder --link /src/src/dashboard/src/media/js/ /src/src/dashboard/src/media/js - -WORKDIR /src/src/dashboard/src +COPY --chown=${USER_ID}:${GROUP_ID} --from=archivematica-dashboard-frontend-builder --link /src/src/archivematica/dashboard/media/js/ /src/src/archivematica/dashboard/media/js -ENV DJANGO_SETTINGS_MODULE=settings.local -ENV PYTHONPATH=/src/src/dashboard/src/:/src/src/archivematicaCommon/lib/ +ENV DJANGO_SETTINGS_MODULE=archivematica.dashboard.settings.local ENV AM_GUNICORN_BIND=0.0.0.0:8000 -ENV AM_GUNICORN_CHDIR=/src/src/dashboard/src ENV FORWARDED_ALLOW_IPS=* RUN set -ex \ - && ./manage.py collectstatic --noinput --clear \ - && ./manage.py compilemessages + && python3 -m archivematica.dashboard.manage collectstatic --noinput --clear \ + && python3 -m archivematica.dashboard.manage compilemessages -ENV DJANGO_SETTINGS_MODULE=settings.production +ENV DJANGO_SETTINGS_MODULE=archivematica.dashboard.settings.production EXPOSE 8000 -ENTRYPOINT ["pyenv", "exec", "python3", "-m", "gunicorn", "--config=/src/src/dashboard/install/dashboard.gunicorn-config.py", "wsgi:application"] +ENTRYPOINT ["pyenv", "exec", "python3", "-m", "gunicorn", "--config=/src/src/archivematica/dashboard/install/dashboard.gunicorn-config.py", "archivematica.dashboard.wsgi:application"] # ----------------------------------------------------------------------------- @@ -298,7 +306,9 @@ FROM base AS archivematica-dashboard-integration-tests USER root -RUN set -ex \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + set -ex \ && python3 -m playwright install-deps firefox USER archivematica @@ -307,8 +317,6 @@ RUN set -ex \ && mkdir -p /var/archivematica/.cache/ms-playwright \ && python3 -m playwright install firefox -ENV PYTHONPATH=/src/src/dashboard/src/:/src/src/archivematicaCommon/lib/ - # ----------------------------------------------------------------------------- FROM ${TARGET} diff --git a/hack/Makefile b/hack/Makefile index 24b07ce58d..3a919feaaf 100644 --- a/hack/Makefile +++ b/hack/Makefile @@ -25,10 +25,10 @@ define compose_tests docker compose -f docker-compose.yml -f docker-compose.tests.yml $(1) endef -define run_toxenvs +define run_toxenv $(call compose_tests, \ run \ - -e DJANGO_SETTINGS_MODULE=settings.testmysql \ + -e DJANGO_SETTINGS_MODULE=$(2) \ -e PYTEST_ADDOPTS=$(subst $(SPACE),\$(SPACE),${PYTEST_ADDOPTS}) \ --user $(CALLER_UID):$(CALLER_GID) \ --workdir /src \ @@ -102,13 +102,13 @@ bootstrap-storage-service: ## Bootstrap Storage Service (new database). docker compose run \ --rm \ --no-deps \ - --entrypoint /src/storage_service/manage.py \ + --entrypoint /src/src/archivematica/storage_service/manage.py \ archivematica-storage-service \ migrate --noinput docker compose run \ --rm \ --no-deps \ - --entrypoint /src/storage_service/manage.py \ + --entrypoint /src/src/archivematica/storage_service/manage.py \ archivematica-storage-service \ create_user \ --username="test" \ @@ -125,7 +125,7 @@ makemigrations-ss: --user $(CALLER_UID):$(CALLER_GID) \ --rm \ --no-deps \ - --entrypoint /src/storage_service/manage.py \ + --entrypoint /src/src/archivematica/storage_service/manage.py \ archivematica-storage-service \ makemigrations @@ -134,7 +134,7 @@ manage-dashboard: ## Run Django /manage.py on Dashboard, suppling [op --user $(CALLER_UID):$(CALLER_GID) \ --rm \ --no-deps \ - --entrypoint /src/src/dashboard/src/manage.py \ + --entrypoint /src/src/archivematica/dashboard/manage.py \ archivematica-dashboard \ $(ARG) @@ -143,7 +143,7 @@ manage-ss: ## Run Django /manage.py on Storage Service, suppling [opt --user $(CALLER_UID):$(CALLER_GID) \ --rm \ --no-deps \ - --entrypoint /src/storage_service/manage.py \ + --entrypoint /src/src/archivematica/storage_service/manage.py \ archivematica-storage-service \ $(ARG) @@ -152,13 +152,13 @@ bootstrap-dashboard-db: ## Bootstrap Dashboard (new database). docker compose run \ --rm \ --no-deps \ - --entrypoint /src/src/dashboard/src/manage.py \ + --entrypoint /src/src/archivematica/dashboard/manage.py \ archivematica-dashboard \ migrate --noinput docker compose run \ --rm \ --no-deps \ - --entrypoint /src/src/dashboard/src/manage.py \ + --entrypoint /src/src/archivematica/dashboard/manage.py \ archivematica-dashboard \ install \ --username="test" \ @@ -182,7 +182,7 @@ bootstrap-dashboard-frontend: ## Build front-end assets. -e HOME=/tmp/npm-config \ --rm \ --user $(CALLER_UID):$(CALLER_GID) \ - --volume "$(SRCDIR)/dashboard:/src/src/dashboard" \ + --volume "$(SRCDIR)/archivematica/dashboard:/src/src/archivematica/dashboard" \ --entrypoint npm \ archivematica-dashboard-frontend-builder \ ci @@ -275,39 +275,42 @@ __TEST_DBS_MCPCLIENT = MCPCLIENTTEST test_MCPCLIENTTEST test_MCPCLIENTTEST_mcp-c __TEST_DBS_MIGRATIONS = DASHBOARDTEST SSTEST __TEST_DBS_DASHBOARD = DASHBOARDTEST test_DASHBOARDTEST test_DASHBOARDTEST_dashboard test_DASHBOARDTEST_archivematica-common __TEST_DBS_MCPSERVER = MCPSERVERTEST test_MCPSERVERTEST test_MCPSERVERTEST_mcp-server +__TEST_DBS_ARCHIVEMATICA = ARCHIVEMATICATEST test_ARCHIVEMATICATEST% __TEST_DBS_STORAGE_SERVICE = test_SSTEST test_SSTEST_storage-service -__TEST_DBS := $(__TEST_DBS_MCPCLIENT) $(__TEST_DBS_MIGRATIONS) $(__TEST_DBS_DASHBOARD) $(__TEST_DBS_MCPSERVER) $(__TEST_DBS_STORAGE_SERVICE) test_MCP test_SS +__TEST_DBS := $(__TEST_DBS_MCPCLIENT) $(__TEST_DBS_MIGRATIONS) $(__TEST_DBS_DASHBOARD) $(__TEST_DBS_MCPSERVER) $(__TEST_DBS_ARCHIVEMATICA) $(__TEST_DBS_STORAGE_SERVICE) test_MCP test_SS flush-test-dbs: start-mysql $(foreach test_db,$(__TEST_DBS),$(call drop_db,$(test_db));) test-build: ## Build archivematica-tests image. - docker build \ - -t archivematica-tests \ - -f $(CURDIR)/Dockerfile \ - --build-arg TARGET=archivematica-tests \ - --build-arg UBUNTU_VERSION \ - --build-arg PYTHON_VERSION \ - ../ + @if [ "$$GITHUB_ACTIONS" != "true" ]; then \ + docker build \ + -t archivematica-tests \ + -f $(CURDIR)/Dockerfile \ + --build-arg TARGET=archivematica-tests \ + --build-arg UBUNTU_VERSION \ + --build-arg PYTHON_VERSION \ + ../; \ + fi __TOXENVS_MCPSERVER := mcp-server test-mcp-server: start-mysql ## Run MCPServer tests. $(call grant_all_on_dbs,$(__TEST_DBS_MCPSERVER)) - $(call run_toxenvs,$(__TOXENVS_MCPSERVER)) + $(call run_toxenv,$(__TOXENVS_MCPSERVER),archivematica.MCPServer.settings.testmysql) __TOXENVS_MCPCLIENT = mcp-client test-mcp-client: start-mysql ## Run MCPClient tests. $(call grant_all_on_dbs,$(__TEST_DBS_MCPCLIENT)) - $(call run_toxenvs,$(__TOXENVS_MCPCLIENT)) + $(call run_toxenv,$(__TOXENVS_MCPCLIENT),archivematica.MCPClient.settings.testmysql) __TOXENVS_DASHBOARD = dashboard test-dashboard: start-mysql ## Run Dashboard tests. $(call grant_all_on_dbs,$(__TEST_DBS_DASHBOARD)) - $(call run_toxenvs,$(__TOXENVS_DASHBOARD)) + $(call run_toxenv,$(__TOXENVS_DASHBOARD),archivematica.dashboard.settings.testmysql) __TOXENVS_STORAGE_SERVICE = storage-service test-storage-service: start-mysql ## Run Storage Service tests. $(call grant_all_on_dbs,$(__TEST_DBS_STORAGE_SERVICE)) - $(call run_toxenvs,$(__TOXENVS_STORAGE_SERVICE)) + $(call run_toxenv,$(__TOXENVS_STORAGE_SERVICE),archivematica.storage_service.storage_service.settings.testmysql) test-storage-service-integration: ## Run Storage Service unit and integration tests using MySQL and MinIO. $(CURDIR)/submodules/archivematica-storage-service/tests/integration/run.sh @@ -315,21 +318,32 @@ test-storage-service-integration: ## Run Storage Service unit and integration t __TOXENVS_ARCHIVEMATICA_COMMON = archivematica-common test-archivematica-common: start-mysql ## Run Archivematica Common tests. $(call grant_all_on_dbs,$(__TEST_DBS_DASHBOARD)) - $(call run_toxenvs,$(__TOXENVS_ARCHIVEMATICA_COMMON)) + $(call run_toxenv,$(__TOXENVS_ARCHIVEMATICA_COMMON),archivematica.dashboard.settings.testmysql) -__TOXENVS_MIGRATIONS = migrations-dashboard migrations-storage-service test-migrations: start-mysql ## Check there are no pending migrations. $(call create_dbs,$(__TEST_DBS_MIGRATIONS)) - $(call run_toxenvs,$(__TOXENVS_MIGRATIONS)) + $(call run_toxenv,migrations-dashboard,archivematica.dashboard.settings.testmysql) + $(call run_toxenv,migrations-storage-service,archivematica.storage_service.storage_service.settings.testmysql) __TOXENVS_LINTING = linting test-linting: ## Check linting. - $(call run_toxenvs,$(__TOXENVS_LINTING)) + $(call run_toxenv,$(__TOXENVS_LINTING),archivematica.dashboard.settings.testmysql) -__TOXENVS_ALL := $(__TOXENVS_MCPSERVER) $(__TOXENVS_MCPCLIENT) $(__TOXENVS_DASHBOARD) $(__TOXENVS_STORAGE_SERVICE) $(__TOXENVS_ARCHIVEMATICA_COMMON) test-all: start-mysql ## Run all tests. $(call grant_all_on_dbs,$(__TEST_DBS_MCPSERVER) $(__TEST_DBS_MCPCLIENT) $(__TEST_DBS_DASHBOARD) $(__TEST_DBS_STORAGE_SERVICE) $(__TEST_DBS_ARCHIVEMATICA_COMMON)) - $(call run_toxenvs,$(__TOXENVS_ALL)) + $(call run_toxenv,$(__TOXENVS_MCPSERVER),archivematica.MCPServer.settings.testmysql) + $(call run_toxenv,$(__TOXENVS_MCPCLIENT),archivematica.MCPClient.settings.testmysql) + $(call run_toxenv,$(__TOXENVS_DASHBOARD),archivematica.dashboard.settings.testmysql) + $(call run_toxenv,$(__TOXENVS_ARCHIVEMATICA_COMMON),archivematica.dashboard.settings.testmysql) + $(call run_toxenv,$(__TOXENVS_STORAGE_SERVICE),archivematica.storage_service.storage_service.settings.testmysql) + +__TOXENVS_ARCHIVEMATICA = archivematica +test-archivematica: start-mysql ## Run Archivematica tests in a single run. + $(call grant_all_on_dbs,$(__TEST_DBS_ARCHIVEMATICA)) + $(call run_toxenv,$(__TOXENVS_ARCHIVEMATICA),archivematica.settings.testmysql) + +test-integration: ## Run integration tests. + $(CURDIR)/tests/integration/run.sh test-at-build: ## AMAUAT: build image. $(call compose_amauat, \ @@ -337,7 +351,7 @@ test-at-build: ## AMAUAT: build image. test-at-check: test-at-build ## AMAUAT: test browsers. $(call compose_amauat, \ - run --rm --no-deps archivematica-acceptance-tests /home/archivematica/acceptance-tests/simplebrowsertest.py) + run --rm --no-deps archivematica-acceptance-tests /home/artefactual/acceptance-tests/simplebrowsertest.py) define AT_HELP @@ -400,13 +414,13 @@ test-frontend: ## Run Dashboard JS tests. -e HOME=/tmp/npm-config \ --rm \ --user $(CALLER_UID):$(CALLER_GID) \ - --volume "$(SRCDIR)/dashboard:/src/src/dashboard" \ + --volume "$(SRCDIR)/archivematica/dashboard:/src/src/archivematica/dashboard" \ --entrypoint npm \ archivematica-dashboard-testing \ install docker run \ --rm \ - --volume "$(SRCDIR)/dashboard/frontend:/src/src/dashboard/frontend" \ + --volume "$(SRCDIR)/archivematica/dashboard/frontend:/src/src/archivematica/dashboard/frontend" \ archivematica-dashboard-testing stop: # Stop all containers. diff --git a/hack/README.md b/hack/README.md index 982332eee9..70a64057f6 100644 --- a/hack/README.md +++ b/hack/README.md @@ -235,21 +235,35 @@ are username: `test`, password: `test`. ## Source code auto-reloading -Dashboard and Storage Service are both served by Gunicorn. We set up Gunicorn -with the [reload](http://docs.gunicorn.org/en/stable/settings.html#reload) -setting enabled meaning that the Gunicorn workers will be restarted as soon as -code changes. +Dashboard and Storage Service are served by Gunicorn. Auto‑reload is disabled by +default because Gunicorn's reload in our setup is poll‑based: it cannot use the +inotify API with our gevent configuration, so it continuously scans the +filesystem which causes unnecessary CPU load even when idle. Enable it only when +you strictly need it in your workflow. -Other components in the stack like the `MCPServer` don't offer this option and -they need to be restarted manually, e.g.: +To enable reload for either service, set flags in Compose's `.env` file in the +`hack` directory and recreate the affected services: + +```env +AM_GUNICORN_RELOAD=true +SS_GUNICORN_RELOAD=true +``` + +Then recreate the services so the new configuration takes effect, e.g.: + +```shell +docker compose up -d --force-recreate --no-deps archivematica-dashboard archivematica-storage-service +``` + +Other components in the stack, such as the `MCPServer`, do not support +auto-reloading and must be restarted manually, for example: ```shell docker compose up -d --force-recreate --no-deps archivematica-mcp-server ``` -If you've added new dependencies or changes the `Dockerfile` you should also -add the `--build` argument to the previous command in order to ensure that the -container is using the newest image, e.g.: +If you've added new dependencies or modified the `Dockerfile`, also include the +`--build` flag to ensure the container uses the latest image: ```shell docker compose up -d --force-recreate --build --no-deps archivematica-mcp-server diff --git a/hack/docker-compose.tests.yml b/hack/docker-compose.tests.yml index 3dde27ea80..2e900537e6 100644 --- a/hack/docker-compose.tests.yml +++ b/hack/docker-compose.tests.yml @@ -8,7 +8,7 @@ services: dockerfile: "hack/Dockerfile" args: TARGET: "archivematica-tests" - UBUNTU_VERSION: ${UBUNTU_VERSION:-22.04} + UBUNTU_VERSION: ${UBUNTU_VERSION:-24.04} PYTHON_VERSION: ${PYTHON_VERSION:-3.9} volumes: - "../:/src" diff --git a/hack/docker-compose.yml b/hack/docker-compose.yml index b123bb4d32..55851af0c6 100644 --- a/hack/docker-compose.yml +++ b/hack/docker-compose.yml @@ -27,7 +27,7 @@ volumes: services: mysql: - image: "percona:8.0" + image: "percona/percona-server:8.0.43-34" command: "--character-set-server=utf8mb4 --collation-server=utf8mb4_0900_ai_ci" environment: MYSQL_ROOT_PASSWORD: "12345" @@ -43,7 +43,7 @@ services: - "SYS_NICE" elasticsearch: - image: "docker.elastic.co/elasticsearch/elasticsearch:6.8.23" + image: "docker.elastic.co/elasticsearch/elasticsearch:8.19.3" environment: - "cluster.name=am-cluster" - "node.name=am-node" @@ -51,6 +51,9 @@ services: - "bootstrap.memory_lock=true" - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - "cluster.routing.allocation.disk.threshold_enabled=${ELASTICSEARCH_DISK_THRESHOLD_ENABLED:-true}" + - "discovery.type=single-node" + - "xpack.security.enabled=false" + - "xpack.security.transport.ssl.enabled=false" ulimits: memlock: soft: -1 @@ -58,37 +61,39 @@ services: healthcheck: test: [ "CMD-SHELL", - "curl -fsSL http://localhost:9200/_cat/health?h=status | sed -r 's/^[[:space:]]+|[[:space:]]+$//g' | grep -q -E 'green|yellow'", + "curl -fsSL http://localhost:9200/_cat/health?h=status | grep -q -E 'green|yellow'", ] interval: 5s timeout: 5s retries: 5 - start_period: 15s + start_period: 25s volumes: - "elasticsearch_data:/usr/share/elasticsearch/data" ports: - "127.0.0.1:62002:9200" gearmand: - image: "artefactual/gearmand:1.1.18-alpine" + image: "artefactual/gearmand:1.1.22-alpine" command: "--queue-type=builtin" user: "gearman" ports: - "127.0.0.1:62004:4730" clamavd: - image: "artefactual/clamav:latest" + image: "clamav/clamav-debian:1.4.3-57" + user: "clamav" + entrypoint: "/init-unprivileged" environment: - CLAMAV_MAX_FILE_SIZE: "42" - CLAMAV_MAX_SCAN_SIZE: "42" - CLAMAV_MAX_STREAM_LENGTH: "100" + CLAMD_CONF_MaxFileSize: "42M" + CLAMD_CONF_MaxScanSize: "42M" + CLAMD_CONF_StreamMaxLength: "100M" ports: - "127.0.0.1:62006:3310" volumes: - "archivematica_pipeline_data:/var/archivematica/sharedDirectory:ro" nginx: - image: "nginx:stable-alpine" + image: "nginx:1.29.1" volumes: - "./etc/nginx/nginx.conf:/etc/nginx/nginx.conf:ro" - "./etc/nginx/conf.d/archivematica.conf:/etc/nginx/conf.d/archivematica.conf:ro" @@ -105,11 +110,11 @@ services: TARGET: "archivematica-mcp-server" USER_ID: ${USER_ID:-1000} GROUP_ID: ${GROUP_ID:-1000} - UBUNTU_VERSION: ${UBUNTU_VERSION:-22.04} + UBUNTU_VERSION: ${UBUNTU_VERSION:-24.04} PYTHON_VERSION: ${PYTHON_VERSION:-3.9} environment: DJANGO_SECRET_KEY: "12345" - DJANGO_SETTINGS_MODULE: "settings.common" + DJANGO_SETTINGS_MODULE: "archivematica.MCPServer.settings.common" ARCHIVEMATICA_MCPSERVER_CLIENT_USER: "archivematica" ARCHIVEMATICA_MCPSERVER_CLIENT_PASSWORD: "demo" ARCHIVEMATICA_MCPSERVER_CLIENT_HOST: "mysql" @@ -133,16 +138,16 @@ services: TARGET: "archivematica-mcp-client" USER_ID: ${USER_ID:-1000} GROUP_ID: ${GROUP_ID:-1000} - UBUNTU_VERSION: ${UBUNTU_VERSION:-22.04} + UBUNTU_VERSION: ${UBUNTU_VERSION:-24.04} PYTHON_VERSION: ${PYTHON_VERSION:-3.9} environment: DJANGO_SECRET_KEY: "12345" - DJANGO_SETTINGS_MODULE: "settings.common" + DJANGO_SETTINGS_MODULE: "archivematica.MCPClient.settings.common" ARCHIVEMATICA_MCPCLIENT_CLIENT_USER: "archivematica" ARCHIVEMATICA_MCPCLIENT_CLIENT_PASSWORD: "demo" ARCHIVEMATICA_MCPCLIENT_CLIENT_HOST: "mysql" ARCHIVEMATICA_MCPCLIENT_CLIENT_DATABASE: "MCP" - ARCHIVEMATICA_MCPCLIENT_MCPCLIENT_ELASTICSEARCHSERVER: "elasticsearch:9200" + ARCHIVEMATICA_MCPCLIENT_MCPCLIENT_ELASTICSEARCHSERVER: "http://elasticsearch:9200" ARCHIVEMATICA_MCPCLIENT_MCPCLIENT_MCPARCHIVEMATICASERVER: "gearmand:4730" ARCHIVEMATICA_MCPCLIENT_MCPCLIENT_SEARCH_ENABLED: "${AM_SEARCH_ENABLED:-true}" ARCHIVEMATICA_MCPCLIENT_MCPCLIENT_CAPTURE_CLIENT_SCRIPT_OUTPUT: "${AM_CAPTURE_CLIENT_SCRIPT_OUTPUT:-true}" @@ -155,6 +160,7 @@ services: ARCHIVEMATICA_MCPCLIENT_MCPCLIENT_PROMETHEUS_BIND_ADDRESS: "0.0.0.0" ARCHIVEMATICA_MCPCLIENT_MCPCLIENT_METADATA_XML_VALIDATION_ENABLED: "true" METADATA_XML_VALIDATION_SETTINGS_FILE: "/src/hack/submodules/archivematica-sampledata/xml-validation/xml_validation.py" + XML_CATALOG_FILES: "/src/hack/submodules/archivematica-sampledata/xml-validation/catalog.xml" volumes: - "../:/src" - "archivematica_pipeline_data:/var/archivematica/sharedDirectory:rw" @@ -173,22 +179,23 @@ services: TARGET: "archivematica-dashboard" USER_ID: ${USER_ID:-1000} GROUP_ID: ${GROUP_ID:-1000} - UBUNTU_VERSION: ${UBUNTU_VERSION:-22.04} + UBUNTU_VERSION: ${UBUNTU_VERSION:-24.04} PYTHON_VERSION: ${PYTHON_VERSION:-3.9} environment: FORWARDED_ALLOW_IPS: "*" AM_GUNICORN_ACCESSLOG: "/dev/null" - AM_GUNICORN_RELOAD: "true" - AM_GUNICORN_RELOAD_ENGINE: "auto" - DJANGO_SETTINGS_MODULE: "settings.local" + AM_GUNICORN_RELOAD: "${AM_GUNICORN_RELOAD:-false}" + DJANGO_SETTINGS_MODULE: "archivematica.dashboard.settings.local" ARCHIVEMATICA_DASHBOARD_DASHBOARD_GEARMAN_SERVER: "gearmand:4730" - ARCHIVEMATICA_DASHBOARD_DASHBOARD_ELASTICSEARCH_SERVER: "elasticsearch:9200" + ARCHIVEMATICA_DASHBOARD_DASHBOARD_ELASTICSEARCH_SERVER: "http://elasticsearch:9200" ARCHIVEMATICA_DASHBOARD_DASHBOARD_PROMETHEUS_ENABLED: "1" ARCHIVEMATICA_DASHBOARD_CLIENT_USER: "archivematica" ARCHIVEMATICA_DASHBOARD_CLIENT_PASSWORD: "demo" ARCHIVEMATICA_DASHBOARD_CLIENT_HOST: "mysql" ARCHIVEMATICA_DASHBOARD_CLIENT_DATABASE: "MCP" ARCHIVEMATICA_DASHBOARD_SEARCH_ENABLED: "${AM_SEARCH_ENABLED:-true}" + ARCHIVEMATICA_DASHBOARD_DASHBOARD_SESSION_COOKIE_SECURE: "false" + ARCHIVEMATICA_DASHBOARD_DASHBOARD_CSRF_COOKIE_SECURE: "false" volumes: - "../:/src" - "archivematica_pipeline_data:/var/archivematica/sharedDirectory:rw" @@ -209,17 +216,18 @@ services: TARGET: "archivematica-storage-service" USER_ID: ${USER_ID:-1000} GROUP_ID: ${GROUP_ID:-1000} - UBUNTU_VERSION: ${UBUNTU_VERSION:-22.04} + UBUNTU_VERSION: ${UBUNTU_VERSION:-24.04} PYTHON_VERSION: ${PYTHON_VERSION:-3.9} environment: FORWARDED_ALLOW_IPS: "*" SS_GUNICORN_ACCESSLOG: "/dev/null" - SS_GUNICORN_RELOAD: "true" - SS_GUNICORN_RELOAD_ENGINE: "auto" - DJANGO_SETTINGS_MODULE: "storage_service.settings.local" + SS_GUNICORN_RELOAD: "${SS_GUNICORN_RELOAD:-false}" + DJANGO_SETTINGS_MODULE: "archivematica.storage_service.storage_service.settings.local" SS_DB_URL: "mysql://archivematica:demo@mysql/SS" SS_GNUPG_HOME_PATH: "/var/archivematica/storage_service/.gnupg" SS_PROMETHEUS_ENABLED: "true" + SESSION_COOKIE_SECURE: "false" + CSRF_COOKIE_SECURE: "false" volumes: - "./submodules/archivematica-storage-service/:/src/" - "./submodules/archivematica-sampledata/:/home/archivematica/archivematica-sampledata/:ro" diff --git a/hack/helpers/compile-i18n-messages.sh b/hack/helpers/compile-i18n-messages.sh index feb6a66336..534508df7a 100755 --- a/hack/helpers/compile-i18n-messages.sh +++ b/hack/helpers/compile-i18n-messages.sh @@ -14,8 +14,8 @@ function dashboard::manage { docker compose run \ --user=$(id -u):$(id -g) \ --rm --no-deps \ - --workdir=/src/src/dashboard/src \ - --entrypoint=/src/src/dashboard/src/manage.py \ + --workdir=/src/src/archivematica/dashboard \ + --entrypoint=/src/src/archivematica/dashboard/manage.py \ archivematica-dashboard "$@" } diff --git a/hack/helpers/extract-i18n-messages.sh b/hack/helpers/extract-i18n-messages.sh index ce20ad0181..94a68bf3ba 100755 --- a/hack/helpers/extract-i18n-messages.sh +++ b/hack/helpers/extract-i18n-messages.sh @@ -20,8 +20,8 @@ function dashboard::manage { docker compose run \ --user=$(id -u):$(id -g) \ --rm --no-deps \ - --workdir=/src/src/dashboard/src \ - --entrypoint=/src/src/dashboard/src/manage.py \ + --workdir=/src/src/archivematica/dashboard \ + --entrypoint=/src/src/archivematica/dashboard/manage.py \ archivematica-dashboard "$@" } @@ -46,7 +46,7 @@ dashboard::manage makemessages --all --domain djangojs --ignore build/* docker compose run \ --user=$(id -u):$(id -g) \ --rm --no-deps \ - --workdir=/src/src/dashboard/frontend \ + --workdir=/src/src/archivematica/dashboard/frontend \ --entrypoint=yarn \ archivematica-dashboard run extract-messages diff --git a/hack/submodules/archivematica-acceptance-tests b/hack/submodules/archivematica-acceptance-tests index 5e87b4680d..c65c6d8148 160000 --- a/hack/submodules/archivematica-acceptance-tests +++ b/hack/submodules/archivematica-acceptance-tests @@ -1 +1 @@ -Subproject commit 5e87b4680d80d965106b4cf3e1f9e200de11f271 +Subproject commit c65c6d81482b1a55ab5a80a09da7c163130e2beb diff --git a/hack/submodules/archivematica-sampledata b/hack/submodules/archivematica-sampledata index 419a8bf3ad..9bdebf9b71 160000 --- a/hack/submodules/archivematica-sampledata +++ b/hack/submodules/archivematica-sampledata @@ -1 +1 @@ -Subproject commit 419a8bf3adfa8611d4d2a8f554da633aa3ca5d08 +Subproject commit 9bdebf9b713517fc058bb3e9f8b2c1a71c6c73f3 diff --git a/hack/submodules/archivematica-storage-service b/hack/submodules/archivematica-storage-service index b3e3f4bd59..0c3129d6f5 160000 --- a/hack/submodules/archivematica-storage-service +++ b/hack/submodules/archivematica-storage-service @@ -1 +1 @@ -Subproject commit b3e3f4bd595603940d5e98fe61ccf334b55c62e5 +Subproject commit 0c3129d6f549e1223a119c07f0d3f9f972b9c890 diff --git a/pyproject.toml b/pyproject.toml index 99e1fa0e6b..081549bca0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,88 @@ +[build-system] +requires = ["setuptools>=74", "wheel>=0.44"] +build-backend = "setuptools.build_meta" + +[project] +name="archivematica" +description="Web- and standards-based, open-source application which allows your institution to preserve long-term access to trustworthy, authentic and reliable digital content." +requires-python = ">=3.9" +authors = [ + {name = "Artefactual Systems Inc.", email = "info@artefactual.com"}, +] +maintainers = [ + {name = "Artefactual Systems Inc.", email = "info@artefactual.com"}, +] +readme = {file = "README.md", content-type = "text/markdown"} +license = {file = "LICENSE"} +dynamic = [ + "version", +] +dependencies = [ + "django-shibboleth-remoteuser @ git+https://github.com/artefactual-labs/django-shibboleth-remoteuser.git@f08a7864d6130416c352981ccf318fff0fd5be58", + "Django>=4.2,<5", + "agentarchives", + "amclient", + "ammcpc", + "bagit", + "brotli", + "clamav-client", + "django-autoslug", + "django-csp", + "django-forms-bootstrap", + "django-prometheus", + "django-tastypie", + "elasticsearch>=8.0.0,<9.0.0", + "gearman3 @ git+https://github.com/artefactual-labs/python-gearman.git@b68efc868c7a494dd6a2d2e820fb098a6da9f797", + "gevent", + "gunicorn", + "inotify-simple", + "jsonschema", + "lazy-paged-sequence", + "lxml", + "metsrw", + "orjson", + "opf-fido @ git+https://github.com/artefactual-labs/fido.git@abc31ecdc93bd9dfefbe6da535341dfc9f03d311", + "prometheus_client", + "python-dateutil", + "requests", + "unidecode", + "whitenoise", + # Required by LDAP authentication + "django-auth-ldap", + "python-ldap", + # Required by CAS authentication + "django-cas-ng", + # Required for OpenID Connect authentication + "mozilla-django-oidc", +] + +[project.optional-dependencies] +dev = [ + "coverage[toml]", + "mockldap @ git+https://github.com/artefactual-labs/mockldap@v0.3.1", + "pip-tools", + "playwright", + "pytest", + "pytest-cov", + "pytest-django", + "pytest-playwright", + "pytest-randomly", + "tox", +] + +[project.urls] +homepage = "https://www.archivematica.org/" +documentation = "https://www.archivematica.org/en/docs/archivematica-latest/" +repository = "https://github.com/artefacutal/archivematica" +issues = "https://github.com/archivematica/Issues/issues" + +[tool.setuptools.packages.find] +where = ["src"] +include = ["archivematica*"] + +[tool.setuptools.dynamic] +version = {attr = "archivematica.archivematicaCommon.version.get_full_version"} + [tool.pytest.ini_options] addopts = "--reuse-db -p no:cacheprovider" DJANGO_SETTINGS_MODULE="settings.test" @@ -5,24 +90,25 @@ norecursedirs = ".svn _build tmp* node_modules bower_components share .tox" [tool.coverage.run] omit = [ - "**/src/archivematicaCommon/lib/externals/*", + "**/src/archivematica/archivematicaCommon/externals/*", "**/migrations/*", - "**/south_migrations/*", - "**/management/commands/*", "**/settings/*", "**/tests/*", "**/wsgi.py", "**/manage.py", ] include = [ - "**/src/archivematicaCommon/lib/*", - "**/src/dashboard/src/*", - "**/src/MCPClient/lib/*", - "**/src/MCPClient/lib/clientScripts/*", - "**/src/MCPServer/lib/*", + "**/src/archivematica/archivematicaCommon/*", + "**/src/archivematica/dashboard/*", + "**/src/archivematica/MCPClient/*", + "**/src/archivematica/MCPClient/clientScripts/*", + "**/src/archivematica/MCPServer/*", ] branch = true +[tool.ruff] +target-version = "py39" + [tool.ruff.lint] # Rule reference: https://docs.astral.sh/ruff/rules/ select = [ @@ -43,8 +129,9 @@ ignore = [ ] [tool.ruff.lint.per-file-ignores] -"src/*/settings/*" = ["F403"] -"src/dashboard/src/settings/*" = ["F405"] +"src/archivematica/**/settings/*" = ["F403"] +"src/archivematica/dashboard/settings/*" = ["F405"] +"src/archivematica/settings/testmysql.py" = ["I001"] [tool.ruff.lint.isort] force-single-line = true @@ -56,29 +143,36 @@ warn_unused_configs = true [[tool.mypy.overrides]] module = [ - "src.archivematicaCommon.lib.executeOrRunSubProcess", - "src.MCPClient.lib.client.*", - "src.MCPClient.lib.clientScripts.characterize_file", - "src.MCPClient.lib.clientScripts.has_packages", - "src.MCPClient.lib.clientScripts.identify_file_format", - "src.MCPClient.lib.clientScripts.normalize", - "src.MCPClient.lib.clientScripts.policy_check", - "src.MCPClient.lib.clientScripts.transcribe_file", - "src.MCPClient.lib.clientScripts.validate_file", + "src.archivematica.archivematicaCommon.executeOrRunSubProcess", + "src.archivematica.MCPClient.client.*", + "src.archivematica.MCPClient.clientScripts.characterize_file", + "src.archivematica.MCPClient.clientScripts.has_packages", + "src.archivematica.MCPClient.clientScripts.identify_file_format", + "src.archivematica.MCPClient.clientScripts.normalize", + "src.archivematica.MCPClient.clientScripts.policy_check", + "src.archivematica.MCPClient.clientScripts.transcribe_file", + "src.archivematica.MCPClient.clientScripts.validate_file", + "src.archivematica.search.service", "tests.archivematicaCommon.test_execute_functions", + "tests.conftest", "tests.dashboard.components.accounts.test_views", "tests.dashboard.components.administration.test_administration", + "tests.dashboard.fpr.test_command_get_fpr_changes", + "tests.dashboard.fpr.test_command_import_pronom_ids", "tests.dashboard.fpr.test_views", "tests.dashboard.test_oidc", "tests.integration.test_oidc_auth", "tests.MCPClient.conftest", "tests.MCPClient.test_characterize_file", + "tests.MCPClient.test_create_mets_v2", + "tests.MCPClient.test_fpr_commands", "tests.MCPClient.test_has_packages", "tests.MCPClient.test_identify_file_format", "tests.MCPClient.test_normalize", "tests.MCPClient.test_policy_check", "tests.MCPClient.test_transcribe_file", "tests.MCPClient.test_validate_file", + "tests.search.test_service", ] check_untyped_defs = true disallow_any_generics = true @@ -95,7 +189,6 @@ warn_unused_ignores = true [tool.tox] legacy_tox_ini = """ [tox] - skipsdist = True envlist = archivematica-common dashboard @@ -105,9 +198,10 @@ legacy_tox_ini = """ migrations-dashboard migrations-storage-service linting + archivematica [testenv] - skip_install = True + package = editable deps = -r{toxinidir}/requirements-dev.txt commands = py.test {posargs} allowlist_externals = @@ -118,35 +212,10 @@ legacy_tox_ini = """ LOGNAME = user DJANGO_SETTINGS_MODULE = {env:DJANGO_SETTINGS_MODULE:settings.test} PYTEST_ADDOPTS = {env:PYTEST_ADDOPTS:} - SRC_DIR = {toxinidir}/src HACK_DIR = {toxinidir}/hack - # Archivematica Common - ARCHIVEMATICA_COMMON_ROOT = {env:SRC_DIR}/archivematicaCommon - ARCHIVEMATICA_COMMON_DIR = {env:ARCHIVEMATICA_COMMON_ROOT}/lib - # Dashboard - DASHBOARD_ROOT = {env:SRC_DIR}/dashboard - DASHBOARD_DIR = {env:DASHBOARD_ROOT}/src - DASHBOARD_PYTHONPATH = {env:DASHBOARD_DIR}:{env:ARCHIVEMATICA_COMMON_DIR} - # MCP Server - MCPSERVER_ROOT = {env:SRC_DIR}/MCPServer - MCPSERVER_DIR = {env:MCPSERVER_ROOT}/lib - MCPSERVER_PYTHONPATH = {env:MCPSERVER_DIR}:{env:ARCHIVEMATICA_COMMON_DIR}:{env:DASHBOARD_DIR} - # MCP Client - MCPCLIENT_ROOT = {env:SRC_DIR}/MCPClient - MCPCLIENT_DIR = {env:MCPCLIENT_ROOT}/lib - MCPCLIENT_PYTHONPATH = {env:MCPCLIENT_DIR}:{env:MCPCLIENT_DIR}/clientScripts:{env:ARCHIVEMATICA_COMMON_DIR}:{env:DASHBOARD_DIR} # Storage Service STORAGE_SERVICE_ROOT = {env:HACK_DIR}/submodules/archivematica-storage-service - STORAGE_SERVICE_DIR = {env:STORAGE_SERVICE_ROOT}/storage_service - STORAGE_SERVICE_PYTHONPATH = {env:STORAGE_SERVICE_DIR}:{env:STORAGE_SERVICE_DIR}/storage_service # TOXENV-specific - archivematica-common: PYTHONPATH = {env:DASHBOARD_PYTHONPATH} - dashboard: PYTHONPATH = {env:DASHBOARD_PYTHONPATH} - mcp-server: PYTHONPATH = {env:MCPSERVER_PYTHONPATH} - mcp-client: PYTHONPATH = {env:MCPCLIENT_PYTHONPATH} - storage-service: PYTHONPATH = {env:STORAGE_SERVICE_PYTHONPATH} - migrations-dashboard: PYTHONPATH = {env:DASHBOARD_PYTHONPATH} - migrations-storage-service: PYTHONPATH = {env:STORAGE_SERVICE_PYTHONPATH} # Setting HOME prevents Python's pwd module to ask for a real uid inside # the container, and using {temp_dir} allows caching the pre-commit # dependencies in the tox host @@ -158,19 +227,28 @@ legacy_tox_ini = """ mcp-client: {toxinidir}/tests/MCPClient storage-service: {env:STORAGE_SERVICE_ROOT} + [testenv:archivematica] + change_dir = {toxinidir}/tests + [testenv:storage-service] deps = -r{env:STORAGE_SERVICE_ROOT}/requirements-dev.txt + commands = + pip install -e {env:STORAGE_SERVICE_ROOT} + {[testenv]commands} [testenv:migrations-dashboard] commands = django-admin makemigrations --check --dry-run [testenv:migrations-storage-service] deps = {[testenv:storage-service]deps} - commands = {[testenv:migrations-dashboard]commands} + commands = + pip install -e {env:STORAGE_SERVICE_ROOT} + {[testenv:migrations-dashboard]commands} [testenv:linting] basepython = python3 + package = skip deps = pre-commit commands = pre-commit run --all-files --show-diff-on-failure """ diff --git a/requirements-dev.in b/requirements-dev.in deleted file mode 100644 index dfcfe84656..0000000000 --- a/requirements-dev.in +++ /dev/null @@ -1,19 +0,0 @@ --r requirements.txt - -git+https://github.com/artefactual-labs/mockldap@v0.3.1#egg=mockldap -pip-tools -pytest -pytest-cov -pytest-django -pytest-playwright -pytest-randomly -tox - -# playwright requires specific versions of greenlet which may clash with our -# gevent dependency in requirements.txt. -# See https://github.com/microsoft/playwright-python/issues/2190 -git+https://github.com/microsoft/playwright-python.git@d9cdfbb1e178b6770625e9f857139aff77516af0#egg=playwright - -# These dependencies dropped support for Python 3.8, so pinning them for now. -coverage[toml]==7.6.1 -pytest-randomly==3.15.0 diff --git a/requirements-dev.txt b/requirements-dev.txt index 188b81c0f8..001370d7b7 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -2,214 +2,188 @@ # This file is autogenerated by pip-compile with Python 3.9 # by the following command: # -# pip-compile --allow-unsafe --output-file=requirements-dev.txt requirements-dev.in +# pip-compile --allow-unsafe --extra=dev --output-file=requirements-dev.txt pyproject.toml # -agentarchives==0.9.0 - # via -r requirements.txt -amclient==1.3.0 - # via -r requirements.txt -ammcpc==0.2.0 - # via -r requirements.txt -asgiref==3.8.1 +agentarchives==0.10.0 + # via archivematica (pyproject.toml) +amclient==1.5.0 + # via archivematica (pyproject.toml) +ammcpc==0.3.0 + # via archivematica (pyproject.toml) +asgiref==3.9.1 + # via django +attrs==25.3.0 # via - # -r requirements.txt - # django -attrs==24.2.0 - # via - # -r requirements.txt # jsonschema # referencing -bagit @ git+https://github.com/artefactual-labs/bagit-python.git@902051d8410219f6c5f4ce6d43e5b272cf29e89b - # via -r requirements.txt +bagit==1.9.0 + # via archivematica (pyproject.toml) brotli==1.1.0 - # via -r requirements.txt -build==1.2.2.post1 + # via archivematica (pyproject.toml) +build==1.3.0 # via pip-tools -cachetools==5.5.0 +cachetools==6.2.0 # via tox -certifi==2024.8.30 +certifi==2025.8.3 # via - # -r requirements.txt + # elastic-transport # requests cffi==1.17.1 - # via - # -r requirements.txt - # cryptography + # via cryptography chardet==5.2.0 # via tox -charset-normalizer==3.4.0 - # via - # -r requirements.txt - # requests -clamd==1.0.2 - # via -r requirements.txt -click==8.1.7 +charset-normalizer==3.4.3 + # via requests +clamav-client==0.7.1 + # via archivematica (pyproject.toml) +click==8.1.8 # via pip-tools colorama==0.4.6 # via tox -coverage[toml]==7.6.1 +coverage[toml]==7.10.6 # via - # -r requirements-dev.in + # archivematica (pyproject.toml) # pytest-cov -cryptography==43.0.3 +cryptography==45.0.7 # via - # -r requirements.txt # josepy # mozilla-django-oidc - # pyopenssl -distlib==0.3.9 +distlib==0.4.0 # via virtualenv -django==4.2.16 +django==4.2.24 # via - # -r requirements.txt + # archivematica (pyproject.toml) # django-auth-ldap # django-cas-ng # django-csp + # django-prometheus # mozilla-django-oidc -django-auth-ldap==5.0.0 - # via -r requirements.txt +django-auth-ldap==5.2.0 + # via archivematica (pyproject.toml) django-autoslug==1.9.9 - # via -r requirements.txt + # via archivematica (pyproject.toml) django-cas-ng==5.0.1 - # via -r requirements.txt -django-csp==3.8 - # via -r requirements.txt + # via archivematica (pyproject.toml) +django-csp==4.0 + # via archivematica (pyproject.toml) django-forms-bootstrap==3.1.0 - # via -r requirements.txt -django-prometheus==2.3.1 - # via -r requirements.txt + # via archivematica (pyproject.toml) +django-prometheus==2.4.1 + # via archivematica (pyproject.toml) django-shibboleth-remoteuser @ git+https://github.com/artefactual-labs/django-shibboleth-remoteuser.git@f08a7864d6130416c352981ccf318fff0fd5be58 - # via -r requirements.txt -django-tastypie==0.14.7 - # via -r requirements.txt -elasticsearch==6.8.2 - # via -r requirements.txt -exceptiongroup==1.2.2 + # via archivematica (pyproject.toml) +django-tastypie==0.15.1 + # via archivematica (pyproject.toml) +elastic-transport==8.17.1 + # via elasticsearch +elasticsearch==8.19.0 + # via archivematica (pyproject.toml) +exceptiongroup==1.3.0 # via pytest -filelock==3.16.1 +filelock==3.19.1 # via # tox # virtualenv funcparserlib==2.0.0a0 # via mockldap gearman3 @ git+https://github.com/artefactual-labs/python-gearman.git@b68efc868c7a494dd6a2d2e820fb098a6da9f797 - # via -r requirements.txt -gevent==24.2.1 - # via -r requirements.txt -greenlet==3.1.1 + # via archivematica (pyproject.toml) +gevent==25.8.2 + # via archivematica (pyproject.toml) +greenlet==3.2.4 # via - # -r requirements.txt # gevent # playwright gunicorn==23.0.0 - # via -r requirements.txt + # via archivematica (pyproject.toml) idna==3.10 + # via requests +importlib-metadata==8.7.0 # via - # -r requirements.txt - # requests -importlib-metadata==8.5.0 - # via - # -r requirements.txt # build # pytest-randomly -importlib-resources==6.4.5 - # via - # -r requirements.txt - # opf-fido -iniconfig==2.0.0 +importlib-resources==6.5.2 + # via opf-fido +iniconfig==2.1.0 # via pytest -inotify-simple==1.3.5 - # via -r requirements.txt -josepy==1.14.0 - # via - # -r requirements.txt - # mozilla-django-oidc -jsonschema==4.23.0 - # via -r requirements.txt -jsonschema-specifications==2023.12.1 - # via - # -r requirements.txt - # jsonschema +inotify-simple==2.0.1 + # via archivematica (pyproject.toml) +josepy==2.1.0 + # via mozilla-django-oidc +jsonschema==4.25.1 + # via archivematica (pyproject.toml) +jsonschema-specifications==2025.4.1 + # via jsonschema lazy-paged-sequence==0.3 - # via -r requirements.txt -lxml==5.3.0 + # via archivematica (pyproject.toml) +lxml==6.0.1 # via - # -r requirements.txt # ammcpc + # archivematica (pyproject.toml) # metsrw # python-cas -metsrw==0.5.1 - # via -r requirements.txt +metsrw==0.6.1 + # via archivematica (pyproject.toml) mockldap @ git+https://github.com/artefactual-labs/mockldap@v0.3.1 - # via -r requirements-dev.in + # via archivematica (pyproject.toml) mozilla-django-oidc==4.0.1 - # via -r requirements.txt -mysqlclient==2.2.5 - # via - # -r requirements.txt - # agentarchives + # via archivematica (pyproject.toml) +mysqlclient==2.2.7 + # via agentarchives olefile==0.47 + # via opf-fido +opf-fido @ git+https://github.com/artefactual-labs/fido.git@abc31ecdc93bd9dfefbe6da535341dfc9f03d311 + # via archivematica (pyproject.toml) +orjson==3.11.3 + # via archivematica (pyproject.toml) +packaging==25.0 # via - # -r requirements.txt - # opf-fido -opf-fido @ git+https://github.com/artefactual-labs/fido.git@564ceb8018a8650fe931cf20e6780ee008e60fca - # via -r requirements.txt -orjson==3.10.10 - # via -r requirements.txt -packaging==24.1 - # via - # -r requirements.txt # build + # django-csp # gunicorn # pyproject-api # pytest # tox -pip-tools==7.4.1 - # via -r requirements-dev.in -platformdirs==4.3.6 +pip-tools==7.5.0 + # via archivematica (pyproject.toml) +platformdirs==4.4.0 # via # tox # virtualenv -playwright @ git+https://github.com/microsoft/playwright-python.git@d9cdfbb1e178b6770625e9f857139aff77516af0 +playwright==1.55.0 # via - # -r requirements-dev.in + # archivematica (pyproject.toml) # pytest-playwright -pluggy==1.5.0 +pluggy==1.6.0 # via # pytest + # pytest-cov # tox -prometheus-client==0.21.0 +prometheus-client==0.22.1 # via - # -r requirements.txt + # archivematica (pyproject.toml) # django-prometheus pyasn1==0.6.1 # via - # -r requirements.txt # pyasn1-modules # python-ldap -pyasn1-modules==0.4.1 - # via - # -r requirements.txt - # python-ldap +pyasn1-modules==0.4.2 + # via python-ldap pycparser==2.22 - # via - # -r requirements.txt - # cffi -pyee==12.0.0 + # via cffi +pyee==13.0.0 # via playwright -pyopenssl==24.2.1 - # via - # -r requirements.txt - # josepy -pyproject-api==1.8.0 +pygments==2.19.2 + # via pytest +pyproject-api==1.9.1 # via tox pyproject-hooks==1.2.0 # via # build # pip-tools -pytest==8.3.3 +pytest==8.4.2 # via - # -r requirements-dev.in + # archivematica (pyproject.toml) # pytest-base-url # pytest-cov # pytest-django @@ -217,65 +191,57 @@ pytest==8.3.3 # pytest-randomly pytest-base-url==2.1.0 # via pytest-playwright -pytest-cov==5.0.0 - # via -r requirements-dev.in -pytest-django==4.9.0 - # via -r requirements-dev.in -pytest-playwright==0.5.2 - # via -r requirements-dev.in -pytest-randomly==3.15.0 - # via -r requirements-dev.in +pytest-cov==6.2.1 + # via archivematica (pyproject.toml) +pytest-django==4.11.1 + # via archivematica (pyproject.toml) +pytest-playwright==0.7.0 + # via archivematica (pyproject.toml) +pytest-randomly==3.16.0 + # via archivematica (pyproject.toml) python-cas==1.6.0 - # via - # -r requirements.txt - # django-cas-ng + # via django-cas-ng python-dateutil==2.9.0.post0 # via - # -r requirements.txt + # archivematica (pyproject.toml) # django-tastypie + # elasticsearch python-ldap==3.4.4 # via - # -r requirements.txt + # archivematica (pyproject.toml) # django-auth-ldap # mockldap python-mimeparse==2.0.0 - # via - # -r requirements.txt - # django-tastypie + # via django-tastypie python-slugify==8.0.4 # via pytest-playwright -referencing==0.35.1 +referencing==0.36.2 # via - # -r requirements.txt # jsonschema # jsonschema-specifications -requests==2.32.3 +requests==2.32.5 # via - # -r requirements.txt # agentarchives # amclient + # archivematica (pyproject.toml) # mozilla-django-oidc # opf-fido # pytest-base-url # python-cas -rpds-py==0.20.0 +rpds-py==0.27.1 # via - # -r requirements.txt # jsonschema # referencing -six==1.16.0 +six==1.17.0 # via - # -r requirements.txt # opf-fido # python-cas # python-dateutil -sqlparse==0.5.1 - # via - # -r requirements.txt - # django +sqlparse==0.5.3 + # via django text-unidecode==1.3 # via python-slugify -tomli==2.0.2 +tomli==2.2.1 # via # build # coverage @@ -283,48 +249,44 @@ tomli==2.0.2 # pyproject-api # pytest # tox -tox==4.23.2 - # via -r requirements-dev.in -typing-extensions==4.12.2 +tox==4.30.2 + # via archivematica (pyproject.toml) +typing-extensions==4.15.0 # via - # -r requirements.txt # asgiref + # elasticsearch + # exceptiongroup # pyee + # referencing # tox -unidecode==1.3.8 - # via -r requirements.txt -urllib3==2.2.3 + # virtualenv +unidecode==1.4.0 + # via archivematica (pyproject.toml) +urllib3==2.5.0 # via - # -r requirements.txt # amclient - # elasticsearch + # elastic-transport # requests -virtualenv==20.27.1 +virtualenv==20.34.0 # via tox -wheel==0.44.0 +wheel==0.45.1 # via pip-tools -whitenoise==6.7.0 - # via -r requirements.txt -zipp==3.20.2 +whitenoise==6.9.0 + # via archivematica (pyproject.toml) +zipp==3.23.0 # via - # -r requirements.txt # importlib-metadata # importlib-resources -zope-event==5.0 - # via - # -r requirements.txt - # gevent -zope-interface==7.1.1 - # via - # -r requirements.txt - # gevent +zope-event==5.1.1 + # via gevent +zope-interface==7.2 + # via gevent # The following packages are considered to be unsafe in a requirements file: -pip==24.3.1 +pip==25.2 # via pip-tools -setuptools==75.2.0 +setuptools==80.9.0 # via - # -r requirements.txt # pip-tools # zope-event # zope-interface diff --git a/requirements.in b/requirements.in deleted file mode 100644 index 204bc96c0a..0000000000 --- a/requirements.in +++ /dev/null @@ -1,44 +0,0 @@ -git+https://github.com/artefactual-labs/django-shibboleth-remoteuser.git@f08a7864d6130416c352981ccf318fff0fd5be58#egg=django-shibboleth-remoteuser -Django>=4.2,<5 -agentarchives -amclient -ammcpc -git+https://github.com/artefactual-labs/bagit-python.git@902051d8410219f6c5f4ce6d43e5b272cf29e89b#egg=bagit -brotli -clamd -django-autoslug -django-csp -django-forms-bootstrap -django-prometheus -django-tastypie -elasticsearch>=6.0.0,<7.0.0 -git+https://github.com/artefactual-labs/python-gearman.git@b68efc868c7a494dd6a2d2e820fb098a6da9f797#egg=gearman3 -gunicorn -importlib-metadata -inotify_simple -jsonschema -lazy-paged-sequence -lxml -metsrw -orjson -git+https://github.com/artefactual-labs/fido.git@564ceb8018a8650fe931cf20e6780ee008e60fca#opf-fido -prometheus_client -python-dateutil -requests -unidecode - -# Required by LDAP authentication -django-auth-ldap -python-ldap - -# Required by CAS authentication -django-cas-ng - -# Required for OpenID Connect authentication -mozilla-django-oidc - -# These dependencies dropped support for Python 3.8, so pinning them for now. -django-auth-ldap==5.0.0 -gevent==24.2.1 -jsonschema-specifications==2023.12.1 -whitenoise==6.7.0 diff --git a/requirements.txt b/requirements.txt index d7eb7776ec..d4c2fc2bb5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,179 +2,181 @@ # This file is autogenerated by pip-compile with Python 3.9 # by the following command: # -# pip-compile --allow-unsafe --output-file=requirements.txt requirements.in +# pip-compile --allow-unsafe --output-file=requirements.txt pyproject.toml # -agentarchives==0.9.0 - # via -r requirements.in -amclient==1.3.0 - # via -r requirements.in -ammcpc==0.2.0 - # via -r requirements.in -asgiref==3.8.1 +agentarchives==0.10.0 + # via archivematica (pyproject.toml) +amclient==1.5.0 + # via archivematica (pyproject.toml) +ammcpc==0.3.0 + # via archivematica (pyproject.toml) +asgiref==3.9.1 # via django -attrs==24.2.0 +attrs==25.3.0 # via # jsonschema # referencing -bagit @ git+https://github.com/artefactual-labs/bagit-python.git@902051d8410219f6c5f4ce6d43e5b272cf29e89b - # via -r requirements.in +bagit==1.9.0 + # via archivematica (pyproject.toml) brotli==1.1.0 - # via -r requirements.in -certifi==2024.8.30 - # via requests + # via archivematica (pyproject.toml) +certifi==2025.8.3 + # via + # elastic-transport + # requests cffi==1.17.1 # via cryptography -charset-normalizer==3.4.0 +charset-normalizer==3.4.3 # via requests -clamd==1.0.2 - # via -r requirements.in -cryptography==43.0.3 +clamav-client==0.7.1 + # via archivematica (pyproject.toml) +cryptography==45.0.7 # via # josepy # mozilla-django-oidc - # pyopenssl -django==4.2.16 +django==4.2.24 # via - # -r requirements.in + # archivematica (pyproject.toml) # django-auth-ldap # django-cas-ng # django-csp + # django-prometheus # mozilla-django-oidc -django-auth-ldap==5.0.0 - # via -r requirements.in +django-auth-ldap==5.2.0 + # via archivematica (pyproject.toml) django-autoslug==1.9.9 - # via -r requirements.in + # via archivematica (pyproject.toml) django-cas-ng==5.0.1 - # via -r requirements.in -django-csp==3.8 - # via -r requirements.in + # via archivematica (pyproject.toml) +django-csp==4.0 + # via archivematica (pyproject.toml) django-forms-bootstrap==3.1.0 - # via -r requirements.in -django-prometheus==2.3.1 - # via -r requirements.in + # via archivematica (pyproject.toml) +django-prometheus==2.4.1 + # via archivematica (pyproject.toml) django-shibboleth-remoteuser @ git+https://github.com/artefactual-labs/django-shibboleth-remoteuser.git@f08a7864d6130416c352981ccf318fff0fd5be58 - # via -r requirements.in -django-tastypie==0.14.7 - # via -r requirements.in -elasticsearch==6.8.2 - # via -r requirements.in + # via archivematica (pyproject.toml) +django-tastypie==0.15.1 + # via archivematica (pyproject.toml) +elastic-transport==8.17.1 + # via elasticsearch +elasticsearch==8.19.0 + # via archivematica (pyproject.toml) gearman3 @ git+https://github.com/artefactual-labs/python-gearman.git@b68efc868c7a494dd6a2d2e820fb098a6da9f797 - # via -r requirements.in -gevent==24.2.1 - # via -r requirements.in -greenlet==3.1.1 + # via archivematica (pyproject.toml) +gevent==25.8.2 + # via archivematica (pyproject.toml) +greenlet==3.2.4 # via gevent gunicorn==23.0.0 - # via -r requirements.in + # via archivematica (pyproject.toml) idna==3.10 # via requests -importlib-metadata==8.5.0 - # via -r requirements.in -importlib-resources==6.4.5 +importlib-resources==6.5.2 # via opf-fido -inotify-simple==1.3.5 - # via -r requirements.in -josepy==1.14.0 +inotify-simple==2.0.1 + # via archivematica (pyproject.toml) +josepy==2.1.0 # via mozilla-django-oidc -jsonschema==4.23.0 - # via -r requirements.in -jsonschema-specifications==2023.12.1 - # via - # -r requirements.in - # jsonschema +jsonschema==4.25.1 + # via archivematica (pyproject.toml) +jsonschema-specifications==2025.4.1 + # via jsonschema lazy-paged-sequence==0.3 - # via -r requirements.in -lxml==5.3.0 + # via archivematica (pyproject.toml) +lxml==6.0.1 # via - # -r requirements.in # ammcpc + # archivematica (pyproject.toml) # metsrw # python-cas -metsrw==0.5.1 - # via -r requirements.in +metsrw==0.6.1 + # via archivematica (pyproject.toml) mozilla-django-oidc==4.0.1 - # via -r requirements.in -mysqlclient==2.2.5 + # via archivematica (pyproject.toml) +mysqlclient==2.2.7 # via agentarchives olefile==0.47 # via opf-fido -opf-fido @ git+https://github.com/artefactual-labs/fido.git@564ceb8018a8650fe931cf20e6780ee008e60fca - # via -r requirements.in -orjson==3.10.10 - # via -r requirements.in -packaging==24.1 - # via gunicorn -prometheus-client==0.21.0 - # via - # -r requirements.in +opf-fido @ git+https://github.com/artefactual-labs/fido.git@abc31ecdc93bd9dfefbe6da535341dfc9f03d311 + # via archivematica (pyproject.toml) +orjson==3.11.3 + # via archivematica (pyproject.toml) +packaging==25.0 + # via + # django-csp + # gunicorn +prometheus-client==0.22.1 + # via + # archivematica (pyproject.toml) # django-prometheus pyasn1==0.6.1 # via # pyasn1-modules # python-ldap -pyasn1-modules==0.4.1 +pyasn1-modules==0.4.2 # via python-ldap pycparser==2.22 # via cffi -pyopenssl==24.2.1 - # via josepy python-cas==1.6.0 # via django-cas-ng python-dateutil==2.9.0.post0 # via - # -r requirements.in + # archivematica (pyproject.toml) # django-tastypie + # elasticsearch python-ldap==3.4.4 # via - # -r requirements.in + # archivematica (pyproject.toml) # django-auth-ldap python-mimeparse==2.0.0 # via django-tastypie -referencing==0.35.1 +referencing==0.36.2 # via # jsonschema # jsonschema-specifications -requests==2.32.3 +requests==2.32.5 # via - # -r requirements.in # agentarchives # amclient + # archivematica (pyproject.toml) # mozilla-django-oidc # opf-fido # python-cas -rpds-py==0.20.0 +rpds-py==0.27.1 # via # jsonschema # referencing -six==1.16.0 +six==1.17.0 # via # opf-fido # python-cas # python-dateutil -sqlparse==0.5.1 +sqlparse==0.5.3 # via django -typing-extensions==4.12.2 - # via asgiref -unidecode==1.3.8 - # via -r requirements.in -urllib3==2.2.3 +typing-extensions==4.15.0 # via - # amclient + # asgiref # elasticsearch - # requests -whitenoise==6.7.0 - # via -r requirements.in -zipp==3.20.2 + # referencing +unidecode==1.4.0 + # via archivematica (pyproject.toml) +urllib3==2.5.0 # via - # importlib-metadata - # importlib-resources -zope-event==5.0 + # amclient + # elastic-transport + # requests +whitenoise==6.9.0 + # via archivematica (pyproject.toml) +zipp==3.23.0 + # via importlib-resources +zope-event==5.1.1 # via gevent -zope-interface==7.1.1 +zope-interface==7.2 # via gevent # The following packages are considered to be unsafe in a requirements file: -setuptools==75.2.0 +setuptools==80.9.0 # via # zope-event # zope-interface diff --git a/src/MCPClient/lib/clientScripts/archivematica_clamscan.py b/src/MCPClient/lib/clientScripts/archivematica_clamscan.py deleted file mode 100755 index f3af3a2c91..0000000000 --- a/src/MCPClient/lib/clientScripts/archivematica_clamscan.py +++ /dev/null @@ -1,373 +0,0 @@ -#!/usr/bin/env python -# This file is part of Archivematica. -# -# Copyright 2010-2017 Artefactual Systems Inc. -# -# Archivematica is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Archivematica is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Archivematica. If not, see . -import abc -import argparse -import errno -import multiprocessing -import os -import re -import subprocess -import uuid - -import django - -django.setup() -from clamd import BufferTooLongError -from clamd import ClamdNetworkSocket -from clamd import ClamdUnixSocket -from clamd import ConnectionError -from custom_handlers import get_script_logger -from databaseFunctions import insertIntoEvents -from django.conf import settings as mcpclient_settings -from django.core.exceptions import ValidationError -from django.db import transaction -from main.models import Event -from main.models import File - -logger = get_script_logger("archivematica.mcp.client.clamscan") - - -def concurrent_instances(): - return multiprocessing.cpu_count() - - -def clamav_version_parts(ver): - """Both clamscan and clamd return a version string that looks like the - following:: - - ClamAV 0.99.2/23992/Fri Oct 27 05:04:12 2017 - - Given the example above, this function returns a tuple as follows:: - - ("ClamAV 0.99.2", "23992/Fri Oct 27 05:04:12 2017") - - Both elements may be None if the matching failed. - """ - parts = ver.split("/") - n = len(parts) - if n == 1: - version = parts[0] - if re.match("^ClamAV", version): - return version, None - elif n == 3: - version, defs, date = parts - return version, f"{defs}/{date}" - return None, None - - -class ScannerBase(metaclass=abc.ABCMeta): - @abc.abstractmethod - def scan(self, path): - """Scan a file and return a tuple of three elements reporting the - results. These are the three elements expected: - 1. passed (bool) - 2. state (str - "OK", "ERROR", or "FOUND") - 3. details (str - extra info when ERROR or FOUND) - """ - - @abc.abstractproperty - def version_attrs(self): - """Obtain the version details. It is expected to return a tuple of two - elements: ClamAV version number and virus definition version number. - The implementor can cache the results. - """ - - def program(self): - return self.PROGRAM - - def version(self): - return self.version_attrs()[0] - - def virus_definitions(self): - return self.version_attrs()[1] - - -class ClamdScanner(ScannerBase): - PROGRAM = "ClamAV (clamd)" - - def __init__(self): - self.addr = mcpclient_settings.CLAMAV_SERVER - self.timeout = mcpclient_settings.CLAMAV_CLIENT_TIMEOUT - self.stream = mcpclient_settings.CLAMAV_PASS_BY_STREAM - self.client = self.get_client() - - def scan(self, path): - if self.stream: - method_name = "pass_by_stream" - result_key = "stream" - else: - method_name = "pass_by_reference" - result_key = path - - passed, state, details = (False, None, None) - try: - result = getattr(self, method_name)(path) - state, details = result[result_key] - except Exception as err: - passed = ClamdScanner.clamd_exception_handler(err) - if state == "OK": - passed = True - return passed, state, details - - @staticmethod - def clamd_exception_handler(err): - """Manage each decision for an exception when it is raised. Ensure - that each decision can be tested to meet the documented Archivematica - antivirus feature definition. - """ - if isinstance(err, IOError): - if err.errno == errno.EPIPE: - logger.error( - "[Errno 32] Broken pipe. File not scanned. Check Clamd " - "StreamMaxLength" - ) - return None - elif isinstance(err, BufferTooLongError): - logger.error( - "Clamd BufferTooLongError. File not scanned. Check Clamd " - "StreamMaxLength" - ) - return None - elif isinstance(err, ConnectionError): - logger.error( - "Clamd ConnectionError. File not scanned. Check Clamd " "output: %s", - err, - ) - return None - # Return False and provide some information to the user for all other - # failures. - logger.error("Virus scanning failed: %s", err, exc_info=True) - return False - - def version_attrs(self): - try: - self._version_attrs - except AttributeError: - self._version_attrs = clamav_version_parts(self.client.version()) - return self._version_attrs - - def get_client(self): - if ":" not in self.addr: - return ClamdUnixSocket(path=self.addr) - host, port = self.addr.split(":") - return ClamdNetworkSocket(host=host, port=int(port), timeout=self.timeout) - - def pass_by_reference(self, path): - logger.info( - "File being being read by Clamdscan from filesystem \ - reference." - ) - return self.client.scan(path) - - def pass_by_stream(self, path): - logger.info("File contents being streamed to Clamdscan.") - return self.client.instream(open(path, "rb")) - - -class ClamScanner(ScannerBase): - PROGRAM = "ClamAV (clamscan)" - COMMAND = "clamscan" - - def _call(self, *args): - return subprocess.check_output((self.COMMAND,) + args) - - def scan(self, path): - passed, state, details = (False, "ERROR", None) - try: - max_file_size = ( - "--max-filesize=%dM" % mcpclient_settings.CLAMAV_CLIENT_MAX_FILE_SIZE - ) - max_scan_size = ( - "--max-scansize=%dM" % mcpclient_settings.CLAMAV_CLIENT_MAX_SCAN_SIZE - ) - self._call(max_file_size, max_scan_size, path) - except subprocess.CalledProcessError as err: - if err.returncode == 1: - state = "FOUND" - else: - logger.error("Virus scanning failed: %s", err.output, exc_info=True) - else: - passed, state = (True, "OK") - return passed, state, details - - def version_attrs(self): - try: - self._version_attrs - except AttributeError: - try: - self._version_attrs = clamav_version_parts(self._call("-V")) - except subprocess.CalledProcessError: - self._version_attrs = (None, None) - return self._version_attrs - - -def file_already_scanned(file_uuid): - return ( - file_uuid != "None" - and Event.objects.filter( - file_uuid_id=file_uuid, event_type="virus check" - ).exists() - ) - - -def queue_event(file_uuid, date, scanner, passed, queue): - if passed is None or file_uuid == "None": - return - - event_detail = "" - if scanner is not None: - event_detail = f'program="{scanner.program()}"; version="{scanner.version()}"; virusDefinitions="{scanner.virus_definitions()}"' - - outcome = "Pass" if passed else "Fail" - logger.info("Recording new event for file %s (outcome: %s)", file_uuid, outcome) - - queue.append( - { - "fileUUID": file_uuid, - "eventIdentifierUUID": str(uuid.uuid4()), - "eventType": "virus check", - "eventDateTime": date, - "eventDetail": event_detail, - "eventOutcome": outcome, - } - ) - - -def get_parser(): - """Return a ``Namespace`` with the parsed arguments.""" - parser = argparse.ArgumentParser() - parser.add_argument("file_uuid", metavar="fileUUID") - parser.add_argument("path", metavar="PATH", help="File or directory location") - parser.add_argument("date", metavar="DATE") - parser.add_argument( - "task_uuid", metavar="taskUUID", help="Currently unused, feel free to ignore." - ) - return parser - - -SCANNERS = (ClamScanner, ClamdScanner) -SCANNERS_NAMES = tuple(b.__name__.lower() for b in SCANNERS) -DEFAULT_SCANNER = ClamdScanner - - -def get_scanner(): - """Return the ClamAV client configured by the user and found in the - installation's environment variables. Clamdscanner may perform quicker - than Clamscanner given a larger number of objects. Return clamdscanner - object as a default if no other, or an incorrect value is specified. - """ - choice = str(mcpclient_settings.CLAMAV_CLIENT_BACKEND).lower() - if choice not in SCANNERS_NAMES: - logger.warning( - "Unexpected antivirus scanner (CLAMAV_CLIENT_BACKEND):" ' "%s"; using %s.', - choice, - DEFAULT_SCANNER.__name__, - ) - return DEFAULT_SCANNER() - return SCANNERS[SCANNERS_NAMES.index(choice)]() - - -def get_size(file_uuid, path): - # We're going to see this happening when files are not part of `objects/`. - if file_uuid != "None": - try: - return File.objects.get(uuid=file_uuid).size - except (File.DoesNotExist, ValidationError): - pass - # Our fallback. - try: - return os.path.getsize(path) - except Exception: - return None - - -def scan_file(event_queue, file_uuid, path, date, task_uuid): - if file_already_scanned(file_uuid): - logger.info("Virus scan already performed, not running scan again") - return 0 - - scanner, passed = None, False - - try: - size = get_size(file_uuid, path) - if size is None: - logger.error("Getting file size returned: %s", size) - return 1 - - max_file_size = mcpclient_settings.CLAMAV_CLIENT_MAX_FILE_SIZE * 1024 * 1024 - max_scan_size = mcpclient_settings.CLAMAV_CLIENT_MAX_SCAN_SIZE * 1024 * 1024 - - valid_scan = True - - if size > max_file_size: - logger.info( - "File will not be scanned. Size %s bytes greater than scanner " - "max file size %s bytes", - size, - max_file_size, - ) - valid_scan = False - elif size > max_scan_size: - logger.info( - "File will not be scanned. Size %s bytes greater than scanner " - "max scan size %s bytes", - size, - max_scan_size, - ) - valid_scan = False - - if valid_scan: - scanner = get_scanner() - logger.info( - "Using scanner %s (%s - %s)", - scanner.program(), - scanner.version(), - scanner.virus_definitions(), - ) - - passed, state, details = scanner.scan(path) - else: - passed, state, details = None, None, None - - except Exception: - logger.error("Unexpected error scanning file %s", path, exc_info=True) - return 1 - else: - # record pass or fail, but not None if the file hasn't - # been scanned, e.g. Max File Size thresholds being too low. - if passed is not None: - logger.info("File %s scanned!", path) - logger.debug("passed=%s state=%s details=%s", passed, state, details) - finally: - queue_event(file_uuid, date, scanner, passed, event_queue) - - # If True or None, then we have no error, the file can move through the - # process as expected... - return 1 if passed is False else 0 - - -def call(jobs): - event_queue = [] - - for job in jobs: - with job.JobContext(logger=logger): - job.set_status(scan_file(event_queue, *job.args[1:])) - - with transaction.atomic(): - for e in event_queue: - insertIntoEvents(**e) diff --git a/src/MCPServer/lib/server/tasks/__init__.py b/src/MCPServer/lib/server/tasks/__init__.py deleted file mode 100644 index 857e7bc711..0000000000 --- a/src/MCPServer/lib/server/tasks/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from server.tasks.backends import GearmanTaskBackend -from server.tasks.backends import TaskBackend -from server.tasks.backends import get_task_backend -from server.tasks.task import Task - -__all__ = ("GearmanTaskBackend", "Task", "TaskBackend", "get_task_backend") diff --git a/src/MCPClient/README.md b/src/archivematica/MCPClient/README.md similarity index 97% rename from src/MCPClient/README.md rename to src/archivematica/MCPClient/README.md index e41ebaeff9..3cb7bcfe0c 100644 --- a/src/MCPClient/README.md +++ b/src/archivematica/MCPClient/README.md @@ -5,7 +5,7 @@ This directory contains the Archivematica MCP client, frequently referred to as `MCPServer`. These tasks are dispatched via Gearman. The programs responsible for performing the Gearman jobs are contained in the -`lib/clientScripts/` directory. Each program is a Python module that +`clientScripts/` directory. Each program is a Python module that implements the following function: def call(jobs) diff --git a/src/MCPClient/lib/clientScripts/__init__.py b/src/archivematica/MCPClient/__init__.py similarity index 100% rename from src/MCPClient/lib/clientScripts/__init__.py rename to src/archivematica/MCPClient/__init__.py diff --git a/src/MCPClient/lib/archivematicaClient.py b/src/archivematica/MCPClient/archivematicaClient.py similarity index 53% rename from src/MCPClient/lib/archivematicaClient.py rename to src/archivematica/MCPClient/archivematicaClient.py index 96b71da686..f9ebb3cf33 100755 --- a/src/MCPClient/lib/archivematicaClient.py +++ b/src/archivematica/MCPClient/archivematicaClient.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -from client.mcp import main +from archivematica.MCPClient.client.mcp import main if __name__ == "__main__": main() diff --git a/src/MCPClient/lib/archivematicaClientModules b/src/archivematica/MCPClient/archivematicaClientModules similarity index 99% rename from src/MCPClient/lib/archivematicaClientModules rename to src/archivematica/MCPClient/archivematicaClientModules index 4dff8ceab2..1a17491124 100644 --- a/src/MCPClient/lib/archivematicaClientModules +++ b/src/archivematica/MCPClient/archivematicaClientModules @@ -23,7 +23,7 @@ assignfileuuids_v0.0 = assign_file_uuids bindpid_v0.0 = bind_pid removeunneededfiles_v0.0 = remove_unneeded_files -archivematicaclamscan_v0.0 = archivematica_clamscan +antivirus_v0.0 = antivirus createevent_v0.0 = create_event examinecontents_v0.0 = examine_contents identifydspacefiles_v0.0 = identify_dspace_files diff --git a/src/MCPClient/lib/assets/README/README.html b/src/archivematica/MCPClient/assets/README/README.html similarity index 100% rename from src/MCPClient/lib/assets/README/README.html rename to src/archivematica/MCPClient/assets/README/README.html diff --git a/src/MCPClient/lib/assets/README/TRANSFER_README.html b/src/archivematica/MCPClient/assets/README/TRANSFER_README.html similarity index 100% rename from src/MCPClient/lib/assets/README/TRANSFER_README.html rename to src/archivematica/MCPClient/assets/README/TRANSFER_README.html diff --git a/src/MCPClient/lib/assets/catalog/catalog.xml b/src/archivematica/MCPClient/assets/catalog/catalog.xml similarity index 100% rename from src/MCPClient/lib/assets/catalog/catalog.xml rename to src/archivematica/MCPClient/assets/catalog/catalog.xml diff --git a/src/MCPClient/lib/assets/mets/mets.xsd b/src/archivematica/MCPClient/assets/mets/mets.xsd similarity index 100% rename from src/MCPClient/lib/assets/mets/mets.xsd rename to src/archivematica/MCPClient/assets/mets/mets.xsd diff --git a/src/MCPClient/lib/assets/mets/xlink.xsd b/src/archivematica/MCPClient/assets/mets/xlink.xsd similarity index 100% rename from src/MCPClient/lib/assets/mets/xlink.xsd rename to src/archivematica/MCPClient/assets/mets/xlink.xsd diff --git a/src/MCPClient/lib/assets/mods/mods.xsd b/src/archivematica/MCPClient/assets/mods/mods.xsd similarity index 100% rename from src/MCPClient/lib/assets/mods/mods.xsd rename to src/archivematica/MCPClient/assets/mods/mods.xsd diff --git a/src/MCPClient/lib/assets/mods/xml.xsd b/src/archivematica/MCPClient/assets/mods/xml.xsd similarity index 100% rename from src/MCPClient/lib/assets/mods/xml.xsd rename to src/archivematica/MCPClient/assets/mods/xml.xsd diff --git a/src/MCPClient/lib/assets/premis/premis-v2-2.xsd b/src/archivematica/MCPClient/assets/premis/premis-v2-2.xsd similarity index 100% rename from src/MCPClient/lib/assets/premis/premis-v2-2.xsd rename to src/archivematica/MCPClient/assets/premis/premis-v2-2.xsd diff --git a/src/MCPClient/lib/assets/premis/premis.xsd b/src/archivematica/MCPClient/assets/premis/premis.xsd similarity index 100% rename from src/MCPClient/lib/assets/premis/premis.xsd rename to src/archivematica/MCPClient/assets/premis/premis.xsd diff --git a/src/MCPClient/lib/client/__init__.py b/src/archivematica/MCPClient/client/__init__.py similarity index 100% rename from src/MCPClient/lib/client/__init__.py rename to src/archivematica/MCPClient/client/__init__.py diff --git a/src/MCPClient/lib/client/gearman.py b/src/archivematica/MCPClient/client/gearman.py similarity index 86% rename from src/MCPClient/lib/client/gearman.py rename to src/archivematica/MCPClient/client/gearman.py index 6653e296e5..d7c67d9845 100644 --- a/src/MCPClient/lib/client/gearman.py +++ b/src/archivematica/MCPClient/client/gearman.py @@ -4,24 +4,20 @@ from datetime import datetime from multiprocessing.synchronize import Event from types import TracebackType -from typing import Dict -from typing import List from typing import Optional -from typing import Tuple -from typing import Type from typing import Union import gearman from django.conf import settings from gearman.job import GearmanJob -from gearman_encoder import JSONDataEncoder -from client import metrics -from client.job import Job -from client.loader import load_job_modules -from client.utils import parse_command_line -from client.utils import replace_task_arguments -from client.worker import run_task +from archivematica.archivematicaCommon.gearman_encoder import JSONDataEncoder +from archivematica.MCPClient.client import metrics +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.client.loader import load_job_modules +from archivematica.MCPClient.client.utils import parse_command_line +from archivematica.MCPClient.client.utils import replace_task_arguments +from archivematica.MCPClient.client.worker import run_task # This is how `gearman_job.data["tasks"]` looks in the `_prepare_jobs`` method # below: @@ -34,8 +30,8 @@ # }, # ... # } -TaskData = Dict[str, Union[str, bool]] -GearmanJobTasks = Dict[str, TaskData] +TaskData = dict[str, Union[str, bool]] +GearmanJobTasks = dict[str, TaskData] # This is how `results` looks in the `_format_job_results` method below: # { @@ -49,8 +45,8 @@ # }, # ... # } -JobData = Dict[str, Union[int, Optional[datetime], str]] -JobResults = Dict[str, JobData] +JobData = dict[str, Union[int, Optional[datetime], str]] +JobResults = dict[str, JobData] logger = logging.getLogger("archivematica.mcp.client.gearman") @@ -60,8 +56,8 @@ class MCPGearmanWorker(gearman.GearmanWorker): # type: ignore def __init__( self, - hosts: List[str], - client_scripts: List[str], + hosts: list[str], + client_scripts: list[str], shutdown_event: Optional[Event] = None, max_jobs_to_process: Optional[int] = None, ) -> None: @@ -82,7 +78,7 @@ def __init__( logger.debug("Worker %s registered tasks: %s", self.client_id, client_scripts) @staticmethod - def _format_job_results(jobs: List[Job]) -> JobResults: + def _format_job_results(jobs: list[Job]) -> JobResults: results = {} for job in jobs: @@ -103,7 +99,7 @@ def _format_job_results(jobs: List[Job]) -> JobResults: return results @staticmethod - def _prepare_jobs(task_name: str, gearman_job: GearmanJob) -> List[Job]: + def _prepare_jobs(task_name: str, gearman_job: GearmanJob) -> list[Job]: """Given a tasks dictionary, return a list of Job objects.""" tasks: GearmanJobTasks = gearman_job.data["tasks"] @@ -132,7 +128,7 @@ def handle_job( task_name: str, gearman_worker: gearman.GearmanWorker, gearman_job: GearmanJob, - ) -> Dict[str, JobResults]: + ) -> dict[str, JobResults]: job_module = self.job_modules[task_name] logger.debug( "Gearman job request %s received for %s", @@ -152,7 +148,7 @@ def handle_job( def on_job_exception( self, current_job: GearmanJob, - exc_info: Tuple[Type[BaseException], BaseException, TracebackType], + exc_info: tuple[type[BaseException], BaseException, TracebackType], ) -> bool: logger.error( "An unhandled exception occurred processing a Gearman job", diff --git a/src/MCPClient/lib/client/job.py b/src/archivematica/MCPClient/client/job.py similarity index 93% rename from src/MCPClient/lib/client/job.py rename to src/archivematica/MCPClient/client/job.py index 018f298e01..8d32ee8d39 100644 --- a/src/MCPClient/lib/client/job.py +++ b/src/archivematica/MCPClient/client/job.py @@ -9,26 +9,25 @@ import logging import sys import traceback +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Mapping from contextlib import contextmanager from logging.handlers import BufferingHandler from typing import Any -from typing import Dict -from typing import Generator -from typing import Iterable -from typing import List -from typing import Mapping from typing import Optional from typing import TypeVar from typing import Union from django.conf import settings from django.utils import timezone -from main.models import Task + +from archivematica.dashboard.main.models import Task logger = logging.getLogger("archivematica.mcp.client.job") SelfJob = TypeVar("SelfJob", bound="Job") -TaskData = Dict[str, Union[int, Optional[datetime.datetime], str]] +TaskData = dict[str, Union[int, Optional[datetime.datetime], str]] class Job: @@ -36,7 +35,7 @@ def __init__( self, name: str, uuid: str, - arguments: List[str], + arguments: list[str], capture_output: bool = False, ) -> None: """ @@ -60,7 +59,7 @@ def __init__( self.end_time: Optional[datetime.datetime] = None @classmethod - def bulk_set_start_times(cls, jobs: List[SelfJob]) -> None: + def bulk_set_start_times(cls, jobs: list[SelfJob]) -> None: """Bulk set the processing start time for a batch of jobs.""" start_time = timezone.now() uuids = [job.uuid for job in jobs] @@ -69,7 +68,7 @@ def bulk_set_start_times(cls, jobs: List[SelfJob]) -> None: job.start_time = start_time @classmethod - def bulk_mark_failed(cls, jobs: List[SelfJob], message: str) -> None: + def bulk_mark_failed(cls, jobs: list[SelfJob], message: str) -> None: uuids = [job.uuid for job in jobs] Task.objects.filter(taskuuid__in=uuids).update( diff --git a/src/MCPClient/lib/client/loader.py b/src/archivematica/MCPClient/client/loader.py similarity index 80% rename from src/MCPClient/lib/client/loader.py rename to src/archivematica/MCPClient/client/loader.py index f61c84724b..6bebefe5ca 100644 --- a/src/MCPClient/lib/client/loader.py +++ b/src/archivematica/MCPClient/client/loader.py @@ -2,13 +2,12 @@ import warnings from configparser import RawConfigParser from types import ModuleType -from typing import Dict from typing import Optional -def get_supported_modules(modules_file_path: str) -> Dict[str, str]: +def get_supported_modules(modules_file_path: str) -> dict[str, str]: """Create and return the ``supported_modules`` dict by parsing the MCPClient - modules config file (typically MCPClient/lib/archivematicaClientModules). + modules config file (typically MCPClient/archivematicaClientModules). """ supported_modules = {} supported_modules_config = RawConfigParser() @@ -24,7 +23,9 @@ def get_supported_modules(modules_file_path: str) -> Dict[str, str]: def load_module(module_name: str) -> Optional[ModuleType]: # No need to cache here as imports are already cached. try: - return importlib.import_module(f"clientScripts.{module_name}") + return importlib.import_module( + f"archivematica.MCPClient.clientScripts.{module_name}" + ) except ImportError as err: warnings.warn( f"Failed to load client script {module_name}: {err}", @@ -41,7 +42,7 @@ def get_module_concurrency(module: ModuleType) -> int: return 1 -def load_job_modules(modules_file_path: str) -> Dict[str, Optional[ModuleType]]: +def load_job_modules(modules_file_path: str) -> dict[str, Optional[ModuleType]]: """Return a dict of {client script name: module}.""" supported_modules = get_supported_modules(modules_file_path) diff --git a/src/MCPClient/lib/client/mcp.py b/src/archivematica/MCPClient/client/mcp.py similarity index 62% rename from src/MCPClient/lib/client/mcp.py rename to src/archivematica/MCPClient/client/mcp.py index ab84002c47..c99b597404 100644 --- a/src/MCPClient/lib/client/mcp.py +++ b/src/archivematica/MCPClient/client/mcp.py @@ -2,15 +2,15 @@ Main MCPClient entrypoint. """ +import importlib.resources import logging import os -import pathlib import signal from types import FrameType from typing import Optional -from client import metrics -from client.pool import WorkerPool +from archivematica.MCPClient.client import metrics +from archivematica.MCPClient.client.pool import WorkerPool logger = logging.getLogger("archivematica.mcp.client") @@ -19,8 +19,15 @@ def main() -> None: metrics.start_prometheus_server() # Use local XML schemas for validation. - os.environ["XML_CATALOG_FILES"] = str( - pathlib.Path(__file__).parent.parent / "assets" / "catalog" / "catalog.xml" + existing_catalogs = os.environ.get("XML_CATALOG_FILES", "") + catalog = str( + importlib.resources.files("archivematica.MCPClient") + / "assets" + / "catalog" + / "catalog.xml" + ) + os.environ["XML_CATALOG_FILES"] = ( + f"{catalog} {existing_catalogs}" if existing_catalogs else catalog ) pool = WorkerPool() diff --git a/src/MCPClient/lib/client/metrics.py b/src/archivematica/MCPClient/client/metrics.py similarity index 95% rename from src/MCPClient/lib/client/metrics.py rename to src/archivematica/MCPClient/client/metrics.py index 20df295a8b..bbed6c292f 100644 --- a/src/MCPClient/lib/client/metrics.py +++ b/src/archivematica/MCPClient/client/metrics.py @@ -8,7 +8,6 @@ import threading from typing import Callable from typing import Optional -from typing import Tuple from typing import TypeVar from wsgiref.simple_server import WSGIServer @@ -16,17 +15,9 @@ django.setup() -from common_metrics import PACKAGE_FILE_COUNT_BUCKETS -from common_metrics import PACKAGE_SIZE_BUCKETS -from common_metrics import PROCESSING_TIME_BUCKETS -from common_metrics import TASK_DURATION_BUCKETS from django.conf import settings from django.db.models import Sum from django.utils import timezone -from fpr.models import FormatVersion -from main.models import File -from main.models import FileFormatVersion -from main.models import Transfer from prometheus_client import CollectorRegistry from prometheus_client import Counter from prometheus_client import Gauge @@ -34,6 +25,15 @@ from prometheus_client import multiprocess from prometheus_client import start_http_server +from archivematica.archivematicaCommon.common_metrics import PACKAGE_FILE_COUNT_BUCKETS +from archivematica.archivematicaCommon.common_metrics import PACKAGE_SIZE_BUCKETS +from archivematica.archivematicaCommon.common_metrics import PROCESSING_TIME_BUCKETS +from archivematica.archivematicaCommon.common_metrics import TASK_DURATION_BUCKETS +from archivematica.dashboard.fpr.models import FormatVersion +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import FileFormatVersion +from archivematica.dashboard.main.models import Transfer + job_counter = Counter( "mcpclient_job_total", "Number of jobs processed, labeled by script", @@ -255,13 +255,13 @@ def worker_exit(process_id: Optional[int]) -> None: @skip_if_prometheus_disabled -def start_prometheus_server() -> Tuple[WSGIServer, threading.Thread]: +def start_prometheus_server() -> tuple[WSGIServer, threading.Thread]: registry = CollectorRegistry() multiprocess.MultiProcessCollector(registry) init_counter_labels() - result: Tuple[WSGIServer, threading.Thread] = start_http_server( + result: tuple[WSGIServer, threading.Thread] = start_http_server( settings.PROMETHEUS_BIND_PORT, addr=settings.PROMETHEUS_BIND_ADDRESS, registry=registry, diff --git a/src/MCPClient/lib/client/pool.py b/src/archivematica/MCPClient/client/pool.py similarity index 93% rename from src/MCPClient/lib/client/pool.py rename to src/archivematica/MCPClient/client/pool.py index 936cca13c2..a57d52d0ae 100644 --- a/src/MCPClient/lib/client/pool.py +++ b/src/archivematica/MCPClient/client/pool.py @@ -20,11 +20,8 @@ import time from multiprocessing.synchronize import Event from types import ModuleType -from typing import Dict -from typing import List from typing import Optional from typing import Protocol -from typing import Tuple from typing import TypeVar import django @@ -34,9 +31,9 @@ from django import db from django.conf import settings -from client import loader -from client import metrics -from client.gearman import MCPGearmanWorker +from archivematica.MCPClient.client import loader +from archivematica.MCPClient.client import metrics +from archivematica.MCPClient.client.gearman import MCPGearmanWorker T = TypeVar("T") @@ -68,14 +65,14 @@ def put_nowait(self, item: T) -> None: ... # ), # ... # ] -WorkerInitArgs = List[Tuple[Tuple[LogQueue, List[str]], Dict[str, Event]]] +WorkerInitArgs = list[tuple[tuple[LogQueue, list[str]], dict[str, Event]]] logger = logging.getLogger("archivematica.mcp.client.worker") def run_gearman_worker( log_queue: LogQueue, - client_scripts: List[str], + client_scripts: list[str], shutdown_event: Optional[Event] = None, ) -> None: """Target function executed by child processes in the pool.""" @@ -110,7 +107,7 @@ class WorkerPool: def __init__(self) -> None: self.log_queue: LogQueue = multiprocessing.Queue() self.shutdown_event = multiprocessing.Event() - self.workers: List[multiprocessing.Process] = [] + self.workers: list[multiprocessing.Process] = [] self.job_modules = loader.load_job_modules(settings.CLIENT_MODULES_FILE) self.worker_function = run_gearman_worker @@ -161,8 +158,8 @@ def stop(self) -> None: self.logging_listener.stop() def _get_script_workers_required( - self, job_modules: Dict[str, Optional[ModuleType]] - ) -> Dict[str, int]: + self, job_modules: dict[str, Optional[ModuleType]] + ) -> dict[str, int]: workers_required = {} for client_script, module in job_modules.items(): concurrency = loader.get_module_concurrency(module) @@ -172,11 +169,11 @@ def _get_script_workers_required( # Use Queue[logging.LogRecord] instead of Any def _get_worker_init_args( - self, script_workers_required: Dict[str, int] + self, script_workers_required: dict[str, int] ) -> WorkerInitArgs: # Don't mutate the argument script_workers_required = script_workers_required.copy() - init_scripts: List[List[str]] = [] + init_scripts: list[list[str]] = [] for i in range(self.pool_size): init_scripts.append([]) diff --git a/src/MCPClient/lib/client/utils.py b/src/archivematica/MCPClient/client/utils.py similarity index 92% rename from src/MCPClient/lib/client/utils.py rename to src/archivematica/MCPClient/client/utils.py index fb2c9213fd..fe90de6946 100644 --- a/src/MCPClient/lib/client/utils.py +++ b/src/archivematica/MCPClient/client/utils.py @@ -1,11 +1,10 @@ import shlex -from typing import List from django.conf import settings from django.utils import timezone -def parse_command_line(s: str) -> List[str]: +def parse_command_line(s: str) -> list[str]: return [_shlex_unescape(x) for x in shlex.split(s)] diff --git a/src/MCPClient/lib/client/worker.py b/src/archivematica/MCPClient/client/worker.py similarity index 77% rename from src/MCPClient/lib/client/worker.py rename to src/archivematica/MCPClient/client/worker.py index ceb9a234c2..45c9d31479 100644 --- a/src/MCPClient/lib/client/worker.py +++ b/src/archivematica/MCPClient/client/worker.py @@ -1,17 +1,15 @@ import logging from types import ModuleType -from typing import List -from dbconns import auto_close_old_connections - -from client import metrics -from client.job import Job +from archivematica.archivematicaCommon.dbconns import auto_close_old_connections +from archivematica.MCPClient.client import metrics +from archivematica.MCPClient.client.job import Job logger = logging.getLogger("archivematica.mcp.client.worker") @auto_close_old_connections() # type: ignore -def run_task(task_name: str, job_module: ModuleType, jobs: List[Job]) -> None: +def run_task(task_name: str, job_module: ModuleType, jobs: list[Job]) -> None: """Do actual processing of the jobs given.""" logger.info("\n\n*** RUNNING TASK: %s***", task_name) Job.bulk_set_start_times(jobs) diff --git a/src/MCPClient/lib/settings/__init__.py b/src/archivematica/MCPClient/clientScripts/__init__.py similarity index 100% rename from src/MCPClient/lib/settings/__init__.py rename to src/archivematica/MCPClient/clientScripts/__init__.py diff --git a/src/archivematica/MCPClient/clientScripts/antivirus.py b/src/archivematica/MCPClient/clientScripts/antivirus.py new file mode 100755 index 0000000000..65f941212a --- /dev/null +++ b/src/archivematica/MCPClient/clientScripts/antivirus.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python +# This file is part of Archivematica. +# +# Copyright 2010-2017 Artefactual Systems Inc. +# +# Archivematica is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Archivematica is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Archivematica. If not, see . +import multiprocessing +import os +import uuid + +import django + +django.setup() +from clamav_client.scanner import get_scanner +from django.conf import settings as mcpclient_settings +from django.core.exceptions import ValidationError +from django.db import transaction + +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.archivematicaCommon.databaseFunctions import insertIntoEvents +from archivematica.dashboard.main.models import Event +from archivematica.dashboard.main.models import File + +logger = get_script_logger("archivematica.mcp.client.clamscan") + + +def concurrent_instances(): + return multiprocessing.cpu_count() + + +def file_already_scanned(file_uuid): + return ( + file_uuid != "None" + and Event.objects.filter( + file_uuid_id=file_uuid, event_type="virus check" + ).exists() + ) + + +def queue_event(file_uuid, date, scanner, passed, queue): + if passed is None or file_uuid == "None": + return + + event_detail = "" + if scanner is not None: + info = scanner.info() # This is cached. + event_detail = f'program="{info.name}"; version="{info.version}"; virusDefinitions="{info.virus_definitions}"' + + outcome = "Pass" if passed else "Fail" + logger.info("Recording new event for file %s (outcome: %s)", file_uuid, outcome) + + queue.append( + { + "fileUUID": file_uuid, + "eventIdentifierUUID": str(uuid.uuid4()), + "eventType": "virus check", + "eventDateTime": date, + "eventDetail": event_detail, + "eventOutcome": outcome, + } + ) + + +# Map the backend names provided by the user in the configuration to their +# corresponding internal values used by the clamav_client package. If no valid +# backend is specified, default to "clamdscanner". +SCANNERS = {"clamscanner": "clamscan", "clamdscanner": "clamd"} +DEFAULT_SCANNER = "clamd" + +# Predefined builders for each backend. +CONFIG_BUILDERS = { + "clamd": lambda: { + "backend": "clamd", + "address": str(mcpclient_settings.CLAMAV_SERVER), + "timeout": int(mcpclient_settings.CLAMAV_CLIENT_TIMEOUT), + "stream": bool(mcpclient_settings.CLAMAV_PASS_BY_STREAM), + }, + "clamscan": lambda: { + "backend": "clamscan", + "max_file_size": float(mcpclient_settings.CLAMAV_CLIENT_MAX_FILE_SIZE), + "max_scan_size": float(mcpclient_settings.CLAMAV_CLIENT_MAX_SCAN_SIZE), + }, +} + + +def create_scanner(): + """Return the ClamAV client configured by the user and found in the + installation's environment variables. Clamdscanner may perform quicker + than Clamscanner given a larger number of objects. Return clamdscanner + object as a default if no other, or an incorrect value is specified. + """ + choice = str(mcpclient_settings.CLAMAV_CLIENT_BACKEND).lower() + backend_key = SCANNERS.get(choice) + if backend_key is None: + logger.warning( + 'Unexpected antivirus scanner (CLAMAV_CLIENT_BACKEND): "%s"; using "%s".', + choice, + DEFAULT_SCANNER, + ) + backend_key = DEFAULT_SCANNER + try: + config = CONFIG_BUILDERS[backend_key]() + except KeyError: + raise ValueError(f"Unexpected backend configuration: {backend_key!r}") + return get_scanner(config) + + +def get_size(file_uuid, path): + # We're going to see this happening when files are not part of `objects/`. + if file_uuid != "None": + try: + return File.objects.get(uuid=file_uuid).size + except (File.DoesNotExist, ValidationError): + pass + # Our fallback. + try: + return os.path.getsize(path) + except Exception: + return None + + +def scan_file(event_queue, file_uuid, path, date): + if file_already_scanned(file_uuid): + logger.info("Virus scan already performed, not running scan again") + return 0 + + scanner, passed = None, False + + try: + size = get_size(file_uuid, path) + if size is None: + logger.error("Getting file size returned: %s", size) + return 1 + + max_file_size = mcpclient_settings.CLAMAV_CLIENT_MAX_FILE_SIZE * 1024 * 1024 + max_scan_size = mcpclient_settings.CLAMAV_CLIENT_MAX_SCAN_SIZE * 1024 * 1024 + + valid_scan = True + + if size > max_file_size: + logger.info( + "File will not be scanned. Size %s bytes greater than scanner " + "max file size %s bytes", + size, + max_file_size, + ) + valid_scan = False + elif size > max_scan_size: + logger.info( + "File will not be scanned. Size %s bytes greater than scanner " + "max scan size %s bytes", + size, + max_scan_size, + ) + valid_scan = False + + if valid_scan: + scanner = create_scanner() + info = scanner.info() + logger.info( + "Using scanner %s (%s - %s)", + info.name, + info.version, + info.virus_definitions, + ) + + result = scanner.scan(path) + passed, state, details = result.passed, result.state, result.details + else: + passed, state, details = None, None, None + + except Exception: + logger.error("Unexpected error scanning file %s", path, exc_info=True) + return 1 + else: + # record pass or fail, but not None if the file hasn't + # been scanned, e.g. Max File Size thresholds being too low. + if passed is not None: + logger.info("File %s scanned!", path) + logger.debug("passed=%s state=%s details=%s", passed, state, details) + finally: + queue_event(file_uuid, date, scanner, passed, event_queue) + + # If True or None, then we have no error, the file can move through the + # process as expected... + return 1 if passed is False else 0 + + +def call(jobs): + event_queue = [] + + for job in jobs: + with job.JobContext(logger=logger): + job.set_status(scan_file(event_queue, *job.args[1:])) + + with transaction.atomic(): + for e in event_queue: + insertIntoEvents(**e) diff --git a/src/MCPClient/lib/clientScripts/archivematicaCreateMETSMetadataCSV.py b/src/archivematica/MCPClient/clientScripts/archivematicaCreateMETSMetadataCSV.py similarity index 98% rename from src/MCPClient/lib/clientScripts/archivematicaCreateMETSMetadataCSV.py rename to src/archivematica/MCPClient/clientScripts/archivematicaCreateMETSMetadataCSV.py index d5bbaa1687..9d71ff8f6d 100755 --- a/src/MCPClient/lib/clientScripts/archivematicaCreateMETSMetadataCSV.py +++ b/src/archivematica/MCPClient/clientScripts/archivematicaCreateMETSMetadataCSV.py @@ -21,7 +21,7 @@ import sys import traceback -import archivematicaFunctions +from archivematica.archivematicaCommon import archivematicaFunctions def parseMetadata(job, SIPPath, state): diff --git a/src/MCPClient/lib/clientScripts/archivematicaCreateMETSMetadataXML.py b/src/archivematica/MCPClient/clientScripts/archivematicaCreateMETSMetadataXML.py similarity index 96% rename from src/MCPClient/lib/clientScripts/archivematicaCreateMETSMetadataXML.py rename to src/archivematica/MCPClient/clientScripts/archivematicaCreateMETSMetadataXML.py index bc045589d2..425dce83a7 100755 --- a/src/MCPClient/lib/clientScripts/archivematicaCreateMETSMetadataXML.py +++ b/src/archivematica/MCPClient/clientScripts/archivematicaCreateMETSMetadataXML.py @@ -19,18 +19,19 @@ """Management of XML metadata files.""" import csv +from importlib.metadata import version from pathlib import Path from urllib.parse import urlparse from urllib.request import urlopen -import create_mets_v2 as createmets2 -import namespaces as ns import requests -from databaseFunctions import insertIntoEvents from django.core.exceptions import ValidationError -from importlib_metadata import version from lxml import etree -from main import models + +from archivematica.archivematicaCommon import namespaces as ns +from archivematica.archivematicaCommon.databaseFunctions import insertIntoEvents +from archivematica.dashboard.main import models +from archivematica.MCPClient.clientScripts import create_mets_v2 as createmets2 def process_xml_metadata(mets, sip_dir, sip_uuid, sip_type, xml_validation): diff --git a/src/MCPClient/lib/clientScripts/archivematicaCreateMETSReingest.py b/src/archivematica/MCPClient/clientScripts/archivematicaCreateMETSReingest.py similarity index 98% rename from src/MCPClient/lib/clientScripts/archivematicaCreateMETSReingest.py rename to src/archivematica/MCPClient/clientScripts/archivematicaCreateMETSReingest.py index faefb82414..8327c834e0 100755 --- a/src/MCPClient/lib/clientScripts/archivematicaCreateMETSReingest.py +++ b/src/archivematica/MCPClient/clientScripts/archivematicaCreateMETSReingest.py @@ -2,14 +2,15 @@ import copy import os -import archivematicaCreateMETSMetadataCSV as createmetscsv -import archivematicaCreateMETSRights as createmetsrights -import create_mets_v2 as createmets2 import metsrw -import namespaces as ns from django.core.exceptions import ValidationError from lxml import etree -from main import models + +import archivematica.MCPClient.clientScripts.archivematicaCreateMETSMetadataCSV as createmetscsv +import archivematica.MCPClient.clientScripts.archivematicaCreateMETSRights as createmetsrights +import archivematica.MCPClient.clientScripts.create_mets_v2 as createmets2 +from archivematica.archivematicaCommon import namespaces as ns +from archivematica.dashboard.main import models def _create_premis_object(premis_object_type): diff --git a/src/MCPClient/lib/clientScripts/archivematicaCreateMETSRights.py b/src/archivematica/MCPClient/clientScripts/archivematicaCreateMETSRights.py similarity index 98% rename from src/MCPClient/lib/clientScripts/archivematicaCreateMETSRights.py rename to src/archivematica/MCPClient/clientScripts/archivematicaCreateMETSRights.py index 37754c963a..61fcce79af 100755 --- a/src/MCPClient/lib/clientScripts/archivematicaCreateMETSRights.py +++ b/src/archivematica/MCPClient/clientScripts/archivematicaCreateMETSRights.py @@ -20,9 +20,10 @@ import uuid import lxml.etree as etree -import namespaces as ns -from countryCodes import getCodeForCountry -from main.models import RightsStatement + +from archivematica.archivematicaCommon import namespaces as ns +from archivematica.archivematicaCommon.countryCodes import getCodeForCountry +from archivematica.dashboard.main.models import RightsStatement RIGHTS_BASIS_OTHER = ["Policy", "Donor"] diff --git a/src/MCPClient/lib/clientScripts/archivematicaCreateMETSRightsDspaceMDRef.py b/src/archivematica/MCPClient/clientScripts/archivematicaCreateMETSRightsDspaceMDRef.py similarity index 97% rename from src/MCPClient/lib/clientScripts/archivematicaCreateMETSRightsDspaceMDRef.py rename to src/archivematica/MCPClient/clientScripts/archivematicaCreateMETSRightsDspaceMDRef.py index 3dd04fd956..906537f5bb 100755 --- a/src/MCPClient/lib/clientScripts/archivematicaCreateMETSRightsDspaceMDRef.py +++ b/src/archivematica/MCPClient/clientScripts/archivematicaCreateMETSRightsDspaceMDRef.py @@ -20,9 +20,10 @@ import sys import lxml.etree as etree -import namespaces as ns from django.core.exceptions import ValidationError -from main.models import File + +from archivematica.archivematicaCommon import namespaces as ns +from archivematica.dashboard.main.models import File def createMDRefDMDSec(LABEL, itemdirectoryPath, directoryPathSTR): diff --git a/src/MCPClient/lib/clientScripts/archivematicaCreateMETSTrim.py b/src/archivematica/MCPClient/clientScripts/archivematicaCreateMETSTrim.py similarity index 98% rename from src/MCPClient/lib/clientScripts/archivematicaCreateMETSTrim.py rename to src/archivematica/MCPClient/clientScripts/archivematicaCreateMETSTrim.py index c3f5ed7e65..e696181731 100755 --- a/src/MCPClient/lib/clientScripts/archivematicaCreateMETSTrim.py +++ b/src/archivematica/MCPClient/clientScripts/archivematicaCreateMETSTrim.py @@ -21,9 +21,10 @@ from datetime import datetime import lxml.etree as etree -import namespaces as ns from django.core.exceptions import ValidationError -from main.models import File + +from archivematica.archivematicaCommon import namespaces as ns +from archivematica.dashboard.main.models import File def getTrimDmdSec(job, baseDirectoryPath, sipUUID): diff --git a/src/MCPClient/lib/clientScripts/assign_file_uuids.py b/src/archivematica/MCPClient/clientScripts/assign_file_uuids.py similarity index 93% rename from src/MCPClient/lib/clientScripts/assign_file_uuids.py rename to src/archivematica/MCPClient/clientScripts/assign_file_uuids.py index 8514cf56a0..8b661d07d2 100755 --- a/src/MCPClient/lib/clientScripts/assign_file_uuids.py +++ b/src/archivematica/MCPClient/clientScripts/assign_file_uuids.py @@ -38,14 +38,15 @@ django.setup() import metsrw -import namespaces as ns -from archivematicaFunctions import chunk_iterable -from archivematicaFunctions import find_mets_file -from custom_handlers import get_script_logger -from fileOperations import addFileToSIP -from fileOperations import addFileToTransfer -from main.models import File -from main.models import Transfer + +from archivematica.archivematicaCommon import namespaces as ns +from archivematica.archivematicaCommon.archivematicaFunctions import chunk_iterable +from archivematica.archivematicaCommon.archivematicaFunctions import find_mets_file +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.archivematicaCommon.fileOperations import addFileToSIP +from archivematica.archivematicaCommon.fileOperations import addFileToTransfer +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import Transfer logger = get_script_logger("archivematica.mcp.client.assignFileUUID") diff --git a/src/MCPClient/lib/clientScripts/assign_uuids_to_directories.py b/src/archivematica/MCPClient/clientScripts/assign_uuids_to_directories.py similarity index 91% rename from src/MCPClient/lib/clientScripts/assign_uuids_to_directories.py rename to src/archivematica/MCPClient/clientScripts/assign_uuids_to_directories.py index 777a426dba..0dd9a6d903 100755 --- a/src/MCPClient/lib/clientScripts/assign_uuids_to_directories.py +++ b/src/archivematica/MCPClient/clientScripts/assign_uuids_to_directories.py @@ -35,14 +35,15 @@ django.setup() -from archivematicaFunctions import format_subdir_path -from archivematicaFunctions import get_dir_uuids -from archivematicaFunctions import str2bool -from custom_handlers import get_script_logger from django.core.exceptions import ValidationError from django.db import transaction -from main.models import Directory -from main.models import Transfer + +from archivematica.archivematicaCommon.archivematicaFunctions import format_subdir_path +from archivematica.archivematicaCommon.archivematicaFunctions import get_dir_uuids +from archivematica.archivematicaCommon.archivematicaFunctions import str2bool +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.dashboard.main.models import Directory +from archivematica.dashboard.main.models import Transfer logger = get_script_logger("archivematica.mcp.client.assignUUIDsToDirectories") diff --git a/src/MCPClient/lib/clientScripts/bag_with_empty_directories.py b/src/archivematica/MCPClient/clientScripts/bag_with_empty_directories.py similarity index 97% rename from src/MCPClient/lib/clientScripts/bag_with_empty_directories.py rename to src/archivematica/MCPClient/clientScripts/bag_with_empty_directories.py index 5022a18c7c..0f11b844fa 100755 --- a/src/MCPClient/lib/clientScripts/bag_with_empty_directories.py +++ b/src/archivematica/MCPClient/clientScripts/bag_with_empty_directories.py @@ -24,10 +24,11 @@ django.setup() -from archivematicaFunctions import get_setting from bagit import make_bag from django.conf import settings as mcpclient_settings +from archivematica.archivematicaCommon.archivematicaFunctions import get_setting + def get_sip_directories(job, sip_dir): """Get a list of directories in the SIP, to be created after bagged.""" diff --git a/src/MCPClient/lib/clientScripts/bind_pid.py b/src/archivematica/MCPClient/clientScripts/bind_pid.py similarity index 93% rename from src/MCPClient/lib/clientScripts/bind_pid.py rename to src/archivematica/MCPClient/clientScripts/bind_pid.py index 15fff4194d..e411da0e25 100755 --- a/src/MCPClient/lib/clientScripts/bind_pid.py +++ b/src/archivematica/MCPClient/clientScripts/bind_pid.py @@ -48,11 +48,11 @@ django.setup() -import bindpid -from archivematicaFunctions import str2bool -from custom_handlers import get_script_logger -from main.models import DashboardSetting -from main.models import File +from archivematica.archivematicaCommon import bindpid +from archivematica.archivematicaCommon.archivematicaFunctions import str2bool +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.dashboard.main.models import DashboardSetting +from archivematica.dashboard.main.models import File logger = get_script_logger("archivematica.mcp.client.bind_pid") diff --git a/src/MCPClient/lib/clientScripts/bind_pids.py b/src/archivematica/MCPClient/clientScripts/bind_pids.py similarity index 92% rename from src/MCPClient/lib/clientScripts/bind_pids.py rename to src/archivematica/MCPClient/clientScripts/bind_pids.py index e0973d0287..5be8acde40 100755 --- a/src/MCPClient/lib/clientScripts/bind_pids.py +++ b/src/archivematica/MCPClient/clientScripts/bind_pids.py @@ -47,19 +47,22 @@ import django django.setup() -import namespaces as ns -from archivematicaFunctions import str2bool -from bindpid import BindPIDException -from bindpid import _validate_entity_type_required_params -from bindpid import _validate_handle_server_config -from bindpid import bind_pid -from custom_handlers import get_script_logger from django.core.exceptions import ValidationError from django.db import transaction from lxml import etree -from main.models import SIP -from main.models import DashboardSetting -from main.models import Directory + +from archivematica.archivematicaCommon import namespaces as ns +from archivematica.archivematicaCommon.archivematicaFunctions import str2bool +from archivematica.archivematicaCommon.bindpid import BindPIDException +from archivematica.archivematicaCommon.bindpid import ( + _validate_entity_type_required_params, +) +from archivematica.archivematicaCommon.bindpid import _validate_handle_server_config +from archivematica.archivematicaCommon.bindpid import bind_pid +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import DashboardSetting +from archivematica.dashboard.main.models import Directory logger = get_script_logger("archivematica.mcp.client.bind_pids") diff --git a/src/MCPClient/lib/clientScripts/change_names.py b/src/archivematica/MCPClient/clientScripts/change_names.py similarity index 98% rename from src/MCPClient/lib/clientScripts/change_names.py rename to src/archivematica/MCPClient/clientScripts/change_names.py index 3ed60176f8..64529ea2ba 100755 --- a/src/MCPClient/lib/clientScripts/change_names.py +++ b/src/archivematica/MCPClient/clientScripts/change_names.py @@ -21,8 +21,6 @@ from unidecode import unidecode -VERSION = "1.10." + "$Id$".split(" ")[1] - # Letters, digits and a few punctuation characters ALLOWED_CHARS = re.compile(r"[^a-zA-Z0-9\-_.\(\)]") REPLACEMENT_CHAR = "_" diff --git a/src/MCPClient/lib/clientScripts/change_object_names.py b/src/archivematica/MCPClient/clientScripts/change_object_names.py similarity index 94% rename from src/MCPClient/lib/clientScripts/change_object_names.py rename to src/archivematica/MCPClient/clientScripts/change_object_names.py index 6146fcee83..29d583dd82 100755 --- a/src/MCPClient/lib/clientScripts/change_object_names.py +++ b/src/archivematica/MCPClient/clientScripts/change_object_names.py @@ -22,14 +22,16 @@ import django django.setup() -import change_names -from custom_handlers import get_script_logger from django.db import transaction -from main.models import SIP -from main.models import Directory -from main.models import Event -from main.models import File -from main.models import Transfer + +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.archivematicaCommon.version import get_full_version +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import Directory +from archivematica.dashboard.main.models import Event +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import Transfer +from archivematica.MCPClient.clientScripts import change_names logger = get_script_logger("archivematica.mcp.client.changeObjectNames") @@ -43,7 +45,7 @@ class NameChanger: BATCH_SIZE = 2000 EVENT_DETAIL = ( 'prohibited characters removed: program="change_names"; version="' - + change_names.VERSION + + get_full_version() + '"' ) EVENT_OUTCOME_DETAIL = 'Original name="{}"; new name="{}"' diff --git a/src/MCPClient/lib/clientScripts/change_sip_name.py b/src/archivematica/MCPClient/clientScripts/change_sip_name.py similarity index 92% rename from src/MCPClient/lib/clientScripts/change_sip_name.py rename to src/archivematica/MCPClient/clientScripts/change_sip_name.py index 0ce261290d..ada621310d 100755 --- a/src/MCPClient/lib/clientScripts/change_sip_name.py +++ b/src/archivematica/MCPClient/clientScripts/change_sip_name.py @@ -21,10 +21,11 @@ import django django.setup() -from change_names import change_path from django.db import transaction -from main.models import SIP -from main.models import Transfer + +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import Transfer +from archivematica.MCPClient.clientScripts.change_names import change_path def call(jobs): diff --git a/src/MCPClient/lib/clientScripts/characterize_file.py b/src/archivematica/MCPClient/clientScripts/characterize_file.py similarity index 87% rename from src/MCPClient/lib/clientScripts/characterize_file.py rename to src/archivematica/MCPClient/clientScripts/characterize_file.py index dd875d5a2a..189f7d0f7a 100755 --- a/src/MCPClient/lib/clientScripts/characterize_file.py +++ b/src/archivematica/MCPClient/clientScripts/characterize_file.py @@ -10,25 +10,27 @@ import dataclasses import multiprocessing import uuid -from typing import List import django django.setup() -from client.job import Job -from databaseFunctions import insertIntoFPCommandOutput -from dicts import ReplacementDict -from dicts import replace_string_values from django.conf import settings as mcpclient_settings from django.core.exceptions import ValidationError from django.db import transaction -from executeOrRunSubProcess import executeOrRun -from fpr.models import FormatVersion -from fpr.models import FPRule -from lib import setup_dicts from lxml import etree -from main.models import FPCommandOutput + +from archivematica.archivematicaCommon.databaseFunctions import ( + insertIntoFPCommandOutput, +) +from archivematica.archivematicaCommon.dicts import ReplacementDict +from archivematica.archivematicaCommon.dicts import replace_string_values +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun +from archivematica.dashboard.fpr.models import FormatVersion +from archivematica.dashboard.fpr.models import FPRule +from archivematica.dashboard.main.models import FPCommandOutput +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts.lib import setup_dicts @dataclasses.dataclass @@ -139,7 +141,7 @@ def parse_args(parser: argparse.ArgumentParser, job: Job) -> CharacterizeFileArg return CharacterizeFileArgs(**vars(namespace)) -def call(jobs: List[Job]) -> None: +def call(jobs: list[Job]) -> None: parser = get_parser() with transaction.atomic(): diff --git a/src/MCPClient/lib/clientScripts/check_for_access_directory.py b/src/archivematica/MCPClient/clientScripts/check_for_access_directory.py similarity index 97% rename from src/MCPClient/lib/clientScripts/check_for_access_directory.py rename to src/archivematica/MCPClient/clientScripts/check_for_access_directory.py index d70bcb1feb..bb84395435 100755 --- a/src/MCPClient/lib/clientScripts/check_for_access_directory.py +++ b/src/archivematica/MCPClient/clientScripts/check_for_access_directory.py @@ -24,9 +24,9 @@ django.setup() -from fileOperations import rename -from fileOperations import updateFileLocation -from main.models import File +from archivematica.archivematicaCommon.fileOperations import rename +from archivematica.archivematicaCommon.fileOperations import updateFileLocation +from archivematica.dashboard.main.models import File def main( diff --git a/src/MCPClient/lib/clientScripts/check_for_service_directory.py b/src/archivematica/MCPClient/clientScripts/check_for_service_directory.py similarity index 99% rename from src/MCPClient/lib/clientScripts/check_for_service_directory.py rename to src/archivematica/MCPClient/clientScripts/check_for_service_directory.py index 4ec5ceb0f5..be107f5862 100755 --- a/src/MCPClient/lib/clientScripts/check_for_service_directory.py +++ b/src/archivematica/MCPClient/clientScripts/check_for_service_directory.py @@ -24,7 +24,7 @@ django.setup() -from main.models import File +from archivematica.dashboard.main.models import File def something(job, SIPDirectory, serviceDirectory, objectsDirectory, SIPUUID, date): diff --git a/src/MCPClient/lib/clientScripts/check_for_submission_documentation.py b/src/archivematica/MCPClient/clientScripts/check_for_submission_documentation.py similarity index 100% rename from src/MCPClient/lib/clientScripts/check_for_submission_documentation.py rename to src/archivematica/MCPClient/clientScripts/check_for_submission_documentation.py diff --git a/src/MCPClient/lib/clientScripts/check_transfer_directory_for_objects.py b/src/archivematica/MCPClient/clientScripts/check_transfer_directory_for_objects.py similarity index 100% rename from src/MCPClient/lib/clientScripts/check_transfer_directory_for_objects.py rename to src/archivematica/MCPClient/clientScripts/check_transfer_directory_for_objects.py diff --git a/src/MCPClient/lib/clientScripts/cmd_chmod.py b/src/archivematica/MCPClient/clientScripts/cmd_chmod.py similarity index 81% rename from src/MCPClient/lib/clientScripts/cmd_chmod.py rename to src/archivematica/MCPClient/clientScripts/cmd_chmod.py index 959cfbae2e..c100a74343 100755 --- a/src/MCPClient/lib/clientScripts/cmd_chmod.py +++ b/src/archivematica/MCPClient/clientScripts/cmd_chmod.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -from executeOrRunSubProcess import executeOrRun +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun def call(jobs): diff --git a/src/MCPClient/lib/clientScripts/cmd_cp.py b/src/archivematica/MCPClient/clientScripts/cmd_cp.py similarity index 81% rename from src/MCPClient/lib/clientScripts/cmd_cp.py rename to src/archivematica/MCPClient/clientScripts/cmd_cp.py index 35b6d2e975..5fd2684d67 100755 --- a/src/MCPClient/lib/clientScripts/cmd_cp.py +++ b/src/archivematica/MCPClient/clientScripts/cmd_cp.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -from executeOrRunSubProcess import executeOrRun +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun def call(jobs): diff --git a/src/MCPClient/lib/clientScripts/cmd_mkdir.py b/src/archivematica/MCPClient/clientScripts/cmd_mkdir.py similarity index 81% rename from src/MCPClient/lib/clientScripts/cmd_mkdir.py rename to src/archivematica/MCPClient/clientScripts/cmd_mkdir.py index f68a594a23..a3d15cf2fa 100755 --- a/src/MCPClient/lib/clientScripts/cmd_mkdir.py +++ b/src/archivematica/MCPClient/clientScripts/cmd_mkdir.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -from executeOrRunSubProcess import executeOrRun +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun def call(jobs): diff --git a/src/MCPClient/lib/clientScripts/cmd_mv.py b/src/archivematica/MCPClient/clientScripts/cmd_mv.py similarity index 81% rename from src/MCPClient/lib/clientScripts/cmd_mv.py rename to src/archivematica/MCPClient/clientScripts/cmd_mv.py index 31aa034c55..18ff8455b2 100755 --- a/src/MCPClient/lib/clientScripts/cmd_mv.py +++ b/src/archivematica/MCPClient/clientScripts/cmd_mv.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -from executeOrRunSubProcess import executeOrRun +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun def call(jobs): diff --git a/src/MCPClient/lib/clientScripts/cmd_test.py b/src/archivematica/MCPClient/clientScripts/cmd_test.py similarity index 81% rename from src/MCPClient/lib/clientScripts/cmd_test.py rename to src/archivematica/MCPClient/clientScripts/cmd_test.py index 86f86d71a5..785bfc90e1 100755 --- a/src/MCPClient/lib/clientScripts/cmd_test.py +++ b/src/archivematica/MCPClient/clientScripts/cmd_test.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -from executeOrRunSubProcess import executeOrRun +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun def call(jobs): diff --git a/src/MCPClient/lib/clientScripts/cmd_tree.py b/src/archivematica/MCPClient/clientScripts/cmd_tree.py similarity index 81% rename from src/MCPClient/lib/clientScripts/cmd_tree.py rename to src/archivematica/MCPClient/clientScripts/cmd_tree.py index f5f1a96baa..b2f0b3292f 100755 --- a/src/MCPClient/lib/clientScripts/cmd_tree.py +++ b/src/archivematica/MCPClient/clientScripts/cmd_tree.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -from executeOrRunSubProcess import executeOrRun +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun def call(jobs): diff --git a/src/MCPClient/lib/clientScripts/compress_aip.py b/src/archivematica/MCPClient/clientScripts/compress_aip.py similarity index 91% rename from src/MCPClient/lib/clientScripts/compress_aip.py rename to src/archivematica/MCPClient/clientScripts/compress_aip.py index 0e1123f10e..86ed79ca51 100755 --- a/src/MCPClient/lib/clientScripts/compress_aip.py +++ b/src/archivematica/MCPClient/clientScripts/compress_aip.py @@ -4,14 +4,14 @@ import sys import django +from django.db import close_old_connections from django.db import transaction django.setup() - -import databaseFunctions -from executeOrRunSubProcess import executeOrRun -from main.models import SIP +from archivematica.archivematicaCommon import databaseFunctions +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun +from archivematica.dashboard.main.models import SIP def update_unit(sip_uuid, compressed_location): @@ -67,7 +67,7 @@ def compress_aip( tool_info_command = ( r'echo program="7z"\; ' rf'algorithm="{compression_algorithm}"\; ' - 'version="`7z | grep Version`"' + "version=\"`7z | awk 'NR==3 && /^p7zip Version/ {print; exit} NR==2 {line2=$0} NR==3 {print line2 $0}'`\"" ) elif program == "pbzip2": compressed_location = uncompressed_location + ".tar.bz2" @@ -151,6 +151,11 @@ def call(jobs): if result: state.append(result) + # Close stale connections before beginning the transaction to ensure a new + # connection. This avoids MySQL timeouts that can occur after lengthy + # compression processes. + close_old_connections() + with transaction.atomic(): for result in state: update_unit(result["sip_uuid"], result["location"]) diff --git a/src/MCPClient/lib/clientScripts/convert_dataverse_structure.py b/src/archivematica/MCPClient/clientScripts/convert_dataverse_structure.py similarity index 99% rename from src/MCPClient/lib/clientScripts/convert_dataverse_structure.py rename to src/archivematica/MCPClient/clientScripts/convert_dataverse_structure.py index 0d2b533060..d8f57032e8 100644 --- a/src/MCPClient/lib/clientScripts/convert_dataverse_structure.py +++ b/src/archivematica/MCPClient/clientScripts/convert_dataverse_structure.py @@ -33,7 +33,8 @@ django.setup() import metsrw -from custom_handlers import get_script_logger + +from archivematica.archivematicaCommon.custom_handlers import get_script_logger logger = get_script_logger("archivematica.mcp.client.convert_dataverse_struct") @@ -393,7 +394,7 @@ def add_dataset_files_to_md(job, sip, dataset_md_latest, contact_information): is_restricted = file_json.get("restricted") if is_restricted is True and contact_information: logger.error( - "Restricted dataset files may not have transferred " "correctly: %s", + "Restricted dataset files may not have transferred correctly: %s", contact_information, ) @@ -407,7 +408,7 @@ def add_dataset_files_to_md(job, sip, dataset_md_latest, contact_information): sip.add_child(bundle) else: logger.error( - "Create Dataverse transfer METS failed. " "Bundle returned: %s", + "Create Dataverse transfer METS failed. Bundle returned: %s", bundle, ) return None diff --git a/src/MCPClient/lib/clientScripts/copyRecursiveIfExists.sh b/src/archivematica/MCPClient/clientScripts/copyRecursiveIfExists.sh similarity index 100% rename from src/MCPClient/lib/clientScripts/copyRecursiveIfExists.sh rename to src/archivematica/MCPClient/clientScripts/copyRecursiveIfExists.sh diff --git a/src/MCPClient/lib/clientScripts/copySubmissionDocs.sh b/src/archivematica/MCPClient/clientScripts/copySubmissionDocs.sh similarity index 100% rename from src/MCPClient/lib/clientScripts/copySubmissionDocs.sh rename to src/archivematica/MCPClient/clientScripts/copySubmissionDocs.sh diff --git a/src/MCPClient/lib/clientScripts/copy_recursive.py b/src/archivematica/MCPClient/clientScripts/copy_recursive.py similarity index 85% rename from src/MCPClient/lib/clientScripts/copy_recursive.py rename to src/archivematica/MCPClient/clientScripts/copy_recursive.py index 2d77b07fc5..9bc09c77b3 100755 --- a/src/MCPClient/lib/clientScripts/copy_recursive.py +++ b/src/archivematica/MCPClient/clientScripts/copy_recursive.py @@ -1,7 +1,7 @@ #!/usr/bin/env python import os -from executeOrRunSubProcess import executeOrRun +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun def call(jobs): diff --git a/src/MCPClient/lib/clientScripts/copy_submission_docs.py b/src/archivematica/MCPClient/clientScripts/copy_submission_docs.py similarity index 85% rename from src/MCPClient/lib/clientScripts/copy_submission_docs.py rename to src/archivematica/MCPClient/clientScripts/copy_submission_docs.py index 9f9828c29e..398136ffe4 100755 --- a/src/MCPClient/lib/clientScripts/copy_submission_docs.py +++ b/src/archivematica/MCPClient/clientScripts/copy_submission_docs.py @@ -1,7 +1,7 @@ #!/usr/bin/env python import os -from executeOrRunSubProcess import executeOrRun +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun def call(jobs): diff --git a/src/MCPClient/lib/clientScripts/copy_thumbnails_to_dip_directory.py b/src/archivematica/MCPClient/clientScripts/copy_thumbnails_to_dip_directory.py similarity index 100% rename from src/MCPClient/lib/clientScripts/copy_thumbnails_to_dip_directory.py rename to src/archivematica/MCPClient/clientScripts/copy_thumbnails_to_dip_directory.py diff --git a/src/MCPClient/lib/clientScripts/copy_transfer_submission_documentation.py b/src/archivematica/MCPClient/clientScripts/copy_transfer_submission_documentation.py similarity index 93% rename from src/MCPClient/lib/clientScripts/copy_transfer_submission_documentation.py rename to src/archivematica/MCPClient/clientScripts/copy_transfer_submission_documentation.py index b5e8bcdc23..1ab4b19342 100755 --- a/src/MCPClient/lib/clientScripts/copy_transfer_submission_documentation.py +++ b/src/archivematica/MCPClient/clientScripts/copy_transfer_submission_documentation.py @@ -23,10 +23,12 @@ django.setup() -from archivematicaFunctions import find_transfer_path_from_ingest -from bag import is_bag -from main.models import SIP -from main.models import File +from archivematica.archivematicaCommon.archivematicaFunctions import ( + find_transfer_path_from_ingest, +) +from archivematica.archivematicaCommon.bag import is_bag +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import File def call(jobs): diff --git a/src/MCPClient/lib/clientScripts/copy_transfers_metadata_and_logs.py b/src/archivematica/MCPClient/clientScripts/copy_transfers_metadata_and_logs.py similarity index 94% rename from src/MCPClient/lib/clientScripts/copy_transfers_metadata_and_logs.py rename to src/archivematica/MCPClient/clientScripts/copy_transfers_metadata_and_logs.py index a642f31f06..ce5a5ddc85 100755 --- a/src/MCPClient/lib/clientScripts/copy_transfers_metadata_and_logs.py +++ b/src/archivematica/MCPClient/clientScripts/copy_transfers_metadata_and_logs.py @@ -24,10 +24,12 @@ django.setup() -from archivematicaFunctions import find_transfer_path_from_ingest -from bag import is_bag -from main.models import SIP -from main.models import File +from archivematica.archivematicaCommon.archivematicaFunctions import ( + find_transfer_path_from_ingest, +) +from archivematica.archivematicaCommon.bag import is_bag +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import File def main( diff --git a/src/MCPClient/lib/clientScripts/create_aic_mets.py b/src/archivematica/MCPClient/clientScripts/create_aic_mets.py similarity index 93% rename from src/MCPClient/lib/clientScripts/create_aic_mets.py rename to src/archivematica/MCPClient/clientScripts/create_aic_mets.py index 7bd393510e..352d344910 100755 --- a/src/MCPClient/lib/clientScripts/create_aic_mets.py +++ b/src/archivematica/MCPClient/clientScripts/create_aic_mets.py @@ -12,13 +12,14 @@ django.setup() -import create_mets_v2 -import databaseFunctions -import fileOperations -import namespaces as ns -import storageService as storage_service from django.utils import timezone -from main.models import UnitVariable + +from archivematica.archivematicaCommon import databaseFunctions +from archivematica.archivematicaCommon import fileOperations +from archivematica.archivematicaCommon import namespaces as ns +from archivematica.archivematicaCommon import storageService as storage_service +from archivematica.dashboard.main.models import UnitVariable +from archivematica.MCPClient.clientScripts import create_mets_v2 def get_aip_info(aic_dir, job): diff --git a/src/MCPClient/lib/clientScripts/create_event.py b/src/archivematica/MCPClient/clientScripts/create_event.py similarity index 97% rename from src/MCPClient/lib/clientScripts/create_event.py rename to src/archivematica/MCPClient/clientScripts/create_event.py index 742de8fcda..6e3dacadc2 100755 --- a/src/MCPClient/lib/clientScripts/create_event.py +++ b/src/archivematica/MCPClient/clientScripts/create_event.py @@ -22,9 +22,10 @@ django.setup() -from databaseFunctions import insertIntoEvents from django.db import transaction +from archivematica.archivematicaCommon.databaseFunctions import insertIntoEvents + def call(jobs): parser = OptionParser() diff --git a/src/MCPClient/lib/clientScripts/create_mets_dataverse_v2.py b/src/archivematica/MCPClient/clientScripts/create_mets_dataverse_v2.py similarity index 95% rename from src/MCPClient/lib/clientScripts/create_mets_dataverse_v2.py rename to src/archivematica/MCPClient/clientScripts/create_mets_dataverse_v2.py index 34c8c5c9cb..d21b0c7310 100644 --- a/src/MCPClient/lib/clientScripts/create_mets_dataverse_v2.py +++ b/src/archivematica/MCPClient/clientScripts/create_mets_dataverse_v2.py @@ -20,9 +20,10 @@ import sys -import archivematicaFunctions import metsrw -from custom_handlers import get_script_logger + +from archivematica.archivematicaCommon import archivematicaFunctions +from archivematica.archivematicaCommon.custom_handlers import get_script_logger logger = get_script_logger("archivematica.mcp.client.createMETSDataverse") diff --git a/src/MCPClient/lib/clientScripts/create_mets_v2.py b/src/archivematica/MCPClient/clientScripts/create_mets_v2.py similarity index 97% rename from src/MCPClient/lib/clientScripts/create_mets_v2.py rename to src/archivematica/MCPClient/clientScripts/create_mets_v2.py index c61a9a3a9c..05abff365f 100755 --- a/src/MCPClient/lib/clientScripts/create_mets_v2.py +++ b/src/archivematica/MCPClient/clientScripts/create_mets_v2.py @@ -35,37 +35,56 @@ django.setup() -import archivematicaCreateMETSMetadataXML -import archivematicaCreateMETSReingest -import namespaces as ns -from archivematicaCreateMETSMetadataCSV import parseMetadata -from archivematicaCreateMETSRights import archivematicaGetRights -from archivematicaCreateMETSRightsDspaceMDRef import ( - archivematicaCreateMETSRightsDspaceMDRef, -) -from archivematicaCreateMETSTrim import getTrimAmdSec -from archivematicaCreateMETSTrim import getTrimDmdSec -from archivematicaCreateMETSTrim import getTrimFileAmdSec -from archivematicaCreateMETSTrim import getTrimFileDmdSec -from archivematicaFunctions import normalizeNonDcElementName from bagit import Bag from bagit import BagError -from change_names import change_name -from create_mets_dataverse_v2 import create_dataverse_sip_dmdsec -from create_mets_dataverse_v2 import create_dataverse_tabfile_dmdsec -from custom_handlers import get_script_logger from django.conf import settings as mcpclient_settings from django.core.exceptions import ValidationError -from main.models import SIP -from main.models import Agent -from main.models import Derivation -from main.models import Directory -from main.models import DublinCore -from main.models import Event -from main.models import File -from main.models import FileID -from main.models import FPCommandOutput -from main.models import SIPArrange + +from archivematica.archivematicaCommon import namespaces as ns +from archivematica.archivematicaCommon.archivematicaFunctions import ( + normalizeNonDcElementName, +) +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import Agent +from archivematica.dashboard.main.models import Derivation +from archivematica.dashboard.main.models import Directory +from archivematica.dashboard.main.models import DublinCore +from archivematica.dashboard.main.models import Event +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import FileID +from archivematica.dashboard.main.models import FPCommandOutput +from archivematica.dashboard.main.models import SIPArrange +from archivematica.MCPClient.clientScripts import archivematicaCreateMETSMetadataXML +from archivematica.MCPClient.clientScripts import archivematicaCreateMETSReingest +from archivematica.MCPClient.clientScripts.archivematicaCreateMETSMetadataCSV import ( + parseMetadata, +) +from archivematica.MCPClient.clientScripts.archivematicaCreateMETSRights import ( + archivematicaGetRights, +) +from archivematica.MCPClient.clientScripts.archivematicaCreateMETSRightsDspaceMDRef import ( + archivematicaCreateMETSRightsDspaceMDRef, +) +from archivematica.MCPClient.clientScripts.archivematicaCreateMETSTrim import ( + getTrimAmdSec, +) +from archivematica.MCPClient.clientScripts.archivematicaCreateMETSTrim import ( + getTrimDmdSec, +) +from archivematica.MCPClient.clientScripts.archivematicaCreateMETSTrim import ( + getTrimFileAmdSec, +) +from archivematica.MCPClient.clientScripts.archivematicaCreateMETSTrim import ( + getTrimFileDmdSec, +) +from archivematica.MCPClient.clientScripts.change_names import change_name +from archivematica.MCPClient.clientScripts.create_mets_dataverse_v2 import ( + create_dataverse_sip_dmdsec, +) +from archivematica.MCPClient.clientScripts.create_mets_dataverse_v2 import ( + create_dataverse_tabfile_dmdsec, +) SIP_DIR_VAR = r"%SIPDirectory%" @@ -1420,7 +1439,7 @@ def create_object_metadata(job, struct_map, baseDirectoryPath, state): ) bag_info = [find_bag_metadata(job, path) for path in paths] - if not transfer and not source and not bag_info: + if not transfer and not source and not any(bag_info): return state.globalAmdSecCounter += 1 @@ -1477,7 +1496,7 @@ def create_object_metadata(job, struct_map, baseDirectoryPath, state): bag_tag = etree.SubElement(bag_metadata, key) except ValueError: job.pyprint( - f"Skipping bag key {key}; not a valid" " XML tag name", + f"Skipping bag key {key}; not a valid XML tag name", file=sys.stderr, ) continue diff --git a/src/MCPClient/lib/clientScripts/create_sip_from_transfer_objects.py b/src/archivematica/MCPClient/clientScripts/create_sip_from_transfer_objects.py similarity index 95% rename from src/MCPClient/lib/clientScripts/create_sip_from_transfer_objects.py rename to src/archivematica/MCPClient/clientScripts/create_sip_from_transfer_objects.py index f68d09d3cf..d590f7bdc3 100755 --- a/src/MCPClient/lib/clientScripts/create_sip_from_transfer_objects.py +++ b/src/archivematica/MCPClient/clientScripts/create_sip_from_transfer_objects.py @@ -24,15 +24,16 @@ django.setup() -import archivematicaFunctions from django.core.exceptions import ValidationError from django.db import transaction -from main.models import SIP -from main.models import Agent -from main.models import Directory -from main.models import File -from main.models import Transfer -from main.models import UnitVariable + +from archivematica.archivematicaCommon import archivematicaFunctions +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import Agent +from archivematica.dashboard.main.models import Directory +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import Transfer +from archivematica.dashboard.main.models import UnitVariable def call(jobs): diff --git a/src/MCPClient/lib/clientScripts/create_sips_from_trim_transfer_containers.py b/src/archivematica/MCPClient/clientScripts/create_sips_from_trim_transfer_containers.py similarity index 95% rename from src/MCPClient/lib/clientScripts/create_sips_from_trim_transfer_containers.py rename to src/archivematica/MCPClient/clientScripts/create_sips_from_trim_transfer_containers.py index ab056933cc..f337d19677 100755 --- a/src/MCPClient/lib/clientScripts/create_sips_from_trim_transfer_containers.py +++ b/src/archivematica/MCPClient/clientScripts/create_sips_from_trim_transfer_containers.py @@ -25,10 +25,9 @@ django.setup() - -import archivematicaFunctions -import databaseFunctions -from main.models import File +from archivematica.archivematicaCommon import archivematicaFunctions +from archivematica.archivematicaCommon import databaseFunctions +from archivematica.dashboard.main.models import File def call(jobs): diff --git a/src/MCPClient/lib/clientScripts/create_transfer_metadata.py b/src/archivematica/MCPClient/clientScripts/create_transfer_metadata.py similarity index 94% rename from src/MCPClient/lib/clientScripts/create_transfer_metadata.py rename to src/archivematica/MCPClient/clientScripts/create_transfer_metadata.py index 322db136f6..6040469919 100755 --- a/src/MCPClient/lib/clientScripts/create_transfer_metadata.py +++ b/src/archivematica/MCPClient/clientScripts/create_transfer_metadata.py @@ -6,8 +6,8 @@ django.setup() -from client import metrics -from main.models import Transfer +from archivematica.dashboard.main.models import Transfer +from archivematica.MCPClient.client import metrics def fetch_set(sip_uuid): diff --git a/src/MCPClient/lib/clientScripts/create_transfer_mets.py b/src/archivematica/MCPClient/clientScripts/create_transfer_mets.py similarity index 97% rename from src/MCPClient/lib/clientScripts/create_transfer_mets.py rename to src/archivematica/MCPClient/clientScripts/create_transfer_mets.py index 070024dd8c..02adbb416c 100755 --- a/src/MCPClient/lib/clientScripts/create_transfer_mets.py +++ b/src/archivematica/MCPClient/clientScripts/create_transfer_mets.py @@ -28,15 +28,16 @@ django.setup() import metsrw -from archivematicaFunctions import get_dashboard_uuid -from countryCodes import getCodeForCountry -from main.models import Agent -from main.models import Derivation -from main.models import Directory -from main.models import File -from main.models import FPCommandOutput -from main.models import RightsStatement -from main.models import Transfer + +from archivematica.archivematicaCommon.archivematicaFunctions import get_dashboard_uuid +from archivematica.archivematicaCommon.countryCodes import getCodeForCountry +from archivematica.dashboard.main.models import Agent +from archivematica.dashboard.main.models import Derivation +from archivematica.dashboard.main.models import Directory +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import FPCommandOutput +from archivematica.dashboard.main.models import RightsStatement +from archivematica.dashboard.main.models import Transfer PREMIS_META = metsrw.plugins.premisrw.PREMIS_3_0_META FILE_PREMIS_META = PREMIS_META.copy() diff --git a/src/MCPClient/lib/clientScripts/determine_aip_version_key_exit_code.py b/src/archivematica/MCPClient/clientScripts/determine_aip_version_key_exit_code.py similarity index 95% rename from src/MCPClient/lib/clientScripts/determine_aip_version_key_exit_code.py rename to src/archivematica/MCPClient/clientScripts/determine_aip_version_key_exit_code.py index 9e76220a20..0b800e677e 100755 --- a/src/MCPClient/lib/clientScripts/determine_aip_version_key_exit_code.py +++ b/src/archivematica/MCPClient/clientScripts/determine_aip_version_key_exit_code.py @@ -17,13 +17,13 @@ # along with Archivematica. If not, see . import os import sys -from typing import Dict from typing import Optional -import namespaces as ns from lxml import etree -VERSION_MAP: Dict[Optional[str], int] = { +from archivematica.archivematicaCommon import namespaces as ns + +VERSION_MAP: dict[Optional[str], int] = { # Only change exit code if AIP format changes. If unknown, default to latest # version. Currently, all AIPs are the same format so no special cases # required. diff --git a/src/MCPClient/lib/clientScripts/dip_generation_helper.py b/src/archivematica/MCPClient/clientScripts/dip_generation_helper.py similarity index 96% rename from src/MCPClient/lib/clientScripts/dip_generation_helper.py rename to src/archivematica/MCPClient/clientScripts/dip_generation_helper.py index 45474b35b1..7dc4187f48 100755 --- a/src/MCPClient/lib/clientScripts/dip_generation_helper.py +++ b/src/archivematica/MCPClient/clientScripts/dip_generation_helper.py @@ -2,13 +2,14 @@ import argparse import csv -import archivematicaFunctions import django from agentarchives import archivesspace -from custom_handlers import get_script_logger from django.core.exceptions import ValidationError from django.db.models import Q -from main import models + +from archivematica.archivematicaCommon import archivematicaFunctions +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.dashboard.main import models django.setup() from django.db import transaction diff --git a/src/MCPClient/lib/clientScripts/email_fail_report.py b/src/archivematica/MCPClient/clientScripts/email_fail_report.py similarity index 96% rename from src/MCPClient/lib/clientScripts/email_fail_report.py rename to src/archivematica/MCPClient/clientScripts/email_fail_report.py index 4edc21da46..549d40d113 100755 --- a/src/MCPClient/lib/clientScripts/email_fail_report.py +++ b/src/archivematica/MCPClient/clientScripts/email_fail_report.py @@ -18,16 +18,17 @@ import argparse import django -from custom_handlers import get_script_logger from django.conf import settings as mcpclient_settings from django.contrib.auth.models import User from django.core.mail import send_mail from django.db import connection from django.db import transaction -from externals.HTML import HTML from lxml import etree -from main.models import Job -from main.models import Report + +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.archivematicaCommon.externals.HTML import HTML +from archivematica.dashboard.main.models import Job +from archivematica.dashboard.main.models import Report django.setup() diff --git a/src/MCPClient/lib/clientScripts/examine_contents.py b/src/archivematica/MCPClient/clientScripts/examine_contents.py similarity index 93% rename from src/MCPClient/lib/clientScripts/examine_contents.py rename to src/archivematica/MCPClient/clientScripts/examine_contents.py index 0a42f8bdec..5a53b805eb 100755 --- a/src/MCPClient/lib/clientScripts/examine_contents.py +++ b/src/archivematica/MCPClient/clientScripts/examine_contents.py @@ -2,7 +2,7 @@ import multiprocessing import os -from executeOrRunSubProcess import executeOrRun +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun def concurrent_instances(): diff --git a/src/MCPClient/lib/clientScripts/extract_contents.py b/src/archivematica/MCPClient/clientScripts/extract_contents.py similarity index 91% rename from src/MCPClient/lib/clientScripts/extract_contents.py rename to src/archivematica/MCPClient/clientScripts/extract_contents.py index 719b52d2ee..60926f1e65 100755 --- a/src/MCPClient/lib/clientScripts/extract_contents.py +++ b/src/archivematica/MCPClient/clientScripts/extract_contents.py @@ -8,19 +8,19 @@ django.setup() -from archivematicaFunctions import format_subdir_path -from archivematicaFunctions import get_dir_uuids -from custom_handlers import get_script_logger -from databaseFunctions import fileWasRemoved -from executeOrRunSubProcess import executeOrRun -from fileOperations import addFileToTransfer -from fileOperations import updateSizeAndChecksum -from fpr.models import FPCommand -from has_packages import already_extracted -from main.models import Directory -from main.models import File -from main.models import FileFormatVersion -from main.models import Transfer +from archivematica.archivematicaCommon.archivematicaFunctions import format_subdir_path +from archivematica.archivematicaCommon.archivematicaFunctions import get_dir_uuids +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.archivematicaCommon.databaseFunctions import fileWasRemoved +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun +from archivematica.archivematicaCommon.fileOperations import addFileToTransfer +from archivematica.archivematicaCommon.fileOperations import updateSizeAndChecksum +from archivematica.dashboard.fpr.models import FPCommand +from archivematica.dashboard.main.models import Directory +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import FileFormatVersion +from archivematica.dashboard.main.models import Transfer +from archivematica.MCPClient.clientScripts.has_packages import already_extracted logger = get_script_logger("archivematica.mcp.client.extractContents") diff --git a/src/MCPClient/lib/clientScripts/extract_maildir_attachments.py b/src/archivematica/MCPClient/clientScripts/extract_maildir_attachments.py similarity index 97% rename from src/MCPClient/lib/clientScripts/extract_maildir_attachments.py rename to src/archivematica/MCPClient/clientScripts/extract_maildir_attachments.py index 292d42e0f5..32329de65a 100755 --- a/src/MCPClient/lib/clientScripts/extract_maildir_attachments.py +++ b/src/archivematica/MCPClient/clientScripts/extract_maildir_attachments.py @@ -27,10 +27,11 @@ django.setup() from django.core.exceptions import ValidationError from django.db import transaction -from externals.extractMaildirAttachments import parse -from fileOperations import addFileToTransfer -from fileOperations import updateSizeAndChecksum -from main.models import File + +from archivematica.archivematicaCommon.externals.extractMaildirAttachments import parse +from archivematica.archivematicaCommon.fileOperations import addFileToTransfer +from archivematica.archivematicaCommon.fileOperations import updateSizeAndChecksum +from archivematica.dashboard.main.models import File class State: diff --git a/src/MCPClient/lib/clientScripts/extract_zipped_transfer.py b/src/archivematica/MCPClient/clientScripts/extract_zipped_transfer.py similarity index 95% rename from src/MCPClient/lib/clientScripts/extract_zipped_transfer.py rename to src/archivematica/MCPClient/clientScripts/extract_zipped_transfer.py index dc6c0406be..8a0d797baa 100755 --- a/src/MCPClient/lib/clientScripts/extract_zipped_transfer.py +++ b/src/archivematica/MCPClient/clientScripts/extract_zipped_transfer.py @@ -24,9 +24,10 @@ django.setup() from django.db import transaction -from executeOrRunSubProcess import executeOrRun -from fileOperations import get_extract_dir_name -from main.models import Transfer + +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun +from archivematica.archivematicaCommon.fileOperations import get_extract_dir_name +from archivematica.dashboard.main.models import Transfer def extract(job, target, destinationDirectory): diff --git a/src/MCPClient/lib/clientScripts/failed_sip_cleanup.py b/src/archivematica/MCPClient/clientScripts/failed_sip_cleanup.py similarity index 88% rename from src/MCPClient/lib/clientScripts/failed_sip_cleanup.py rename to src/archivematica/MCPClient/clientScripts/failed_sip_cleanup.py index 3caa74a2e7..fc9d546b32 100755 --- a/src/MCPClient/lib/clientScripts/failed_sip_cleanup.py +++ b/src/archivematica/MCPClient/clientScripts/failed_sip_cleanup.py @@ -6,10 +6,9 @@ django.setup() - -import storageService as storage_service -from client import metrics -from main import models +import archivematica.archivematicaCommon.storageService as storage_service +from archivematica.dashboard.main import models +from archivematica.MCPClient.client import metrics REJECTED = "reject" FAILED = "fail" diff --git a/src/MCPClient/lib/clientScripts/failed_transfer_cleanup.py b/src/archivematica/MCPClient/clientScripts/failed_transfer_cleanup.py similarity index 90% rename from src/MCPClient/lib/clientScripts/failed_transfer_cleanup.py rename to src/archivematica/MCPClient/clientScripts/failed_transfer_cleanup.py index b3289090e4..40921a7b41 100755 --- a/src/MCPClient/lib/clientScripts/failed_transfer_cleanup.py +++ b/src/archivematica/MCPClient/clientScripts/failed_transfer_cleanup.py @@ -6,11 +6,12 @@ django.setup() -import storageService as storage_service -from client import metrics from django.db import transaction -from main.models import File -from main.models import Transfer + +import archivematica.archivematicaCommon.storageService as storage_service +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import Transfer +from archivematica.MCPClient.client import metrics REJECTED = "reject" FAILED = "fail" diff --git a/src/MCPClient/lib/clientScripts/file_to_folder.py b/src/archivematica/MCPClient/clientScripts/file_to_folder.py similarity index 97% rename from src/MCPClient/lib/clientScripts/file_to_folder.py rename to src/archivematica/MCPClient/clientScripts/file_to_folder.py index a65204b246..db1cc6ded4 100755 --- a/src/MCPClient/lib/clientScripts/file_to_folder.py +++ b/src/archivematica/MCPClient/clientScripts/file_to_folder.py @@ -6,7 +6,8 @@ django.setup() from django.db import transaction -from main.models import Transfer + +from archivematica.dashboard.main.models import Transfer def main(job, transfer_path, transfer_uuid, shared_path): diff --git a/src/MCPClient/lib/clientScripts/get_aip_storage_locations.py b/src/archivematica/MCPClient/clientScripts/get_aip_storage_locations.py similarity index 88% rename from src/MCPClient/lib/clientScripts/get_aip_storage_locations.py rename to src/archivematica/MCPClient/clientScripts/get_aip_storage_locations.py index 9b6e4f44cd..49227f7d43 100755 --- a/src/MCPClient/lib/clientScripts/get_aip_storage_locations.py +++ b/src/archivematica/MCPClient/clientScripts/get_aip_storage_locations.py @@ -6,8 +6,8 @@ django.setup() -import storageService as storage_service -from custom_handlers import get_script_logger +from archivematica.archivematicaCommon import storageService as storage_service +from archivematica.archivematicaCommon.custom_handlers import get_script_logger logger = get_script_logger("archivematica.mcp.client.get_aip_storage_locations") diff --git a/src/MCPClient/lib/clientScripts/has_packages.py b/src/archivematica/MCPClient/clientScripts/has_packages.py similarity index 82% rename from src/MCPClient/lib/clientScripts/has_packages.py rename to src/archivematica/MCPClient/clientScripts/has_packages.py index 2a073df960..e6222358db 100755 --- a/src/MCPClient/lib/clientScripts/has_packages.py +++ b/src/archivematica/MCPClient/clientScripts/has_packages.py @@ -1,16 +1,15 @@ #!/usr/bin/env python -from typing import List import django django.setup() -from client.job import Job -from fpr.models import FPRule -from main.models import Event -from main.models import File -from main.models import FileFormatVersion -from main.models import Transfer +from archivematica.dashboard.fpr.models import FPRule +from archivematica.dashboard.main.models import Event +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import FileFormatVersion +from archivematica.dashboard.main.models import Transfer +from archivematica.MCPClient.client.job import Job def is_extractable(f: File) -> bool: @@ -63,7 +62,7 @@ def main(job: Job, sip_uuid: str) -> int: return 1 -def call(jobs: List[Job]) -> None: +def call(jobs: list[Job]) -> None: for job in jobs: with job.JobContext(): job.set_status(main(job, job.args[1])) diff --git a/src/MCPClient/lib/clientScripts/identify_dspace_files.py b/src/archivematica/MCPClient/clientScripts/identify_dspace_files.py similarity index 96% rename from src/MCPClient/lib/clientScripts/identify_dspace_files.py rename to src/archivematica/MCPClient/clientScripts/identify_dspace_files.py index bbdf188038..4c02895f66 100755 --- a/src/MCPClient/lib/clientScripts/identify_dspace_files.py +++ b/src/archivematica/MCPClient/clientScripts/identify_dspace_files.py @@ -18,13 +18,14 @@ import os import django -import namespaces from django.db import transaction from lxml import etree +from archivematica.archivematicaCommon import namespaces + django.setup() -from main.models import File +from archivematica.dashboard.main.models import File def identify_dspace_files( diff --git a/src/MCPClient/lib/clientScripts/identify_dspace_mets_files.py b/src/archivematica/MCPClient/clientScripts/identify_dspace_mets_files.py similarity index 95% rename from src/MCPClient/lib/clientScripts/identify_dspace_mets_files.py rename to src/archivematica/MCPClient/clientScripts/identify_dspace_mets_files.py index dbc4aaf50e..4e6ad68004 100755 --- a/src/MCPClient/lib/clientScripts/identify_dspace_mets_files.py +++ b/src/archivematica/MCPClient/clientScripts/identify_dspace_mets_files.py @@ -20,7 +20,8 @@ django.setup() from django.db import transaction -from main.models import File + +from archivematica.dashboard.main.models import File def call(jobs): diff --git a/src/MCPClient/lib/clientScripts/identify_file_format.py b/src/archivematica/MCPClient/clientScripts/identify_file_format.py similarity index 91% rename from src/MCPClient/lib/clientScripts/identify_file_format.py rename to src/archivematica/MCPClient/clientScripts/identify_file_format.py index 24f16e1ac3..43df7f1e2d 100755 --- a/src/MCPClient/lib/clientScripts/identify_file_format.py +++ b/src/archivematica/MCPClient/clientScripts/identify_file_format.py @@ -1,27 +1,28 @@ #!/usr/bin/env python import argparse import dataclasses +import json import multiprocessing import uuid -from typing import List from typing import Optional import django django.setup() -from client.job import Job -from databaseFunctions import insertIntoEvents from django.db import transaction from django.utils import timezone -from executeOrRunSubProcess import executeOrRun -from fpr.models import FormatVersion -from fpr.models import IDCommand -from fpr.models import IDRule -from main.models import File -from main.models import FileFormatVersion -from main.models import FileID -from main.models import UnitVariable + +from archivematica.archivematicaCommon.databaseFunctions import insertIntoEvents +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun +from archivematica.dashboard.fpr.models import FormatVersion +from archivematica.dashboard.fpr.models import IDCommand +from archivematica.dashboard.fpr.models import IDRule +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import FileFormatVersion +from archivematica.dashboard.main.models import FileID +from archivematica.dashboard.main.models import UnitVariable +from archivematica.MCPClient.client.job import Job SUCCESS = 0 ERROR = 255 @@ -57,7 +58,7 @@ def _save_id_preference(file_: File, value: bool) -> None: rd = {"%IDCommand%": str(value)} UnitVariable.objects.create( - unituuid=unit.pk, variable="replacementDict", variablevalue=str(rd) + unituuid=unit.pk, variable="replacementDict", variablevalue=json.dumps(rd) ) @@ -242,7 +243,7 @@ def parse_args(parser: argparse.ArgumentParser, job: Job) -> IdentifyFileFormatA return IdentifyFileFormatArgs(**vars(namespace)) -def call(jobs: List[Job]) -> None: +def call(jobs: list[Job]) -> None: parser = get_parser() with transaction.atomic(): diff --git a/src/MCPClient/lib/clientScripts/index_aip.py b/src/archivematica/MCPClient/clientScripts/index_aip.py similarity index 70% rename from src/MCPClient/lib/clientScripts/index_aip.py rename to src/archivematica/MCPClient/clientScripts/index_aip.py index ebd9c15c37..fad03bd2ea 100755 --- a/src/MCPClient/lib/clientScripts/index_aip.py +++ b/src/archivematica/MCPClient/clientScripts/index_aip.py @@ -1,16 +1,22 @@ #!/usr/bin/env python import os import sys +import time import traceback from glob import glob import django -import elasticSearchFunctions -import identifier_functions -import storageService as storage_service -from custom_handlers import get_script_logger from django.core.exceptions import ValidationError -from main.models import UnitVariable + +from archivematica.archivematicaCommon import identifier_functions +from archivematica.archivematicaCommon import storageService as storage_service +from archivematica.archivematicaCommon import version +from archivematica.archivematicaCommon.aip_mets_parser import AIPMETSParser +from archivematica.archivematicaCommon.archivematicaFunctions import get_dashboard_uuid +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.archivematicaCommon.databaseFunctions import get_sip_identifiers +from archivematica.dashboard.main.models import UnitVariable +from archivematica.search.service import setup_search_service_from_conf django.setup() @@ -53,13 +59,12 @@ def index_aip(job): location_description = storage_service.retrieve_storage_location_description( aip_location, logger ) - elasticSearchFunctions.setup_reading_from_conf(mcpclient_settings) - client = elasticSearchFunctions.get_client() + search_service = setup_search_service_from_conf(mcpclient_settings) aip_info = storage_service.get_file_info(uuid=sip_uuid) job.pyprint("AIP info:", aip_info) aip_info = aip_info[0] mets_staging_path = os.path.join(sip_staging_path, f"METS.{sip_uuid}.xml") - identifiers = get_identifiers(job, sip_staging_path) + identifiers = get_identifiers(job, sip_staging_path) + get_sip_identifiers(sip_uuid) # If this is an AIC, find the number of AIP stored in it and index that aips_in_aic = None if sip_type == "AIC": @@ -77,27 +82,43 @@ def index_aip(job): sip_uuid, "from archival storage", ) - elasticSearchFunctions.delete_aip(client, sip_uuid) - elasticSearchFunctions.delete_aip_files(client, sip_uuid) + search_service.delete_aip(sip_uuid) + search_service.delete_aip_files(sip_uuid) + # Stop if METS file is not at staging path. + if not os.path.exists(mets_staging_path): + error_message = "METS file does not exist at: " + mets_staging_path + logger.error(error_message) + job.pyprint(error_message, file=sys.stderr) + return 1 + + parser = AIPMETSParser(mets_staging_path) + job.pyprint("Indexing AIP and AIP files") + job.pyprint("AIP UUID: " + sip_uuid) + job.pyprint("Indexing AIP files ...") # Even though we treat MODS identifiers as SIP-level, we need to index them # here because the archival storage tab actually searches on the # aips/aipfile index. - ret = elasticSearchFunctions.index_aip_and_files( - client=client, + am_version = version.get_version() + indexed_at = time.time() + ret = search_service.index_aip( uuid=sip_uuid, aip_stored_path=aip_info["current_full_path"], - mets_staging_path=mets_staging_path, + parser=parser, name=sip_name, aip_size=aip_info["size"], - aips_in_aic=aips_in_aic, + am_version=am_version, + indexed_at=indexed_at, identifiers=identifiers, + aips_in_aic=aips_in_aic, encrypted=aip_info["encrypted"], location=location_description, - printfn=job.pyprint, + dashboard_uuid=get_dashboard_uuid() or "", ) if ret == 1: job.pyprint("Error indexing AIP and AIP files", file=sys.stderr) + else: + job.pyprint("Done.") return ret diff --git a/src/MCPClient/lib/clientScripts/is_maildir_aip.py b/src/archivematica/MCPClient/clientScripts/is_maildir_aip.py similarity index 100% rename from src/MCPClient/lib/clientScripts/is_maildir_aip.py rename to src/archivematica/MCPClient/clientScripts/is_maildir_aip.py diff --git a/src/MCPClient/lib/clientScripts/json_metadata_to_csv.py b/src/archivematica/MCPClient/clientScripts/json_metadata_to_csv.py similarity index 100% rename from src/MCPClient/lib/clientScripts/json_metadata_to_csv.py rename to src/archivematica/MCPClient/clientScripts/json_metadata_to_csv.py diff --git a/src/MCPClient/lib/clientScripts/lib/__init__.py b/src/archivematica/MCPClient/clientScripts/lib/__init__.py similarity index 83% rename from src/MCPClient/lib/clientScripts/lib/__init__.py rename to src/archivematica/MCPClient/clientScripts/lib/__init__.py index bd00928a7a..34c0762713 100644 --- a/src/MCPClient/lib/clientScripts/lib/__init__.py +++ b/src/archivematica/MCPClient/clientScripts/lib/__init__.py @@ -1,4 +1,4 @@ -import dicts +from archivematica.archivematicaCommon import dicts def setup_dicts(settings): diff --git a/src/MCPClient/lib/clientScripts/load_dublin_core.py b/src/archivematica/MCPClient/clientScripts/load_dublin_core.py similarity index 96% rename from src/MCPClient/lib/clientScripts/load_dublin_core.py rename to src/archivematica/MCPClient/clientScripts/load_dublin_core.py index 4f70e5004f..65fe947550 100755 --- a/src/MCPClient/lib/clientScripts/load_dublin_core.py +++ b/src/archivematica/MCPClient/clientScripts/load_dublin_core.py @@ -7,7 +7,8 @@ django.setup() from django.db import transaction -from main import models + +from archivematica.dashboard.main import models # This is the UUID of SIP from the `MetadataAppliesToTypes` table INGEST_METADATA_TYPE = "3e48343d-e2d2-4956-aaa3-b54d26eb9761" diff --git a/src/MCPClient/lib/clientScripts/load_labels_from_csv.py b/src/archivematica/MCPClient/clientScripts/load_labels_from_csv.py similarity index 97% rename from src/MCPClient/lib/clientScripts/load_labels_from_csv.py rename to src/archivematica/MCPClient/clientScripts/load_labels_from_csv.py index 472ec2f74f..9a3fe40132 100755 --- a/src/MCPClient/lib/clientScripts/load_labels_from_csv.py +++ b/src/archivematica/MCPClient/clientScripts/load_labels_from_csv.py @@ -22,7 +22,8 @@ django.setup() from django.db import transaction -from main.models import File + +from archivematica.dashboard.main.models import File def call(jobs): diff --git a/src/MCPClient/lib/clientScripts/load_premis_events_from_xml.py b/src/archivematica/MCPClient/clientScripts/load_premis_events_from_xml.py similarity index 99% rename from src/MCPClient/lib/clientScripts/load_premis_events_from_xml.py rename to src/archivematica/MCPClient/clientScripts/load_premis_events_from_xml.py index 4e6242e4a9..1d878c9482 100644 --- a/src/MCPClient/lib/clientScripts/load_premis_events_from_xml.py +++ b/src/archivematica/MCPClient/clientScripts/load_premis_events_from_xml.py @@ -9,9 +9,10 @@ from django.utils import dateparse from django.utils import timezone from lxml import etree -from main.models import Agent -from main.models import Event -from main.models import File + +from archivematica.dashboard.main.models import Agent +from archivematica.dashboard.main.models import Event +from archivematica.dashboard.main.models import File logger = logging.getLogger(__name__) FAILURE = 1 diff --git a/src/MCPClient/lib/clientScripts/manual_normalization_check_for_manual_normalization_directory.py b/src/archivematica/MCPClient/clientScripts/manual_normalization_check_for_manual_normalization_directory.py similarity index 100% rename from src/MCPClient/lib/clientScripts/manual_normalization_check_for_manual_normalization_directory.py rename to src/archivematica/MCPClient/clientScripts/manual_normalization_check_for_manual_normalization_directory.py diff --git a/src/MCPClient/lib/clientScripts/manual_normalization_create_metadata_and_restructure.py b/src/archivematica/MCPClient/clientScripts/manual_normalization_create_metadata_and_restructure.py similarity index 96% rename from src/MCPClient/lib/clientScripts/manual_normalization_create_metadata_and_restructure.py rename to src/archivematica/MCPClient/clientScripts/manual_normalization_create_metadata_and_restructure.py index bbd13ce02e..1632a5acfe 100755 --- a/src/MCPClient/lib/clientScripts/manual_normalization_create_metadata_and_restructure.py +++ b/src/archivematica/MCPClient/clientScripts/manual_normalization_create_metadata_and_restructure.py @@ -19,12 +19,13 @@ django.setup() -import databaseFunctions -import fileOperations from django.core.exceptions import ValidationError from django.db.models import Q -from main.models import Event -from main.models import File + +from archivematica.archivematicaCommon import databaseFunctions +from archivematica.archivematicaCommon import fileOperations +from archivematica.dashboard.main.models import Event +from archivematica.dashboard.main.models import File def main(job): diff --git a/src/MCPClient/lib/clientScripts/manual_normalization_identify_files_included.py b/src/archivematica/MCPClient/clientScripts/manual_normalization_identify_files_included.py similarity index 93% rename from src/MCPClient/lib/clientScripts/manual_normalization_identify_files_included.py rename to src/archivematica/MCPClient/clientScripts/manual_normalization_identify_files_included.py index f88d8af5a4..90fb7067d6 100755 --- a/src/MCPClient/lib/clientScripts/manual_normalization_identify_files_included.py +++ b/src/archivematica/MCPClient/clientScripts/manual_normalization_identify_files_included.py @@ -21,7 +21,7 @@ django.setup() -from fileOperations import updateFileGrpUse +from archivematica.archivematicaCommon.fileOperations import updateFileGrpUse def call(jobs): diff --git a/src/MCPClient/lib/clientScripts/manual_normalization_move_access_files_to_dip.py b/src/archivematica/MCPClient/clientScripts/manual_normalization_move_access_files_to_dip.py similarity index 98% rename from src/MCPClient/lib/clientScripts/manual_normalization_move_access_files_to_dip.py rename to src/archivematica/MCPClient/clientScripts/manual_normalization_move_access_files_to_dip.py index 58cd8d9734..4562cddd6a 100755 --- a/src/MCPClient/lib/clientScripts/manual_normalization_move_access_files_to_dip.py +++ b/src/archivematica/MCPClient/clientScripts/manual_normalization_move_access_files_to_dip.py @@ -16,17 +16,17 @@ # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . import os +from optparse import OptionParser import django from django.db import transaction django.setup() -from optparse import OptionParser - -import fileOperations from django.core.exceptions import ValidationError -from main.models import File + +from archivematica.archivematicaCommon import fileOperations +from archivematica.dashboard.main.models import File def main(job): diff --git a/src/MCPClient/lib/clientScripts/manual_normalization_remove_mn_directories.py b/src/archivematica/MCPClient/clientScripts/manual_normalization_remove_mn_directories.py similarity index 96% rename from src/MCPClient/lib/clientScripts/manual_normalization_remove_mn_directories.py rename to src/archivematica/MCPClient/clientScripts/manual_normalization_remove_mn_directories.py index 7978e8587a..59e2ae8734 100755 --- a/src/MCPClient/lib/clientScripts/manual_normalization_remove_mn_directories.py +++ b/src/archivematica/MCPClient/clientScripts/manual_normalization_remove_mn_directories.py @@ -22,9 +22,10 @@ django.setup() -import databaseFunctions from django.db import transaction -from main.models import File + +from archivematica.archivematicaCommon import databaseFunctions +from archivematica.dashboard.main.models import File def recursivelyRemoveEmptyDirectories(job, dir): diff --git a/src/MCPClient/lib/clientScripts/move_or_merge.py b/src/archivematica/MCPClient/clientScripts/move_or_merge.py similarity index 97% rename from src/MCPClient/lib/clientScripts/move_or_merge.py rename to src/archivematica/MCPClient/clientScripts/move_or_merge.py index 1dccaa4d19..9b1159214b 100755 --- a/src/MCPClient/lib/clientScripts/move_or_merge.py +++ b/src/archivematica/MCPClient/clientScripts/move_or_merge.py @@ -4,7 +4,7 @@ import os import shutil -from custom_handlers import get_script_logger +from archivematica.archivematicaCommon.custom_handlers import get_script_logger logger = get_script_logger("archivematica.mcp.client.move_or_merge") diff --git a/src/MCPClient/lib/clientScripts/move_sip.py b/src/archivematica/MCPClient/clientScripts/move_sip.py similarity index 94% rename from src/MCPClient/lib/clientScripts/move_sip.py rename to src/archivematica/MCPClient/clientScripts/move_sip.py index 37f20a8bf1..be975cc768 100755 --- a/src/MCPClient/lib/clientScripts/move_sip.py +++ b/src/archivematica/MCPClient/clientScripts/move_sip.py @@ -23,8 +23,9 @@ django.setup() from django.db import transaction -from fileOperations import rename -from main.models import SIP + +from archivematica.archivematicaCommon.fileOperations import rename +from archivematica.dashboard.main.models import SIP def updateDB(dst, sip_uuid): diff --git a/src/MCPClient/lib/clientScripts/move_to_backlog.py b/src/archivematica/MCPClient/clientScripts/move_to_backlog.py similarity index 76% rename from src/MCPClient/lib/clientScripts/move_to_backlog.py rename to src/archivematica/MCPClient/clientScripts/move_to_backlog.py index c9af819664..b4138c53b0 100755 --- a/src/MCPClient/lib/clientScripts/move_to_backlog.py +++ b/src/archivematica/MCPClient/clientScripts/move_to_backlog.py @@ -12,26 +12,33 @@ import os import pprint import shutil +import sys import uuid import django django.setup() -import elasticSearchFunctions import metsrw -import storageService as storage_service -from archivematicaFunctions import get_bag_size -from archivematicaFunctions import get_setting from bagit import make_bag -from custom_handlers import get_script_logger -from databaseFunctions import insertIntoEvents from django.conf import settings as mcpclient_settings from django.core.exceptions import ValidationError from django.db import transaction from django.db.models import Q -from main.models import Agent -from main.models import File -from main.models import UnitVariable + +import archivematica.archivematicaCommon.storageService as storage_service +from archivematica.archivematicaCommon.archivematicaFunctions import get_bag_size +from archivematica.archivematicaCommon.archivematicaFunctions import get_dashboard_uuid +from archivematica.archivematicaCommon.archivematicaFunctions import get_setting +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.archivematicaCommon.databaseFunctions import ( + build_transfer_file_index_data, +) +from archivematica.archivematicaCommon.databaseFunctions import get_transfer_details +from archivematica.archivematicaCommon.databaseFunctions import insertIntoEvents +from archivematica.dashboard.main.models import Agent +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import UnitVariable +from archivematica.search.service import setup_search_service_from_conf logger = get_script_logger("archivematica.mcp.client.move_to_backlog") @@ -73,13 +80,36 @@ def _create_file( def _index_transfer(job, transfer_id, transfer_path, size): """Index the transfer and its files in Elasticsearch.""" if "transfers" not in mcpclient_settings.SEARCH_ENABLED: - logger.info("Skipping indexing:" " Transfers indexing is currently disabled.") + logger.info("Skipping indexing: Transfers indexing is currently disabled.") return - elasticSearchFunctions.setup_reading_from_conf(mcpclient_settings) - client = elasticSearchFunctions.get_client() - elasticSearchFunctions.index_transfer_and_files( - client, transfer_id, transfer_path, size, printfn=job.pyprint + + # Stop if Transfer does not exist + if not os.path.exists(transfer_path): + error_message = "Transfer does not exist at: " + transfer_path + logger.error(error_message) + job.pyprint(error_message, file=sys.stderr) + return 1 + + job.pyprint("Transfer UUID: " + transfer_id) + job.pyprint("Indexing Transfer files ...") + + transfer_name, accession_id, ingest_date = get_transfer_details(transfer_id) + transfer_index_data = build_transfer_file_index_data( + transfer_path, transfer_name, transfer_id, job.pyprint + ) + search_service = setup_search_service_from_conf(mcpclient_settings) + search_service.index_transfer( + uuid=transfer_id, + path=transfer_path, + size=size, + transfer_index_data=transfer_index_data, + printfn=job.pyprint, + dashboard_uuid=get_dashboard_uuid() or "", + transfer_name=transfer_name, + accession_id=accession_id, + ingest_date=ingest_date, ) + job.pyprint("Done.") def _create_bag(transfer_id, transfer_path): @@ -196,7 +226,7 @@ def main(job, transfer_id, transfer_path, created_at): relative_transfer_path = transfer_path.replace(shared_path, "") # TODO this should use the same value as - # dashboard/src/components/filesystem_ajax/views.py DEFAULT_BACKLOG_PATH + # dashboard/components/filesystem_ajax/views.py DEFAULT_BACKLOG_PATH transfer_name = os.path.basename(transfer_path.rstrip("/")) backlog_path = os.path.join("originals", transfer_name) diff --git a/src/MCPClient/lib/clientScripts/move_transfer.py b/src/archivematica/MCPClient/clientScripts/move_transfer.py similarity index 93% rename from src/MCPClient/lib/clientScripts/move_transfer.py rename to src/archivematica/MCPClient/clientScripts/move_transfer.py index 6271499da8..098931f5e6 100755 --- a/src/MCPClient/lib/clientScripts/move_transfer.py +++ b/src/archivematica/MCPClient/clientScripts/move_transfer.py @@ -21,8 +21,9 @@ django.setup() from django.db import transaction -from fileOperations import rename -from main.models import Transfer + +from archivematica.archivematicaCommon.fileOperations import rename +from archivematica.dashboard.main.models import Transfer def updateDB(dst, transferUUID): diff --git a/src/MCPClient/lib/clientScripts/normalize.py b/src/archivematica/MCPClient/clientScripts/normalize.py similarity index 97% rename from src/MCPClient/lib/clientScripts/normalize.py rename to src/archivematica/MCPClient/clientScripts/normalize.py index 6e240b6b00..0f621eab7c 100755 --- a/src/MCPClient/lib/clientScripts/normalize.py +++ b/src/archivematica/MCPClient/clientScripts/normalize.py @@ -9,27 +9,28 @@ import traceback import uuid from typing import Callable -from typing import List from typing import Optional import django -import transcoder from django.utils import timezone +from archivematica.MCPClient.clientScripts import transcoder + django.setup() -import databaseFunctions -import fileOperations -from client.job import Job -from dicts import ReplacementDict from django.conf import settings as mcpclient_settings from django.core.exceptions import ValidationError from django.db import transaction -from fpr.models import FPRule -from lib import setup_dicts -from main.models import Derivation -from main.models import File -from main.models import FileFormatVersion -from main.models import FileID + +from archivematica.archivematicaCommon import databaseFunctions +from archivematica.archivematicaCommon import fileOperations +from archivematica.archivematicaCommon.dicts import ReplacementDict +from archivematica.dashboard.fpr.models import FPRule +from archivematica.dashboard.main.models import Derivation +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import FileFormatVersion +from archivematica.dashboard.main.models import FileID +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts.lib import setup_dicts # Return codes SUCCESS = 0 @@ -608,7 +609,7 @@ def parse_args(parser: argparse.ArgumentParser, job: Job) -> NormalizeArgs: return NormalizeArgs(**vars(namespace)) -def call(jobs: List[Job]) -> None: +def call(jobs: list[Job]) -> None: parser = get_parser() with transaction.atomic(): diff --git a/src/MCPClient/lib/clientScripts/normalize_report.py b/src/archivematica/MCPClient/clientScripts/normalize_report.py similarity index 94% rename from src/MCPClient/lib/clientScripts/normalize_report.py rename to src/archivematica/MCPClient/clientScripts/normalize_report.py index 331c814c13..28a4108926 100755 --- a/src/MCPClient/lib/clientScripts/normalize_report.py +++ b/src/archivematica/MCPClient/clientScripts/normalize_report.py @@ -3,9 +3,7 @@ import os.path from argparse import ArgumentParser -import components.helpers as helpers import django -from custom_handlers import get_script_logger from django.conf import settings as mcpclient_settings from django.contrib.auth.models import User from django.core.exceptions import ValidationError @@ -13,11 +11,14 @@ from django.db import transaction from django.template import Context from django.template import Template -from main.models import SIP -from main.models import File -from main.models import Job -from main.models import Report -from main.models import Task + +import archivematica.dashboard.components.helpers as helpers +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import Job +from archivematica.dashboard.main.models import Report +from archivematica.dashboard.main.models import Task django.setup() diff --git a/src/MCPClient/lib/clientScripts/parse_dataverse_mets.py b/src/archivematica/MCPClient/clientScripts/parse_dataverse_mets.py similarity index 95% rename from src/MCPClient/lib/clientScripts/parse_dataverse_mets.py rename to src/archivematica/MCPClient/clientScripts/parse_dataverse_mets.py index 79142b6260..da61f96bec 100644 --- a/src/MCPClient/lib/clientScripts/parse_dataverse_mets.py +++ b/src/archivematica/MCPClient/clientScripts/parse_dataverse_mets.py @@ -11,14 +11,15 @@ import django django.setup() -import databaseFunctions import metsrw -from archivematicaFunctions import get_file_checksum -from custom_handlers import get_script_logger from django.core.exceptions import ValidationError from django.utils import timezone -from main.models import Agent -from main.models import File + +from archivematica.archivematicaCommon import databaseFunctions +from archivematica.archivematicaCommon.archivematicaFunctions import get_file_checksum +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.dashboard.main.models import Agent +from archivematica.dashboard.main.models import File logger = get_script_logger("archivematica.mcp.client.parse_dataverse_mets") transfer_objects_directory = "%transferDirectory%objects" @@ -75,7 +76,7 @@ def get_db_objects(job, mets, transfer_uuid): continue except (File.DoesNotExist, ValidationError): logger.debug( - "Could not find file type: '%s' in the database: %s with " "path: %s", + "Could not find file type: '%s' in the database: %s with path: %s", entry.type, entry.path, item_path, @@ -252,7 +253,7 @@ def verify_checksum( checksumtype = checksumtype.lower() generated_checksum = get_file_checksum(path, checksumtype) - event_detail = 'program="python"; ' f'module="hashlib.{checksumtype}()"' + event_detail = f'program="python"; module="hashlib.{checksumtype}()"' if checksum != generated_checksum: job.pyprint("Checksum failed") event_outcome = "Fail" @@ -299,7 +300,7 @@ def init_parse_dataverse_mets(job): transfer_dir = job.args[1] transfer_uuid = job.args[2] logger.info( - "Parse Dataverse METS with dir: '%s' and transfer " "uuid: %s", + "Parse Dataverse METS with dir: '%s' and transfer uuid: %s", transfer_dir, transfer_uuid, ) diff --git a/src/MCPClient/lib/clientScripts/parse_external_mets.py b/src/archivematica/MCPClient/clientScripts/parse_external_mets.py similarity index 87% rename from src/MCPClient/lib/clientScripts/parse_external_mets.py rename to src/archivematica/MCPClient/clientScripts/parse_external_mets.py index 63d2fce5a4..db5d772161 100755 --- a/src/MCPClient/lib/clientScripts/parse_external_mets.py +++ b/src/archivematica/MCPClient/clientScripts/parse_external_mets.py @@ -2,11 +2,12 @@ import argparse import os -import parse_mets_to_db -from archivematicaFunctions import find_mets_file -from custom_handlers import get_script_logger from lxml import etree +from archivematica.archivematicaCommon.archivematicaFunctions import find_mets_file +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.MCPClient.clientScripts import parse_mets_to_db + logger = get_script_logger("archivematica.mcp.client.parse_external_mets") diff --git a/src/MCPClient/lib/clientScripts/parse_mets_to_db.py b/src/archivematica/MCPClient/clientScripts/parse_mets_to_db.py similarity index 98% rename from src/MCPClient/lib/clientScripts/parse_mets_to_db.py rename to src/archivematica/MCPClient/clientScripts/parse_mets_to_db.py index 8f4b7ddcc2..e7e4ac1b7b 100755 --- a/src/MCPClient/lib/clientScripts/parse_mets_to_db.py +++ b/src/archivematica/MCPClient/clientScripts/parse_mets_to_db.py @@ -10,11 +10,11 @@ django.setup() -import databaseFunctions -import fileOperations -import namespaces as ns -from fpr import models as fpr_models -from main import models +from archivematica.archivematicaCommon import databaseFunctions +from archivematica.archivematicaCommon import fileOperations +from archivematica.archivematicaCommon import namespaces as ns +from archivematica.dashboard.fpr import models as fpr_models +from archivematica.dashboard.main import models MD_TYPE_SIP_ID = "3e48343d-e2d2-4956-aaa3-b54d26eb9761" diff --git a/src/MCPClient/lib/clientScripts/pid_declaration.py b/src/archivematica/MCPClient/clientScripts/pid_declaration.py similarity index 97% rename from src/MCPClient/lib/clientScripts/pid_declaration.py rename to src/archivematica/MCPClient/clientScripts/pid_declaration.py index 38d93a0102..65b31faedf 100644 --- a/src/MCPClient/lib/clientScripts/pid_declaration.py +++ b/src/archivematica/MCPClient/clientScripts/pid_declaration.py @@ -16,11 +16,12 @@ django.setup() -from change_names import change_name from django.core.exceptions import ValidationError -from main.models import SIP -from main.models import Directory -from main.models import File + +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import Directory +from archivematica.dashboard.main.models import File +from archivematica.MCPClient.clientScripts.change_names import change_name class DeclarePIDsException(Exception): diff --git a/src/MCPClient/lib/clientScripts/policy_check.py b/src/archivematica/MCPClient/clientScripts/policy_check.py similarity index 94% rename from src/MCPClient/lib/clientScripts/policy_check.py rename to src/archivematica/MCPClient/clientScripts/policy_check.py index b3a6374276..097a2281b6 100755 --- a/src/MCPClient/lib/clientScripts/policy_check.py +++ b/src/archivematica/MCPClient/clientScripts/policy_check.py @@ -13,30 +13,29 @@ import json import os +from typing import Optional import django -from custom_handlers import get_script_logger + +from archivematica.archivematicaCommon.custom_handlers import get_script_logger django.setup() -from typing import Dict -from typing import List -from typing import Optional -from typing import Tuple -import databaseFunctions -from client.job import Job -from dicts import replace_string_values from django.conf import settings as mcpclient_settings from django.core.exceptions import ValidationError from django.db import transaction -from executeOrRunSubProcess import executeOrRun -from fpr.models import FormatVersion -from fpr.models import FPRule -from lib import setup_dicts -from main.models import SIP -from main.models import Derivation -from main.models import File -from main.models import Transfer + +from archivematica.archivematicaCommon import databaseFunctions +from archivematica.archivematicaCommon.dicts import replace_string_values +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun +from archivematica.dashboard.fpr.models import FormatVersion +from archivematica.dashboard.fpr.models import FPRule +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import Derivation +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import Transfer +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts.lib import setup_dicts # Note that linkTaskManagerFiles.py will take the highest exit code it has seen # from all tasks and will use that as the exit code of the job as a whole. @@ -117,8 +116,7 @@ def check(self) -> int: rules = self._get_rules() if not rules: self.job.pyprint( - "Not performing a policy check because there are no relevant" - " FPR rules" + "Not performing a policy check because there are no relevant FPR rules" ) return NOT_APPLICABLE_CODE rule_outputs = [] @@ -315,7 +313,7 @@ def _execute_rule_command(self, rule: FPRule) -> str: ) return result - def _get_command_to_execute(self, rule: FPRule) -> Tuple[str, List[str]]: + def _get_command_to_execute(self, rule: FPRule) -> tuple[str, list[str]]: """Return a 2-tuple consisting of a) the FPR rule ``rule``'s command and b) a list of arguments to pass to it. """ @@ -332,14 +330,14 @@ def _get_command_to_execute(self, rule: FPRule) -> Tuple[str, List[str]]: else: return (rule.command.command, [self.file_path, self.policies_dir]) - def _save_to_logs_dir(self, output: Dict[str, str]) -> None: + def _save_to_logs_dir(self, output: dict[str, str]) -> None: """Save the MediaConch policy file as well as the raw MediaConch stdout for the target file to the logs/ directory of the SIP. """ self._save_stdout_to_logs_dir(output) self._save_policy_to_subm_doc_dir(output) - def _save_stdout_to_logs_dir(self, output: Dict[str, str]) -> None: + def _save_stdout_to_logs_dir(self, output: dict[str, str]) -> None: """Save the output of running MediaConch's policy checker against the input file to logs/policyChecks//.xml in the SIP. @@ -356,7 +354,7 @@ def _save_stdout_to_logs_dir(self, output: Dict[str, str]) -> None: with open(stdout_path, "w") as f: f.write(mc_stdout) - def _save_policy_to_subm_doc_dir(self, output: Dict[str, str]) -> None: + def _save_policy_to_subm_doc_dir(self, output: dict[str, str]) -> None: """Save the policy file text in ``output['policy']`` to a file named ``output['policyFileName']`` in metadata/submissionDocumentation/policies/ in the SIP, if it is not @@ -464,7 +462,7 @@ def sip_policy_checks_dir(self) -> Optional[str]: return self._sip_policy_checks_dir -def call(jobs: List[Job]) -> None: +def call(jobs: list[Job]) -> None: with transaction.atomic(): for job in jobs: with job.JobContext(logger=logger): diff --git a/src/MCPClient/lib/clientScripts/post_store_aip_hook.py b/src/archivematica/MCPClient/clientScripts/post_store_aip_hook.py similarity index 81% rename from src/MCPClient/lib/clientScripts/post_store_aip_hook.py rename to src/archivematica/MCPClient/clientScripts/post_store_aip_hook.py index 20c032f32c..52ae839b70 100755 --- a/src/MCPClient/lib/clientScripts/post_store_aip_hook.py +++ b/src/archivematica/MCPClient/clientScripts/post_store_aip_hook.py @@ -8,14 +8,18 @@ import requests django.setup() -import elasticSearchFunctions -import storageService as storage_service -from archivematicaFunctions import find_transfer_path_from_ingest -from custom_handlers import get_script_logger from django.conf import settings as mcpclient_settings from django.core.exceptions import ValidationError from django.db import transaction -from main import models +from django.db.models import Q + +from archivematica.archivematicaCommon import storageService as storage_service +from archivematica.archivematicaCommon.archivematicaFunctions import ( + find_transfer_path_from_ingest, +) +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.dashboard.main import models +from archivematica.search.service import setup_search_service_from_conf logger = get_script_logger("archivematica.mcp.client.post_store_aip_hook") @@ -24,6 +28,22 @@ ERROR = 2 +def find_transfer_ids_by_unit_uuid(unit_uuid: str) -> set[str]: + """Find transfer IDs by looking up files associated with a unit UUID. + + Searches for files that belong to either the given transfer UUID or SIP UUID, + then returns the transfer UUIDs of those files. This is useful for finding + all transfers associated with a SIP during storage operations. + + :param str unit_uuid: UUID of a transfer or SIP to search for + :return: Set of transfer UUIDs associated with files in the unit + """ + condition = Q(transfer_id=unit_uuid) | Q(sip_id=unit_uuid) + return { + f[0] for f in models.File.objects.filter(condition).values_list("transfer_id") + } + + def delete_transfer_directory(job, sip_uuid): """Delete the transfer directory that sourced this SIP. @@ -136,10 +156,10 @@ def post_store_hook(job, sip_uuid): """ update_es = "transfers" in mcpclient_settings.SEARCH_ENABLED if update_es: - elasticSearchFunctions.setup_reading_from_conf(mcpclient_settings) - client = elasticSearchFunctions.get_client() + search_service = setup_search_service_from_conf(mcpclient_settings) else: logger.info("Skipping indexing: Transfers indexing is currently disabled.") + search_service = None # SIP ARRANGEMENT @@ -157,9 +177,9 @@ def post_store_hook(job, sip_uuid): for transfer_uuid in transfer_uuids: job.pyprint("Checking if transfer", transfer_uuid, "is fully stored...") arranged_uuids = set( - models.SIPArrange.objects.filter(transfer_uuid=transfer_uuid) - .filter(aip_created=True) - .values_list("file_uuid", flat=True) + models.SIPArrange.objects.filter( + transfer_uuid=transfer_uuid, aip_created=True + ).values_list("file_uuid", flat=True) ) backlog_uuids = set( models.File.objects.filter(transfer=transfer_uuid).values_list( @@ -175,19 +195,20 @@ def post_store_hook(job, sip_uuid): ) # Submit delete req to SS (not actually delete), remove from ES storage_service.request_file_deletion( - uuid=transfer_uuid, + uuid=str(transfer_uuid), user_id=0, user_email="archivematica system", reason_for_deletion="All files in Transfer are now in AIPs.", ) if update_es: - elasticSearchFunctions.remove_sip_transfer_files(client, transfer_uuid) + transfer_ids = find_transfer_ids_by_unit_uuid(transfer_uuid) + search_service.delete_transfer_files(transfer_ids) # DSPACE HANDLE TO ARCHIVESSPACE dspace_handle_to_archivesspace(job, sip_uuid) # POST-STORE CALLBACK - storage_service.post_store_aip_callback(sip_uuid) + storage_service.post_store_aip_callback(str(sip_uuid)) # When not using SIP arrangement, we perform best-effort deletion of the # original transfer directory under currentlyProcessing. diff --git a/src/MCPClient/lib/clientScripts/remove_directories.py b/src/archivematica/MCPClient/clientScripts/remove_directories.py similarity index 100% rename from src/MCPClient/lib/clientScripts/remove_directories.py rename to src/archivematica/MCPClient/clientScripts/remove_directories.py diff --git a/src/MCPClient/lib/clientScripts/remove_files_without_premis_metadata.py b/src/archivematica/MCPClient/clientScripts/remove_files_without_premis_metadata.py similarity index 97% rename from src/MCPClient/lib/clientScripts/remove_files_without_premis_metadata.py rename to src/archivematica/MCPClient/clientScripts/remove_files_without_premis_metadata.py index 9e782e6d1b..7b14db5625 100755 --- a/src/MCPClient/lib/clientScripts/remove_files_without_premis_metadata.py +++ b/src/archivematica/MCPClient/clientScripts/remove_files_without_premis_metadata.py @@ -21,7 +21,7 @@ import django django.setup() -from main.models import File +from archivematica.dashboard.main.models import File def verifyFileUUID(job, fileUUID, filePath, sipDirectory): diff --git a/src/MCPClient/lib/clientScripts/remove_hidden_files_and_directories.py b/src/archivematica/MCPClient/clientScripts/remove_hidden_files_and_directories.py similarity index 100% rename from src/MCPClient/lib/clientScripts/remove_hidden_files_and_directories.py rename to src/archivematica/MCPClient/clientScripts/remove_hidden_files_and_directories.py diff --git a/src/MCPClient/lib/clientScripts/remove_unneeded_files.py b/src/archivematica/MCPClient/clientScripts/remove_unneeded_files.py similarity index 94% rename from src/MCPClient/lib/clientScripts/remove_unneeded_files.py rename to src/archivematica/MCPClient/clientScripts/remove_unneeded_files.py index 93e6df09a5..dc769ebfad 100755 --- a/src/MCPClient/lib/clientScripts/remove_unneeded_files.py +++ b/src/archivematica/MCPClient/clientScripts/remove_unneeded_files.py @@ -15,9 +15,10 @@ django.setup() -from databaseFunctions import fileWasRemoved from django.conf import settings as mcpclient_settings +from archivematica.archivematicaCommon.databaseFunctions import fileWasRemoved + def remove_file(job, target_file, file_uuid): removableFiles = {e.strip() for e in mcpclient_settings.REMOVABLE_FILES.split(",")} diff --git a/src/MCPClient/lib/clientScripts/restructure_bag_aip_to_sip.py b/src/archivematica/MCPClient/clientScripts/restructure_bag_aip_to_sip.py similarity index 96% rename from src/MCPClient/lib/clientScripts/restructure_bag_aip_to_sip.py rename to src/archivematica/MCPClient/clientScripts/restructure_bag_aip_to_sip.py index 4be52e82fb..d09d3810e4 100755 --- a/src/MCPClient/lib/clientScripts/restructure_bag_aip_to_sip.py +++ b/src/archivematica/MCPClient/clientScripts/restructure_bag_aip_to_sip.py @@ -18,8 +18,8 @@ import os import shutil -import archivematicaFunctions -from custom_handlers import get_script_logger +from archivematica.archivematicaCommon import archivematicaFunctions +from archivematica.archivematicaCommon.custom_handlers import get_script_logger logger = get_script_logger("archivematica.mcp.client.restructureBagAIPToSIP") diff --git a/src/MCPClient/lib/clientScripts/restructure_dip_for_content_dm_upload.py b/src/archivematica/MCPClient/clientScripts/restructure_dip_for_content_dm_upload.py similarity index 98% rename from src/MCPClient/lib/clientScripts/restructure_dip_for_content_dm_upload.py rename to src/archivematica/MCPClient/clientScripts/restructure_dip_for_content_dm_upload.py index 0f38d64149..5a300df1ee 100755 --- a/src/MCPClient/lib/clientScripts/restructure_dip_for_content_dm_upload.py +++ b/src/archivematica/MCPClient/clientScripts/restructure_dip_for_content_dm_upload.py @@ -22,10 +22,11 @@ import re import sys -import archivematicaFunctions -import namespaces as ns from lxml import etree +from archivematica.archivematicaCommon import archivematicaFunctions +from archivematica.archivematicaCommon import namespaces as ns + def parseDmdSec(dmdSec, label="[Placeholder title]"): """ diff --git a/src/MCPClient/lib/clientScripts/restructure_for_compliance.py b/src/archivematica/MCPClient/clientScripts/restructure_for_compliance.py similarity index 90% rename from src/MCPClient/lib/clientScripts/restructure_for_compliance.py rename to src/archivematica/MCPClient/clientScripts/restructure_for_compliance.py index b02a42c3c3..ef5da77305 100755 --- a/src/MCPClient/lib/clientScripts/restructure_for_compliance.py +++ b/src/archivematica/MCPClient/clientScripts/restructure_for_compliance.py @@ -22,16 +22,23 @@ import django django.setup() -import bag -from archivematicaFunctions import OPTIONAL_FILES -from archivematicaFunctions import REQUIRED_DIRECTORIES -from archivematicaFunctions import create_structured_directory -from archivematicaFunctions import reconstruct_empty_directories -from custom_handlers import get_script_logger from django.core.exceptions import ValidationError from django.db import transaction -from main.models import SIP -from main.models import Transfer + +from archivematica.archivematicaCommon import bag +from archivematica.archivematicaCommon.archivematicaFunctions import OPTIONAL_FILES +from archivematica.archivematicaCommon.archivematicaFunctions import ( + REQUIRED_DIRECTORIES, +) +from archivematica.archivematicaCommon.archivematicaFunctions import ( + create_structured_directory, +) +from archivematica.archivematicaCommon.archivematicaFunctions import ( + reconstruct_empty_directories, +) +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import Transfer logger = get_script_logger("archivematica.mcp.client.restructureForCompliance") diff --git a/src/MCPClient/lib/clientScripts/restructure_for_compliance_maildir.py b/src/archivematica/MCPClient/clientScripts/restructure_for_compliance_maildir.py similarity index 100% rename from src/MCPClient/lib/clientScripts/restructure_for_compliance_maildir.py rename to src/archivematica/MCPClient/clientScripts/restructure_for_compliance_maildir.py diff --git a/src/MCPClient/lib/clientScripts/restructure_for_compliance_sip.py b/src/archivematica/MCPClient/clientScripts/restructure_for_compliance_sip.py similarity index 92% rename from src/MCPClient/lib/clientScripts/restructure_for_compliance_sip.py rename to src/archivematica/MCPClient/clientScripts/restructure_for_compliance_sip.py index 0236a3d81e..2a654fbc60 100755 --- a/src/MCPClient/lib/clientScripts/restructure_for_compliance_sip.py +++ b/src/archivematica/MCPClient/clientScripts/restructure_for_compliance_sip.py @@ -7,12 +7,15 @@ django.setup() -import archivematicaFunctions -import fileOperations -from archivematicaFunctions import OPTIONAL_FILES -from archivematicaFunctions import REQUIRED_DIRECTORIES from django.db import transaction +from archivematica.archivematicaCommon import archivematicaFunctions +from archivematica.archivematicaCommon import fileOperations +from archivematica.archivematicaCommon.archivematicaFunctions import OPTIONAL_FILES +from archivematica.archivematicaCommon.archivematicaFunctions import ( + REQUIRED_DIRECTORIES, +) + def restructureForComplianceFileUUIDsAssigned( job, diff --git a/src/MCPClient/lib/clientScripts/retry_normalize_remove_normalized.py b/src/archivematica/MCPClient/clientScripts/retry_normalize_remove_normalized.py similarity index 96% rename from src/MCPClient/lib/clientScripts/retry_normalize_remove_normalized.py rename to src/archivematica/MCPClient/clientScripts/retry_normalize_remove_normalized.py index 3ba38b1f27..7eb85b2974 100755 --- a/src/MCPClient/lib/clientScripts/retry_normalize_remove_normalized.py +++ b/src/archivematica/MCPClient/clientScripts/retry_normalize_remove_normalized.py @@ -27,9 +27,10 @@ from django.db import transaction from django.utils import timezone -from main.models import Derivation -from main.models import Event -from main.models import File + +from archivematica.dashboard.main.models import Derivation +from archivematica.dashboard.main.models import Event +from archivematica.dashboard.main.models import File def removeDIP(job, SIPDirectory, SIPUUID): diff --git a/src/MCPClient/lib/clientScripts/rights_from_csv.py b/src/archivematica/MCPClient/clientScripts/rights_from_csv.py similarity index 99% rename from src/MCPClient/lib/clientScripts/rights_from_csv.py rename to src/archivematica/MCPClient/clientScripts/rights_from_csv.py index cfc9587f50..d66b5aba88 100755 --- a/src/MCPClient/lib/clientScripts/rights_from_csv.py +++ b/src/archivematica/MCPClient/clientScripts/rights_from_csv.py @@ -22,7 +22,8 @@ django.setup() from django.db import transaction -from main import models + +from archivematica.dashboard.main import models class RightsRowException(Exception): diff --git a/src/MCPClient/lib/clientScripts/save_dublin_core.py b/src/archivematica/MCPClient/clientScripts/save_dublin_core.py similarity index 96% rename from src/MCPClient/lib/clientScripts/save_dublin_core.py rename to src/archivematica/MCPClient/clientScripts/save_dublin_core.py index 7915b1512e..efdbe948e1 100755 --- a/src/MCPClient/lib/clientScripts/save_dublin_core.py +++ b/src/archivematica/MCPClient/clientScripts/save_dublin_core.py @@ -6,7 +6,8 @@ django.setup() from django.db import transaction -from main import models + +from archivematica.dashboard.main import models FIELDS = ( "title", diff --git a/src/MCPClient/lib/clientScripts/set_maildir_file_grp_use_and_file_ids.py b/src/archivematica/MCPClient/clientScripts/set_maildir_file_grp_use_and_file_ids.py similarity index 93% rename from src/MCPClient/lib/clientScripts/set_maildir_file_grp_use_and_file_ids.py rename to src/archivematica/MCPClient/clientScripts/set_maildir_file_grp_use_and_file_ids.py index 2a19364ace..037720f0b7 100755 --- a/src/MCPClient/lib/clientScripts/set_maildir_file_grp_use_and_file_ids.py +++ b/src/archivematica/MCPClient/clientScripts/set_maildir_file_grp_use_and_file_ids.py @@ -18,14 +18,16 @@ import os import django -from custom_handlers import get_script_logger + +from archivematica.archivematicaCommon.custom_handlers import get_script_logger django.setup() from django.db import connection from django.db import transaction -from main.models import File -from main.models import FileFormatVersion -from main.models import FileID + +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import FileFormatVersion +from archivematica.dashboard.main.models import FileID logger = get_script_logger("archivematica.mcp.client.setMaildirFileGrpUseAndFileIDs") diff --git a/src/MCPClient/lib/clientScripts/set_transfer_type.py b/src/archivematica/MCPClient/clientScripts/set_transfer_type.py similarity index 87% rename from src/MCPClient/lib/clientScripts/set_transfer_type.py rename to src/archivematica/MCPClient/clientScripts/set_transfer_type.py index 036f6c8c52..766b95012d 100755 --- a/src/MCPClient/lib/clientScripts/set_transfer_type.py +++ b/src/archivematica/MCPClient/clientScripts/set_transfer_type.py @@ -19,10 +19,11 @@ django.setup() -from client import metrics -from custom_handlers import get_script_logger from django.db import transaction -from main.models import Transfer + +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.dashboard.main.models import Transfer +from archivematica.MCPClient.client import metrics logger = get_script_logger("archivematica.mcp.client.setTransferType") diff --git a/src/MCPClient/lib/clientScripts/store_aip.py b/src/archivematica/MCPClient/clientScripts/store_aip.py similarity index 95% rename from src/MCPClient/lib/clientScripts/store_aip.py rename to src/archivematica/MCPClient/clientScripts/store_aip.py index 5ff1413f13..9df3ead13e 100755 --- a/src/MCPClient/lib/clientScripts/store_aip.py +++ b/src/archivematica/MCPClient/clientScripts/store_aip.py @@ -23,18 +23,19 @@ import django django.setup() -import storageService as storage_service -from archivematicaFunctions import escape -from client import metrics -from custom_handlers import get_script_logger from django.core.exceptions import ValidationError from django.db import transaction -from main.models import Agent -from main.models import DublinCore -from main.models import Event -from main.models import UnitVariable from metsrw.plugins import premisrw +from archivematica.archivematicaCommon import storageService as storage_service +from archivematica.archivematicaCommon.archivematicaFunctions import escape +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.dashboard.main.models import Agent +from archivematica.dashboard.main.models import DublinCore +from archivematica.dashboard.main.models import Event +from archivematica.dashboard.main.models import UnitVariable +from archivematica.MCPClient.client import metrics + logger = get_script_logger("archivematica.mcp.client.storeAIP") diff --git a/src/MCPClient/lib/clientScripts/store_file_modification_dates.py b/src/archivematica/MCPClient/clientScripts/store_file_modification_dates.py similarity index 95% rename from src/MCPClient/lib/clientScripts/store_file_modification_dates.py rename to src/archivematica/MCPClient/clientScripts/store_file_modification_dates.py index 0dab5ad6d9..b48da71c2d 100755 --- a/src/MCPClient/lib/clientScripts/store_file_modification_dates.py +++ b/src/archivematica/MCPClient/clientScripts/store_file_modification_dates.py @@ -21,10 +21,11 @@ import django django.setup() -from custom_handlers import get_script_logger from django.db import transaction from django.utils.timezone import get_current_timezone -from main import models + +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.dashboard.main import models logger = get_script_logger("archivematica.mcp.client.storeFileModificationDates") diff --git a/src/MCPClient/lib/clientScripts/transcoder.py b/src/archivematica/MCPClient/clientScripts/transcoder.py similarity index 98% rename from src/MCPClient/lib/clientScripts/transcoder.py rename to src/archivematica/MCPClient/clientScripts/transcoder.py index 72822f90f8..3e3fb63dbd 100755 --- a/src/MCPClient/lib/clientScripts/transcoder.py +++ b/src/archivematica/MCPClient/clientScripts/transcoder.py @@ -16,7 +16,8 @@ # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . from django.db.models import F -from executeOrRunSubProcess import executeOrRun + +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun class Command: diff --git a/src/MCPClient/lib/clientScripts/transcribe_file.py b/src/archivematica/MCPClient/clientScripts/transcribe_file.py similarity index 82% rename from src/MCPClient/lib/clientScripts/transcribe_file.py rename to src/archivematica/MCPClient/clientScripts/transcribe_file.py index 906db4061c..fadb211032 100755 --- a/src/MCPClient/lib/clientScripts/transcribe_file.py +++ b/src/archivematica/MCPClient/clientScripts/transcribe_file.py @@ -4,29 +4,28 @@ import multiprocessing import os import uuid -from typing import List +from collections.abc import Sequence from typing import Optional -from typing import Sequence -from typing import Tuple import django django.setup() -import databaseFunctions -import fileOperations -from client.job import Job -from dicts import ReplacementDict from django.conf import settings as mcpclient_settings from django.core.exceptions import ValidationError from django.db import transaction from django.utils import timezone -from executeOrRunSubProcess import executeOrRun -from fpr.models import FPRule -from lib import setup_dicts -from main.models import Derivation -from main.models import File -from main.models import FileFormatVersion + +from archivematica.archivematicaCommon import databaseFunctions +from archivematica.archivematicaCommon import fileOperations +from archivematica.archivematicaCommon.dicts import ReplacementDict +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun +from archivematica.dashboard.fpr.models import FPRule +from archivematica.dashboard.main.models import Derivation +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import FileFormatVersion +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts.lib import setup_dicts @dataclasses.dataclass @@ -105,7 +104,7 @@ def fetch_rules_for(file_: File) -> Sequence[FPRule]: return [] -def fetch_rules_for_derivatives(file_: File) -> Tuple[Optional[File], Sequence[FPRule]]: +def fetch_rules_for_derivatives(file_: File) -> tuple[Optional[File], Sequence[FPRule]]: derivs = Derivation.objects.filter(source_file=file_) for deriv in derivs: derived_file = deriv.derived_file @@ -190,7 +189,9 @@ def main(job: Job, task_uuid: uuid.UUID, file_uuid: uuid.UUID) -> int: def get_parser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser(description="Transcribe file.") + parser = argparse.ArgumentParser( + description="Transcribe file.", exit_on_error=False + ) parser.add_argument("task_uuid", type=uuid.UUID) parser.add_argument("file_uuid", type=uuid.UUID) @@ -203,12 +204,18 @@ def parse_args(parser: argparse.ArgumentParser, job: Job) -> TranscribeFileArgs: return TranscribeFileArgs(**vars(namespace)) -def call(jobs: List[Job]) -> None: +def call(jobs: list[Job]) -> None: parser = get_parser() with transaction.atomic(): for job in jobs: with job.JobContext(): - args = parse_args(parser, job) - - job.set_status(main(job, args.task_uuid, args.file_uuid)) + try: + args = parse_args(parser, job) + except argparse.ArgumentError as err: + job.print_error(str(err)) + status = 1 + else: + status = main(job, args.task_uuid, args.file_uuid) + + job.set_status(status) diff --git a/src/MCPClient/lib/clientScripts/trim_create_rights_entries.py b/src/archivematica/MCPClient/clientScripts/trim_create_rights_entries.py similarity index 92% rename from src/MCPClient/lib/clientScripts/trim_create_rights_entries.py rename to src/archivematica/MCPClient/clientScripts/trim_create_rights_entries.py index a62cd9894c..77d965c695 100755 --- a/src/MCPClient/lib/clientScripts/trim_create_rights_entries.py +++ b/src/archivematica/MCPClient/clientScripts/trim_create_rights_entries.py @@ -29,13 +29,16 @@ django.setup() from django.db import transaction -from fileOperations import getFileUUIDLike -from main.models import RightsStatement -from main.models import RightsStatementOtherRightsDocumentationIdentifier -from main.models import RightsStatementOtherRightsInformation -from main.models import RightsStatementRightsGranted -from main.models import RightsStatementRightsGrantedNote -from main.models import RightsStatementRightsGrantedRestriction + +from archivematica.archivematicaCommon.fileOperations import getFileUUIDLike +from archivematica.dashboard.main.models import RightsStatement +from archivematica.dashboard.main.models import ( + RightsStatementOtherRightsDocumentationIdentifier, +) +from archivematica.dashboard.main.models import RightsStatementOtherRightsInformation +from archivematica.dashboard.main.models import RightsStatementRightsGranted +from archivematica.dashboard.main.models import RightsStatementRightsGrantedNote +from archivematica.dashboard.main.models import RightsStatementRightsGrantedRestriction def callWithException(exception): diff --git a/src/MCPClient/lib/clientScripts/trim_restructure_for_compliance.py b/src/archivematica/MCPClient/clientScripts/trim_restructure_for_compliance.py similarity index 95% rename from src/MCPClient/lib/clientScripts/trim_restructure_for_compliance.py rename to src/archivematica/MCPClient/clientScripts/trim_restructure_for_compliance.py index 124ed60057..84defcd719 100755 --- a/src/MCPClient/lib/clientScripts/trim_restructure_for_compliance.py +++ b/src/archivematica/MCPClient/clientScripts/trim_restructure_for_compliance.py @@ -21,11 +21,14 @@ django.setup() -import archivematicaFunctions -import fileOperations -from archivematicaFunctions import REQUIRED_DIRECTORIES from django.db import transaction +from archivematica.archivematicaCommon import archivematicaFunctions +from archivematica.archivematicaCommon import fileOperations +from archivematica.archivematicaCommon.archivematicaFunctions import ( + REQUIRED_DIRECTORIES, +) + def restructureTRIMForComplianceFileUUIDsAssigned( job, diff --git a/src/MCPClient/lib/clientScripts/trim_verify_checksums.py b/src/archivematica/MCPClient/clientScripts/trim_verify_checksums.py similarity index 95% rename from src/MCPClient/lib/clientScripts/trim_verify_checksums.py rename to src/archivematica/MCPClient/clientScripts/trim_verify_checksums.py index c48c6b046c..b03cd9d8f2 100755 --- a/src/MCPClient/lib/clientScripts/trim_verify_checksums.py +++ b/src/archivematica/MCPClient/clientScripts/trim_verify_checksums.py @@ -23,10 +23,11 @@ from lxml import etree as etree django.setup() -import databaseFunctions -from archivematicaFunctions import get_file_checksum from django.db import transaction -from fileOperations import getFileUUIDLike + +from archivematica.archivematicaCommon import databaseFunctions +from archivematica.archivematicaCommon.archivematicaFunctions import get_file_checksum +from archivematica.archivematicaCommon.fileOperations import getFileUUIDLike def call(jobs): diff --git a/src/MCPClient/lib/clientScripts/trim_verify_manifest.py b/src/archivematica/MCPClient/clientScripts/trim_verify_manifest.py similarity index 98% rename from src/MCPClient/lib/clientScripts/trim_verify_manifest.py rename to src/archivematica/MCPClient/clientScripts/trim_verify_manifest.py index 3a57cfddaa..b8f6fb3058 100755 --- a/src/MCPClient/lib/clientScripts/trim_verify_manifest.py +++ b/src/archivematica/MCPClient/clientScripts/trim_verify_manifest.py @@ -23,9 +23,10 @@ import django django.setup() -import databaseFunctions from django.db import transaction -from fileOperations import getFileUUIDLike + +from archivematica.archivematicaCommon import databaseFunctions +from archivematica.archivematicaCommon.fileOperations import getFileUUIDLike def call(jobs): diff --git a/src/MCPClient/lib/clientScripts/update_size_and_checksum.py b/src/archivematica/MCPClient/clientScripts/update_size_and_checksum.py similarity index 93% rename from src/MCPClient/lib/clientScripts/update_size_and_checksum.py rename to src/archivematica/MCPClient/clientScripts/update_size_and_checksum.py index 3f06991e56..207e4b5101 100755 --- a/src/MCPClient/lib/clientScripts/update_size_and_checksum.py +++ b/src/archivematica/MCPClient/clientScripts/update_size_and_checksum.py @@ -25,14 +25,15 @@ django.setup() import metsrw -import parse_mets_to_db -from archivematicaFunctions import find_mets_file -from custom_handlers import get_script_logger -from databaseFunctions import insertIntoDerivations -from fileOperations import get_size_and_checksum -from fileOperations import updateSizeAndChecksum -from main.models import File -from main.models import FileFormatVersion + +from archivematica.archivematicaCommon.archivematicaFunctions import find_mets_file +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.archivematicaCommon.databaseFunctions import insertIntoDerivations +from archivematica.archivematicaCommon.fileOperations import get_size_and_checksum +from archivematica.archivematicaCommon.fileOperations import updateSizeAndChecksum +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import FileFormatVersion +from archivematica.MCPClient.clientScripts import parse_mets_to_db logger = get_script_logger("archivematica.mcp.client.updateSizeAndChecksum") diff --git a/src/MCPClient/lib/clientScripts/upload_archivesspace.py b/src/archivematica/MCPClient/clientScripts/upload_archivesspace.py similarity index 97% rename from src/MCPClient/lib/clientScripts/upload_archivesspace.py rename to src/archivematica/MCPClient/clientScripts/upload_archivesspace.py index 97fd7361d0..ec6a728452 100755 --- a/src/MCPClient/lib/clientScripts/upload_archivesspace.py +++ b/src/archivematica/MCPClient/clientScripts/upload_archivesspace.py @@ -6,10 +6,11 @@ import django from agentarchives.archivesspace import ArchivesSpaceClient from agentarchives.archivesspace import ArchivesSpaceError -from fpr.models import FormatVersion -from main.models import ArchivesSpaceDIPObjectResourcePairing -from main.models import File -from xml2obj import mets_file + +from archivematica.archivematicaCommon.xml2obj import mets_file +from archivematica.dashboard.fpr.models import FormatVersion +from archivematica.dashboard.main.models import ArchivesSpaceDIPObjectResourcePairing +from archivematica.dashboard.main.models import File django.setup() from django.core.exceptions import ValidationError diff --git a/src/MCPClient/lib/clientScripts/upload_qubit.py b/src/archivematica/MCPClient/clientScripts/upload_qubit.py similarity index 98% rename from src/MCPClient/lib/clientScripts/upload_qubit.py rename to src/archivematica/MCPClient/clientScripts/upload_qubit.py index 5568fc6a0e..687a3a47d8 100755 --- a/src/MCPClient/lib/clientScripts/upload_qubit.py +++ b/src/archivematica/MCPClient/clientScripts/upload_qubit.py @@ -26,15 +26,17 @@ import django import requests -from custom_handlers import get_script_logger + +from archivematica.archivematicaCommon.custom_handlers import get_script_logger django.setup() -import main.models as models from django.conf import settings as mcpclient_settings from django.core.exceptions import ValidationError from django.db import transaction +import archivematica.dashboard.main.models as models + logger = get_script_logger("archivematica.upload.qubit") PREFIX = "[uploadDIP]" diff --git a/src/MCPClient/lib/clientScripts/validate_file.py b/src/archivematica/MCPClient/clientScripts/validate_file.py similarity index 80% rename from src/MCPClient/lib/clientScripts/validate_file.py rename to src/archivematica/MCPClient/clientScripts/validate_file.py index 0318949a60..a5db83608d 100755 --- a/src/MCPClient/lib/clientScripts/validate_file.py +++ b/src/archivematica/MCPClient/clientScripts/validate_file.py @@ -15,33 +15,33 @@ """ -import ast +import json import os import sys +from dataclasses import dataclass from pprint import pformat from typing import Any -from typing import List -from typing import Mapping from typing import Optional import django django.setup() -import databaseFunctions -from client.job import Job -from custom_handlers import get_script_logger -from dicts import replace_string_values from django.conf import settings as mcpclient_settings from django.core.exceptions import ValidationError from django.db import transaction -from executeOrRunSubProcess import executeOrRun -from fpr.models import FormatVersion -from fpr.models import FPRule -from lib import setup_dicts -from main.models import SIP -from main.models import Derivation -from main.models import File + +from archivematica.archivematicaCommon import databaseFunctions +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.archivematicaCommon.dicts import replace_string_values +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun +from archivematica.dashboard.fpr.models import FormatVersion +from archivematica.dashboard.fpr.models import FPRule +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import Derivation +from archivematica.dashboard.main.models import File +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts.lib import setup_dicts SUCCESS_CODE = 0 FAIL_CODE = 1 @@ -67,6 +67,48 @@ def main( logger = get_script_logger("archivematica.mcp.client.validateFile") +@dataclass +class ValidationResult: + """Capture the validation result determined by the FPR command. + + This class records the stdout output from the FPR command and extracts the + event outcome information and detail note. The outcome information indicates + if the file validation was successful, partially successful, or failed. + """ + + event_outcome_information: Optional[str] + event_outcome_detail_note: Optional[str] + stdout: Optional[str] + + @staticmethod + def _parse_stdout(stdout: str) -> dict[str, Any]: + """Parse a stdout string into a dictionary using JSON. + + Expects the stdout to be a valid JSON string. + """ + result = json.loads(stdout) + if not isinstance(result, dict): + raise ValueError("Parsed output is not a dictionary.") + return result + + @staticmethod + def from_stdout(stdout: str) -> "ValidationResult": + output = ValidationResult._parse_stdout(stdout) + return ValidationResult( + output.get("eventOutcomeInformation"), + output.get("eventOutcomeDetailNote"), + output.get("stdout"), + ) + + def passed(self) -> bool: + """Return True if the event outcome information is "pass".""" + return self.event_outcome_information == "pass" + + def partially_passed(self) -> bool: + """Return True if the event outcome information is "partial pass".""" + return self.event_outcome_information == "partial pass" + + class Validator: """A validator validates a file, during transfer or ingest, using FPR config. @@ -168,9 +210,7 @@ def _execute_rule_command(self, rule: FPRule) -> str: ) return "failed" # Parse output and generate an Event - # TODO: Evaluating a python string from a user-definable script seems - # insecure practice; should be JSON. - output: Mapping[str, Any] = ast.literal_eval(stdout) + validation_result = ValidationResult.from_stdout(stdout) event_detail = ( f'program="{rule.command.tool.description}";' f' version="{rule.command.tool.version}"' @@ -179,14 +219,11 @@ def _execute_rule_command(self, rule: FPRule) -> str: # determined that the file is not valid, then we want to both create a # validation event in the db and set ``failed`` to ``True`` because we # want the micro-service in the dashboard GUI to indicate "Failed". - # NOTE: this requires that the stdout of all validation FPR commands be - # a dict (preferably a JSON object) with an ``eventOutcomeInformation`` - # boolean attribute. - if output.get("eventOutcomeInformation") == "pass": + if validation_result.passed(): self.job.print_output( f'Command "{rule.command.description}" was successful' ) - elif output.get("eventOutcomeInformation") == "partial pass": + elif validation_result.partially_passed(): self.job.print_output( f'Command "{rule.command.description}" was partially successful' ) @@ -198,7 +235,7 @@ def _execute_rule_command(self, rule: FPRule) -> str: ) result = "failed" if self.file_type == "preservation": - self._save_stdout_to_logs_dir(output) + self._save_stdout_to_logs_dir(validation_result.stdout) self.job.print_output( f"Creating {self.purpose} event for {self.file_path} ({self.file_uuid})" ) @@ -206,17 +243,15 @@ def _execute_rule_command(self, rule: FPRule) -> str: fileUUID=self.file_uuid, eventType="validation", # From PREMIS controlled vocab. eventDetail=event_detail, - eventOutcome=output.get("eventOutcomeInformation"), - eventOutcomeDetailNote=output.get("eventOutcomeDetailNote"), + eventOutcome=validation_result.event_outcome_information, + eventOutcomeDetailNote=validation_result.event_outcome_detail_note, ) return result - def _save_stdout_to_logs_dir(self, output: Mapping[str, Any]) -> None: + def _save_stdout_to_logs_dir(self, stdout: Optional[str]) -> None: """Save the validation command's output from validating the file to a file at logs/implementationChecks/.xml in the SIP. - ``output`` is expected to be a dict with a ``stdout`` key. """ - stdout = output.get("stdout") if stdout and self.sip_pres_val_dir: filename = os.path.basename(self.file_path) stdout_path = os.path.join(self.sip_pres_val_dir, f"{filename}.xml") @@ -324,21 +359,21 @@ def sip_pres_val_dir(self) -> Optional[str]: return self._sip_pres_val_dir -def _get_shared_path(argv: List[str]) -> Optional[str]: +def _get_shared_path(argv: list[str]) -> Optional[str]: try: return argv[4] except IndexError: return None -def _get_file_type(argv: List[str]) -> str: +def _get_file_type(argv: list[str]) -> str: try: return argv[5] except IndexError: return "original" -def call(jobs: List[Job]) -> None: +def call(jobs: list[Job]) -> None: with transaction.atomic(): for job in jobs: with job.JobContext(logger=logger): diff --git a/src/MCPClient/lib/clientScripts/verify_aip.py b/src/archivematica/MCPClient/clientScripts/verify_aip.py similarity index 95% rename from src/MCPClient/lib/clientScripts/verify_aip.py rename to src/archivematica/MCPClient/clientScripts/verify_aip.py index 32feae86a6..f7352ab24d 100755 --- a/src/MCPClient/lib/clientScripts/verify_aip.py +++ b/src/archivematica/MCPClient/clientScripts/verify_aip.py @@ -7,15 +7,16 @@ import django django.setup() -import databaseFunctions -from archivematicaFunctions import get_setting from bagit import Bag from bagit import BagError from django.conf import settings as mcpclient_settings from django.db import transaction -from executeOrRunSubProcess import executeOrRun -from main.models import SIP -from main.models import File + +from archivematica.archivematicaCommon import databaseFunctions +from archivematica.archivematicaCommon.archivematicaFunctions import get_setting +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import File class VerifyChecksumsError(Exception): @@ -215,9 +216,7 @@ def verify_aip(job): except VerifyChecksumsError: return_code = 1 else: - job.pyprint( - "Not verifying checksums because other tests have already" " failed." - ) + job.pyprint("Not verifying checksums because other tests have already failed.") # cleanup if not is_uncompressed_aip: diff --git a/src/MCPClient/lib/clientScripts/verify_and_restructure_transfer_bag.py b/src/archivematica/MCPClient/clientScripts/verify_and_restructure_transfer_bag.py similarity index 92% rename from src/MCPClient/lib/clientScripts/verify_and_restructure_transfer_bag.py rename to src/archivematica/MCPClient/clientScripts/verify_and_restructure_transfer_bag.py index 865cba3395..2bd61f8165 100755 --- a/src/MCPClient/lib/clientScripts/verify_and_restructure_transfer_bag.py +++ b/src/archivematica/MCPClient/clientScripts/verify_and_restructure_transfer_bag.py @@ -23,13 +23,15 @@ django.setup() -import bag -import fileOperations -from archivematicaFunctions import OPTIONAL_FILES -from archivematicaFunctions import REQUIRED_DIRECTORIES -from databaseFunctions import insertIntoEvents -from main.models import File -from move_or_merge import move_or_merge +from archivematica.archivematicaCommon import bag +from archivematica.archivematicaCommon import fileOperations +from archivematica.archivematicaCommon.archivematicaFunctions import OPTIONAL_FILES +from archivematica.archivematicaCommon.archivematicaFunctions import ( + REQUIRED_DIRECTORIES, +) +from archivematica.archivematicaCommon.databaseFunctions import insertIntoEvents +from archivematica.dashboard.main.models import File +from archivematica.MCPClient.clientScripts.move_or_merge import move_or_merge def restructureBagForComplianceFileUUIDsAssigned( diff --git a/src/MCPClient/lib/clientScripts/verify_checksum.py b/src/archivematica/MCPClient/clientScripts/verify_checksum.py similarity index 97% rename from src/MCPClient/lib/clientScripts/verify_checksum.py rename to src/archivematica/MCPClient/clientScripts/verify_checksum.py index 8e4efed629..e7f71daa7e 100755 --- a/src/MCPClient/lib/clientScripts/verify_checksum.py +++ b/src/archivematica/MCPClient/clientScripts/verify_checksum.py @@ -37,10 +37,10 @@ from django.db import transaction django.setup() -from custom_handlers import get_script_logger -from main.models import Event -from main.models import File -from main.models import Transfer +from archivematica.archivematicaCommon.custom_handlers import get_script_logger +from archivematica.dashboard.main.models import Event +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import Transfer logger = get_script_logger("archivematica.mcp.client.verify_checksum") diff --git a/src/MCPClient/lib/clientScripts/verify_checksums_in_file_sec_of_dspace_mets_files.py b/src/archivematica/MCPClient/clientScripts/verify_checksums_in_file_sec_of_dspace_mets_files.py similarity index 97% rename from src/MCPClient/lib/clientScripts/verify_checksums_in_file_sec_of_dspace_mets_files.py rename to src/archivematica/MCPClient/clientScripts/verify_checksums_in_file_sec_of_dspace_mets_files.py index fdca386cd8..11d6c8b79a 100755 --- a/src/MCPClient/lib/clientScripts/verify_checksums_in_file_sec_of_dspace_mets_files.py +++ b/src/archivematica/MCPClient/clientScripts/verify_checksums_in_file_sec_of_dspace_mets_files.py @@ -20,7 +20,8 @@ import sys import lxml.etree as etree -from archivematicaFunctions import get_file_checksum + +from archivematica.archivematicaCommon.archivematicaFunctions import get_file_checksum def verifyMetsFileSecChecksums(job, metsFile, date, taskUUID, relativeDirectory="./"): diff --git a/src/MCPClient/lib/clientScripts/verify_mets.py b/src/archivematica/MCPClient/clientScripts/verify_mets.py similarity index 100% rename from src/MCPClient/lib/clientScripts/verify_mets.py rename to src/archivematica/MCPClient/clientScripts/verify_mets.py diff --git a/src/MCPClient/lib/clientScripts/verify_sip_compliance.py b/src/archivematica/MCPClient/clientScripts/verify_sip_compliance.py similarity index 98% rename from src/MCPClient/lib/clientScripts/verify_sip_compliance.py rename to src/archivematica/MCPClient/clientScripts/verify_sip_compliance.py index eb9dd43df9..5678ccc2c4 100755 --- a/src/MCPClient/lib/clientScripts/verify_sip_compliance.py +++ b/src/archivematica/MCPClient/clientScripts/verify_sip_compliance.py @@ -18,7 +18,7 @@ import os import sys -from client import metrics +from archivematica.MCPClient.client import metrics REQUIRED_DIRECTORIES = ( "objects", diff --git a/src/MCPClient/lib/clientScripts/verify_transfer_compliance.py b/src/archivematica/MCPClient/clientScripts/verify_transfer_compliance.py similarity index 96% rename from src/MCPClient/lib/clientScripts/verify_transfer_compliance.py rename to src/archivematica/MCPClient/clientScripts/verify_transfer_compliance.py index 137aa6513c..9b2a8bdbe2 100755 --- a/src/MCPClient/lib/clientScripts/verify_transfer_compliance.py +++ b/src/archivematica/MCPClient/clientScripts/verify_transfer_compliance.py @@ -18,7 +18,7 @@ import os import sys -from verify_sip_compliance import checkDirectory +from archivematica.MCPClient.clientScripts.verify_sip_compliance import checkDirectory REQUIRED_DIRECTORIES = ( "objects", diff --git a/src/MCPClient/install/README.md b/src/archivematica/MCPClient/install/README.md similarity index 98% rename from src/MCPClient/install/README.md rename to src/archivematica/MCPClient/install/README.md index f42f080578..e329d02b1b 100644 --- a/src/MCPClient/install/README.md +++ b/src/archivematica/MCPClient/install/README.md @@ -126,10 +126,12 @@ This is the full list of variables supported by MCPClient: - **Default:** `/var/archivematica/sharedDirectory/watchedDirectories/` - **`ARCHIVEMATICA_MCPCLIENT_MCPCLIENT_CLIENTASSETSDIRECTORY`**: - - **Description:** location of the client assets directory. + - **Description:** location of the client assets directory. If this is empty, + Archivematica will load assets from the `assets` directory of the + `archivematica.MCPClient` package. - **Config file example:** `MCPClient.clientAssetsDirectory` - **Type:** `string` - - **Default:** `/usr/lib/archivematica/MCPClient/assets/` + - **Default:** `""` - **`ARCHIVEMATICA_MCPCLIENT_MCPCLIENT_MCPARCHIVEMATICASERVER`**: - **Description:** address of the Gearman server. @@ -141,16 +143,18 @@ This is the full list of variables supported by MCPClient: - **Description:** location of the client modules configuration file. This can be useful when the user wants to set up workers that can only work in a limited number of tasks, e.g. a worker exclusively dedicated to antivirus - scanning or file identification. + scanning or file identification. If this is empty, Archivematica will load + the `archivematicaClientModules` file contained in the + `archivematica.MCPClient` package. - **Config file example:** `MCPClient.archivematicaClientModules` - **Type:** `string` - - **Default:** `/usr/lib/archivematica/MCPClient/archivematicaClientModules` + - **Default:** `""` - **`ARCHIVEMATICA_MCPCLIENT_MCPCLIENT_ELASTICSEARCHSERVER`**: - **Description:** address of the Elasticsearch server. - **Config file example:** `MCPClient.elasticsearchServer` - **Type:** `string` - - **Default:** `localhost:9200` + - **Default:** `http://localhost:9200` - **`ARCHIVEMATICA_MCPCLIENT_MCPCLIENT_ELASTICSEARCHTIMEOUT`**: - **Description:** configures the Elasticsearch client to stop waiting for a diff --git a/src/MCPClient/install/clientConfig.conf b/src/archivematica/MCPClient/install/clientConfig.conf similarity index 87% rename from src/MCPClient/install/clientConfig.conf rename to src/archivematica/MCPClient/install/clientConfig.conf index 92b9d4716f..9947a5a329 100644 --- a/src/MCPClient/install/clientConfig.conf +++ b/src/archivematica/MCPClient/install/clientConfig.conf @@ -4,8 +4,6 @@ sharedDirectoryMounted = /var/archivematica/sharedDirectory/ watchDirectoryPath = /var/archivematica/sharedDirectory/watchedDirectories/ processingDirectory = /var/archivematica/sharedDirectory/currentlyProcessing/ rejectedDirectory = /var/archivematica/sharedDirectory/rejected/ -archivematicaClientModules = /usr/lib/archivematica/MCPClient/archivematicaClientModules -clientAssetsDirectory = /usr/lib/archivematica/MCPClient/assets/ elasticsearchServer = localhost:9200 elasticsearchTimeout = 10 search_enabled = true diff --git a/src/MCPClient/install/clientConfig.logging.json b/src/archivematica/MCPClient/install/clientConfig.logging.json similarity index 81% rename from src/MCPClient/install/clientConfig.logging.json rename to src/archivematica/MCPClient/install/clientConfig.logging.json index e25426db1e..2db616ab74 100644 --- a/src/MCPClient/install/clientConfig.logging.json +++ b/src/archivematica/MCPClient/install/clientConfig.logging.json @@ -9,7 +9,7 @@ "handlers": { "logfile": { "backupCount": 5, - "class": "custom_handlers.GroupWriteRotatingFileHandler", + "class": "archivematica.archivematicaCommon.custom_handlers.GroupWriteRotatingFileHandler", "filename": "/var/log/archivematica/MCPClient/MCPClient.log", "formatter": "detailed", "level": "INFO", @@ -17,7 +17,7 @@ }, "verboselogfile": { "backupCount": 5, - "class": "custom_handlers.GroupWriteRotatingFileHandler", + "class": "archivematica.archivematicaCommon.custom_handlers.GroupWriteRotatingFileHandler", "filename": "/var/log/archivematica/MCPClient/MCPClient.debug.log", "formatter": "detailed", "level": "DEBUG", diff --git a/src/MCPServer/lib/settings/__init__.py b/src/archivematica/MCPClient/settings/__init__.py similarity index 100% rename from src/MCPServer/lib/settings/__init__.py rename to src/archivematica/MCPClient/settings/__init__.py diff --git a/src/MCPClient/lib/settings/common.py b/src/archivematica/MCPClient/settings/common.py similarity index 93% rename from src/MCPClient/lib/settings/common.py rename to src/archivematica/MCPClient/settings/common.py index dab115af11..4961309882 100644 --- a/src/MCPClient/lib/settings/common.py +++ b/src/archivematica/MCPClient/settings/common.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . import configparser +import importlib.resources import importlib.util import json import logging.config @@ -23,11 +24,12 @@ from io import StringIO from pathlib import Path -import email_settings -from appconfig import Config -from appconfig import process_search_enabled from django.core.exceptions import ImproperlyConfigured +from archivematica.archivematicaCommon import email_settings +from archivematica.archivematicaCommon.appconfig import Config +from archivematica.archivematicaCommon.appconfig import process_search_enabled + def _get_settings_from_file(path): spec = importlib.util.spec_from_file_location(path.stem, path) @@ -217,9 +219,9 @@ def workers(config, section): watchDirectoryPath = /var/archivematica/sharedDirectory/watchedDirectories/ processingDirectory = /var/archivematica/sharedDirectory/currentlyProcessing/ rejectedDirectory = /var/archivematica/sharedDirectory/rejected/ -archivematicaClientModules = /usr/lib/archivematica/MCPClient/archivematicaClientModules -clientAssetsDirectory = /usr/lib/archivematica/MCPClient/assets/ -elasticsearchServer = localhost:9200 +archivematicaClientModules = +clientAssetsDirectory = +elasticsearchServer = http://localhost:9200 elasticsearchTimeout = 10 search_enabled = true metadata_xml_validation_enabled = false @@ -304,11 +306,11 @@ def workers(config, section): "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", - "components.accounts", - "main", - "components.mcp", - "components.administration", - "fpr", + "archivematica.dashboard.components.accounts", + "archivematica.dashboard.main", + "archivematica.dashboard.components.mcp", + "archivematica.dashboard.components.administration", + "archivematica.dashboard.fpr", # Only needed because archivematicaClient calls django.setup() # which imports the ApiAccess model through the helpers module of # the dashboard @@ -386,6 +388,18 @@ def workers(config, section): else: PROMETHEUS_ENABLED = True +if not CLIENT_ASSETS_DIRECTORY: + assets_directory_path = str( + importlib.resources.files("archivematica.MCPClient") / "assets" + ) + CLIENT_ASSETS_DIRECTORY = f"{assets_directory_path}/" + +if not CLIENT_MODULES_FILE: + CLIENT_MODULES_FILE = str( + importlib.resources.files("archivematica.MCPClient") + / "archivematicaClientModules" + ) + TEMPLATES = [{"BACKEND": "django.template.backends.django.DjangoTemplates"}] # Apply email settings diff --git a/src/MCPServer/lib/settings/test.py b/src/archivematica/MCPClient/settings/test.py similarity index 95% rename from src/MCPServer/lib/settings/test.py rename to src/archivematica/MCPClient/settings/test.py index d786c2d26c..9ade43b419 100644 --- a/src/MCPServer/lib/settings/test.py +++ b/src/archivematica/MCPClient/settings/test.py @@ -16,7 +16,7 @@ # along with Archivematica. If not, see . """Test settings and globals.""" -from .common import * +from archivematica.MCPClient.settings.common import * DATABASES = { "default": { diff --git a/src/MCPClient/lib/settings/testmysql.py b/src/archivematica/MCPClient/settings/testmysql.py similarity index 85% rename from src/MCPClient/lib/settings/testmysql.py rename to src/archivematica/MCPClient/settings/testmysql.py index 27feae1582..f3e031f1ab 100644 --- a/src/MCPClient/lib/settings/testmysql.py +++ b/src/archivematica/MCPClient/settings/testmysql.py @@ -1,4 +1,4 @@ -from .test import * +from archivematica.MCPClient.settings.test import * DATABASES = { "default": { diff --git a/src/archivematicaCommon/lib/__init__.py b/src/archivematica/MCPServer/__init__.py similarity index 100% rename from src/archivematicaCommon/lib/__init__.py rename to src/archivematica/MCPServer/__init__.py diff --git a/src/MCPServer/lib/archivematicaMCP.py b/src/archivematica/MCPServer/archivematicaMCP.py similarity index 61% rename from src/MCPServer/lib/archivematicaMCP.py rename to src/archivematica/MCPServer/archivematicaMCP.py index 9bbe6f8ecf..c0d0ec8029 100755 --- a/src/MCPServer/lib/archivematicaMCP.py +++ b/src/archivematica/MCPServer/archivematicaMCP.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -from server.mcp import main +from archivematica.MCPServer.server.mcp import main # TODO: update the entrypoint and remove this alias main() diff --git a/src/MCPServer/lib/assets/workflow-schema-v1.json b/src/archivematica/MCPServer/assets/workflow-schema-v1.json similarity index 100% rename from src/MCPServer/lib/assets/workflow-schema-v1.json rename to src/archivematica/MCPServer/assets/workflow-schema-v1.json diff --git a/src/MCPServer/lib/assets/workflow.json b/src/archivematica/MCPServer/assets/workflow.json similarity index 99% rename from src/MCPServer/lib/assets/workflow.json rename to src/archivematica/MCPServer/assets/workflow.json index a1d72cf024..3b735bd427 100644 --- a/src/MCPServer/lib/assets/workflow.json +++ b/src/archivematica/MCPServer/assets/workflow.json @@ -2524,8 +2524,8 @@ "config": { "@manager": "linkTaskManagerFiles", "@model": "StandardTaskConfig", - "arguments": "\"%fileUUID%\" \"%relativeLocation%\" \"%date%\" \"%taskUUID%\"", - "execute": "archivematicaClamscan_v0.0", + "arguments": "\"%fileUUID%\" \"%relativeLocation%\" \"%date%\"", + "execute": "antivirus_v0.0", "filter_subdir": "objects/submissionDocumentation", "stderr_file": "%SIPLogsDirectory%clamAVScan.txt" }, @@ -2555,8 +2555,8 @@ "config": { "@manager": "linkTaskManagerFiles", "@model": "StandardTaskConfig", - "arguments": "\"%fileUUID%\" \"%relativeLocation%\" \"%date%\" \"%taskUUID%\"", - "execute": "archivematicaClamscan_v0.0", + "arguments": "\"%fileUUID%\" \"%relativeLocation%\" \"%date%\"", + "execute": "antivirus_v0.0", "stderr_file": "%SIPLogsDirectory%clamAVScan.txt" }, "description": { @@ -2884,8 +2884,8 @@ "config": { "@manager": "linkTaskManagerFiles", "@model": "StandardTaskConfig", - "arguments": "\"%fileUUID%\" \"%relativeLocation%\" \"%date%\" \"%taskUUID%\"", - "execute": "archivematicaClamscan_v0.0", + "arguments": "\"%fileUUID%\" \"%relativeLocation%\" \"%date%\"", + "execute": "antivirus_v0.0", "stderr_file": "%SIPLogsDirectory%clamAVScan.txt" }, "description": { @@ -6623,8 +6623,8 @@ "config": { "@manager": "linkTaskManagerFiles", "@model": "StandardTaskConfig", - "arguments": "\"%fileUUID%\" \"%relativeLocation%\" \"%date%\" \"%taskUUID%\"", - "execute": "archivematicaClamscan_v0.0", + "arguments": "\"%fileUUID%\" \"%relativeLocation%\" \"%date%\"", + "execute": "antivirus_v0.0", "filter_subdir": "objects/" }, "description": { @@ -6773,7 +6773,7 @@ "@manager": "linkTaskManagerSetUnitVariable", "@model": "TaskConfigSetUnitVariable", "variable": "normalize_v1.0", - "variable_value": "{'filterSubDir':'objects/attachments'}" + "variable_value": "{\"filterSubDir\":\"objects/attachments\"}" }, "description": { "en": "Set files to normalize", @@ -7202,8 +7202,8 @@ "config": { "@manager": "linkTaskManagerFiles", "@model": "StandardTaskConfig", - "arguments": "\"%fileUUID%\" \"%relativeLocation%\" \"%date%\" \"%taskUUID%\"", - "execute": "archivematicaClamscan_v0.0", + "arguments": "\"%fileUUID%\" \"%relativeLocation%\" \"%date%\"", + "execute": "antivirus_v0.0", "filter_subdir": "objects/metadata", "stderr_file": "%SIPLogsDirectory%clamAVScan.txt" }, @@ -9216,7 +9216,7 @@ "@manager": "linkTaskManagerSetUnitVariable", "@model": "TaskConfigSetUnitVariable", "variable": "identifyFileFormat_v0.0", - "variable_value": "{'filterSubDir':'objects/attachments'}" + "variable_value": "{\"filterSubDir\":\"objects/attachments\"}" }, "description": { "en": "Set files to identify", diff --git a/src/MCPServer/install/README.md b/src/archivematica/MCPServer/install/README.md similarity index 100% rename from src/MCPServer/install/README.md rename to src/archivematica/MCPServer/install/README.md diff --git a/src/MCPServer/install/serverConfig.conf b/src/archivematica/MCPServer/install/serverConfig.conf similarity index 100% rename from src/MCPServer/install/serverConfig.conf rename to src/archivematica/MCPServer/install/serverConfig.conf diff --git a/src/MCPServer/install/serverConfig.logging.json b/src/archivematica/MCPServer/install/serverConfig.logging.json similarity index 81% rename from src/MCPServer/install/serverConfig.logging.json rename to src/archivematica/MCPServer/install/serverConfig.logging.json index 622532ce0e..8f169418b1 100644 --- a/src/MCPServer/install/serverConfig.logging.json +++ b/src/archivematica/MCPServer/install/serverConfig.logging.json @@ -9,7 +9,7 @@ "handlers": { "logfile": { "backupCount": 5, - "class": "custom_handlers.GroupWriteRotatingFileHandler", + "class": "archivematica.archivematicaCommon.custom_handlers.GroupWriteRotatingFileHandler", "filename": "/var/log/archivematica/MCPServer/MCPServer.log", "formatter": "detailed", "level": "INFO", @@ -17,7 +17,7 @@ }, "verboselogfile": { "backupCount": 5, - "class": "custom_handlers.GroupWriteRotatingFileHandler", + "class": "archivematica.archivematicaCommon.custom_handlers.GroupWriteRotatingFileHandler", "filename": "/var/log/archivematica/MCPServer/MCPServer.debug.log", "formatter": "detailed", "level": "DEBUG", diff --git a/src/MCPServer/lib/server/__init__.py b/src/archivematica/MCPServer/server/__init__.py similarity index 100% rename from src/MCPServer/lib/server/__init__.py rename to src/archivematica/MCPServer/server/__init__.py diff --git a/src/MCPServer/lib/server/jobs/__init__.py b/src/archivematica/MCPServer/server/jobs/__init__.py similarity index 50% rename from src/MCPServer/lib/server/jobs/__init__.py rename to src/archivematica/MCPServer/server/jobs/__init__.py index 6e515bf25c..913db2007c 100644 --- a/src/MCPServer/lib/server/jobs/__init__.py +++ b/src/archivematica/MCPServer/server/jobs/__init__.py @@ -13,19 +13,19 @@ * `LocalJob`, handling work done directly on MCPServer """ -from server.jobs.base import Job -from server.jobs.chain import JobChain -from server.jobs.client import ClientScriptJob -from server.jobs.client import DirectoryClientScriptJob -from server.jobs.client import FilesClientScriptJob -from server.jobs.client import OutputClientScriptJob -from server.jobs.decisions import DecisionJob -from server.jobs.decisions import NextChainDecisionJob -from server.jobs.decisions import OutputDecisionJob -from server.jobs.decisions import UpdateContextDecisionJob -from server.jobs.local import GetUnitVarLinkJob -from server.jobs.local import LocalJob -from server.jobs.local import SetUnitVarLinkJob +from archivematica.MCPServer.server.jobs.base import Job +from archivematica.MCPServer.server.jobs.chain import JobChain +from archivematica.MCPServer.server.jobs.client import ClientScriptJob +from archivematica.MCPServer.server.jobs.client import DirectoryClientScriptJob +from archivematica.MCPServer.server.jobs.client import FilesClientScriptJob +from archivematica.MCPServer.server.jobs.client import OutputClientScriptJob +from archivematica.MCPServer.server.jobs.decisions import DecisionJob +from archivematica.MCPServer.server.jobs.decisions import NextChainDecisionJob +from archivematica.MCPServer.server.jobs.decisions import OutputDecisionJob +from archivematica.MCPServer.server.jobs.decisions import UpdateContextDecisionJob +from archivematica.MCPServer.server.jobs.local import GetUnitVarLinkJob +from archivematica.MCPServer.server.jobs.local import LocalJob +from archivematica.MCPServer.server.jobs.local import SetUnitVarLinkJob __all__ = ( "ClientScriptJob", diff --git a/src/MCPServer/lib/server/jobs/base.py b/src/archivematica/MCPServer/server/jobs/base.py similarity index 96% rename from src/MCPServer/lib/server/jobs/base.py rename to src/archivematica/MCPServer/server/jobs/base.py index 2c7249571c..5847d4f0a5 100644 --- a/src/MCPServer/lib/server/jobs/base.py +++ b/src/archivematica/MCPServer/server/jobs/base.py @@ -6,9 +6,10 @@ import logging import uuid -from dbconns import auto_close_old_connections from django.utils import timezone -from main import models + +from archivematica.archivematicaCommon.dbconns import auto_close_old_connections +from archivematica.dashboard.main import models logger = logging.getLogger("archivematica.mcp.server.jobs") diff --git a/src/MCPServer/lib/server/jobs/chain.py b/src/archivematica/MCPServer/server/jobs/chain.py similarity index 86% rename from src/MCPServer/lib/server/jobs/chain.py rename to src/archivematica/MCPServer/server/jobs/chain.py index fd5ac0c741..cc8fafd983 100644 --- a/src/MCPServer/lib/server/jobs/chain.py +++ b/src/archivematica/MCPServer/server/jobs/chain.py @@ -11,14 +11,14 @@ import logging -from server.jobs.client import DirectoryClientScriptJob -from server.jobs.client import FilesClientScriptJob -from server.jobs.client import OutputClientScriptJob -from server.jobs.decisions import NextChainDecisionJob -from server.jobs.decisions import OutputDecisionJob -from server.jobs.decisions import UpdateContextDecisionJob -from server.jobs.local import GetUnitVarLinkJob -from server.jobs.local import SetUnitVarLinkJob +from archivematica.MCPServer.server.jobs.client import DirectoryClientScriptJob +from archivematica.MCPServer.server.jobs.client import FilesClientScriptJob +from archivematica.MCPServer.server.jobs.client import OutputClientScriptJob +from archivematica.MCPServer.server.jobs.decisions import NextChainDecisionJob +from archivematica.MCPServer.server.jobs.decisions import OutputDecisionJob +from archivematica.MCPServer.server.jobs.decisions import UpdateContextDecisionJob +from archivematica.MCPServer.server.jobs.local import GetUnitVarLinkJob +from archivematica.MCPServer.server.jobs.local import SetUnitVarLinkJob logger = logging.getLogger("archivematica.mcp.server.jobs.chain") diff --git a/src/MCPServer/lib/server/jobs/client.py b/src/archivematica/MCPServer/server/jobs/client.py similarity index 94% rename from src/MCPServer/lib/server/jobs/client.py rename to src/archivematica/MCPServer/server/jobs/client.py index 6ea2f72d95..334a4a34ed 100644 --- a/src/MCPServer/lib/server/jobs/client.py +++ b/src/archivematica/MCPServer/server/jobs/client.py @@ -3,19 +3,18 @@ """ import abc -import ast import json import logging -from dbconns import auto_close_old_connections from django.conf import settings from django.core.exceptions import ValidationError -from main import models -from server import metrics -from server.jobs.base import Job -from server.tasks import Task -from server.tasks import get_task_backend +from archivematica.archivematicaCommon.dbconns import auto_close_old_connections +from archivematica.dashboard.main import models +from archivematica.MCPServer.server import metrics +from archivematica.MCPServer.server.jobs.base import Job +from archivematica.MCPServer.server.tasks import Task +from archivematica.MCPServer.server.tasks import get_task_backend logger = logging.getLogger("archivematica.mcp.server.jobs.client") @@ -202,8 +201,8 @@ def filter_subdir(self): if var: try: - script_override = ast.literal_eval(var.variablevalue) - except (SyntaxError, ValueError): + script_override = json.loads(var.variablevalue) + except (TypeError, ValueError): pass else: filter_subdir = script_override.get("filterSubDir") diff --git a/src/MCPServer/lib/server/jobs/decisions.py b/src/archivematica/MCPServer/server/jobs/decisions.py similarity index 95% rename from src/MCPServer/lib/server/jobs/decisions.py rename to src/archivematica/MCPServer/server/jobs/decisions.py index d17e728eb2..bab0913550 100644 --- a/src/MCPServer/lib/server/jobs/decisions.py +++ b/src/archivematica/MCPServer/server/jobs/decisions.py @@ -7,14 +7,13 @@ import threading from collections import OrderedDict -from dbconns import auto_close_old_connections -from main import models - -from server.jobs.base import Job -from server.processing_config import load_preconfigured_choice -from server.processing_config import load_processing_xml -from server.translation import TranslationLabel -from server.workflow_abilities import choice_is_available +from archivematica.archivematicaCommon.dbconns import auto_close_old_connections +from archivematica.dashboard.main import models +from archivematica.MCPServer.server.jobs.base import Job +from archivematica.MCPServer.server.processing_config import load_preconfigured_choice +from archivematica.MCPServer.server.processing_config import load_processing_xml +from archivematica.MCPServer.server.translation import TranslationLabel +from archivematica.MCPServer.server.workflow_abilities import choice_is_available logger = logging.getLogger("archivematica.mcp.server.jobs.decisions") @@ -112,7 +111,7 @@ def get_choices(self): @auto_close_old_connections() def decide(self, choice): # TODO: fix circular imports :( - from server.jobs import JobChain + from archivematica.MCPServer.server.jobs import JobChain if choice not in self.get_choices(): raise ValueError(f"{choice} is not one of the available choices") diff --git a/src/MCPServer/lib/server/jobs/local.py b/src/archivematica/MCPServer/server/jobs/local.py similarity index 91% rename from src/MCPServer/lib/server/jobs/local.py rename to src/archivematica/MCPServer/server/jobs/local.py index c9d869c461..a7f6a2e355 100644 --- a/src/MCPServer/lib/server/jobs/local.py +++ b/src/archivematica/MCPServer/server/jobs/local.py @@ -5,11 +5,11 @@ import abc import logging -from dbconns import auto_close_old_connections from django.core.exceptions import ValidationError -from main import models -from server.jobs.base import Job +from archivematica.archivematicaCommon.dbconns import auto_close_old_connections +from archivematica.dashboard.main import models +from archivematica.MCPServer.server.jobs.base import Job logger = logging.getLogger("archivematica.mcp.server.jobs.local") diff --git a/src/MCPServer/lib/server/mcp.py b/src/archivematica/MCPServer/server/mcp.py similarity index 86% rename from src/MCPServer/lib/server/mcp.py rename to src/archivematica/MCPServer/server/mcp.py index 5c8808165a..c917da8b22 100644 --- a/src/MCPServer/lib/server/mcp.py +++ b/src/archivematica/MCPServer/server/mcp.py @@ -34,19 +34,19 @@ from django.conf import settings -from server import metrics -from server import rpc_server -from server import shared_dirs -from server.jobs import Job -from server.jobs import JobChain -from server.packages import DIP -from server.packages import SIP -from server.packages import Package -from server.packages import Transfer -from server.queues import PackageQueue -from server.tasks import Task -from server.watch_dirs import watch_directories -from server.workflow import load_workflow +from archivematica.MCPServer.server import metrics +from archivematica.MCPServer.server import rpc_server +from archivematica.MCPServer.server import shared_dirs +from archivematica.MCPServer.server.jobs import Job +from archivematica.MCPServer.server.jobs import JobChain +from archivematica.MCPServer.server.packages import DIP +from archivematica.MCPServer.server.packages import SIP +from archivematica.MCPServer.server.packages import Package +from archivematica.MCPServer.server.packages import Transfer +from archivematica.MCPServer.server.queues import PackageQueue +from archivematica.MCPServer.server.tasks import Task +from archivematica.MCPServer.server.watch_dirs import watch_directories +from archivematica.MCPServer.server.workflow import load_workflow logger = logging.getLogger("archivematica.mcp.server") diff --git a/src/MCPServer/lib/server/metrics.py b/src/archivematica/MCPServer/server/metrics.py similarity index 96% rename from src/MCPServer/lib/server/metrics.py rename to src/archivematica/MCPServer/server/metrics.py index e328f1229e..e1119b4849 100644 --- a/src/MCPServer/lib/server/metrics.py +++ b/src/archivematica/MCPServer/server/metrics.py @@ -5,14 +5,15 @@ import functools import os -from common_metrics import TASK_DURATION_BUCKETS from django.conf import settings from prometheus_client import Counter from prometheus_client import Gauge from prometheus_client import Histogram from prometheus_client import Info from prometheus_client import start_http_server -from version import get_full_version + +from archivematica.archivematicaCommon.common_metrics import TASK_DURATION_BUCKETS +from archivematica.archivematicaCommon.version import get_full_version gearman_active_jobs_gauge = Gauge( "mcpserver_gearman_active_jobs", "Number of gearman jobs currently being processed" diff --git a/src/MCPServer/lib/server/packages.py b/src/archivematica/MCPServer/server/packages.py similarity index 97% rename from src/MCPServer/lib/server/packages.py rename to src/archivematica/MCPServer/server/packages.py index 80f4d3a090..389fba2fb8 100644 --- a/src/MCPServer/lib/server/packages.py +++ b/src/archivematica/MCPServer/server/packages.py @@ -1,8 +1,8 @@ """Package management.""" import abc -import ast import collections +import json import logging import os from pathlib import Path @@ -10,16 +10,18 @@ from uuid import UUID from uuid import uuid4 -import storageService as storage_service -from dbconns import auto_close_old_connections from django.conf import settings from django.core.exceptions import ValidationError from django.utils import timezone -from main import models -from server.jobs import JobChain -from server.processing_config import processing_configuration_file_exists -from server.utils import uuid_from_path +import archivematica.archivematicaCommon.storageService as storage_service +from archivematica.archivematicaCommon.dbconns import auto_close_old_connections +from archivematica.dashboard.main import models +from archivematica.MCPServer.server.jobs import JobChain +from archivematica.MCPServer.server.processing_config import ( + processing_configuration_file_exists, +) +from archivematica.MCPServer.server.utils import uuid_from_path logger = logging.getLogger("archivematica.mcp.server.packages") @@ -408,7 +410,7 @@ def _start_package_transfer_with_auto_approval( """ transfer_rel, filepath, path = _determine_transfer_paths(name, path, tmpdir) logger.debug( - "Package %s: determined vars" " transfer_rel=%s, filepath=%s, path=%s", + "Package %s: determined vars transfer_rel=%s, filepath=%s, path=%s", transfer.pk, transfer_rel, filepath, @@ -416,7 +418,7 @@ def _start_package_transfer_with_auto_approval( ) logger.debug( - "Package %s: copying chosen contents from transfer sources" " (from=%s, to=%s)", + "Package %s: copying chosen contents from transfer sources (from=%s, to=%s)", transfer.pk, path, transfer_rel, @@ -450,7 +452,7 @@ def _start_package_transfer(transfer, name, path, tmpdir, starting_point): """ transfer_rel, filepath, path = _determine_transfer_paths(name, path, tmpdir) logger.debug( - "Package %s: determined vars" " transfer_rel=%s, filepath=%s, path=%s", + "Package %s: determined vars transfer_rel=%s, filepath=%s, path=%s", transfer.pk, transfer_rel, filepath, @@ -458,7 +460,7 @@ def _start_package_transfer(transfer, name, path, tmpdir, starting_point): ) logger.debug( - "Package %s: copying chosen contents from transfer sources" " (from=%s, to=%s)", + "Package %s: copying chosen contents from transfer sources (from=%s, to=%s)", transfer.pk, path, transfer_rel, @@ -466,7 +468,7 @@ def _start_package_transfer(transfer, name, path, tmpdir, starting_point): _copy_from_transfer_sources([path], transfer_rel) logger.debug( - "Package %s: moving package to activeTransfers dir (from=%s," " to=%s)", + "Package %s: moving package to activeTransfers dir (from=%s, to=%s)", transfer.pk, filepath, starting_point.watched_dir, @@ -701,7 +703,7 @@ def set_variable(self, key, value, chain_link_id): if created: message = "New UnitVariable %s created for %s: %s (MSCL: %s)" else: - message = "Existing UnitVariable %s for %s updated to %s (MSCL" " %s)" + message = "Existing UnitVariable %s for %s updated to %s (MSCL %s)" logger.info(message, key, self.uuid, value, chain_link_id) @@ -926,10 +928,9 @@ def load_from_db(cls, uuid): # Distinct helps here, at least unit_vars_queryset = unit_vars_queryset.values_list("variablevalue").distinct() for unit_var_value in unit_vars_queryset: - # TODO: nope nope nope, fix eval usage try: - unit_var = ast.literal_eval(unit_var_value[0]) - except (ValueError, SyntaxError): + unit_var = json.loads(unit_var_value[0]) + except (ValueError, TypeError): logger.exception( "Failed to eval unit variable value %s", unit_var_value[0] ) diff --git a/src/MCPServer/lib/server/processing_config.py b/src/archivematica/MCPServer/server/processing_config.py similarity index 98% rename from src/MCPServer/lib/server/processing_config.py rename to src/archivematica/MCPServer/server/processing_config.py index b896b7e7ca..c42f0a5f5e 100644 --- a/src/MCPServer/lib/server/processing_config.py +++ b/src/archivematica/MCPServer/server/processing_config.py @@ -9,11 +9,11 @@ import logging import os -import storageService as storage_service from django.conf import settings from lxml import etree -from server.workflow_abilities import choice_is_available +import archivematica.archivematicaCommon.storageService as storage_service +from archivematica.MCPServer.server.workflow_abilities import choice_is_available logger = logging.getLogger("archivematica.mcp.server.processing_config") diff --git a/src/MCPServer/lib/server/queues.py b/src/archivematica/MCPServer/server/queues.py similarity index 98% rename from src/MCPServer/lib/server/queues.py rename to src/archivematica/MCPServer/server/queues.py index a68d692098..5e0b762f74 100644 --- a/src/MCPServer/lib/server/queues.py +++ b/src/archivematica/MCPServer/server/queues.py @@ -9,10 +9,10 @@ from django.conf import settings -from server import metrics -from server.jobs import DecisionJob -from server.packages import DIP -from server.packages import SIP +from archivematica.MCPServer.server import metrics +from archivematica.MCPServer.server.jobs import DecisionJob +from archivematica.MCPServer.server.packages import DIP +from archivematica.MCPServer.server.packages import SIP logger = logging.getLogger("archivematica.mcp.server.queues") diff --git a/src/MCPServer/lib/server/rpc_server.py b/src/archivematica/MCPServer/server/rpc_server.py similarity index 96% rename from src/MCPServer/lib/server/rpc_server.py rename to src/archivematica/MCPServer/server/rpc_server.py index c14090f171..8b4d8ec6b0 100644 --- a/src/MCPServer/lib/server/rpc_server.py +++ b/src/archivematica/MCPServer/server/rpc_server.py @@ -18,19 +18,19 @@ from socket import gethostname import gearman -from dbconns import auto_close_old_connections from django.conf import settings as django_settings from django.db import connection from gearman import GearmanWorker -from gearman_encoder import JSONDataEncoder from lxml import etree -from main.models import SIP -from main.models import Job -from main.models import Transfer -from server.packages import create_package -from server.packages import get_approve_transfer_chain_id -from server.processing_config import get_processing_fields +from archivematica.archivematicaCommon.dbconns import auto_close_old_connections +from archivematica.archivematicaCommon.gearman_encoder import JSONDataEncoder +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import Job +from archivematica.dashboard.main.models import Transfer +from archivematica.MCPServer.server.packages import create_package +from archivematica.MCPServer.server.packages import get_approve_transfer_chain_id +from archivematica.MCPServer.server.processing_config import get_processing_fields logger = logging.getLogger("archivematica.mcp.server.rpc_server") @@ -309,8 +309,7 @@ def _approve_partial_reingest_handler(self, worker, job, payload): ).first() if not job: # No job to be found. raise NotFoundError( - 'There is no "Reingest AIP" job awaiting a' - f" decision for SIP {sip_uuid}" + f'There is no "Reingest AIP" job awaiting a decision for SIP {sip_uuid}' ) not_found = NotFoundError("Could not find choice for approve AIP reingest") try: @@ -514,7 +513,7 @@ def start(workflow, shutdown_event, package_queue, executor): worker.work(poll_timeout=5.0) except gearman.errors.ServerUnavailable as inst: logger.error( - "Gearman server is unavailable: %s. Retrying in %d" " seconds.", + "Gearman server is unavailable: %s. Retrying in %d seconds.", inst.args, fail_sleep, ) diff --git a/src/MCPServer/lib/server/shared_dirs.py b/src/archivematica/MCPServer/server/shared_dirs.py similarity index 94% rename from src/MCPServer/lib/server/shared_dirs.py rename to src/archivematica/MCPServer/server/shared_dirs.py index d359bafedb..e8f8bdd464 100644 --- a/src/MCPServer/lib/server/shared_dirs.py +++ b/src/archivematica/MCPServer/server/shared_dirs.py @@ -4,8 +4,9 @@ import os from django.conf import settings -from processing import BUILTIN_CONFIGS -from processing import install_builtin_config + +from archivematica.archivematicaCommon.processing import BUILTIN_CONFIGS +from archivematica.archivematicaCommon.processing import install_builtin_config logger = logging.getLogger("archivematica.mcp.server.shareddirs") diff --git a/src/archivematica/MCPServer/server/tasks/__init__.py b/src/archivematica/MCPServer/server/tasks/__init__.py new file mode 100644 index 0000000000..abf57bcf47 --- /dev/null +++ b/src/archivematica/MCPServer/server/tasks/__init__.py @@ -0,0 +1,6 @@ +from archivematica.MCPServer.server.tasks.backends import GearmanTaskBackend +from archivematica.MCPServer.server.tasks.backends import TaskBackend +from archivematica.MCPServer.server.tasks.backends import get_task_backend +from archivematica.MCPServer.server.tasks.task import Task + +__all__ = ("GearmanTaskBackend", "Task", "TaskBackend", "get_task_backend") diff --git a/src/MCPServer/lib/server/tasks/backends/__init__.py b/src/archivematica/MCPServer/server/tasks/backends/__init__.py similarity index 77% rename from src/MCPServer/lib/server/tasks/backends/__init__.py rename to src/archivematica/MCPServer/server/tasks/backends/__init__.py index 1d013a132f..e5816360a6 100644 --- a/src/MCPServer/lib/server/tasks/backends/__init__.py +++ b/src/archivematica/MCPServer/server/tasks/backends/__init__.py @@ -4,8 +4,10 @@ import threading -from server.tasks.backends.base import TaskBackend -from server.tasks.backends.gearman_backend import GearmanTaskBackend +from archivematica.MCPServer.server.tasks.backends.base import TaskBackend +from archivematica.MCPServer.server.tasks.backends.gearman_backend import ( + GearmanTaskBackend, +) backend_local = threading.local() diff --git a/src/MCPServer/lib/server/tasks/backends/base.py b/src/archivematica/MCPServer/server/tasks/backends/base.py similarity index 100% rename from src/MCPServer/lib/server/tasks/backends/base.py rename to src/archivematica/MCPServer/server/tasks/backends/base.py diff --git a/src/MCPServer/lib/server/tasks/backends/gearman_backend.py b/src/archivematica/MCPServer/server/tasks/backends/gearman_backend.py similarity index 97% rename from src/MCPServer/lib/server/tasks/backends/gearman_backend.py rename to src/archivematica/MCPServer/server/tasks/backends/gearman_backend.py index 1518a3e4a7..ced79b8e82 100644 --- a/src/MCPServer/lib/server/tasks/backends/gearman_backend.py +++ b/src/archivematica/MCPServer/server/tasks/backends/gearman_backend.py @@ -12,11 +12,11 @@ from gearman.constants import JOB_COMPLETE from gearman.constants import JOB_FAILED from gearman.constants import JOB_UNKNOWN -from gearman_encoder import JSONDataEncoder -from server import metrics -from server.tasks.backends.base import TaskBackend -from server.tasks.task import Task +from archivematica.archivematicaCommon.gearman_encoder import JSONDataEncoder +from archivematica.MCPServer.server import metrics +from archivematica.MCPServer.server.tasks.backends.base import TaskBackend +from archivematica.MCPServer.server.tasks.task import Task logger = logging.getLogger("archivematica.mcp.server.jobs.tasks") diff --git a/src/MCPServer/lib/server/tasks/task.py b/src/archivematica/MCPServer/server/tasks/task.py similarity index 96% rename from src/MCPServer/lib/server/tasks/task.py rename to src/archivematica/MCPServer/server/tasks/task.py index c455c6566b..747c0dfcfb 100644 --- a/src/MCPServer/lib/server/tasks/task.py +++ b/src/archivematica/MCPServer/server/tasks/task.py @@ -9,9 +9,10 @@ import os import uuid -from dbconns import auto_close_old_connections from django.utils import timezone -from main import models + +from archivematica.archivematicaCommon.dbconns import auto_close_old_connections +from archivematica.dashboard.main import models logger = logging.getLogger("archivematica.mcp.server.tasks") diff --git a/src/MCPServer/lib/server/translation.py b/src/archivematica/MCPServer/server/translation.py similarity index 100% rename from src/MCPServer/lib/server/translation.py rename to src/archivematica/MCPServer/server/translation.py diff --git a/src/MCPServer/lib/server/utils.py b/src/archivematica/MCPServer/server/utils.py similarity index 100% rename from src/MCPServer/lib/server/utils.py rename to src/archivematica/MCPServer/server/utils.py diff --git a/src/MCPServer/lib/server/watch_dirs.py b/src/archivematica/MCPServer/server/watch_dirs.py similarity index 100% rename from src/MCPServer/lib/server/watch_dirs.py rename to src/archivematica/MCPServer/server/watch_dirs.py diff --git a/src/MCPServer/lib/server/workflow.py b/src/archivematica/MCPServer/server/workflow.py similarity index 95% rename from src/MCPServer/lib/server/workflow.py rename to src/archivematica/MCPServer/server/workflow.py index 0e697081d1..025f1b8e3b 100644 --- a/src/MCPServer/lib/server/workflow.py +++ b/src/archivematica/MCPServer/server/workflow.py @@ -14,24 +14,22 @@ sets. """ +import importlib.resources import json -import os from django.conf import settings as django_settings from jsonschema import FormatChecker from jsonschema import validate from jsonschema.exceptions import ValidationError -from server.jobs import Job -from server.translation import FALLBACK_LANG -from server.translation import TranslationLabel +from archivematica.MCPServer.server.jobs import Job +from archivematica.MCPServer.server.translation import FALLBACK_LANG +from archivematica.MCPServer.server.translation import TranslationLabel _LATEST_SCHEMA = "workflow-schema-v1.json" -ASSETS_DIR = os.path.join( - os.path.dirname(os.path.dirname(os.path.abspath(os.path.join(__file__)))), "assets" -) +ASSETS_DIR = importlib.resources.files("archivematica.MCPServer") / "assets" -DEFAULT_WORKFLOW = os.path.join(ASSETS_DIR, "workflow.json") +DEFAULT_WORKFLOW = ASSETS_DIR / "workflow.json" def _invert_job_statuses(): @@ -262,6 +260,5 @@ def _validate(blob): def _get_schema(): """Decode the default schema and return it.""" - schema = os.path.join(ASSETS_DIR, _LATEST_SCHEMA) - with open(schema) as fp: + with open(ASSETS_DIR / _LATEST_SCHEMA) as fp: return json.load(fp) diff --git a/src/MCPServer/lib/server/workflow_abilities.py b/src/archivematica/MCPServer/server/workflow_abilities.py similarity index 100% rename from src/MCPServer/lib/server/workflow_abilities.py rename to src/archivematica/MCPServer/server/workflow_abilities.py diff --git a/src/archivematicaCommon/lib/externals/HTML/__init__.py b/src/archivematica/MCPServer/settings/__init__.py similarity index 100% rename from src/archivematicaCommon/lib/externals/HTML/__init__.py rename to src/archivematica/MCPServer/settings/__init__.py diff --git a/src/MCPServer/lib/settings/common.py b/src/archivematica/MCPServer/settings/common.py similarity index 95% rename from src/MCPServer/lib/settings/common.py rename to src/archivematica/MCPServer/settings/common.py index a6e62ff000..a5b324b8ec 100644 --- a/src/MCPServer/lib/settings/common.py +++ b/src/archivematica/MCPServer/settings/common.py @@ -21,10 +21,12 @@ import os from io import StringIO -import email_settings -from appconfig import Config -from appconfig import process_search_enabled -from appconfig import process_watched_directory_interval +from archivematica.archivematicaCommon import email_settings +from archivematica.archivematicaCommon.appconfig import Config +from archivematica.archivematicaCommon.appconfig import process_search_enabled +from archivematica.archivematicaCommon.appconfig import ( + process_watched_directory_interval, +) CONFIG_MAPPING = { # [MCPServer] @@ -204,9 +206,9 @@ INSTALLED_APPS = ( "django.contrib.auth", "django.contrib.contenttypes", - "main", - "components.administration", - "fpr", + "archivematica.dashboard.main", + "archivematica.dashboard.components.administration", + "archivematica.dashboard.fpr", ) # Make this unique, and don't share it with anybody. diff --git a/src/MCPClient/lib/settings/test.py b/src/archivematica/MCPServer/settings/test.py similarity index 95% rename from src/MCPClient/lib/settings/test.py rename to src/archivematica/MCPServer/settings/test.py index d786c2d26c..0c4d027054 100644 --- a/src/MCPClient/lib/settings/test.py +++ b/src/archivematica/MCPServer/settings/test.py @@ -16,7 +16,7 @@ # along with Archivematica. If not, see . """Test settings and globals.""" -from .common import * +from archivematica.MCPServer.settings.common import * DATABASES = { "default": { diff --git a/src/MCPServer/lib/settings/testmysql.py b/src/archivematica/MCPServer/settings/testmysql.py similarity index 85% rename from src/MCPServer/lib/settings/testmysql.py rename to src/archivematica/MCPServer/settings/testmysql.py index 69b4e01c4b..b67ff016bc 100644 --- a/src/MCPServer/lib/settings/testmysql.py +++ b/src/archivematica/MCPServer/settings/testmysql.py @@ -1,4 +1,4 @@ -from .test import * +from archivematica.MCPServer.settings.test import * DATABASES = { "default": { diff --git a/src/archivematicaCommon/README.txt b/src/archivematica/archivematicaCommon/README.txt similarity index 100% rename from src/archivematicaCommon/README.txt rename to src/archivematica/archivematicaCommon/README.txt diff --git a/src/archivematicaCommon/lib/externals/__init__.py b/src/archivematica/archivematicaCommon/__init__.py similarity index 100% rename from src/archivematicaCommon/lib/externals/__init__.py rename to src/archivematica/archivematicaCommon/__init__.py diff --git a/src/archivematica/archivematicaCommon/aip_mets_parser.py b/src/archivematica/archivematicaCommon/aip_mets_parser.py new file mode 100644 index 0000000000..cf4a7a0caa --- /dev/null +++ b/src/archivematica/archivematicaCommon/aip_mets_parser.py @@ -0,0 +1,444 @@ +import calendar +import copy +import os +import re +import time +from collections.abc import Generator +from typing import Any +from typing import Optional + +from lxml import etree + +from archivematica.archivematicaCommon import namespaces as ns +from archivematica.archivematicaCommon.externals import xmltodict +from archivematica.dashboard.main.models import Identifier + + +class AIPMETSParser: + def __init__(self, mets_path: str) -> None: + self.mets_path = mets_path + self.mets = self.parse() + self.dublincore: Optional[etree._Element] = ns.xml_find_premis( + self.mets, "mets:dmdSec/mets:mdWrap/mets:xmlData/dcterms:dublincore" + ) + self.aic_identifier: Optional[str] = None + self.is_part_of: Optional[str] = None + if self.dublincore is not None: + aip_type = ns.xml_findtext_premis( + self.dublincore, "dc:type" + ) or ns.xml_findtext_premis(self.dublincore, "dcterms:type") + if aip_type == "Archival Information Collection": + self.aic_identifier = ns.xml_findtext_premis( + self.dublincore, "dc:identifier" + ) or ns.xml_findtext_premis(self.dublincore, "dcterms:identifier") + elif aip_type == "Archival Information Package": + self.is_part_of = ns.xml_findtext_premis( + self.dublincore, "dcterms:isPartOf" + ) + self.created: int = self.get_create_date() + self.aip_metadata: list[dict[str, Any]] = self._get_aip_metadata() + self.file_count: int = 0 + + def get_create_date(self) -> int: + result = int(time.time()) + # Pull the create time from the METS header. + # Old METS did not use `metsHdr`. + mets_hdr = ns.xml_find_premis(self.mets, "mets:metsHdr") + if mets_hdr is not None: + mets_created_attr = mets_hdr.get("CREATEDATE") + if mets_created_attr: + try: + result = calendar.timegm( + time.strptime(mets_created_attr, "%Y-%m-%dT%H:%M:%S") + ) + except ValueError: + # Failed to parse METS CREATEDATE + pass + return result + + def parse(self) -> etree._Element: + tree = etree.parse(self.mets_path) + root = tree.getroot() + self._remove_tool_output_from_mets(root) + return root + + def _remove_tool_output_from_mets(self, root: etree._Element) -> None: + """Remove tool output from a METS ElementTree. + + Given an ElementTree object, removes all objectsCharacteristicsExtensions + elements. This modifies the existing document in-place; it does not return + a new document. This helps index METS files, which might otherwise get too + large to be usable. + """ + # Remove tool output nodes + toolNodes = ns.xml_findall_premis( + root, + "mets:amdSec/mets:techMD/mets:mdWrap/mets:xmlData/premis:object/premis:objectCharacteristics/premis:objectCharacteristicsExtension", + ) + + for parent in toolNodes: + parent.clear() + + print("Removed FITS output from METS.") + + def _get_aip_metadata(self) -> list[dict[str, Any]]: + """Get metadata about the directories in the AIP. + + Given a doc representing a METS file, look for Directory entries + that have descriptive or administrative metadata in the physical + structMap and return dictionaries with metadata attributes for + each directory. + """ + result: list[dict[str, Any]] = [] + physical_struct_map = ns.xml_find_premis( + self.mets, 'mets:structMap[@TYPE="physical"]' + ) + if physical_struct_map is not None: + for directory in self._get_directories_with_metadata(physical_struct_map): + directory_metadata = self._get_directory_metadata(directory) + if directory_metadata: + result.append(directory_metadata) + return result + + def _get_directories_with_metadata( + self, container: etree._Element + ) -> list[etree._Element]: + """Return Directory entries with metadata sections. + + Check if the entry references dmdSec (descriptive) or amdSec + (administrative) metadata sections. + """ + result = ns.xml_xpath_premis( + container, './/mets:div[@TYPE="Directory"][@DMDID or @ADMID]' + ) + return result if isinstance(result, list) else [] + + def _get_directory_metadata(self, directory: etree._Element) -> dict[str, Any]: + """Get descriptive or administrive metadata for a directory. + + There are three types of metadata elements extracted: + + 1. Dublin core, set through the metadata/metadata.csv file or the + transfer/ingest metadata form in the dashboard + 2. Custom (non dublin core), set through the metadata/metadata.csv + file + 3. Bag/disk image metadata, set through the bag-info.txt file or + the disk image metadata form in the dashboard + 4. Custom XML metadata, set through the source-metadata.csv files + and related XML files + + These types are parsed and combined into a single dictionary of + metadata attributes. A marker element with the label of the + Directory entry is added to the result. + """ + result = {} + elements_with_metadata = [] + dmd_id = directory.attrib.get("DMDID") + if dmd_id: + for dmd_sec in self._get_latest_dmd_secs(dmd_id): + elements_with_metadata += self._get_descriptive_section_metadata( + dmd_sec + ) + for ADMID in directory.attrib.get("ADMID", "").split(): + amd_sec = ns.xml_find_premis(self.mets, f'mets:amdSec[@ID="{ADMID}"]') + if amd_sec is not None: + # look for bag/disk image metadata + elements_with_metadata += ns.xml_findall_premis( + amd_sec, "mets:sourceMD/mets:mdWrap/mets:xmlData/transfer_metadata" + ) + if elements_with_metadata: + # add an attribute with the relative path of the Directory entry + elements_with_metadata.append(self._get_relative_path_element(directory)) + result = self._combine_elements(elements_with_metadata) + return self._normalize_dict(result) + + def _get_relative_path_element(self, directory: etree._Element) -> etree._Element: + """Build an element with the relative path of the directory.""" + TAG = "__DIRECTORY_LABEL__" + result = etree.Element("container") + path = etree.SubElement(result, TAG) + path.text = directory.attrib.get("LABEL", "") + return result + + def files(self) -> Generator[tuple[dict[str, Any], set[str]], None, None]: + # Index all files in a fileGrup with USE='original' or USE='metadata'. + original_files = ns.xml_findall_premis( + self.mets, "mets:fileSec/mets:fileGrp[@USE='original']/mets:file" + ) + metadata_files = ns.xml_findall_premis( + self.mets, "mets:fileSec/mets:fileGrp[@USE='metadata']/mets:file" + ) + files = original_files + metadata_files + self.file_count = len(files) + for file in files: + yield self.get_aip_file_index_data(file) + + def get_aip_file_index_data( + self, file_: etree._Element + ) -> tuple[dict[str, Any], set[str]]: + # Establish the structure to be indexed for each file item. + indexData: dict[str, Any] = { + "FILEUUID": "", + "filePath": "", + "fileExtension": "", + "METS": {"dmdSec": {}, "amdSec": {}}, + "accessionid": "", + } + + accession_ids: set[str] = set() + + # Get file UUID. If an ADMID exists, look in the amdSec for the UUID, + # otherwise parse it out of the file ID. + # 'Original' files have ADMIDs, 'Metadata' files do not. + admID = file_.attrib.get("ADMID", None) + fileUUID: Optional[str] = None + if admID is None: + # Parse UUID from the file ID. + uuix_regex = r"\w{8}-?\w{4}-?\w{4}-?\w{4}-?\w{12}" + uuids = re.findall(uuix_regex, file_.attrib["ID"]) + # Multiple UUIDs may be returned - if they are all identical, + # use that UUID, otherwise use None. + # To determine all UUIDs are identical, use the size of the set. + if len(set(uuids)) == 1: + fileUUID = uuids[0] + else: + amdSec = self._get_amdSec(admID) + fileUUID = self._get_file_uuid(amdSec) + accession_id = self._get_accession_number(amdSec) + if accession_id is not None: + indexData["accessionid"] = accession_id + accession_ids.add(accession_id) + + # Index amdSec information. + if amdSec is not None: + xml = etree.tostring(amdSec, encoding="utf8") + indexData["METS"]["amdSec"] = self._normalize_dict(xmltodict.parse(xml)) + + indexData["FILEUUID"] = fileUUID + + file_metadata: list[dict[str, Any]] = [] + + # Get the parent division for the file pointer by searching the + # physical structural map section (structMap). + file_id = file_.attrib.get("ID", None) + file_pointer_division = ns.xml_find_premis( + self.mets, + f"mets:structMap[@TYPE='physical']//mets:fptr[@FILEID='{file_id}']/..", + ) + if file_pointer_division is not None: + descriptive_metadata = self._get_file_metadata(file_pointer_division) + if descriptive_metadata: + file_metadata.append(descriptive_metadata) + # If the parent division has a DMDID attribute then index + # its data from the descriptive metadata section (dmdSec). + dmd_section_id = file_pointer_division.attrib.get("DMDID", None) + if dmd_section_id is not None: + # dmd_section_id can contain one id (e.g., "dmdSec_2") or + # more than one (e.g., "dmdSec_2 dmdSec_3", when a file + # has both DC and non-DC metadata). + # Attempt to index only the DC dmdSec if available. + for dmd_section_id_item in dmd_section_id.split(): + dmd_section_info = ns.xml_find_premis( + self.mets, + f"mets:dmdSec[@ID='{dmd_section_id_item}']/mets:mdWrap[@MDTYPE='DC']/mets:xmlData", + ) + if dmd_section_info is not None: + xml = etree.tostring(dmd_section_info, encoding="utf8") + data = self._normalize_dict(xmltodict.parse(xml)) + indexData["METS"]["dmdSec"] = data + break + + indexData["transferMetadata"] = file_metadata + + # Get file path from FLocat and extension. + flocat = ns.xml_find_premis(file_, "mets:FLocat") + if flocat is not None: + filePath = flocat.attrib["{http://www.w3.org/1999/xlink}href"] + indexData["filePath"] = filePath + _, fileExtension = os.path.splitext(filePath) + if fileExtension: + indexData["fileExtension"] = fileExtension[1:].lower() + + indexData["identifiers"] = self._get_file_identifiers(fileUUID) + + return indexData, accession_ids + + def _get_amdSec(self, admID: str) -> Optional[etree._Element]: + """Get amdSec with given admID. + + :param admID: admID. + :param doc: ElementTree object. + :return: amdSec. + """ + return ns.xml_find_premis(self.mets, f"mets:amdSec[@ID='{admID}']") + + def _get_file_uuid(self, amdSec: Optional[etree._Element]) -> Optional[str]: + """Get UUID of a file from amdSec. + + :param amdSec: amdSec. + :return: File UUID. + """ + uuid = ns.xml_findtext_premis( + amdSec, + "mets:techMD/mets:mdWrap/mets:xmlData/premis:object/premis:objectIdentifier/premis:objectIdentifierValue", + ) + return uuid if isinstance(uuid, str) else None + + def _get_accession_number(self, amdSec: Optional[etree._Element]) -> Optional[str]: + """Get accession number associated with a file from amdSec. + + Look for a entry within file's amdSec that has a + of "registration". Return the text value (with leading + "accession#" stripped out) from the . + + If no matching entry is found, return None. + + :param amdSec: amdSec. + :return: Accession number or None. + """ + registration_detail_notes = ns.xml_xpath_premis( + amdSec, + ".//premis:event[premis:eventType='registration']/premis:eventOutcomeInformation/premis:eventOutcomeDetail/premis:eventOutcomeDetailNote", + ) + if not registration_detail_notes or not isinstance( + registration_detail_notes, list + ): + return None + if not registration_detail_notes: + return None + element = registration_detail_notes[0] + if not hasattr(element, "text"): + return None + detail_text = element.text + if not isinstance(detail_text, str): + return None + ACCESSION_PREFIX = "accession#" + return detail_text[len(ACCESSION_PREFIX) :] + + def _get_file_metadata( + self, file_pointer_division: etree._Element + ) -> dict[str, Any]: + """Get descriptive metadata for a file pointer. + + There are various types of metadata elements extracted: dublin core + and custom (non dublin core), both set through the metadata/metadata.csv + file; and metadata added through the source-metadata.csv files and + related XML files. + + These types are parsed and combined into a single dictionary of + metadata attributes. + """ + result: dict[str, Any] = {} + elements_with_metadata: list[etree._Element] = [] + dmd_id = file_pointer_division.attrib.get("DMDID") + if not dmd_id: + return result + for dmd_sec in self._get_latest_dmd_secs(dmd_id): + elements_with_metadata += self._get_descriptive_section_metadata(dmd_sec) + if elements_with_metadata: + result = self._combine_elements(elements_with_metadata) + return self._normalize_dict(result) + + def _get_latest_dmd_secs(self, dmd_id: str) -> list[etree._Element]: + # Build a mapping of dmdSec by metadata type. + dmd_secs: dict[str, list[etree._Element]] = {} + for id in dmd_id.split(): + dmd_sec = ns.xml_find_premis(self.mets, f'mets:dmdSec[@ID="{id}"]') + if not dmd_sec: + continue + # Use mdWrap MDTYPE and OTHERMDTYPE to generate a unique key. + md_wrap = ns.xml_find_premis(dmd_sec, "mets:mdWrap") + if not md_wrap: + continue + mdtype_key = md_wrap.attrib.get("MDTYPE") + # Ignore PREMIS:OBJECT. + if mdtype_key == "PREMIS:OBJECT": + continue + if mdtype_key is None: + continue + other_mdtype = md_wrap.attrib.get("OTHERMDTYPE") + if other_mdtype: + mdtype_key += "_" + other_mdtype + if mdtype_key not in dmd_secs: + dmd_secs[mdtype_key] = [] + dmd_secs[mdtype_key].append(dmd_sec) + # Get only the latest dmdSec by type. + final_dmd_secs: list[etree._Element] = [] + for _, secs in dmd_secs.items(): + latest_date = "" + latest_sec: Optional[etree._Element] = None + for sec in secs: + date = sec.attrib.get("CREATED", "") + if not latest_date or date > latest_date: + latest_date = date + latest_sec = sec + # Do not add latest dmdSec if it's deleted. + if latest_sec and latest_sec.attrib.get("STATUS", "") != "deleted": + final_dmd_secs.append(latest_sec) + return final_dmd_secs + + def _get_descriptive_section_metadata( + self, dmdSec: etree._Element + ) -> list[etree._Element]: + """Get dublin core and custom descriptive metadata.""" + result: list[etree._Element] = [] + # look for dublincore terms in the dmdSec + result += ns.xml_findall_premis( + dmdSec, 'mets:mdWrap[@MDTYPE="DC"]/mets:xmlData/dcterms:dublincore' + ) + # look for non dublincore (custom) metadata + result += ns.xml_findall_premis( + dmdSec, 'mets:mdWrap[@MDTYPE="OTHER"]/mets:xmlData' + ) + return result + + def _combine_elements(self, elements: list[etree._Element]) -> dict[str, Any]: + """Serialize all the elements as a JSON compatible string. + + Combine the elements contents into a single container and parse it + with xmltodict. + """ + # wrap the data from all metadata elements in a container + container = etree.Element("container") + for element in elements: + for child in element: + # add a copy to not modify the METS file in place + container.append(copy.deepcopy(child)) + # parse the container with xmltodict ignoring element attributes + return ( + xmltodict.parse( + etree.tostring(container, encoding="utf8"), xml_attribs=False + ).get("container") + or {} + ) + + def _normalize_dict(self, data: dict[str, Any]) -> dict[str, Any]: + """Normalize dictionary from xmltodict for ES indexing. + + Because an XML element may contain text value or other elements, conversion + to a dict can result in different value types for the same key. This causes + problems in the Elasticsearch index as it expects consistent types. This + function recurses a dict and appends "_dict" to the keys with a dict value. + """ + new: dict[str, Any] = {} + for key, value in data.items(): + dict_key = key + "_dict" + if isinstance(value, dict): + new[dict_key] = self._normalize_dict(value) + elif isinstance(value, list): + for item in value: + new_key = key + if isinstance(item, dict): + new_key = dict_key + item = self._normalize_dict(item) + if new_key not in new: + new[new_key] = [] + new[new_key].append(item) + else: + new[key] = value + return new + + def _get_file_identifiers(self, uuid: Optional[str]) -> list[str]: + return list( + Identifier.objects.filter(file=uuid).values_list("value", flat=True) + ) diff --git a/src/archivematicaCommon/lib/appconfig.py b/src/archivematica/archivematicaCommon/appconfig.py similarity index 96% rename from src/archivematicaCommon/lib/appconfig.py rename to src/archivematica/archivematicaCommon/appconfig.py index c08de919e1..8ca14ea7d1 100644 --- a/src/archivematicaCommon/lib/appconfig.py +++ b/src/archivematica/archivematicaCommon/appconfig.py @@ -1,9 +1,9 @@ """ Configuration helper for MCPServer, MCPClient and Dashboard, used in: - - MCPClient/lib/settings/common.py - - MCPServer/lib/settings/common.py - - Dashboard/src/settings/base.py + - MCPClient/settings/common.py + - MCPServer/settings/common.py + - dashboard/settings/base.py Config. attributes are declared on those settings files and they can be defined by a dictionary indicating the 'section', 'option' and 'type' to be parsed by @@ -17,7 +17,8 @@ import configparser from django.core.exceptions import ImproperlyConfigured -from env_configparser import EnvConfigParser + +from archivematica.archivematicaCommon.env_configparser import EnvConfigParser class Config: diff --git a/src/archivematicaCommon/lib/archivematicaFunctions.py b/src/archivematica/archivematicaCommon/archivematicaFunctions.py similarity index 80% rename from src/archivematicaCommon/lib/archivematicaFunctions.py rename to src/archivematica/archivematicaCommon/archivematicaFunctions.py index 6dfa48c867..55532d8dad 100644 --- a/src/archivematicaCommon/lib/archivematicaFunctions.py +++ b/src/archivematica/archivematicaCommon/archivematicaFunctions.py @@ -23,21 +23,23 @@ import errno import glob import hashlib +import json import locale import os import pprint import re +from collections.abc import Iterable from itertools import zip_longest from pathlib import Path -from typing import Dict -from typing import Iterable +from typing import Union from uuid import uuid4 from amclient import AMClient from django.apps import apps from lxml import etree -from namespaces import NSMAP -from namespaces import xml_find_premis + +from archivematica.archivematicaCommon.namespaces import NSMAP +from archivematica.archivematicaCommon.namespaces import xml_find_premis REQUIRED_DIRECTORIES = ( "logs", @@ -528,9 +530,13 @@ def chunk_iterable(iterable, chunk_size=10, fillvalue=None): return zip_longest(*args, fillvalue=fillvalue) +ProviderConfig = dict[str, Union[str, bool]] + + def get_oidc_secondary_providers( oidc_secondary_provider_names: Iterable[str], -) -> Dict[str, Dict[str, str]]: + default_oidc_claims: dict[str, str], +) -> dict[str, ProviderConfig]: """Build secondary OIDC provider details dict. Takes a list of secondary OIDC providers and gathers details about these providers from env vars. Output dict contains details for each OIDC connection which can then be @@ -550,9 +556,35 @@ def get_oidc_secondary_providers( user_endpoint = os.environ.get(f"OIDC_OP_USER_ENDPOINT_{provider_name}", "") jwks_endpoint = os.environ.get(f"OIDC_OP_JWKS_ENDPOINT_{provider_name}", "") logout_endpoint = os.environ.get(f"OIDC_OP_LOGOUT_ENDPOINT_{provider_name}", "") + set_roles_from_claims = os.environ.get( + f"OIDC_OP_SET_ROLES_FROM_CLAIMS_{provider_name}", "" + ).lower() in ( + "true", + "yes", + "on", + "1", + ) + role_claim_path = os.environ.get( + f"OIDC_OP_ROLE_CLAIM_PATH_{provider_name}", "realm_access.roles" + ) + role_claim_admin = os.environ.get( + f"OIDC_ROLE_CLAIM_ADMIN_{provider_name}", "admin" + ) + role_claim_default = os.environ.get( + f"OIDC_ROLE_CLAIM_DEFAULT_{provider_name}", "default" + ) + try: + access_attribute_map = json.loads( + os.environ.get( + f"OIDC_ACCESS_ATTRIBUTE_MAP_{provider_name}", + json.dumps(default_oidc_claims), + ) + ) + except json.JSONDecodeError: + access_attribute_map = default_oidc_claims if client_id and client_secret: - providers[provider_name] = { + provider_config: ProviderConfig = { "OIDC_RP_CLIENT_ID": client_id, "OIDC_RP_CLIENT_SECRET": client_secret, "OIDC_OP_AUTHORIZATION_ENDPOINT": authorization_endpoint, @@ -560,6 +592,111 @@ def get_oidc_secondary_providers( "OIDC_OP_USER_ENDPOINT": user_endpoint, "OIDC_OP_JWKS_ENDPOINT": jwks_endpoint, "OIDC_OP_LOGOUT_ENDPOINT": logout_endpoint, + "OIDC_OP_SET_ROLES_FROM_CLAIMS": set_roles_from_claims, + "OIDC_OP_ROLE_CLAIM_PATH": role_claim_path, + "OIDC_ACCESS_ATTRIBUTE_MAP": access_attribute_map, + "OIDC_ROLE_CLAIM_ADMIN": role_claim_admin, + "OIDC_ROLE_CLAIM_DEFAULT": role_claim_default, } + providers[provider_name] = provider_config return providers + + +def jhove_validation_command(module): + NEW_JHOVE_VALIDATION_CMD = r""" +import json +import subprocess +import sys + +from lxml import etree +from archivematica.archivematicaCommon.namespaces import jhoveBNS + +class JhoveException(Exception): + pass + +def parse_jhove_data(target): + args = ['jhove', '-h', 'xml', '-m', '%s', target] + try: + output = subprocess.check_output(args).decode("utf8") + except subprocess.CalledProcessError: + raise JhoveException("Jhove failed when running: " + ' '.join(args)) + + return etree.fromstring(output.encode("utf8")) + +def get_status(doc): + status = doc.find(f".{jhoveBNS}repInfo/{jhoveBNS}status") + if status is None: + raise JhoveException("Unable to find status!") + + return status.text + +def get_outcome(status, format=None): + # JHOVE returns "bytestream" for unrecognized file formats. + # That can include unrecognized or malformed PDFs, JPEG2000s, etc. + # Since we're whitelisting the formats we're passing in, + # "bytestream" indicates that the format is not in fact well-formed + # regardless of what the status reads. + if format == "bytestream": + return "partial pass" + + if status == "Well-Formed and valid": + return "pass" + elif status == "Well-Formed, but not valid": + return "partial pass" + else: + return "fail" + +def get_format(doc): + format = doc.find(f".{jhoveBNS}repInfo/{jhoveBNS}format") + version = doc.find(f".{jhoveBNS}repInfo/{jhoveBNS}version") + + if format is None: + format = "Not detected" + else: + format = format.text + + if version is not None: + version = version.text + + return (format, version) + +def get_errors(doc): + errors = doc.findall(f'.{jhoveBNS}repInfo/{jhoveBNS}messages/{jhoveBNS}message[@severity="error"]') + messages = [f"JHOVE error: {error.get('id')}, message: {error.text}" for error in errors] + return '; '.join(messages) + +def format_event_outcome_detail_note(format, version, result, errors): + note = 'format="{}";'.format(format) + if version is not None: + note = note + ' version="{}";'.format(version) + note = note + ' result="{}"'.format(result) + if errors: + note = note + '; error="{}"'.format(errors) + + return note + +def main(target): + try: + doc = parse_jhove_data(target) + status = get_status(doc) + format, version = get_format(doc) + outcome = get_outcome(status, format) + errors = get_errors(doc) + note = format_event_outcome_detail_note(format, version, status, errors) + + out = { + "eventOutcomeInformation": outcome, + "eventOutcomeDetailNote": note + } + print(json.dumps(out)) + + return 0 + except JhoveException as e: + return e + +if __name__ == '__main__': + target = sys.argv[1] + sys.exit(main(target)) +""" + return NEW_JHOVE_VALIDATION_CMD % module diff --git a/src/archivematicaCommon/lib/bag.py b/src/archivematica/archivematicaCommon/bag.py similarity index 100% rename from src/archivematicaCommon/lib/bag.py rename to src/archivematica/archivematicaCommon/bag.py diff --git a/src/archivematicaCommon/lib/bindpid.py b/src/archivematica/archivematicaCommon/bindpid.py similarity index 98% rename from src/archivematicaCommon/lib/bindpid.py rename to src/archivematica/archivematicaCommon/bindpid.py index 7f4daf3b41..28276f4cf2 100755 --- a/src/archivematicaCommon/lib/bindpid.py +++ b/src/archivematica/archivematicaCommon/bindpid.py @@ -383,15 +383,14 @@ def _add_parser_args(parser): "desired_pid", type=str, action="store", - help="The PID that you want to create or whose resolve" - " URL you want to modify.", + help="The PID that you want to create or whose resolve URL you want to modify.", default=None, ) parser.add_argument( "entity_type", type=str, action="store", - help="The type of entity---file or unit---that you want" " to create a PID for", + help="The type of entity---file or unit---that you want to create a PID for", choices=tuple(ENTITY_TYPES.keys()), default=None, ) @@ -401,9 +400,7 @@ def _add_parser_args(parser): type=str, action="store", dest="naming_authority", - help="A handle naming" - " authority that you can create PIDs under, e.g.," - " 12345.", + help="A handle naming authority that you can create PIDs under, e.g., 12345.", ) parser.add_argument( "-e", @@ -453,7 +450,7 @@ def _add_parser_args(parser): type=str, action="store", dest="resolve_url_template_archive", - help="Template for the URL that a DIP's PURL should" " resolve to", + help="Template for the URL that a DIP's PURL should resolve to", default=None, ) parser.add_argument( diff --git a/src/archivematicaCommon/lib/common_metrics.py b/src/archivematica/archivematicaCommon/common_metrics.py similarity index 100% rename from src/archivematicaCommon/lib/common_metrics.py rename to src/archivematica/archivematicaCommon/common_metrics.py diff --git a/src/archivematicaCommon/lib/countryCodes.py b/src/archivematica/archivematicaCommon/countryCodes.py similarity index 100% rename from src/archivematicaCommon/lib/countryCodes.py rename to src/archivematica/archivematicaCommon/countryCodes.py diff --git a/src/archivematicaCommon/lib/custom_handlers.py b/src/archivematica/archivematicaCommon/custom_handlers.py similarity index 100% rename from src/archivematicaCommon/lib/custom_handlers.py rename to src/archivematica/archivematicaCommon/custom_handlers.py diff --git a/src/archivematicaCommon/lib/databaseFunctions.py b/src/archivematica/archivematicaCommon/databaseFunctions.py similarity index 64% rename from src/archivematicaCommon/lib/databaseFunctions.py rename to src/archivematica/archivematicaCommon/databaseFunctions.py index de31eb5067..f9c783e94e 100644 --- a/src/archivematicaCommon/lib/databaseFunctions.py +++ b/src/archivematica/archivematicaCommon/databaseFunctions.py @@ -14,17 +14,25 @@ # # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . +import datetime import logging +import os import sys import uuid +from dataclasses import dataclass +from django.db.models import Min +from django.db.models import Q from django.utils import timezone -from main.models import SIP -from main.models import Agent -from main.models import Derivation -from main.models import Event -from main.models import File -from main.models import FPCommandOutput + +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import Agent +from archivematica.dashboard.main.models import Derivation +from archivematica.dashboard.main.models import Event +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import FPCommandOutput +from archivematica.dashboard.main.models import Identifier +from archivematica.dashboard.main.models import Transfer LOGGER = logging.getLogger("archivematica.common") @@ -268,3 +276,154 @@ def deUnicode(unicode_string): if unicode_string is None: return None return str(unicode_string).encode("utf-8") + + +def get_transfer_details(transfer_uuid): + transfer_name, accession_id, ingest_date = "", "", str(datetime.date.today()) + try: + transfer = Transfer.objects.get(uuid=transfer_uuid) + except Transfer.DoesNotExist: + pass + else: + transfer_name = transfer.currentlocation.split("/")[-2] + if transfer.accessionid: + accession_id = transfer.accessionid + # It doesn't seem that Archivematica records the ingestion date + # associated with the Transfer but we can look at the earliest file + # entry instead - as long as there is a match which may not always be + # the case. + dt = File.objects.filter(transfer=transfer).aggregate(Min("enteredsystem"))[ + "enteredsystem__min" + ] + if dt: + ingest_date = str(dt.date()) + + return transfer_name, accession_id, ingest_date + + +def get_sip_identifiers(uuid): + # Also index Directory identifiers so the AIP can be found through them + return list( + Identifier.objects.filter(Q(sip=uuid) | Q(directory__sip=uuid)).values_list( + "value", flat=True + ) + ) + + +def _list_files_in_dir(path, filepaths=None): + if filepaths is None: + filepaths = [] + + # Define entries + for file in os.listdir(path): + child_path = os.path.join(path, file) + filepaths.append(child_path) + + # If entry is a directory, recurse + if os.path.isdir(child_path) and os.access(child_path, os.R_OK): + _list_files_in_dir(child_path, filepaths) + + # Return fully traversed data + return filepaths + + +@dataclass +class TransferFileIndexData: + file_paths: list[str] + files_by_location: dict[str, File] + bulk_extractor_reports: dict[str, list[str]] + format_cache: dict[str, list[dict]] + + +def build_transfer_file_index_data( + path: str, transfer_name: str, uuid: str, printfn +) -> TransferFileIndexData: + """Prepare file paths, database lookups, and bulk extractor cache for transfer files. + + :param path: Transfer path on disk + :param transfer_name: Name of the transfer + :param uuid: Transfer UUID + :param printfn: Print function for logging + :return: TransferFileIndexData containing file_paths, files_by_location, bulk_extractor_reports, format_cache + """ + ignore_files = ["processingMCP.xml"] + file_paths = [] + currentlocations_to_encode = [] + currentlocation_to_filepath = {} + + # Step 1: Single directory traversal - gather file paths and currentlocations + for filepath in _list_files_in_dir(path): + if not os.path.isfile(filepath): + continue + + filename = os.path.basename(filepath) + if filename in ignore_files: + stripped_path = filepath.replace(path, transfer_name + "/") + printfn(f"Skipping indexing {stripped_path}") + continue + + # Compute currentlocation for database lookup + currentlocation = "%transferDirectory%" + os.path.relpath( + filepath, path + ).removeprefix("data/") + + file_paths.append(filepath) + currentlocations_to_encode.append(currentlocation.encode()) + currentlocation_to_filepath[currentlocation] = filepath + + # Step 2: Batch database query for all files with optimized joins + if currentlocations_to_encode: + file_records = File.objects.filter( + currentlocation__in=currentlocations_to_encode, transfer_id=uuid + ).prefetch_related( + "fileformatversion_set__format_version", + "fileformatversion_set__format_version__format__group", + ) + + # Create lookup map from currentlocation to File record + files_by_location = {f.currentlocation.decode(): f for f in file_records} + + else: + files_by_location = {} + + # Step 3 & 4: Single traversal for format cache and bulk extractor reports + format_cache = {} + bulk_extractor_reports: dict[str, list[str]] = {} + logs_dir = os.path.join(path, "data", "logs") + logs_dir_exists = os.path.isdir(logs_dir) + + for f in files_by_location.values(): + file_uuid = str(f.uuid) + + # Build format cache + formats = [] + for format_version in f.fileformatversion_set.all(): + formats.append( + { + "puid": format_version.format_version.pronom_id, + "format": format_version.format_version.description, + "group": format_version.format_version.format.group.description, + } + ) + format_cache[file_uuid] = formats + + # Build bulk extractor reports + log_path = os.path.join(logs_dir, "bulk-" + file_uuid) + if not file_uuid or not logs_dir_exists or not os.path.isdir(log_path): + bulk_extractor_reports[file_uuid] = [] + continue + + reports = [] + for report in ["telephone", "ccn", "ccn_track2", "pii"]: + report_path = os.path.join(log_path, report + ".txt") + if os.path.isfile(report_path) and os.path.getsize(report_path) > 0: + reports.append(report) + + bulk_extractor_reports[file_uuid] = reports + + return TransferFileIndexData( + file_paths=file_paths, + files_by_location=files_by_location, + bulk_extractor_reports=bulk_extractor_reports, + format_cache=format_cache, + ) diff --git a/src/archivematicaCommon/lib/dbconns.py b/src/archivematica/archivematicaCommon/dbconns.py similarity index 97% rename from src/archivematicaCommon/lib/dbconns.py rename to src/archivematica/archivematicaCommon/dbconns.py index bd5b9a5060..dd12cab88c 100644 --- a/src/archivematicaCommon/lib/dbconns.py +++ b/src/archivematica/archivematicaCommon/dbconns.py @@ -21,7 +21,6 @@ import threading import traceback from contextlib import ContextDecorator -from typing import Type from django.conf import settings from django.db import close_old_connections @@ -85,7 +84,7 @@ def emit(self, record): ) -auto_close_old_connections: Type[AutoCloseOldConnections] +auto_close_old_connections: type[AutoCloseOldConnections] if settings.DEBUG: logger.debug("Using DEBUG auto_close_old_connections") diff --git a/src/archivematicaCommon/lib/dicts.py b/src/archivematica/archivematicaCommon/dicts.py similarity index 94% rename from src/archivematicaCommon/lib/dicts.py rename to src/archivematica/archivematicaCommon/dicts.py index 18218dc17b..d3c2d534ef 100644 --- a/src/archivematicaCommon/lib/dicts.py +++ b/src/archivematica/archivematicaCommon/dicts.py @@ -14,12 +14,11 @@ # # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . -import ast import os import re import uuid -from main import models +from archivematica.dashboard.main import models config = {} @@ -45,16 +44,6 @@ def replace_string_values(string, **kwargs): class ReplacementDict(dict): - @staticmethod - def fromstring(s): - """ - Create a new ReplacementDict given a string representing a - serialized Python dict. This is commonly used within the - MCPServer, where unit variables are frequently dicts stored - in the database. - """ - return ReplacementDict(ast.literal_eval(s)) - @staticmethod def frommodel(type_="file", sip=None, file_=None, expand_path=True): """ @@ -230,12 +219,3 @@ def to_gnu_options(self): args.append(f"--{optname}={value}") return args - - -class ChoicesDict(ReplacementDict): - @staticmethod - def fromstring(s): - """ - See ReplacementDict.fromstring. - """ - return ChoicesDict(ast.literal_eval(s)) diff --git a/src/archivematicaCommon/lib/email_settings.py b/src/archivematica/archivematicaCommon/email_settings.py similarity index 100% rename from src/archivematicaCommon/lib/email_settings.py rename to src/archivematica/archivematicaCommon/email_settings.py diff --git a/src/archivematicaCommon/lib/env_configparser.py b/src/archivematica/archivematicaCommon/env_configparser.py similarity index 100% rename from src/archivematicaCommon/lib/env_configparser.py rename to src/archivematica/archivematicaCommon/env_configparser.py diff --git a/src/archivematicaCommon/lib/executeOrRunSubProcess.py b/src/archivematica/archivematicaCommon/executeOrRunSubProcess.py similarity index 97% rename from src/archivematicaCommon/lib/executeOrRunSubProcess.py rename to src/archivematica/archivematicaCommon/executeOrRunSubProcess.py index 0b8ada6cf5..e62600c292 100644 --- a/src/archivematicaCommon/lib/executeOrRunSubProcess.py +++ b/src/archivematica/archivematicaCommon/executeOrRunSubProcess.py @@ -21,19 +21,16 @@ import subprocess import sys import tempfile -from typing import Dict -from typing import List from typing import Optional -from typing import Tuple from typing import Union -from archivematicaFunctions import escape +from archivematica.archivematicaCommon.archivematicaFunctions import escape -Arguments = List[str] +Arguments = list[str] Input = Union[str, bytes, io.IOBase] -Environment = Dict[str, str] -Command = Union[str, List[str]] -Result = Tuple[int, str, str] +Environment = dict[str, str] +Command = Union[str, list[str]] +Result = tuple[int, str, str] def launchSubProcess( diff --git a/src/archivematicaCommon/lib/externals/HTML/HTML.py b/src/archivematica/archivematicaCommon/externals/HTML/HTML.py similarity index 100% rename from src/archivematicaCommon/lib/externals/HTML/HTML.py rename to src/archivematica/archivematicaCommon/externals/HTML/HTML.py diff --git a/src/archivematicaCommon/lib/externals/HTML/Licence_CeCILL_V2-en.html b/src/archivematica/archivematicaCommon/externals/HTML/Licence_CeCILL_V2-en.html similarity index 100% rename from src/archivematicaCommon/lib/externals/HTML/Licence_CeCILL_V2-en.html rename to src/archivematica/archivematicaCommon/externals/HTML/Licence_CeCILL_V2-en.html diff --git a/src/archivematicaCommon/lib/externals/HTML/Licence_CeCILL_V2-fr.html b/src/archivematica/archivematicaCommon/externals/HTML/Licence_CeCILL_V2-fr.html similarity index 100% rename from src/archivematicaCommon/lib/externals/HTML/Licence_CeCILL_V2-fr.html rename to src/archivematica/archivematicaCommon/externals/HTML/Licence_CeCILL_V2-fr.html diff --git a/src/dashboard/src/__init__.py b/src/archivematica/archivematicaCommon/externals/HTML/__init__.py similarity index 100% rename from src/dashboard/src/__init__.py rename to src/archivematica/archivematicaCommon/externals/HTML/__init__.py diff --git a/src/dashboard/src/components/__init__.py b/src/archivematica/archivematicaCommon/externals/__init__.py similarity index 100% rename from src/dashboard/src/components/__init__.py rename to src/archivematica/archivematicaCommon/externals/__init__.py diff --git a/src/archivematicaCommon/lib/externals/extractMaildirAttachments.py b/src/archivematica/archivematicaCommon/externals/extractMaildirAttachments.py similarity index 100% rename from src/archivematicaCommon/lib/externals/extractMaildirAttachments.py rename to src/archivematica/archivematicaCommon/externals/extractMaildirAttachments.py diff --git a/src/archivematicaCommon/lib/externals/fido/archivematica_format_extensions.xml b/src/archivematica/archivematicaCommon/externals/fido/archivematica_format_extensions.xml similarity index 100% rename from src/archivematicaCommon/lib/externals/fido/archivematica_format_extensions.xml rename to src/archivematica/archivematicaCommon/externals/fido/archivematica_format_extensions.xml diff --git a/src/archivematicaCommon/lib/externals/maildirToMbox.py b/src/archivematica/archivematicaCommon/externals/maildirToMbox.py similarity index 100% rename from src/archivematicaCommon/lib/externals/maildirToMbox.py rename to src/archivematica/archivematicaCommon/externals/maildirToMbox.py diff --git a/src/archivematicaCommon/lib/externals/xmltodict.py b/src/archivematica/archivematicaCommon/externals/xmltodict.py similarity index 100% rename from src/archivematicaCommon/lib/externals/xmltodict.py rename to src/archivematica/archivematicaCommon/externals/xmltodict.py diff --git a/src/archivematicaCommon/lib/fileOperations.py b/src/archivematica/archivematicaCommon/fileOperations.py similarity index 96% rename from src/archivematicaCommon/lib/fileOperations.py rename to src/archivematica/archivematicaCommon/fileOperations.py index 1371b8c683..f4ca83ef6e 100644 --- a/src/archivematicaCommon/lib/fileOperations.py +++ b/src/archivematica/archivematicaCommon/fileOperations.py @@ -22,13 +22,13 @@ import uuid from pathlib import Path -from archivematicaFunctions import get_file_checksum -from archivematicaFunctions import get_setting -from databaseFunctions import insertIntoEvents -from databaseFunctions import insertIntoFiles -from executeOrRunSubProcess import executeOrRun -from main.models import File -from main.models import Transfer +from archivematica.archivematicaCommon.archivematicaFunctions import get_file_checksum +from archivematica.archivematicaCommon.archivematicaFunctions import get_setting +from archivematica.archivematicaCommon.databaseFunctions import insertIntoEvents +from archivematica.archivematicaCommon.databaseFunctions import insertIntoFiles +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import Transfer def get_size_and_checksum(file_path, file_size=None, checksum=None, checksum_type=None): diff --git a/src/archivematicaCommon/lib/gearman_encoder.py b/src/archivematica/archivematicaCommon/gearman_encoder.py similarity index 100% rename from src/archivematicaCommon/lib/gearman_encoder.py rename to src/archivematica/archivematicaCommon/gearman_encoder.py diff --git a/src/archivematicaCommon/lib/identifier_functions.py b/src/archivematica/archivematicaCommon/identifier_functions.py similarity index 91% rename from src/archivematicaCommon/lib/identifier_functions.py rename to src/archivematica/archivematicaCommon/identifier_functions.py index 549996a7ee..4b6ae4f04a 100644 --- a/src/archivematicaCommon/lib/identifier_functions.py +++ b/src/archivematica/archivematicaCommon/identifier_functions.py @@ -1,7 +1,7 @@ """ Functions to fetch identifiers for indexing in ElasticSearch. -See also src/MCPClient/lib/clientScripts/index_aip.py where these are used. +See also src/MCPClient/clientScripts/index_aip.py where these are used. """ from lxml import etree diff --git a/src/archivematicaCommon/lib/namespaces.py b/src/archivematica/archivematicaCommon/namespaces.py similarity index 95% rename from src/archivematicaCommon/lib/namespaces.py rename to src/archivematica/archivematicaCommon/namespaces.py index 012e56f3f6..061435b39e 100644 --- a/src/archivematicaCommon/lib/namespaces.py +++ b/src/archivematica/archivematicaCommon/namespaces.py @@ -9,6 +9,7 @@ premisNS_V2 = "info:lc/xmlns/premis-v2" xlinkNS = "http://www.w3.org/1999/xlink" xsiNS = "http://www.w3.org/2001/XMLSchema-instance" +jhoveNS = "http://schema.openpreservation.org/ois/xml/ns/jhove" dcBNS = "{" + dcNS + "}" dctermsBNS = "{" + dctermsNS + "}" @@ -19,6 +20,7 @@ premisBNS_V2 = "{" + premisNS_V2 + "}" xlinkBNS = "{" + xlinkNS + "}" xsiBNS = "{" + xsiNS + "}" +jhoveBNS = "{" + jhoveNS + "}" NSMAP = { "dc": dcNS, @@ -29,6 +31,7 @@ "premis": premisNS, "xlink": xlinkNS, "xsi": xsiNS, + "jhove": jhoveNS, } diff --git a/src/archivematicaCommon/lib/processing.py b/src/archivematica/archivematicaCommon/processing.py similarity index 100% rename from src/archivematicaCommon/lib/processing.py rename to src/archivematica/archivematicaCommon/processing.py diff --git a/src/archivematicaCommon/lib/storageService.py b/src/archivematica/archivematicaCommon/storageService.py similarity index 99% rename from src/archivematicaCommon/lib/storageService.py rename to src/archivematica/archivematicaCommon/storageService.py index dfc038a4f0..f9a5cc0400 100644 --- a/src/archivematicaCommon/lib/storageService.py +++ b/src/archivematica/archivematicaCommon/storageService.py @@ -3,12 +3,13 @@ import platform import urllib -import archivematicaFunctions as am import requests -from common_metrics import ss_api_timer from django.conf import settings as django_settings from requests.auth import AuthBase +from archivematica.archivematicaCommon import archivematicaFunctions as am +from archivematica.archivematicaCommon.common_metrics import ss_api_timer + LOGGER = logging.getLogger("archivematica.common") diff --git a/src/archivematicaCommon/lib/version.py b/src/archivematica/archivematicaCommon/version.py similarity index 93% rename from src/archivematicaCommon/lib/version.py rename to src/archivematica/archivematicaCommon/version.py index 574134445b..1f69c7b4c6 100644 --- a/src/archivematicaCommon/lib/version.py +++ b/src/archivematica/archivematicaCommon/version.py @@ -1,4 +1,4 @@ -ARCHIVEMATICA_VERSION = (1, 17, 1) +ARCHIVEMATICA_VERSION = (1, 18, 0) def get_version(): diff --git a/src/archivematicaCommon/lib/xml2obj.py b/src/archivematica/archivematicaCommon/xml2obj.py similarity index 100% rename from src/archivematicaCommon/lib/xml2obj.py rename to src/archivematica/archivematicaCommon/xml2obj.py diff --git a/src/dashboard/src/components/access/__init__.py b/src/archivematica/dashboard/__init__.py similarity index 100% rename from src/dashboard/src/components/access/__init__.py rename to src/archivematica/dashboard/__init__.py diff --git a/src/dashboard/src/components/accounts/__init__.py b/src/archivematica/dashboard/components/__init__.py similarity index 100% rename from src/dashboard/src/components/accounts/__init__.py rename to src/archivematica/dashboard/components/__init__.py diff --git a/src/dashboard/src/components/administration/__init__.py b/src/archivematica/dashboard/components/access/__init__.py similarity index 100% rename from src/dashboard/src/components/administration/__init__.py rename to src/archivematica/dashboard/components/access/__init__.py diff --git a/src/dashboard/src/components/access/urls.py b/src/archivematica/dashboard/components/access/urls.py similarity index 95% rename from src/dashboard/src/components/access/urls.py rename to src/archivematica/dashboard/components/access/urls.py index 5e9937fa16..eea9d7bb45 100644 --- a/src/dashboard/src/components/access/urls.py +++ b/src/archivematica/dashboard/components/access/urls.py @@ -1,7 +1,8 @@ -from components.access import views from django.urls import path from django.urls import re_path +from archivematica.dashboard.components.access import views + app_name = "access" urlpatterns = [ path("archivesspace/", views.all_records), diff --git a/src/dashboard/src/components/access/views.py b/src/archivematica/dashboard/components/access/views.py similarity index 96% rename from src/dashboard/src/components/access/views.py rename to src/archivematica/dashboard/components/access/views.py index 5ac3b35045..3cc193ddb1 100644 --- a/src/dashboard/src/components/access/views.py +++ b/src/archivematica/dashboard/components/access/views.py @@ -4,20 +4,21 @@ import uuid from functools import wraps -import archivematicaFunctions -import components.filesystem_ajax.views as filesystem_views import django.http from agentarchives.archivesspace import ArchivesSpaceError from agentarchives.archivesspace import AuthenticationError -from components import helpers -from components.ingest.views_as import get_as_system_client from django.shortcuts import redirect from django.utils import timezone -from main.models import SIP -from main.models import ArchivesSpaceDigitalObject -from main.models import DublinCore -from main.models import SIPArrange -from main.models import SIPArrangeAccessMapping + +import archivematica.dashboard.components.filesystem_ajax.views as filesystem_views +from archivematica.archivematicaCommon import archivematicaFunctions +from archivematica.dashboard.components import helpers +from archivematica.dashboard.components.ingest.views_as import get_as_system_client +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import ArchivesSpaceDigitalObject +from archivematica.dashboard.main.models import DublinCore +from archivematica.dashboard.main.models import SIPArrange +from archivematica.dashboard.main.models import SIPArrangeAccessMapping logger = logging.getLogger("archivematica.dashboard") diff --git a/src/dashboard/src/components/administration/migrations/__init__.py b/src/archivematica/dashboard/components/accounts/__init__.py similarity index 100% rename from src/dashboard/src/components/administration/migrations/__init__.py rename to src/archivematica/dashboard/components/accounts/__init__.py diff --git a/src/dashboard/src/components/accounts/apps.py b/src/archivematica/dashboard/components/accounts/apps.py similarity index 64% rename from src/dashboard/src/components/accounts/apps.py rename to src/archivematica/dashboard/components/accounts/apps.py index 2e8ad652bc..0248bf3d22 100644 --- a/src/dashboard/src/components/accounts/apps.py +++ b/src/archivematica/dashboard/components/accounts/apps.py @@ -3,11 +3,11 @@ class AccountsConfig(AppConfig): default_auto_field = "django.db.models.AutoField" - name = "components.accounts" + name = "archivematica.dashboard.components.accounts" verbose_name = "Dashboard Accounts" def ready(self): try: - import components.accounts.signals # noqa + import archivematica.dashboard.components.accounts.signals # noqa except ImportError: pass diff --git a/src/archivematica/dashboard/components/accounts/backends.py b/src/archivematica/dashboard/components/accounts/backends.py new file mode 100644 index 0000000000..e929cdd1ea --- /dev/null +++ b/src/archivematica/dashboard/components/accounts/backends.py @@ -0,0 +1,226 @@ +import json +from typing import Any +from typing import Optional + +from django.conf import settings +from django.contrib.auth.models import User +from django.core.exceptions import ImproperlyConfigured +from django.http import HttpRequest +from django_auth_ldap.backend import LDAPBackend +from django_cas_ng.backends import CASBackend +from josepy.jws import JWS +from mozilla_django_oidc.auth import OIDCAuthenticationBackend +from shibboleth.backends import ShibbolethRemoteUserBackend + +from archivematica.dashboard.components.helpers import generate_api_key + + +class CustomShibbolethRemoteUserBackend(ShibbolethRemoteUserBackend): + def configure_user(self, user): + generate_api_key(user) + return user + + +class CustomCASBackend(CASBackend): + def configure_user(self, user): + generate_api_key(user) + # If CAS_AUTOCONFIGURE_EMAIL and CAS_EMAIL_DOMAIN settings are + # configured, add an email address for this user, using rule + # username@domain. + if settings.CAS_AUTOCONFIGURE_EMAIL and settings.CAS_EMAIL_DOMAIN: + user.email = f"{user.username}@{settings.CAS_EMAIL_DOMAIN}" + user.save() + return user + + +class CustomLDAPBackend(LDAPBackend): + """Append a usernamed suffix to LDAP users, if configured""" + + def ldap_to_django_username(self, username): + return username.rstrip(settings.AUTH_LDAP_USERNAME_SUFFIX) + + def django_to_ldap_username(self, username): + return username + settings.AUTH_LDAP_USERNAME_SUFFIX + + +class CustomOIDCBackend(OIDCAuthenticationBackend): + """ + Provide OpenID Connect authentication + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + # Store additional settings as instance attributes. + self.OIDC_OP_SET_ROLES_FROM_CLAIMS = getattr( + settings, "OIDC_OP_SET_ROLES_FROM_CLAIMS", False + ) + + self.OIDC_OP_ROLE_CLAIM_PATH = getattr( + settings, "OIDC_OP_ROLE_CLAIM_PATH", "realm_access.roles" + ) + + self.OIDC_ACCESS_ATTRIBUTE_MAP = getattr( + settings, "OIDC_ACCESS_ATTRIBUTE_MAP", settings.DEFAULT_OIDC_CLAIMS + ) + + # Valid role claim name which may be extracted from OIDC token. + self.OIDC_ROLE_CLAIM_ADMIN = getattr(settings, "OIDC_ROLE_CLAIM_ADMIN", "admin") + self.OIDC_ROLE_CLAIM_DEFAULT = getattr( + settings, "OIDC_ROLE_CLAIM_DEFAULT", "default" + ) + # Valid user roles. + self.USER_ROLE_ADMIN = "admin" + self.USER_ROLE_DEFAULT = "default" + + def get_settings(self, attr: str, *args: Any) -> Any: + if attr in [ + "OIDC_RP_CLIENT_ID", + "OIDC_RP_CLIENT_SECRET", + "OIDC_OP_AUTHORIZATION_ENDPOINT", + "OIDC_OP_TOKEN_ENDPOINT", + "OIDC_OP_USER_ENDPOINT", + "OIDC_OP_JWKS_ENDPOINT", + "OIDC_OP_LOGOUT_ENDPOINT", + "OIDC_OP_SET_ROLES_FROM_CLAIMS", + "OIDC_OP_ROLE_CLAIM_PATH", + "OIDC_ACCESS_ATTRIBUTE_MAP", + "OIDC_ROLE_CLAIM_ADMIN", + "OIDC_ROLE_CLAIM_DEFAULT", + ]: + # Retrieve the request object stored in the instance. + request = getattr(self, "request", None) + + if request: + provider_name = request.session.get("providername") + + if provider_name and provider_name in settings.OIDC_PROVIDERS: + provider_settings = settings.OIDC_PROVIDERS.get(provider_name, {}) + value = provider_settings.get(attr) + + if value is None: + raise ImproperlyConfigured( + f"Setting {attr} for provider {provider_name} not found" + ) + return value + + # If request is None or provider_name session var is not set or attr is + # not in the list, call the superclass's get_settings method. + return OIDCAuthenticationBackend.get_settings(attr, *args) + + def authenticate(self, request: HttpRequest, **kwargs: Any) -> Any: + self.request = request + self.OIDC_RP_CLIENT_ID = self.get_settings("OIDC_RP_CLIENT_ID") + self.OIDC_RP_CLIENT_SECRET = self.get_settings("OIDC_RP_CLIENT_SECRET") + self.OIDC_OP_TOKEN_ENDPOINT = self.get_settings("OIDC_OP_TOKEN_ENDPOINT") + self.OIDC_OP_USER_ENDPOINT = self.get_settings("OIDC_OP_USER_ENDPOINT") + self.OIDC_OP_JWKS_ENDPOINT = self.get_settings("OIDC_OP_JWKS_ENDPOINT") + self.OIDC_OP_SET_ROLES_FROM_CLAIMS = self.get_settings( + "OIDC_OP_SET_ROLES_FROM_CLAIMS" + ) + self.OIDC_OP_ROLE_CLAIM_PATH = self.get_settings("OIDC_OP_ROLE_CLAIM_PATH") + self.OIDC_ACCESS_ATTRIBUTE_MAP = self.get_settings("OIDC_ACCESS_ATTRIBUTE_MAP") + self.OIDC_ROLE_CLAIM_ADMIN = self.get_settings("OIDC_ROLE_CLAIM_ADMIN") + self.OIDC_ROLE_CLAIM_DEFAULT = self.get_settings("OIDC_ROLE_CLAIM_DEFAULT") + + return super().authenticate(request, **kwargs) + + def get_userinfo( + self, access_token: str, id_token: str, verified_id: dict[str, Any] + ) -> dict[str, Any]: + """ + Extract user details from JSON web tokens + These map to fields on the user field. + """ + + def decode_token(token): + sig = JWS.from_compact(token.encode("utf-8")) + payload = sig.payload.decode("utf-8") + return json.loads(payload) + + access_info = decode_token(access_token) + id_info = decode_token(id_token) + + info: dict[str, Any] = {} + + for oidc_attr, user_attr in self.OIDC_ACCESS_ATTRIBUTE_MAP.items(): + if oidc_attr in access_info: + info.setdefault(user_attr, access_info[oidc_attr]) + + for oidc_attr, user_attr in settings.OIDC_ID_ATTRIBUTE_MAP.items(): + if oidc_attr in id_info: + info.setdefault(user_attr, id_info[oidc_attr]) + + return info + + def create_user(self, user_info: dict[str, Any]) -> Optional[User]: + role = self.get_user_role(user_info) + if role is None: + return None + + user = super().create_user(user_info) + for attr, value in user_info.items(): + setattr(user, attr, value) + self.set_user_role(user, role) + generate_api_key(user) + return user + + def update_user(self, user: User, user_info: dict[str, Any]) -> Optional[User]: + """ + Updates the user's role only if the setting allows roles to be set from OIDC claims. + If the setting is False roles are being managed by an admin so do not update the role. + """ + if self.OIDC_OP_SET_ROLES_FROM_CLAIMS: + role = self.get_user_role(user_info) + if role is None: + return None + self.set_user_role(user, role) + return user + + def set_user_role(self, user: User, role: str) -> None: + """Assign a new role to a User given the role codename.""" + # Only users with the admin role are Django superusers. + user.is_superuser = role == self.USER_ROLE_ADMIN + user.save() + + def get_user_role(self, user_info: dict[str, Any]) -> Optional[str]: + """ + Returns the highest-permission valid role found in the OIDC token claims. + Returns the default user role if the setting is False. + Returns None if no valid roles are found. + """ + if not self.OIDC_OP_SET_ROLES_FROM_CLAIMS: + return self.USER_ROLE_DEFAULT + + claim_path = self.OIDC_OP_ROLE_CLAIM_PATH.split(".") + role_claims = user_info + + # Traverse the claim path to find the role claims. + for key in claim_path: + if isinstance(role_claims, dict): + role_claims = role_claims.get(key, {}) + else: + return None + + # If the claim contains a single role, convert to list. + if isinstance(role_claims, str): + role_claims = [role_claims] + + # Neither a string nor a list of roles. + if not isinstance(role_claims, list): + return None + + # The roles are ordered from highest to lowest permission in this list. + # This feature is used in OIDC authentication to determine the highest + # permission role of a user based on the claims received when multiple + # roles are received. + USER_ROLE_TO_ROLE_CLAIM_MAP = { + self.USER_ROLE_ADMIN: self.OIDC_ROLE_CLAIM_ADMIN, + self.USER_ROLE_DEFAULT: self.OIDC_ROLE_CLAIM_DEFAULT, + } + + # Iterate over USER_ROLE_TO_ROLE_CLAIM_MAP and return the first matching role. + for role_key, token_claim in USER_ROLE_TO_ROLE_CLAIM_MAP.items(): + if token_claim in role_claims: + return role_key + + return None # No match found. diff --git a/src/dashboard/src/components/accounts/forms.py b/src/archivematica/dashboard/components/accounts/forms.py similarity index 63% rename from src/dashboard/src/components/accounts/forms.py rename to src/archivematica/dashboard/components/accounts/forms.py index 91ad4072c1..828aa6ba52 100644 --- a/src/dashboard/src/components/accounts/forms.py +++ b/src/archivematica/dashboard/components/accounts/forms.py @@ -22,7 +22,8 @@ from django.contrib.auth.password_validation import validate_password from django.core.exceptions import ValidationError from django.utils.translation import gettext as _ -from main.models import UserProfile + +from archivematica.dashboard.main.models import UserProfile class UserCreationForm(DjangoUserCreationForm): @@ -71,30 +72,57 @@ def _post_clean(self): class UserChangeForm(DjangoUserChangeForm): error_messages = { "password_mismatch": _( - "The two password fields didn’t match. Enter the same password as before, for verification." - ) + "The two password fields didn't match. Enter the same password as before, for verification." + ), + "current_password_required": _( + "Your current password is required when setting a new password." + ), + "current_password_incorrect": _("Your current password is incorrect."), } email = forms.EmailField(required=True) + current_password = forms.CharField( + widget=forms.PasswordInput, + required=False, + label=_("Your current password"), + help_text=_("Required when setting a new password."), + ) password = forms.CharField( widget=forms.PasswordInput, required=False, + label=_("New password"), help_text=password_validators_help_text_html(), ) - password_confirmation = forms.CharField(widget=forms.PasswordInput, required=False) + password_confirmation = forms.CharField( + widget=forms.PasswordInput, required=False, label="New password confirmation" + ) is_superuser = forms.BooleanField(label="Administrator", required=False) regenerate_api_key = forms.CharField( widget=forms.CheckboxInput, - label="Regenerate API key (shown below)?", + label="Regenerate API key?", required=False, ) + field_order = [ + "username", + "first_name", + "last_name", + "email", + "is_active", + "is_superuser", + "current_password", + "password", + "password_confirmation", + "regenerate_api_key", + ] + def __init__(self, *args, **kwargs): - suppress_administrator_toggle = kwargs.get( + suppress_administrator_toggle = kwargs.pop( "suppress_administrator_toggle", False ) - - if "suppress_administrator_toggle" in kwargs: - del kwargs["suppress_administrator_toggle"] + try: + self.requesting_user = kwargs.pop("requesting_user") + except KeyError: + raise ValueError("requesting_user is required for UserChangeForm") super().__init__(*args, **kwargs) @@ -116,18 +144,41 @@ def clean_password(self): data = self.cleaned_data.get("password") return data + def _validate_current_password(self, current_password): + if not current_password: + self.add_error( + "current_password", self.error_messages["current_password_required"] + ) + return False + + if not self.requesting_user.check_password(current_password): + self.add_error( + "current_password", self.error_messages["current_password_incorrect"] + ) + return False + + return True + + def _validate_password_change(self, cleaned_data): + """Validate password change requirements including current password verification.""" + password = cleaned_data.get("password", "") + password_confirmation = cleaned_data.get("password_confirmation", "") + current_password = cleaned_data.get("current_password", "") + + # Only validate if password change is being attempted. + if not (password or password_confirmation): + return + + if password != password_confirmation: + self.add_error( + "password_confirmation", self.error_messages["password_mismatch"] + ) + + self._validate_current_password(current_password) + def clean(self): cleaned_data = super().clean() - if ( - cleaned_data.get("password") != "" - or cleaned_data.get("password_confirmation") != "" - ): - if cleaned_data.get("password") != cleaned_data.get( - "password_confirmation" - ): - self.add_error( - "password_confirmation", self.error_messages["password_mismatch"] - ) + self._validate_password_change(cleaned_data) return cleaned_data def _post_clean(self): @@ -151,7 +202,7 @@ def save(self, commit=True): class ApiKeyForm(forms.Form): regenerate_api_key = forms.BooleanField( widget=forms.CheckboxInput, - label="Regenerate API key (shown below)?", + label="Regenerate API key?", required=False, ) diff --git a/src/dashboard/src/components/accounts/signals.py b/src/archivematica/dashboard/components/accounts/signals.py similarity index 97% rename from src/dashboard/src/components/accounts/signals.py rename to src/archivematica/dashboard/components/accounts/signals.py index 436cb435d5..6443dd2c6a 100644 --- a/src/dashboard/src/components/accounts/signals.py +++ b/src/archivematica/dashboard/components/accounts/signals.py @@ -1,6 +1,5 @@ import logging -from components.helpers import generate_api_key from django.conf import settings from django.contrib.auth import get_user_model from django.db import transaction @@ -8,6 +7,8 @@ from django_auth_ldap.backend import populate_user from django_cas_ng.signals import cas_user_authenticated +from archivematica.dashboard.components.helpers import generate_api_key + logger = logging.getLogger("archivematica.dashboard") diff --git a/src/dashboard/src/components/accounts/urls.py b/src/archivematica/dashboard/components/accounts/urls.py similarity index 93% rename from src/dashboard/src/components/accounts/urls.py rename to src/archivematica/dashboard/components/accounts/urls.py index e9e360fb99..d8f60c22d9 100644 --- a/src/dashboard/src/components/accounts/urls.py +++ b/src/archivematica/dashboard/components/accounts/urls.py @@ -15,10 +15,11 @@ # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . import django.contrib.auth.views -from components.accounts import views from django.conf import settings from django.urls import path +from archivematica.dashboard.components.accounts import views + app_name = "accounts" urlpatterns = [ path("", views.list, name="accounts_index"), @@ -38,7 +39,7 @@ ] elif "mozilla_django_oidc" in settings.INSTALLED_APPS: - from components.accounts.views import CustomOIDCLogoutView + from archivematica.dashboard.components.accounts.views import CustomOIDCLogoutView urlpatterns += [ path( diff --git a/src/dashboard/src/components/accounts/validators.py b/src/archivematica/dashboard/components/accounts/validators.py similarity index 100% rename from src/dashboard/src/components/accounts/validators.py rename to src/archivematica/dashboard/components/accounts/validators.py diff --git a/src/dashboard/src/components/accounts/views.py b/src/archivematica/dashboard/components/accounts/views.py similarity index 86% rename from src/dashboard/src/components/accounts/views.py rename to src/archivematica/dashboard/components/accounts/views.py index 86f33cb66e..b9fa924e0f 100644 --- a/src/dashboard/src/components/accounts/views.py +++ b/src/archivematica/dashboard/components/accounts/views.py @@ -16,12 +16,6 @@ # along with Archivematica. If not, see . from urllib.parse import urlencode -import components.decorators as decorators -from components.accounts.forms import ApiKeyForm -from components.accounts.forms import UserChangeForm -from components.accounts.forms import UserCreationForm -from components.accounts.forms import UserProfileForm -from components.helpers import generate_api_key from django.conf import settings from django.contrib import messages from django.contrib.auth.decorators import user_passes_test @@ -32,13 +26,21 @@ from django.shortcuts import get_object_or_404 from django.shortcuts import redirect from django.shortcuts import render +from django.template.loader import render_to_string from django.urls import reverse from django.utils.translation import gettext as _ -from main.models import UserProfile from mozilla_django_oidc.views import OIDCAuthenticationRequestView from mozilla_django_oidc.views import OIDCLogoutView from tastypie.models import ApiKey +import archivematica.dashboard.components.decorators as decorators +from archivematica.dashboard.components.accounts.forms import ApiKeyForm +from archivematica.dashboard.components.accounts.forms import UserChangeForm +from archivematica.dashboard.components.accounts.forms import UserCreationForm +from archivematica.dashboard.components.accounts.forms import UserProfileForm +from archivematica.dashboard.components.helpers import generate_api_key +from archivematica.dashboard.main.models import UserProfile + @user_passes_test(lambda u: u.is_superuser, login_url="/forbidden/") def list(request): @@ -75,10 +77,17 @@ def add(request): ) +def _show_api_key_alert(request, key): + messages.success( + request, + render_to_string("accounts/user_api_key_alert.html", {"key": key}, request), + ) + + def profile(request): # If users are editable in this setup, go to the editable profile view if settings.ALLOW_USER_EDITS: - return edit(request) + return edit(request, return_view="accounts:profile") user = request.user user_profile = UserProfile.objects.get(user=user) @@ -89,7 +98,9 @@ def profile(request): userprofileform = UserProfileForm(request.POST, instance=user_profile) if form.is_valid() and userprofileform.is_valid(): if form.cleaned_data["regenerate_api_key"]: - generate_api_key(user) + key = generate_api_key(user) + _show_api_key_alert(request, key) + userprofileform.save() return redirect("accounts:profile") @@ -104,7 +115,7 @@ def profile(request): ) -def edit(request, id=None): +def edit(request, id=None, return_view=None): # Forbidden if user isn't an admin and is trying to edit another user if str(request.user.id) != str(id) and id is not None: if request.user.is_superuser is False: @@ -122,7 +133,7 @@ def edit(request, id=None): # Form if request.method == "POST": - form = UserChangeForm(request.POST, instance=user) + form = UserChangeForm(request.POST, instance=user, requesting_user=request.user) userprofileform = UserProfileForm(request.POST, instance=user_profile) if form.is_valid() and userprofileform.is_valid(): user = form.save(commit=False) @@ -142,11 +153,16 @@ def edit(request, id=None): # regenerate API key if requested regenerate_api_key = request.POST.get("regenerate_api_key", "") if regenerate_api_key != "": - generate_api_key(user) + key = generate_api_key(user) + _show_api_key_alert(request, key) # determine where to redirect to if request.user.is_superuser: - return_view = "accounts:accounts_index" + return_view = request.POST.get("return_view") + if return_view: + return_view = return_view + else: + return_view = reverse("accounts:edit", kwargs={"id": user.pk}) else: return_view = "accounts:profile" @@ -157,7 +173,9 @@ def edit(request, id=None): if request.user.is_superuser: suppress_administrator_toggle = False form = UserChangeForm( - instance=user, suppress_administrator_toggle=suppress_administrator_toggle + instance=user, + suppress_administrator_toggle=suppress_administrator_toggle, + requesting_user=request.user, ) userprofileform = UserProfileForm(instance=user_profile) @@ -169,6 +187,7 @@ def edit(request, id=None): "userprofileform": userprofileform, "user": user, "title": title, + "return_view": return_view, }, ) diff --git a/src/dashboard/src/components/api/__init__.py b/src/archivematica/dashboard/components/administration/__init__.py similarity index 100% rename from src/dashboard/src/components/api/__init__.py rename to src/archivematica/dashboard/components/administration/__init__.py diff --git a/src/dashboard/src/components/administration/apps.py b/src/archivematica/dashboard/components/administration/apps.py similarity index 67% rename from src/dashboard/src/components/administration/apps.py rename to src/archivematica/dashboard/components/administration/apps.py index 4a3ff61b4c..5b81a3dd14 100644 --- a/src/dashboard/src/components/administration/apps.py +++ b/src/archivematica/dashboard/components/administration/apps.py @@ -3,4 +3,4 @@ class AdministrationAppConfig(AppConfig): default_auto_field = "django.db.models.AutoField" - name = "components.administration" + name = "archivematica.dashboard.components.administration" diff --git a/src/dashboard/src/components/administration/forms.py b/src/archivematica/dashboard/components/administration/forms.py similarity index 95% rename from src/dashboard/src/components/administration/forms.py rename to src/archivematica/dashboard/components/administration/forms.py index 451bdc1e4a..51adb675ee 100644 --- a/src/dashboard/src/components/administration/forms.py +++ b/src/archivematica/dashboard/components/administration/forms.py @@ -16,18 +16,19 @@ # along with Archivematica. If not, see . import os -from components import helpers -from contrib.mcp.client import MCPClient from django import forms from django.conf import settings from django.forms.widgets import Select from django.forms.widgets import TextInput from django.utils.translation import gettext_lazy as _ -from installer.forms import load_site_url -from installer.forms import site_url_field from lxml import etree -from main.models import Agent -from main.models import TaxonomyTerm + +from archivematica.dashboard.components import helpers +from archivematica.dashboard.contrib.mcp.client import MCPClient +from archivematica.dashboard.installer.forms import load_site_url +from archivematica.dashboard.installer.forms import site_url_field +from archivematica.dashboard.main.models import Agent +from archivematica.dashboard.main.models import TaxonomyTerm class AgentForm(forms.ModelForm): @@ -230,6 +231,8 @@ def to_python(self, value): help_text=_( "API key of the storage service user. E.g. 45f7684483044809b2de045ba59dc876b11b9810" ), + required=False, + widget=forms.PasswordInput, ) storage_service_use_default_config = forms.BooleanField( required=False, @@ -240,6 +243,13 @@ def to_python(self, value): ), ) + def save(self, *args, **kwargs): + if not self.cleaned_data["storage_service_apikey"]: + # If the API key was not provided, remove it from the cleaned data + # to avoid overwriting its existing stored value. + del self.cleaned_data["storage_service_apikey"] + return super().save(*args, **kwargs) + class ChecksumSettingsForm(SettingsForm): CHOICES = ( diff --git a/src/dashboard/src/components/administration/forms_dip_upload.py b/src/archivematica/dashboard/components/administration/forms_dip_upload.py similarity index 100% rename from src/dashboard/src/components/administration/forms_dip_upload.py rename to src/archivematica/dashboard/components/administration/forms_dip_upload.py diff --git a/src/dashboard/src/components/administration/migrations/0001_initial.py b/src/archivematica/dashboard/components/administration/migrations/0001_initial.py similarity index 93% rename from src/dashboard/src/components/administration/migrations/0001_initial.py rename to src/archivematica/dashboard/components/administration/migrations/0001_initial.py index 8a28bd1bd9..aa86a31568 100644 --- a/src/dashboard/src/components/administration/migrations/0001_initial.py +++ b/src/archivematica/dashboard/components/administration/migrations/0001_initial.py @@ -1,14 +1,13 @@ import uuid -from typing import List -from typing import Tuple -import main.models from django.db import migrations from django.db import models +import archivematica.dashboard.main.models as main_models + class Migration(migrations.Migration): - dependencies: List[Tuple[str, str]] = [] + dependencies: list[tuple[str, str]] = [] operations = [ migrations.CreateModel( @@ -16,7 +15,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -55,7 +54,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, diff --git a/src/dashboard/src/components/administration/migrations/0002_archivesspaceconfig.py b/src/archivematica/dashboard/components/administration/migrations/0002_archivesspaceconfig.py similarity index 98% rename from src/dashboard/src/components/administration/migrations/0002_archivesspaceconfig.py rename to src/archivematica/dashboard/components/administration/migrations/0002_archivesspaceconfig.py index 28427dfa89..8ede3da86c 100644 --- a/src/dashboard/src/components/administration/migrations/0002_archivesspaceconfig.py +++ b/src/archivematica/dashboard/components/administration/migrations/0002_archivesspaceconfig.py @@ -1,9 +1,10 @@ import uuid -import main.models from django.db import migrations from django.db import models +import archivematica.dashboard.main.models as main_models + def insert_data(apps, schema_editor): # Insert default values into the ArchivesSpaceConfig table. @@ -23,7 +24,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, diff --git a/src/dashboard/src/components/administration/migrations/0003_archivesspace_help_text.py b/src/archivematica/dashboard/components/administration/migrations/0003_archivesspace_help_text.py similarity index 100% rename from src/dashboard/src/components/administration/migrations/0003_archivesspace_help_text.py rename to src/archivematica/dashboard/components/administration/migrations/0003_archivesspace_help_text.py diff --git a/src/dashboard/src/components/administration/migrations/0004_archivesspaceconfig_inherit_notes.py b/src/archivematica/dashboard/components/administration/migrations/0004_archivesspaceconfig_inherit_notes.py similarity index 100% rename from src/dashboard/src/components/administration/migrations/0004_archivesspaceconfig_inherit_notes.py rename to src/archivematica/dashboard/components/administration/migrations/0004_archivesspaceconfig_inherit_notes.py diff --git a/src/dashboard/src/components/administration/migrations/0005_archivesspace_inherit_note_default.py b/src/archivematica/dashboard/components/administration/migrations/0005_archivesspace_inherit_note_default.py similarity index 100% rename from src/dashboard/src/components/administration/migrations/0005_archivesspace_inherit_note_default.py rename to src/archivematica/dashboard/components/administration/migrations/0005_archivesspace_inherit_note_default.py diff --git a/src/dashboard/src/components/administration/migrations/0006_use_statement_optional.py b/src/archivematica/dashboard/components/administration/migrations/0006_use_statement_optional.py similarity index 100% rename from src/dashboard/src/components/administration/migrations/0006_use_statement_optional.py rename to src/archivematica/dashboard/components/administration/migrations/0006_use_statement_optional.py diff --git a/src/dashboard/src/components/administration/migrations/0007_delete_unused_models.py b/src/archivematica/dashboard/components/administration/migrations/0007_delete_unused_models.py similarity index 100% rename from src/dashboard/src/components/administration/migrations/0007_delete_unused_models.py rename to src/archivematica/dashboard/components/administration/migrations/0007_delete_unused_models.py diff --git a/src/dashboard/src/components/appraisal/__init__.py b/src/archivematica/dashboard/components/administration/migrations/__init__.py similarity index 100% rename from src/dashboard/src/components/appraisal/__init__.py rename to src/archivematica/dashboard/components/administration/migrations/__init__.py diff --git a/src/dashboard/src/components/administration/models.py b/src/archivematica/dashboard/components/administration/models.py similarity index 100% rename from src/dashboard/src/components/administration/models.py rename to src/archivematica/dashboard/components/administration/models.py diff --git a/src/dashboard/src/components/administration/urls.py b/src/archivematica/dashboard/components/administration/urls.py similarity index 91% rename from src/dashboard/src/components/administration/urls.py rename to src/archivematica/dashboard/components/administration/urls.py index 9c444278d2..ddb97222f1 100644 --- a/src/dashboard/src/components/administration/urls.py +++ b/src/archivematica/dashboard/components/administration/urls.py @@ -14,16 +14,19 @@ # # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . -from components.administration import views -from components.administration import views_dip_upload -from components.administration import views_processing -from components.administration.forms import ProcessingConfigurationForm from django.conf import settings from django.urls import include from django.urls import path from django.urls import re_path from django.views.generic import TemplateView +from archivematica.dashboard.components.administration import views +from archivematica.dashboard.components.administration import views_dip_upload +from archivematica.dashboard.components.administration import views_processing +from archivematica.dashboard.components.administration.forms import ( + ProcessingConfigurationForm, +) + app_name = "administration" urlpatterns = [ path("", views.administration, name="administration_index"), diff --git a/src/dashboard/src/components/administration/views.py b/src/archivematica/dashboard/components/administration/views.py similarity index 95% rename from src/dashboard/src/components/administration/views.py rename to src/archivematica/dashboard/components/administration/views.py index e5918f6c1d..29466ce9de 100644 --- a/src/dashboard/src/components/administration/views.py +++ b/src/archivematica/dashboard/components/administration/views.py @@ -20,16 +20,6 @@ import shutil import subprocess -import components.administration.views_processing as processing_views -import components.decorators as decorators -import components.helpers as helpers -import storageService as storage_service -from components.administration.forms import AgentForm -from components.administration.forms import ChecksumSettingsForm -from components.administration.forms import GeneralSettingsForm -from components.administration.forms import HandleForm -from components.administration.forms import StorageSettingsForm -from components.administration.forms import TaxonomyTermForm from django.conf import settings as django_settings from django.contrib import messages from django.contrib.auth.decorators import user_passes_test @@ -43,10 +33,21 @@ from django.template.defaultfilters import filesizeformat from django.urls import reverse from django.utils.translation import gettext as _ -from installer.steps import setup_pipeline_in_ss -from main import models -from version import get_full_version -from version import get_preservation_system_identifier + +import archivematica.archivematicaCommon.storageService as storage_service +import archivematica.dashboard.components.administration.views_processing as processing_views +import archivematica.dashboard.components.decorators as decorators +import archivematica.dashboard.components.helpers as helpers +from archivematica.archivematicaCommon.version import get_full_version +from archivematica.archivematicaCommon.version import get_preservation_system_identifier +from archivematica.dashboard.components.administration.forms import AgentForm +from archivematica.dashboard.components.administration.forms import ChecksumSettingsForm +from archivematica.dashboard.components.administration.forms import GeneralSettingsForm +from archivematica.dashboard.components.administration.forms import HandleForm +from archivematica.dashboard.components.administration.forms import StorageSettingsForm +from archivematica.dashboard.components.administration.forms import TaxonomyTermForm +from archivematica.dashboard.installer.steps import setup_pipeline_in_ss +from archivematica.dashboard.main import models logger = logging.getLogger("archivematica.dashboard") diff --git a/src/dashboard/src/components/administration/views_dip_upload.py b/src/archivematica/dashboard/components/administration/views_dip_upload.py similarity index 81% rename from src/dashboard/src/components/administration/views_dip_upload.py rename to src/archivematica/dashboard/components/administration/views_dip_upload.py index 6596a54ca8..bc55d495f8 100644 --- a/src/dashboard/src/components/administration/views_dip_upload.py +++ b/src/archivematica/dashboard/components/administration/views_dip_upload.py @@ -1,9 +1,14 @@ -from components.administration.forms_dip_upload import ArchivesSpaceConfigForm -from components.administration.forms_dip_upload import AtomConfigForm from django.contrib import messages from django.shortcuts import render from django.utils.translation import gettext as _ -from main.models import DashboardSetting + +from archivematica.dashboard.components.administration.forms_dip_upload import ( + ArchivesSpaceConfigForm, +) +from archivematica.dashboard.components.administration.forms_dip_upload import ( + AtomConfigForm, +) +from archivematica.dashboard.main.models import DashboardSetting _AS_DICTNAME = "upload-archivesspace_v0.0" _ATOM_DICTNAME = "upload-qubit_v0.0" diff --git a/src/dashboard/src/components/administration/views_processing.py b/src/archivematica/dashboard/components/administration/views_processing.py similarity index 94% rename from src/dashboard/src/components/administration/views_processing.py rename to src/archivematica/dashboard/components/administration/views_processing.py index abd23e539d..8c46c5c02c 100644 --- a/src/dashboard/src/components/administration/views_processing.py +++ b/src/archivematica/dashboard/components/administration/views_processing.py @@ -19,15 +19,17 @@ import re from glob import iglob -from components import helpers from django.contrib import messages from django.http import Http404 from django.shortcuts import redirect from django.shortcuts import render from django.utils.translation import gettext as _ -from processing import install_builtin_config -from .forms import ProcessingConfigurationForm +from archivematica.archivematicaCommon.processing import install_builtin_config +from archivematica.dashboard.components import helpers +from archivematica.dashboard.components.administration.forms import ( + ProcessingConfigurationForm, +) logger = logging.getLogger("archivematica.dashboard") diff --git a/src/dashboard/src/components/advanced_search.py b/src/archivematica/dashboard/components/advanced_search.py similarity index 97% rename from src/dashboard/src/components/advanced_search.py rename to src/archivematica/dashboard/components/advanced_search.py index f6e9c37425..76e504de93 100644 --- a/src/dashboard/src/components/advanced_search.py +++ b/src/archivematica/dashboard/components/advanced_search.py @@ -227,10 +227,3 @@ def _query_clause(index, queries, ops, fields, types): query = {"range": {fields[index]: {"gte": start, "lt": end}}} return query - - -def indexed_count(es_client, index, query=None): - try: - return es_client.count(index=index, body=query)["count"] - except Exception: - return 0 diff --git a/src/dashboard/src/components/archival_storage/__init__.py b/src/archivematica/dashboard/components/api/__init__.py similarity index 100% rename from src/dashboard/src/components/archival_storage/__init__.py rename to src/archivematica/dashboard/components/api/__init__.py diff --git a/src/dashboard/src/components/api/urls.py b/src/archivematica/dashboard/components/api/urls.py similarity index 98% rename from src/dashboard/src/components/api/urls.py rename to src/archivematica/dashboard/components/api/urls.py index f5b5c953d1..ace95cf2b7 100644 --- a/src/dashboard/src/components/api/urls.py +++ b/src/archivematica/dashboard/components/api/urls.py @@ -14,11 +14,12 @@ # # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . -from components.api import views from django.conf import settings from django.urls import path from django.urls import re_path +from archivematica.dashboard.components.api import views + app_name = "api" urlpatterns = [ re_path(r"transfer/approve", views.approve_transfer, name="approve_transfer"), diff --git a/src/dashboard/src/components/api/validators.py b/src/archivematica/dashboard/components/api/validators.py similarity index 99% rename from src/dashboard/src/components/api/validators.py rename to src/archivematica/dashboard/components/api/validators.py index 39e321e840..d3a1118122 100644 --- a/src/dashboard/src/components/api/validators.py +++ b/src/archivematica/dashboard/components/api/validators.py @@ -188,7 +188,7 @@ def _check_file_exts(row, file_cols): q in row[c] for q in [".high.", ".medium.", ".low"] ): raise ValidationError( - "Filepath " + row[c] + " contains" " more than one period." + "Filepath " + row[c] + " contains more than one period." ) @staticmethod diff --git a/src/dashboard/src/components/api/views.py b/src/archivematica/dashboard/components/api/views.py similarity index 97% rename from src/dashboard/src/components/api/views.py rename to src/archivematica/dashboard/components/api/views.py index 6e6f7b707d..3c0edf8ee4 100644 --- a/src/dashboard/src/components/api/views.py +++ b/src/archivematica/dashboard/components/api/views.py @@ -22,24 +22,26 @@ import uuid from cgi import parse_header -import archivematicaFunctions import django.http -from components import helpers -from components.filesystem_ajax import views as filesystem_ajax_views -from components.unit import views as unit_views -from contrib.mcp.client import MCPClient from django.conf import settings as django_settings from django.core.exceptions import ValidationError from django.db.models import Q from django.views.decorators.csrf import csrf_exempt -from main import models -from processing import install_builtin_config from tastypie.authentication import ApiKeyAuthentication from tastypie.authentication import MultiAuthentication from tastypie.authentication import SessionAuthentication -from version import get_full_version -from . import validators +from archivematica.archivematicaCommon import archivematicaFunctions +from archivematica.archivematicaCommon.processing import install_builtin_config +from archivematica.archivematicaCommon.version import get_full_version +from archivematica.dashboard.components import helpers +from archivematica.dashboard.components.api import validators +from archivematica.dashboard.components.filesystem_ajax import ( + views as filesystem_ajax_views, +) +from archivematica.dashboard.components.unit import views as unit_views +from archivematica.dashboard.contrib.mcp.client import MCPClient +from archivematica.dashboard.main import models LOGGER = logging.getLogger("archivematica.dashboard") SHARED_PATH_TEMPLATE_VAL = "%sharedPath%" @@ -413,7 +415,7 @@ def _completed_units(unit_type="transfer"): completed.append(str(unit.uuid)) if status_err: LOGGER.warning( - "Unable to determine status of at least one unit," " e.g.: unit %s (%s)", + "Unable to determine status of at least one unit, e.g.: unit %s (%s)", unit.uuid, model_name, ) diff --git a/src/dashboard/src/components/backlog/__init__.py b/src/archivematica/dashboard/components/appraisal/__init__.py similarity index 100% rename from src/dashboard/src/components/backlog/__init__.py rename to src/archivematica/dashboard/components/appraisal/__init__.py diff --git a/src/dashboard/src/components/appraisal/urls.py b/src/archivematica/dashboard/components/appraisal/urls.py similarity index 65% rename from src/dashboard/src/components/appraisal/urls.py rename to src/archivematica/dashboard/components/appraisal/urls.py index f9aea1b859..5cfd8789e2 100644 --- a/src/dashboard/src/components/appraisal/urls.py +++ b/src/archivematica/dashboard/components/appraisal/urls.py @@ -1,5 +1,6 @@ -from components.appraisal import views from django.urls import path +from archivematica.dashboard.components.appraisal import views + app_name = "appraisal" urlpatterns = [path("", views.appraisal, name="appraisal_index")] diff --git a/src/dashboard/src/components/appraisal/views.py b/src/archivematica/dashboard/components/appraisal/views.py similarity index 100% rename from src/dashboard/src/components/appraisal/views.py rename to src/archivematica/dashboard/components/appraisal/views.py diff --git a/src/dashboard/src/components/file/__init__.py b/src/archivematica/dashboard/components/archival_storage/__init__.py similarity index 100% rename from src/dashboard/src/components/file/__init__.py rename to src/archivematica/dashboard/components/archival_storage/__init__.py diff --git a/src/dashboard/src/components/archival_storage/atom.py b/src/archivematica/dashboard/components/archival_storage/atom.py similarity index 96% rename from src/dashboard/src/components/archival_storage/atom.py rename to src/archivematica/dashboard/components/archival_storage/atom.py index d5f8dbcca2..95e3111354 100644 --- a/src/dashboard/src/components/archival_storage/atom.py +++ b/src/archivematica/dashboard/components/archival_storage/atom.py @@ -22,9 +22,10 @@ from agentarchives.atom.client import AtomClient from agentarchives.atom.client import AtomError from agentarchives.atom.client import CommunicationError -from main.models import DashboardSetting from metsrw import METSDocument -from storageService import extract_file + +from archivematica.archivematicaCommon.storageService import extract_file +from archivematica.dashboard.main.models import DashboardSetting logger = logging.getLogger("archivematica.dashboard") diff --git a/src/dashboard/src/components/archival_storage/forms.py b/src/archivematica/dashboard/components/archival_storage/forms.py similarity index 96% rename from src/dashboard/src/components/archival_storage/forms.py rename to src/archivematica/dashboard/components/archival_storage/forms.py index baeb8111cd..93fe598847 100644 --- a/src/dashboard/src/components/archival_storage/forms.py +++ b/src/archivematica/dashboard/components/archival_storage/forms.py @@ -17,13 +17,14 @@ import pathlib from agentarchives.atom.client import CommunicationError -from components import helpers -from components.archival_storage.atom import get_atom_client from django import forms from django.utils.translation import gettext as _ from requests import ConnectionError from requests import Timeout +from archivematica.dashboard.components import helpers +from archivematica.dashboard.components.archival_storage.atom import get_atom_client + class CreateAICForm(forms.Form): results = forms.CharField( diff --git a/src/dashboard/src/components/archival_storage/urls.py b/src/archivematica/dashboard/components/archival_storage/urls.py similarity index 96% rename from src/dashboard/src/components/archival_storage/urls.py rename to src/archivematica/dashboard/components/archival_storage/urls.py index 41c95c808a..13b4882659 100644 --- a/src/dashboard/src/components/archival_storage/urls.py +++ b/src/archivematica/dashboard/components/archival_storage/urls.py @@ -14,11 +14,12 @@ # # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . -from components.archival_storage import views from django.conf import settings from django.urls import path from django.urls import re_path +from archivematica.dashboard.components.archival_storage import views + app_name = "archival_storage" urlpatterns = [ re_path( diff --git a/src/dashboard/src/components/archival_storage/views.py b/src/archivematica/dashboard/components/archival_storage/views.py similarity index 71% rename from src/dashboard/src/components/archival_storage/views.py rename to src/archivematica/dashboard/components/archival_storage/views.py index 8a41e6d2b6..0835632ead 100644 --- a/src/dashboard/src/components/archival_storage/views.py +++ b/src/archivematica/dashboard/components/archival_storage/views.py @@ -22,16 +22,6 @@ from collections import OrderedDict from datetime import datetime -import databaseFunctions -import elasticSearchFunctions as es -import storageService as storage_service -from archivematicaFunctions import AMCLIENT_ERROR_CODES -from archivematicaFunctions import setup_amclient -from components import advanced_search -from components import helpers -from components.archival_storage import forms -from components.archival_storage.atom import AtomMetadataUploadError -from components.archival_storage.atom import upload_dip_metadata_to_atom from django.conf import settings from django.contrib import messages from django.http import Http404 @@ -43,15 +33,34 @@ from django.utils.timezone import get_current_timezone from django.utils.timezone import make_aware from django.utils.translation import gettext as _ -from elasticsearch import ElasticsearchException + +import archivematica.search.constants +from archivematica.archivematicaCommon import databaseFunctions +from archivematica.archivematicaCommon import storageService as storage_service +from archivematica.archivematicaCommon.archivematicaFunctions import ( + AMCLIENT_ERROR_CODES, +) +from archivematica.archivematicaCommon.archivematicaFunctions import setup_amclient +from archivematica.dashboard.components import advanced_search +from archivematica.dashboard.components import helpers +from archivematica.dashboard.components.archival_storage import forms +from archivematica.dashboard.components.archival_storage.atom import ( + AtomMetadataUploadError, +) +from archivematica.dashboard.components.archival_storage.atom import ( + upload_dip_metadata_to_atom, +) +from archivematica.search.service import AIPNotFoundError +from archivematica.search.service import SortSpec +from archivematica.search.service import setup_search_service_from_conf logger = logging.getLogger("archivematica.dashboard") AIPSTOREPATH = "/var/archivematica/sharedDirectory/www/AIPsStore" AIP_STATUS_DESCRIPTIONS = { - es.STATUS_UPLOADED: _("Stored"), - es.STATUS_DELETE_REQUESTED: _("Deletion requested"), + archivematica.search.constants.STATUS_UPLOADED: _("Stored"), + archivematica.search.constants.STATUS_DELETE_REQUESTED: _("Deletion requested"), } DIRECTORY_PERMISSIONS = 0o770 @@ -96,20 +105,21 @@ def sync_es_aip_status_with_storage_service(uuid, es_status): ) return keep_in_results + search_service = setup_search_service_from_conf(settings) if ( - aip_status == es.STATUS_DELETE_REQUESTED - and es_status != es.STATUS_DELETE_REQUESTED + aip_status == archivematica.search.constants.STATUS_DELETE_REQUESTED + and es_status != archivematica.search.constants.STATUS_DELETE_REQUESTED + ): + search_service.mark_aip_for_deletion(uuid) + elif ( + aip_status == archivematica.search.constants.STATUS_UPLOADED + and es_status != archivematica.search.constants.STATUS_UPLOADED ): - es_client = es.get_client() - es.mark_aip_deletion_requested(es_client, uuid) - elif aip_status == es.STATUS_UPLOADED and es_status != es.STATUS_UPLOADED: - es_client = es.get_client() - es.revert_aip_deletion_request(es_client, uuid) - elif aip_status == es.STATUS_DELETED: + search_service.unmark_aip_for_deletion(uuid) + elif aip_status == archivematica.search.constants.STATUS_DELETED: keep_in_results = False - es_client = es.get_client() - es.delete_aip(es_client, uuid) - es.delete_aip_files(es_client, uuid) + search_service.delete_aip(uuid) + search_service.delete_aip_files(uuid) return keep_in_results @@ -120,11 +130,11 @@ def execute(request): :param request: Django request object. :return: The main archival storage page rendered. """ - if es.AIPS_INDEX in settings.SEARCH_ENABLED: - es_client = es.get_client() + if archivematica.search.constants.AIPS_INDEX in settings.SEARCH_ENABLED: + search_service = setup_search_service_from_conf(settings) - total_size = total_size_of_aips(es_client) - aip_indexed_file_count = aip_file_count(es_client) + total_size = total_size_of_aips(search_service) + aip_indexed_file_count = aip_file_count(search_service) return render( request, @@ -195,19 +205,19 @@ def get_es_property_from_column_index(index, file_mode): # translatable. _ORDERED_DICT_ES_FIELDS = OrderedDict( [ - (es.ES_FIELD_NAME, _("Name")), - (es.ES_FIELD_UUID, _("UUID")), - (es.ES_FIELD_AICID, _("AICID")), - (es.ES_FIELD_AICCOUNT, _("Count AIPs in AIC")), + (archivematica.search.constants.ES_FIELD_NAME, _("Name")), + (archivematica.search.constants.ES_FIELD_UUID, _("UUID")), + (archivematica.search.constants.ES_FIELD_AICID, _("AICID")), + (archivematica.search.constants.ES_FIELD_AICCOUNT, _("Count AIPs in AIC")), ("bytes", _("Bytes")), - (es.ES_FIELD_SIZE, _("Size")), - (es.ES_FIELD_FILECOUNT, _("File count")), - (es.ES_FIELD_ACCESSION_IDS, _("Accession IDs")), - (es.ES_FIELD_CREATED, _("Created date (UTC)")), - (es.ES_FIELD_STATUS, _("Status")), + (archivematica.search.constants.ES_FIELD_SIZE, _("Size")), + (archivematica.search.constants.ES_FIELD_FILECOUNT, _("File count")), + (archivematica.search.constants.ES_FIELD_ACCESSION_IDS, _("Accession IDs")), + (archivematica.search.constants.ES_FIELD_CREATED, _("Created date (UTC)")), + (archivematica.search.constants.ES_FIELD_STATUS, _("Status")), ("type", _("Type")), - (es.ES_FIELD_ENCRYPTED, _("Encrypted")), - (es.ES_FIELD_LOCATION, _("Location")), + (archivematica.search.constants.ES_FIELD_ENCRYPTED, _("Encrypted")), + (archivematica.search.constants.ES_FIELD_LOCATION, _("Location")), ] ) @@ -229,20 +239,24 @@ def generate_search_as_csv_rows(csvwriter, es_results): # Normalize accession identifiers. try: - accession_ids = "; ".join(row[es.ES_FIELD_ACCESSION_IDS]) + accession_ids = "; ".join( + row[archivematica.search.constants.ES_FIELD_ACCESSION_IDS] + ) except TypeError: - accession_ids = row[es.ES_FIELD_ACCESSION_IDS] - row[es.ES_FIELD_ACCESSION_IDS] = accession_ids + accession_ids = row[archivematica.search.constants.ES_FIELD_ACCESSION_IDS] + row[archivematica.search.constants.ES_FIELD_ACCESSION_IDS] = accession_ids # Localize date output. try: created = make_aware( - datetime.fromtimestamp(row[es.ES_FIELD_CREATED]), + datetime.fromtimestamp( + row[archivematica.search.constants.ES_FIELD_CREATED] + ), timezone=current_timezone, ) except TypeError: - created = row[es.ES_FIELD_CREATED] - row[es.ES_FIELD_CREATED] = created + created = row[archivematica.search.constants.ES_FIELD_CREATED] + row[archivematica.search.constants.ES_FIELD_CREATED] = created yield csvwriter.writerow(list(row.values())) @@ -265,6 +279,106 @@ def write(self, value): return response +def _search_aip_files( + query: dict, + search_service, + start: int, + page_size: int, + order_by: str, + sort_direction: str, +) -> tuple: + """ + Execute Elasticsearch search for AIP files using search service. + + :param query: The Elasticsearch query to execute + :param search_service: The search service instance + :param start: Start index for pagination + :param page_size: Number of results per page + :param order_by: Field to sort by + :param sort_direction: Sort direction (asc/desc) + :return: Tuple of (hits, hit_count) + """ + sort_param = SortSpec(field=order_by, order=sort_direction) if order_by else None + fields = ["filePath", "FILEUUID", "AIPUUID", "accessionid", "status"] + + hits = search_service.search_aip_files( + query=query, size=page_size, from_=start, sort=sort_param, fields=fields + ) + hit_count = hits["hits"]["total"]["value"] + return hits, hit_count + + +def _search_aips( + query: dict, + search_service, + start: int, + page_size: int, + order_by: str, + sort_direction: str, +) -> tuple: + """ + Execute Elasticsearch search for AIPs. + + :param query: The Elasticsearch query to execute + :param search_service: The search service instance + :param start: Start index for pagination + :param page_size: Number of results per page + :param order_by: Field to sort by + :param sort_direction: Sort direction (asc/desc) + :return: Tuple of (hits, hit_count) + """ + # AIP mode: + # Query to aipfiles, but only fetch & aggregate AIP UUIDs. + # Based on AIP UUIDs, query to aips. + # ES query will limit to 10 aggregation results by default, + # add size parameter in terms to override. + # TODO: Use composite aggregation when it gets out of beta. + query["aggs"] = { + "aip_uuids": { + "terms": { + "field": "AIPUUID", + "size": str(settings.ELASTICSEARCH_MAX_QUERY_SIZE), + } + } + } + hits = search_service.search_aip_files(query, size=0) + buckets = hits.get("aggregations", {}).get("aip_uuids", {}).get("buckets", []) + uuids = [bucket["key"] for bucket in buckets] + uuid_file_counts = {bucket["key"]: bucket["doc_count"] for bucket in buckets} + + # If no AIPs found, return empty result + if not uuids: + empty_result = {"hits": {"hits": [], "total": 0}} + return empty_result, 0, {} + + # Recreate query to search over AIPs + aip_query = {"query": {"terms": {"uuid": uuids}}} + sort_param = SortSpec(field=order_by, order=sort_direction) if order_by else None + fields = [ + "name", + "uuid", + "size", + "accessionids", + "created", + "status", + "encrypted", + "AICID", + "isPartOf", + "countAIPsinAIC", + "location", + ] + + hits = search_service.search_aips( + query=aip_query, + size=page_size, + from_=start, + sort=sort_param, + fields=fields, + ) + hit_count = hits["hits"]["total"]["value"] + return hits, hit_count, uuid_file_counts + + def search(request): """A JSON end point that returns results for AIPs and their files. @@ -288,7 +402,7 @@ def search(request): DEFAULT_PAGE_SIZE = 10 page_size = None if request.GET.get(RETURN_ALL, "").lower() == "true": - page_size = es.MAX_QUERY_SIZE + page_size = archivematica.search.constants.MAX_QUERY_SIZE if page_size is None: page_size = int(request.GET.get("iDisplayLength", DEFAULT_PAGE_SIZE)) @@ -313,61 +427,23 @@ def search(request): ) sort_direction = request.GET.get("sSortDir_0", "asc") - es_client = es.get_client() - try: - if file_mode: - index = es.AIP_FILES_INDEX - source = "filePath,FILEUUID,AIPUUID,accessionid,status" - else: - # Fetch all unique AIP UUIDs in the returned set of files. - # ES query will limit to 10 aggregation results by default; - # add size parameter in terms to override. - # TODO: Use composite aggregation when it gets out of beta. - query["aggs"] = { - "aip_uuids": { - "terms": { - "field": "AIPUUID", - "size": str(settings.ELASTICSEARCH_MAX_QUERY_SIZE), - } - } - } - # Don't return results, just the aggregation. - query["size"] = 0 - # Searching for AIPs still actually searches type 'aipfile', and - # returns the UUID of the AIP the files are a part of. To search - # for an attribute of an AIP, the aipfile must index that - # information about their AIP. - results = es_client.search(body=query, index=es.AIP_FILES_INDEX) - # Given these AIP UUIDs, now fetch the actual information we want - # from AIPs/AIP. - buckets = results["aggregations"]["aip_uuids"]["buckets"] - uuids = [bucket["key"] for bucket in buckets] - uuid_file_counts = { - bucket["key"]: bucket["doc_count"] for bucket in buckets - } - query = {"query": {"terms": {"uuid": uuids}}} - index = es.AIPS_INDEX - source = "name,uuid,size,accessionids,created,status,encrypted,AICID,isPartOf,countAIPsinAIC,location" - - results = es_client.search( - index=index, - body=query, - from_=start, - size=page_size, - sort=order_by + ":" + sort_direction if order_by else "", - _source=source, - ) + search_service = setup_search_service_from_conf(settings) + try: if file_mode: - augmented_results = search_augment_file_results(es_client, results) + results, hit_count = _search_aip_files( + query, search_service, start, page_size, order_by, sort_direction + ) + augmented_results = search_augment_file_results(search_service, results) else: + results, hit_count, uuid_file_counts = _search_aips( + query, search_service, start, page_size, order_by, sort_direction + ) augmented_results = search_augment_aip_results(results, uuid_file_counts) if request_file and not file_mode: return search_as_csv(augmented_results, file_name=file_name) - hit_count = results["hits"]["total"] - return helpers.json_response( { "iTotalRecords": hit_count, @@ -379,7 +455,7 @@ def search(request): } ) - except ElasticsearchException: + except Exception: err_desc = "Error accessing AIPs index" logger.exception(err_desc) return HttpResponse(err_desc) @@ -408,7 +484,9 @@ def search_augment_aip_results(raw_results, counts): "isPartOf": fields.get("isPartOf"), "AICID": fields.get("AICID"), "countAIPsinAIC": fields.get("countAIPsinAIC", "(unknown)"), - "status": AIP_STATUS_DESCRIPTIONS[fields.get("status", es.STATUS_UPLOADED)], + "status": AIP_STATUS_DESCRIPTIONS[ + fields.get("status", archivematica.search.constants.STATUS_UPLOADED) + ], "encrypted": fields.get("encrypted", False), "accessionids": fields.get("accessionids", []), "location": fields.get("location", ""), @@ -440,10 +518,10 @@ def search_augment_aip_results(raw_results, counts): return modified_results -def search_augment_file_results(es_client, raw_results): +def search_augment_file_results(search_service, raw_results): """Augment file results. - :param es_client: Elasticsearch client. + :param search_service: Search service instance. :param raw_results: Raw results returned from ES. :return: Augmented and formatted results. @@ -457,10 +535,17 @@ def search_augment_file_results(es_client, raw_results): clone = item["_source"].copy() try: - aip = es.get_aip_data( - es_client, + aip = search_service.get_aip_data( clone["AIPUUID"], - fields="uuid,name,filePath,size,origin,created,encrypted", + fields=[ + "uuid", + "name", + "filePath", + "size", + "origin", + "created", + "encrypted", + ], ) clone["sipname"] = aip["_source"]["name"] @@ -469,12 +554,12 @@ def search_augment_file_results(es_client, raw_results): AIPSTOREPATH + "/", "AIPsStore/" ) - except ElasticsearchException: + except Exception: aip = None clone["sipname"] = False clone["status"] = AIP_STATUS_DESCRIPTIONS[ - clone.get("status", es.STATUS_UPLOADED) + clone.get("status", archivematica.search.constants.STATUS_UPLOADED) ] clone["filename"] = os.path.basename(clone["filePath"]) clone["document_id"] = item["_id"] @@ -501,7 +586,7 @@ def create_aic(request): if types[0] == "range": fields = ["indexedAt"] query = advanced_search.assemble_query(queries, ops, fields, types) - es_client = es.get_client() + search_service = setup_search_service_from_conf(settings) try: query["aggs"] = { @@ -512,12 +597,11 @@ def create_aic(request): } } } - query["size"] = 0 - results = es_client.search(body=query, index=es.AIP_FILES_INDEX) + results = search_service.search_aip_files(query, size=0) buckets = results["aggregations"]["aip_uuids"]["buckets"] aip_uuids = [bucket["key"] for bucket in buckets] - except ElasticsearchException: + except Exception: err_desc = "Error accessing AIPs index" logger.exception(err_desc) return HttpResponse(err_desc) @@ -526,8 +610,10 @@ def create_aic(request): # Use the AIP UUIDs to fetch names, which are used to produce files below. query = {"query": {"terms": {"uuid": aip_uuids}}} - results = es_client.search( - body=query, index=es.AIPS_INDEX, _source="uuid,name", size=es.MAX_QUERY_SIZE + results = search_service.search_aips( + query, + fields=["uuid", "name"], + size=archivematica.search.constants.MAX_QUERY_SIZE, ) # Create SIP (AIC) directory in a staging directory. @@ -565,15 +651,17 @@ def aip_download(request, uuid): def aip_file_download(request, uuid): - es_client = es.get_client() + search_service = setup_search_service_from_conf(settings) # get AIP file properties - aipfile = es.get_aipfile_data(es_client, uuid, fields="filePath,FILEUUID,AIPUUID") + aipfile = search_service.get_aipfile_data( + uuid, fields=["filePath", "FILEUUID", "AIPUUID"] + ) # get file's AIP's properties sipuuid = aipfile["_source"]["AIPUUID"] - aip = es.get_aip_data( - es_client, sipuuid, fields="uuid,name,filePath,size,origin,created" + aip = search_service.get_aip_data( + sipuuid, fields=["uuid", "name", "filePath", "size", "origin", "created"] ) aip_filepath = aip["_source"]["filePath"] @@ -598,10 +686,10 @@ def aip_file_download(request, uuid): def aip_mets_file_download(request, uuid): """Download an individual AIP METS file.""" - es_client = es.get_client() + search_service = setup_search_service_from_conf(settings) try: - aip = es.get_aip_data(es_client, uuid, fields="name") - except IndexError: + aip = search_service.get_aip_data(uuid, fields=["name"]) + except AIPNotFoundError: # TODO: 404 settings for the project do not display this to the user (only DEBUG). raise Http404( _("The AIP package containing the requested METS cannot be found") @@ -621,8 +709,8 @@ def aip_pointer_file_download(request, uuid): def send_thumbnail(request, fileuuid): # get AIP location to use to find root of AIP storage - es_client = es.get_client() - aipfile = es.get_aipfile_data(es_client, fileuuid, fields="AIPUUID") + search_service = setup_search_service_from_conf(settings) + aipfile = search_service.get_aipfile_data(fileuuid, fields=["AIPUUID"]) sipuuid = aipfile["_source"]["AIPUUID"] thumbnail_path = os.path.join( @@ -643,7 +731,9 @@ def send_thumbnail(request, fileuuid): def aips_pending_deletion(): aip_uuids = [] try: - aips = storage_service.get_file_info(status=es.STATUS_DELETE_REQUESTED) + aips = storage_service.get_file_info( + status=archivematica.search.constants.STATUS_DELETE_REQUESTED + ) except Exception as e: # TODO this should be messages.warning, but we need 'request' here logger.warning( @@ -670,16 +760,16 @@ def elasticsearch_query_excluding_aips_pending_deletion(uuid_field_name): return query -def aip_file_count(es_client): +def aip_file_count(search_service): query = elasticsearch_query_excluding_aips_pending_deletion("AIPUUID") - return advanced_search.indexed_count(es_client, es.AIP_FILES_INDEX, query) + return search_service.count_aip_files(query) -def total_size_of_aips(es_client): +def total_size_of_aips(search_service): query = elasticsearch_query_excluding_aips_pending_deletion("uuid") query["_source"] = "size" query["aggs"] = {"total": {"sum": {"field": "size"}}} - results = es_client.search(body=query, index=es.AIPS_INDEX) + results = search_service.search_aips(query, size=0) # TODO handle the return object total_size = results["aggregations"]["total"]["value"] # Size is stored in ES as MBs @@ -691,23 +781,25 @@ def total_size_of_aips(es_client): def _document_json_response(document_id_modified, index): document_id = document_id_modified.replace("____", "-") - es_client = es.get_client() - data = es_client.get(index=index, doc_type=es.DOC_TYPE, id=document_id) + search_service = setup_search_service_from_conf(settings) + data = search_service.get_aipfile_by_document_id(document_id) pretty_json = json.dumps(data, sort_keys=True, indent=2) return HttpResponse(pretty_json, content_type="application/json") def file_json(request, document_id_modified): - return _document_json_response(document_id_modified, es.AIP_FILES_INDEX) + return _document_json_response( + document_id_modified, archivematica.search.constants.AIP_FILES_INDEX + ) def view_aip(request, uuid): - es_client = es.get_client() + search_service = setup_search_service_from_conf(settings) try: - es_aip_doc = es.get_aip_data( - es_client, uuid, fields="name,size,created,status,filePath,encrypted" + es_aip_doc = search_service.get_aip_data( + uuid, fields=["name", "size", "created", "status", "filePath", "encrypted"] ) - except IndexError: + except AIPNotFoundError: raise Http404 source = es_aip_doc["_source"] @@ -784,15 +876,17 @@ def view_aip(request, uuid): form_delete.cleaned_data["reason"], ) messages.info(request, response["message"]) - es_client = es.get_client() - es.mark_aip_deletion_requested(es_client, uuid) + search_service = setup_search_service_from_conf(settings) + search_service.mark_aip_for_deletion(uuid) return redirect("archival_storage:archival_storage_index") context = { "uuid": uuid, "name": name, "created": source.get("created"), - "status": AIP_STATUS_DESCRIPTIONS[source.get("status", es.STATUS_UPLOADED)], + "status": AIP_STATUS_DESCRIPTIONS[ + source.get("status", archivematica.search.constants.STATUS_UPLOADED) + ], "encrypted": source.get("encrypted", False), "size": "{:.2f} MB".format(source.get("size", 0)), "location_basename": os.path.basename(source.get("filePath")), diff --git a/src/dashboard/src/components/filesystem_ajax/__init__.py b/src/archivematica/dashboard/components/backlog/__init__.py similarity index 100% rename from src/dashboard/src/components/filesystem_ajax/__init__.py rename to src/archivematica/dashboard/components/backlog/__init__.py diff --git a/src/dashboard/src/components/backlog/urls.py b/src/archivematica/dashboard/components/backlog/urls.py similarity index 95% rename from src/dashboard/src/components/backlog/urls.py rename to src/archivematica/dashboard/components/backlog/urls.py index a5ef37e8e7..b483a5289e 100644 --- a/src/dashboard/src/components/backlog/urls.py +++ b/src/archivematica/dashboard/components/backlog/urls.py @@ -14,11 +14,12 @@ # # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . -from components.backlog import views from django.conf import settings from django.urls import path from django.urls import re_path +from archivematica.dashboard.components.backlog import views + app_name = "backlog" urlpatterns = [ path("", views.execute, name="backlog_index"), diff --git a/src/dashboard/src/components/backlog/views.py b/src/archivematica/dashboard/components/backlog/views.py similarity index 66% rename from src/dashboard/src/components/backlog/views.py rename to src/archivematica/dashboard/components/backlog/views.py index 0b2ecdf2fe..c737a1cb2e 100644 --- a/src/dashboard/src/components/backlog/views.py +++ b/src/archivematica/dashboard/components/backlog/views.py @@ -17,14 +17,7 @@ import json import logging -import elasticSearchFunctions as es import requests -import storageService as storage_service -from archivematicaFunctions import AMCLIENT_ERROR_CODES -from archivematicaFunctions import setup_amclient -from components import advanced_search -from components import decorators -from components import helpers from django.conf import settings from django.contrib import messages from django.http import Http404 @@ -35,6 +28,19 @@ from django.urls import reverse from django.utils.translation import gettext as _ +import archivematica.search.constants +from archivematica.archivematicaCommon import storageService as storage_service +from archivematica.archivematicaCommon.archivematicaFunctions import ( + AMCLIENT_ERROR_CODES, +) +from archivematica.archivematicaCommon.archivematicaFunctions import setup_amclient +from archivematica.dashboard.components import advanced_search +from archivematica.dashboard.components import decorators +from archivematica.dashboard.components import helpers +from archivematica.search.constants import STATUS_BACKLOG +from archivematica.search.service import SortSpec +from archivematica.search.service import setup_search_service_from_conf + logger = logging.getLogger("archivematica.dashboard") @@ -74,17 +80,23 @@ def sync_es_transfer_status_with_storage_service(uuid, pending_deletion): ) return keep_in_results - if transfer_status == es.STATUS_DELETE_REQUESTED and pending_deletion is False: - es_client = es.get_client() - es.mark_backlog_deletion_requested(es_client, uuid) - elif transfer_status == es.STATUS_UPLOADED and pending_deletion is True: - es_client = es.get_client() - es.revert_backlog_deletion_request(es_client, uuid) - elif transfer_status == es.STATUS_DELETED: + search_service = setup_search_service_from_conf(settings) + + if ( + transfer_status == archivematica.search.constants.STATUS_DELETE_REQUESTED + and pending_deletion is False + ): + search_service.mark_transfer_for_deletion(uuid) + elif ( + transfer_status == archivematica.search.constants.STATUS_UPLOADED + and pending_deletion is True + ): + search_service.unmark_transfer_for_deletion(uuid) + elif transfer_status == archivematica.search.constants.STATUS_DELETED: keep_in_results = False - es_client = es.get_client() - es.remove_backlog_transfer_files(es_client, uuid) - es.remove_backlog_transfer(es_client, uuid) + transfer_ids = {uuid} + search_service.delete_transfer_files(transfer_ids) + search_service.delete_transfer(uuid) return keep_in_results @@ -138,6 +150,95 @@ def get_es_property_from_column_index(index, file_mode): return table_columns[file_mode][index] +def _search_transfer_files( + query: dict, + search_service, + start: int, + page_size: int, + order_by: str, + sort_direction: str, +) -> tuple: + """ + Execute Elasticsearch search for transfer files using search service. + + :param query: The Elasticsearch query to execute + :param search_service: The search service instance + :param start: Start index for pagination + :param page_size: Number of results per page + :param order_by: Field to sort by + :param sort_direction: Sort direction (asc/desc) + :return: Tuple of (hits, hit_count) + """ + sort_param = SortSpec(field=order_by, order=sort_direction) if order_by else None + fields = ["filename", "sipuuid", "relative_path", "accessionid", "pending_deletion"] + + hits = search_service.search_transfer_files( + query=query, size=page_size, from_=start, sort=sort_param, fields=fields + ) + hit_count = hits["hits"]["total"]["value"] + return hits, hit_count + + +def _search_transfers( + query: dict, + search_service, + start: int, + page_size: int, + order_by: str, + sort_direction: str, +) -> tuple: + """ + Execute Elasticsearch search for transfers. + + :param query: The Elasticsearch query to execute + :param search_service: The search service instance + :param start: Start index for pagination + :param page_size: Number of results per page + :param order_by: Field to sort by + :param sort_direction: Sort direction (asc/desc) + :return: Tuple of (hits, hit_count) + """ + # Transfer mode: + # Query to transferfile, but only fetch & aggregrate transfer UUIDs. + # Based on transfer UUIDs, query to transfers. + # ES query will limit to 10 aggregation results by default, + # add size parameter in terms to override. + # TODO: Use composite aggregation when it gets out of beta. + query["aggs"] = { + "transfer_uuid": { + "terms": { + "field": "sipuuid", + "size": str(settings.ELASTICSEARCH_MAX_QUERY_SIZE), + } + } + } + hits = search_service.search_transfer_files(query, size=0) + uuids = [x["key"] for x in hits["aggregations"]["transfer_uuid"]["buckets"]] + + # Recreate query to search over transfers + transfer_query = {"query": {"terms": {"uuid": uuids}}} + sort_param = SortSpec(field=order_by, order=sort_direction) if order_by else None + fields = [ + "name", + "uuid", + "file_count", + "ingest_date", + "accessionid", + "size", + "pending_deletion", + ] + + hits = search_service.search_transfers( + query=transfer_query, + size=page_size, + from_=start, + sort=sort_param, + fields=fields, + ) + hit_count = hits["hits"]["total"]["value"] + return hits, hit_count + + def search(request): """ A JSON end point that returns results for various backlog transfers and their files. @@ -157,58 +258,30 @@ def search(request): ) sort_direction = request.GET.get("sSortDir_0", "asc") - es_client = es.get_client() + search_service = setup_search_service_from_conf(settings) if "query" not in request.GET: queries, ops, fields, types = (["*"], ["or"], [""], ["term"]) query = advanced_search.assemble_query( - queries, ops, fields, types, filters=[{"term": {"status": "backlog"}}] + queries, ops, fields, types, filters=[{"term": {"status": STATUS_BACKLOG}}] ) try: if file_mode: - index = es.TRANSFER_FILES_INDEX - source = "filename,sipuuid,relative_path,accessionid,pending_deletion" - else: - # Transfer mode: - # Query to transferfile, but only fetch & aggregrate transfer UUIDs. - # Based on transfer UUIDs, query to transfers. - # ES query will limit to 10 aggregation results by default, - # add size parameter in terms to override. - # TODO: Use composite aggregation when it gets out of beta. - query["aggs"] = { - "transfer_uuid": { - "terms": { - "field": "sipuuid", - "size": str(settings.ELASTICSEARCH_MAX_QUERY_SIZE), - } - } - } - hits = es_client.search( - index=es.TRANSFER_FILES_INDEX, - body=query, - size=0, # Don't return results, only aggregation + hits, hit_count = _search_transfer_files( + query, search_service, start, page_size, order_by, sort_direction ) - uuids = [x["key"] for x in hits["aggregations"]["transfer_uuid"]["buckets"]] - - # Recreate query to search over transfers - query = {"query": {"terms": {"uuid": uuids}}} - index = es.TRANSFERS_INDEX - source = ( - "name,uuid,file_count,ingest_date,accessionid,size,pending_deletion" + else: + hits, hit_count = _search_transfers( + query, + search_service, + start, + page_size, + order_by, + sort_direction, ) - hits = es_client.search( - index=index, - body=query, - from_=start, - size=page_size, - sort=order_by + ":" + sort_direction if order_by else "", - _source=source, - ) - hit_count = hits["hits"]["total"] - except Exception: err_desc = "Error accessing transfers index" logger.exception(err_desc) @@ -279,8 +352,8 @@ def delete(request, uuid): ) messages.info(request, response["message"]) - es_client = es.get_client() - es.mark_backlog_deletion_requested(es_client, uuid) + search_service = setup_search_service_from_conf(settings) + search_service.mark_transfer_for_deletion(uuid) except requests.exceptions.ConnectionError: messages.warning( diff --git a/src/dashboard/src/components/decorators.py b/src/archivematica/dashboard/components/decorators.py similarity index 97% rename from src/dashboard/src/components/decorators.py rename to src/archivematica/dashboard/components/decorators.py index 0c74bf6b48..1e2eceb69a 100644 --- a/src/dashboard/src/components/decorators.py +++ b/src/archivematica/dashboard/components/decorators.py @@ -17,7 +17,8 @@ from django.http import Http404 from django.shortcuts import render from django.utils.functional import wraps -from main import models + +from archivematica.dashboard.main import models # Try to update context instead of sending new params diff --git a/src/dashboard/src/components/ingest/__init__.py b/src/archivematica/dashboard/components/file/__init__.py similarity index 100% rename from src/dashboard/src/components/ingest/__init__.py rename to src/archivematica/dashboard/components/file/__init__.py diff --git a/src/dashboard/src/components/file/urls.py b/src/archivematica/dashboard/components/file/urls.py similarity index 90% rename from src/dashboard/src/components/file/urls.py rename to src/archivematica/dashboard/components/file/urls.py index 2829d70ddf..d964af16e2 100644 --- a/src/dashboard/src/components/file/urls.py +++ b/src/archivematica/dashboard/components/file/urls.py @@ -1,7 +1,8 @@ -from components.file import views from django.conf import settings from django.urls import re_path +from archivematica.dashboard.components.file import views + app_name = "file" urlpatterns = [ re_path( diff --git a/src/dashboard/src/components/file/views.py b/src/archivematica/dashboard/components/file/views.py similarity index 66% rename from src/dashboard/src/components/file/views.py rename to src/archivematica/dashboard/components/file/views.py index 7eadd86dfc..9d2df7d270 100644 --- a/src/dashboard/src/components/file/views.py +++ b/src/archivematica/dashboard/components/file/views.py @@ -2,13 +2,16 @@ import logging import os -import elasticSearchFunctions import requests -import storageService as storage_service -from components import helpers from django.conf import settings as django_settings from django.views.generic import View -from main import models + +from archivematica.archivematicaCommon import storageService as storage_service +from archivematica.dashboard.components import helpers +from archivematica.dashboard.main import models +from archivematica.search.service import SearchServiceError +from archivematica.search.service import TransferFileNotFoundError +from archivematica.search.service import setup_search_service_from_conf logger = logging.getLogger("archivematica.dashboard") @@ -26,14 +29,15 @@ def get(self, request, fileuuid): Returns a JSON-encoded list of the file's tags on success. """ try: - es_client = elasticSearchFunctions.get_client() - tags = elasticSearchFunctions.get_file_tags(es_client, fileuuid) - except elasticSearchFunctions.ElasticsearchError as e: + search_service = setup_search_service_from_conf(django_settings) + tags = search_service.get_transfer_file_tags(fileuuid) + except TransferFileNotFoundError as e: + response = {"success": False, "message": str(e)} + status_code = 404 + return helpers.json_response(response, status_code=status_code) + except Exception as e: response = {"success": False, "message": str(e)} - if isinstance(e, elasticSearchFunctions.EmptySearchResultError): - status_code = 404 - else: - status_code = 400 + status_code = 400 return helpers.json_response(response, status_code=status_code) return helpers.json_response(tags) @@ -61,14 +65,15 @@ def put(self, request, fileuuid): return helpers.json_response(response, status_code=400) try: - es_client = elasticSearchFunctions.get_client() - elasticSearchFunctions.set_file_tags(es_client, fileuuid, tags) - except elasticSearchFunctions.ElasticsearchError as e: + search_service = setup_search_service_from_conf(django_settings) + search_service.set_transfer_file_tags(fileuuid, tags) + except TransferFileNotFoundError as e: + response = {"success": False, "message": str(e)} + status_code = 404 + return helpers.json_response(response, status_code=status_code) + except SearchServiceError as e: response = {"success": False, "message": str(e)} - if isinstance(e, elasticSearchFunctions.EmptySearchResultError): - status_code = 404 - else: - status_code = 400 + status_code = 400 return helpers.json_response(response, status_code=status_code) return helpers.json_response({"success": True}) @@ -78,14 +83,15 @@ def delete(self, request, fileuuid): Deletes all tags for the given file. """ try: - es_client = elasticSearchFunctions.get_client() - elasticSearchFunctions.set_file_tags(es_client, fileuuid, []) - except elasticSearchFunctions.ElasticsearchError as e: + search_service = setup_search_service_from_conf(django_settings) + search_service.set_transfer_file_tags(fileuuid, []) + except TransferFileNotFoundError as e: + response = {"success": False, "message": str(e)} + status_code = 404 + return helpers.json_response(response, status_code=status_code) + except SearchServiceError as e: response = {"success": False, "message": str(e)} - if isinstance(e, elasticSearchFunctions.EmptySearchResultError): - status_code = 404 - else: - status_code = 400 + status_code = 400 return helpers.json_response(response, status_code=status_code) return helpers.json_response({"success": True}) @@ -119,18 +125,22 @@ def bulk_extractor(request, fileuuid): return helpers.json_response(response, status_code=400) try: - es_client = elasticSearchFunctions.get_client() - record = elasticSearchFunctions.get_transfer_file_info( - es_client, "fileuuid", fileuuid - ) - except elasticSearchFunctions.ElasticsearchError as e: + search_service = setup_search_service_from_conf(django_settings) + record = search_service.get_transfer_file_data(fileuuid) + except TransferFileNotFoundError: + response = { + "success": False, + "message": "get_transfer_file_info returned no exact results", + } + return helpers.json_response(response, status_code=404) + except SearchServiceError as e: message = str(e) response = {"success": False, "message": message} - if "no exact results" in message: - status_code = 404 - else: - status_code = 500 - return helpers.json_response(response, status_code=status_code) + return helpers.json_response(response, status_code=500) + except Exception as e: + message = str(e) + response = {"success": False, "message": message} + return helpers.json_response(response, status_code=500) bulk_extractor_reports = record.get("bulk_extractor_reports", []) missing_reports = [] @@ -184,18 +194,22 @@ def _parse_bulk_extractor_report(data): def file_details(request, fileuuid): try: - es_client = elasticSearchFunctions.get_client() - source = elasticSearchFunctions.get_transfer_file_info( - es_client, "fileuuid", fileuuid - ) - except elasticSearchFunctions.ElasticsearchError as e: + search_service = setup_search_service_from_conf(django_settings) + source = search_service.get_transfer_file_data(fileuuid) + except TransferFileNotFoundError: + response = { + "success": False, + "message": "get_transfer_file_info returned no exact results", + } + return helpers.json_response(response, status_code=404) + except SearchServiceError as e: message = str(e) response = {"success": False, "message": message} - if "no exact results" in message: - status_code = 404 - else: - status_code = 500 - return helpers.json_response(response, status_code=status_code) + return helpers.json_response(response, status_code=500) + except Exception as e: + message = str(e) + response = {"success": False, "message": message} + return helpers.json_response(response, status_code=500) format_info = source.get("format", [{}])[0] record = { diff --git a/src/dashboard/src/components/mcp/__init__.py b/src/archivematica/dashboard/components/filesystem_ajax/__init__.py similarity index 100% rename from src/dashboard/src/components/mcp/__init__.py rename to src/archivematica/dashboard/components/filesystem_ajax/__init__.py diff --git a/src/dashboard/src/components/filesystem_ajax/helpers.py b/src/archivematica/dashboard/components/filesystem_ajax/helpers.py similarity index 94% rename from src/dashboard/src/components/filesystem_ajax/helpers.py rename to src/archivematica/dashboard/components/filesystem_ajax/helpers.py index 6a6d33a708..aea98ad0d1 100644 --- a/src/dashboard/src/components/filesystem_ajax/helpers.py +++ b/src/archivematica/dashboard/components/filesystem_ajax/helpers.py @@ -17,8 +17,8 @@ import logging import os -from archivematicaFunctions import b64encode_string -from components import helpers +from archivematica.archivematicaCommon.archivematicaFunctions import b64encode_string +from archivematica.dashboard.components import helpers logger = logging.getLogger("archivematica.dashboard") diff --git a/src/dashboard/src/components/filesystem_ajax/urls.py b/src/archivematica/dashboard/components/filesystem_ajax/urls.py similarity index 96% rename from src/dashboard/src/components/filesystem_ajax/urls.py rename to src/archivematica/dashboard/components/filesystem_ajax/urls.py index 5ca1ae0583..3b1261d2a1 100644 --- a/src/dashboard/src/components/filesystem_ajax/urls.py +++ b/src/archivematica/dashboard/components/filesystem_ajax/urls.py @@ -14,11 +14,12 @@ # # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . -from components.filesystem_ajax import views from django.conf import settings from django.urls import path from django.urls import re_path +from archivematica.dashboard.components.filesystem_ajax import views + app_name = "filesystem_ajax" urlpatterns = [ path("download_ss/", views.download_ss), diff --git a/src/dashboard/src/components/filesystem_ajax/views.py b/src/archivematica/dashboard/components/filesystem_ajax/views.py similarity index 98% rename from src/dashboard/src/components/filesystem_ajax/views.py rename to src/archivematica/dashboard/components/filesystem_ajax/views.py index 20acde5de0..0d92eeed4b 100644 --- a/src/dashboard/src/components/filesystem_ajax/views.py +++ b/src/archivematica/dashboard/components/filesystem_ajax/views.py @@ -22,20 +22,22 @@ import tempfile import uuid -import archivematicaFunctions -import components.filesystem_ajax.helpers as filesystem_ajax_helpers -import databaseFunctions import django.http import django.template.defaultfilters -import elasticSearchFunctions -import storageService as storage_service -from archivematicaFunctions import b64decode_string -from archivematicaFunctions import b64encode_string -from components import helpers from django.conf import settings as django_settings from django.utils.translation import gettext as _ from django.utils.translation import ngettext -from main import models + +import archivematica.dashboard.components.filesystem_ajax.helpers as filesystem_ajax_helpers +from archivematica.archivematicaCommon import archivematicaFunctions +from archivematica.archivematicaCommon import databaseFunctions +from archivematica.archivematicaCommon import storageService as storage_service +from archivematica.archivematicaCommon.archivematicaFunctions import b64decode_string +from archivematica.archivematicaCommon.archivematicaFunctions import b64encode_string +from archivematica.dashboard.components import helpers +from archivematica.dashboard.main import models +from archivematica.search.service import SearchServiceError +from archivematica.search.service import setup_search_service_from_conf logger = logging.getLogger("archivematica.dashboard") @@ -1119,10 +1121,9 @@ def download_by_uuid(request, uuid, preview_file=False): ) try: - record = elasticSearchFunctions.get_transfer_file_info( - elasticSearchFunctions.get_client(), "fileuuid", uuid - ) - except elasticSearchFunctions.ElasticsearchError: + search_service = setup_search_service_from_conf(django_settings) + record = search_service.get_transfer_file_data(uuid) + except SearchServiceError: return not_found_err try: diff --git a/src/dashboard/src/components/helpers.py b/src/archivematica/dashboard/components/helpers.py similarity index 99% rename from src/dashboard/src/components/helpers.py rename to src/archivematica/dashboard/components/helpers.py index 8404bbd740..279db1d38c 100644 --- a/src/dashboard/src/components/helpers.py +++ b/src/archivematica/dashboard/components/helpers.py @@ -38,9 +38,10 @@ from django.urls import reverse from django.utils.dateformat import format from django.utils.translation import gettext as _ -from main import models from tastypie.models import ApiKey +from archivematica.dashboard.main import models + logger = logging.getLogger("archivematica.dashboard") @@ -451,9 +452,12 @@ def generate_api_key(user): Generate API key for a user """ api_key, _ = ApiKey.objects.get_or_create(user=user) - api_key.key = api_key.generate_key() + result = api_key.generate_key() + api_key.key = result api_key.save() + return result + def get_api_allowlist(): """Get API allowlist setting.""" diff --git a/src/dashboard/src/components/rights/__init__.py b/src/archivematica/dashboard/components/ingest/__init__.py similarity index 100% rename from src/dashboard/src/components/rights/__init__.py rename to src/archivematica/dashboard/components/ingest/__init__.py diff --git a/src/dashboard/src/components/ingest/forms.py b/src/archivematica/dashboard/components/ingest/forms.py similarity index 98% rename from src/dashboard/src/components/ingest/forms.py rename to src/archivematica/dashboard/components/ingest/forms.py index d6832f2d3d..554cf2cec2 100644 --- a/src/dashboard/src/components/ingest/forms.py +++ b/src/archivematica/dashboard/components/ingest/forms.py @@ -16,7 +16,8 @@ # along with Archivematica. If not, see . from django import forms from django.conf import settings -from main import models + +from archivematica.dashboard.main import models class DublinCoreMetadataForm(forms.ModelForm): diff --git a/src/dashboard/src/components/ingest/pair_matcher.py b/src/archivematica/dashboard/components/ingest/pair_matcher.py similarity index 98% rename from src/dashboard/src/components/ingest/pair_matcher.py rename to src/archivematica/dashboard/components/ingest/pair_matcher.py index c6b25b8fc3..a3d032e0bc 100644 --- a/src/dashboard/src/components/ingest/pair_matcher.py +++ b/src/archivematica/dashboard/components/ingest/pair_matcher.py @@ -2,7 +2,6 @@ import os import xml.etree.ElementTree as ElementTree -from components import helpers from django.conf import settings as django_settings from django.http import Http404 from django.http import HttpResponse @@ -10,7 +9,9 @@ from django.shortcuts import render from django.urls import reverse from lazy_paged_sequence import LazyPagedSequence -from main import models + +from archivematica.dashboard.components import helpers +from archivematica.dashboard.main import models PAGE_SIZE = 30 diff --git a/src/dashboard/src/components/ingest/urls.py b/src/archivematica/dashboard/components/ingest/urls.py similarity index 97% rename from src/dashboard/src/components/ingest/urls.py rename to src/archivematica/dashboard/components/ingest/urls.py index 95ec61d23a..e1d2830f80 100644 --- a/src/dashboard/src/components/ingest/urls.py +++ b/src/archivematica/dashboard/components/ingest/urls.py @@ -14,12 +14,13 @@ # # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . -from components.ingest import views -from components.ingest import views_as from django.conf import settings from django.urls import path from django.urls import re_path +from archivematica.dashboard.components.ingest import views +from archivematica.dashboard.components.ingest import views_as + app_name = "ingest" urlpatterns = [ path("", views.ingest_grid, name="ingest_index"), diff --git a/src/dashboard/src/components/ingest/views.py b/src/archivematica/dashboard/components/ingest/views.py similarity index 95% rename from src/dashboard/src/components/ingest/views.py rename to src/archivematica/dashboard/components/ingest/views.py index 4627558c49..c02719d465 100644 --- a/src/dashboard/src/components/ingest/views.py +++ b/src/archivematica/dashboard/components/ingest/views.py @@ -22,16 +22,7 @@ import uuid from urllib.parse import urljoin -import elasticSearchFunctions import requests -import storageService as storage_service -from archivematicaFunctions import b64encode_string -from components import advanced_search -from components import decorators -from components import helpers -from components.ingest import forms as ingest_forms -from components.ingest.views_NormalizationReport import getNormalizationReportQuery -from contrib.mcp.client import MCPClient from django.conf import settings as django_settings from django.contrib import messages from django.core.exceptions import ValidationError @@ -45,8 +36,22 @@ from django.utils.text import slugify from django.utils.translation import gettext as _ from django.views.generic import View -from main import forms -from main import models + +from archivematica.archivematicaCommon import storageService as storage_service +from archivematica.archivematicaCommon.archivematicaFunctions import b64encode_string +from archivematica.dashboard.components import advanced_search +from archivematica.dashboard.components import decorators +from archivematica.dashboard.components import helpers +from archivematica.dashboard.components.ingest import forms as ingest_forms +from archivematica.dashboard.components.ingest.views_NormalizationReport import ( + getNormalizationReportQuery, +) +from archivematica.dashboard.contrib.mcp.client import MCPClient +from archivematica.dashboard.main import forms +from archivematica.dashboard.main import models +from archivematica.search import utils +from archivematica.search.constants import STATUS_BACKLOG +from archivematica.search.service import setup_search_service_from_conf logger = logging.getLogger("archivematica.dashboard") @@ -602,11 +607,11 @@ def transfer_backlog(request, ui): """ AJAX endpoint to query for and return transfer backlog items. """ - es_client = elasticSearchFunctions.get_client() + search_service = setup_search_service_from_conf(django_settings) results = None # Return files which are in the backlog - backlog_filter = {"bool": {"must": {"term": {"status": "backlog"}}}} + backlog_filter = {"bool": {"must": {"term": {"status": STATUS_BACKLOG}}}} # Omit files without UUIDs (metadata and logs directories): # - When the `hidemetadatalogs` param is sent from SIP arrange. if request.GET.get("hidemetadatalogs"): @@ -629,15 +634,13 @@ def transfer_backlog(request, ui): # perform search try: - results = elasticSearchFunctions.search_all_results( - es_client, body=query, index="transferfiles" - ) + results = search_service.search_transfer_files(query) except Exception: logger.exception("Error accessing index.") return HttpResponse("Error accessing index.") # Convert results into a more workable form - results = elasticSearchFunctions.augment_raw_search_results(results) + results = utils.augment_raw_search_results(results) # Convert to a form JS can use: # [{'name': , diff --git a/src/dashboard/src/components/ingest/views_NormalizationReport.py b/src/archivematica/dashboard/components/ingest/views_NormalizationReport.py similarity index 98% rename from src/dashboard/src/components/ingest/views_NormalizationReport.py rename to src/archivematica/dashboard/components/ingest/views_NormalizationReport.py index c170a2355a..0ef340a0de 100755 --- a/src/dashboard/src/components/ingest/views_NormalizationReport.py +++ b/src/archivematica/dashboard/components/ingest/views_NormalizationReport.py @@ -16,9 +16,10 @@ # # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . -from components import helpers from django.db import connection +from archivematica.dashboard.components import helpers + def getNormalizationReportQuery(sipUUID, idsRestriction=""): if idsRestriction: diff --git a/src/dashboard/src/components/ingest/views_as.py b/src/archivematica/dashboard/components/ingest/views_as.py similarity index 96% rename from src/dashboard/src/components/ingest/views_as.py rename to src/archivematica/dashboard/components/ingest/views_as.py index aeaadfc8d1..3b88c8f4ff 100644 --- a/src/dashboard/src/components/ingest/views_as.py +++ b/src/archivematica/dashboard/components/ingest/views_as.py @@ -6,9 +6,6 @@ from agentarchives.archivesspace import ArchivesSpaceError from agentarchives.archivesspace import AuthenticationError from agentarchives.archivesspace import ConnectionError -from components import advanced_search -from components.ingest import pair_matcher -from contrib.mcp.client import MCPClient from django.contrib import messages from django.http import HttpResponse from django.http import HttpResponseBadRequest @@ -16,7 +13,11 @@ from django.http import HttpResponseServerError from django.urls import reverse from django.utils.translation import gettext as _ -from main import models + +from archivematica.dashboard.components import advanced_search +from archivematica.dashboard.components.ingest import pair_matcher +from archivematica.dashboard.contrib.mcp.client import MCPClient +from archivematica.dashboard.main import models logger = logging.getLogger("archivematica.dashboard") @@ -25,7 +26,7 @@ def get_as_system_client(): config = models.DashboardSetting.objects.get_dict("upload-archivesspace_v0.0") config_err = ArchivesSpaceError( - "ArchivesSpace has not been configured properly" " - base URL is missing" + "ArchivesSpace has not been configured properly - base URL is missing" ) try: base_url = config["base_url"] diff --git a/src/dashboard/src/components/transfer/__init__.py b/src/archivematica/dashboard/components/mcp/__init__.py similarity index 100% rename from src/dashboard/src/components/transfer/__init__.py rename to src/archivematica/dashboard/components/mcp/__init__.py diff --git a/src/dashboard/src/components/mcp/urls.py b/src/archivematica/dashboard/components/mcp/urls.py similarity index 94% rename from src/dashboard/src/components/mcp/urls.py rename to src/archivematica/dashboard/components/mcp/urls.py index 78dd73a717..f099b05ac0 100644 --- a/src/dashboard/src/components/mcp/urls.py +++ b/src/archivematica/dashboard/components/mcp/urls.py @@ -14,9 +14,10 @@ # # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . -from components.mcp import views from django.urls import path +from archivematica.dashboard.components.mcp import views + app_name = "mcp" urlpatterns = [ path("execute/", views.execute, name="execute"), diff --git a/src/dashboard/src/components/mcp/views.py b/src/archivematica/dashboard/components/mcp/views.py similarity index 95% rename from src/dashboard/src/components/mcp/views.py rename to src/archivematica/dashboard/components/mcp/views.py index a60373d304..0c75c6fc33 100644 --- a/src/dashboard/src/components/mcp/views.py +++ b/src/archivematica/dashboard/components/mcp/views.py @@ -14,10 +14,11 @@ # # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . -from contrib.mcp.client import MCPClient from django.http import HttpResponse from lxml import etree +from archivematica.dashboard.contrib.mcp.client import MCPClient + def execute(request): result = "" diff --git a/src/dashboard/src/components/unit/__init__.py b/src/archivematica/dashboard/components/rights/__init__.py similarity index 100% rename from src/dashboard/src/components/unit/__init__.py rename to src/archivematica/dashboard/components/rights/__init__.py diff --git a/src/dashboard/src/components/rights/forms.py b/src/archivematica/dashboard/components/rights/forms.py similarity index 99% rename from src/dashboard/src/components/rights/forms.py rename to src/archivematica/dashboard/components/rights/forms.py index b474b80be7..a2ce699fa3 100644 --- a/src/dashboard/src/components/rights/forms.py +++ b/src/archivematica/dashboard/components/rights/forms.py @@ -17,7 +17,8 @@ from django import forms from django.conf import settings from django.utils.translation import gettext as _ -from main import models + +from archivematica.dashboard.main import models class RightsForm(forms.ModelForm): diff --git a/src/dashboard/src/components/rights/ingest_urls.py b/src/archivematica/dashboard/components/rights/ingest_urls.py similarity index 95% rename from src/dashboard/src/components/rights/ingest_urls.py rename to src/archivematica/dashboard/components/rights/ingest_urls.py index d1715e96ed..4eb92280c8 100644 --- a/src/dashboard/src/components/rights/ingest_urls.py +++ b/src/archivematica/dashboard/components/rights/ingest_urls.py @@ -14,9 +14,10 @@ # # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . -from components.rights import views from django.urls import path +from archivematica.dashboard.components.rights import views + app_name = "rights_ingest" urlpatterns = [ path("", views.ingest_rights_list, name="index"), diff --git a/src/dashboard/src/components/rights/load.py b/src/archivematica/dashboard/components/rights/load.py similarity index 95% rename from src/dashboard/src/components/rights/load.py rename to src/archivematica/dashboard/components/rights/load.py index 9265f635fa..f82f42bf95 100644 --- a/src/dashboard/src/components/rights/load.py +++ b/src/archivematica/dashboard/components/rights/load.py @@ -7,16 +7,18 @@ ``premisrw``. """ -from components.helpers import get_metadata_type_id_by_description -from main.models import SIP -from main.models import File -from main.models import RightsStatement -from main.models import RightsStatementCopyright -from main.models import RightsStatementLicense -from main.models import RightsStatementOtherRightsInformation -from main.models import RightsStatementRightsGranted -from main.models import RightsStatementStatuteInformation -from main.models import Transfer +from archivematica.dashboard.components.helpers import ( + get_metadata_type_id_by_description, +) +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import RightsStatement +from archivematica.dashboard.main.models import RightsStatementCopyright +from archivematica.dashboard.main.models import RightsStatementLicense +from archivematica.dashboard.main.models import RightsStatementOtherRightsInformation +from archivematica.dashboard.main.models import RightsStatementRightsGranted +from archivematica.dashboard.main.models import RightsStatementStatuteInformation +from archivematica.dashboard.main.models import Transfer def load_rights(obj, rights): diff --git a/src/dashboard/src/components/rights/transfer_urls.py b/src/archivematica/dashboard/components/rights/transfer_urls.py similarity index 95% rename from src/dashboard/src/components/rights/transfer_urls.py rename to src/archivematica/dashboard/components/rights/transfer_urls.py index dfed6f5c1e..5e3d434464 100644 --- a/src/dashboard/src/components/rights/transfer_urls.py +++ b/src/archivematica/dashboard/components/rights/transfer_urls.py @@ -14,9 +14,10 @@ # # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . -from components.rights import views from django.urls import path +from archivematica.dashboard.components.rights import views + app_name = "rights_transfer" urlpatterns = [ path("", views.transfer_rights_list, name="index"), diff --git a/src/dashboard/src/components/rights/views.py b/src/archivematica/dashboard/components/rights/views.py similarity index 99% rename from src/dashboard/src/components/rights/views.py rename to src/archivematica/dashboard/components/rights/views.py index 6e28b0f9af..490b18bce0 100644 --- a/src/dashboard/src/components/rights/views.py +++ b/src/archivematica/dashboard/components/rights/views.py @@ -17,16 +17,17 @@ import logging import re -from components import decorators -from components import helpers -from components.rights import forms from django.forms.models import inlineformset_factory from django.http import HttpResponse from django.shortcuts import redirect from django.shortcuts import render from django.urls import reverse from django.utils.translation import gettext as _ -from main import models + +from archivematica.dashboard.components import decorators +from archivematica.dashboard.components import helpers +from archivematica.dashboard.components.rights import forms +from archivematica.dashboard.main import models LOGGER = logging.getLogger("archivematica.dashboard") diff --git a/src/dashboard/src/contrib/__init__.py b/src/archivematica/dashboard/components/transfer/__init__.py similarity index 100% rename from src/dashboard/src/contrib/__init__.py rename to src/archivematica/dashboard/components/transfer/__init__.py diff --git a/src/dashboard/src/components/transfer/urls.py b/src/archivematica/dashboard/components/transfer/urls.py similarity index 97% rename from src/dashboard/src/components/transfer/urls.py rename to src/archivematica/dashboard/components/transfer/urls.py index 0417cb7c34..eacc4c6e76 100644 --- a/src/dashboard/src/components/transfer/urls.py +++ b/src/archivematica/dashboard/components/transfer/urls.py @@ -14,11 +14,12 @@ # # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . -from components.transfer import views from django.conf import settings from django.urls import path from django.urls import re_path +from archivematica.dashboard.components.transfer import views + app_name = "transfer" urlpatterns = [ path("", views.grid, name="transfer_index"), diff --git a/src/dashboard/src/components/transfer/views.py b/src/archivematica/dashboard/components/transfer/views.py similarity index 95% rename from src/dashboard/src/components/transfer/views.py rename to src/archivematica/dashboard/components/transfer/views.py index 2909858438..3be65a1f34 100644 --- a/src/dashboard/src/components/transfer/views.py +++ b/src/archivematica/dashboard/components/transfer/views.py @@ -18,18 +18,19 @@ import logging from uuid import uuid4 -import components.decorators as decorators -import storageService as storage_service -from components import helpers -from components.ingest.forms import DublinCoreMetadataForm -from contrib.mcp.client import MCPClient from django.conf import settings as django_settings from django.core.exceptions import ValidationError from django.http import HttpResponse from django.shortcuts import redirect from django.shortcuts import render from django.utils.translation import gettext as _ -from main import models + +import archivematica.dashboard.components.decorators as decorators +from archivematica.archivematicaCommon import storageService as storage_service +from archivematica.dashboard.components import helpers +from archivematica.dashboard.components.ingest.forms import DublinCoreMetadataForm +from archivematica.dashboard.contrib.mcp.client import MCPClient +from archivematica.dashboard.main import models logger = logging.getLogger("archivematica.dashboard") diff --git a/src/dashboard/src/contrib/mcp/__init__.py b/src/archivematica/dashboard/components/unit/__init__.py similarity index 100% rename from src/dashboard/src/contrib/mcp/__init__.py rename to src/archivematica/dashboard/components/unit/__init__.py diff --git a/src/dashboard/src/components/unit/urls.py b/src/archivematica/dashboard/components/unit/urls.py similarity index 96% rename from src/dashboard/src/components/unit/urls.py rename to src/archivematica/dashboard/components/unit/urls.py index 3a88d62102..1350b28347 100644 --- a/src/dashboard/src/components/unit/urls.py +++ b/src/archivematica/dashboard/components/unit/urls.py @@ -14,11 +14,12 @@ # # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . -from components.unit import views from django.conf import settings from django.urls import path from django.urls import re_path +from archivematica.dashboard.components.unit import views + app_name = "unit" # The first segment of these urls is '^(?Ptransfer|ingest)/' # All views should expect a first parameter of unit_type, with a value of 'transfer' or 'ingest' diff --git a/src/dashboard/src/components/unit/views.py b/src/archivematica/dashboard/components/unit/views.py similarity index 96% rename from src/dashboard/src/components/unit/views.py rename to src/archivematica/dashboard/components/unit/views.py index 20b1fb001a..f954c09bc1 100644 --- a/src/dashboard/src/components/unit/views.py +++ b/src/archivematica/dashboard/components/unit/views.py @@ -17,10 +17,11 @@ import logging import django.http -from components import helpers -from contrib.mcp.client import MCPClient from django.shortcuts import render -from main import models + +from archivematica.dashboard.components import helpers +from archivematica.dashboard.contrib.mcp.client import MCPClient +from archivematica.dashboard.main import models LOGGER = logging.getLogger("archivematica.dashboard") diff --git a/src/dashboard/src/fpr/management/__init__.py b/src/archivematica/dashboard/contrib/__init__.py similarity index 100% rename from src/dashboard/src/fpr/management/__init__.py rename to src/archivematica/dashboard/contrib/__init__.py diff --git a/src/dashboard/src/fpr/management/commands/__init__.py b/src/archivematica/dashboard/contrib/mcp/__init__.py similarity index 100% rename from src/dashboard/src/fpr/management/commands/__init__.py rename to src/archivematica/dashboard/contrib/mcp/__init__.py diff --git a/src/dashboard/src/contrib/mcp/client.py b/src/archivematica/dashboard/contrib/mcp/client.py similarity index 98% rename from src/dashboard/src/contrib/mcp/client.py rename to src/archivematica/dashboard/contrib/mcp/client.py index f27d924358..13f5a20d6a 100644 --- a/src/dashboard/src/contrib/mcp/client.py +++ b/src/archivematica/dashboard/contrib/mcp/client.py @@ -19,8 +19,9 @@ import gearman from django.conf import settings from django.utils.translation import get_language -from gearman_encoder import JSONDataEncoder -from main.models import Job + +from archivematica.archivematicaCommon.gearman_encoder import JSONDataEncoder +from archivematica.dashboard.main.models import Job LOGGER = logging.getLogger("archivematica.dashboard.mcp.client") diff --git a/src/dashboard/src/fpr/__init__.py b/src/archivematica/dashboard/fpr/__init__.py similarity index 100% rename from src/dashboard/src/fpr/__init__.py rename to src/archivematica/dashboard/fpr/__init__.py diff --git a/src/dashboard/src/fpr/apps.py b/src/archivematica/dashboard/fpr/apps.py similarity index 74% rename from src/dashboard/src/fpr/apps.py rename to src/archivematica/dashboard/fpr/apps.py index 6c9ada111d..3ffe44e282 100644 --- a/src/dashboard/src/fpr/apps.py +++ b/src/archivematica/dashboard/fpr/apps.py @@ -3,4 +3,4 @@ class FPRAppConfig(AppConfig): default_auto_field = "django.db.models.AutoField" - name = "fpr" + name = "archivematica.dashboard.fpr" diff --git a/src/dashboard/src/fpr/forms.py b/src/archivematica/dashboard/fpr/forms.py similarity index 96% rename from src/dashboard/src/fpr/forms.py rename to src/archivematica/dashboard/fpr/forms.py index e2e23a2ba6..350b30bf57 100644 --- a/src/dashboard/src/fpr/forms.py +++ b/src/archivematica/dashboard/fpr/forms.py @@ -1,6 +1,7 @@ from django import forms from django.utils.translation import gettext_lazy as _ -from fpr import models as fprmodels + +from archivematica.dashboard.fpr import models as fprmodels # ########## FORMATS ############ @@ -57,7 +58,7 @@ def clean(self): ).exists() ): raise forms.ValidationError( - _("An ID tool with this description and version already" " exists") + _("An ID tool with this description and version already exists") ) return cleaned_data @@ -137,9 +138,9 @@ def clean(self): # give its UUID so that the user can reasonably track it down. else: ex_fpr_uuid = existing_fprule.uuid - msg = _( - "An identical FP rule already exists. See rule" " %(uuid)s." - ) % {"uuid": ex_fpr_uuid} + msg = _("An identical FP rule already exists. See rule %(uuid)s.") % { + "uuid": ex_fpr_uuid + } if not existing_fprule.enabled: replacers = fprmodels.FPRule.objects.filter( replaces=existing_fprule.uuid diff --git a/src/dashboard/src/fpr/helpers.py b/src/archivematica/dashboard/fpr/helpers.py similarity index 100% rename from src/dashboard/src/fpr/helpers.py rename to src/archivematica/dashboard/fpr/helpers.py diff --git a/src/dashboard/src/fpr/migrations/__init__.py b/src/archivematica/dashboard/fpr/management/__init__.py similarity index 100% rename from src/dashboard/src/fpr/migrations/__init__.py rename to src/archivematica/dashboard/fpr/management/__init__.py diff --git a/src/dashboard/src/fpr/south_migrations/__init__.py b/src/archivematica/dashboard/fpr/management/commands/__init__.py similarity index 100% rename from src/dashboard/src/fpr/south_migrations/__init__.py rename to src/archivematica/dashboard/fpr/management/commands/__init__.py diff --git a/src/dashboard/src/fpr/management/commands/get_fpr_changes.py b/src/archivematica/dashboard/fpr/management/commands/get_fpr_changes.py similarity index 100% rename from src/dashboard/src/fpr/management/commands/get_fpr_changes.py rename to src/archivematica/dashboard/fpr/management/commands/get_fpr_changes.py diff --git a/src/dashboard/src/fpr/management/commands/import_pronom_ids.py b/src/archivematica/dashboard/fpr/management/commands/import_pronom_ids.py similarity index 77% rename from src/dashboard/src/fpr/management/commands/import_pronom_ids.py rename to src/archivematica/dashboard/fpr/management/commands/import_pronom_ids.py index 0f4d79147b..f02b63d0f8 100644 --- a/src/dashboard/src/fpr/management/commands/import_pronom_ids.py +++ b/src/archivematica/dashboard/fpr/management/commands/import_pronom_ids.py @@ -1,21 +1,21 @@ import os import sys import uuid -from typing import Dict from django.core.management.base import BaseCommand from django.db import connection -from fpr.models import Format -from fpr.models import FormatGroup -from fpr.models import FormatVersion -from fpr.models import IDCommand -from fpr.models import IDRule from lxml import etree +from archivematica.dashboard.fpr.models import Format +from archivematica.dashboard.fpr.models import FormatGroup +from archivematica.dashboard.fpr.models import FormatVersion +from archivematica.dashboard.fpr.models import IDCommand +from archivematica.dashboard.fpr.models import IDRule + # Introduced in fpr/migrations/0035_python3_compatibility.py FILE_BY_EXTENSION_CMD_UUID = "8546b624-7894-4201-8df6-f239d5e0d5ba" -archivematica_formats: Dict[str, Format] = {} +archivematica_formats: dict[str, Format] = {} unknown_format_group = FormatGroup.objects.get(description="Unknown") file_by_extension = IDCommand.objects.get(uuid=FILE_BY_EXTENSION_CMD_UUID) @@ -37,8 +37,7 @@ def add_arguments(self, parser): parser.add_argument( "--output-filename", "-o", - help='File to output the migration to. "stdout" outputs to ' - "standard output", + help='File to output the migration to. "stdout" outputs to standard output', default="stdout", ) @@ -96,6 +95,35 @@ def __init__(self, xml): else: self.extension = None + pronom_classifications = xml.find(".details/content_type") + if pronom_classifications is not None and pronom_classifications.text: + self.pronom_classifications = pronom_classifications.text + else: + self.pronom_classifications = None + + +def get_format_group_from_pronom_classifications(pronom_format): + if pronom_format.pronom_classifications is not None: + classifications = [ + c.strip() for c in pronom_format.pronom_classifications.split(",") + ] + if len(classifications) == 1: + try: + return FormatGroup.objects.get(description=classifications[0]) + except (FormatGroup.DoesNotExist, FormatGroup.MultipleObjectsReturned): + print( + f"Could not match an existing format group for format '{pronom_format.format_name}' from its PRONOM classification '{classifications[0]}'. Setting the 'Unknown' group instead." + ) + else: + print( + f"Format '{pronom_format.format_name}' contains multiple PRONOM classifications '{pronom_format.pronom_classifications}'. Setting the 'Unknown' group." + ) + else: + print( + f"Format '{pronom_format.format_name}' does not contain any PRONOM classifications. Setting the 'Unknown' group." + ) + return unknown_format_group + def main(pronom_xml, output_format=SQL_OUTPUT, output_file=sys.stdout): formats = etree.parse(pronom_xml) @@ -132,12 +160,13 @@ def main(pronom_xml, output_format=SQL_OUTPUT, output_file=sys.stdout): try: parent_format = Format.objects.get(description=new_format.format_name) except Format.DoesNotExist: + group = get_format_group_from_pronom_classifications(new_format) parent_format = Format( description=new_format.format_name, - group=unknown_format_group, + group=group, uuid=str(uuid.uuid4()), ) - migration = f''' Format.objects.create(description="""{new_format.format_name}""", group_id="{unknown_format_group.uuid}", uuid="{parent_format.uuid}")''' + migration = f''' Format.objects.create(description="""{new_format.format_name}""", group_id="{group.uuid}", uuid="{parent_format.uuid}")''' sql = save_object(parent_format) choose_output(output_format, output_file, sql, migration) archivematica_formats[new_format.format_name] = parent_format diff --git a/src/dashboard/src/fpr/migrations-data/mc_validate_cmd.py b/src/archivematica/dashboard/fpr/migrations-data/mc_validate_cmd.py similarity index 99% rename from src/dashboard/src/fpr/migrations-data/mc_validate_cmd.py rename to src/archivematica/dashboard/fpr/migrations-data/mc_validate_cmd.py index 673332612a..a7552279e8 100644 --- a/src/dashboard/src/fpr/migrations-data/mc_validate_cmd.py +++ b/src/archivematica/dashboard/fpr/migrations-data/mc_validate_cmd.py @@ -33,7 +33,7 @@ def parse_mediaconch_data(target): return Parse(etree_el=etree.fromstring(output), stdout=output) except etree.XMLSyntaxError: raise MediaConchException( - "MediaConch failed when attempting to parse the XML output by" " MediaConch" + "MediaConch failed when attempting to parse the XML output by MediaConch" ) diff --git a/src/dashboard/src/fpr/migrations/0001_initial.py b/src/archivematica/dashboard/fpr/migrations/0001_initial.py similarity index 98% rename from src/dashboard/src/fpr/migrations/0001_initial.py rename to src/archivematica/dashboard/fpr/migrations/0001_initial.py index ba43b39f8e..856d6a2862 100644 --- a/src/dashboard/src/fpr/migrations/0001_initial.py +++ b/src/archivematica/dashboard/fpr/migrations/0001_initial.py @@ -1,15 +1,14 @@ import uuid -from typing import List -from typing import Tuple import autoslug.fields -import main.models from django.db import migrations from django.db import models +import archivematica.dashboard.main.models as main_models + class Migration(migrations.Migration): - dependencies: List[Tuple[str, str]] = [] + dependencies: list[tuple[str, str]] = [] operations = [ migrations.CreateModel( @@ -274,7 +273,7 @@ class Migration(migrations.Migration): ), ( "uuid", - main.models.UUIDField( + main_models.UUIDField( help_text="Unique identifier", unique=True, max_length=36, @@ -306,7 +305,7 @@ class Migration(migrations.Migration): ), ( "uuid", - main.models.UUIDField( + main_models.UUIDField( help_text="Unique identifier", unique=True, max_length=36, @@ -340,7 +339,7 @@ class Migration(migrations.Migration): ("lastmodified", models.DateTimeField(auto_now_add=True)), ( "uuid", - main.models.UUIDField( + main_models.UUIDField( help_text="Unique identifier", unique=True, max_length=36, @@ -406,7 +405,7 @@ class Migration(migrations.Migration): ("lastmodified", models.DateTimeField(auto_now_add=True)), ( "uuid", - main.models.UUIDField( + main_models.UUIDField( help_text="Unique identifier", unique=True, max_length=36, @@ -499,7 +498,7 @@ class Migration(migrations.Migration): ("lastmodified", models.DateTimeField(auto_now_add=True)), ( "uuid", - main.models.UUIDField( + main_models.UUIDField( help_text="Unique identifier", unique=True, max_length=36, @@ -571,7 +570,7 @@ class Migration(migrations.Migration): ), ( "uuid", - main.models.UUIDField( + main_models.UUIDField( help_text="Unique identifier", unique=True, max_length=36, @@ -607,7 +606,7 @@ class Migration(migrations.Migration): ("lastmodified", models.DateTimeField(auto_now_add=True)), ( "uuid", - main.models.UUIDField( + main_models.UUIDField( help_text="Unique identifier", unique=True, max_length=36, @@ -681,7 +680,7 @@ class Migration(migrations.Migration): ("lastmodified", models.DateTimeField(auto_now_add=True)), ( "uuid", - main.models.UUIDField( + main_models.UUIDField( help_text="Unique identifier", unique=True, max_length=36, @@ -733,7 +732,7 @@ class Migration(migrations.Migration): ), ( "uuid", - main.models.UUIDField( + main_models.UUIDField( help_text="Unique identifier", unique=True, max_length=36, diff --git a/src/dashboard/src/fpr/migrations/0002_initial_data.py b/src/archivematica/dashboard/fpr/migrations/0002_initial_data.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0002_initial_data.py rename to src/archivematica/dashboard/fpr/migrations/0002_initial_data.py diff --git a/src/dashboard/src/fpr/migrations/0003_pronom_84.py b/src/archivematica/dashboard/fpr/migrations/0003_pronom_84.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0003_pronom_84.py rename to src/archivematica/dashboard/fpr/migrations/0003_pronom_84.py diff --git a/src/dashboard/src/fpr/migrations/0004_pronom_88.py b/src/archivematica/dashboard/fpr/migrations/0004_pronom_88.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0004_pronom_88.py rename to src/archivematica/dashboard/fpr/migrations/0004_pronom_88.py diff --git a/src/dashboard/src/fpr/migrations/0005_update_tool_versions.py b/src/archivematica/dashboard/fpr/migrations/0005_update_tool_versions.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0005_update_tool_versions.py rename to src/archivematica/dashboard/fpr/migrations/0005_update_tool_versions.py diff --git a/src/dashboard/src/fpr/migrations/0006_i18n_models.py b/src/archivematica/dashboard/fpr/migrations/0006_i18n_models.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0006_i18n_models.py rename to src/archivematica/dashboard/fpr/migrations/0006_i18n_models.py diff --git a/src/dashboard/src/fpr/migrations/0007_embedded_default_thumbnail.py b/src/archivematica/dashboard/fpr/migrations/0007_embedded_default_thumbnail.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0007_embedded_default_thumbnail.py rename to src/archivematica/dashboard/fpr/migrations/0007_embedded_default_thumbnail.py diff --git a/src/dashboard/src/fpr/migrations/0008_update_django_autoslug.py b/src/archivematica/dashboard/fpr/migrations/0008_update_django_autoslug.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0008_update_django_autoslug.py rename to src/archivematica/dashboard/fpr/migrations/0008_update_django_autoslug.py diff --git a/src/dashboard/src/fpr/migrations/0009_pronom_90.py b/src/archivematica/dashboard/fpr/migrations/0009_pronom_90.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0009_pronom_90.py rename to src/archivematica/dashboard/fpr/migrations/0009_pronom_90.py diff --git a/src/dashboard/src/fpr/migrations/0010_update_fido_136.py b/src/archivematica/dashboard/fpr/migrations/0010_update_fido_136.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0010_update_fido_136.py rename to src/archivematica/dashboard/fpr/migrations/0010_update_fido_136.py diff --git a/src/dashboard/src/fpr/migrations/0011_mediaconch_validation.py b/src/archivematica/dashboard/fpr/migrations/0011_mediaconch_validation.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0011_mediaconch_validation.py rename to src/archivematica/dashboard/fpr/migrations/0011_mediaconch_validation.py diff --git a/src/dashboard/src/fpr/migrations/0012_mediaconch_policy_checks.py b/src/archivematica/dashboard/fpr/migrations/0012_mediaconch_policy_checks.py similarity index 99% rename from src/dashboard/src/fpr/migrations/0012_mediaconch_policy_checks.py rename to src/archivematica/dashboard/fpr/migrations/0012_mediaconch_policy_checks.py index 54c3ce66e0..c77e9cfbeb 100644 --- a/src/dashboard/src/fpr/migrations/0012_mediaconch_policy_checks.py +++ b/src/archivematica/dashboard/fpr/migrations/0012_mediaconch_policy_checks.py @@ -53,7 +53,7 @@ def data_migration(apps, schema_editor): uuid=mediaconch_policy_check_command_uuid, tool=mediaconch_tool, description=( - "Check against policy PLACEHOLDER_FOR_POLICY_FILE_NAME" " using MediaConch" + "Check against policy PLACEHOLDER_FOR_POLICY_FILE_NAME using MediaConch" ), command=POLICY_CHECK_CMD, script_type="pythonScript", diff --git a/src/dashboard/src/fpr/migrations/0013_normalization_rules_mov.py b/src/archivematica/dashboard/fpr/migrations/0013_normalization_rules_mov.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0013_normalization_rules_mov.py rename to src/archivematica/dashboard/fpr/migrations/0013_normalization_rules_mov.py diff --git a/src/dashboard/src/fpr/migrations/0014_fix_fits_command.py b/src/archivematica/dashboard/fpr/migrations/0014_fix_fits_command.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0014_fix_fits_command.py rename to src/archivematica/dashboard/fpr/migrations/0014_fix_fits_command.py diff --git a/src/dashboard/src/fpr/migrations/0015_pronom_92.py b/src/archivematica/dashboard/fpr/migrations/0015_pronom_92.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0015_pronom_92.py rename to src/archivematica/dashboard/fpr/migrations/0015_pronom_92.py diff --git a/src/dashboard/src/fpr/migrations/0016_update_idtools.py b/src/archivematica/dashboard/fpr/migrations/0016_update_idtools.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0016_update_idtools.py rename to src/archivematica/dashboard/fpr/migrations/0016_update_idtools.py diff --git a/src/dashboard/src/fpr/migrations/0017_ocr_unique_names.py b/src/archivematica/dashboard/fpr/migrations/0017_ocr_unique_names.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0017_ocr_unique_names.py rename to src/archivematica/dashboard/fpr/migrations/0017_ocr_unique_names.py diff --git a/src/dashboard/src/fpr/migrations/0018_slug_unique.py b/src/archivematica/dashboard/fpr/migrations/0018_slug_unique.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0018_slug_unique.py rename to src/archivematica/dashboard/fpr/migrations/0018_slug_unique.py diff --git a/src/dashboard/src/fpr/migrations/0019_fix_gs_command.py b/src/archivematica/dashboard/fpr/migrations/0019_fix_gs_command.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0019_fix_gs_command.py rename to src/archivematica/dashboard/fpr/migrations/0019_fix_gs_command.py diff --git a/src/dashboard/src/fpr/migrations/0020_pronom_93.py b/src/archivematica/dashboard/fpr/migrations/0020_pronom_93.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0020_pronom_93.py rename to src/archivematica/dashboard/fpr/migrations/0020_pronom_93.py diff --git a/src/dashboard/src/fpr/migrations/0021_normalize_jp2_with_ffmpeg_for_preservation.py b/src/archivematica/dashboard/fpr/migrations/0021_normalize_jp2_with_ffmpeg_for_preservation.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0021_normalize_jp2_with_ffmpeg_for_preservation.py rename to src/archivematica/dashboard/fpr/migrations/0021_normalize_jp2_with_ffmpeg_for_preservation.py diff --git a/src/dashboard/src/fpr/migrations/0022_pronom_94.py b/src/archivematica/dashboard/fpr/migrations/0022_pronom_94.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0022_pronom_94.py rename to src/archivematica/dashboard/fpr/migrations/0022_pronom_94.py diff --git a/src/dashboard/src/fpr/migrations/0023_update_idtools.py b/src/archivematica/dashboard/fpr/migrations/0023_update_idtools.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0023_update_idtools.py rename to src/archivematica/dashboard/fpr/migrations/0023_update_idtools.py diff --git a/src/dashboard/src/fpr/migrations/0024_update_fido.py b/src/archivematica/dashboard/fpr/migrations/0024_update_fido.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0024_update_fido.py rename to src/archivematica/dashboard/fpr/migrations/0024_update_fido.py diff --git a/src/dashboard/src/fpr/migrations/0025_update_fido_1312.py b/src/archivematica/dashboard/fpr/migrations/0025_update_fido_1312.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0025_update_fido_1312.py rename to src/archivematica/dashboard/fpr/migrations/0025_update_fido_1312.py diff --git a/src/dashboard/src/fpr/migrations/0026_fits_nailgun_compat.py b/src/archivematica/dashboard/fpr/migrations/0026_fits_nailgun_compat.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0026_fits_nailgun_compat.py rename to src/archivematica/dashboard/fpr/migrations/0026_fits_nailgun_compat.py diff --git a/src/dashboard/src/fpr/migrations/0027_idcommand_tool_disallow_blank.py b/src/archivematica/dashboard/fpr/migrations/0027_idcommand_tool_disallow_blank.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0027_idcommand_tool_disallow_blank.py rename to src/archivematica/dashboard/fpr/migrations/0027_idcommand_tool_disallow_blank.py diff --git a/src/dashboard/src/fpr/migrations/0028_idcommand_unique_enabled.py b/src/archivematica/dashboard/fpr/migrations/0028_idcommand_unique_enabled.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0028_idcommand_unique_enabled.py rename to src/archivematica/dashboard/fpr/migrations/0028_idcommand_unique_enabled.py diff --git a/src/dashboard/src/fpr/migrations/0029_update_inkscape_svg_command.py b/src/archivematica/dashboard/fpr/migrations/0029_update_inkscape_svg_command.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0029_update_inkscape_svg_command.py rename to src/archivematica/dashboard/fpr/migrations/0029_update_inkscape_svg_command.py diff --git a/src/dashboard/src/fpr/migrations/0030_update_jhove_command.py b/src/archivematica/dashboard/fpr/migrations/0030_update_jhove_command.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0030_update_jhove_command.py rename to src/archivematica/dashboard/fpr/migrations/0030_update_jhove_command.py diff --git a/src/dashboard/src/fpr/migrations/0031_update_normalization_rules.py b/src/archivematica/dashboard/fpr/migrations/0031_update_normalization_rules.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0031_update_normalization_rules.py rename to src/archivematica/dashboard/fpr/migrations/0031_update_normalization_rules.py diff --git a/src/dashboard/src/fpr/migrations/0032_pronom_96.py b/src/archivematica/dashboard/fpr/migrations/0032_pronom_96.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0032_pronom_96.py rename to src/archivematica/dashboard/fpr/migrations/0032_pronom_96.py diff --git a/src/dashboard/src/fpr/migrations/0033_update_idtools.py b/src/archivematica/dashboard/fpr/migrations/0033_update_idtools.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0033_update_idtools.py rename to src/archivematica/dashboard/fpr/migrations/0033_update_idtools.py diff --git a/src/dashboard/src/fpr/migrations/0034_delete_unused_models.py b/src/archivematica/dashboard/fpr/migrations/0034_delete_unused_models.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0034_delete_unused_models.py rename to src/archivematica/dashboard/fpr/migrations/0034_delete_unused_models.py diff --git a/src/dashboard/src/fpr/migrations/0035_python3_compatibility.py b/src/archivematica/dashboard/fpr/migrations/0035_python3_compatibility.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0035_python3_compatibility.py rename to src/archivematica/dashboard/fpr/migrations/0035_python3_compatibility.py diff --git a/src/dashboard/src/fpr/migrations/0036_pronom_109.py b/src/archivematica/dashboard/fpr/migrations/0036_pronom_109.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0036_pronom_109.py rename to src/archivematica/dashboard/fpr/migrations/0036_pronom_109.py diff --git a/src/dashboard/src/fpr/migrations/0037_update_idtools.py b/src/archivematica/dashboard/fpr/migrations/0037_update_idtools.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0037_update_idtools.py rename to src/archivematica/dashboard/fpr/migrations/0037_update_idtools.py diff --git a/src/dashboard/src/fpr/migrations/0038_use_uuidfield.py b/src/archivematica/dashboard/fpr/migrations/0038_use_uuidfield.py similarity index 85% rename from src/dashboard/src/fpr/migrations/0038_use_uuidfield.py rename to src/archivematica/dashboard/fpr/migrations/0038_use_uuidfield.py index c2c6ff2c40..15a4decd81 100644 --- a/src/dashboard/src/fpr/migrations/0038_use_uuidfield.py +++ b/src/archivematica/dashboard/fpr/migrations/0038_use_uuidfield.py @@ -1,9 +1,10 @@ # Generated by Django 1.11.29 on 2023-08-11 16:29 import uuid -import main.models from django.db import migrations +import archivematica.dashboard.main.models as main_models + class Migration(migrations.Migration): dependencies = [ @@ -14,7 +15,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="format", name="uuid", - field=main.models.UUIDField( + field=main_models.UUIDField( default=uuid.uuid4, editable=False, help_text="Unique identifier", @@ -24,7 +25,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="formatgroup", name="uuid", - field=main.models.UUIDField( + field=main_models.UUIDField( default=uuid.uuid4, editable=False, help_text="Unique identifier", @@ -34,7 +35,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="formatversion", name="uuid", - field=main.models.UUIDField( + field=main_models.UUIDField( default=uuid.uuid4, editable=False, help_text="Unique identifier", @@ -44,7 +45,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="fpcommand", name="uuid", - field=main.models.UUIDField( + field=main_models.UUIDField( default=uuid.uuid4, editable=False, help_text="Unique identifier", @@ -54,7 +55,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="fprule", name="uuid", - field=main.models.UUIDField( + field=main_models.UUIDField( default=uuid.uuid4, editable=False, help_text="Unique identifier", @@ -64,7 +65,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="fptool", name="uuid", - field=main.models.UUIDField( + field=main_models.UUIDField( default=uuid.uuid4, editable=False, help_text="Unique identifier", @@ -74,7 +75,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="idcommand", name="uuid", - field=main.models.UUIDField( + field=main_models.UUIDField( default=uuid.uuid4, editable=False, help_text="Unique identifier", @@ -84,7 +85,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="idrule", name="uuid", - field=main.models.UUIDField( + field=main_models.UUIDField( default=uuid.uuid4, editable=False, help_text="Unique identifier", @@ -94,7 +95,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="idtool", name="uuid", - field=main.models.UUIDField( + field=main_models.UUIDField( default=uuid.uuid4, editable=False, help_text="Unique identifier", diff --git a/src/dashboard/src/fpr/migrations/0039_django32.py b/src/archivematica/dashboard/fpr/migrations/0039_django32.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0039_django32.py rename to src/archivematica/dashboard/fpr/migrations/0039_django32.py diff --git a/src/dashboard/src/fpr/migrations/0040_update_jhove_validation.py b/src/archivematica/dashboard/fpr/migrations/0040_update_jhove_validation.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0040_update_jhove_validation.py rename to src/archivematica/dashboard/fpr/migrations/0040_update_jhove_validation.py diff --git a/src/dashboard/src/fpr/migrations/0041_pronom_116.py b/src/archivematica/dashboard/fpr/migrations/0041_pronom_116.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0041_pronom_116.py rename to src/archivematica/dashboard/fpr/migrations/0041_pronom_116.py diff --git a/src/dashboard/src/fpr/migrations/0042_update_idtools.py b/src/archivematica/dashboard/fpr/migrations/0042_update_idtools.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0042_update_idtools.py rename to src/archivematica/dashboard/fpr/migrations/0042_update_idtools.py diff --git a/src/dashboard/src/fpr/migrations/0043_update_default_thumbnail_command.py b/src/archivematica/dashboard/fpr/migrations/0043_update_default_thumbnail_command.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0043_update_default_thumbnail_command.py rename to src/archivematica/dashboard/fpr/migrations/0043_update_default_thumbnail_command.py diff --git a/src/dashboard/src/fpr/migrations/0044_remove_fits.py b/src/archivematica/dashboard/fpr/migrations/0044_remove_fits.py similarity index 100% rename from src/dashboard/src/fpr/migrations/0044_remove_fits.py rename to src/archivematica/dashboard/fpr/migrations/0044_remove_fits.py diff --git a/src/archivematica/dashboard/fpr/migrations/0045_update_event_detail_commands.py b/src/archivematica/dashboard/fpr/migrations/0045_update_event_detail_commands.py new file mode 100644 index 0000000000..907e5f6460 --- /dev/null +++ b/src/archivematica/dashboard/fpr/migrations/0045_update_event_detail_commands.py @@ -0,0 +1,59 @@ +import uuid + +from django.db import migrations + +OLD_COMMAND_ID = uuid.UUID("914fe3e5-fa45-47c6-858d-e11bc71f5b76") +NEW_COMMAND_ID = uuid.UUID("8f177f1c-5eac-45c4-aa6f-2214107d1a7b") + +COMMANDS = [ + # Extraction -> Extracting 7Zip compatible format. + uuid.UUID("b8864e2c-87a0-4aec-a8cf-a747a073be28"), +] + + +def data_migration_up(apps, schema_editor): + FPCommand = apps.get_model("fpr", "FPCommand") + + # Get the current command. + old_command = FPCommand.objects.get(uuid=OLD_COMMAND_ID) + + # Add a new command that replaces the current one with a new bash script. + new_command = FPCommand.objects.create( + uuid=NEW_COMMAND_ID, + replaces=old_command, + tool=old_command.tool, + enabled=old_command.enabled, + command=r"echo program=\"7z\"\; version=\"`7z | awk 'NR==3 && /^p7zip Version/ {print; exit} NR==2 {line2=$0} NR==3 {print line2 $0}'`\"", + script_type=old_command.script_type, + command_usage=old_command.command_usage, + description=old_command.description, + ) + + # Update extraction commands that use the event detail command. + FPCommand.objects.filter(uuid__in=COMMANDS).update(event_detail_command=new_command) + + # Disable previous command. + FPCommand.objects.filter(uuid=OLD_COMMAND_ID).update(enabled=False) + + +def data_migration_down(apps, schema_editor): + FPCommand = apps.get_model("fpr", "FPCommand") + + # Get previous command. + old_command = FPCommand.objects.get(uuid=OLD_COMMAND_ID) + + # Update extraction commands that use the event detail command. + FPCommand.objects.filter(uuid__in=COMMANDS).update(event_detail_command=old_command) + + # Delete the newest command. + FPCommand.objects.filter(uuid=NEW_COMMAND_ID).delete() + + # Re-enable the previous command. + old_command.enabled = True + old_command.save() + + +class Migration(migrations.Migration): + dependencies = [("fpr", "0044_remove_fits")] + + operations = [migrations.RunPython(data_migration_up, data_migration_down)] diff --git a/src/archivematica/dashboard/fpr/migrations/0046_update_jhove_module_validation.py b/src/archivematica/dashboard/fpr/migrations/0046_update_jhove_module_validation.py new file mode 100644 index 0000000000..62d7bc18a8 --- /dev/null +++ b/src/archivematica/dashboard/fpr/migrations/0046_update_jhove_module_validation.py @@ -0,0 +1,221 @@ +from django.db import migrations + +from archivematica.archivematicaCommon.archivematicaFunctions import ( + jhove_validation_command, +) + +JHOVE_TOOL_ID = "085d8690-93b7-4d31-84f7-2c5f4cbf6735" +OLD_JHOVE_CMD_ID = "cb335c49-e6ce-445f-a774-494a6f2300c6" + +NEW_JHOVE_COMMAND_CHANGES = [ + { + "NEW_JHOVE_CMD_ID": "1dd10753-12f4-4a71-bd69-532186b77d93", + "MODULE": "JPEG-hul", + "JHOVE_VALIDATION_RULES": ( + "005d14f1-5b67-43fc-b3a5-5048ec915b0b", + "aa4ad350-7e66-4637-a643-6e0bd037645d", + "5bc4c892-fe7b-4d22-8a9a-ea8c3dd0d171", + "f3f9652a-c903-491b-be89-5fc2469aaa1a", + "cddbffd4-4ada-4a6e-a713-82077a54e89e", + "913ff712-1856-48d7-85e9-415617fc9fdc", + "cc464095-02b3-471b-8f1d-221aecf37741", + "ab286afc-f429-4e50-8a40-452c6331d630", + "c5a30e3c-2100-4b5b-a9b5-27a236a345dd", + "56c72d8a-139b-4cdf-8dd0-d65a373301d2", + "62f0e3bd-a5bb-4fa0-b78b-dab15253b429", + "5df96ec2-b5a3-48b5-8599-3f292ff525c1", + ), + "COMMAND_DESCRIPTION": "Validate using JHOVE JPEG-hul", + }, + { + "NEW_JHOVE_CMD_ID": "34858217-507c-4e65-a7d9-ca62e1f72642", + "MODULE": "JPEG2000-hul", + "JHOVE_VALIDATION_RULES": ("e0cdb544-97d3-4915-9b08-fffad57bda10",), + "COMMAND_DESCRIPTION": "Validate using JHOVE JPEG2000-hul", + }, + { + "NEW_JHOVE_CMD_ID": "09a14b6b-3f4c-49d8-9a62-0c7212aca83c", + "MODULE": "TIFF-hul", + "JHOVE_VALIDATION_RULES": ( + "6b3ba38b-e208-450d-9b48-07897b6b7c42", + "48086e84-a933-42e0-87fd-ce195137c48d", + "4ea200fa-182c-4b17-9493-e9d3f7e467ff", + "a01418ce-fcb9-4554-add5-72010c719865", + "a7a6cc14-4d61-4030-b8dc-a1ca8ed97402", + ), + "COMMAND_DESCRIPTION": "Validate using JHOVE TIFF-hul", + }, + { + "NEW_JHOVE_CMD_ID": "33510bbe-8115-48af-8e6c-7cc7e1773d35", + "MODULE": "WAVE-hul", + "JHOVE_VALIDATION_RULES": ( + "67c0b096-63f4-4e30-b26f-6ed9365ea67c", + "981eae6c-4d7b-40ce-9bfd-1193c600a143", + "662caf44-cd04-4990-8e28-9f8425dba782", + "40616003-8af5-48d8-94ca-1871ae2cfaf1", + "0cd29763-b64a-43cc-9a72-5f8e6317bbae", + "18c019e8-ea26-49eb-a900-ec8388f1483d", + "37a5d85d-58dc-4f4e-8be9-b9ecced85d0d", + "1a01813e-430f-4a91-bda2-182e4620d328", + "8a0a1d71-5e56-482e-81b4-b3d425106d49", + "471303d4-de26-435c-83b2-8e72beccc60d", + "e13d6459-a749-4d31-9dd0-e0a59aab36cd", + "ee56ca6d-f6d0-4948-9834-2c82f5d223d5", + "dc9dc6a9-82b6-44b7-866a-db3e6314922e", + "10e514c0-e72a-4f70-afd0-4aed3bfa0ab9", + "ff989185-1b11-4f96-8075-e605e4cf4be4", + "42f3756b-7966-4a47-b029-59688bfc6e43", + "c6d7590f-83c1-4612-a300-3bff3d358199", + "ecd66812-c89a-4231-802e-2e69b47bae2a", + "88cb0134-7808-450f-a0f4-365a818d583a", + "ffa25cf6-c1a5-45f2-9bee-798aa04df172", + ), + "COMMAND_DESCRIPTION": "Validate using JHOVE WAVE-hul", + }, + { + "NEW_JHOVE_CMD_ID": "d610e7d6-f78c-4fb8-bd19-09a10fa48672", + "MODULE": "AIFF-hul", + "JHOVE_VALIDATION_RULES": ( + "76bfa370-ac87-41e2-995b-e01bb8c977d0", + "7af37625-f547-4d13-ab52-e5bddf249027", + "fcefa9af-322c-4c9b-afd2-82231dd953fc", + "407dcd55-71f5-4d83-8e21-6e3809a3fba8", + ), + "COMMAND_DESCRIPTION": "Validate using JHOVE AIFF-hul", + }, + { + "NEW_JHOVE_CMD_ID": "8816399f-ca5d-4861-9ae9-a4d22cc89e63", + "MODULE": "gif-hul", + "JHOVE_VALIDATION_RULES": ( + "4324a41f-6016-4b28-a0b0-c343dbaca42e", + "6217dbf1-2b4f-49ce-ab87-d0ed1e1ef890", + "986b53a8-3407-4d87-89ec-20575e61292a", + ), + "COMMAND_DESCRIPTION": "Validate using JHOVE gif-hul", + }, + { + "NEW_JHOVE_CMD_ID": "228f6676-97d7-44ff-bdb8-bfe181dee1a1", + "MODULE": "WARC-kb", + "JHOVE_VALIDATION_RULES": ( + "886eeaba-55b3-4ed9-9441-59aa1454ecdc", + "7497c57b-ee7a-420f-b197-e1752a0f071f", + "27c0dabc-fda4-4060-ab77-ce6e86f424c8", + ), + "COMMAND_DESCRIPTION": "Validate using JHOVE WARC-kb", + }, + { + "NEW_JHOVE_CMD_ID": "2d2fd8b9-5215-41f8-bb33-58be6a2b4abb", + "MODULE": "PDF-hul", + "JHOVE_VALIDATION_RULES": ( + "085d5286-7616-4acd-88a4-ef65066362b9", + "95ef736c-e477-442e-86f4-4e9049be2b88", + "eebc3670-6692-4daf-92a2-c8b76606049a", + "dcc9bcd7-f085-4028-9599-bf4fd12816ee", + "535152f5-88a5-439b-8619-6f42fc2e4468", + "aa93748e-5899-4ecc-870e-3d47a38fda59", + "713bf728-e583-4cb5-a079-f36baf1a77e7", + "8e995eb3-4023-4168-b1e1-7b5f2b22237b", + "40966c69-42dd-49fb-8740-b22a85bc7e32", + "26f687fe-255a-469b-bca5-ac0992038789", + "9d3325a1-cc0a-4fa8-9f3b-ccd5b8c884d1", + "802e24ec-5e63-4e92-a0cf-33f11b4edf06", + "ab728cab-3072-4e20-a64b-ba2560467d93", + "b1a60f26-8927-46c5-843b-7eddeef6213e", + "c799f39a-10fd-4125-b11c-1011ef1ca15c", + "d4a1faba-a5a3-4955-a20a-6f71da1d35bc", + "f712b5a9-7dd5-4e39-b818-c7cda54b9366", + "1386de15-3152-4a24-afa6-eab7a224da65", + "fdd758b0-99a6-4447-b082-3a1098f13bf6", + "f51ed8e0-edb3-4ebc-84d5-11135cc1fe62", + "80ecc092-8f29-4810-8918-e81133092290", + "8835348d-60f2-4dba-a834-cf26c57f821c", + "57bbe864-2004-45a4-81be-d40aab02f170", + "87c23f92-ee9a-44b3-89b2-c024bbcc70a3", + "26573246-96de-4682-bd17-f0bccb50abfe", + "b7dd794b-7618-4d13-a2f9-e01dae884cf6", + "f3d2b70b-0b9d-43f6-80e0-9b987b77719d", + "6f4cbfc5-c560-4709-8d3e-aa5685bc4fd5", + "a0f916de-ed95-4f2a-9f6d-0cbfd8949cc2", + "f4074907-c111-4e6c-91ae-9c0526475a9a", + ), + "COMMAND_DESCRIPTION": "Validate using JHOVE PDF-hul", + }, +] + + +def data_migration_up(apps, schema_editor): + """Update commands and rules.""" + FPCommand = apps.get_model("fpr", "FPCommand") + + # Get the old command + old_command = FPCommand.objects.get(uuid=OLD_JHOVE_CMD_ID) + + # Disable the old command + old_command.enabled = False + old_command.save() + + for d in NEW_JHOVE_COMMAND_CHANGES: + _upgrade_jhove_validation_command( + apps, + d["NEW_JHOVE_CMD_ID"], + jhove_validation_command(d["MODULE"]), + d["JHOVE_VALIDATION_RULES"], + d["COMMAND_DESCRIPTION"], + ) + + +def _upgrade_jhove_validation_command( + apps, new_cmd_uuid, new_cmd, rule_uuids, command_description +): + FPCommand = apps.get_model("fpr", "FPCommand") + FPRule = apps.get_model("fpr", "FPRule") + + # Add new command with the following + FPCommand.objects.create( + uuid=new_cmd_uuid, + tool_id=JHOVE_TOOL_ID, + command=new_cmd, + script_type="pythonScript", + command_usage="validation", + description=command_description, + ) + + # Update existing rules + FPRule.objects.filter(uuid__in=rule_uuids).update( + command_id=new_cmd_uuid, + ) + + +def data_migration_down(apps, schema_editor): + FPCommand = apps.get_model("fpr", "FPCommand") + + # Enable the old command + old_command = FPCommand.objects.get(uuid=OLD_JHOVE_CMD_ID) + old_command.enabled = True + old_command.save() + + for d in NEW_JHOVE_COMMAND_CHANGES: + _downgrade_jhove_validation_command( + apps, + d["NEW_JHOVE_CMD_ID"], + OLD_JHOVE_CMD_ID, + d["JHOVE_VALIDATION_RULES"], + ) + + +def _downgrade_jhove_validation_command(apps, new_cmd_uuid, old_cmd_uuid, rule_uuids): + FPCommand = apps.get_model("fpr", "FPCommand") + FPRule = apps.get_model("fpr", "FPRule") + + # We make sure that the rules point to the previous command before the latter is deleted. + FPRule.objects.filter(uuid__in=rule_uuids).update( + command_id=old_cmd_uuid, + ) + + # Delete the new command + FPCommand.objects.filter(uuid=new_cmd_uuid).delete() + + +class Migration(migrations.Migration): + dependencies = [("fpr", "0045_update_event_detail_commands")] + operations = [migrations.RunPython(data_migration_up, data_migration_down)] diff --git a/src/archivematica/dashboard/fpr/migrations/0047_update_format_groups.py b/src/archivematica/dashboard/fpr/migrations/0047_update_format_groups.py new file mode 100644 index 0000000000..cf5222a8ac --- /dev/null +++ b/src/archivematica/dashboard/fpr/migrations/0047_update_format_groups.py @@ -0,0 +1,47 @@ +from django.db import migrations + +# This maps the existing FPR format group descriptions with the PRONOM +# classification that should become the new format group descriptions. +FORMAT_GROUP_TO_PRONOM_CLASSIFICATION = { + "Text (Markup)": "Text (Mark-up)", + "Word Processing": "Word Processor", +} + +NEW_FORMAT_GROUPS = ["Text (Unstructured)"] + + +def data_migration_up(apps, schema_editor): + FormatGroup = apps.get_model("fpr", "FormatGroup") + + # Update existing format group descriptions. + for old_desc, new_desc in FORMAT_GROUP_TO_PRONOM_CLASSIFICATION.items(): + format_groups = FormatGroup.objects.filter(description=old_desc) + if format_groups.exists(): + format_groups.update(description=new_desc) + + # Add new format groups. + FormatGroup.objects.bulk_create( + [ + FormatGroup(description=desc) + for desc in NEW_FORMAT_GROUPS + if not FormatGroup.objects.filter(description=desc).exists() + ] + ) + + +def data_migration_down(apps, schema_editor): + FormatGroup = apps.get_model("fpr", "FormatGroup") + + # Revert format groups descriptions. + for old_desc, new_desc in FORMAT_GROUP_TO_PRONOM_CLASSIFICATION.items(): + format_groups = FormatGroup.objects.filter(description=new_desc) + if format_groups.exists(): + format_groups.update(description=old_desc) + + # Delete format groups added in this migration. + FormatGroup.objects.filter(description__in=NEW_FORMAT_GROUPS).delete() + + +class Migration(migrations.Migration): + dependencies = [("fpr", "0046_update_jhove_module_validation")] + operations = [migrations.RunPython(data_migration_up, data_migration_down)] diff --git a/src/archivematica/dashboard/fpr/migrations/0048_pronom_120.py b/src/archivematica/dashboard/fpr/migrations/0048_pronom_120.py new file mode 100644 index 0000000000..d78072cc54 --- /dev/null +++ b/src/archivematica/dashboard/fpr/migrations/0048_pronom_120.py @@ -0,0 +1,14 @@ +import pathlib + +from django.core.management import call_command +from django.db import migrations + + +def data_migration_up(apps, schema_editor): + fixture_file = pathlib.Path(__file__).parent / "pronom_120.json" + call_command("loaddata", fixture_file, app_label="fpr") + + +class Migration(migrations.Migration): + dependencies = [("fpr", "0047_update_format_groups")] + operations = [migrations.RunPython(data_migration_up, migrations.RunPython.noop)] diff --git a/src/archivematica/dashboard/fpr/migrations/0049_update_idtools.py b/src/archivematica/dashboard/fpr/migrations/0049_update_idtools.py new file mode 100644 index 0000000000..cc3cd9707c --- /dev/null +++ b/src/archivematica/dashboard/fpr/migrations/0049_update_idtools.py @@ -0,0 +1,98 @@ +from django.db import migrations + +OLD_FIDO_CMD_DESCRIPTION = "Identify using Fido 1.6.2rc1" +OLD_FIDO_CMD_UUID = "2cc8bb8e-1f6f-42f1-90b6-31207612c4c9" +OLD_FIDO_TOOL_SLUG = "fido-162rc1" +OLD_FIDO_TOOL_UUID = "c33c9d4d-121f-4db1-aa31-3d248c705e44" +OLD_FIDO_TOOL_VERSION = "1.6.2rc1" + +OLD_SIEGFRIED_CMD_DESCRIPTION = "Identify using Siegfried 1.11.0" +OLD_SIEGFRIED_CMD_UUID = "88c747f5-7b6c-4913-8dc2-3957dcd5e6b8" +OLD_SIEGFRIED_TOOL_SLUG = "siegfried-1110" +OLD_SIEGFRIED_TOOL_UUID = "454df69d-5cc0-49fc-93e4-6fbb6ac659e7" +OLD_SIEGFRIED_TOOL_VERSION = "1.11.0" + +NEW_FIDO_CMD_DESCRIPTION = "Identify using Fido 1.6.3rc1" +NEW_FIDO_CMD_UUID = "4ab42bc8-1537-4fa9-9c54-e454f2c5dcb7" +NEW_FIDO_TOOL_SLUG = "fido-163rc1" +NEW_FIDO_TOOL_VERSION = "1.6.3rc1" + +NEW_SIEGFRIED_CMD_DESCRIPTION = "Identify using Siegfried 1.11.2" +NEW_SIEGFRIED_CMD_UUID = "4914841c-3555-4519-86e3-5bf622d28351" +NEW_SIEGFRIED_TOOL_SLUG = "siegfried-1112" +NEW_SIEGFRIED_TOOL_VERSION = "1.11.2" + + +def data_migration_up(apps, schema_editor): + idtool = apps.get_model("fpr", "IDTool") + idcommand = apps.get_model("fpr", "IDCommand") + + # Update FIDO tool + idtool.objects.filter(uuid=OLD_FIDO_TOOL_UUID).update( + version=NEW_FIDO_TOOL_VERSION, slug=NEW_FIDO_TOOL_SLUG + ) + + # Find old FIDO command. + old_fido_command = idcommand.objects.get(uuid=OLD_FIDO_CMD_UUID) + + # Create new FIDO, but do not enable. + idcommand.objects.create( + replaces=old_fido_command, + uuid=NEW_FIDO_CMD_UUID, + description=NEW_FIDO_CMD_DESCRIPTION, + config=old_fido_command.config, + script=old_fido_command.script, + script_type=old_fido_command.script_type, + tool=idtool.objects.get(uuid=OLD_FIDO_TOOL_UUID), + enabled=False, + ) + + # Update Siegfried tool. + idtool.objects.filter(uuid=OLD_SIEGFRIED_TOOL_UUID).update( + version=NEW_SIEGFRIED_TOOL_VERSION, slug=NEW_SIEGFRIED_TOOL_SLUG + ) + + # Find old Siegfried command and disable it. + old_siegfried_command = idcommand.objects.get(uuid=OLD_SIEGFRIED_CMD_UUID) + old_siegfried_command.enabled = False + old_siegfried_command.save() + + # Create new command using the new version of Siegfried + idcommand.objects.create( + replaces=old_siegfried_command, + uuid=NEW_SIEGFRIED_CMD_UUID, + description=NEW_SIEGFRIED_CMD_DESCRIPTION, + config=old_siegfried_command.config, + script=old_siegfried_command.script, + script_type=old_siegfried_command.script_type, + tool=idtool.objects.get(uuid=OLD_SIEGFRIED_TOOL_UUID), + enabled=True, + ) + + +def data_migration_down(apps, schema_editor): + idtool = apps.get_model("fpr", "IDTool") + idcommand = apps.get_model("fpr", "IDCommand") + + # Remove new ID Commands + idcommand.objects.filter(uuid=NEW_FIDO_CMD_UUID).delete() + idcommand.objects.filter(uuid=NEW_SIEGFRIED_CMD_UUID).delete() + + # Revert Fido tool + idtool.objects.filter(uuid=OLD_FIDO_TOOL_UUID).update( + version=OLD_FIDO_TOOL_VERSION, slug=OLD_FIDO_TOOL_SLUG + ) + + # Revert Siegfried tool + idtool.objects.filter(uuid=OLD_SIEGFRIED_TOOL_UUID).update( + version=OLD_SIEGFRIED_TOOL_VERSION, slug=OLD_SIEGFRIED_TOOL_SLUG + ) + + # Restore old Siegfried command. + idcommand.objects.filter(uuid=OLD_SIEGFRIED_CMD_UUID).update(enabled=True) + + +class Migration(migrations.Migration): + dependencies = [("fpr", "0048_pronom_120")] + + operations = [migrations.RunPython(data_migration_up, data_migration_down)] diff --git a/src/archivematica/dashboard/fpr/migrations/0050_remove_usr_dir_references.py b/src/archivematica/dashboard/fpr/migrations/0050_remove_usr_dir_references.py new file mode 100644 index 0000000000..44e46891ea --- /dev/null +++ b/src/archivematica/dashboard/fpr/migrations/0050_remove_usr_dir_references.py @@ -0,0 +1,255 @@ +from django.db import migrations + +OLD_FIDO_CMD_UUID = "4ab42bc8-1537-4fa9-9c54-e454f2c5dcb7" + +OLD_FIDO_CMD_SCRIPT = r''' +from __future__ import print_function +import os.path +import re +import subprocess +import sys + +def file_tool(path): + return subprocess.check_output(['file', path]).decode("utf8").strip() + +class FidoFailed(Exception): + def __init__(self, stdout, stderr, retcode): + message = """ +Fido exited {retcode} and no format was found. +stdout: {stdout} +--- +stderr: {stderr} +""".format(stdout=stdout, stderr=stderr, retcode=retcode) + super(FidoFailed, self).__init__(message) + +def identify(file_): + # The default buffer size fido uses, 256KB, is too small to be able to detect certain formats + # Formats like office documents and Adobe Illustrator .ai files will be identified as other, less-specific formats + # This larger buffer size is a bit slower and consumes more RAM, so some users may wish to customize this to reduce the buffer size + # See: https://projects.artefactual.com/issues/5941, https://projects.artefactual.com/issues/5731 + cmd = ['fido', '-bufsize', '1048576', + '-loadformats', '/usr/lib/archivematica/archivematicaCommon/externals/fido/archivematica_format_extensions.xml', + os.path.abspath(file_)] + process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + stdout, stderr = process.communicate() + stdout = stdout.decode("utf8") + stderr = stderr.decode("utf8") + + try: + results = stdout.split('\n')[0].split(',') + except Exception: + raise FidoFailed(stdout, stderr, process.returncode) + + if process.returncode != 0 or results[-1] == '"fail"': + raise FidoFailed(stdout, stderr, process.returncode) + else: + puid = results[2] + if re.match('(.+)?fmt\/\d+', puid): + return puid + else: + print("File identified as non-standard Fido code: {id}".format(id=puid), file=sys.stderr) + return "" + +def main(argv): + try: + print(identify(argv[1])) + return 0 + except FidoFailed as e: + file_output = file_tool(argv[1]) + # FIDO can't currently identify text files with no extension, and this + # is a common enough usecase to special-case it + if 'text' in file_output: + print('x-fmt/111') + else: + return e + except Exception as e: + return e + +if __name__ == '__main__': + exit(main(sys.argv)) +''' + +NEW_FIDO_CMD_UUID = "25734211-bdad-45d4-9f01-fcbb77bdb58b" + +NEW_FIDO_CMD_SCRIPT = r''' +import importlib.resources +import os.path +import re +import subprocess +import sys + +def file_tool(path): + return subprocess.check_output(['file', path]).decode("utf8").strip() + +class FidoFailed(Exception): + def __init__(self, stdout, stderr, retcode): + message = """ +Fido exited {retcode} and no format was found. +stdout: {stdout} +--- +stderr: {stderr} +""".format(stdout=stdout, stderr=stderr, retcode=retcode) + super(FidoFailed, self).__init__(message) + +def identify(file_): + # The default buffer size fido uses, 256KB, is too small to be able to detect certain formats + # Formats like office documents and Adobe Illustrator .ai files will be identified as other, less-specific formats + # This larger buffer size is a bit slower and consumes more RAM, so some users may wish to customize this to reduce the buffer size + # See: https://projects.artefactual.com/issues/5941, https://projects.artefactual.com/issues/5731 + cmd = ['fido', '-bufsize', '1048576', + '-loadformats', str(importlib.resources.files("archivematica.archivematicaCommon") / "externals" / "fido" / "archivematica_format_extensions.xml"), + os.path.abspath(file_)] + process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + stdout, stderr = process.communicate() + stdout = stdout.decode("utf8") + stderr = stderr.decode("utf8") + + try: + results = stdout.split('\n')[0].split(',') + except Exception: + raise FidoFailed(stdout, stderr, process.returncode) + + if process.returncode != 0 or results[-1] == '"fail"': + raise FidoFailed(stdout, stderr, process.returncode) + else: + puid = results[2] + if re.match('(.+)?fmt\/\d+', puid): + return puid + else: + print("File identified as non-standard Fido code: {id}".format(id=puid), file=sys.stderr) + return "" + +def main(argv): + try: + print(identify(argv[1])) + return 0 + except FidoFailed as e: + file_output = file_tool(argv[1]) + # FIDO can't currently identify text files with no extension, and this + # is a common enough usecase to special-case it + if 'text' in file_output: + print('x-fmt/111') + else: + return e + except Exception as e: + return e + +if __name__ == '__main__': + exit(main(sys.argv)) +''' + +OLD_FIWALK_CMD_UUID = "928ce834-8830-44cc-a282-4ba1271f7842" + +OLD_FIWALK_CMD_SCRIPT = r"fiwalk -x %relativeLocation% -c /usr/lib/archivematica/archivematicaCommon/externals/fiwalk_plugins/ficonfig.txt" + +NEW_FIWALK_CMD_UUID = "fcd033ec-8e29-48e8-99b9-6dbccd8f9c6e" + +NEW_FIWALK_CMD_SCRIPT = r"fiwalk -x %relativeLocation%" + +FIWALK_CHARACTERIZATION_RULES = ( + "825b31d8-6ee0-46fc-bdd7-9bf32edb5755", + "38ab3a16-e7fc-464d-8be9-1398908f78a9", + "20cad741-3cf1-4b6a-9e71-d1e8af13ba3f", + "ad4927da-94fa-4ac3-942e-9bafebf96a91", + "223c794f-cfcb-458d-bc31-d90d8fe0d774", + "5e661576-af61-44b5-834a-14ef21fb2051", + "6f7e51b4-60b2-417f-b690-0f286a97304c", + "c618f3d7-1fcb-476c-a076-e7413f19337b", + "369140d2-ffdd-4050-8ff8-1947b325b4a5", +) + + +def update_fido_command(apps): + IDCommand = apps.get_model("fpr", "IDCommand") + + command = IDCommand.objects.get(uuid=OLD_FIDO_CMD_UUID) + + IDCommand.objects.create( + replaces=command, + uuid=NEW_FIDO_CMD_UUID, + description=command.description, + config=command.config, + script=NEW_FIDO_CMD_SCRIPT, + script_type=command.script_type, + tool=command.tool, + enabled=command.enabled, + ) + + +def restore_fido_command(apps): + IDCommand = apps.get_model("fpr", "IDCommand") + + try: + command = IDCommand.objects.get(uuid=NEW_FIDO_CMD_UUID) + except IDCommand.DoesNotExist: + enabled = False + else: + enabled = command.enabled + command.delete() + + IDCommand.objects.filter(uuid=OLD_FIDO_CMD_UUID).update( + script=OLD_FIDO_CMD_SCRIPT, enabled=enabled + ) + + +def update_fiwalk_command(apps): + FPCommand = apps.get_model("fpr", "FPCommand") + FPRule = apps.get_model("fpr", "FPRule") + + command = FPCommand.objects.get(uuid=OLD_FIWALK_CMD_UUID) + + FPCommand.objects.create( + replaces=command, + uuid=NEW_FIWALK_CMD_UUID, + tool=command.tool, + description=command.description, + command=NEW_FIWALK_CMD_SCRIPT, + script_type=command.script_type, + output_location=command.output_location, + output_format=command.output_format, + command_usage=command.command_usage, + verification_command=command.verification_command, + event_detail_command=command.event_detail_command, + enabled=command.enabled, + ) + + FPRule.objects.filter(uuid__in=FIWALK_CHARACTERIZATION_RULES).update( + command_id=NEW_FIWALK_CMD_UUID + ) + + +def restore_fiwalk_command(apps): + FPCommand = apps.get_model("fpr", "FPCommand") + FPRule = apps.get_model("fpr", "FPRule") + + try: + command = FPCommand.objects.get(uuid=NEW_FIWALK_CMD_UUID) + except FPCommand.DoesNotExist: + enabled = False + else: + enabled = command.enabled + command.delete() + + FPCommand.objects.filter(uuid=OLD_FIWALK_CMD_UUID).update( + command=OLD_FIWALK_CMD_SCRIPT, enabled=enabled + ) + + FPRule.objects.filter(uuid__in=FIWALK_CHARACTERIZATION_RULES).update( + command_id=OLD_FIWALK_CMD_UUID + ) + + +def data_migration_up(apps, schema_editor): + update_fido_command(apps) + update_fiwalk_command(apps) + + +def data_migration_down(apps, schema_editor): + restore_fido_command(apps) + restore_fiwalk_command(apps) + + +class Migration(migrations.Migration): + dependencies = [("fpr", "0049_update_idtools")] + + operations = [migrations.RunPython(data_migration_up, data_migration_down)] diff --git a/src/archivematica/dashboard/fpr/migrations/0051_remove_maildir_commands.py b/src/archivematica/dashboard/fpr/migrations/0051_remove_maildir_commands.py new file mode 100644 index 0000000000..43c3f216a7 --- /dev/null +++ b/src/archivematica/dashboard/fpr/migrations/0051_remove_maildir_commands.py @@ -0,0 +1,28 @@ +from django.db import migrations +from django.db.models import Q + + +def remove_maildir_commands(apps): + FPCommand = apps.get_model("fpr", "FPCommand") + + # Deleting the normalization command deletes the related rules as well. + normalization_command = Q( + description="Transcoding maildir to mbox", + command_usage="normalization", + ) + event_detail_command = Q( + description="Transcoding maildir to mbox event detail", + command_usage="event_detail", + ) + + FPCommand.objects.filter(normalization_command | event_detail_command).delete() + + +def data_migration_up(apps, schema_editor): + remove_maildir_commands(apps) + + +class Migration(migrations.Migration): + dependencies = [("fpr", "0050_remove_usr_dir_references")] + + operations = [migrations.RunPython(data_migration_up, migrations.RunPython.noop)] diff --git a/src/archivematica/dashboard/fpr/migrations/0052_update_validation_rules.py b/src/archivematica/dashboard/fpr/migrations/0052_update_validation_rules.py new file mode 100644 index 0000000000..b3bc48b023 --- /dev/null +++ b/src/archivematica/dashboard/fpr/migrations/0052_update_validation_rules.py @@ -0,0 +1,42 @@ +from django.db import migrations + +PRONOM_IDS = ( + "fmt/156", # Tagged Image File Format for Internet Fax (TIFF-FX) + "fmt/154", # Tagged Image File Format for Electronic Photography (TIFF/EP) + "x-fmt/388", # Exchangeable Image File Format (Uncompressed) 2.1 + "x-fmt/387", # Exchangeable Image File Format (Uncompressed) 2.2 + "x-fmt/399", # Exchangeable Image File Format (Uncompressed) 2.0 + "fmt/153", # Tagged Image File Format for Image Technology (TIFF/IT) + "fmt/155", # Geographic Tagged Image File Format (GeoTIFF) +) + + +def data_migration_up(apps, schema_editor): + FPCommand = apps.get_model("fpr", "FPCommand") + FormatVersion = apps.get_model("fpr", "FormatVersion") + FPRule = apps.get_model("fpr", "FPRule") + + command = FPCommand.objects.get(description="Validate using JHOVE TIFF-hul") + + for format_version in FormatVersion.objects.filter(pronom_id__in=PRONOM_IDS): + FPRule.objects.get_or_create( + purpose="validation", command=command, format=format_version + ) + + +def data_migration_down(apps, schema_editor): + FPCommand = apps.get_model("fpr", "FPCommand") + FormatVersion = apps.get_model("fpr", "FormatVersion") + FPRule = apps.get_model("fpr", "FPRule") + + command = FPCommand.objects.get(description="Validate using JHOVE TIFF-hul") + + for format_version in FormatVersion.objects.filter(pronom_id__in=PRONOM_IDS): + FPRule.objects.filter( + purpose="validation", command=command, format=format_version + ).delete() + + +class Migration(migrations.Migration): + dependencies = [("fpr", "0051_remove_maildir_commands")] + operations = [migrations.RunPython(data_migration_up, data_migration_down)] diff --git a/src/archivematica/dashboard/fpr/migrations/0053_update_jhove_tool.py b/src/archivematica/dashboard/fpr/migrations/0053_update_jhove_tool.py new file mode 100644 index 0000000000..296c8a8982 --- /dev/null +++ b/src/archivematica/dashboard/fpr/migrations/0053_update_jhove_tool.py @@ -0,0 +1,20 @@ +from django.db import migrations + +JHOVE_TOOL_ID = "085d8690-93b7-4d31-84f7-2c5f4cbf6735" + + +def data_migration_up(apps, schema_editor): + FPTool = apps.get_model("fpr", "FPTool") + + FPTool.objects.filter(uuid=JHOVE_TOOL_ID).update(version="1.34", slug="jhove-134") + + +def data_migration_down(apps, schema_editor): + FPTool = apps.get_model("fpr", "FPTool") + + FPTool.objects.filter(uuid=JHOVE_TOOL_ID).update(version="1.26", slug="jhove-126") + + +class Migration(migrations.Migration): + dependencies = [("fpr", "0052_update_validation_rules")] + operations = [migrations.RunPython(data_migration_up, data_migration_down)] diff --git a/src/dashboard/src/fpr/templatetags/__init__.py b/src/archivematica/dashboard/fpr/migrations/__init__.py similarity index 100% rename from src/dashboard/src/fpr/templatetags/__init__.py rename to src/archivematica/dashboard/fpr/migrations/__init__.py diff --git a/src/dashboard/src/fpr/migrations/initial_data.json b/src/archivematica/dashboard/fpr/migrations/initial_data.json similarity index 100% rename from src/dashboard/src/fpr/migrations/initial_data.json rename to src/archivematica/dashboard/fpr/migrations/initial_data.json diff --git a/src/dashboard/src/fpr/migrations/pronom84_formatgroups_new.json b/src/archivematica/dashboard/fpr/migrations/pronom84_formatgroups_new.json similarity index 100% rename from src/dashboard/src/fpr/migrations/pronom84_formatgroups_new.json rename to src/archivematica/dashboard/fpr/migrations/pronom84_formatgroups_new.json diff --git a/src/dashboard/src/fpr/migrations/pronom84_formats_all.json b/src/archivematica/dashboard/fpr/migrations/pronom84_formats_all.json similarity index 100% rename from src/dashboard/src/fpr/migrations/pronom84_formats_all.json rename to src/archivematica/dashboard/fpr/migrations/pronom84_formats_all.json diff --git a/src/dashboard/src/fpr/migrations/pronom84_formatversions_all.json b/src/archivematica/dashboard/fpr/migrations/pronom84_formatversions_all.json similarity index 100% rename from src/dashboard/src/fpr/migrations/pronom84_formatversions_all.json rename to src/archivematica/dashboard/fpr/migrations/pronom84_formatversions_all.json diff --git a/src/dashboard/src/fpr/migrations/pronom84_fprules_all.json b/src/archivematica/dashboard/fpr/migrations/pronom84_fprules_all.json similarity index 100% rename from src/dashboard/src/fpr/migrations/pronom84_fprules_all.json rename to src/archivematica/dashboard/fpr/migrations/pronom84_fprules_all.json diff --git a/src/dashboard/src/fpr/migrations/pronom84_fptools_all.json b/src/archivematica/dashboard/fpr/migrations/pronom84_fptools_all.json similarity index 100% rename from src/dashboard/src/fpr/migrations/pronom84_fptools_all.json rename to src/archivematica/dashboard/fpr/migrations/pronom84_fptools_all.json diff --git a/src/dashboard/src/fpr/migrations/pronom84_idcommands_all.json b/src/archivematica/dashboard/fpr/migrations/pronom84_idcommands_all.json similarity index 100% rename from src/dashboard/src/fpr/migrations/pronom84_idcommands_all.json rename to src/archivematica/dashboard/fpr/migrations/pronom84_idcommands_all.json diff --git a/src/dashboard/src/fpr/migrations/pronom84_idrules_all.json b/src/archivematica/dashboard/fpr/migrations/pronom84_idrules_all.json similarity index 100% rename from src/dashboard/src/fpr/migrations/pronom84_idrules_all.json rename to src/archivematica/dashboard/fpr/migrations/pronom84_idrules_all.json diff --git a/src/dashboard/src/fpr/migrations/pronom84_idtools_all.json b/src/archivematica/dashboard/fpr/migrations/pronom84_idtools_all.json similarity index 100% rename from src/dashboard/src/fpr/migrations/pronom84_idtools_all.json rename to src/archivematica/dashboard/fpr/migrations/pronom84_idtools_all.json diff --git a/src/dashboard/src/fpr/migrations/pronom_109.json b/src/archivematica/dashboard/fpr/migrations/pronom_109.json similarity index 100% rename from src/dashboard/src/fpr/migrations/pronom_109.json rename to src/archivematica/dashboard/fpr/migrations/pronom_109.json diff --git a/src/dashboard/src/fpr/migrations/pronom_116.json b/src/archivematica/dashboard/fpr/migrations/pronom_116.json similarity index 100% rename from src/dashboard/src/fpr/migrations/pronom_116.json rename to src/archivematica/dashboard/fpr/migrations/pronom_116.json diff --git a/src/archivematica/dashboard/fpr/migrations/pronom_120.json b/src/archivematica/dashboard/fpr/migrations/pronom_120.json new file mode 100644 index 0000000000..42749f3f5c --- /dev/null +++ b/src/archivematica/dashboard/fpr/migrations/pronom_120.json @@ -0,0 +1,2559 @@ +[ + { + "model": "fpr.format", + "fields": { + "uuid": "d002c654-a010-43bb-8782-733af0a0a43b", + "description": "Draw.io Diagram (XML) File", + "group": "fdf9e267-a18c-46a4-a162-b81bcba6322f", + "slug": "drawio-diagram-xml-file" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "34448b47-cc4d-4c99-b081-a7b1673f4d35", + "description": "OpenWayback CDXJ File Format", + "group": "57361413-1c3b-405d-a9c0-7d3ea381090e", + "slug": "openwayback-cdxj-file-format" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "e778d72f-db63-431c-bfd7-b38191b30ba2", + "description": "Common Data Format dotCDF", + "group": "9c183a8f-89b7-47cc-a6ba-4ed038cf63d5", + "slug": "common-data-format-dotcdf" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "44a6fd9a-6f43-4057-940d-e5b25f64834f", + "description": "PechaMaker Format", + "group": "df5d5107-c803-48ba-844d-feba093a571c", + "slug": "pechamaker-format" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "0864df9c-8543-49f7-99fa-5d529157d9e3", + "description": "Zoom Project Settings", + "group": "c94ce0e6-c275-4c09-b802-695a18b7bf2a", + "slug": "zoom-project-settings" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "960a7a3a-a77a-4ff8-9e25-cf2fb40ce0e7", + "description": "Sandboxels Save File", + "group": "9c183a8f-89b7-47cc-a6ba-4ed038cf63d5", + "slug": "sandboxels-save-file" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "ea93a548-6dac-46af-8d06-f218f186934e", + "description": "Program Embroidery Stitch (PES) File", + "group": "c030a94d-5942-47c8-893b-ddb326b5da6c", + "slug": "program-embroidery-stitch-pes-file" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "093f5f5e-1d49-4e79-971d-3aa8e6c2d7fd", + "description": "Melco OFM Project", + "group": "be86edbf-f62c-431f-b549-300d23c7cd0d", + "slug": "melco-ofm-project" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "14c43189-5a20-46cf-8f1d-d39fe8598369", + "description": "Disklavier E-Seq Music", + "group": "c94ce0e6-c275-4c09-b802-695a18b7bf2a", + "slug": "disklavier-e-seq-music" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "1b61aebf-923b-4cae-9031-c1a8739bb668", + "description": "Shorten (codec)", + "group": "c94ce0e6-c275-4c09-b802-695a18b7bf2a", + "slug": "shorten-codec" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "baf1ad97-6588-4a02-9ff4-11391d66061b", + "description": "SolidWorks Material Database File", + "group": "901b79ad-4107-42f8-93d6-a28b631ada05", + "slug": "solidworks-material-database-file" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "fe46d74b-f3bb-400b-a9a0-e33a750d883f", + "description": "NEC Thermo Tracer Image File", + "group": "be86edbf-f62c-431f-b549-300d23c7cd0d", + "slug": "nec-thermo-tracer-image-file" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "83d0c7da-8941-4988-a4da-4326effffc6a", + "description": "JPH (JPEG 2000 part 15)", + "group": "be86edbf-f62c-431f-b549-300d23c7cd0d", + "slug": "jph-jpeg-2000-part-15" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "4035be98-61d2-4b07-9ebf-1365eef8224f", + "description": "Papyrus Document", + "group": "805d359a-32f0-4767-8a9c-cc14f13d8392", + "slug": "papyrus-document" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "7de7cf90-01fb-4a69-b2e7-abcba08eebab", + "description": "Final Writer Document", + "group": "805d359a-32f0-4767-8a9c-cc14f13d8392", + "slug": "final-writer-document" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "00df1c6c-7e93-4259-b94d-945f03504d79", + "description": "Solidworks Design Document Files", + "group": "289ce9cf-7991-48e4-abbe-ca373ef632cf", + "slug": "solidworks-design-document-files" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "b91ea2c5-f745-4ed3-9743-63b040b2b45c", + "description": "Atrac Codec File", + "group": "c94ce0e6-c275-4c09-b802-695a18b7bf2a", + "slug": "atrac-codec-file" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "bb1677a1-3ece-4239-ada1-e17bb4dd5d08", + "description": "ETC Express/Expression Show File", + "group": "9c183a8f-89b7-47cc-a6ba-4ed038cf63d5", + "slug": "etc-expressexpression-show-file" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "59c4ab9c-1dd8-4b23-a91a-48f9f9c9d63b", + "description": "MOXCEL", + "group": "fea8c626-4c6d-4d34-8747-b7df5c67c17c", + "slug": "moxcel" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "8b8bf6ff-bd4c-40ea-ae72-7178b1063c18", + "description": "Finale Performance Assessment", + "group": "9c183a8f-89b7-47cc-a6ba-4ed038cf63d5", + "slug": "finale-performance-assessment" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "f6a1e52e-9451-4828-963a-79bc10b8d032", + "description": "Finale Notation File", + "group": "9c183a8f-89b7-47cc-a6ba-4ed038cf63d5", + "slug": "finale-notation-file" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "2ea04202-e4fe-4e1b-a11a-1782469aedd1", + "description": "ICC Profile", + "group": "9c183a8f-89b7-47cc-a6ba-4ed038cf63d5", + "slug": "icc-profile" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "17f42669-2d81-496d-b14f-cb3dd82ed801", + "description": "Sibelius Score", + "group": "9c183a8f-89b7-47cc-a6ba-4ed038cf63d5", + "slug": "sibelius-score" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "9fd09e78-091c-4c82-9295-e805bfbde356", + "description": "Sibelius Scorch", + "group": "9c183a8f-89b7-47cc-a6ba-4ed038cf63d5", + "slug": "sibelius-scorch" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "ae54ee29-5b7a-4cb0-b3b0-703516a1f3ec", + "description": "WinFax Fax Image", + "group": "be86edbf-f62c-431f-b549-300d23c7cd0d", + "slug": "winfax-fax-image" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "02937b41-c87c-45d9-9e24-0897c89892c2", + "description": "SPIR-V", + "group": "c030a94d-5942-47c8-893b-ddb326b5da6c", + "slug": "spir-v" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "ca7a58ac-3125-4f61-9bf8-e6719cb273f7", + "description": "IMF Package Asset Map", + "group": "89961b53-4d5c-49e2-9910-8825d96c8640", + "slug": "imf-package-asset-map" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "4dd81041-9644-4270-97a6-299aa675a64b", + "description": "IMF Package Packing List", + "group": "89961b53-4d5c-49e2-9910-8825d96c8640", + "slug": "imf-package-packing-list" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "e8394ae8-fb2c-49fc-bcbb-ee30fa06fd9c", + "description": "IMF Package Composition Playlist", + "group": "89961b53-4d5c-49e2-9910-8825d96c8640", + "slug": "imf-package-composition-playlist" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "54ca3c25-f8a2-418c-b68d-050c344b7721", + "description": "Husqvarna Embroidery Stitch File", + "group": "9c183a8f-89b7-47cc-a6ba-4ed038cf63d5", + "slug": "husqvarna-embroidery-stitch-file" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "2d0203e5-9176-4412-ae81-dd0d5bc8704d", + "description": "Husqvarna / Pfaff Embroidery Stitch File", + "group": "c030a94d-5942-47c8-893b-ddb326b5da6c", + "slug": "husqvarna-pfaff-embroidery-stitch-file" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "6b5fb194-fc87-49d7-a9e8-e0f3204a62ba", + "description": "Husqvarna / TruE Embroidery Stitch File", + "group": "c030a94d-5942-47c8-893b-ddb326b5da6c", + "slug": "husqvarna-true-embroidery-stitch-file" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "c2aa12a6-d6ab-4632-bde6-61c2689db700", + "description": "Husqvarna / Premier+ Embroidery Stitch File", + "group": "c030a94d-5942-47c8-893b-ddb326b5da6c", + "slug": "husqvarna-premier-embroidery-stitch-file" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "8254bb32-b22a-4115-bce5-d92701076fef", + "description": "Husqvarna-Viking Designer 1 Stitch File", + "group": "9c183a8f-89b7-47cc-a6ba-4ed038cf63d5", + "slug": "husqvarna-viking-designer-1-stitch-file" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "9defae19-8a2a-4886-81f0-26682466dea5", + "description": "Compressed MusicXML", + "group": "c7a96cdf-dde2-42f1-a679-5a7f251c6ce9", + "slug": "compressed-musicxml" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "07924713-c889-434e-8703-e04764600506", + "description": "Protein Data Bank File", + "group": "57361413-1c3b-405d-a9c0-7d3ea381090e", + "slug": "protein-data-bank-file" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "02234cb1-2615-47d5-9ccd-ea1c60b9a877", + "description": "Visualization Toolkit", + "group": "8d32bf98-b569-4469-8953-10416b53b920", + "slug": "visualization-toolkit" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "0d7a0f03-9df5-481d-97fa-19187c697539", + "description": "RawACF", + "group": "9c183a8f-89b7-47cc-a6ba-4ed038cf63d5", + "slug": "rawacf" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "617e119d-b394-41fe-ba46-65a40f7c0859", + "description": "Axon Binary Format", + "group": "9c183a8f-89b7-47cc-a6ba-4ed038cf63d5", + "slug": "axon-binary-format" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "8b4c1389-57e7-4cb6-b193-b4187d7fa070", + "description": "KryoFlux Stream", + "group": "289ce9cf-7991-48e4-abbe-ca373ef632cf", + "slug": "kryoflux-stream" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "47498ffa-f70c-4b40-b281-113fa83cc656", + "description": "Binvox", + "group": "8d32bf98-b569-4469-8953-10416b53b920", + "slug": "binvox" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "3714eeb2-324c-46ca-9d66-c9b06004c8ff", + "description": "Sony OpenMG Audio", + "group": "c94ce0e6-c275-4c09-b802-695a18b7bf2a", + "slug": "sony-openmg-audio" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "a0dbdee2-6e38-4a6b-9acf-e1edc20f889f", + "description": "askSam Document for DOS", + "group": "7d161ac1-2879-445d-8e6d-cbc9eff5c225", + "slug": "asksam-document-for-dos" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "75bf6e5a-f40f-479d-843a-0670c5641856", + "description": "askSam Document for Windows", + "group": "7d161ac1-2879-445d-8e6d-cbc9eff5c225", + "slug": "asksam-document-for-windows" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "d245bbe6-b461-4f3a-bd4d-f8bf7b9e151d", + "description": "Parquet File", + "group": "57361413-1c3b-405d-a9c0-7d3ea381090e", + "slug": "parquet-file" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "906669ab-969a-463d-b815-a7cb1e208e01", + "description": "CD Architect Project File", + "group": "7d161ac1-2879-445d-8e6d-cbc9eff5c225", + "slug": "cd-architect-project-file" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "d467c620-1ab7-43d2-b131-8399f02a6186", + "description": "Codebook Exchange Format", + "group": "57361413-1c3b-405d-a9c0-7d3ea381090e", + "slug": "codebook-exchange-format" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "29f6de66-8720-4d23-92f7-02e7d22c89a9", + "description": "Apache Avro", + "group": "57361413-1c3b-405d-a9c0-7d3ea381090e", + "slug": "apache-avro" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "15c7eca5-c42a-4cf6-9094-7421f582bd4c", + "description": "Apache ORC", + "group": "57361413-1c3b-405d-a9c0-7d3ea381090e", + "slug": "apache-orc" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "88c9c247-58ed-4865-94e1-4b5b84ff9a89", + "description": "HxC Floppy Emulator Disk Image", + "group": "3616a69f-e9c4-4357-b366-57082cf75a3e", + "slug": "hxc-floppy-emulator-disk-image" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "13136e4b-a224-4097-b6b2-beb455110c82", + "description": "Open Packaging Format", + "group": "c94ce0e6-c275-4c09-b802-695a18b7bf2a", + "slug": "open-packaging-format" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "1e82cd06-a79b-41f9-93e8-9e072faa3b61", + "description": "Daisy Talking Book Navigation Control File", + "group": "c94ce0e6-c275-4c09-b802-695a18b7bf2a", + "slug": "daisy-talking-book-navigation-control-file" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "cd8b7550-5d70-437d-8361-474881c513fa", + "description": "Daisy Talking Book Resource File", + "group": "c94ce0e6-c275-4c09-b802-695a18b7bf2a", + "slug": "daisy-talking-book-resource-file" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "253d9cd2-6da1-4280-86e6-42a96d631070", + "description": "Plextalk Project File (imph)", + "group": "c94ce0e6-c275-4c09-b802-695a18b7bf2a", + "slug": "plextalk-project-file-imph" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "d3361ad2-a623-48de-9887-dc981471a78a", + "description": "Plextalk Project File (imdn)", + "group": "c94ce0e6-c275-4c09-b802-695a18b7bf2a", + "slug": "plextalk-project-file-imdn" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "b9a9b719-5188-4a7a-a59b-4aea7df12028", + "description": "Plextalk Project File (imtt)", + "group": "c94ce0e6-c275-4c09-b802-695a18b7bf2a", + "slug": "plextalk-project-file-imtt" + } + }, + { + "model": "fpr.format", + "fields": { + "uuid": "e5000f9a-0ec6-455d-b242-f739310294b3", + "description": "HxC Floppy Emulator Stream Image", + "group": "3616a69f-e9c4-4357-b366-57082cf75a3e", + "slug": "hxc-floppy-emulator-stream-image" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.251Z", + "uuid": "258b1a5f-8a46-469e-b17e-d6964e1c4af4", + "format": "d002c654-a010-43bb-8782-733af0a0a43b", + "version": "None", + "pronom_id": "fmt/1946", + "description": "Draw.io Diagram", + "access_format": false, + "preservation_format": false, + "slug": "drawio-diagram" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.260Z", + "uuid": "634e1cb1-8999-4c3f-84ed-e218c2cc3421", + "format": "34448b47-cc4d-4c99-b081-a7b1673f4d35", + "version": "None", + "pronom_id": "fmt/1947", + "description": "OpenWayback CDXJ", + "access_format": false, + "preservation_format": false, + "slug": "openwayback-cdxj" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.288Z", + "uuid": "b9d7a274-7738-47f1-9d26-a1e77dfa67c9", + "format": "e778d72f-db63-431c-bfd7-b38191b30ba2", + "version": "2.0-2.5", + "pronom_id": "fmt/1948", + "description": "CDF 2.0-2.5", + "access_format": false, + "preservation_format": false, + "slug": "cdf-20-25" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.295Z", + "uuid": "d3a434d6-7e5f-4ee0-960c-76804c799f17", + "format": "e778d72f-db63-431c-bfd7-b38191b30ba2", + "version": "2.6-2.7", + "pronom_id": "fmt/1949", + "description": "CDF 2.6-2.7", + "access_format": false, + "preservation_format": false, + "slug": "cdf-26-27" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.340Z", + "uuid": "2c671999-2daf-4912-a2d6-674d60c73e35", + "format": "e778d72f-db63-431c-bfd7-b38191b30ba2", + "version": "3.x", + "pronom_id": "fmt/1950", + "description": "CDF 3.x", + "access_format": false, + "preservation_format": false, + "slug": "cdf-3x" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.347Z", + "uuid": "56f60af3-7e47-4157-aa60-0d73b5de57ab", + "format": "7ba5971f-3bd6-4f1a-84ec-35fd3cced676", + "version": "5-9", + "pronom_id": "fmt/1951", + "description": "Pro Tools Session File 5-9", + "access_format": false, + "preservation_format": false, + "slug": "pro-tools-session-file-5-9" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.371Z", + "uuid": "8aaa1412-9bdf-455b-b9ee-97afb324dc4e", + "format": "44a6fd9a-6f43-4057-940d-e5b25f64834f", + "version": "None", + "pronom_id": "fmt/1952", + "description": "PechaMaker Format", + "access_format": false, + "preservation_format": false, + "slug": "pechamaker-format" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.383Z", + "uuid": "1a19f0c9-e520-4d93-8d61-e9624ce9d791", + "format": "0864df9c-8543-49f7-99fa-5d529157d9e3", + "version": "H5", + "pronom_id": "fmt/1953", + "description": "Zoom Project Settings H5", + "access_format": false, + "preservation_format": false, + "slug": "zoom-project-settings-h5" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.392Z", + "uuid": "9be09e4e-a61e-4862-9f91-50651c0f3871", + "format": "0864df9c-8543-49f7-99fa-5d529157d9e3", + "version": "H6", + "pronom_id": "fmt/1954", + "description": "Zoom Project Settings H6", + "access_format": false, + "preservation_format": false, + "slug": "zoom-project-settings-h6" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.406Z", + "uuid": "6e91a64e-d81c-44d7-95df-8cfda2ad2d24", + "format": "299a6aec-e095-4754-af86-f63ac7f5df93", + "version": "6-9", + "pronom_id": "fmt/1955", + "description": "Graphisoft Archicad Project versions 6-9 (Intel Mac, little-endian)", + "access_format": false, + "preservation_format": false, + "slug": "graphisoft-archicad-project-versions-6-9-intel-mac" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.418Z", + "uuid": "58e7369d-e6ca-4e16-84e8-0a779177d3ca", + "format": "960a7a3a-a77a-4ff8-9e25-cf2fb40ce0e7", + "version": "None", + "pronom_id": "fmt/1956", + "description": "Sandboxels Save File", + "access_format": false, + "preservation_format": false, + "slug": "sandboxels-save-file" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.429Z", + "uuid": "feca53f3-0eca-4079-a9e1-cbc3ed767a72", + "format": "ea93a548-6dac-46af-8d06-f218f186934e", + "version": "None", + "pronom_id": "fmt/1957", + "description": "Program Embroidery Stitch (PES) File", + "access_format": false, + "preservation_format": false, + "slug": "program-embroidery-stitch-pes-file" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.447Z", + "uuid": "5dc10c9e-3923-40a9-af7a-3d91c8bdb424", + "format": "093f5f5e-1d49-4e79-971d-3aa8e6c2d7fd", + "version": "11", + "pronom_id": "fmt/1958", + "description": "Melco OFM Project v.11", + "access_format": false, + "preservation_format": false, + "slug": "melco-ofm-project-v11" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.454Z", + "uuid": "59fda400-81df-4891-9525-9ea02687b886", + "format": "093f5f5e-1d49-4e79-971d-3aa8e6c2d7fd", + "version": "pre v.11", + "pronom_id": "fmt/1959", + "description": "Melco OFM Project", + "access_format": false, + "preservation_format": false, + "slug": "melco-ofm-project" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.468Z", + "uuid": "e649f4d1-cb8c-4f8c-99c0-194a1e8de016", + "format": "14c43189-5a20-46cf-8f1d-d39fe8598369", + "version": "None", + "pronom_id": "fmt/1960", + "description": "Disklavier E-Seq Music", + "access_format": false, + "preservation_format": false, + "slug": "disklavier-e-seq-music" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.479Z", + "uuid": "d619f2ed-9107-4338-ab64-3efd1906e539", + "format": "1b61aebf-923b-4cae-9031-c1a8739bb668", + "version": "None", + "pronom_id": "fmt/1961", + "description": "Shorten (codec)", + "access_format": false, + "preservation_format": false, + "slug": "shorten-codec" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.488Z", + "uuid": "243a75a1-0cf8-4678-b6c1-192c7d8113a1", + "format": "baf1ad97-6588-4a02-9ff4-11391d66061b", + "version": "None", + "pronom_id": "fmt/1962", + "description": "SolidWorks Material Database File", + "access_format": false, + "preservation_format": false, + "slug": "solidworks-material-database-file" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.507Z", + "uuid": "c91858b5-6778-42a4-8038-8aac60af1875", + "format": "fe46d74b-f3bb-400b-a9a0-e33a750d883f", + "version": "TH5100", + "pronom_id": "fmt/1963", + "description": "NEC TH5100 Thermo Tracer Image File", + "access_format": false, + "preservation_format": false, + "slug": "nec-th5100-thermo-tracer-image-file" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.517Z", + "uuid": "f309e32f-1499-40f8-b8d4-9a278682ecc9", + "format": "83d0c7da-8941-4988-a4da-4326effffc6a", + "version": "None", + "pronom_id": "fmt/1964", + "description": "JPEG2000 \u2013 JPH/HTJ2K", + "access_format": false, + "preservation_format": false, + "slug": "jpeg2000-jphhtj2k" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.527Z", + "uuid": "d30c0d9a-55ea-40b8-a5d6-0fa45a847b5e", + "format": "4035be98-61d2-4b07-9ebf-1365eef8224f", + "version": "None", + "pronom_id": "fmt/1965", + "description": "Papyrus Document", + "access_format": false, + "preservation_format": false, + "slug": "papyrus-document" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.535Z", + "uuid": "8f3680f1-f294-4d00-a286-3f389fe86828", + "format": "7de7cf90-01fb-4a69-b2e7-abcba08eebab", + "version": "None", + "pronom_id": "fmt/1966", + "description": "Final Writer Document", + "access_format": false, + "preservation_format": false, + "slug": "final-writer-document" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.547Z", + "uuid": "07801de2-7a99-43ac-8e74-0e2d97d883ae", + "format": "00df1c6c-7e93-4259-b94d-945f03504d79", + "version": "2015+", + "pronom_id": "fmt/1967", + "description": "Solidworks Design Document Files 2015+", + "access_format": false, + "preservation_format": false, + "slug": "solidworks-design-document-files-2015" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.557Z", + "uuid": "7242bcbc-7dc0-41cc-864e-cfadb6694c40", + "format": "b91ea2c5-f745-4ed3-9743-63b040b2b45c", + "version": "v.1", + "pronom_id": "fmt/1968", + "description": "Atrac Codec File v.1", + "access_format": false, + "preservation_format": false, + "slug": "atrac-codec-file-v1" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.565Z", + "uuid": "875a8dd3-53ae-4dd1-884a-3fb1cc4901e0", + "format": "bb1677a1-3ece-4239-ada1-e17bb4dd5d08", + "version": "None", + "pronom_id": "fmt/1969", + "description": "ETC Express/Expression Show File", + "access_format": false, + "preservation_format": false, + "slug": "etc-expressexpression-show-file" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.583Z", + "uuid": "ce8af456-4561-4b38-bf73-8f84148496d3", + "format": "59c4ab9c-1dd8-4b23-a91a-48f9f9c9d63b", + "version": "None", + "pronom_id": "fmt/1970", + "description": "Moxcel Spreadsheet", + "access_format": false, + "preservation_format": false, + "slug": "moxcel-spreadsheet" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.595Z", + "uuid": "1586ee31-dc61-45d0-9677-d4964a183854", + "format": "c269901a-a61b-4790-99ce-af6d9dbe79f5", + "version": "1", + "pronom_id": "fmt/1971", + "description": "Enigma Binary File (Finale) v.1", + "access_format": false, + "preservation_format": false, + "slug": "enigma-binary-file-finale-v1" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.604Z", + "uuid": "ad589321-3a03-4009-a8ed-c3d53de4a9fc", + "format": "c269901a-a61b-4790-99ce-af6d9dbe79f5", + "version": "2", + "pronom_id": "fmt/1972", + "description": "Enigma Binary File (Finale) v.2", + "access_format": false, + "preservation_format": false, + "slug": "enigma-binary-file-finale-v2" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.612Z", + "uuid": "184513af-8194-4023-80d6-c0232d5ebf31", + "format": "8b8bf6ff-bd4c-40ea-ae72-7178b1063c18", + "version": "None", + "pronom_id": "fmt/1973", + "description": "Finale Performance Assessment", + "access_format": false, + "preservation_format": false, + "slug": "finale-performance-assessment" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.628Z", + "uuid": "f24f2ab1-f804-4c22-b9ff-949a44020f72", + "format": "f6a1e52e-9451-4828-963a-79bc10b8d032", + "version": "2014+", + "pronom_id": "fmt/1974", + "description": "Finale Notation File", + "access_format": false, + "preservation_format": false, + "slug": "finale-notation-file" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.643Z", + "uuid": "c627f600-9578-47c5-9283-baecd69d4b23", + "format": "2ea04202-e4fe-4e1b-a11a-1782469aedd1", + "version": "2", + "pronom_id": "fmt/1975", + "description": "ICC Profie v2", + "access_format": false, + "preservation_format": false, + "slug": "icc-profie-v2" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.650Z", + "uuid": "d9ebb87b-f1fb-4186-a99f-06dc97560a9f", + "format": "2ea04202-e4fe-4e1b-a11a-1782469aedd1", + "version": "4", + "pronom_id": "fmt/1976", + "description": "ICC Profile v4", + "access_format": false, + "preservation_format": false, + "slug": "icc-profile-v4" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.661Z", + "uuid": "ea63bd0d-6668-47e2-b7e7-a7c5d08f88d5", + "format": "2ea04202-e4fe-4e1b-a11a-1782469aedd1", + "version": "iccMAX", + "pronom_id": "fmt/1977", + "description": "ICC Profile v5", + "access_format": false, + "preservation_format": false, + "slug": "icc-profile-v5" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.670Z", + "uuid": "9877789c-464d-43f0-97fb-4a2903fa9514", + "format": "17f42669-2d81-496d-b14f-cb3dd82ed801", + "version": "1.2", + "pronom_id": "fmt/1978", + "description": "Sibelius Score v.1.2", + "access_format": false, + "preservation_format": false, + "slug": "sibelius-score-v12" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.679Z", + "uuid": "95ae84bd-03a1-4e34-9a30-6b3f9f01f3d6", + "format": "17f42669-2d81-496d-b14f-cb3dd82ed801", + "version": "2", + "pronom_id": "fmt/1979", + "description": "Sibelius Score v.2", + "access_format": false, + "preservation_format": false, + "slug": "sibelius-score-v2" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.685Z", + "uuid": "3423b862-b6cd-4973-a0d1-16f950a0c680", + "format": "17f42669-2d81-496d-b14f-cb3dd82ed801", + "version": "3", + "pronom_id": "fmt/1980", + "description": "Sibelius Score v.3", + "access_format": false, + "preservation_format": false, + "slug": "sibelius-score-v3" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.694Z", + "uuid": "79955af3-ac70-424c-90e9-43276bffca63", + "format": "17f42669-2d81-496d-b14f-cb3dd82ed801", + "version": "4", + "pronom_id": "fmt/1981", + "description": "Sibelius Score v.4", + "access_format": false, + "preservation_format": false, + "slug": "sibelius-score-v4" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.700Z", + "uuid": "14ddd997-d3af-4c8a-b542-642d779bd57d", + "format": "17f42669-2d81-496d-b14f-cb3dd82ed801", + "version": "5", + "pronom_id": "fmt/1982", + "description": "Sibelius Score v.5", + "access_format": false, + "preservation_format": false, + "slug": "sibelius-score-v5" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.711Z", + "uuid": "66f6deef-ef9e-4e14-bc45-f4d24b742f3a", + "format": "17f42669-2d81-496d-b14f-cb3dd82ed801", + "version": "6", + "pronom_id": "fmt/1983", + "description": "Sibelius Score v.6", + "access_format": false, + "preservation_format": false, + "slug": "sibelius-score-v6" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.718Z", + "uuid": "953f41ab-e98a-4f0a-a0b4-fb84e625f312", + "format": "17f42669-2d81-496d-b14f-cb3dd82ed801", + "version": "7", + "pronom_id": "fmt/1984", + "description": "Sibelius Score v.7", + "access_format": false, + "preservation_format": false, + "slug": "sibelius-score-v7" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.729Z", + "uuid": "67819faf-bcca-425d-a56f-28fa279e23d1", + "format": "17f42669-2d81-496d-b14f-cb3dd82ed801", + "version": "7.5-8.0", + "pronom_id": "fmt/1985", + "description": "Sibelius Score v.7.5-8.0", + "access_format": false, + "preservation_format": false, + "slug": "sibelius-score-v75-80" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.734Z", + "uuid": "788e0a72-18b7-4d75-9797-7b91a94ccef4", + "format": "17f42669-2d81-496d-b14f-cb3dd82ed801", + "version": "8.1-8.5", + "pronom_id": "fmt/1986", + "description": "Sibelius Score v.8.1-8.5", + "access_format": false, + "preservation_format": false, + "slug": "sibelius-score-v81-85" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.745Z", + "uuid": "c86d7daa-420c-4068-8f0e-6437a3a424a2", + "format": "17f42669-2d81-496d-b14f-cb3dd82ed801", + "version": "", + "pronom_id": "fmt/1987", + "description": "Sibelius Score v.8.6-2019.12", + "access_format": false, + "preservation_format": false, + "slug": "sibelius-score-v86-201912" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.750Z", + "uuid": "aabcfb8a-3a2f-41b9-a9e5-617e60fc2388", + "format": "17f42669-2d81-496d-b14f-cb3dd82ed801", + "version": "2020.1", + "pronom_id": "fmt/1988", + "description": "Sibelius Score v.2020.1", + "access_format": false, + "preservation_format": false, + "slug": "sibelius-score-v20201" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.761Z", + "uuid": "f503b129-1ebf-4866-a7e0-7cd46e0fa161", + "format": "17f42669-2d81-496d-b14f-cb3dd82ed801", + "version": "", + "pronom_id": "fmt/1989", + "description": "Sibelius Score v.2020.3-2022.5", + "access_format": false, + "preservation_format": false, + "slug": "sibelius-score-v20203-20225" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.769Z", + "uuid": "5e251d3b-2e91-4150-89bd-da93931cf812", + "format": "17f42669-2d81-496d-b14f-cb3dd82ed801", + "version": "", + "pronom_id": "fmt/1990", + "description": "Sibelius Score v.2022.7-2022.11", + "access_format": false, + "preservation_format": false, + "slug": "sibelius-score-v20227-202211" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.781Z", + "uuid": "2c28819c-cc31-4fa6-a284-54eb0c505490", + "format": "17f42669-2d81-496d-b14f-cb3dd82ed801", + "version": "", + "pronom_id": "fmt/1991", + "description": "Sibelius Score v.2022.12-2023.3", + "access_format": false, + "preservation_format": false, + "slug": "sibelius-score-v202212-20233" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.787Z", + "uuid": "4e09b394-0b04-4c3c-a0d1-e7656fdb83dc", + "format": "17f42669-2d81-496d-b14f-cb3dd82ed801", + "version": "", + "pronom_id": "fmt/1992", + "description": "Sibelius Score v.2023.5-2023.8", + "access_format": false, + "preservation_format": false, + "slug": "sibelius-score-v20235-20238" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.797Z", + "uuid": "d9be4eb6-7695-4724-99d4-a904236c444e", + "format": "17f42669-2d81-496d-b14f-cb3dd82ed801", + "version": "2024", + "pronom_id": "fmt/1993", + "description": "Sibelius Score v.2024", + "access_format": false, + "preservation_format": false, + "slug": "sibelius-score-v2024" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.807Z", + "uuid": "0bfff967-a249-4c48-9412-9185e8838158", + "format": "9fd09e78-091c-4c82-9295-e805bfbde356", + "version": "None", + "pronom_id": "fmt/1994", + "description": "Sibelius Scorch", + "access_format": false, + "preservation_format": false, + "slug": "sibelius-scorch" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.816Z", + "uuid": "48e8e2f6-4767-4bf2-a9d2-53c3441dab6f", + "format": "ae54ee29-5b7a-4cb0-b3b0-703516a1f3ec", + "version": "None", + "pronom_id": "fmt/1995", + "description": "WinFax Fax Image", + "access_format": false, + "preservation_format": false, + "slug": "winfax-fax-image" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.824Z", + "uuid": "eef17fa2-e12d-43bd-b9a6-6f52ecc3e37d", + "format": "02937b41-c87c-45d9-9e24-0897c89892c2", + "version": "None", + "pronom_id": "fmt/1996", + "description": "SPIR-V", + "access_format": false, + "preservation_format": false, + "slug": "spir-v" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.833Z", + "uuid": "d5069141-6f1b-449f-a175-3a31b3f022f9", + "format": "ca7a58ac-3125-4f61-9bf8-e6719cb273f7", + "version": "None", + "pronom_id": "fmt/1997", + "description": "IMF Package Asset Map", + "access_format": false, + "preservation_format": false, + "slug": "imf-package-asset-map" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.845Z", + "uuid": "30be40eb-f7bd-4dff-bd24-d9c397c51534", + "format": "4dd81041-9644-4270-97a6-299aa675a64b", + "version": "None", + "pronom_id": "fmt/1998", + "description": "IMF Package Packing List", + "access_format": false, + "preservation_format": false, + "slug": "imf-package-packing-list" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.854Z", + "uuid": "2f9c3586-08b1-42ca-aa4c-9395513368eb", + "format": "e8394ae8-fb2c-49fc-bcbb-ee30fa06fd9c", + "version": "None", + "pronom_id": "fmt/1999", + "description": "IMF Package Composition Playlist", + "access_format": false, + "preservation_format": false, + "slug": "imf-package-composition-playlist" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.867Z", + "uuid": "a14634af-09e3-49e1-af5c-86107759638e", + "format": "54ca3c25-f8a2-418c-b68d-050c344b7721", + "version": "None", + "pronom_id": "fmt/2000", + "description": "Husqvarna Embroidery Stitch File", + "access_format": false, + "preservation_format": false, + "slug": "husqvarna-embroidery-stitch-file" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.875Z", + "uuid": "aaabe296-406c-4573-9a39-69e03620b876", + "format": "2d0203e5-9176-4412-ae81-dd0d5bc8704d", + "version": "None", + "pronom_id": "fmt/2001", + "description": "Husqvarna / Pfaff Embroidery Stitch File", + "access_format": false, + "preservation_format": false, + "slug": "husqvarna-pfaff-embroidery-stitch-file" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.886Z", + "uuid": "64899ad4-d7c0-4052-b212-a9d8fae13945", + "format": "6b5fb194-fc87-49d7-a9e8-e0f3204a62ba", + "version": "None", + "pronom_id": "fmt/2002", + "description": "Husqvarna / TruE Embroidery Stitch File", + "access_format": false, + "preservation_format": false, + "slug": "husqvarna-true-embroidery-stitch-file" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.895Z", + "uuid": "59ecf79a-8ca9-4d65-8818-9ebfcbceec44", + "format": "c2aa12a6-d6ab-4632-bde6-61c2689db700", + "version": "None", + "pronom_id": "fmt/2003", + "description": "Husqvarna / Premier+ Stitch File", + "access_format": false, + "preservation_format": false, + "slug": "husqvarna-premier-stitch-file" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.911Z", + "uuid": "189e2172-9a64-4394-9484-58f533456ccd", + "format": "8254bb32-b22a-4115-bce5-d92701076fef", + "version": "None", + "pronom_id": "fmt/2004", + "description": "Husqvarna-Viking Designer 1 Stitch File", + "access_format": false, + "preservation_format": false, + "slug": "husqvarna-viking-designer-1-stitch-file" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.920Z", + "uuid": "a04f84bc-0879-4843-b33a-28d08602e4f5", + "format": "9defae19-8a2a-4886-81f0-26682466dea5", + "version": "3.1+", + "pronom_id": "fmt/2005", + "description": "Compressed MusicXML", + "access_format": false, + "preservation_format": false, + "slug": "compressed-musicxml" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.926Z", + "uuid": "ad619dad-2f34-4a2b-a3b7-7cf48ee5ba20", + "format": "896e3639-f500-4b6f-9d1d-9747519ed17f", + "version": "18", + "pronom_id": "fmt/2006", + "description": "QuarkXPress 18 (Motorola)", + "access_format": false, + "preservation_format": false, + "slug": "quarkxpress-18-motorola" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.937Z", + "uuid": "90ddeca1-dd2c-46b5-857f-1ee8d4a66365", + "format": "896e3639-f500-4b6f-9d1d-9747519ed17f", + "version": "19", + "pronom_id": "fmt/2007", + "description": "QuarkXPress 19 (Motorola)", + "access_format": false, + "preservation_format": false, + "slug": "quarkxpress-19-motorola" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.943Z", + "uuid": "2c0de7f2-2e75-499e-ae39-26a82ccd3261", + "format": "896e3639-f500-4b6f-9d1d-9747519ed17f", + "version": "20", + "pronom_id": "fmt/2008", + "description": "QuarkXPress 20 (Motorola)", + "access_format": false, + "preservation_format": false, + "slug": "quarkxpress-20-motorola" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.956Z", + "uuid": "3e59d742-521f-431a-bfe9-77d35313c993", + "format": "07924713-c889-434e-8703-e04764600506", + "version": "3.3", + "pronom_id": "fmt/2009", + "description": "Protein Data Bank File v.3.3", + "access_format": false, + "preservation_format": false, + "slug": "protein-data-bank-file-v33" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.969Z", + "uuid": "f64c9a9e-94db-4c12-adb1-a6508fb1b077", + "format": "02234cb1-2615-47d5-9ccd-ea1c60b9a877", + "version": "1.0", + "pronom_id": "fmt/2010", + "description": "vtk v.1", + "access_format": false, + "preservation_format": false, + "slug": "vtk-v1" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.974Z", + "uuid": "0420ebed-803a-4bac-b87e-9d64661bc4d6", + "format": "02234cb1-2615-47d5-9ccd-ea1c60b9a877", + "version": "2.0", + "pronom_id": "fmt/2011", + "description": "vtk v.2", + "access_format": false, + "preservation_format": false, + "slug": "vtk-v2" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.985Z", + "uuid": "fea63b8e-ef1b-4dc1-a2ac-9290f3feef58", + "format": "02234cb1-2615-47d5-9ccd-ea1c60b9a877", + "version": "4.2", + "pronom_id": "fmt/2012", + "description": "vtk v.4.2", + "access_format": false, + "preservation_format": false, + "slug": "vtk-v42" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.993Z", + "uuid": "78935e71-625a-408c-a51a-9e212be15144", + "format": "0d7a0f03-9df5-481d-97fa-19187c697539", + "version": "None", + "pronom_id": "fmt/2013", + "description": "RawACF", + "access_format": false, + "preservation_format": false, + "slug": "rawacf" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.002Z", + "uuid": "3ec8b5d3-b6f1-4234-8da3-e4ab920677d5", + "format": "617e119d-b394-41fe-ba46-65a40f7c0859", + "version": "None", + "pronom_id": "fmt/2014", + "description": "Axon Binary Format", + "access_format": false, + "preservation_format": false, + "slug": "axon-binary-format" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.011Z", + "uuid": "e133cc8c-3855-4014-8c28-6e75e8ebd52d", + "format": "8b4c1389-57e7-4cb6-b193-b4187d7fa070", + "version": "3", + "pronom_id": "fmt/2015", + "description": "KryoFlux Stream v.3", + "access_format": false, + "preservation_format": false, + "slug": "kryoflux-stream-v3" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.024Z", + "uuid": "93fb6830-358b-40fa-844f-00934a7ac455", + "format": "47498ffa-f70c-4b40-b281-113fa83cc656", + "version": "1", + "pronom_id": "fmt/2016", + "description": "Binvox v.1", + "access_format": false, + "preservation_format": false, + "slug": "binvox-v1" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.030Z", + "uuid": "55abc77f-264d-4f3a-b6b5-f264baf55686", + "format": "39d89ff4-f419-455a-902e-895255b21261", + "version": "5-9", + "pronom_id": "fmt/2017", + "description": "GraphPad Prism 5-9", + "access_format": false, + "preservation_format": false, + "slug": "graphpad-prism-5-9" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.039Z", + "uuid": "0127fa9c-5d19-424b-9f10-05fcbea34d78", + "format": "3714eeb2-324c-46ca-9d66-c9b06004c8ff", + "version": "None", + "pronom_id": "fmt/2018", + "description": "Sony Open MG Audio", + "access_format": false, + "preservation_format": false, + "slug": "sony-open-mg-audio" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.048Z", + "uuid": "9efdec30-d1bc-4abd-ab3f-54f596e00008", + "format": "a0dbdee2-6e38-4a6b-9acf-e1edc20f889f", + "version": "None", + "pronom_id": "fmt/2019", + "description": "askSam for DOS", + "access_format": false, + "preservation_format": false, + "slug": "asksam-for-dos" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.056Z", + "uuid": "ba903ffd-50ee-49bd-96d5-d337b34fdbf2", + "format": "75bf6e5a-f40f-479d-843a-0670c5641856", + "version": "1", + "pronom_id": "fmt/2020", + "description": "askSam for Windows v.1", + "access_format": false, + "preservation_format": false, + "slug": "asksam-for-windows-v1" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.067Z", + "uuid": "57223ceb-7047-452a-a800-f291c9ba9b7c", + "format": "75bf6e5a-f40f-479d-843a-0670c5641856", + "version": "2-3", + "pronom_id": "fmt/2021", + "description": "askSam for Windows v.2-3", + "access_format": false, + "preservation_format": false, + "slug": "asksam-for-windows-v2-3" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.072Z", + "uuid": "8cb1e911-2689-4ae4-a670-deaf302b8d45", + "format": "75bf6e5a-f40f-479d-843a-0670c5641856", + "version": "4-7", + "pronom_id": "fmt/2022", + "description": "askSam for Windows v.4-7", + "access_format": false, + "preservation_format": false, + "slug": "asksam-for-windows-v4-7" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.086Z", + "uuid": "9221caf5-5419-4efd-ab11-282925d2bb03", + "format": "d245bbe6-b461-4f3a-bd4d-f8bf7b9e151d", + "version": "None", + "pronom_id": "fmt/2023", + "description": "Parquet File", + "access_format": false, + "preservation_format": false, + "slug": "parquet-file" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.094Z", + "uuid": "89d5ced6-c147-47ff-bdae-f527d2ef01da", + "format": "906669ab-969a-463d-b815-a7cb1e208e01", + "version": "4", + "pronom_id": "fmt/2024", + "description": "CD Architect 4", + "access_format": false, + "preservation_format": false, + "slug": "cd-architect-4" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.100Z", + "uuid": "dd584c1a-5c65-49e1-9e11-761b6defb6ca", + "format": "906669ab-969a-463d-b815-a7cb1e208e01", + "version": "5", + "pronom_id": "fmt/2025", + "description": "CD Architect 5", + "access_format": false, + "preservation_format": false, + "slug": "cd-architect-5" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.112Z", + "uuid": "fe37498d-6549-45e0-b5c0-4ad4cde2b903", + "format": "d467c620-1ab7-43d2-b131-8399f02a6186", + "version": "None", + "pronom_id": "fmt/2026", + "description": "Codebook Exchange Format", + "access_format": false, + "preservation_format": false, + "slug": "codebook-exchange-format" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.119Z", + "uuid": "d05a45b7-53b2-4491-ba5c-84c509dcbc4b", + "format": "efe751be-6225-469f-96d8-4abb25ba0302", + "version": "1", + "pronom_id": "fmt/2027", + "description": "Microsoft Project v.1", + "access_format": false, + "preservation_format": false, + "slug": "microsoft-project-v1" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.130Z", + "uuid": "d1f3a088-f472-4067-a6e9-22a66285945b", + "format": "efe751be-6225-469f-96d8-4abb25ba0302", + "version": "3", + "pronom_id": "fmt/2028", + "description": "Microsoft Project v.3", + "access_format": false, + "preservation_format": false, + "slug": "microsoft-project-v3" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.147Z", + "uuid": "28dc979e-42eb-4fda-9f6d-4e0ca62e59c8", + "format": "29f6de66-8720-4d23-92f7-02e7d22c89a9", + "version": "None", + "pronom_id": "fmt/2029", + "description": "Apache Avro", + "access_format": false, + "preservation_format": false, + "slug": "apache-avro" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.157Z", + "uuid": "37278345-edb5-4073-9907-04f4e017ada8", + "format": "15c7eca5-c42a-4cf6-9094-7421f582bd4c", + "version": "V0 and V1", + "pronom_id": "fmt/2030", + "description": "Apache ORC", + "access_format": false, + "preservation_format": false, + "slug": "apache-orc" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.167Z", + "uuid": "600f1f17-3cc3-4c2c-9fa8-85759738a968", + "format": "88c9c247-58ed-4865-94e1-4b5b84ff9a89", + "version": "None", + "pronom_id": "fmt/2031", + "description": "HxC Floppy Emulator Disk Image", + "access_format": false, + "preservation_format": false, + "slug": "hxc-floppy-emulator-disk-image" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.176Z", + "uuid": "3e5ad5f8-3959-479c-8528-f6c42394f905", + "format": "13136e4b-a224-4097-b6b2-beb455110c82", + "version": "3", + "pronom_id": "fmt/2032", + "description": "Open Packaging Format", + "access_format": false, + "preservation_format": false, + "slug": "open-packaging-format" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.191Z", + "uuid": "d171a85f-ddda-4c22-887f-908b7c1b7056", + "format": "1e82cd06-a79b-41f9-93e8-9e072faa3b61", + "version": "3", + "pronom_id": "fmt/2033", + "description": "Daisy Talking Book Navigation Control File v.3", + "access_format": false, + "preservation_format": false, + "slug": "daisy-talking-book-navigation-control-file-v3" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.201Z", + "uuid": "2268e8ad-12f5-4103-a9af-be5f45da8c41", + "format": "cd8b7550-5d70-437d-8361-474881c513fa", + "version": "3", + "pronom_id": "fmt/2034", + "description": "Daisy Talking Book Resource file v.3", + "access_format": false, + "preservation_format": false, + "slug": "daisy-talking-book-resource-file-v3" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.210Z", + "uuid": "57791b95-220a-4715-812b-04f57d1916d4", + "format": "253d9cd2-6da1-4280-86e6-42a96d631070", + "version": "None", + "pronom_id": "fmt/2035", + "description": "Plextalk Project File (imph)", + "access_format": false, + "preservation_format": false, + "slug": "plextalk-project-file-imph" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.220Z", + "uuid": "c762de68-f599-4350-bb5f-d1c9046162d3", + "format": "d3361ad2-a623-48de-9887-dc981471a78a", + "version": "None", + "pronom_id": "fmt/2036", + "description": "Plextalk Project File (imdn)", + "access_format": false, + "preservation_format": false, + "slug": "plextalk-project-file-imdn" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.229Z", + "uuid": "c01732ec-9d92-4d0b-884b-e1aea95f46a6", + "format": "b9a9b719-5188-4a7a-a59b-4aea7df12028", + "version": "None", + "pronom_id": "fmt/2037", + "description": "Plextalk Project File (imtt)", + "access_format": false, + "preservation_format": false, + "slug": "plextalk-project-file-imtt" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.235Z", + "uuid": "0a0ed164-a86c-44f0-94cb-3672ac11b0f6", + "format": "88c9c247-58ed-4865-94e1-4b5b84ff9a89", + "version": "3", + "pronom_id": "fmt/2038", + "description": "HxC Floppy Emulator Disk Image v.3", + "access_format": false, + "preservation_format": false, + "slug": "hxc-floppy-emulator-disk-image-v3" + } + }, + { + "model": "fpr.formatversion", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.249Z", + "uuid": "1ed80e9c-3a01-4e3f-891b-d0acde9a02e1", + "format": "e5000f9a-0ec6-455d-b242-f739310294b3", + "version": "None", + "pronom_id": "fmt/2039", + "description": "HxC Floppy Emulator Stream Image", + "access_format": false, + "preservation_format": false, + "slug": "hxc-floppy-emulator-stream-image" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.257Z", + "uuid": "d21b5ef4-bc97-40fe-87b2-556d93eb530d", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "258b1a5f-8a46-469e-b17e-d6964e1c4af4", + "command_output": ".drawio" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.346Z", + "uuid": "b98e56d7-9325-4539-a978-69e29a887216", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "2c671999-2daf-4912-a2d6-674d60c73e35", + "command_output": ".cdf" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.354Z", + "uuid": "350af898-4e2b-4a47-971f-61e328590b0d", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "56f60af3-7e47-4157-aa60-0d73b5de57ab", + "command_output": ".ptf" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.378Z", + "uuid": "0e413220-6521-4cbd-8ee9-b3d5b3c9ee05", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "8aaa1412-9bdf-455b-b9ee-97afb324dc4e", + "command_output": ".pxp" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.413Z", + "uuid": "42c0fd5b-df02-4862-836e-68bb244a1cff", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "6e91a64e-d81c-44d7-95df-8cfda2ad2d24", + "command_output": ".pla" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.424Z", + "uuid": "0533c570-31dd-4a97-8482-22741740683d", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "58e7369d-e6ca-4e16-84e8-0a779177d3ca", + "command_output": ".sbxls" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.435Z", + "uuid": "da171a62-91b0-4909-b82d-4ec9d362a4c3", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "feca53f3-0eca-4079-a9e1-cbc3ed767a72", + "command_output": ".pes" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.475Z", + "uuid": "bb6331f0-ca29-45ae-8aac-9c44b91041e0", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "e649f4d1-cb8c-4f8c-99c0-194a1e8de016", + "command_output": ".fil" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.484Z", + "uuid": "797b8132-93c9-4f17-b55f-005c48b34189", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "d619f2ed-9107-4338-ab64-3efd1906e539", + "command_output": ".shn" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.494Z", + "uuid": "8dc37734-3d47-40ed-ad1e-3641fdfa7521", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "243a75a1-0cf8-4678-b6c1-192c7d8113a1", + "command_output": ".sldmat" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.513Z", + "uuid": "e3f17ecc-4993-4527-8812-71e0573da4ec", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "c91858b5-6778-42a4-8038-8aac60af1875", + "command_output": ".tmp" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.523Z", + "uuid": "1b6a065b-6ea9-4582-a932-cfcecec876d6", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "f309e32f-1499-40f8-b8d4-9a278682ecc9", + "command_output": ".jph" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.531Z", + "uuid": "b89a28a1-7694-4b4a-a665-e20cd07bf285", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "d30c0d9a-55ea-40b8-a5d6-0fa45a847b5e", + "command_output": ".pap" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.553Z", + "uuid": "e6dc212d-0b5e-49ed-9714-4707ca2841ea", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "07801de2-7a99-43ac-8e74-0e2d97d883ae", + "command_output": ".sldprt" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.562Z", + "uuid": "b70bb89b-d9c9-4621-a167-f7e48bd595b5", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "7242bcbc-7dc0-41cc-864e-cfadb6694c40", + "command_output": ".aea" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.609Z", + "uuid": "469435df-a6df-4d33-a6e8-9271476d3dc8", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "ad589321-3a03-4009-a8ed-c3d53de4a9fc", + "command_output": ".mus" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.617Z", + "uuid": "7aa8347d-4756-430f-a687-f7a90a6891db", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "184513af-8194-4023-80d6-c0232d5ebf31", + "command_output": ".fpa" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.666Z", + "uuid": "c03a79dc-d68c-4eba-bb56-b8813c88b3d8", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "ea63bd0d-6668-47e2-b7e7-a7c5d08f88d5", + "command_output": ".icc" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.804Z", + "uuid": "a7246919-f24d-4d20-993f-a254c8768abe", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "d9be4eb6-7695-4724-99d4-a904236c444e", + "command_output": ".sib" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.812Z", + "uuid": "dd171de5-0a7e-474f-990c-8e52d2879dfc", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "0bfff967-a249-4c48-9412-9185e8838158", + "command_output": ".sco" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.821Z", + "uuid": "290aeacc-836e-4d97-9d82-b138d111c5b5", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "48e8e2f6-4767-4bf2-a9d2-53c3441dab6f", + "command_output": ".fxr" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.829Z", + "uuid": "00eef400-5d28-448f-8028-dd1b44478ab0", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "eef17fa2-e12d-43bd-b9a6-6f52ecc3e37d", + "command_output": ".spirv" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.872Z", + "uuid": "3a2e0850-3dc5-43cb-9126-3ecd03cfaa4e", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "a14634af-09e3-49e1-af5c-86107759638e", + "command_output": ".hus" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.881Z", + "uuid": "be02e2aa-615d-4ddc-8d99-cf5603302b42", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "aaabe296-406c-4573-9a39-69e03620b876", + "command_output": ".vip" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.891Z", + "uuid": "69b91cc8-7f0c-42a2-8d9a-0e177156ac34", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "64899ad4-d7c0-4052-b212-a9d8fae13945", + "command_output": ".vp3" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.901Z", + "uuid": "e64862fc-6fd7-4ce9-8128-541d1184c17a", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "59ecf79a-8ca9-4d65-8818-9ebfcbceec44", + "command_output": ".vp4" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.917Z", + "uuid": "f00142a3-8d90-48f9-81f0-2c8630b04126", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "189e2172-9a64-4394-9484-58f533456ccd", + "command_output": ".shv" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.925Z", + "uuid": "23465d54-1bfd-46ba-9814-51468aa06e57", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "a04f84bc-0879-4843-b33a-28d08602e4f5", + "command_output": ".mxl" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.990Z", + "uuid": "bae52118-2d94-4d26-8f24-57a1101facfa", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "fea63b8e-ef1b-4dc1-a2ac-9290f3feef58", + "command_output": ".vtk" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:17.998Z", + "uuid": "69058a31-35a5-4a16-8f65-da703bbd6672", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "78935e71-625a-408c-a51a-9e212be15144", + "command_output": ".rawacf" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.007Z", + "uuid": "d83fd345-21d1-4bf6-8b9f-12b609ffeb74", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "3ec8b5d3-b6f1-4234-8da3-e4ab920677d5", + "command_output": ".abf" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.029Z", + "uuid": "4b8368f2-4447-44fe-bae5-6a7587de6fe6", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "93fb6830-358b-40fa-844f-00934a7ac455", + "command_output": ".binvox" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.035Z", + "uuid": "4829890a-7d39-40e8-96e5-39de6a5bfdab", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "55abc77f-264d-4f3a-b6b5-f264baf55686", + "command_output": ".pzfx" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.044Z", + "uuid": "5edb63ae-5a1e-4707-ba9e-9b8e0594e5e9", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "0127fa9c-5d19-424b-9f10-05fcbea34d78", + "command_output": ".oma" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.090Z", + "uuid": "31b3bc9e-1868-458b-970a-dba0088b805c", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "9221caf5-5419-4efd-ab11-282925d2bb03", + "command_output": ".parquet" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.117Z", + "uuid": "db6f23ed-04f7-4f73-b8b3-1eaa367b85a2", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "fe37498d-6549-45e0-b5c0-4ad4cde2b903", + "command_output": ".qdc" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.136Z", + "uuid": "7d4f7b52-c2fa-48dd-8721-d88b7bf70220", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "d1f3a088-f472-4067-a6e9-22a66285945b", + "command_output": ".mpp" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.153Z", + "uuid": "8018849b-092d-48f3-bbcc-df0525fda4f4", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "28dc979e-42eb-4fda-9f6d-4e0ca62e59c8", + "command_output": ".avro" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.163Z", + "uuid": "68ec5b6d-4837-4007-9bd4-ad59be53f298", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "37278345-edb5-4073-9907-04f4e017ada8", + "command_output": ".orc" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.197Z", + "uuid": "8c8defc0-3600-498c-8ab7-cf684e0de9fe", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "d171a85f-ddda-4c22-887f-908b7c1b7056", + "command_output": ".ncx" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.206Z", + "uuid": "51aebce8-4713-4a00-8b80-0b9efcf2a0ab", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "2268e8ad-12f5-4103-a9af-be5f45da8c41", + "command_output": ".res" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.216Z", + "uuid": "160e1b5f-6b46-4875-a09e-18a09a18ba1f", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "57791b95-220a-4715-812b-04f57d1916d4", + "command_output": ".imph" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.225Z", + "uuid": "9cf71f92-0742-43ad-982d-6c8faf266642", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "c762de68-f599-4350-bb5f-d1c9046162d3", + "command_output": ".imdn" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.234Z", + "uuid": "95052e22-ff10-4f8b-8a76-41ef91438049", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "c01732ec-9d92-4d0b-884b-e1aea95f46a6", + "command_output": ".imtt" + } + }, + { + "model": "fpr.idrule", + "fields": { + "replaces": null, + "enabled": true, + "lastmodified": "2025-04-14T17:02:18.255Z", + "uuid": "4a3e317e-e411-4a40-8c96-3d5d73bf9037", + "command": "8546b624-7894-4201-8df6-f239d5e0d5ba", + "format": "1ed80e9c-3a01-4e3f-891b-d0acde9a02e1", + "command_output": ".hfe" + } + } +] diff --git a/src/dashboard/src/fpr/migrations/pronom_88.json b/src/archivematica/dashboard/fpr/migrations/pronom_88.json similarity index 100% rename from src/dashboard/src/fpr/migrations/pronom_88.json rename to src/archivematica/dashboard/fpr/migrations/pronom_88.json diff --git a/src/dashboard/src/fpr/migrations/pronom_90.json b/src/archivematica/dashboard/fpr/migrations/pronom_90.json similarity index 100% rename from src/dashboard/src/fpr/migrations/pronom_90.json rename to src/archivematica/dashboard/fpr/migrations/pronom_90.json diff --git a/src/dashboard/src/fpr/migrations/pronom_92.json b/src/archivematica/dashboard/fpr/migrations/pronom_92.json similarity index 100% rename from src/dashboard/src/fpr/migrations/pronom_92.json rename to src/archivematica/dashboard/fpr/migrations/pronom_92.json diff --git a/src/dashboard/src/fpr/migrations/pronom_93.json b/src/archivematica/dashboard/fpr/migrations/pronom_93.json similarity index 100% rename from src/dashboard/src/fpr/migrations/pronom_93.json rename to src/archivematica/dashboard/fpr/migrations/pronom_93.json diff --git a/src/dashboard/src/fpr/migrations/pronom_94.json b/src/archivematica/dashboard/fpr/migrations/pronom_94.json similarity index 100% rename from src/dashboard/src/fpr/migrations/pronom_94.json rename to src/archivematica/dashboard/fpr/migrations/pronom_94.json diff --git a/src/dashboard/src/fpr/migrations/pronom_96.json b/src/archivematica/dashboard/fpr/migrations/pronom_96.json similarity index 100% rename from src/dashboard/src/fpr/migrations/pronom_96.json rename to src/archivematica/dashboard/fpr/migrations/pronom_96.json diff --git a/src/dashboard/src/fpr/models.py b/src/archivematica/dashboard/fpr/models.py similarity index 99% rename from src/dashboard/src/fpr/models.py rename to src/archivematica/dashboard/fpr/models.py index e504394f14..ae72166f0a 100644 --- a/src/dashboard/src/fpr/models.py +++ b/src/archivematica/dashboard/fpr/models.py @@ -14,7 +14,8 @@ from django.db import connection from django.db import models from django.utils.translation import gettext_lazy as _ -from main.models import UUIDField + +from archivematica.dashboard.main.models import UUIDField logger = logging.getLogger(__name__) diff --git a/src/dashboard/src/fpr/templates/fpr/_sidebar.html b/src/archivematica/dashboard/fpr/templates/fpr/_sidebar.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/_sidebar.html rename to src/archivematica/dashboard/fpr/templates/fpr/_sidebar.html diff --git a/src/dashboard/src/fpr/templates/fpr/app_layout.html b/src/archivematica/dashboard/fpr/templates/fpr/app_layout.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/app_layout.html rename to src/archivematica/dashboard/fpr/templates/fpr/app_layout.html diff --git a/src/dashboard/src/fpr/templates/fpr/disable.html b/src/archivematica/dashboard/fpr/templates/fpr/disable.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/disable.html rename to src/archivematica/dashboard/fpr/templates/fpr/disable.html diff --git a/src/dashboard/src/fpr/templates/fpr/format/detail.html b/src/archivematica/dashboard/fpr/templates/fpr/format/detail.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/format/detail.html rename to src/archivematica/dashboard/fpr/templates/fpr/format/detail.html diff --git a/src/dashboard/src/fpr/templates/fpr/format/form.html b/src/archivematica/dashboard/fpr/templates/fpr/format/form.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/format/form.html rename to src/archivematica/dashboard/fpr/templates/fpr/format/form.html diff --git a/src/dashboard/src/fpr/templates/fpr/format/group/delete.html b/src/archivematica/dashboard/fpr/templates/fpr/format/group/delete.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/format/group/delete.html rename to src/archivematica/dashboard/fpr/templates/fpr/format/group/delete.html diff --git a/src/dashboard/src/fpr/templates/fpr/format/group/form.html b/src/archivematica/dashboard/fpr/templates/fpr/format/group/form.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/format/group/form.html rename to src/archivematica/dashboard/fpr/templates/fpr/format/group/form.html diff --git a/src/dashboard/src/fpr/templates/fpr/format/group/list.html b/src/archivematica/dashboard/fpr/templates/fpr/format/group/list.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/format/group/list.html rename to src/archivematica/dashboard/fpr/templates/fpr/format/group/list.html diff --git a/src/dashboard/src/fpr/templates/fpr/format/list.html b/src/archivematica/dashboard/fpr/templates/fpr/format/list.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/format/list.html rename to src/archivematica/dashboard/fpr/templates/fpr/format/list.html diff --git a/src/dashboard/src/fpr/templates/fpr/format/version/detail.html b/src/archivematica/dashboard/fpr/templates/fpr/format/version/detail.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/format/version/detail.html rename to src/archivematica/dashboard/fpr/templates/fpr/format/version/detail.html diff --git a/src/dashboard/src/fpr/templates/fpr/format/version/form.html b/src/archivematica/dashboard/fpr/templates/fpr/format/version/form.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/format/version/form.html rename to src/archivematica/dashboard/fpr/templates/fpr/format/version/form.html diff --git a/src/dashboard/src/fpr/templates/fpr/fpcommand/detail.html b/src/archivematica/dashboard/fpr/templates/fpr/fpcommand/detail.html similarity index 98% rename from src/dashboard/src/fpr/templates/fpr/fpcommand/detail.html rename to src/archivematica/dashboard/fpr/templates/fpr/fpcommand/detail.html index a2c82943fb..0187cf2015 100644 --- a/src/dashboard/src/fpr/templates/fpr/fpcommand/detail.html +++ b/src/archivematica/dashboard/fpr/templates/fpr/fpcommand/detail.html @@ -24,7 +24,7 @@
{% trans "UUID" %}
-
{{ fpcommand.uuid }} Rule
+
{{ fpcommand.uuid }}
{% trans "Description" %}
{{ fpcommand.description }}
{% trans "Tool" %}
diff --git a/src/dashboard/src/fpr/templates/fpr/fpcommand/form.html b/src/archivematica/dashboard/fpr/templates/fpr/fpcommand/form.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/fpcommand/form.html rename to src/archivematica/dashboard/fpr/templates/fpr/fpcommand/form.html diff --git a/src/dashboard/src/fpr/templates/fpr/fpcommand/list.html b/src/archivematica/dashboard/fpr/templates/fpr/fpcommand/list.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/fpcommand/list.html rename to src/archivematica/dashboard/fpr/templates/fpr/fpcommand/list.html diff --git a/src/dashboard/src/fpr/templates/fpr/fprule/detail.html b/src/archivematica/dashboard/fpr/templates/fpr/fprule/detail.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/fprule/detail.html rename to src/archivematica/dashboard/fpr/templates/fpr/fprule/detail.html diff --git a/src/dashboard/src/fpr/templates/fpr/fprule/form.html b/src/archivematica/dashboard/fpr/templates/fpr/fprule/form.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/fprule/form.html rename to src/archivematica/dashboard/fpr/templates/fpr/fprule/form.html diff --git a/src/dashboard/src/fpr/templates/fpr/fprule/list.html b/src/archivematica/dashboard/fpr/templates/fpr/fprule/list.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/fprule/list.html rename to src/archivematica/dashboard/fpr/templates/fpr/fprule/list.html diff --git a/src/dashboard/src/fpr/templates/fpr/fptool/detail.html b/src/archivematica/dashboard/fpr/templates/fpr/fptool/detail.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/fptool/detail.html rename to src/archivematica/dashboard/fpr/templates/fpr/fptool/detail.html diff --git a/src/dashboard/src/fpr/templates/fpr/fptool/form.html b/src/archivematica/dashboard/fpr/templates/fpr/fptool/form.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/fptool/form.html rename to src/archivematica/dashboard/fpr/templates/fpr/fptool/form.html diff --git a/src/dashboard/src/fpr/templates/fpr/fptool/list.html b/src/archivematica/dashboard/fpr/templates/fpr/fptool/list.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/fptool/list.html rename to src/archivematica/dashboard/fpr/templates/fpr/fptool/list.html diff --git a/src/dashboard/src/fpr/templates/fpr/idcommand/detail.html b/src/archivematica/dashboard/fpr/templates/fpr/idcommand/detail.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/idcommand/detail.html rename to src/archivematica/dashboard/fpr/templates/fpr/idcommand/detail.html diff --git a/src/dashboard/src/fpr/templates/fpr/idcommand/form.html b/src/archivematica/dashboard/fpr/templates/fpr/idcommand/form.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/idcommand/form.html rename to src/archivematica/dashboard/fpr/templates/fpr/idcommand/form.html diff --git a/src/dashboard/src/fpr/templates/fpr/idcommand/list.html b/src/archivematica/dashboard/fpr/templates/fpr/idcommand/list.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/idcommand/list.html rename to src/archivematica/dashboard/fpr/templates/fpr/idcommand/list.html diff --git a/src/dashboard/src/fpr/templates/fpr/idrule/detail.html b/src/archivematica/dashboard/fpr/templates/fpr/idrule/detail.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/idrule/detail.html rename to src/archivematica/dashboard/fpr/templates/fpr/idrule/detail.html diff --git a/src/dashboard/src/fpr/templates/fpr/idrule/form.html b/src/archivematica/dashboard/fpr/templates/fpr/idrule/form.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/idrule/form.html rename to src/archivematica/dashboard/fpr/templates/fpr/idrule/form.html diff --git a/src/dashboard/src/fpr/templates/fpr/idrule/list.html b/src/archivematica/dashboard/fpr/templates/fpr/idrule/list.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/idrule/list.html rename to src/archivematica/dashboard/fpr/templates/fpr/idrule/list.html diff --git a/src/dashboard/src/fpr/templates/fpr/idtool/detail.html b/src/archivematica/dashboard/fpr/templates/fpr/idtool/detail.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/idtool/detail.html rename to src/archivematica/dashboard/fpr/templates/fpr/idtool/detail.html diff --git a/src/dashboard/src/fpr/templates/fpr/idtool/form.html b/src/archivematica/dashboard/fpr/templates/fpr/idtool/form.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/idtool/form.html rename to src/archivematica/dashboard/fpr/templates/fpr/idtool/form.html diff --git a/src/dashboard/src/fpr/templates/fpr/idtool/list.html b/src/archivematica/dashboard/fpr/templates/fpr/idtool/list.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/idtool/list.html rename to src/archivematica/dashboard/fpr/templates/fpr/idtool/list.html diff --git a/src/dashboard/src/fpr/templates/fpr/revisions/list.html b/src/archivematica/dashboard/fpr/templates/fpr/revisions/list.html similarity index 100% rename from src/dashboard/src/fpr/templates/fpr/revisions/list.html rename to src/archivematica/dashboard/fpr/templates/fpr/revisions/list.html diff --git a/src/dashboard/src/installer/__init__.py b/src/archivematica/dashboard/fpr/templatetags/__init__.py similarity index 100% rename from src/dashboard/src/installer/__init__.py rename to src/archivematica/dashboard/fpr/templatetags/__init__.py diff --git a/src/dashboard/src/fpr/templatetags/revisions_link.py b/src/archivematica/dashboard/fpr/templatetags/revisions_link.py similarity index 100% rename from src/dashboard/src/fpr/templatetags/revisions_link.py rename to src/archivematica/dashboard/fpr/templatetags/revisions_link.py diff --git a/src/dashboard/src/fpr/urls.py b/src/archivematica/dashboard/fpr/urls.py similarity index 99% rename from src/dashboard/src/fpr/urls.py rename to src/archivematica/dashboard/fpr/urls.py index a56494e961..a8ff5186a4 100644 --- a/src/dashboard/src/fpr/urls.py +++ b/src/archivematica/dashboard/fpr/urls.py @@ -1,6 +1,7 @@ from django.urls import path from django.urls import re_path -from fpr import views + +from archivematica.dashboard.fpr import views UUID_REGEX = r"[\w]{8}(-[\w]{4}){3}-[\w]{12}" diff --git a/src/dashboard/src/fpr/utils.py b/src/archivematica/dashboard/fpr/utils.py similarity index 100% rename from src/dashboard/src/fpr/utils.py rename to src/archivematica/dashboard/fpr/utils.py diff --git a/src/dashboard/src/fpr/views.py b/src/archivematica/dashboard/fpr/views.py similarity index 99% rename from src/dashboard/src/fpr/views.py rename to src/archivematica/dashboard/fpr/views.py index 7b15734baa..a421cd2e96 100644 --- a/src/dashboard/src/fpr/views.py +++ b/src/archivematica/dashboard/fpr/views.py @@ -7,9 +7,10 @@ from django.shortcuts import render from django.urls import reverse from django.utils.translation import gettext as _ -from fpr import forms as fprforms -from fpr import models as fprmodels -from fpr import utils + +from archivematica.dashboard.fpr import forms as fprforms +from archivematica.dashboard.fpr import models as fprmodels +from archivematica.dashboard.fpr import utils CLASS_CATEGORY_MAP = { "format": fprmodels.Format, diff --git a/src/dashboard/frontend/.eslintrc b/src/archivematica/dashboard/frontend/.eslintrc similarity index 100% rename from src/dashboard/frontend/.eslintrc rename to src/archivematica/dashboard/frontend/.eslintrc diff --git a/src/dashboard/frontend/.gitignore b/src/archivematica/dashboard/frontend/.gitignore similarity index 100% rename from src/dashboard/frontend/.gitignore rename to src/archivematica/dashboard/frontend/.gitignore diff --git a/src/dashboard/frontend/README.md b/src/archivematica/dashboard/frontend/README.md similarity index 100% rename from src/dashboard/frontend/README.md rename to src/archivematica/dashboard/frontend/README.md diff --git a/src/dashboard/frontend/app/alert/alert.controller.js b/src/archivematica/dashboard/frontend/app/alert/alert.controller.js similarity index 100% rename from src/dashboard/frontend/app/alert/alert.controller.js rename to src/archivematica/dashboard/frontend/app/alert/alert.controller.js diff --git a/src/dashboard/frontend/app/analysis/analysis.controller.js b/src/archivematica/dashboard/frontend/app/analysis/analysis.controller.js similarity index 100% rename from src/dashboard/frontend/app/analysis/analysis.controller.js rename to src/archivematica/dashboard/frontend/app/analysis/analysis.controller.js diff --git a/src/dashboard/frontend/app/analysis/analysis.html b/src/archivematica/dashboard/frontend/app/analysis/analysis.html similarity index 100% rename from src/dashboard/frontend/app/analysis/analysis.html rename to src/archivematica/dashboard/frontend/app/analysis/analysis.html diff --git a/src/dashboard/frontend/app/app.css b/src/archivematica/dashboard/frontend/app/app.css similarity index 100% rename from src/dashboard/frontend/app/app.css rename to src/archivematica/dashboard/frontend/app/app.css diff --git a/src/dashboard/frontend/app/app.js b/src/archivematica/dashboard/frontend/app/app.js similarity index 100% rename from src/dashboard/frontend/app/app.js rename to src/archivematica/dashboard/frontend/app/app.js diff --git a/src/dashboard/frontend/app/archivesspace/archivesspace.controller.js b/src/archivematica/dashboard/frontend/app/archivesspace/archivesspace.controller.js similarity index 100% rename from src/dashboard/frontend/app/archivesspace/archivesspace.controller.js rename to src/archivematica/dashboard/frontend/app/archivesspace/archivesspace.controller.js diff --git a/src/dashboard/frontend/app/archivesspace/form.html b/src/archivematica/dashboard/frontend/app/archivesspace/form.html similarity index 100% rename from src/dashboard/frontend/app/archivesspace/form.html rename to src/archivematica/dashboard/frontend/app/archivesspace/form.html diff --git a/src/dashboard/frontend/app/arrangement/arrangement.controller.js b/src/archivematica/dashboard/frontend/app/arrangement/arrangement.controller.js similarity index 100% rename from src/dashboard/frontend/app/arrangement/arrangement.controller.js rename to src/archivematica/dashboard/frontend/app/arrangement/arrangement.controller.js diff --git a/src/dashboard/frontend/app/arrangement/edit_metadata_form.html b/src/archivematica/dashboard/frontend/app/arrangement/edit_metadata_form.html similarity index 100% rename from src/dashboard/frontend/app/arrangement/edit_metadata_form.html rename to src/archivematica/dashboard/frontend/app/arrangement/edit_metadata_form.html diff --git a/src/dashboard/frontend/app/browse/browse.controller.js b/src/archivematica/dashboard/frontend/app/browse/browse.controller.js similarity index 100% rename from src/dashboard/frontend/app/browse/browse.controller.js rename to src/archivematica/dashboard/frontend/app/browse/browse.controller.js diff --git a/src/dashboard/frontend/app/browser.css b/src/archivematica/dashboard/frontend/app/browser.css similarity index 100% rename from src/dashboard/frontend/app/browser.css rename to src/archivematica/dashboard/frontend/app/browser.css diff --git a/src/dashboard/frontend/app/checklist/checklist.directive.js b/src/archivematica/dashboard/frontend/app/checklist/checklist.directive.js similarity index 100% rename from src/dashboard/frontend/app/checklist/checklist.directive.js rename to src/archivematica/dashboard/frontend/app/checklist/checklist.directive.js diff --git a/src/dashboard/frontend/app/components/version/interpolate-filter.js b/src/archivematica/dashboard/frontend/app/components/version/interpolate-filter.js similarity index 100% rename from src/dashboard/frontend/app/components/version/interpolate-filter.js rename to src/archivematica/dashboard/frontend/app/components/version/interpolate-filter.js diff --git a/src/dashboard/frontend/app/components/version/interpolate-filter_test.js b/src/archivematica/dashboard/frontend/app/components/version/interpolate-filter_test.js similarity index 100% rename from src/dashboard/frontend/app/components/version/interpolate-filter_test.js rename to src/archivematica/dashboard/frontend/app/components/version/interpolate-filter_test.js diff --git a/src/dashboard/frontend/app/components/version/version-directive.js b/src/archivematica/dashboard/frontend/app/components/version/version-directive.js similarity index 100% rename from src/dashboard/frontend/app/components/version/version-directive.js rename to src/archivematica/dashboard/frontend/app/components/version/version-directive.js diff --git a/src/dashboard/frontend/app/components/version/version-directive_test.js b/src/archivematica/dashboard/frontend/app/components/version/version-directive_test.js similarity index 100% rename from src/dashboard/frontend/app/components/version/version-directive_test.js rename to src/archivematica/dashboard/frontend/app/components/version/version-directive_test.js diff --git a/src/dashboard/frontend/app/components/version/version.js b/src/archivematica/dashboard/frontend/app/components/version/version.js similarity index 100% rename from src/dashboard/frontend/app/components/version/version.js rename to src/archivematica/dashboard/frontend/app/components/version/version.js diff --git a/src/dashboard/frontend/app/components/version/version_test.js b/src/archivematica/dashboard/frontend/app/components/version/version_test.js similarity index 100% rename from src/dashboard/frontend/app/components/version/version_test.js rename to src/archivematica/dashboard/frontend/app/components/version/version_test.js diff --git a/src/dashboard/frontend/app/examine_contents/examine_contents.controller.js b/src/archivematica/dashboard/frontend/app/examine_contents/examine_contents.controller.js similarity index 100% rename from src/dashboard/frontend/app/examine_contents/examine_contents.controller.js rename to src/archivematica/dashboard/frontend/app/examine_contents/examine_contents.controller.js diff --git a/src/dashboard/frontend/app/examine_contents/examine_contents.html b/src/archivematica/dashboard/frontend/app/examine_contents/examine_contents.html similarity index 100% rename from src/dashboard/frontend/app/examine_contents/examine_contents.html rename to src/archivematica/dashboard/frontend/app/examine_contents/examine_contents.html diff --git a/src/dashboard/frontend/app/examine_contents/file_info.html b/src/archivematica/dashboard/frontend/app/examine_contents/file_info.html similarity index 100% rename from src/dashboard/frontend/app/examine_contents/file_info.html rename to src/archivematica/dashboard/frontend/app/examine_contents/file_info.html diff --git a/src/dashboard/frontend/app/facet_selector/facet_selector.controller.js b/src/archivematica/dashboard/frontend/app/facet_selector/facet_selector.controller.js similarity index 100% rename from src/dashboard/frontend/app/facet_selector/facet_selector.controller.js rename to src/archivematica/dashboard/frontend/app/facet_selector/facet_selector.controller.js diff --git a/src/dashboard/frontend/app/file_list/file_list.controller.js b/src/archivematica/dashboard/frontend/app/file_list/file_list.controller.js similarity index 100% rename from src/dashboard/frontend/app/file_list/file_list.controller.js rename to src/archivematica/dashboard/frontend/app/file_list/file_list.controller.js diff --git a/src/dashboard/frontend/app/filters/aggregation.filter.js b/src/archivematica/dashboard/frontend/app/filters/aggregation.filter.js similarity index 100% rename from src/dashboard/frontend/app/filters/aggregation.filter.js rename to src/archivematica/dashboard/frontend/app/filters/aggregation.filter.js diff --git a/src/dashboard/frontend/app/filters/facet.filter.js b/src/archivematica/dashboard/frontend/app/filters/facet.filter.js similarity index 100% rename from src/dashboard/frontend/app/filters/facet.filter.js rename to src/archivematica/dashboard/frontend/app/filters/facet.filter.js diff --git a/src/dashboard/frontend/app/fixtures/archivesspace/levels b/src/archivematica/dashboard/frontend/app/fixtures/archivesspace/levels similarity index 100% rename from src/dashboard/frontend/app/fixtures/archivesspace/levels rename to src/archivematica/dashboard/frontend/app/fixtures/archivesspace/levels diff --git a/src/dashboard/frontend/app/front_page/appraisal_tab.html b/src/archivematica/dashboard/frontend/app/front_page/appraisal_tab.html similarity index 100% rename from src/dashboard/frontend/app/front_page/appraisal_tab.html rename to src/archivematica/dashboard/frontend/app/front_page/appraisal_tab.html diff --git a/src/dashboard/frontend/app/front_page/transfer_browser.html b/src/archivematica/dashboard/frontend/app/front_page/transfer_browser.html similarity index 100% rename from src/dashboard/frontend/app/front_page/transfer_browser.html rename to src/archivematica/dashboard/frontend/app/front_page/transfer_browser.html diff --git a/src/dashboard/frontend/app/header/header.controller.js b/src/archivematica/dashboard/frontend/app/header/header.controller.js similarity index 100% rename from src/dashboard/frontend/app/header/header.controller.js rename to src/archivematica/dashboard/frontend/app/header/header.controller.js diff --git a/src/dashboard/frontend/app/locale/extract.pot b/src/archivematica/dashboard/frontend/app/locale/extract.pot similarity index 100% rename from src/dashboard/frontend/app/locale/extract.pot rename to src/archivematica/dashboard/frontend/app/locale/extract.pot diff --git a/src/dashboard/frontend/app/locale/translations.es.po b/src/archivematica/dashboard/frontend/app/locale/translations.es.po similarity index 100% rename from src/dashboard/frontend/app/locale/translations.es.po rename to src/archivematica/dashboard/frontend/app/locale/translations.es.po diff --git a/src/dashboard/frontend/app/locale/translations.fr.po b/src/archivematica/dashboard/frontend/app/locale/translations.fr.po similarity index 100% rename from src/dashboard/frontend/app/locale/translations.fr.po rename to src/archivematica/dashboard/frontend/app/locale/translations.fr.po diff --git a/src/dashboard/frontend/app/locale/translations.ja.po b/src/archivematica/dashboard/frontend/app/locale/translations.ja.po similarity index 100% rename from src/dashboard/frontend/app/locale/translations.ja.po rename to src/archivematica/dashboard/frontend/app/locale/translations.ja.po diff --git a/src/dashboard/frontend/app/locale/translations.json b/src/archivematica/dashboard/frontend/app/locale/translations.json similarity index 100% rename from src/dashboard/frontend/app/locale/translations.json rename to src/archivematica/dashboard/frontend/app/locale/translations.json diff --git a/src/dashboard/frontend/app/locale/translations.pt.po b/src/archivematica/dashboard/frontend/app/locale/translations.pt.po similarity index 100% rename from src/dashboard/frontend/app/locale/translations.pt.po rename to src/archivematica/dashboard/frontend/app/locale/translations.pt.po diff --git a/src/dashboard/frontend/app/locale/translations.pt_BR.po b/src/archivematica/dashboard/frontend/app/locale/translations.pt_BR.po similarity index 100% rename from src/dashboard/frontend/app/locale/translations.pt_BR.po rename to src/archivematica/dashboard/frontend/app/locale/translations.pt_BR.po diff --git a/src/dashboard/frontend/app/locale/translations.sv.po b/src/archivematica/dashboard/frontend/app/locale/translations.sv.po similarity index 100% rename from src/dashboard/frontend/app/locale/translations.sv.po rename to src/archivematica/dashboard/frontend/app/locale/translations.sv.po diff --git a/src/dashboard/frontend/app/media/images/logo_header.png b/src/archivematica/dashboard/frontend/app/media/images/logo_header.png similarity index 100% rename from src/dashboard/frontend/app/media/images/logo_header.png rename to src/archivematica/dashboard/frontend/app/media/images/logo_header.png diff --git a/src/dashboard/frontend/app/preview/preview.controller.js b/src/archivematica/dashboard/frontend/app/preview/preview.controller.js similarity index 100% rename from src/dashboard/frontend/app/preview/preview.controller.js rename to src/archivematica/dashboard/frontend/app/preview/preview.controller.js diff --git a/src/dashboard/frontend/app/preview/preview.html b/src/archivematica/dashboard/frontend/app/preview/preview.html similarity index 100% rename from src/dashboard/frontend/app/preview/preview.html rename to src/archivematica/dashboard/frontend/app/preview/preview.html diff --git a/src/dashboard/frontend/app/report/format.html b/src/archivematica/dashboard/frontend/app/report/format.html similarity index 100% rename from src/dashboard/frontend/app/report/format.html rename to src/archivematica/dashboard/frontend/app/report/format.html diff --git a/src/dashboard/frontend/app/report/report.controller.js b/src/archivematica/dashboard/frontend/app/report/report.controller.js similarity index 100% rename from src/dashboard/frontend/app/report/report.controller.js rename to src/archivematica/dashboard/frontend/app/report/report.controller.js diff --git a/src/dashboard/frontend/app/report/tags.html b/src/archivematica/dashboard/frontend/app/report/tags.html similarity index 100% rename from src/dashboard/frontend/app/report/tags.html rename to src/archivematica/dashboard/frontend/app/report/tags.html diff --git a/src/dashboard/frontend/app/search/search.controller.js b/src/archivematica/dashboard/frontend/app/search/search.controller.js similarity index 100% rename from src/dashboard/frontend/app/search/search.controller.js rename to src/archivematica/dashboard/frontend/app/search/search.controller.js diff --git a/src/dashboard/frontend/app/services/alert.service.js b/src/archivematica/dashboard/frontend/app/services/alert.service.js similarity index 100% rename from src/dashboard/frontend/app/services/alert.service.js rename to src/archivematica/dashboard/frontend/app/services/alert.service.js diff --git a/src/dashboard/frontend/app/services/archivesspace.service.js b/src/archivematica/dashboard/frontend/app/services/archivesspace.service.js similarity index 100% rename from src/dashboard/frontend/app/services/archivesspace.service.js rename to src/archivematica/dashboard/frontend/app/services/archivesspace.service.js diff --git a/src/dashboard/frontend/app/services/browse.service.js b/src/archivematica/dashboard/frontend/app/services/browse.service.js similarity index 100% rename from src/dashboard/frontend/app/services/browse.service.js rename to src/archivematica/dashboard/frontend/app/services/browse.service.js diff --git a/src/dashboard/frontend/app/services/facet.service.js b/src/archivematica/dashboard/frontend/app/services/facet.service.js similarity index 100% rename from src/dashboard/frontend/app/services/facet.service.js rename to src/archivematica/dashboard/frontend/app/services/facet.service.js diff --git a/src/dashboard/frontend/app/services/file.service.js b/src/archivematica/dashboard/frontend/app/services/file.service.js similarity index 100% rename from src/dashboard/frontend/app/services/file.service.js rename to src/archivematica/dashboard/frontend/app/services/file.service.js diff --git a/src/dashboard/frontend/app/services/selected.service.js b/src/archivematica/dashboard/frontend/app/services/selected.service.js similarity index 100% rename from src/dashboard/frontend/app/services/selected.service.js rename to src/archivematica/dashboard/frontend/app/services/selected.service.js diff --git a/src/dashboard/frontend/app/services/sip_arrange.service.js b/src/archivematica/dashboard/frontend/app/services/sip_arrange.service.js similarity index 100% rename from src/dashboard/frontend/app/services/sip_arrange.service.js rename to src/archivematica/dashboard/frontend/app/services/sip_arrange.service.js diff --git a/src/dashboard/frontend/app/services/source_locations.service.js b/src/archivematica/dashboard/frontend/app/services/source_locations.service.js similarity index 100% rename from src/dashboard/frontend/app/services/source_locations.service.js rename to src/archivematica/dashboard/frontend/app/services/source_locations.service.js diff --git a/src/dashboard/frontend/app/services/tag.service.js b/src/archivematica/dashboard/frontend/app/services/tag.service.js similarity index 100% rename from src/dashboard/frontend/app/services/tag.service.js rename to src/archivematica/dashboard/frontend/app/services/tag.service.js diff --git a/src/dashboard/frontend/app/services/transfer.service.js b/src/archivematica/dashboard/frontend/app/services/transfer.service.js similarity index 100% rename from src/dashboard/frontend/app/services/transfer.service.js rename to src/archivematica/dashboard/frontend/app/services/transfer.service.js diff --git a/src/dashboard/frontend/app/services/transfer_browser_transfer.service.js b/src/archivematica/dashboard/frontend/app/services/transfer_browser_transfer.service.js similarity index 100% rename from src/dashboard/frontend/app/services/transfer_browser_transfer.service.js rename to src/archivematica/dashboard/frontend/app/services/transfer_browser_transfer.service.js diff --git a/src/dashboard/frontend/app/tree/tree.controller.js b/src/archivematica/dashboard/frontend/app/tree/tree.controller.js similarity index 100% rename from src/dashboard/frontend/app/tree/tree.controller.js rename to src/archivematica/dashboard/frontend/app/tree/tree.controller.js diff --git a/src/dashboard/frontend/app/tree/tree.directive.js b/src/archivematica/dashboard/frontend/app/tree/tree.directive.js similarity index 100% rename from src/dashboard/frontend/app/tree/tree.directive.js rename to src/archivematica/dashboard/frontend/app/tree/tree.directive.js diff --git a/src/dashboard/frontend/app/ui/minimize-bar.html b/src/archivematica/dashboard/frontend/app/ui/minimize-bar.html similarity index 100% rename from src/dashboard/frontend/app/ui/minimize-bar.html rename to src/archivematica/dashboard/frontend/app/ui/minimize-bar.html diff --git a/src/dashboard/frontend/app/ui/minimize-panel.html b/src/archivematica/dashboard/frontend/app/ui/minimize-panel.html similarity index 100% rename from src/dashboard/frontend/app/ui/minimize-panel.html rename to src/archivematica/dashboard/frontend/app/ui/minimize-panel.html diff --git a/src/dashboard/frontend/app/ui/ui.directive.js b/src/archivematica/dashboard/frontend/app/ui/ui.directive.js similarity index 100% rename from src/dashboard/frontend/app/ui/ui.directive.js rename to src/archivematica/dashboard/frontend/app/ui/ui.directive.js diff --git a/src/dashboard/frontend/app/vendor/angular-charts/angular-charts.js b/src/archivematica/dashboard/frontend/app/vendor/angular-charts/angular-charts.js similarity index 100% rename from src/dashboard/frontend/app/vendor/angular-charts/angular-charts.js rename to src/archivematica/dashboard/frontend/app/vendor/angular-charts/angular-charts.js diff --git a/src/dashboard/frontend/app/visualizations/formats_by_files.html b/src/archivematica/dashboard/frontend/app/visualizations/formats_by_files.html similarity index 100% rename from src/dashboard/frontend/app/visualizations/formats_by_files.html rename to src/archivematica/dashboard/frontend/app/visualizations/formats_by_files.html diff --git a/src/dashboard/frontend/app/visualizations/formats_by_size.html b/src/archivematica/dashboard/frontend/app/visualizations/formats_by_size.html similarity index 100% rename from src/dashboard/frontend/app/visualizations/formats_by_size.html rename to src/archivematica/dashboard/frontend/app/visualizations/formats_by_size.html diff --git a/src/dashboard/frontend/app/visualizations/visualizations.controller.js b/src/archivematica/dashboard/frontend/app/visualizations/visualizations.controller.js similarity index 100% rename from src/dashboard/frontend/app/visualizations/visualizations.controller.js rename to src/archivematica/dashboard/frontend/app/visualizations/visualizations.controller.js diff --git a/src/dashboard/frontend/app/visualizations/visualizations.html b/src/archivematica/dashboard/frontend/app/visualizations/visualizations.html similarity index 100% rename from src/dashboard/frontend/app/visualizations/visualizations.html rename to src/archivematica/dashboard/frontend/app/visualizations/visualizations.html diff --git a/src/dashboard/frontend/package-lock.json b/src/archivematica/dashboard/frontend/package-lock.json similarity index 73% rename from src/dashboard/frontend/package-lock.json rename to src/archivematica/dashboard/frontend/package-lock.json index e69372f035..b3e22f3029 100644 --- a/src/dashboard/frontend/package-lock.json +++ b/src/archivematica/dashboard/frontend/package-lock.json @@ -19,7 +19,7 @@ "angular-ui-bootstrap": "^0.14.3", "angular-ui-validate": "^1.2.2", "archivematica-browse-helpers": "git+https://github.com/artefactual-labs/archivematica-browse-helpers.git", - "base64-helpers": "github:artefactual-labs/base64-helpers#v0.1.2", + "base64-helpers": "github:artefactual-labs/base64-helpers#v0.1.4", "d3": "^3.5.17", "font-awesome": "^4.7.0", "lodash": "^4.17.21", @@ -27,18 +27,19 @@ "restangular": "^1.6.1" }, "devDependencies": { - "@babel/core": "^7.25.8", - "@babel/plugin-transform-modules-commonjs": "^7.25.7", - "@babel/plugin-transform-object-assign": "^7.25.7", - "@babel/plugin-transform-runtime": "^7.25.7", - "@babel/preset-env": "^7.25.8", + "@babel/core": "^7.28.3", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-object-assign": "^7.27.1", + "@babel/plugin-transform-runtime": "^7.28.3", + "@babel/preset-env": "^7.28.3", + "@babel/runtime": "^7.28.3", "angular-gettext-cli": "^1.2.0", - "babel-loader": "^9.2.1", + "babel-loader": "^10.0.0", "css-loader": "^7.1.2", "html-loader": "^5.1.0", "http-server": "^14.1.1", "imports-loader": "^5.0.0", - "jasmine-core": "^5.4.0", + "jasmine-core": "^5.10.0", "karma": "^6.4.4", "karma-chrome-launcher": "^3.2.0", "karma-firefox-launcher": "^2.1.3", @@ -46,11 +47,11 @@ "karma-junit-reporter": "^2.0.1", "karma-webpack": "^5.0.1", "path-browserify": "^1.0.1", - "postcss": "^8.4.47", - "shelljs": "^0.8.5", + "postcss": "^8.5.6", + "shelljs": "^0.10.0", "style-loader": "^4.0.0", - "webpack": "^5.95.0", - "webpack-cli": "^5.1.4" + "webpack": "^5.101.3", + "webpack-cli": "^6.0.1" } }, "node_modules/@ampproject/remapping": { @@ -68,23 +69,24 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", - "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/highlight": "^7.25.7", - "picocolors": "^1.0.0" + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.8.tgz", - "integrity": "sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", "dev": true, "license": "MIT", "engines": { @@ -92,22 +94,22 @@ } }, "node_modules/@babel/core": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.8.tgz", - "integrity": "sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", + "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helpers": "^7.25.7", - "@babel/parser": "^7.25.8", - "@babel/template": "^7.25.7", - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.8", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.3", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -123,15 +125,16 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", - "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.7", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -139,41 +142,27 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.7.tgz", - "integrity": "sha512-4xwU8StnqnlIhhioZf1tqnVWeQ9pvH/ujS8hRfw/WOza+/a+1qv69BWNy+oY231maTCWgKWhfBU7kDpsds6zAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.7.tgz", - "integrity": "sha512-12xfNeKNH7jubQNm7PAkzlLwEmCs1tfuX3UjIw6vP6QXi+leKh6+LyC/+Ed4EIQermwd58wsyh070yjDHFlNGg==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/types": "^7.27.3" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", - "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.7", - "@babel/helper-validator-option": "^7.25.7", + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -183,18 +172,18 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.7.tgz", - "integrity": "sha512-bD4WQhbkx80mAyj/WCm4ZHcF4rDxkoLFO6ph8/5/mQ3z4vAzltQXAmbc7GvVJx5H+lk5Mi5EmbTeox5nMGCsbw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", + "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", - "@babel/helper-member-expression-to-functions": "^7.25.7", - "@babel/helper-optimise-call-expression": "^7.25.7", - "@babel/helper-replace-supers": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", - "@babel/traverse": "^7.25.7", + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.3", "semver": "^6.3.1" }, "engines": { @@ -205,14 +194,14 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.7.tgz", - "integrity": "sha512-byHhumTj/X47wJ6C6eLpK7wW/WBEcnUeb7D0FNc/jFQnQVw7DOso3Zz5u9x/zLrFVkHa89ZGDbkAa1D54NdrCQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", + "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", - "regexpu-core": "^6.1.1", + "@babel/helper-annotate-as-pure": "^7.27.1", + "regexpu-core": "^6.2.0", "semver": "^6.3.1" }, "engines": { @@ -223,61 +212,70 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", + "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "debug": "^4.4.1", "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" + "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.7.tgz", - "integrity": "sha512-O31Ssjd5K6lPbTX9AAYpSKrZmLeagt9uwschJd+Ixo6QiRyfpvgtVQp8qrDR9UNFjZ8+DO34ZkdrN+BnPXemeA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", - "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", - "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.7", - "@babel/helper-simple-access": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -287,22 +285,22 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.7.tgz", - "integrity": "sha512-VAwcwuYhv/AT+Vfr28c9y6SHzTan1ryqrydSTFGjU0uDJHw3uZ+PduI8plCLkRsDnqK2DMEDmwrOQRsK/Ykjng==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.7" + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", - "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, "license": "MIT", "engines": { @@ -310,15 +308,15 @@ } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.7.tgz", - "integrity": "sha512-kRGE89hLnPfcz6fTrlNU+uhgcwv0mBE4Gv3P9Ke9kLVJYpi4AMVVEElXvB5CabrPZW4nCM8P8UyyjrzCM0O2sw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", - "@babel/helper-wrap-function": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -328,15 +326,15 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.7.tgz", - "integrity": "sha512-iy8JhqlUW9PtZkd4pHM96v6BdJ66Ba9yWSE4z0W4TvSZwLBPkyDsiIU3ENe4SmrzRBs76F7rQXTy1lYC49n6Lw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.25.7", - "@babel/helper-optimise-call-expression": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -345,38 +343,24 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", - "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.7.tgz", - "integrity": "sha512-pPbNbchZBkPMD50K0p3JGcFMNLVUCuU/ABybm/PGNj4JiHrpmNyqqCphBk4i19xXtNV0JhldQJJtbSW5aUvbyA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", - "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", "engines": { @@ -384,9 +368,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", - "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "dev": true, "license": "MIT", "engines": { @@ -394,9 +378,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", - "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "license": "MIT", "engines": { @@ -404,58 +388,42 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.7.tgz", - "integrity": "sha512-MA0roW3JF2bD1ptAaJnvcabsVlNQShUaThyJbCDD4bCp8NEgiFvpoqRI2YS22hHlc2thjO/fTg2ShLMC3jygAg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz", + "integrity": "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.25.7", - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", - "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", - "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", + "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz", - "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.8" + "@babel/types": "^7.28.2" }, "bin": { "parser": "bin/babel-parser.js" @@ -465,14 +433,14 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.7.tgz", - "integrity": "sha512-UV9Lg53zyebzD1DwQoT9mzkEKa922LNUp5YkTJ6Uta0RbyXaQNUgcvSt7qIu1PpPzVb6rd10OVNTzkyBGeVmxQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", + "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -482,13 +450,13 @@ } }, "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.7.tgz", - "integrity": "sha512-GDDWeVLNxRIkQTnJn2pDOM1pkCgYdSqPeT1a9vh9yIqu2uzzgw1zcqEb+IJOhy+dTBMlNdThrDIksr2o09qrrQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", + "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -498,13 +466,13 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.7.tgz", - "integrity": "sha512-wxyWg2RYaSUYgmd9MR0FyRGyeOMQE/Uzr1wzd/g5cf5bwi9A4v6HFdDm7y1MgDtod/fLOSTZY6jDgV0xU9d5bA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -514,15 +482,15 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.7.tgz", - "integrity": "sha512-Xwg6tZpLxc4iQjorYsyGMyfJE7nP5MV8t/Ka58BgiA7Jw0fRqQNcANlLfdJ/yvBt9z9LD2We+BEkT7vLqZRWng==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", - "@babel/plugin-transform-optional-chaining": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -532,14 +500,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.7.tgz", - "integrity": "sha512-UVATLMidXrnH+GMUIuxq55nejlj02HP7F5ETyBONzP6G87fPBogG4CH6kxrSrdIuAjdwNO9VzyaYsrZPscWUrw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz", + "integrity": "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -562,13 +530,13 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.7.tgz", - "integrity": "sha512-ZvZQRmME0zfJnDQnVBKYzHxXT7lYBB3Revz1GuS7oLXWMgqUPX4G+DDbT30ICClht9WKV34QVrZhSw6WdklwZQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", + "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -578,13 +546,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.7.tgz", - "integrity": "sha512-AqVo+dguCgmpi/3mYBdu9lkngOBlQ2w2vnNpa6gfiCxQZLzV4ZbhsXitJ2Yblkoe1VQwtHSaNmIaGll/26YWRw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -611,13 +579,13 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.7.tgz", - "integrity": "sha512-EJN2mKxDwfOUCPxMO6MUI58RN3ganiRAG/MS/S3HfB6QFNjroAMelQo/gybyYq97WerCBAZoyrAoW8Tzdq2jWg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -627,15 +595,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.8.tgz", - "integrity": "sha512-9ypqkozyzpG+HxlH4o4gdctalFGIjjdufzo7I2XPda0iBnZ6a+FO0rIEQcdSPXp02CkvGsII1exJhmROPQd5oA==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", + "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-remap-async-to-generator": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -645,15 +613,15 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.7.tgz", - "integrity": "sha512-ZUCjAavsh5CESCmi/xCpX1qcCaAglzs/7tmuvoFnJgA1dM7gQplsguljoTg+Ru8WENpX89cQyAtWoaE0I3X3Pg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", + "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-remap-async-to-generator": "^7.25.7" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -663,13 +631,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.7.tgz", - "integrity": "sha512-xHttvIM9fvqW+0a3tZlYcZYSBpSWzGBFIt/sYG3tcdSzBB8ZeVgz2gBP7Df+sM0N1850jrviYSSeUuc+135dmQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -679,13 +647,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.7.tgz", - "integrity": "sha512-ZEPJSkVZaeTFG/m2PARwLZQ+OG0vFIhPlKHK/JdIMy8DbRJ/htz6LRrTFtdzxi9EHmcwbNPAKDnadpNSIW+Aow==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", + "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -695,14 +663,14 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.7.tgz", - "integrity": "sha512-mhyfEW4gufjIqYFo9krXHJ3ElbFLIze5IDp+wQTxoPd+mwFb1NxatNAwmv8Q8Iuxv7Zc+q8EkiMQwc9IhyGf4g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", + "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -712,14 +680,14 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.25.8.tgz", - "integrity": "sha512-e82gl3TCorath6YLf9xUwFehVvjvfqFhdOo4+0iVIVju+6XOi5XHkqB3P2AXnSwoeTX0HBoXq5gJFtvotJzFnQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz", + "integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-class-features-plugin": "^7.28.3", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -729,18 +697,18 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.7.tgz", - "integrity": "sha512-9j9rnl+YCQY0IGoeipXvnk3niWicIB6kCsWRGLwX241qSXpbA4MKxtp/EdvFxsc4zI5vqfLxzOd0twIJ7I99zg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.3.tgz", + "integrity": "sha512-DoEWC5SuxuARF2KdKmGUq3ghfPMO6ZzR12Dnp5gubwbeWJo4dbNWXJPVlwvh4Zlq6Z7YVvL8VFxeSOJgjsx4Sg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-replace-supers": "^7.25.7", - "@babel/traverse": "^7.25.7", - "globals": "^11.1.0" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -750,14 +718,14 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.7.tgz", - "integrity": "sha512-QIv+imtM+EtNxg/XBKL3hiWjgdLjMOmZ+XzQwSgmBfKbfxUjBzGgVPklUuE55eq5/uVoh8gg3dqlrwR/jw3ZeA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", + "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/template": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/template": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -767,13 +735,14 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.7.tgz", - "integrity": "sha512-xKcfLTlJYUczdaM1+epcdh1UGewJqr9zATgrNHcLBcV2QmfvPPEixo/sK/syql9cEmbr7ulu5HMFG5vbbt/sEA==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", + "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -783,14 +752,14 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.7.tgz", - "integrity": "sha512-kXzXMMRzAtJdDEgQBLF4oaiT6ZCU3oWHgpARnTKDAqPkDJ+bs3NrZb310YYevR5QlRo3Kn7dzzIdHbZm1VzJdQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", + "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -800,13 +769,13 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.7.tgz", - "integrity": "sha512-by+v2CjoL3aMnWDOyCIg+yxU9KXSRa9tN6MbqggH5xvymmr9p4AMjYkNlQy4brMceBnUyHZ9G8RnpvT8wP7Cfg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -816,14 +785,14 @@ } }, "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.7.tgz", - "integrity": "sha512-HvS6JF66xSS5rNKXLqkk7L9c/jZ/cdIVIcoPVrnl8IsVpLggTjXs8OWekbLHs/VtYDDh5WXnQyeE3PPUGm22MA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -833,13 +802,30 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.8.tgz", - "integrity": "sha512-gznWY+mr4ZQL/EWPcbBQUP3BXS5FwZp8RUOw06BaRn8tQLzN4XLIxXejpHN9Qo8x8jjBmAAKp6FoS51AgkSA/A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", + "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -849,14 +835,13 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.7.tgz", - "integrity": "sha512-yjqtpstPfZ0h/y40fAXRv2snciYr0OAoMXY/0ClC7tm4C/nG5NJKmIItlaYlLbIVAWNfrYuy9dq1bE0SbX0PEg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", + "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -866,13 +851,13 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.8.tgz", - "integrity": "sha512-sPtYrduWINTQTW7FtOy99VCTWp4H23UX7vYcut7S4CIMEXU+54zKX9uCoGkLsWXteyaMXzVHgzWbLfQ1w4GZgw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -882,14 +867,14 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.7.tgz", - "integrity": "sha512-n/TaiBGJxYFWvpJDfsxSj9lEEE44BFM1EPGz4KEiTipTgkoFVVcCmzAL3qA7fdQU96dpo4gGf5HBx/KnDvqiHw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -899,15 +884,15 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.7.tgz", - "integrity": "sha512-5MCTNcjCMxQ63Tdu9rxyN6cAWurqfrDZ76qvVPrGYdBxIj+EawuuxTu/+dgJlhK5eRz3v1gLwp6XwS8XaX2NiQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -917,13 +902,13 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.8.tgz", - "integrity": "sha512-4OMNv7eHTmJ2YXs3tvxAfa/I43di+VcF+M4Wt66c88EAED1RoGaf1D64cL5FkRpNL+Vx9Hds84lksWvd/wMIdA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", + "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -933,13 +918,13 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.7.tgz", - "integrity": "sha512-fwzkLrSu2fESR/cm4t6vqd7ebNIopz2QHGtjoU+dswQo/P6lwAG04Q98lliE3jkz/XqnbGFLnUcE0q0CVUf92w==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -949,13 +934,13 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.8.tgz", - "integrity": "sha512-f5W0AhSbbI+yY6VakT04jmxdxz+WsID0neG7+kQZbCOjuyJNdL5Nn4WIBm4hRpKnUcO9lP0eipUhFN12JpoH8g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", + "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -965,13 +950,13 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.7.tgz", - "integrity": "sha512-Std3kXwpXfRV0QtQy5JJcRpkqP8/wG4XL7hSKZmGlxPlDqmpXtEPRmhF7ztnlTCtUN3eXRUJp+sBEZjaIBVYaw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -981,14 +966,14 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.7.tgz", - "integrity": "sha512-CgselSGCGzjQvKzghCvDTxKHP3iooenLpJDO842ehn5D2G5fJB222ptnDwQho0WjEvg7zyoxb9P+wiYxiJX5yA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -998,15 +983,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.7.tgz", - "integrity": "sha512-L9Gcahi0kKFYXvweO6n0wc3ZG1ChpSFdgG+eV1WYZ3/dGbJK7vvk91FgGgak8YwRgrCuihF8tE/Xg07EkL5COg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-simple-access": "^7.25.7" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1016,16 +1000,16 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.7.tgz", - "integrity": "sha512-t9jZIvBmOXJsiuyOwhrIGs8dVcD6jDyg2icw1VL4A/g+FnWyJKwUfSSU2nwJuMV2Zqui856El9u+ElB+j9fV1g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", + "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1035,14 +1019,14 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.7.tgz", - "integrity": "sha512-p88Jg6QqsaPh+EB7I9GJrIqi1Zt4ZBHUQtjw3z1bzEXcLh6GfPqzZJ6G+G1HBGKUNukT58MnKG7EN7zXQBCODw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1052,14 +1036,14 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.7.tgz", - "integrity": "sha512-BtAT9LzCISKG3Dsdw5uso4oV1+v2NlVXIIomKJgQybotJY3OwCwJmkongjHgwGKoZXd0qG5UZ12JUlDQ07W6Ow==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1069,13 +1053,13 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.7.tgz", - "integrity": "sha512-CfCS2jDsbcZaVYxRFo2qtavW8SpdzmBXC2LOI4oO0rP+JSRDxxF3inF4GcPsLgfb5FjkhXG5/yR/lxuRs2pySA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1085,13 +1069,13 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.8.tgz", - "integrity": "sha512-Z7WJJWdQc8yCWgAmjI3hyC+5PXIubH9yRKzkl9ZEG647O9szl9zvmKLzpbItlijBnVhTUf1cpyWBsZ3+2wjWPQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", + "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1101,13 +1085,13 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.8.tgz", - "integrity": "sha512-rm9a5iEFPS4iMIy+/A/PiS0QN0UyjPIeVvbU5EMZFKJZHt8vQnasbpo3T3EFcxzCeYO0BHfc4RqooCZc51J86Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", + "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1117,13 +1101,13 @@ } }, "node_modules/@babel/plugin-transform-object-assign": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.25.7.tgz", - "integrity": "sha512-snTWKDjknsLh7l67henNYebPZ809tYTAunlSkPHu0upP70ehLMCHnozh4Dpq7OD2e7iYxhy560iqP+FlU8c2uQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.27.1.tgz", + "integrity": "sha512-LP6tsnirA6iy13uBKiYgjJsfQrodmlSrpZModtlo1Vk8sOO68gfo7dfA9TGJyEgxTiO7czK4EGZm8FJEZtk4kQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1133,15 +1117,17 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.8.tgz", - "integrity": "sha512-LkUu0O2hnUKHKE7/zYOIjByMa4VRaV2CD/cdGz0AxU9we+VA3kDDggKEzI0Oz1IroG+6gUP6UmWEHBMWZU316g==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.0.tgz", + "integrity": "sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-transform-parameters": "^7.25.7" + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -1151,14 +1137,14 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.7.tgz", - "integrity": "sha512-pWT6UXCEW3u1t2tcAGtE15ornCBvopHj9Bps9D2DsH15APgNVOTwwczGckX+WkAvBmuoYKRCFa4DK+jM8vh5AA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-replace-supers": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1168,13 +1154,13 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.8.tgz", - "integrity": "sha512-EbQYweoMAHOn7iJ9GgZo14ghhb9tTjgOc88xFgYngifx7Z9u580cENCV159M4xDh3q/irbhSjZVpuhpC2gKBbg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", + "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1184,14 +1170,14 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.8.tgz", - "integrity": "sha512-q05Bk7gXOxpTHoQ8RSzGSh/LHVB9JEIkKnk3myAWwZHnYiTGYtbdrYkIsS8Xyh4ltKf7GNUSgzs/6P2bJtBAQg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", + "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1201,13 +1187,13 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.7.tgz", - "integrity": "sha512-FYiTvku63me9+1Nz7TOx4YMtW3tWXzfANZtrzHhUZrz4d47EEtMQhzFoZWESfXuAMMT5mwzD4+y1N8ONAX6lMQ==", + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1217,14 +1203,14 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.7.tgz", - "integrity": "sha512-KY0hh2FluNxMLwOCHbxVOKfdB5sjWG4M183885FmaqWWiGMhRZq4DQRKH6mHdEucbJnyDyYiZNwNG424RymJjA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", + "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1234,15 +1220,15 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.8.tgz", - "integrity": "sha512-8Uh966svuB4V8RHHg0QJOB32QK287NBksJOByoKmHMp1TAobNniNalIkI2i5IPj5+S9NYCG4VIjbEuiSN8r+ow==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", + "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", - "@babel/helper-create-class-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1252,13 +1238,13 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.7.tgz", - "integrity": "sha512-lQEeetGKfFi0wHbt8ClQrUSUMfEeI3MMm74Z73T9/kuz990yYVtfofjf3NuA42Jy3auFOpbjDyCSiIkTs1VIYw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1268,14 +1254,13 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.7.tgz", - "integrity": "sha512-mgDoQCRjrY3XK95UuV60tZlFCQGXEtMg8H+IsW72ldw1ih1jZhzYXbJvghmAEpg5UVhhnCeia1CkGttUvCkiMQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.3.tgz", + "integrity": "sha512-K3/M/a4+ESb5LEldjQb+XSrpY0nF+ZBFlTCbSnKaYAMfD8v33O6PMs4uYnOk19HlcsI8WMu3McdFPTiQHF/1/A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "regenerator-transform": "^0.15.2" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1284,14 +1269,31 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", + "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.7.tgz", - "integrity": "sha512-3OfyfRRqiGeOvIWSagcwUTVk2hXBsr/ww7bLn6TRTuXnexA+Udov2icFOxFX9abaj4l96ooYkcNN1qi2Zvqwng==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1301,17 +1303,17 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.7.tgz", - "integrity": "sha512-Y9p487tyTzB0yDYQOtWnC+9HGOuogtP3/wNpun1xJXEEvI6vip59BSBTsHnekZLqxmPcgsrAKt46HAAb//xGhg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.3.tgz", + "integrity": "sha512-Y6ab1kGqZ0u42Zv/4a7l0l72n9DKP/MKoKWaUSBylrhNZO2prYuqFOLbn5aW5SIFXwSH93yfjbgllL8lxuGKLg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.6", - "babel-plugin-polyfill-regenerator": "^0.6.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", "semver": "^6.3.1" }, "engines": { @@ -1322,13 +1324,13 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.7.tgz", - "integrity": "sha512-uBbxNwimHi5Bv3hUccmOFlUy3ATO6WagTApenHz9KzoIdn0XeACdB12ZJ4cjhuB2WSi80Ez2FWzJnarccriJeA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1338,14 +1340,14 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.7.tgz", - "integrity": "sha512-Mm6aeymI0PBh44xNIv/qvo8nmbkpZze1KvR8MkEqbIREDxoiWTi18Zr2jryfRMwDfVZF9foKh060fWgni44luw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", + "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1355,13 +1357,13 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.7.tgz", - "integrity": "sha512-ZFAeNkpGuLnAQ/NCsXJ6xik7Id+tHuS+NT+ue/2+rn/31zcdnupCdmunOizEaP0JsUmTFSTOPoQY7PkK2pttXw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1371,13 +1373,13 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.7.tgz", - "integrity": "sha512-SI274k0nUsFFmyQupiO7+wKATAmMFf8iFgq2O+vVFXZ0SV9lNfT1NGzBEhjquFmD8I9sqHLguH+gZVN3vww2AA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1387,13 +1389,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.7.tgz", - "integrity": "sha512-OmWmQtTHnO8RSUbL0NTdtpbZHeNTnm68Gj5pA4Y2blFNh+V4iZR68V1qL9cI37J21ZN7AaCnkfdHtLExQPf2uA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1403,13 +1405,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.7.tgz", - "integrity": "sha512-BN87D7KpbdiABA+t3HbVqHzKWUDN3dymLaTnPFAMyc8lV+KN3+YzNhVRNdinaCPA4AUqx7ubXbQ9shRjYBl3SQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1419,14 +1421,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.7.tgz", - "integrity": "sha512-IWfR89zcEPQGB/iB408uGtSPlQd3Jpq11Im86vUgcmSTcoWAiQMCTOa2K2yNNqFJEBVICKhayctee65Ka8OB0w==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", + "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1436,14 +1438,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.7.tgz", - "integrity": "sha512-8JKfg/hiuA3qXnlLx8qtv5HWRbgyFx2hMMtpDDuU2rTckpKkGu4ycK5yYHwuEa16/quXfoxHBIApEsNyMWnt0g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1453,14 +1455,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.7.tgz", - "integrity": "sha512-YRW8o9vzImwmh4Q3Rffd09bH5/hvY0pxg+1H1i0f7APoUeg12G7+HhLj9ZFNIrYkgBXhIijPJ+IXypN0hLTIbw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", + "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1470,79 +1472,81 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.8.tgz", - "integrity": "sha512-58T2yulDHMN8YMUxiLq5YmWUnlDCyY1FsHM+v12VMx+1/FlrUj5tY50iDCpofFQEM8fMYOaY9YRvym2jcjn1Dg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.3.tgz", + "integrity": "sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.8", - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-validator-option": "^7.25.7", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.7", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.7", + "@babel/compat-data": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.25.7", - "@babel/plugin-syntax-import-attributes": "^7.25.7", + "@babel/plugin-syntax-import-assertions": "^7.27.1", + "@babel/plugin-syntax-import-attributes": "^7.27.1", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.25.7", - "@babel/plugin-transform-async-generator-functions": "^7.25.8", - "@babel/plugin-transform-async-to-generator": "^7.25.7", - "@babel/plugin-transform-block-scoped-functions": "^7.25.7", - "@babel/plugin-transform-block-scoping": "^7.25.7", - "@babel/plugin-transform-class-properties": "^7.25.7", - "@babel/plugin-transform-class-static-block": "^7.25.8", - "@babel/plugin-transform-classes": "^7.25.7", - "@babel/plugin-transform-computed-properties": "^7.25.7", - "@babel/plugin-transform-destructuring": "^7.25.7", - "@babel/plugin-transform-dotall-regex": "^7.25.7", - "@babel/plugin-transform-duplicate-keys": "^7.25.7", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.7", - "@babel/plugin-transform-dynamic-import": "^7.25.8", - "@babel/plugin-transform-exponentiation-operator": "^7.25.7", - "@babel/plugin-transform-export-namespace-from": "^7.25.8", - "@babel/plugin-transform-for-of": "^7.25.7", - "@babel/plugin-transform-function-name": "^7.25.7", - "@babel/plugin-transform-json-strings": "^7.25.8", - "@babel/plugin-transform-literals": "^7.25.7", - "@babel/plugin-transform-logical-assignment-operators": "^7.25.8", - "@babel/plugin-transform-member-expression-literals": "^7.25.7", - "@babel/plugin-transform-modules-amd": "^7.25.7", - "@babel/plugin-transform-modules-commonjs": "^7.25.7", - "@babel/plugin-transform-modules-systemjs": "^7.25.7", - "@babel/plugin-transform-modules-umd": "^7.25.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.7", - "@babel/plugin-transform-new-target": "^7.25.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.8", - "@babel/plugin-transform-numeric-separator": "^7.25.8", - "@babel/plugin-transform-object-rest-spread": "^7.25.8", - "@babel/plugin-transform-object-super": "^7.25.7", - "@babel/plugin-transform-optional-catch-binding": "^7.25.8", - "@babel/plugin-transform-optional-chaining": "^7.25.8", - "@babel/plugin-transform-parameters": "^7.25.7", - "@babel/plugin-transform-private-methods": "^7.25.7", - "@babel/plugin-transform-private-property-in-object": "^7.25.8", - "@babel/plugin-transform-property-literals": "^7.25.7", - "@babel/plugin-transform-regenerator": "^7.25.7", - "@babel/plugin-transform-reserved-words": "^7.25.7", - "@babel/plugin-transform-shorthand-properties": "^7.25.7", - "@babel/plugin-transform-spread": "^7.25.7", - "@babel/plugin-transform-sticky-regex": "^7.25.7", - "@babel/plugin-transform-template-literals": "^7.25.7", - "@babel/plugin-transform-typeof-symbol": "^7.25.7", - "@babel/plugin-transform-unicode-escapes": "^7.25.7", - "@babel/plugin-transform-unicode-property-regex": "^7.25.7", - "@babel/plugin-transform-unicode-regex": "^7.25.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.25.7", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.28.0", + "@babel/plugin-transform-async-to-generator": "^7.27.1", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.0", + "@babel/plugin-transform-class-properties": "^7.27.1", + "@babel/plugin-transform-class-static-block": "^7.28.3", + "@babel/plugin-transform-classes": "^7.28.3", + "@babel/plugin-transform-computed-properties": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-dotall-regex": "^7.27.1", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.0", + "@babel/plugin-transform-exponentiation-operator": "^7.27.1", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.27.1", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-modules-systemjs": "^7.27.1", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", + "@babel/plugin-transform-numeric-separator": "^7.27.1", + "@babel/plugin-transform-object-rest-spread": "^7.28.0", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/plugin-transform-private-methods": "^7.27.1", + "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.28.3", + "@babel/plugin-transform-regexp-modifiers": "^7.27.1", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.27.1", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.27.1", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.6", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.38.1", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "core-js-compat": "^3.43.0", "semver": "^6.3.1" }, "engines": { @@ -1568,62 +1572,58 @@ } }, "node_modules/@babel/runtime": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", - "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz", + "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==", "dev": true, "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", - "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", - "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", + "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz", - "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1640,28 +1640,24 @@ } }, "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", + "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": ">=14.17.0" } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -1674,20 +1670,10 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", "dev": true, "license": "MIT", "dependencies": { @@ -1696,16 +1682,16 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1713,6 +1699,44 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@socket.io/component-emitter": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", @@ -1720,27 +1744,42 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/cors": { - "version": "2.8.17", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", - "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" } }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, "license": "MIT" }, @@ -1752,83 +1791,83 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.5.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz", - "integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==", + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", + "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~7.10.0" } }, "node_modules/@webassemblyjs/ast": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", - "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, "license": "MIT", "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, "license": "MIT", "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", - "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, "license": "MIT", "dependencies": { @@ -1836,9 +1875,9 @@ } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1846,122 +1885,122 @@ } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", - "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-opt": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1", - "@webassemblyjs/wast-printer": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", - "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", - "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", - "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", - "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, "node_modules/@webpack-cli/configtest": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", - "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz", + "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==", "dev": true, "license": "MIT", "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" }, "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" } }, "node_modules/@webpack-cli/info": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", - "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz", + "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" }, "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" } }, "node_modules/@webpack-cli/serve": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", - "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz", + "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==", "dev": true, "license": "MIT", "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" }, "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" }, "peerDependenciesMeta": { "webpack-dev-server": { @@ -1998,9 +2037,9 @@ } }, "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", "bin": { @@ -2010,14 +2049,17 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "dev": true, "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, "peerDependencies": { - "acorn": "^8" + "acorn": "^8.14.0" } }, "node_modules/ajv": { @@ -2176,16 +2218,19 @@ } }, "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/anymatch": { @@ -2211,42 +2256,38 @@ } }, "node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.14" - } + "license": "MIT" }, "node_modules/babel-loader": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", - "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-10.0.0.tgz", + "integrity": "sha512-z8jt+EdS61AMw22nSfoNJAZ0vrtmhPRVi6ghL3rCeRZI8cdNYFiV5xeV3HbE7rlZZNmGH8BVccwWt8/ED0QOHA==", "dev": true, "license": "MIT", "dependencies": { - "find-cache-dir": "^4.0.0", - "schema-utils": "^4.0.0" + "find-up": "^5.0.0" }, "engines": { - "node": ">= 14.15.0" + "node": "^18.20.0 || ^20.10.0 || >=22.0.0" }, "peerDependencies": { "@babel/core": "^7.12.0", - "webpack": ">=5" + "webpack": ">=5.61.0" } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", - "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", + "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.2", + "@babel/compat-data": "^7.27.7", + "@babel/helper-define-polyfill-provider": "^0.6.5", "semver": "^6.3.1" }, "peerDependencies": { @@ -2254,27 +2295,27 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", - "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", + "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2", - "core-js-compat": "^3.38.0" + "@babel/helper-define-polyfill-provider": "^0.6.5", + "core-js-compat": "^3.43.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", - "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", + "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2" + "@babel/helper-define-polyfill-provider": "^0.6.5" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -2288,8 +2329,8 @@ "license": "MIT" }, "node_modules/base64-helpers": { - "version": "0.1.2", - "resolved": "git+ssh://git@github.com/artefactual-labs/base64-helpers.git#dc7b82c3d30e30d1a218fde234feddf8619c15db", + "version": "0.1.3", + "resolved": "git+ssh://git@github.com/artefactual-labs/base64-helpers.git#ef8910fa020a732be7a5c01f05a204d956eb0f3e", "license": "AGPL-3.0" }, "node_modules/base64id": { @@ -2398,9 +2439,9 @@ "license": "ISC" }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -2422,9 +2463,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "version": "4.25.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.2.tgz", + "integrity": "sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==", "dev": true, "funding": [ { @@ -2442,10 +2483,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.1" + "caniuse-lite": "^1.0.30001733", + "electron-to-chromium": "^1.5.199", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -2471,18 +2512,29 @@ "node": ">= 0.8" } }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0", "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -2491,10 +2543,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, "node_modules/caniuse-lite": { - "version": "1.0.30001669", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz", - "integrity": "sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==", + "version": "1.0.30001735", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz", + "integrity": "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==", "dev": true, "funding": [ { @@ -2513,41 +2576,43 @@ "license": "CC-BY-4.0" }, "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/cheerio": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", - "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.1.2.tgz", + "integrity": "sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==", "dev": true, "license": "MIT", "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", - "domutils": "^3.1.0", - "encoding-sniffer": "^0.2.0", - "htmlparser2": "^9.1.0", - "parse5": "^7.1.2", - "parse5-htmlparser2-tree-adapter": "^7.0.0", + "domutils": "^3.2.2", + "encoding-sniffer": "^0.2.1", + "htmlparser2": "^10.0.0", + "parse5": "^7.3.0", + "parse5-htmlparser2-tree-adapter": "^7.1.0", "parse5-parser-stream": "^7.1.2", - "undici": "^6.19.5", + "undici": "^7.12.0", "whatwg-mimetype": "^4.0.0" }, "engines": { - "node": ">=18.17" + "node": ">=20.18.1" }, "funding": { "url": "https://github.com/cheeriojs/cheerio?sponsor=1" @@ -2606,6 +2671,19 @@ "node": ">=6.0" } }, + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -2634,19 +2712,22 @@ } }, "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "1.1.3" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "license": "MIT" }, @@ -2657,12 +2738,15 @@ "dev": true, "license": "MIT" }, - "node_modules/common-path-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true, - "license": "ISC" + "license": "MIT", + "engines": { + "node": ">=14" + } }, "node_modules/concat-map": { "version": "0.0.1", @@ -2732,13 +2816,13 @@ } }, "node_modules/core-js-compat": { - "version": "3.38.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", - "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", + "version": "3.45.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.0.tgz", + "integrity": "sha512-gRoVMBawZg0OnxaVv3zpqLLxaHmsubEGyTnqdpI/CEBvX4JadI1dMSHxagThprYRtSVbuQxvi6iUatdPxohHpA==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.23.3" + "browserslist": "^4.25.1" }, "funding": { "type": "opencollective", @@ -2770,9 +2854,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -2837,9 +2921,9 @@ } }, "node_modules/css-loader/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "license": "ISC", "bin": { @@ -2850,9 +2934,9 @@ } }, "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -2867,9 +2951,9 @@ } }, "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -2916,9 +3000,9 @@ } }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2933,24 +3017,6 @@ } } }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -3037,9 +3103,9 @@ } }, "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -3062,25 +3128,19 @@ "tslib": "^2.0.3" } }, - "node_modules/dot-case/node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/dot-case/node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dev": true, "license": "MIT", "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/ee-first": { @@ -3091,9 +3151,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.42", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.42.tgz", - "integrity": "sha512-gIfKavKDw1mhvic9nbzA5lZw8QSHpdMwLwXc0cWidQz9B15pDoDdDH4boIatuFfeoCatb3a/NGL6CYRVFxGZ9g==", + "version": "1.5.203", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.203.tgz", + "integrity": "sha512-uz4i0vLhfm6dLZWbz/iH88KNDV+ivj5+2SA+utpgjKaj9Q0iDLuwk6Idhe9BTxciHudyx6IvTvijhkPvFGUQ0g==", "dev": true, "license": "ISC" }, @@ -3115,9 +3175,9 @@ } }, "node_modules/encoding-sniffer": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", - "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", "dev": true, "license": "MIT", "dependencies": { @@ -3129,13 +3189,12 @@ } }, "node_modules/engine.io": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz", - "integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==", + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", "dev": true, "license": "MIT", "dependencies": { - "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", @@ -3160,28 +3219,49 @@ "node": ">=10.0.0" } }, - "node_modules/enhanced-resolve": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", - "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "ms": "^2.1.3" }, "engines": { - "node": ">=10.13.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" } }, "node_modules/ent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.1.tgz", - "integrity": "sha512-QHuXVeZx9d+tIQAz/XztU0ZwZf2Agg9CcXcgE1rurqvdBeDBrpSwjl8/6XUqMg7tw2Y7uAdKb2sRv+bSEFqQ5A==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.2.tgz", + "integrity": "sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw==", "dev": true, "license": "MIT", "dependencies": { - "punycode": "^1.4.1" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "punycode": "^1.4.1", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -3214,14 +3294,11 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, "engines": { "node": ">= 0.4" } @@ -3237,12 +3314,25 @@ } }, "node_modules/es-module-lexer": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", - "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", "dev": true, "license": "MIT" }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -3260,16 +3350,6 @@ "dev": true, "license": "MIT" }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -3344,6 +3424,30 @@ "node": ">=0.8.x" } }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -3358,19 +3462,39 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } }, "node_modules/fast-uri": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", - "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", "dev": true, - "license": "MIT" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" }, "node_modules/fastest-levenshtein": { "version": "1.0.16", @@ -3382,6 +3506,16 @@ "node": ">= 4.9.1" } }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -3444,35 +3578,18 @@ "node": ">= 0.8" } }, - "node_modules/find-cache-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", - "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "common-path-prefix": "^3.0.0", - "pkg-dir": "^7.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -3489,16 +3606,16 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, "license": "ISC" }, "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "dev": true, "funding": [ { @@ -3547,6 +3664,21 @@ "dev": true, "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3578,17 +3710,22 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -3597,6 +3734,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -3639,24 +3803,14 @@ "dev": true, "license": "BSD-2-Clause" }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3670,32 +3824,19 @@ "license": "ISC" }, "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, "license": "MIT", "engines": { @@ -3705,12 +3846,15 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, "engines": { "node": ">= 0.4" }, @@ -3810,55 +3954,10 @@ "node": "^14.13.1 || >=16.0.0" } }, - "node_modules/html-minifier-terser/node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/html-minifier-terser/node_modules/clean-css": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", - "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 10.0" - } - }, - "node_modules/html-minifier-terser/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/html-minifier-terser/node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, - "license": "MIT", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, "node_modules/htmlparser2": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", - "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", + "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", "dev": true, "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", @@ -3871,8 +3970,21 @@ "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", - "domutils": "^3.1.0", - "entities": "^4.5.0" + "domutils": "^3.2.1", + "entities": "^6.0.0" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/http-errors": { @@ -3945,80 +4057,14 @@ "node": ">=12" } }, - "node_modules/http-server/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/http-server/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/http-server/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/http-server/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/http-server/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/http-server/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, + "license": "Apache-2.0", "engines": { - "node": ">=8" + "node": ">=10.17.0" } }, "node_modules/iconv-lite": { @@ -4067,85 +4113,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-local/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/import-local/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/import-local/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/import-local/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/import-local/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/imports-loader": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/imports-loader/-/imports-loader-5.0.0.tgz", @@ -4187,13 +4154,13 @@ "license": "ISC" }, "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">=10.13.0" } }, "node_modules/is-binary-path": { @@ -4210,9 +4177,9 @@ } }, "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "license": "MIT", "dependencies": { @@ -4297,6 +4264,38 @@ "node": ">=0.10.0" } }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -4341,9 +4340,9 @@ } }, "node_modules/jasmine-core": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.4.0.tgz", - "integrity": "sha512-T4fio3W++llLd7LGSGsioriDHgWyhoL6YTu4k37uwJLF7DzOzspz7mNxRoM3cQdLWtL/ebazQpIf/yZGJx/gzg==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.10.0.tgz", + "integrity": "sha512-MrChbWV5LBo+EaeKwTM1eZ6oYSz1brvFExnRafraEkJkbJ9evbUxABhnIgGQimhpMxhg+BD6QmOvb/e3NXsNdg==", "dev": true, "license": "MIT" }, @@ -4362,16 +4361,6 @@ "node": ">= 10.13.0" } }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/jest-worker/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -4396,9 +4385,9 @@ "license": "MIT" }, "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "license": "MIT", "bin": { @@ -4580,9 +4569,9 @@ } }, "node_modules/karma-webpack/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4639,16 +4628,16 @@ } }, "node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^6.0.0" + "p-locate": "^5.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4684,6 +4673,16 @@ "node": ">=8.0" } }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -4694,6 +4693,16 @@ "yallist": "^3.0.2" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -4711,6 +4720,30 @@ "dev": true, "license": "MIT" }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -4747,6 +4780,16 @@ "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4800,9 +4843,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, "funding": [ { @@ -4835,10 +4878,21 @@ "dev": true, "license": "MIT" }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true, "license": "MIT" }, @@ -4852,6 +4906,19 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -4876,9 +4943,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, "license": "MIT", "engines": { @@ -4911,6 +4978,22 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/opener": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", @@ -4922,32 +5005,32 @@ } }, "node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", "dependencies": { - "yocto-queue": "^1.0.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^4.0.0" + "p-limit": "^3.0.2" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4963,27 +5046,38 @@ "node": ">=6" } }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", "dev": true, "license": "MIT", "dependencies": { - "entities": "^4.4.0" + "entities": "^6.0.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" } }, "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", - "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", "dev": true, "license": "MIT", "dependencies": { - "domhandler": "^5.0.2", + "domhandler": "^5.0.3", "parse5": "^7.0.0" }, "funding": { @@ -5003,6 +5097,19 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -5024,27 +5131,6 @@ "tslib": "^2.0.3" } }, - "node_modules/pascal-case/node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/pascal-case/node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", @@ -5053,13 +5139,13 @@ "license": "MIT" }, "node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=8" } }, "node_modules/path-is-absolute": { @@ -5090,9 +5176,9 @@ "license": "MIT" }, "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, @@ -5110,21 +5196,74 @@ } }, "node_modules/pkg-dir": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", - "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "license": "MIT", "dependencies": { - "find-up": "^6.3.0" + "p-try": "^2.0.0" }, "engines": { - "node": ">=14.16" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/pofile": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pofile/-/pofile-1.0.11.tgz", @@ -5132,34 +5271,23 @@ "dev": true }, "node_modules/portfinder": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", - "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.37.tgz", + "integrity": "sha512-yuGIEjDAYnnOex9ddMnKZEMFE0CcGo6zbfzDklkmT1m5z734ss6JMzN9rNB3+RR7iS+F10D4/BVIaXOyh8PQKw==", "dev": true, "license": "MIT", "dependencies": { - "async": "^2.6.4", - "debug": "^3.2.7", - "mkdirp": "^0.5.6" + "async": "^3.2.6", + "debug": "^4.3.6" }, "engines": { - "node": ">= 0.12.0" - } - }, - "node_modules/portfinder/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" + "node": ">= 10.12" } }, "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, "funding": [ { @@ -5177,8 +5305,8 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.1.0", + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { @@ -5199,14 +5327,14 @@ } }, "node_modules/postcss-modules-local-by-default": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", - "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", "dev": true, "license": "MIT", "dependencies": { "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", + "postcss-selector-parser": "^7.0.0", "postcss-value-parser": "^4.1.0" }, "engines": { @@ -5217,13 +5345,13 @@ } }, "node_modules/postcss-modules-scope": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", - "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", "dev": true, "license": "ISC", "dependencies": { - "postcss-selector-parser": "^6.0.4" + "postcss-selector-parser": "^7.0.0" }, "engines": { "node": "^10 || ^12 || >= 14" @@ -5249,9 +5377,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", "dependencies": { @@ -5302,6 +5430,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -5365,15 +5514,16 @@ } }, "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, + "license": "MIT", "dependencies": { - "resolve": "^1.1.6" + "resolve": "^1.20.0" }, "engines": { - "node": ">= 0.10" + "node": ">= 10.13.0" } }, "node_modules/regenerate": { @@ -5396,34 +5546,17 @@ "node": ">=4" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true, - "license": "MIT" - }, - "node_modules/regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, "node_modules/regexpu-core": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", - "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", "dev": true, "license": "MIT", "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.0", "regjsgen": "^0.8.0", - "regjsparser": "^0.11.0", + "regjsparser": "^0.12.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" }, @@ -5439,9 +5572,9 @@ "license": "MIT" }, "node_modules/regjsparser": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.1.tgz", - "integrity": "sha512-1DHODs4B8p/mQHU9kr+jv8+wIC9mtG4eBHxWxIq5mhjE3D5oORhCc6deRKzTjs9DcfRFmj9BHSDguZklqCGFWQ==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -5451,6 +5584,19 @@ "regjsparser": "bin/parser" } }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", @@ -5489,19 +5635,22 @@ "license": "MIT" }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5544,6 +5693,17 @@ "angular": ">=1.3.12" } }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rfdc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", @@ -5568,6 +5728,30 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -5575,6 +5759,24 @@ "dev": true, "license": "MIT" }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -5583,9 +5785,9 @@ "license": "MIT" }, "node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5595,7 +5797,7 @@ "ajv-keywords": "^5.1.0" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 10.13.0" }, "funding": { "type": "opencollective", @@ -5629,24 +5831,6 @@ "randombytes": "^2.1.0" } }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -5676,49 +5860,102 @@ "dependencies": { "shebang-regex": "^3.0.0" }, - "engines": { - "node": ">=8" + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shelljs": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.10.0.tgz", + "integrity": "sha512-Jex+xw5Mg2qMZL3qnzXIfaxEtBaC4n7xifqaqtrZDdlheR70OGkydrPJWT0V1cA1k3nanC86x9FwAmQl6w3Klw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "execa": "^5.1.1", + "fast-glob": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "dev": true, "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" }, "engines": { - "node": ">=4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -5727,10 +5964,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, "node_modules/socket.io": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.0.tgz", - "integrity": "sha512-8U6BEgGjQOfGz3HHTYaC/L1GaxDCJ/KM0XTkJly0EhZ5U/du9uNEZy4ZgYzEzIqlx2CMm25CrCqr1ck899eLNA==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", "dev": true, "license": "MIT", "dependencies": { @@ -5757,6 +6001,24 @@ "ws": "~8.17.1" } }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", @@ -5771,6 +6033,42 @@ "node": ">=10.0.0" } }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -5865,6 +6163,16 @@ "node": ">=10" } }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/style-loader": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-4.0.0.tgz", @@ -5883,16 +6191,16 @@ } }, "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/supports-preserve-symlinks-flag": { @@ -5909,9 +6217,9 @@ } }, "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", "dev": true, "license": "MIT", "engines": { @@ -5919,14 +6227,14 @@ } }, "node_modules/terser": { - "version": "5.33.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.33.0.tgz", - "integrity": "sha512-JuPVaB7s1gdFKPKTelwUyRq5Sid2A3Gko2S0PncwdBq7kN9Ti9HPWDQ06MPsEDGsZeVESjKEnyGy68quBk1w6g==", + "version": "5.43.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", + "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", + "acorn": "^8.14.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -5938,17 +6246,17 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", + "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" }, "engines": { "node": ">= 10.13.0" @@ -5972,59 +6280,6 @@ } } }, - "node_modules/terser-webpack-plugin/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -6033,25 +6288,15 @@ "license": "MIT" }, "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "dev": true, "license": "MIT", "engines": { "node": ">=14.14" } }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6076,9 +6321,9 @@ } }, "node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, "license": "0BSD" }, @@ -6097,9 +6342,9 @@ } }, "node_modules/ua-parser-js": { - "version": "0.7.39", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.39.tgz", - "integrity": "sha512-IZ6acm6RhQHNibSt7+c09hhvsKy9WUr4DVbeq9U8o71qxyYtJpQeDxQnMrVqnIFMLcQjHO0I9wgfO2vIahht4w==", + "version": "0.7.40", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.40.tgz", + "integrity": "sha512-us1E3K+3jJppDBa3Tl0L3MOJiGhe1C6P0+nIvQAFYbxlMAx0h81eOwLmU57xgqToduDDPx3y5QsdjPfDu+FgOQ==", "dev": true, "funding": [ { @@ -6124,19 +6369,19 @@ } }, "node_modules/undici": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz", - "integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.14.0.tgz", + "integrity": "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=18.17" + "node": ">=20.18.1" } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", "dev": true, "license": "MIT" }, @@ -6217,9 +6462,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "dev": true, "funding": [ { @@ -6238,7 +6483,7 @@ "license": "MIT", "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -6247,26 +6492,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/uri-js/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", @@ -6312,9 +6537,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", - "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", "dev": true, "license": "MIT", "dependencies": { @@ -6326,21 +6551,23 @@ } }, "node_modules/webpack": { - "version": "5.95.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.95.0.tgz", - "integrity": "sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==", + "version": "5.101.3", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.3.tgz", + "integrity": "sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-attributes": "^1.9.5", - "browserslist": "^4.21.10", + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", + "enhanced-resolve": "^5.17.3", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -6350,11 +6577,11 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", + "schema-utils": "^4.3.2", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", + "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" + "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" @@ -6373,43 +6600,40 @@ } }, "node_modules/webpack-cli": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", - "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", + "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", "dev": true, "license": "MIT", "dependencies": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^2.1.1", - "@webpack-cli/info": "^2.0.2", - "@webpack-cli/serve": "^2.0.5", + "@discoveryjs/json-ext": "^0.6.1", + "@webpack-cli/configtest": "^3.0.1", + "@webpack-cli/info": "^3.0.1", + "@webpack-cli/serve": "^3.0.1", "colorette": "^2.0.14", - "commander": "^10.0.1", + "commander": "^12.1.0", "cross-spawn": "^7.0.3", - "envinfo": "^7.7.3", + "envinfo": "^7.14.0", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", "interpret": "^3.1.1", "rechoir": "^0.8.0", - "webpack-merge": "^5.7.3" + "webpack-merge": "^6.0.1" }, "bin": { "webpack-cli": "bin/cli.js" }, "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "5.x.x" + "webpack": "^5.82.0" }, "peerDependenciesMeta": { - "@webpack-cli/generators": { - "optional": true - }, "webpack-bundle-analyzer": { "optional": true }, @@ -6419,51 +6643,28 @@ } }, "node_modules/webpack-cli/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/webpack-cli/node_modules/interpret": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", - "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack-cli/node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, "license": "MIT", - "dependencies": { - "resolve": "^1.20.0" - }, "engines": { - "node": ">= 10.13.0" + "node": ">=18" } }, "node_modules/webpack-cli/node_modules/webpack-merge": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", - "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", "dev": true, "license": "MIT", "dependencies": { "clone-deep": "^4.0.1", "flat": "^5.0.2", - "wildcard": "^2.0.0" + "wildcard": "^2.0.1" }, "engines": { - "node": ">=10.0.0" + "node": ">=18.0.0" } }, "node_modules/webpack-merge": { @@ -6477,68 +6678,15 @@ } }, "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", "dev": true, "license": "MIT", "engines": { "node": ">=10.13.0" } }, - "node_modules/webpack/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/webpack/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/whatwg-encoding": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", @@ -6600,42 +6748,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -6722,13 +6834,13 @@ } }, "node_modules/yocto-queue": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", - "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=12.20" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" diff --git a/src/dashboard/frontend/package.json b/src/archivematica/dashboard/frontend/package.json similarity index 80% rename from src/dashboard/frontend/package.json rename to src/archivematica/dashboard/frontend/package.json index 0c24476fcc..c5a8d34666 100644 --- a/src/dashboard/frontend/package.json +++ b/src/archivematica/dashboard/frontend/package.json @@ -4,18 +4,19 @@ "version": "0.0.0", "description": "Dashboard components (Appraisal tab and Transfer browser) for Archivematica", "devDependencies": { - "@babel/core": "^7.25.8", - "@babel/plugin-transform-modules-commonjs": "^7.25.7", - "@babel/plugin-transform-object-assign": "^7.25.7", - "@babel/plugin-transform-runtime": "^7.25.7", - "@babel/preset-env": "^7.25.8", + "@babel/core": "^7.28.3", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-object-assign": "^7.27.1", + "@babel/plugin-transform-runtime": "^7.28.3", + "@babel/preset-env": "^7.28.3", + "@babel/runtime": "^7.28.3", "angular-gettext-cli": "^1.2.0", - "babel-loader": "^9.2.1", + "babel-loader": "^10.0.0", "css-loader": "^7.1.2", "html-loader": "^5.1.0", "http-server": "^14.1.1", "imports-loader": "^5.0.0", - "jasmine-core": "^5.4.0", + "jasmine-core": "^5.10.0", "karma": "^6.4.4", "karma-chrome-launcher": "^3.2.0", "karma-firefox-launcher": "^2.1.3", @@ -23,11 +24,11 @@ "karma-junit-reporter": "^2.0.1", "karma-webpack": "^5.0.1", "path-browserify": "^1.0.1", - "postcss": "^8.4.47", - "shelljs": "^0.8.5", + "postcss": "^8.5.6", + "shelljs": "^0.10.0", "style-loader": "^4.0.0", - "webpack": "^5.95.0", - "webpack-cli": "^5.1.4" + "webpack": "^5.101.3", + "webpack-cli": "^6.0.1" }, "scripts": { "extract-messages": "angular-gettext-cli --files \"./app/**/*.+(js|html)\" --dest \"./app/locale/extract.pot\" --marker-name \"i18n\"", @@ -50,7 +51,7 @@ "angular-ui-bootstrap": "^0.14.3", "angular-ui-validate": "^1.2.2", "archivematica-browse-helpers": "git+https://github.com/artefactual-labs/archivematica-browse-helpers.git", - "base64-helpers": "github:artefactual-labs/base64-helpers#v0.1.2", + "base64-helpers": "github:artefactual-labs/base64-helpers#v0.1.4", "d3": "^3.5.17", "font-awesome": "^4.7.0", "lodash": "^4.17.21", diff --git a/src/dashboard/frontend/test/karma.conf.js b/src/archivematica/dashboard/frontend/test/karma.conf.js similarity index 100% rename from src/dashboard/frontend/test/karma.conf.js rename to src/archivematica/dashboard/frontend/test/karma.conf.js diff --git a/src/dashboard/frontend/test/tests.webpack.js b/src/archivematica/dashboard/frontend/test/tests.webpack.js similarity index 100% rename from src/dashboard/frontend/test/tests.webpack.js rename to src/archivematica/dashboard/frontend/test/tests.webpack.js diff --git a/src/dashboard/frontend/test/unit/aggregationFiltersSpec.js b/src/archivematica/dashboard/frontend/test/unit/aggregationFiltersSpec.js similarity index 100% rename from src/dashboard/frontend/test/unit/aggregationFiltersSpec.js rename to src/archivematica/dashboard/frontend/test/unit/aggregationFiltersSpec.js diff --git a/src/dashboard/frontend/test/unit/archivesspaceSpec.js b/src/archivematica/dashboard/frontend/test/unit/archivesspaceSpec.js similarity index 100% rename from src/dashboard/frontend/test/unit/archivesspaceSpec.js rename to src/archivematica/dashboard/frontend/test/unit/archivesspaceSpec.js diff --git a/src/dashboard/frontend/test/unit/arrangementSpec.js b/src/archivematica/dashboard/frontend/test/unit/arrangementSpec.js similarity index 100% rename from src/dashboard/frontend/test/unit/arrangementSpec.js rename to src/archivematica/dashboard/frontend/test/unit/arrangementSpec.js diff --git a/src/dashboard/frontend/test/unit/browseSpec.js b/src/archivematica/dashboard/frontend/test/unit/browseSpec.js similarity index 100% rename from src/dashboard/frontend/test/unit/browseSpec.js rename to src/archivematica/dashboard/frontend/test/unit/browseSpec.js diff --git a/src/dashboard/frontend/test/unit/facetSpec.js b/src/archivematica/dashboard/frontend/test/unit/facetSpec.js similarity index 100% rename from src/dashboard/frontend/test/unit/facetSpec.js rename to src/archivematica/dashboard/frontend/test/unit/facetSpec.js diff --git a/src/dashboard/frontend/test/unit/fileSpec.js b/src/archivematica/dashboard/frontend/test/unit/fileSpec.js similarity index 100% rename from src/dashboard/frontend/test/unit/fileSpec.js rename to src/archivematica/dashboard/frontend/test/unit/fileSpec.js diff --git a/src/dashboard/frontend/test/unit/locationsSpec.js b/src/archivematica/dashboard/frontend/test/unit/locationsSpec.js similarity index 100% rename from src/dashboard/frontend/test/unit/locationsSpec.js rename to src/archivematica/dashboard/frontend/test/unit/locationsSpec.js diff --git a/src/dashboard/frontend/test/unit/selectedFilesSpec.js b/src/archivematica/dashboard/frontend/test/unit/selectedFilesSpec.js similarity index 100% rename from src/dashboard/frontend/test/unit/selectedFilesSpec.js rename to src/archivematica/dashboard/frontend/test/unit/selectedFilesSpec.js diff --git a/src/dashboard/frontend/test/unit/sip_arrangeSpec.js b/src/archivematica/dashboard/frontend/test/unit/sip_arrangeSpec.js similarity index 100% rename from src/dashboard/frontend/test/unit/sip_arrangeSpec.js rename to src/archivematica/dashboard/frontend/test/unit/sip_arrangeSpec.js diff --git a/src/dashboard/frontend/test/unit/tagSpec.js b/src/archivematica/dashboard/frontend/test/unit/tagSpec.js similarity index 100% rename from src/dashboard/frontend/test/unit/tagSpec.js rename to src/archivematica/dashboard/frontend/test/unit/tagSpec.js diff --git a/src/dashboard/frontend/test/unit/transferSpec.js b/src/archivematica/dashboard/frontend/test/unit/transferSpec.js similarity index 100% rename from src/dashboard/frontend/test/unit/transferSpec.js rename to src/archivematica/dashboard/frontend/test/unit/transferSpec.js diff --git a/src/dashboard/frontend/test/unit/treeSpec.js b/src/archivematica/dashboard/frontend/test/unit/treeSpec.js similarity index 100% rename from src/dashboard/frontend/test/unit/treeSpec.js rename to src/archivematica/dashboard/frontend/test/unit/treeSpec.js diff --git a/src/dashboard/frontend/test/unit/vendor/jquery.js b/src/archivematica/dashboard/frontend/test/unit/vendor/jquery.js similarity index 100% rename from src/dashboard/frontend/test/unit/vendor/jquery.js rename to src/archivematica/dashboard/frontend/test/unit/vendor/jquery.js diff --git a/src/dashboard/frontend/tools/extract_features.py b/src/archivematica/dashboard/frontend/tools/extract_features.py similarity index 100% rename from src/dashboard/frontend/tools/extract_features.py rename to src/archivematica/dashboard/frontend/tools/extract_features.py diff --git a/src/dashboard/frontend/webpack.config.js b/src/archivematica/dashboard/frontend/webpack.config.js similarity index 96% rename from src/dashboard/frontend/webpack.config.js rename to src/archivematica/dashboard/frontend/webpack.config.js index 9dfb7a177f..13c4e12f3d 100644 --- a/src/dashboard/frontend/webpack.config.js +++ b/src/archivematica/dashboard/frontend/webpack.config.js @@ -4,7 +4,7 @@ module.exports = { mode: 'development', context: __dirname + '/app', output: { - path: __dirname + '/../src/media/js/build', + path: __dirname + '/../media/js/build', filename: 'dashboard.js', }, module: { diff --git a/src/dashboard/install/README.md b/src/archivematica/dashboard/install/README.md similarity index 88% rename from src/dashboard/install/README.md rename to src/archivematica/dashboard/install/README.md index 9245b47563..caa8a53361 100644 --- a/src/dashboard/install/README.md +++ b/src/archivematica/dashboard/install/README.md @@ -137,6 +137,51 @@ variables or in the gunicorn configuration file. - **Type:** `string` - :red_circle: **Mandatory!** +- **`ARCHIVEMATICA_DASHBOARD_DASHBOARD_SESSION_COOKIE_SECURE`**: + - **Description:** determines if session cookies should only be sent over + HTTPS connections. + - **Config file example:** `Dashboard.session_cookie_secure` + - **Type:** `boolean` + - **Default:** `true` + +- **`ARCHIVEMATICA_DASHBOARD_DASHBOARD_SESSION_COOKIE_HTTPONLY`**: + - **Description:** determines if session cookies should be accessible only + via HTTP and not via JavaScript. + - **Config file example:** `Dashboard.session_cookie_httponly` + - **Type:** `boolean` + - **Default:** `true` + +- **`ARCHIVEMATICA_DASHBOARD_DASHBOARD_SESSION_COOKIE_SAMESITE`**: + - **Description:** controls when session cookies are sent with cross-site + requests. Options are "Strict", "Lax", or "None". + - **Config file example:** `Dashboard.session_cookie_samesite` + - **Type:** `string` + - **Default:** `"Strict"` + +- **`ARCHIVEMATICA_DASHBOARD_DASHBOARD_CSRF_COOKIE_SECURE`**: + - **Description:** determines if CSRF cookies should only be sent over HTTPS + connections. + - **Config file example:** `Dashboard.csrf_cookie_secure` + - **Type:** `boolean` + - **Default:** `true` + +- **`ARCHIVEMATICA_DASHBOARD_DASHBOARD_CSRF_COOKIE_HTTPONLY`**: + - **Description:** determines if CSRF cookies should be accessible only via + HTTP and not via JavaScript. **Note**: The default is `false` because the + Dashboard's user interface JavaScript code currently requires access to the + CSRF token. If set to `true`, the token must be retrieved differently, as + described in the [Django documentation][csrf-cookie-httponly]. + - **Config file example:** `Dashboard.csrf_cookie_httponly` + - **Type:** `boolean` + - **Default:** `false` + +- **`ARCHIVEMATICA_DASHBOARD_DASHBOARD_CSRF_COOKIE_SAMESITE`**: + - **Description:** controls when CSRF cookies are sent with cross-site + requests. Options are "Strict", "Lax", or "None". + - **Config file example:** `Dashboard.csrf_cookie_samesite` + - **Type:** `string` + - **Default:** `"Strict"` + - **`ARCHIVEMATICA_DASHBOARD_DASHBOARD_SHARED_DIRECTORY`**: - **Description:** location of the Archivematica Shared Directory. - **Config file example:** `Dashboard.shared_directory` @@ -153,7 +198,7 @@ variables or in the gunicorn configuration file. - **Description:** address of the Elasticsearch server. - **Config file example:** `Dashboard.elasticsearch_server` - **Type:** `string` - - **Default:** `"127.0.0.1:9200"` + - **Default:** `"http://127.0.0.1:9200"` - **`ARCHIVEMATICA_DASHBOARD_DASHBOARD_ELASTICSEARCH_TIMEOUT`**: - **Description:** configures the Elasticsearch client to stop waiting for a @@ -176,8 +221,8 @@ variables or in the gunicorn configuration file. - **`ARCHIVEMATICA_DASHBOARD_DASHBOARD_SEARCH_ENABLED`**: - **Description:** controls what Elasticsearch indexes are enabled: - - When set to `aips`, the Backlog tab, Appraisal tab, and the SIP Arrange - pane in the Ingest tab will not be displayed. + - When set to `aips`, the Backlog tab and Appraisal tab will not be + displayed. - When set to `transfers`, the Archival storage tab will not be displayed. - When set to `false`, all the mentioned parts in the previous cases will not be displayed. @@ -474,9 +519,11 @@ variables or in the gunicorn configuration file. - **Default:** `auto` - **`AM_GUNICORN_CHDIR`**: - - **Description:** Directory to load apps from. See [CHDIR] + - **Description:** Directory to load apps from. See [CHDIR]. If this is + empty, Archivematica will load apps from the top level directory of the + `archivematica.dashboard` package. - **Type:** `string` - - **Default:** `/usr/share/archivematica/dashboard` + - **Default:** `""` - **`AM_GUNICORN_ACCESSLOG`**: - **Description:** Location to write access log to. See [ACCESSLOG] @@ -741,11 +788,61 @@ set. - **Type:** `string` - **Default:** `` +- **`OIDC_OP_SET_ROLES_FROM_CLAIMS`**: + - **Description:** Set user roles from OIDC token claims + - **Type:** `boolean` + - **Default:** `False` + +- **`OIDC_OP_ROLE_CLAIM_PATH`**: + - **Description:** Set OIDC token path for extracting role info + - **Type:** `string` + - **Default:** `'realm_access.roles'` + +- **`OIDC_ROLE_CLAIM_ADMIN`**: + - **Description:** The OIDC role claim value which maps to the Admin role. + - **Type:** `string` + - **Default:** `admin` + +- **`OIDC_ROLE_CLAIM_DEFAULT`**: + - **Description:** The OIDC role claim value which maps to the default role. + - **Type:** `string` + - **Default:** `default` + +- **`OIDC_ACCESS_ATTRIBUTE_MAP`** + - **Description:** Set OIDC token details to extract. This string should be + JSON-decodable. If `OIDC_OP_SET_ROLES_FROM_CLAIMS` is set to `True` then + the entry `"realm_access": "realm_access"` must be included in this setting. + - **Type:** `string` + - **Default:** `{"given_name": "first_name", "family_name": "last_name"}` + - **`OIDC_RP_SIGN_ALGO`**: - **Description:** Algorithm used by the ID provider to sign ID tokens - **Type:** `string` - **Default:** `HS256` +- **`OIDC_USE_PKCE`**: + - **Description:** Controls whether the authentication backend uses PKCE +(Proof Key For Code Exchange) during the authorization code flow. + - **Type:** `boolean` + - **Default:** `false` + +- **`OIDC_CODE_CHALLENGE_METHOD`**: + - **Description:** Sets the method used to generate the PKCE code challenge. +This only has an effect if ``OIDC_USE_PKCE`` is ``True``. + - **Type:** `string` + - **Default:** `S256` + +- **`OIDC_USE_SESSION_REFRESH_MIDDLEWARE`**: + - **Description:** Allows existing sessions to be refreshed when OIDC tokens expire + - **Type:** `boolean` + - **Default:** `false` + +- **`OIDC_RENEW_ID_TOKEN_EXPIRY_SECONDS`**: + - **Description:** Time in seconds before reauthentication is required to + refresh the ID token. Should align with the token lifetime set by your OIDC Provider. + - **Type:** `integer` + - **Default:** `900` + ### CSP variables **CSP support is experimental, please share your feedback!** @@ -806,3 +903,4 @@ provides an example that implements the logging behaviour used in Archivematica [LOGLEVEL]: https://docs.gunicorn.org/en/stable/settings.html#loglevel [PROC-NAME]: https://docs.gunicorn.org/en/stable/settings.html#proc-name [django-csp policy settings]: https://django-csp.readthedocs.io/en/latest/configuration.html#policy-settings +[csrf-cookie-httponly]: https://docs.djangoproject.com/en/4.2/howto/csrf/#acquiring-the-token-if-csrf-use-sessions-or-csrf-cookie-httponly-is-true diff --git a/src/dashboard/install/archivematica-dashboard.service b/src/archivematica/dashboard/install/archivematica-dashboard.service similarity index 65% rename from src/dashboard/install/archivematica-dashboard.service rename to src/archivematica/dashboard/install/archivematica-dashboard.service index c9af72a5c9..a39a7276e8 100644 --- a/src/dashboard/install/archivematica-dashboard.service +++ b/src/archivematica/dashboard/install/archivematica-dashboard.service @@ -7,8 +7,7 @@ PIDFile=/run/archivematica-dashboard_gunicorn.pid User=archivematica Group=archivematica EnvironmentFile=/etc/default/archivematica-dashboard -WorkingDirectory=/usr/share/archivematica/dashboard/ -ExecStart=/usr/share/python/archivematica-dashboard/bin/gunicorn --config /etc/archivematica/dashboard.gunicorn-config.py wsgi:application +ExecStart=/usr/share/archivematica/virtualenvs/archivematica --config /etc/archivematica/dashboard.gunicorn-config.py archivematica.dashboard.wsgi:application ExecReload=/bin/kill -s HUP $MAINPID ExecStop=/bin/kill -s TERM $MAINPID PrivateTmp=true diff --git a/src/dashboard/install/dashboard.conf b/src/archivematica/dashboard/install/dashboard.conf similarity index 100% rename from src/dashboard/install/dashboard.conf rename to src/archivematica/dashboard/install/dashboard.conf diff --git a/src/dashboard/install/dashboard.gunicorn-config.py b/src/archivematica/dashboard/install/dashboard.gunicorn-config.py similarity index 93% rename from src/dashboard/install/dashboard.gunicorn-config.py rename to src/archivematica/dashboard/install/dashboard.gunicorn-config.py index fe71f394cd..31e3b7829e 100644 --- a/src/dashboard/install/dashboard.gunicorn-config.py +++ b/src/archivematica/dashboard/install/dashboard.gunicorn-config.py @@ -1,9 +1,12 @@ # Documentation: http://docs.gunicorn.org/en/stable/configure.html # Example: https://github.com/benoitc/gunicorn/blob/master/examples/example_config.py +import importlib.resources import os import shutil import tempfile +dashboard_path = str(importlib.resources.files("archivematica.dashboard")) + # http://docs.gunicorn.org/en/stable/settings.html#user user = os.environ.get("AM_GUNICORN_USER", "archivematica") @@ -29,7 +32,7 @@ reload_engine = os.environ.get("AM_GUNICORN_RELOAD_ENGINE", "auto") # http://docs.gunicorn.org/en/stable/settings.html#chdir -chdir = os.environ.get("AM_GUNICORN_CHDIR", "/usr/share/archivematica/dashboard") +chdir = os.environ.get("AM_GUNICORN_CHDIR", dashboard_path) # http://docs.gunicorn.org/en/stable/settings.html#accesslog accesslog = os.environ.get("AM_GUNICORN_ACCESSLOG", "/dev/null") diff --git a/src/dashboard/install/dashboard.logging.json b/src/archivematica/dashboard/install/dashboard.logging.json similarity index 89% rename from src/dashboard/install/dashboard.logging.json rename to src/archivematica/dashboard/install/dashboard.logging.json index 50818dbbe1..32d5655a80 100644 --- a/src/dashboard/install/dashboard.logging.json +++ b/src/archivematica/dashboard/install/dashboard.logging.json @@ -22,7 +22,7 @@ }, "logfile": { "backupCount": 5, - "class": "custom_handlers.GroupWriteRotatingFileHandler", + "class": "archivematica.archivematicaCommon.custom_handlers.GroupWriteRotatingFileHandler", "filename": "/var/log/archivematica/dashboard/dashboard.log", "formatter": "detailed", "level": "INFO", @@ -41,7 +41,7 @@ }, "verboselogfile": { "backupCount": 5, - "class": "custom_handlers.GroupWriteRotatingFileHandler", + "class": "archivematica.archivematicaCommon.custom_handlers.GroupWriteRotatingFileHandler", "filename": "/var/log/archivematica/dashboard/dashboard.debug.log", "formatter": "detailed", "level": "DEBUG", diff --git a/src/dashboard/src/installer/management/__init__.py b/src/archivematica/dashboard/installer/__init__.py similarity index 100% rename from src/dashboard/src/installer/management/__init__.py rename to src/archivematica/dashboard/installer/__init__.py diff --git a/src/dashboard/src/installer/forms.py b/src/archivematica/dashboard/installer/forms.py similarity index 98% rename from src/dashboard/src/installer/forms.py rename to src/archivematica/dashboard/installer/forms.py index 48e7091343..11bed1a323 100644 --- a/src/dashboard/src/installer/forms.py +++ b/src/archivematica/dashboard/installer/forms.py @@ -20,7 +20,8 @@ from django.contrib.auth.models import User from django.forms.widgets import TextInput from django.utils.translation import gettext_lazy as _ -from main.models import DashboardSetting + +from archivematica.dashboard.main.models import DashboardSetting site_url_field = forms.CharField( label=_("Site URL"), diff --git a/src/dashboard/src/installer/management/commands/__init__.py b/src/archivematica/dashboard/installer/management/__init__.py similarity index 100% rename from src/dashboard/src/installer/management/commands/__init__.py rename to src/archivematica/dashboard/installer/management/__init__.py diff --git a/src/dashboard/src/main/__init__.py b/src/archivematica/dashboard/installer/management/commands/__init__.py similarity index 100% rename from src/dashboard/src/main/__init__.py rename to src/archivematica/dashboard/installer/management/commands/__init__.py diff --git a/src/dashboard/src/installer/management/commands/install.py b/src/archivematica/dashboard/installer/management/commands/install.py similarity index 89% rename from src/dashboard/src/installer/management/commands/install.py rename to src/archivematica/dashboard/installer/management/commands/install.py index 47ce61222d..7a63105a85 100644 --- a/src/dashboard/src/installer/management/commands/install.py +++ b/src/archivematica/dashboard/installer/management/commands/install.py @@ -14,15 +14,16 @@ # # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . -from components import helpers -from components.administration.forms import StorageSettingsForm from django.core.management.base import BaseCommand from django.core.management.base import CommandError from django.http import QueryDict from django.utils import termcolors -from installer.steps import create_super_user -from installer.steps import setup_pipeline -from installer.steps import setup_pipeline_in_ss + +from archivematica.dashboard.components import helpers +from archivematica.dashboard.components.administration.forms import StorageSettingsForm +from archivematica.dashboard.installer.steps import create_super_user +from archivematica.dashboard.installer.steps import setup_pipeline +from archivematica.dashboard.installer.steps import setup_pipeline_in_ss class Command(BaseCommand): diff --git a/src/dashboard/src/installer/middleware.py b/src/archivematica/dashboard/installer/middleware.py similarity index 97% rename from src/dashboard/src/installer/middleware.py rename to src/archivematica/dashboard/installer/middleware.py index 6fb7fe0fe5..613d61453b 100644 --- a/src/dashboard/src/installer/middleware.py +++ b/src/archivematica/dashboard/installer/middleware.py @@ -16,12 +16,13 @@ # along with Archivematica. If not, see . from re import compile as re_compile -import components.helpers as helpers from django.conf import settings from django.shortcuts import redirect from django.urls import reverse from django.utils.deprecation import MiddlewareMixin +import archivematica.dashboard.components.helpers as helpers + EXEMPT_URLS = None diff --git a/src/dashboard/src/installer/steps.py b/src/archivematica/dashboard/installer/steps.py similarity index 92% rename from src/dashboard/src/installer/steps.py rename to src/archivematica/dashboard/installer/steps.py index 52128cf01f..393b334098 100644 --- a/src/dashboard/src/installer/steps.py +++ b/src/archivematica/dashboard/installer/steps.py @@ -17,15 +17,16 @@ import logging import uuid -import components.helpers as helpers -import storageService as storage_service from django.conf import settings as django_settings from django.contrib.auth import get_user_model -from main.models import Agent -from main.models import DashboardSetting -from main.models import User from tastypie.models import ApiKey +import archivematica.dashboard.components.helpers as helpers +from archivematica.archivematicaCommon import storageService as storage_service +from archivematica.dashboard.main.models import Agent +from archivematica.dashboard.main.models import DashboardSetting +from archivematica.dashboard.main.models import User + logger = logging.getLogger("archivematica.dashboard") diff --git a/src/dashboard/src/installer/urls.py b/src/archivematica/dashboard/installer/urls.py similarity index 94% rename from src/dashboard/src/installer/urls.py rename to src/archivematica/dashboard/installer/urls.py index 62f2bfcd5c..ec640e0f9e 100644 --- a/src/dashboard/src/installer/urls.py +++ b/src/archivematica/dashboard/installer/urls.py @@ -15,7 +15,8 @@ # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . from django.urls import path -from installer import views + +from archivematica.dashboard.installer import views app_name = "installer" urlpatterns = [ diff --git a/src/dashboard/src/installer/views.py b/src/archivematica/dashboard/installer/views.py similarity index 89% rename from src/dashboard/src/installer/views.py rename to src/archivematica/dashboard/installer/views.py index 7254a9586e..7a4086eef2 100644 --- a/src/dashboard/src/installer/views.py +++ b/src/archivematica/dashboard/installer/views.py @@ -14,8 +14,6 @@ # # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . -from components import helpers -from components.administration.forms import StorageSettingsForm from django.contrib import messages from django.contrib.auth import authenticate from django.contrib.auth import login @@ -23,12 +21,15 @@ from django.shortcuts import redirect from django.shortcuts import render from django.utils.translation import gettext as _ -from installer.forms import OrganizationForm -from installer.forms import SuperUserCreationForm -from installer.steps import setup_pipeline -from installer.steps import setup_pipeline_in_ss from tastypie.models import ApiKey +from archivematica.dashboard.components import helpers +from archivematica.dashboard.components.administration.forms import StorageSettingsForm +from archivematica.dashboard.installer.forms import OrganizationForm +from archivematica.dashboard.installer.forms import SuperUserCreationForm +from archivematica.dashboard.installer.steps import setup_pipeline +from archivematica.dashboard.installer.steps import setup_pipeline_in_ss + def welcome(request): # This form will be only accessible when there is no uuid diff --git a/src/dashboard/src/locale/en/LC_MESSAGES/django.po b/src/archivematica/dashboard/locale/en/LC_MESSAGES/django.po similarity index 100% rename from src/dashboard/src/locale/en/LC_MESSAGES/django.po rename to src/archivematica/dashboard/locale/en/LC_MESSAGES/django.po diff --git a/src/dashboard/src/locale/en/LC_MESSAGES/djangojs.po b/src/archivematica/dashboard/locale/en/LC_MESSAGES/djangojs.po similarity index 100% rename from src/dashboard/src/locale/en/LC_MESSAGES/djangojs.po rename to src/archivematica/dashboard/locale/en/LC_MESSAGES/djangojs.po diff --git a/src/dashboard/src/locale/es/LC_MESSAGES/django.po b/src/archivematica/dashboard/locale/es/LC_MESSAGES/django.po similarity index 100% rename from src/dashboard/src/locale/es/LC_MESSAGES/django.po rename to src/archivematica/dashboard/locale/es/LC_MESSAGES/django.po diff --git a/src/dashboard/src/locale/es/LC_MESSAGES/djangojs.po b/src/archivematica/dashboard/locale/es/LC_MESSAGES/djangojs.po similarity index 100% rename from src/dashboard/src/locale/es/LC_MESSAGES/djangojs.po rename to src/archivematica/dashboard/locale/es/LC_MESSAGES/djangojs.po diff --git a/src/dashboard/src/locale/fr/LC_MESSAGES/django.po b/src/archivematica/dashboard/locale/fr/LC_MESSAGES/django.po similarity index 100% rename from src/dashboard/src/locale/fr/LC_MESSAGES/django.po rename to src/archivematica/dashboard/locale/fr/LC_MESSAGES/django.po diff --git a/src/dashboard/src/locale/fr/LC_MESSAGES/djangojs.po b/src/archivematica/dashboard/locale/fr/LC_MESSAGES/djangojs.po similarity index 100% rename from src/dashboard/src/locale/fr/LC_MESSAGES/djangojs.po rename to src/archivematica/dashboard/locale/fr/LC_MESSAGES/djangojs.po diff --git a/src/dashboard/src/locale/ja/LC_MESSAGES/django.po b/src/archivematica/dashboard/locale/ja/LC_MESSAGES/django.po similarity index 100% rename from src/dashboard/src/locale/ja/LC_MESSAGES/django.po rename to src/archivematica/dashboard/locale/ja/LC_MESSAGES/django.po diff --git a/src/dashboard/src/locale/ja/LC_MESSAGES/djangojs.po b/src/archivematica/dashboard/locale/ja/LC_MESSAGES/djangojs.po similarity index 100% rename from src/dashboard/src/locale/ja/LC_MESSAGES/djangojs.po rename to src/archivematica/dashboard/locale/ja/LC_MESSAGES/djangojs.po diff --git a/src/dashboard/src/locale/no/LC_MESSAGES/django.po b/src/archivematica/dashboard/locale/no/LC_MESSAGES/django.po similarity index 100% rename from src/dashboard/src/locale/no/LC_MESSAGES/django.po rename to src/archivematica/dashboard/locale/no/LC_MESSAGES/django.po diff --git a/src/dashboard/src/locale/no/LC_MESSAGES/djangojs.po b/src/archivematica/dashboard/locale/no/LC_MESSAGES/djangojs.po similarity index 100% rename from src/dashboard/src/locale/no/LC_MESSAGES/djangojs.po rename to src/archivematica/dashboard/locale/no/LC_MESSAGES/djangojs.po diff --git a/src/dashboard/src/locale/pt/LC_MESSAGES/django.po b/src/archivematica/dashboard/locale/pt/LC_MESSAGES/django.po similarity index 100% rename from src/dashboard/src/locale/pt/LC_MESSAGES/django.po rename to src/archivematica/dashboard/locale/pt/LC_MESSAGES/django.po diff --git a/src/dashboard/src/locale/pt/LC_MESSAGES/djangojs.po b/src/archivematica/dashboard/locale/pt/LC_MESSAGES/djangojs.po similarity index 100% rename from src/dashboard/src/locale/pt/LC_MESSAGES/djangojs.po rename to src/archivematica/dashboard/locale/pt/LC_MESSAGES/djangojs.po diff --git a/src/dashboard/src/locale/pt_BR/LC_MESSAGES/django.po b/src/archivematica/dashboard/locale/pt_BR/LC_MESSAGES/django.po similarity index 100% rename from src/dashboard/src/locale/pt_BR/LC_MESSAGES/django.po rename to src/archivematica/dashboard/locale/pt_BR/LC_MESSAGES/django.po diff --git a/src/dashboard/src/locale/pt_BR/LC_MESSAGES/djangojs.po b/src/archivematica/dashboard/locale/pt_BR/LC_MESSAGES/djangojs.po similarity index 100% rename from src/dashboard/src/locale/pt_BR/LC_MESSAGES/djangojs.po rename to src/archivematica/dashboard/locale/pt_BR/LC_MESSAGES/djangojs.po diff --git a/src/dashboard/src/locale/sv/LC_MESSAGES/django.po b/src/archivematica/dashboard/locale/sv/LC_MESSAGES/django.po similarity index 100% rename from src/dashboard/src/locale/sv/LC_MESSAGES/django.po rename to src/archivematica/dashboard/locale/sv/LC_MESSAGES/django.po diff --git a/src/dashboard/src/locale/sv/LC_MESSAGES/djangojs.po b/src/archivematica/dashboard/locale/sv/LC_MESSAGES/djangojs.po similarity index 100% rename from src/dashboard/src/locale/sv/LC_MESSAGES/djangojs.po rename to src/archivematica/dashboard/locale/sv/LC_MESSAGES/djangojs.po diff --git a/src/dashboard/src/main/management/__init__.py b/src/archivematica/dashboard/main/__init__.py similarity index 100% rename from src/dashboard/src/main/management/__init__.py rename to src/archivematica/dashboard/main/__init__.py diff --git a/src/dashboard/src/main/apps.py b/src/archivematica/dashboard/main/apps.py similarity index 57% rename from src/dashboard/src/main/apps.py rename to src/archivematica/dashboard/main/apps.py index f28d2e21c8..409559d235 100644 --- a/src/dashboard/src/main/apps.py +++ b/src/archivematica/dashboard/main/apps.py @@ -3,7 +3,7 @@ class MainAppConfig(AppConfig): default_auto_field = "django.db.models.AutoField" - name = "main" + name = "archivematica.dashboard.main" def ready(self): - import main.signals # noqa: F401 + import archivematica.dashboard.main.signals # noqa: F401 diff --git a/src/dashboard/src/main/context_processors.py b/src/archivematica/dashboard/main/context_processors.py similarity index 100% rename from src/dashboard/src/main/context_processors.py rename to src/archivematica/dashboard/main/context_processors.py diff --git a/src/dashboard/src/main/forms.py b/src/archivematica/dashboard/main/forms.py similarity index 95% rename from src/dashboard/src/main/forms.py rename to src/archivematica/dashboard/main/forms.py index 7423a5f6f4..1066f79685 100644 --- a/src/dashboard/src/main/forms.py +++ b/src/archivematica/dashboard/main/forms.py @@ -16,7 +16,8 @@ # along with Archivematica. If not, see . from django import forms from django.conf import settings -from main import models + +from archivematica.dashboard.main import models class EventDetailForm(forms.ModelForm): diff --git a/src/dashboard/src/main/migrations/__init__.py b/src/archivematica/dashboard/main/management/__init__.py similarity index 100% rename from src/dashboard/src/main/migrations/__init__.py rename to src/archivematica/dashboard/main/management/__init__.py diff --git a/src/dashboard/src/main/management/commands/__init__.py b/src/archivematica/dashboard/main/management/commands/__init__.py similarity index 68% rename from src/dashboard/src/main/management/commands/__init__.py rename to src/archivematica/dashboard/main/management/commands/__init__.py index a4d8ed54cf..2c4cc4f6c9 100644 --- a/src/dashboard/src/main/management/commands/__init__.py +++ b/src/archivematica/dashboard/main/management/commands/__init__.py @@ -1,10 +1,12 @@ import time -import elasticSearchFunctions as es from django.conf import settings as django_settings from django.core.management.base import BaseCommand from django.core.management.base import CommandError -from elasticsearch import ElasticsearchException + +import archivematica.search.constants +from archivematica.search.service import SearchServiceError +from archivematica.search.service import setup_search_service_from_conf class DashboardCommand(BaseCommand): @@ -39,7 +41,7 @@ def setup_es_for_aip_reindexing(cmd, delete_all=False): :returns: ES client. """ - if es.AIPS_INDEX not in django_settings.SEARCH_ENABLED: + if archivematica.search.constants.AIPS_INDEX not in django_settings.SEARCH_ENABLED: raise CommandError( "The AIPs indexes are not enabled. Please, make sure to " "set the *_SEARCH_ENABLED environment variables to `true` " @@ -48,16 +50,18 @@ def setup_es_for_aip_reindexing(cmd, delete_all=False): ) try: - es.setup_reading_from_conf(django_settings) - es_client = es.get_client() - except ElasticsearchException as err: - raise CommandError(f"Unable to connect to Elasticsearch: {err}") + search_service = setup_search_service_from_conf(django_settings) + except SearchServiceError as err: + raise CommandError(f"Unable to connect to the search service: {err}") if delete_all: cmd.info("Deleting all AIPs in the 'aips' and 'aipfiles' indices") time.sleep(3) # Time for the user to panic and kill the process. - indices = [es.AIPS_INDEX, es.AIP_FILES_INDEX] - es_client.indices.delete(",".join(indices), ignore=404) - es.create_indexes_if_needed(es_client, indices) + indices = [ + archivematica.search.constants.AIPS_INDEX, + archivematica.search.constants.AIP_FILES_INDEX, + ] + search_service.delete_indexes(indices) + search_service.ensure_indexes_exist(indices) - return es_client + return search_service diff --git a/src/dashboard/src/main/management/commands/purge_transient_processing_data.py b/src/archivematica/dashboard/main/management/commands/purge_transient_processing_data.py similarity index 86% rename from src/dashboard/src/main/management/commands/purge_transient_processing_data.py rename to src/archivematica/dashboard/main/management/commands/purge_transient_processing_data.py index 4e1be20e3a..ac840a16eb 100644 --- a/src/dashboard/src/main/management/commands/purge_transient_processing_data.py +++ b/src/archivematica/dashboard/main/management/commands/purge_transient_processing_data.py @@ -34,14 +34,16 @@ import logging import traceback -import elasticSearchFunctions as es from django.conf import settings as django_settings from django.core.management.base import CommandError from django.utils import timezone from django.utils.dateparse import parse_duration -from elasticsearch import ElasticsearchException -from main import models -from main.management.commands import DashboardCommand + +import archivematica.search.constants +from archivematica.dashboard.main import models +from archivematica.dashboard.main.management.commands import DashboardCommand +from archivematica.search.service import SearchServiceError +from archivematica.search.service import setup_search_service_from_conf class Command(DashboardCommand): @@ -87,15 +89,15 @@ def add_arguments(self, parser): ) def handle(self, *args, **options): + search_service = None if not options["keep_searches"] and django_settings.SEARCH_ENABLED: # Ignore elasticsearch-py logging events unless they're errors. logging.getLogger("elasticsearch").setLevel(logging.ERROR) logging.getLogger("archivematica.common").setLevel(logging.ERROR) try: - es.setup_reading_from_conf(django_settings) - es_client = es.get_client() - except ElasticsearchException as err: - raise CommandError(f"Unable to connect to Elasticsearch: {err}") + search_service = setup_search_service_from_conf(django_settings) + except SearchServiceError as err: + raise CommandError(f"Unable to connect to the search service: {err}") # Build look-up options. kwargs = { @@ -137,12 +139,13 @@ def handle(self, *args, **options): ) if ( not options["keep_searches"] - and es.AIPS_INDEX in django_settings.SEARCH_ENABLED + and archivematica.search.constants.AIPS_INDEX + in django_settings.SEARCH_ENABLED ): if not options["quiet"]: self.info(" Purging search documents...") - es.delete_aip(es_client, package_id) - es.delete_aip_files(es_client, package_id) + search_service.delete_aip(package_id) + search_service.delete_aip_files(package_id) except Exception as err: self.error(f" Error: {err}") self.stdout.write(traceback.print_exc()) @@ -176,12 +179,14 @@ def handle(self, *args, **options): ) if ( not options["keep_searches"] - and es.TRANSFERS_INDEX in django_settings.SEARCH_ENABLED + and archivematica.search.constants.TRANSFERS_INDEX + in django_settings.SEARCH_ENABLED ): if not options["quiet"]: self.info(" Purging search documents...") - es.remove_backlog_transfer(es_client, package_id) - es.remove_backlog_transfer_files(es_client, package_id) + search_service.delete_transfer(str(package_id)) + transfer_ids = {str(package_id)} + search_service.delete_transfer_files(transfer_ids) except Exception as err: self.error(f" Error: {err}") self.stdout.write(traceback.format_exc()) diff --git a/src/dashboard/src/main/management/commands/rebuild_aip_index_from_storage_service.py b/src/archivematica/dashboard/main/management/commands/rebuild_aip_index_from_storage_service.py similarity index 79% rename from src/dashboard/src/main/management/commands/rebuild_aip_index_from_storage_service.py rename to src/archivematica/dashboard/main/management/commands/rebuild_aip_index_from_storage_service.py index f7c06aecdb..701e7bf9cb 100644 --- a/src/dashboard/src/main/management/commands/rebuild_aip_index_from_storage_service.py +++ b/src/archivematica/dashboard/main/management/commands/rebuild_aip_index_from_storage_service.py @@ -38,14 +38,18 @@ import shutil import sys import tempfile +import time -import archivematicaFunctions as am -import elasticSearchFunctions as es -import storageService -from elasticsearch import ElasticsearchException from lxml import etree -from main.management.commands import DashboardCommand -from main.management.commands import setup_es_for_aip_reindexing + +from archivematica.archivematicaCommon import archivematicaFunctions as am +from archivematica.archivematicaCommon import storageService +from archivematica.archivematicaCommon import version +from archivematica.archivematicaCommon.aip_mets_parser import AIPMETSParser +from archivematica.archivematicaCommon.databaseFunctions import get_sip_identifiers +from archivematica.dashboard.main.management.commands import DashboardCommand +from archivematica.dashboard.main.management.commands import setup_es_for_aip_reindexing +from archivematica.search.service import SearchServiceError PACKAGE_TYPES_TO_INDEX = ("AIP", "AIC") @@ -82,6 +86,10 @@ def get_aips_in_aic(mets_root, temp_dir, uuid): class Command(DashboardCommand): help = __doc__ + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.dashboard_uuid = am.get_dashboard_uuid() or "" + def add_arguments(self, parser): """Entry point to add custom arguments.""" parser.add_argument( @@ -106,7 +114,7 @@ def add_arguments(self, parser): parser.add_argument( "--pipeline", help="Pipeline UUID to use when filtering packages", - default=am.get_dashboard_uuid(), + default=self.dashboard_uuid, ) def handle(self, *args, **options): @@ -145,8 +153,9 @@ def handle(self, *args, **options): self.error("No AIPs found to index. Quitting.") sys.exit(1) - # Setup es_client and delete indices if required. - es_client = setup_es_for_aip_reindexing(self, delete_all) + # Setup search service and delete indices if required. + search_service = setup_es_for_aip_reindexing(self, delete_all) + self.info("Rebuilding 'aips' and 'aipfiles' indices") # Index packages. @@ -157,7 +166,11 @@ def handle(self, *args, **options): if aip["package_type"] == "AIC": is_aic = True index_success = self.process_package( - es_client, aip, temp_dir, delete_before_reindexing, is_aic=is_aic + aip, + temp_dir, + search_service, + delete_before_reindexing, + is_aic=is_aic, ) if index_success: aip_indexed_count += 1 @@ -185,14 +198,19 @@ def handle(self, *args, **options): ) def process_package( - self, es_client, package_info, temp_dir, delete_before_reindexing, is_aic=False + self, + package_info, + temp_dir, + search_service, + delete_before_reindexing, + is_aic=False, ): """Index package in 'aips' and 'aipfiles' indices. - :param es_client: Elasticsearch client. :param package_info: Package info dict returned by Storage Service. :param temp_dir: Path to tempdir for downloaded METS files. + :param search_service: Search service instance. :param delete_before_reindexing: Boolean of whether to delete package from indices prior to reindexing. :is_aic: Optional boolean to indicate if package being indexed @@ -231,26 +249,43 @@ def process_package( if delete_before_reindexing: self.info(f"Deleting package {uuid} from 'aips' and 'aipfiles' indices.") - es.delete_aip(es_client, uuid) - es.delete_aip_files(es_client, uuid) + search_service.delete_aip(uuid) + search_service.delete_aip_files(uuid) # Index the AIP and then immediately delete the METS file. try: - es.index_aip_and_files( - client=es_client, + # Stop if METS file is not at staging path. + if not os.path.exists(mets_download_path): + error_message = "METS file does not exist at: " + mets_download_path + self.error(f"Error indexing package {uuid}. Details: {error_message}") + os.remove(mets_download_path) + return False + + parser = AIPMETSParser(mets_download_path) + + am_version = version.get_version() + indexed_at = time.time() + self.info(f"AIP UUID: {uuid}") + self.info("Indexing AIP files ...") + search_service.index_aip( uuid=uuid, aip_stored_path=package_info["current_full_path"], - mets_staging_path=mets_download_path, + parser=parser, name=package_name, aip_size=package_info["size"], + am_version=am_version, + indexed_at=indexed_at, + identifiers=get_sip_identifiers(uuid), aips_in_aic=aips_in_aic, encrypted=package_info.get("encrypted", False), location=location_description, + dashboard_uuid=self.dashboard_uuid, ) + self.info("Done.") self.info(f"Successfully indexed package {uuid}") os.remove(mets_download_path) return True - except (ElasticsearchException, etree.XMLSyntaxError) as err: + except (SearchServiceError, etree.XMLSyntaxError) as err: self.error(f"Error indexing package {uuid}. Details: {err}") os.remove(mets_download_path) return False diff --git a/src/dashboard/src/main/management/commands/rebuild_elasticsearch_aip_index_from_files.py b/src/archivematica/dashboard/main/management/commands/rebuild_elasticsearch_aip_index_from_files.py similarity index 85% rename from src/dashboard/src/main/management/commands/rebuild_elasticsearch_aip_index_from_files.py rename to src/archivematica/dashboard/main/management/commands/rebuild_elasticsearch_aip_index_from_files.py index 01352d3b19..089f9f7d09 100644 --- a/src/dashboard/src/main/management/commands/rebuild_elasticsearch_aip_index_from_files.py +++ b/src/archivematica/dashboard/main/management/commands/rebuild_elasticsearch_aip_index_from_files.py @@ -39,15 +39,19 @@ import subprocess import sys import tempfile +import time -import archivematicaFunctions as am -import elasticSearchFunctions -import namespaces as ns -import storageService as storage_service from django.core.management.base import CommandError from lxml import etree -from main.management.commands import DashboardCommand -from main.management.commands import setup_es_for_aip_reindexing + +from archivematica.archivematicaCommon import archivematicaFunctions as am +from archivematica.archivematicaCommon import namespaces as ns +from archivematica.archivematicaCommon import storageService as storage_service +from archivematica.archivematicaCommon import version +from archivematica.archivematicaCommon.aip_mets_parser import AIPMETSParser +from archivematica.archivematicaCommon.databaseFunctions import get_sip_identifiers +from archivematica.dashboard.main.management.commands import DashboardCommand +from archivematica.dashboard.main.management.commands import setup_es_for_aip_reindexing def extract_file(archive_path, destination_dir, relative_path): @@ -102,7 +106,9 @@ def get_aips_in_aic(mets_root, temp_dir, archive_path): return aips_in_aic -def processAIPThenDeleteMETSFile(path, temp_dir, es_client, delete_existing_data=False): +def processAIPThenDeleteMETSFile( + path, temp_dir, search_service, delete_existing_data=False +): archive_file = os.path.basename(path) # Regex match the UUID - AIP might end with .7z, .tar.bz2, or @@ -119,8 +125,8 @@ def processAIPThenDeleteMETSFile(path, temp_dir, es_client, delete_existing_data if delete_existing_data is True: print("Deleting AIP", aip_uuid, "from aips/aip and aips/aipfile.") - elasticSearchFunctions.delete_aip(es_client, aip_uuid) - elasticSearchFunctions.delete_aip_files(es_client, aip_uuid) + search_service.delete_aip(aip_uuid) + search_service.delete_aip_files(aip_uuid) # AIP filenames are - # Index of match end is right before the extension @@ -159,17 +165,33 @@ def processAIPThenDeleteMETSFile(path, temp_dir, es_client, delete_existing_data aip_location ) - return elasticSearchFunctions.index_aip_and_files( - client=es_client, + # Stop if METS file is not at staging path. + if not os.path.exists(path_to_mets): + error_message = "METS file does not exist at: " + path_to_mets + print(error_message, file=sys.stderr) + return 1 + + parser = AIPMETSParser(path_to_mets) + + am_version = version.get_version() + indexed_at = time.time() + print("AIP UUID: " + aip_uuid) + print("Indexing AIP files ...") + ret = search_service.index_aip( uuid=aip_uuid, aip_stored_path=path, - mets_staging_path=path_to_mets, + parser=parser, name=aip_name, aip_size=aip_info[0]["size"], + am_version=am_version, + indexed_at=indexed_at, + identifiers=get_sip_identifiers(aip_uuid), aips_in_aic=aips_in_aic, - identifiers=[], # TODO get these location=location_description, ) + if ret == 0: + print("Done.") + return ret def is_hex(string): @@ -234,8 +256,8 @@ def handle(self, *args, **options): "Arguments --delete-only and --delete-all are mutually exclusive." ) - # Setup es_client and delete indices if required. - es_client = setup_es_for_aip_reindexing(self, options["delete_all"]) + # Setup search service and delete indices if required. + search_service = setup_es_for_aip_reindexing(self, options["delete_all"]) # If delete-only option is used, just delete the AIP data, # do not need to index thereafter. @@ -247,8 +269,8 @@ def handle(self, *args, **options): options["uuid"] ) ) - elasticSearchFunctions.delete_aip(es_client, options["uuid"]) - elasticSearchFunctions.delete_aip_files(es_client, options["uuid"]) + search_service.delete_aip(options["uuid"]) + search_service.delete_aip_files(options["uuid"]) return if not options["uuid"]: @@ -279,7 +301,7 @@ def handle(self, *args, **options): ret = processAIPThenDeleteMETSFile( path=os.path.join(root, directory), temp_dir=temp_dir, - es_client=es_client, + search_service=search_service, delete_existing_data=options["delete"], ) # Don't recurse into this directory @@ -300,7 +322,7 @@ def handle(self, *args, **options): ret = processAIPThenDeleteMETSFile( path=os.path.join(root, filename), temp_dir=temp_dir, - es_client=es_client, + search_service=search_service, delete_existing_data=options["delete"], ) # Update count on successful index diff --git a/src/dashboard/src/main/management/commands/rebuild_transfer_backlog.py b/src/archivematica/dashboard/main/management/commands/rebuild_transfer_backlog.py similarity index 77% rename from src/dashboard/src/main/management/commands/rebuild_transfer_backlog.py rename to src/archivematica/dashboard/main/management/commands/rebuild_transfer_backlog.py index 0d65a65e7d..5e8da30fb3 100644 --- a/src/dashboard/src/main/management/commands/rebuild_transfer_backlog.py +++ b/src/archivematica/dashboard/main/management/commands/rebuild_transfer_backlog.py @@ -47,24 +47,30 @@ from pathlib import Path from subprocess import CalledProcessError -import archivematicaFunctions as am import bagit -import elasticSearchFunctions as es import metsrw -import storageService -from components.rights.load import load_rights from django.conf import settings as django_settings from django.core.exceptions import ValidationError from django.core.management.base import CommandError -from fileOperations import addFileToTransfer -from fileOperations import extract_package -from fpr.models import FormatVersion -from main.management.commands import DashboardCommand -from main.management.commands import boolean_input -from main.models import Agent -from main.models import FileFormatVersion -from main.models import FileID -from main.models import Transfer + +import archivematica.search.constants +from archivematica.archivematicaCommon import archivematicaFunctions as am +from archivematica.archivematicaCommon import storageService +from archivematica.archivematicaCommon.databaseFunctions import ( + build_transfer_file_index_data, +) +from archivematica.archivematicaCommon.databaseFunctions import get_transfer_details +from archivematica.archivematicaCommon.fileOperations import addFileToTransfer +from archivematica.archivematicaCommon.fileOperations import extract_package +from archivematica.dashboard.components.rights.load import load_rights +from archivematica.dashboard.fpr.models import FormatVersion +from archivematica.dashboard.main.management.commands import DashboardCommand +from archivematica.dashboard.main.management.commands import boolean_input +from archivematica.dashboard.main.models import Agent +from archivematica.dashboard.main.models import FileFormatVersion +from archivematica.dashboard.main.models import FileID +from archivematica.dashboard.main.models import Transfer +from archivematica.search.service import setup_search_service_from_conf logger = logging.getLogger("archivematica.dashboard") @@ -85,6 +91,10 @@ class Command(DashboardCommand): CHECKSUM_TYPE = "sha256" CHECKSUM_UTIL = "sha256sum" + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.dashboard_uuid = am.get_dashboard_uuid() or "" + def add_arguments(self, parser): """Entry point to add custom arguments.""" parser.add_argument( @@ -99,7 +109,7 @@ def add_arguments(self, parser): parser.add_argument( "--pipeline", help="Pipeline UUID to use when filtering packages from Storage Service", - default=am.get_dashboard_uuid(), + default=self.dashboard_uuid, ) parser.add_argument( "--delete-all", @@ -129,7 +139,10 @@ def add_arguments(self, parser): def handle(self, *args, **options): """Entry point of the rebuild_transfer_backlog command.""" # Check that the `transfers` part of the search is enabled - if es.TRANSFERS_INDEX not in django_settings.SEARCH_ENABLED: + if ( + archivematica.search.constants.TRANSFERS_INDEX + not in django_settings.SEARCH_ENABLED + ): print( "The Transfers indexes are not enabled. Please, make sure to " "set the *_SEARCH_ENABLED environment variables to `true` " @@ -158,29 +171,29 @@ def handle(self, *args, **options): ) self.info(f'Rebuilding "transfers" index from {transfer_backlog_dir}.') - # Connect to Elasticsearch. - es.setup_reading_from_conf(django_settings) - es_client = es.get_client() + # Connect to search backend. try: - es_info = es_client.info() + search_service = setup_search_service_from_conf(django_settings) + search_info = search_service.get_cluster_info() except Exception as err: - raise CommandError("Unable to connect to Elasticsearch: %s" % err) + raise CommandError("Unable to connect to search backend: %s" % err) else: self.info( - "Connected to Elasticsearch node {} (v{}).".format( - es_info["name"], es_info["version"]["number"] - ) + f"Connected to search backend node {search_info.name} (v{search_info.version})." ) if options["delete_all"]: - indexes = [es.TRANSFERS_INDEX, es.TRANSFER_FILES_INDEX] - self.delete_indexes(es_client, indexes) - self.create_indexes(es_client, indexes) + indexes = [ + archivematica.search.constants.TRANSFERS_INDEX, + archivematica.search.constants.TRANSFER_FILES_INDEX, + ] + self.delete_indexes(search_service, indexes) + self.create_indexes(search_service, indexes) if options["from_storage_service"]: pipeline_uuid = options["pipeline"] self.populate_data_from_storage_service( - es_client, + search_service, pipeline_uuid, uuid=options["uuid"], skip_to=options["skip_to"], @@ -188,7 +201,7 @@ def handle(self, *args, **options): ) else: self.populate_data_from_files( - es_client, + search_service, transfer_backlog_dir, uuid=options["uuid"], skip_to=options["skip_to"], @@ -213,24 +226,32 @@ def prepdir(self, path): if tail != suffix: return os.path.join(path, suffix) - def delete_indexes(self, es_client, indexes): + def delete_indexes(self, search_service, indexes): """Delete search indexes.""" self.stdout.write( "Deleting all transfers and transfer files " 'in the "transfers" and "transferfiles" indexes...' ) time.sleep(3) # Time for the user to panic and kill the process. - es_client.indices.delete(",".join(indexes), ignore=404) + search_service.delete_indexes(indexes) - def create_indexes(self, es_client, indexes): + def create_indexes(self, search_service, indexes): """Create search indexes.""" self.stdout.write("Creating indexes...") - es.create_indexes_if_needed(es_client, indexes) + search_service.ensure_indexes_exist(indexes) def populate_data_from_files( - self, es_client, transfer_backlog_dir, uuid=None, skip_to=None, delete=False + self, + search_service, + transfer_backlog_dir, + uuid=None, + skip_to=None, + delete=False, ): - """Populate indices and/or database from files.""" + """Populate indices and/or database from files. + + :param search_service: Search service instance. + """ transfer_backlog_dir = Path(transfer_backlog_dir) processed = 0 skip_found = False @@ -257,15 +278,16 @@ def populate_data_from_files( # if delete option specified delete before reindexing if delete: self.info(f"Deleting index data of {transfer_uuid}") - es.remove_backlog_transfer(es_client, transfer_uuid) - es.remove_backlog_transfer_files(es_client, transfer_uuid) + search_service.delete_transfer(transfer_uuid) + transfer_ids = {transfer_uuid} + search_service.delete_transfer_files(transfer_ids) if bag and "External-Identifier" in bag.info: self.info(f"Importing self-describing transfer {transfer_uuid}.") size = am.get_bag_size(bag, str(transfer_dir)) _import_self_describing_transfer( - self, es_client, self.stdout, transfer_dir, transfer_uuid, size + self, search_service, self.stdout, transfer_dir, transfer_uuid, size ) else: self.info(f"Rebuilding known transfer {transfer_uuid}.") @@ -275,17 +297,22 @@ def populate_data_from_files( size = am.walk_dir(str(transfer_dir)) _import_pipeline_dependant_transfer( - self, es_client, self.stdout, transfer_dir, transfer_uuid, size + self, search_service, self.stdout, transfer_dir, transfer_uuid, size ) processed += 1 self.success(f"{processed} transfers indexed!") def populate_data_from_storage_service( - self, es_client, pipeline_uuid, uuid=None, skip_to=None, delete=False + self, + search_service, + pipeline_uuid, + uuid=None, + skip_to=None, + delete=False, ): """Populate indices and/or database from Storage Service. - :param es_client: Elasticsearch client. + :param search_service: Search service instance. :param pipeline_uuid: UUID of origin pipeline for transfers to reindex. @@ -311,8 +338,9 @@ def populate_data_from_storage_service( # if delete option specified delete before reindexing if delete: self.info(f"Deleting index data of {transfer_uuid}") - es.remove_backlog_transfer(es_client, transfer_uuid) - es.remove_backlog_transfer_files(es_client, transfer_uuid) + search_service.delete_transfer(transfer_uuid) + transfer_ids = {transfer_uuid} + search_service.delete_transfer_files(transfer_ids) temp_backlog_dir = tempfile.mkdtemp() try: @@ -343,7 +371,7 @@ def populate_data_from_storage_service( ) _import_self_describing_transfer( self, - es_client, + search_service, self.stdout, Path(transfer_path), transfer_uuid, @@ -496,7 +524,7 @@ def _get_transfer_mets_path(transfer_dir): def _import_self_describing_transfer( - cmd, es_client, stdout, transfer_dir, transfer_uuid, size + cmd, search_service, stdout, transfer_dir, transfer_uuid, size ): """Import a self-describing transfer. @@ -505,7 +533,7 @@ def _import_self_describing_transfer( arrangement and ingest work as expected. :param cmd: Command object - :param es_client: Elasticsearch client. + :param search_service: Search service instance. :param stdout: stdout handler. :param transfer_dir: Path to transfer. :param transfer_uuid: Transfer UUID. @@ -548,17 +576,38 @@ def _import_self_describing_transfer( if django_settings.DEBUG: traceback.print_exc() - es.index_transfer_and_files( - es_client, - transfer_uuid, - str(transfer_dir) + "/", - size, + transfer_path = str(transfer_dir) + "/" + + # Stop if Transfer does not exist + if not os.path.exists(transfer_path): + error_message = "Transfer does not exist at: " + transfer_path + logger.error(error_message) + print(error_message, file=sys.stderr) + return 1 + + print("Transfer UUID: " + transfer_uuid) + print("Indexing Transfer files ...") + + transfer_name, accession_id, ingest_date = get_transfer_details(transfer_uuid) + transfer_index_data = build_transfer_file_index_data( + transfer_path, transfer_name, transfer_uuid, _elasticsearch_noop_printfn + ) + search_service.index_transfer( + uuid=transfer_uuid, + path=transfer_path, + size=size, + transfer_index_data=transfer_index_data, printfn=_elasticsearch_noop_printfn, + dashboard_uuid=cmd.dashboard_uuid, + transfer_name=transfer_name, + accession_id=accession_id, + ingest_date=ingest_date, ) + print("Done.") def _import_pipeline_dependant_transfer( - cmd, es_client, stdout, transfer_dir, transfer_uuid, size + cmd, search_service, stdout, transfer_dir, transfer_uuid, size ): """Import a pipeline-dependant transfer. @@ -571,13 +620,35 @@ def _import_pipeline_dependant_transfer( except (Transfer.DoesNotExist, ValidationError): cmd.warning(f"Skipping transfer {transfer_uuid} - not found in the database!") return - es.index_transfer_and_files( - es_client, - transfer_uuid, - str(transfer_dir) + "/", - size, + + transfer_path = str(transfer_dir) + "/" + + # Stop if Transfer does not exist + if not os.path.exists(transfer_path): + error_message = "Transfer does not exist at: " + transfer_path + logger.error(error_message) + print(error_message, file=sys.stderr) + return 1 + + print("Transfer UUID: " + transfer_uuid) + print("Indexing Transfer files ...") + + transfer_name, accession_id, ingest_date = get_transfer_details(transfer_uuid) + transfer_index_data = build_transfer_file_index_data( + transfer_path, transfer_name, transfer_uuid, _elasticsearch_noop_printfn + ) + search_service.index_transfer( + uuid=transfer_uuid, + path=transfer_path, + size=size, + transfer_index_data=transfer_index_data, printfn=_elasticsearch_noop_printfn, + dashboard_uuid=cmd.dashboard_uuid, + transfer_name=transfer_name, + accession_id=accession_id, + ingest_date=ingest_date, ) + print("Done.") try: storageService.reindex_file(transfer_uuid) except Exception as err: diff --git a/src/dashboard/src/main/management/commands/reindex_from_remote_cluster.py b/src/archivematica/dashboard/main/management/commands/reindex_from_remote_cluster.py similarity index 58% rename from src/dashboard/src/main/management/commands/reindex_from_remote_cluster.py rename to src/archivematica/dashboard/main/management/commands/reindex_from_remote_cluster.py index 8cf3523988..718e14cd50 100644 --- a/src/dashboard/src/main/management/commands/reindex_from_remote_cluster.py +++ b/src/archivematica/dashboard/main/management/commands/reindex_from_remote_cluster.py @@ -15,10 +15,11 @@ import json import sys -import elasticsearch -import elasticSearchFunctions from django.conf import settings -from main.management.commands import DashboardCommand + +import archivematica.search.constants +from archivematica.dashboard.main.management.commands import DashboardCommand +from archivematica.search.service import setup_search_service class Command(DashboardCommand): @@ -43,7 +44,7 @@ def add_arguments(self, parser): "--size", type=int, default=10, - help="Batch size, reduce it to limit request chunk size. " "Default: 10.", + help="Batch size, reduce it to limit request chunk size. Default: 10.", ) parser.add_argument( "-t", @@ -68,10 +69,9 @@ def handle(self, *args, **options): # Setup new cluster connection. Do not pass SEARCH_ENABLED # setting to avoid the creation of the indexes on setup, # and use the timeout passed to the command. - elasticSearchFunctions.setup( - settings.ELASTICSEARCH_SERVER, options["timeout"], [] + search_service = setup_search_service( + settings.ELASTICSEARCH_SERVER, options["timeout"], () ) - es_client = elasticSearchFunctions.get_client() # Get enabled indexes based on setting indexes = [] @@ -83,10 +83,8 @@ def handle(self, *args, **options): # Delete all indexes and create enabled ones in new cluster self.info("Creating new indexes.") try: - es_client.indices.delete( - ",".join(elasticSearchFunctions.INDEXES), ignore=404 - ) - elasticSearchFunctions.create_indexes_if_needed(es_client, indexes) + search_service.delete_indexes(archivematica.search.constants.INDEXES) + search_service.ensure_indexes_exist(indexes) except Exception as e: self.error( f"The Elasticsearch indexes could not be recreated in {settings.ELASTICSEARCH_SERVER}. " @@ -94,71 +92,54 @@ def handle(self, *args, **options): ) sys.exit(1) - # Default body for reindex requests - body = { - "source": { - "remote": { - "host": options["host"], - "socket_timeout": "{}s".format(options["timeout"]), - "connect_timeout": "{}s".format(options["timeout"]), - }, - "index": "", - "type": "", - "size": options["size"], - }, - "dest": {"index": "", "type": elasticSearchFunctions.DOC_TYPE}, + # Remote configuration for reindex requests + remote_config = { + "host": options["host"], + "socket_timeout": "{}s".format(options["timeout"]), + "connect_timeout": "{}s".format(options["timeout"]), + "size": options["size"], } # Add basic auth if options["username"] != "": - body["source"]["remote"]["username"] = options["username"] + remote_config["username"] = options["username"] if options["password"] != "": - body["source"]["remote"]["password"] = options["password"] + remote_config["password"] = options["password"] # Indexes and types to reindex - indexes_relations = [ - {"dest_index": "aips", "source_index": "aips", "source_type": "aip"}, + index_mappings = [ + {"dest_index": "aips", "source_index": "aips"}, { "dest_index": "aipfiles", - "source_index": "aips", - "source_type": "aipfile", + "source_index": "aipfiles", }, { "dest_index": "transfers", "source_index": "transfers", - "source_type": "transfer", }, { "dest_index": "transferfiles", - "source_index": "transfers", - "source_type": "transferfile", + "source_index": "transferfiles", }, ] + # Filter index mappings to only include enabled indexes + enabled_mappings = [ + mapping for mapping in index_mappings if mapping["dest_index"] in indexes + ] + # Reindex documents from remote cluster - fails = 0 - for indexes_relation in indexes_relations: - # Skip not enabled indexes - if indexes_relation["dest_index"] not in indexes: - continue - # Update request body - body["dest"]["index"] = indexes_relation["dest_index"] - body["source"]["index"] = indexes_relation["source_index"] - body["source"]["type"] = indexes_relation["source_type"] - # Reindex request - self.info("Reindexing %s:" % indexes_relation["dest_index"]) - try: - response = es_client.reindex(body=body) - except elasticsearch.TransportError as exc: - fails += 1 - self.error(f"Error: {exc}. Details:\n{exc.info}") - except Exception as exc: - fails += 1 - self.error(f"Error: {exc}") - else: - self.info("Response:\n%s" % json.dumps(response, indent=4)) - - if fails > 0: - self.error("%s reindex request(s) failed!" % fails) + results = search_service.reindex_from_remote(remote_config, enabled_mappings) + + # Report results + for success in results["successful"]: + self.info(f"Reindexing {success['index']}:") + self.info("Response:\n%s" % json.dumps(success["response"], indent=4)) + + for failure in results["failed"]: + self.error(f"Error reindexing {failure['index']}: {failure['error']}") + + if results["failed"]: + self.error(f"{len(results['failed'])} reindex request(s) failed!") else: self.success("All reindex requests ended successfully!") diff --git a/src/dashboard/src/main/management/commands/resolve_pending_jobs.py b/src/archivematica/dashboard/main/management/commands/resolve_pending_jobs.py similarity index 97% rename from src/dashboard/src/main/management/commands/resolve_pending_jobs.py rename to src/archivematica/dashboard/main/management/commands/resolve_pending_jobs.py index 95492b5654..ea0285b21a 100644 --- a/src/dashboard/src/main/management/commands/resolve_pending_jobs.py +++ b/src/archivematica/dashboard/main/management/commands/resolve_pending_jobs.py @@ -13,11 +13,12 @@ public APIs. This is an alternative to the old mcp-rpc-cli command. """ -from contrib.mcp.client import MCPClient from django.contrib.auth import get_user_model from django.core.management.base import CommandError from lxml import etree -from main.management.commands import DashboardCommand + +from archivematica.dashboard.contrib.mcp.client import MCPClient +from archivematica.dashboard.main.management.commands import DashboardCommand class Command(DashboardCommand): diff --git a/src/archivematica/dashboard/main/management/commands/update_elasticsearch_mappings.py b/src/archivematica/dashboard/main/management/commands/update_elasticsearch_mappings.py new file mode 100644 index 0000000000..2899cb9c57 --- /dev/null +++ b/src/archivematica/dashboard/main/management/commands/update_elasticsearch_mappings.py @@ -0,0 +1,79 @@ +"""Update Elasticsearch mappings for Archivematica 1.12 + +This command updates the Elasticsearch mappings for the aips and +aipfiles indices for Archivematica 1.12 to enable sorting on all fields +displayed in the new Archival Storage DataTable and populates the new +filePath.raw subfield in the aipfiles index using an Update By Query. + +Execution example: + +./manage.py update_elasticsearch_mappings +""" + +import sys + +from django.conf import settings + +import archivematica.search.constants +from archivematica.dashboard.main.management.commands import DashboardCommand +from archivematica.search.service import SearchServiceError +from archivematica.search.service import setup_search_service_from_conf + + +class Command(DashboardCommand): + help = __doc__ + + def handle(self, *args, **options): + # Check that the AIPs index is enabled before proceeding. + if archivematica.search.constants.AIPS_INDEX not in settings.SEARCH_ENABLED: + self.error( + "The AIPs indexes are not enabled. Please, make sure to " + "set the *_SEARCH_ENABLED environment variables to `true` " + "to enable the AIPs and Transfers indexes, or to `aips` " + "to only enable the AIPs indexes." + ) + sys.exit(1) + + try: + search_service = setup_search_service_from_conf(settings) + except SearchServiceError: + self.error("Error: the search service may not be running.") + sys.exit(1) + + # Update the AIPs index mappings. + aips_mappings = { + "properties": { + archivematica.search.constants.ES_FIELD_ACCESSION_IDS: { + "type": "keyword" + }, + archivematica.search.constants.ES_FIELD_STATUS: {"type": "keyword"}, + archivematica.search.constants.ES_FIELD_FILECOUNT: {"type": "integer"}, + archivematica.search.constants.ES_FIELD_LOCATION: {"type": "keyword"}, + } + } + search_service.update_index_mappings( + archivematica.search.constants.AIPS_INDEX, aips_mappings + ) + + # Update the AIP files index mapping. + aip_files_mappings = { + "properties": { + "accessionid": {"type": "keyword"}, + archivematica.search.constants.ES_FIELD_STATUS: {"type": "keyword"}, + "filePath": { + "type": "text", + "analyzer": "file_path_and_name", + "fields": {"raw": {"type": "keyword"}}, + }, + } + } + search_service.update_index_mappings( + archivematica.search.constants.AIP_FILES_INDEX, aip_files_mappings + ) + + # Perform an update by query on the aipfiles index to populate + # the filePath.raw subfield from existing text values. We do + # not specify a query to ensure that all documents are updated. + search_service.update_all_documents( + archivematica.search.constants.AIP_FILES_INDEX + ) diff --git a/src/dashboard/src/main/migrations/0001_initial.py b/src/archivematica/dashboard/main/migrations/0001_initial.py similarity index 98% rename from src/dashboard/src/main/migrations/0001_initial.py rename to src/archivematica/dashboard/main/migrations/0001_initial.py index 1196db369b..81c90de7ce 100644 --- a/src/dashboard/src/main/migrations/0001_initial.py +++ b/src/archivematica/dashboard/main/migrations/0001_initial.py @@ -1,9 +1,10 @@ import uuid -import main.models from django.db import migrations from django.db import models +import archivematica.dashboard.main.models as main_models + class Migration(migrations.Migration): dependencies = [("fpr", "__first__")] @@ -18,7 +19,7 @@ class Migration(migrations.Migration): ), ( "sipuuid", - main.models.UUIDField( + main_models.UUIDField( max_length=36, db_column="SIPUUID", blank=True, @@ -190,7 +191,7 @@ class Migration(migrations.Migration): ), ( "event_id", - main.models.UUIDField( + main_models.UUIDField( null=True, db_column="eventIdentifierUUID", editable=False, @@ -227,7 +228,7 @@ class Migration(migrations.Migration): fields=[ ( "uuid", - main.models.UUIDField( + main_models.UUIDField( max_length=36, serialize=False, primary_key=True, @@ -374,7 +375,7 @@ class Migration(migrations.Migration): fields=[ ( "jobuuid", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="jobUUID", serialize=False, @@ -401,7 +402,7 @@ class Migration(migrations.Migration): ("directory", models.TextField(blank=True)), ( "sipuuid", - main.models.UUIDField( + main_models.UUIDField( max_length=36, db_column="SIPUUID", default=uuid.uuid4 ), ), @@ -435,7 +436,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -468,7 +469,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -502,7 +503,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -533,7 +534,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -570,7 +571,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -633,7 +634,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -1492,7 +1493,7 @@ class Migration(migrations.Migration): fields=[ ( "uuid", - main.models.UUIDField( + main_models.UUIDField( max_length=36, serialize=False, primary_key=True, @@ -1563,7 +1564,7 @@ class Migration(migrations.Migration): ("arrange_path", models.CharField(max_length=255)), ( "file_uuid", - main.models.UUIDField( + main_models.UUIDField( default=uuid.uuid4, max_length=36, null=True, @@ -1573,7 +1574,7 @@ class Migration(migrations.Migration): ), ( "transfer_uuid", - main.models.UUIDField( + main_models.UUIDField( default=uuid.uuid4, max_length=36, null=True, @@ -1592,7 +1593,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -1674,7 +1675,7 @@ class Migration(migrations.Migration): fields=[ ( "taskuuid", - main.models.UUIDField( + main_models.UUIDField( max_length=36, serialize=False, primary_key=True, @@ -1727,7 +1728,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -1772,7 +1773,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -1816,7 +1817,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -1859,7 +1860,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -1902,7 +1903,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -1937,7 +1938,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -1967,7 +1968,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -2001,7 +2002,7 @@ class Migration(migrations.Migration): fields=[ ( "uuid", - main.models.UUIDField( + main_models.UUIDField( max_length=36, serialize=False, primary_key=True, @@ -2051,7 +2052,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -2092,7 +2093,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -2124,7 +2125,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -2148,7 +2149,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -2166,7 +2167,7 @@ class Migration(migrations.Migration): ), ( "unituuid", - main.models.UUIDField( + main_models.UUIDField( help_text="Semantically a foreign key to SIP or Transfer", max_length=36, null=True, @@ -2207,7 +2208,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, @@ -2249,7 +2250,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, diff --git a/src/dashboard/src/main/migrations/0002_initial_data.py b/src/archivematica/dashboard/main/migrations/0002_initial_data.py similarity index 100% rename from src/dashboard/src/main/migrations/0002_initial_data.py rename to src/archivematica/dashboard/main/migrations/0002_initial_data.py diff --git a/src/dashboard/src/main/migrations/0003_archivesspacedipobjectresourcepairing.py b/src/archivematica/dashboard/main/migrations/0003_archivesspacedipobjectresourcepairing.py similarity index 89% rename from src/dashboard/src/main/migrations/0003_archivesspacedipobjectresourcepairing.py rename to src/archivematica/dashboard/main/migrations/0003_archivesspacedipobjectresourcepairing.py index d81950ba44..3c763e1434 100644 --- a/src/dashboard/src/main/migrations/0003_archivesspacedipobjectresourcepairing.py +++ b/src/archivematica/dashboard/main/migrations/0003_archivesspacedipobjectresourcepairing.py @@ -1,9 +1,10 @@ import uuid -import main.models from django.db import migrations from django.db import models +import archivematica.dashboard.main.models as main_models + class Migration(migrations.Migration): dependencies = [("main", "0002_initial_data")] @@ -18,13 +19,13 @@ class Migration(migrations.Migration): ), ( "dipuuid", - main.models.UUIDField( + main_models.UUIDField( max_length=50, db_column="dipUUID", default=uuid.uuid4 ), ), ( "fileuuid", - main.models.UUIDField( + main_models.UUIDField( max_length=50, db_column="fileUUID", default=uuid.uuid4 ), ), diff --git a/src/dashboard/src/main/migrations/0003_archivesspacedipobjectresourcepairing_data.py b/src/archivematica/dashboard/main/migrations/0003_archivesspacedipobjectresourcepairing_data.py similarity index 100% rename from src/dashboard/src/main/migrations/0003_archivesspacedipobjectresourcepairing_data.py rename to src/archivematica/dashboard/main/migrations/0003_archivesspacedipobjectresourcepairing_data.py diff --git a/src/dashboard/src/main/migrations/0004_rights.py b/src/archivematica/dashboard/main/migrations/0004_rights.py similarity index 100% rename from src/dashboard/src/main/migrations/0004_rights.py rename to src/archivematica/dashboard/main/migrations/0004_rights.py diff --git a/src/dashboard/src/main/migrations/0005_reingest.py b/src/archivematica/dashboard/main/migrations/0005_reingest.py similarity index 100% rename from src/dashboard/src/main/migrations/0005_reingest.py rename to src/archivematica/dashboard/main/migrations/0005_reingest.py diff --git a/src/dashboard/src/main/migrations/0005_reingest_data.py b/src/archivematica/dashboard/main/migrations/0005_reingest_data.py similarity index 100% rename from src/dashboard/src/main/migrations/0005_reingest_data.py rename to src/archivematica/dashboard/main/migrations/0005_reingest_data.py diff --git a/src/dashboard/src/main/migrations/0006_levelofdescription.py b/src/archivematica/dashboard/main/migrations/0006_levelofdescription.py similarity index 95% rename from src/dashboard/src/main/migrations/0006_levelofdescription.py rename to src/archivematica/dashboard/main/migrations/0006_levelofdescription.py index c8d823e4f7..0d26f61c4f 100644 --- a/src/dashboard/src/main/migrations/0006_levelofdescription.py +++ b/src/archivematica/dashboard/main/migrations/0006_levelofdescription.py @@ -1,9 +1,10 @@ import uuid -import main.models from django.db import migrations from django.db import models +import archivematica.dashboard.main.models as main_models + def data_migration(apps, schema_editor): # Set AtoM DIP upload defaults @@ -33,7 +34,7 @@ class Migration(migrations.Migration): fields=[ ( "id", - main.models.UUIDField( + main_models.UUIDField( primary_key=True, db_column="pk", serialize=False, diff --git a/src/dashboard/src/main/migrations/0007_django_upgrade_tweaks.py b/src/archivematica/dashboard/main/migrations/0007_django_upgrade_tweaks.py similarity index 100% rename from src/dashboard/src/main/migrations/0007_django_upgrade_tweaks.py rename to src/archivematica/dashboard/main/migrations/0007_django_upgrade_tweaks.py diff --git a/src/dashboard/src/main/migrations/0008_fpcommandoutput.py b/src/archivematica/dashboard/main/migrations/0008_fpcommandoutput.py similarity index 100% rename from src/dashboard/src/main/migrations/0008_fpcommandoutput.py rename to src/archivematica/dashboard/main/migrations/0008_fpcommandoutput.py diff --git a/src/dashboard/src/main/migrations/0009_matching_gui.py b/src/archivematica/dashboard/main/migrations/0009_matching_gui.py similarity index 100% rename from src/dashboard/src/main/migrations/0009_matching_gui.py rename to src/archivematica/dashboard/main/migrations/0009_matching_gui.py diff --git a/src/dashboard/src/main/migrations/0010_dip_upload_store.py b/src/archivematica/dashboard/main/migrations/0010_dip_upload_store.py similarity index 100% rename from src/dashboard/src/main/migrations/0010_dip_upload_store.py rename to src/archivematica/dashboard/main/migrations/0010_dip_upload_store.py diff --git a/src/dashboard/src/main/migrations/0011_version_number.py b/src/archivematica/dashboard/main/migrations/0011_version_number.py similarity index 100% rename from src/dashboard/src/main/migrations/0011_version_number.py rename to src/archivematica/dashboard/main/migrations/0011_version_number.py diff --git a/src/dashboard/src/main/migrations/0012_file_id_text.py b/src/archivematica/dashboard/main/migrations/0012_file_id_text.py similarity index 100% rename from src/dashboard/src/main/migrations/0012_file_id_text.py rename to src/archivematica/dashboard/main/migrations/0012_file_id_text.py diff --git a/src/dashboard/src/main/migrations/0013_upload_archivesspace_inherit_notes.py b/src/archivematica/dashboard/main/migrations/0013_upload_archivesspace_inherit_notes.py similarity index 100% rename from src/dashboard/src/main/migrations/0013_upload_archivesspace_inherit_notes.py rename to src/archivematica/dashboard/main/migrations/0013_upload_archivesspace_inherit_notes.py diff --git a/src/dashboard/src/main/migrations/0014_aic_fixes.py b/src/archivematica/dashboard/main/migrations/0014_aic_fixes.py similarity index 100% rename from src/dashboard/src/main/migrations/0014_aic_fixes.py rename to src/archivematica/dashboard/main/migrations/0014_aic_fixes.py diff --git a/src/dashboard/src/main/migrations/0015_no_normalize_thumbnails.py b/src/archivematica/dashboard/main/migrations/0015_no_normalize_thumbnails.py similarity index 100% rename from src/dashboard/src/main/migrations/0015_no_normalize_thumbnails.py rename to src/archivematica/dashboard/main/migrations/0015_no_normalize_thumbnails.py diff --git a/src/dashboard/src/main/migrations/0016_file_currentlocation_nullable.py b/src/archivematica/dashboard/main/migrations/0016_file_currentlocation_nullable.py similarity index 100% rename from src/dashboard/src/main/migrations/0016_file_currentlocation_nullable.py rename to src/archivematica/dashboard/main/migrations/0016_file_currentlocation_nullable.py diff --git a/src/dashboard/src/main/migrations/0017_update_seigfried.py b/src/archivematica/dashboard/main/migrations/0017_update_seigfried.py similarity index 100% rename from src/dashboard/src/main/migrations/0017_update_seigfried.py rename to src/archivematica/dashboard/main/migrations/0017_update_seigfried.py diff --git a/src/dashboard/src/main/migrations/0018_archivesspace_sip_arrange.py b/src/archivematica/dashboard/main/migrations/0018_archivesspace_sip_arrange.py similarity index 100% rename from src/dashboard/src/main/migrations/0018_archivesspace_sip_arrange.py rename to src/archivematica/dashboard/main/migrations/0018_archivesspace_sip_arrange.py diff --git a/src/dashboard/src/main/migrations/0019_normalization_report.py b/src/archivematica/dashboard/main/migrations/0019_normalization_report.py similarity index 100% rename from src/dashboard/src/main/migrations/0019_normalization_report.py rename to src/archivematica/dashboard/main/migrations/0019_normalization_report.py diff --git a/src/dashboard/src/main/migrations/0020_index_after_processing_decision.py b/src/archivematica/dashboard/main/migrations/0020_index_after_processing_decision.py similarity index 100% rename from src/dashboard/src/main/migrations/0020_index_after_processing_decision.py rename to src/archivematica/dashboard/main/migrations/0020_index_after_processing_decision.py diff --git a/src/dashboard/src/main/migrations/0021_checksum_algorithms.py b/src/archivematica/dashboard/main/migrations/0021_checksum_algorithms.py similarity index 100% rename from src/dashboard/src/main/migrations/0021_checksum_algorithms.py rename to src/archivematica/dashboard/main/migrations/0021_checksum_algorithms.py diff --git a/src/dashboard/src/main/migrations/0022_email_report_args.py b/src/archivematica/dashboard/main/migrations/0022_email_report_args.py similarity index 100% rename from src/dashboard/src/main/migrations/0022_email_report_args.py rename to src/archivematica/dashboard/main/migrations/0022_email_report_args.py diff --git a/src/dashboard/src/main/migrations/0023_blob_fields.py b/src/archivematica/dashboard/main/migrations/0023_blob_fields.py similarity index 100% rename from src/dashboard/src/main/migrations/0023_blob_fields.py rename to src/archivematica/dashboard/main/migrations/0023_blob_fields.py diff --git a/src/dashboard/src/main/migrations/0024_agenttype.py b/src/archivematica/dashboard/main/migrations/0024_agenttype.py similarity index 100% rename from src/dashboard/src/main/migrations/0024_agenttype.py rename to src/archivematica/dashboard/main/migrations/0024_agenttype.py diff --git a/src/dashboard/src/main/migrations/0025_copyrightstatus_controlled_vocab.py b/src/archivematica/dashboard/main/migrations/0025_copyrightstatus_controlled_vocab.py similarity index 100% rename from src/dashboard/src/main/migrations/0025_copyrightstatus_controlled_vocab.py rename to src/archivematica/dashboard/main/migrations/0025_copyrightstatus_controlled_vocab.py diff --git a/src/dashboard/src/main/migrations/0026_agent_m2m_event.py b/src/archivematica/dashboard/main/migrations/0026_agent_m2m_event.py similarity index 100% rename from src/dashboard/src/main/migrations/0026_agent_m2m_event.py rename to src/archivematica/dashboard/main/migrations/0026_agent_m2m_event.py diff --git a/src/dashboard/src/main/migrations/0027_full_reingest.py b/src/archivematica/dashboard/main/migrations/0027_full_reingest.py similarity index 100% rename from src/dashboard/src/main/migrations/0027_full_reingest.py rename to src/archivematica/dashboard/main/migrations/0027_full_reingest.py diff --git a/src/dashboard/src/main/migrations/0028_version_number.py b/src/archivematica/dashboard/main/migrations/0028_version_number.py similarity index 100% rename from src/dashboard/src/main/migrations/0028_version_number.py rename to src/archivematica/dashboard/main/migrations/0028_version_number.py diff --git a/src/dashboard/src/main/migrations/0029_backlog_removal_event.py b/src/archivematica/dashboard/main/migrations/0029_backlog_removal_event.py similarity index 100% rename from src/dashboard/src/main/migrations/0029_backlog_removal_event.py rename to src/archivematica/dashboard/main/migrations/0029_backlog_removal_event.py diff --git a/src/dashboard/src/main/migrations/0030_rights_import.py b/src/archivematica/dashboard/main/migrations/0030_rights_import.py similarity index 100% rename from src/dashboard/src/main/migrations/0030_rights_import.py rename to src/archivematica/dashboard/main/migrations/0030_rights_import.py diff --git a/src/dashboard/src/main/migrations/0031_job_currentstep_choices.py b/src/archivematica/dashboard/main/migrations/0031_job_currentstep_choices.py similarity index 100% rename from src/dashboard/src/main/migrations/0031_job_currentstep_choices.py rename to src/archivematica/dashboard/main/migrations/0031_job_currentstep_choices.py diff --git a/src/dashboard/src/main/migrations/0032_dashboardsetting_scope.py b/src/archivematica/dashboard/main/migrations/0032_dashboardsetting_scope.py similarity index 97% rename from src/dashboard/src/main/migrations/0032_dashboardsetting_scope.py rename to src/archivematica/dashboard/main/migrations/0032_dashboardsetting_scope.py index 44a753d582..ffd9e1c03c 100644 --- a/src/dashboard/src/main/migrations/0032_dashboardsetting_scope.py +++ b/src/archivematica/dashboard/main/migrations/0032_dashboardsetting_scope.py @@ -2,9 +2,10 @@ from django.db import migrations from django.db import models -from main.models import DashboardSetting -from main.models import DashboardSettingManager -from main.models import Job + +from archivematica.dashboard.main.models import DashboardSetting +from archivematica.dashboard.main.models import DashboardSettingManager +from archivematica.dashboard.main.models import Job def data_migration_atom_to_dict(apps, schema_editor): @@ -150,7 +151,7 @@ def data_migration_as_to_dict(apps, schema_editor): ) # Default to empty string for every argument - config_dict = {arg_name: "" for arg_name in cmd_args} + config_dict = dict.fromkeys(cmd_args, "") # Attempt to read defaults from MicroServiceChoiceReplacementDic try: @@ -219,7 +220,7 @@ def data_migration_atk_to_dict(apps, schema_editor): ) # Default to empty string for every argument - config_dict = {arg_name: "" for arg_name in cmd_args} + config_dict = dict.fromkeys(cmd_args, "") # Attempt to read defaults from MicroServiceChoiceReplacementDic try: diff --git a/src/dashboard/src/main/migrations/0033_store_transfer_file_modification_dates.py b/src/archivematica/dashboard/main/migrations/0033_store_transfer_file_modification_dates.py similarity index 98% rename from src/dashboard/src/main/migrations/0033_store_transfer_file_modification_dates.py rename to src/archivematica/dashboard/main/migrations/0033_store_transfer_file_modification_dates.py index 7e26a3683e..f984c999e6 100644 --- a/src/dashboard/src/main/migrations/0033_store_transfer_file_modification_dates.py +++ b/src/archivematica/dashboard/main/migrations/0033_store_transfer_file_modification_dates.py @@ -1,6 +1,7 @@ from django.db import migrations from django.db import models -from main.models import Job + +from archivematica.dashboard.main.models import Job # Can't use apps.get_model for this model as we need to access class attributes diff --git a/src/dashboard/src/main/migrations/0034_add_readme_to_aips.py b/src/archivematica/dashboard/main/migrations/0034_add_readme_to_aips.py similarity index 98% rename from src/dashboard/src/main/migrations/0034_add_readme_to_aips.py rename to src/archivematica/dashboard/main/migrations/0034_add_readme_to_aips.py index 1d8dd38fe0..44e10d51fa 100644 --- a/src/dashboard/src/main/migrations/0034_add_readme_to_aips.py +++ b/src/archivematica/dashboard/main/migrations/0034_add_readme_to_aips.py @@ -1,5 +1,6 @@ from django.db import migrations -from main.models import Job + +from archivematica.dashboard.main.models import Job # Can't use apps.get_model for this model as we need to access class attributes diff --git a/src/dashboard/src/main/migrations/0035_fix_workflow_dip_generation.py b/src/archivematica/dashboard/main/migrations/0035_fix_workflow_dip_generation.py similarity index 100% rename from src/dashboard/src/main/migrations/0035_fix_workflow_dip_generation.py rename to src/archivematica/dashboard/main/migrations/0035_fix_workflow_dip_generation.py diff --git a/src/dashboard/src/main/migrations/0036_mediaconch_validation.py b/src/archivematica/dashboard/main/migrations/0036_mediaconch_validation.py similarity index 99% rename from src/dashboard/src/main/migrations/0036_mediaconch_validation.py rename to src/archivematica/dashboard/main/migrations/0036_mediaconch_validation.py index f49d556e2e..9d4cd2599e 100644 --- a/src/dashboard/src/main/migrations/0036_mediaconch_validation.py +++ b/src/archivematica/dashboard/main/migrations/0036_mediaconch_validation.py @@ -160,7 +160,7 @@ def data_migration(apps, schema_editor): id=vldt_ccss_drvtv_stc_pk, execute="validateFile_v1.0", arguments=( - '"%relativeLocation%" "%fileUUID%" "%SIPUUID%"' ' "%sharedPath%" "access"' + '"%relativeLocation%" "%fileUUID%" "%SIPUUID%" "%sharedPath%" "access"' ), filter_subdir="DIP/objects/", ) diff --git a/src/dashboard/src/main/migrations/0037_mediaconch_policy_checks.py b/src/archivematica/dashboard/main/migrations/0037_mediaconch_policy_checks.py similarity index 99% rename from src/dashboard/src/main/migrations/0037_mediaconch_policy_checks.py rename to src/archivematica/dashboard/main/migrations/0037_mediaconch_policy_checks.py index c0e42b53a2..b5aec34c1d 100644 --- a/src/dashboard/src/main/migrations/0037_mediaconch_policy_checks.py +++ b/src/archivematica/dashboard/main/migrations/0037_mediaconch_policy_checks.py @@ -95,7 +95,7 @@ def data_migration(apps, schema_editor): id=ccss_drvtv_policy_check_stc_pk, execute="policyCheck_v0.0", arguments=( - '"%relativeLocation%" "%fileUUID%" "%SIPUUID%"' ' "%sharedPath%" "access"' + '"%relativeLocation%" "%fileUUID%" "%SIPUUID%" "%sharedPath%" "access"' ), ) @@ -315,7 +315,7 @@ def data_migration(apps, schema_editor): id=rgnl_policy_check_stc_pk, execute="policyCheck_v0.0", arguments=( - '"%relativeLocation%" "%fileUUID%" "%SIPUUID%"' ' "%sharedPath%" "original"' + '"%relativeLocation%" "%fileUUID%" "%SIPUUID%" "%sharedPath%" "original"' ), # filter_subdir='objects/' <- not needed during transfer, I believe ... ) diff --git a/src/dashboard/src/main/migrations/0038_fix_workflow_store_dip.py b/src/archivematica/dashboard/main/migrations/0038_fix_workflow_store_dip.py similarity index 97% rename from src/dashboard/src/main/migrations/0038_fix_workflow_store_dip.py rename to src/archivematica/dashboard/main/migrations/0038_fix_workflow_store_dip.py index 7f06190779..565e61e649 100644 --- a/src/dashboard/src/main/migrations/0038_fix_workflow_store_dip.py +++ b/src/archivematica/dashboard/main/migrations/0038_fix_workflow_store_dip.py @@ -152,9 +152,7 @@ def data_migration(apps, schema_editor): completed_tc.save() completed_stc = StandardTaskConfig.objects.get(id=completed_stc_uuid) completed_stc.arguments = ( - '"%SIPDirectory%" ' - '"%rejectedDirectory%" ' - '"%watchDirectoryPath%uploadedDIPs/"' + '"%SIPDirectory%" "%rejectedDirectory%" "%watchDirectoryPath%uploadedDIPs/"' ) completed_stc.execute = "handleUnstoredDIP_v0.0" completed_stc.save() @@ -169,7 +167,7 @@ def data_migration(apps, schema_editor): move_uploaded_dips_stc_uuid = "302be9f9-af3f-45da-9305-02706d81b742" StandardTaskConfig.objects.filter(id=move_uploaded_dips_stc_uuid).update( execute="copy_v0.0", - arguments='"%SIPDirectory%" ' '"%watchDirectoryPath%uploadedDIPs/." ' "-R", + arguments='"%SIPDirectory%" "%watchDirectoryPath%uploadedDIPs/." -R', ) diff --git a/src/dashboard/src/main/migrations/0039_delete_sudo_clientscript.py b/src/archivematica/dashboard/main/migrations/0039_delete_sudo_clientscript.py similarity index 100% rename from src/dashboard/src/main/migrations/0039_delete_sudo_clientscript.py rename to src/archivematica/dashboard/main/migrations/0039_delete_sudo_clientscript.py diff --git a/src/dashboard/src/main/migrations/0040_directory_model.py b/src/archivematica/dashboard/main/migrations/0040_directory_model.py similarity index 98% rename from src/dashboard/src/main/migrations/0040_directory_model.py rename to src/archivematica/dashboard/main/migrations/0040_directory_model.py index bdbd806d9e..672aa248f6 100644 --- a/src/dashboard/src/main/migrations/0040_directory_model.py +++ b/src/archivematica/dashboard/main/migrations/0040_directory_model.py @@ -1,9 +1,10 @@ import uuid -import main.models from django.db import migrations from django.db import models +import archivematica.dashboard.main.models as main_models + def data_migration(apps, schema_editor): """Creates the ``Directory`` model, primarily so that directories can be @@ -93,8 +94,7 @@ def data_migration(apps, schema_editor): id=dir_uuid_stc_uuid, execute="assignUUIDsToDirectories_v0.0", arguments=( - '"%SIPDirectory%" "%SIPUUID%" --include-dirs' - ' "%AssignUUIDsToDirectories%"' + '"%SIPDirectory%" "%SIPUUID%" --include-dirs "%AssignUUIDsToDirectories%"' ), ) @@ -229,7 +229,7 @@ class Migration(migrations.Migration): fields=[ ( "uuid", - main.models.UUIDField( + main_models.UUIDField( max_length=36, serialize=False, primary_key=True, diff --git a/src/dashboard/src/main/migrations/0041_bind_pids.py b/src/archivematica/dashboard/main/migrations/0041_bind_pids.py similarity index 99% rename from src/dashboard/src/main/migrations/0041_bind_pids.py rename to src/archivematica/dashboard/main/migrations/0041_bind_pids.py index fa5ffddc4f..9e5eda2069 100644 --- a/src/dashboard/src/main/migrations/0041_bind_pids.py +++ b/src/archivematica/dashboard/main/migrations/0041_bind_pids.py @@ -23,7 +23,7 @@ def data_migration(apps, schema_editor): PID---generally the UUID of the entity---is passed during handle creation.) That the PURL (i.e., PID's URL) may be configured to resolve to different external (resolve) URLs depending on the qualifier (GET query - parameter) that is appended to it. See archivematicaCommon/lib/bindpid.py + parameter) that is appended to it. See archivematicaCommon/bindpid.py for more details. """ diff --git a/src/dashboard/src/main/migrations/0042_directory_uuids_all_transfer_types.py b/src/archivematica/dashboard/main/migrations/0042_directory_uuids_all_transfer_types.py similarity index 100% rename from src/dashboard/src/main/migrations/0042_directory_uuids_all_transfer_types.py rename to src/archivematica/dashboard/main/migrations/0042_directory_uuids_all_transfer_types.py diff --git a/src/dashboard/src/main/migrations/0043_remove_create_pointer_file_ms.py b/src/archivematica/dashboard/main/migrations/0043_remove_create_pointer_file_ms.py similarity index 100% rename from src/dashboard/src/main/migrations/0043_remove_create_pointer_file_ms.py rename to src/archivematica/dashboard/main/migrations/0043_remove_create_pointer_file_ms.py diff --git a/src/dashboard/src/main/migrations/0044_update_idtools.py b/src/archivematica/dashboard/main/migrations/0044_update_idtools.py similarity index 100% rename from src/dashboard/src/main/migrations/0044_update_idtools.py rename to src/archivematica/dashboard/main/migrations/0044_update_idtools.py diff --git a/src/dashboard/src/main/migrations/0045_archivesspace_config_dict.py b/src/archivematica/dashboard/main/migrations/0045_archivesspace_config_dict.py similarity index 100% rename from src/dashboard/src/main/migrations/0045_archivesspace_config_dict.py rename to src/archivematica/dashboard/main/migrations/0045_archivesspace_config_dict.py diff --git a/src/dashboard/src/main/migrations/0046_optional_normative_structmap.py b/src/archivematica/dashboard/main/migrations/0046_optional_normative_structmap.py similarity index 99% rename from src/dashboard/src/main/migrations/0046_optional_normative_structmap.py rename to src/archivematica/dashboard/main/migrations/0046_optional_normative_structmap.py index 4a600c9336..46ddda9170 100644 --- a/src/dashboard/src/main/migrations/0046_optional_normative_structmap.py +++ b/src/archivematica/dashboard/main/migrations/0046_optional_normative_structmap.py @@ -1,5 +1,6 @@ from django.db import migrations -from main.models import Job + +from archivematica.dashboard.main.models import Job # Can't use apps.get_model for this model as we need to access class attributes diff --git a/src/dashboard/src/main/migrations/0047_version_number.py b/src/archivematica/dashboard/main/migrations/0047_version_number.py similarity index 100% rename from src/dashboard/src/main/migrations/0047_version_number.py rename to src/archivematica/dashboard/main/migrations/0047_version_number.py diff --git a/src/dashboard/src/main/migrations/0048_fix_upload_qubit_setting.py b/src/archivematica/dashboard/main/migrations/0048_fix_upload_qubit_setting.py similarity index 100% rename from src/dashboard/src/main/migrations/0048_fix_upload_qubit_setting.py rename to src/archivematica/dashboard/main/migrations/0048_fix_upload_qubit_setting.py diff --git a/src/dashboard/src/main/migrations/0049_change_pointer_file_filegrpuse.py b/src/archivematica/dashboard/main/migrations/0049_change_pointer_file_filegrpuse.py similarity index 100% rename from src/dashboard/src/main/migrations/0049_change_pointer_file_filegrpuse.py rename to src/archivematica/dashboard/main/migrations/0049_change_pointer_file_filegrpuse.py diff --git a/src/dashboard/src/main/migrations/0050_remove_magic_links.py b/src/archivematica/dashboard/main/migrations/0050_remove_magic_links.py similarity index 99% rename from src/dashboard/src/main/migrations/0050_remove_magic_links.py rename to src/archivematica/dashboard/main/migrations/0050_remove_magic_links.py index 1dac6e0947..a140886895 100644 --- a/src/dashboard/src/main/migrations/0050_remove_magic_links.py +++ b/src/archivematica/dashboard/main/migrations/0050_remove_magic_links.py @@ -3,7 +3,8 @@ from collections import namedtuple from django.db import migrations -from main.models import Job + +from archivematica.dashboard.main.models import Job # Can't use apps.get_model for this model as we need to access class attributes. @@ -53,7 +54,7 @@ def data_migration(apps, schema_editor): ########################################################################### assign_magic_link = namedtuple( - "AssignMagicLink", "mscl_pk tc_pk tcmagic_pk goto_mscl_pk " "new_uuid new_var" + "AssignMagicLink", "mscl_pk tc_pk tcmagic_pk goto_mscl_pk new_uuid new_var" ) assign_magic_links = [ # These two determine the path after the package is moved to quarantine. diff --git a/src/dashboard/src/main/migrations/0051_remove_verify_premis_checksums.py b/src/archivematica/dashboard/main/migrations/0051_remove_verify_premis_checksums.py similarity index 100% rename from src/dashboard/src/main/migrations/0051_remove_verify_premis_checksums.py rename to src/archivematica/dashboard/main/migrations/0051_remove_verify_premis_checksums.py diff --git a/src/dashboard/src/main/migrations/0052_correct_extract_packages_fallback_link.py b/src/archivematica/dashboard/main/migrations/0052_correct_extract_packages_fallback_link.py similarity index 100% rename from src/dashboard/src/main/migrations/0052_correct_extract_packages_fallback_link.py rename to src/archivematica/dashboard/main/migrations/0052_correct_extract_packages_fallback_link.py diff --git a/src/dashboard/src/main/migrations/0053_remove_mcp_unused_field.py b/src/archivematica/dashboard/main/migrations/0053_remove_mcp_unused_field.py similarity index 100% rename from src/dashboard/src/main/migrations/0053_remove_mcp_unused_field.py rename to src/archivematica/dashboard/main/migrations/0053_remove_mcp_unused_field.py diff --git a/src/dashboard/src/main/migrations/0054_index_aip_silent_error_branch.py b/src/archivematica/dashboard/main/migrations/0054_index_aip_silent_error_branch.py similarity index 96% rename from src/dashboard/src/main/migrations/0054_index_aip_silent_error_branch.py rename to src/archivematica/dashboard/main/migrations/0054_index_aip_silent_error_branch.py index 56395db45c..42653c50ad 100644 --- a/src/dashboard/src/main/migrations/0054_index_aip_silent_error_branch.py +++ b/src/archivematica/dashboard/main/migrations/0054_index_aip_silent_error_branch.py @@ -1,5 +1,6 @@ from django.db import migrations -from main.models import Job + +from archivematica.dashboard.main.models import Job # Can't use apps.get_model for this model as we need to access class attributes. diff --git a/src/dashboard/src/main/migrations/0055_normalize_thumbnail_mode_selection.py b/src/archivematica/dashboard/main/migrations/0055_normalize_thumbnail_mode_selection.py similarity index 100% rename from src/dashboard/src/main/migrations/0055_normalize_thumbnail_mode_selection.py rename to src/archivematica/dashboard/main/migrations/0055_normalize_thumbnail_mode_selection.py diff --git a/src/dashboard/src/main/migrations/0056_transfer_access_system_id.py b/src/archivematica/dashboard/main/migrations/0056_transfer_access_system_id.py similarity index 100% rename from src/dashboard/src/main/migrations/0056_transfer_access_system_id.py rename to src/archivematica/dashboard/main/migrations/0056_transfer_access_system_id.py diff --git a/src/dashboard/src/main/migrations/0057_7zip_no_compression.py b/src/archivematica/dashboard/main/migrations/0057_7zip_no_compression.py similarity index 100% rename from src/dashboard/src/main/migrations/0057_7zip_no_compression.py rename to src/archivematica/dashboard/main/migrations/0057_7zip_no_compression.py diff --git a/src/dashboard/src/main/migrations/0058_fix_unit_variable_pull_link.py b/src/archivematica/dashboard/main/migrations/0058_fix_unit_variable_pull_link.py similarity index 94% rename from src/dashboard/src/main/migrations/0058_fix_unit_variable_pull_link.py rename to src/archivematica/dashboard/main/migrations/0058_fix_unit_variable_pull_link.py index 708d674ce0..ed8600d290 100644 --- a/src/dashboard/src/main/migrations/0058_fix_unit_variable_pull_link.py +++ b/src/archivematica/dashboard/main/migrations/0058_fix_unit_variable_pull_link.py @@ -3,7 +3,8 @@ """ from django.db import migrations -from main.models import Job + +from archivematica.dashboard.main.models import Job # Can't use apps.get_model for this model as we need to access class attributes. diff --git a/src/dashboard/src/main/migrations/0059_siparrange_longblob.py b/src/archivematica/dashboard/main/migrations/0059_siparrange_longblob.py similarity index 95% rename from src/dashboard/src/main/migrations/0059_siparrange_longblob.py rename to src/archivematica/dashboard/main/migrations/0059_siparrange_longblob.py index e4eca2719a..17a38298b1 100644 --- a/src/dashboard/src/main/migrations/0059_siparrange_longblob.py +++ b/src/archivematica/dashboard/main/migrations/0059_siparrange_longblob.py @@ -1,10 +1,11 @@ import uuid -import main.models from django.db import OperationalError from django.db import migrations from django.db import models +import archivematica.dashboard.main.models as main_models + def drop_original_path_unique_key(apps, schema_editor): """Drop the unique key index from `original_path`. @@ -47,7 +48,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="siparrange", name="file_uuid", - field=main.models.UUIDField( + field=main_models.UUIDField( null=True, default=uuid.uuid4, editable=False, diff --git a/src/dashboard/src/main/migrations/0060_delete_orphan_mscl.py b/src/archivematica/dashboard/main/migrations/0060_delete_orphan_mscl.py similarity index 100% rename from src/dashboard/src/main/migrations/0060_delete_orphan_mscl.py rename to src/archivematica/dashboard/main/migrations/0060_delete_orphan_mscl.py diff --git a/src/dashboard/src/main/migrations/0061_create_dataverse_transfer_type.py b/src/archivematica/dashboard/main/migrations/0061_create_dataverse_transfer_type.py similarity index 99% rename from src/dashboard/src/main/migrations/0061_create_dataverse_transfer_type.py rename to src/archivematica/dashboard/main/migrations/0061_create_dataverse_transfer_type.py index 288f2908e9..475d4f2fad 100644 --- a/src/dashboard/src/main/migrations/0061_create_dataverse_transfer_type.py +++ b/src/archivematica/dashboard/main/migrations/0061_create_dataverse_transfer_type.py @@ -17,7 +17,8 @@ """ from django.db import migrations -from main.models import Job + +from archivematica.dashboard.main.models import Job # We can't use apps.get_model for this model as we need to access class # attributes. diff --git a/src/dashboard/src/main/migrations/0062_atom_upload_exit_code.py b/src/archivematica/dashboard/main/migrations/0062_atom_upload_exit_code.py similarity index 96% rename from src/dashboard/src/main/migrations/0062_atom_upload_exit_code.py rename to src/archivematica/dashboard/main/migrations/0062_atom_upload_exit_code.py index a8dc839e02..615fc40116 100644 --- a/src/dashboard/src/main/migrations/0062_atom_upload_exit_code.py +++ b/src/archivematica/dashboard/main/migrations/0062_atom_upload_exit_code.py @@ -4,7 +4,8 @@ """ from django.db import migrations -from main.models import Job + +from archivematica.dashboard.main.models import Job ATOM_DIP_UPLOAD_CONFIG_EXIT_CODE_ZERO = "c9e90d83-533f-44c3-8220-083a6eb91751" diff --git a/src/dashboard/src/main/migrations/0063_update_idtools.py b/src/archivematica/dashboard/main/migrations/0063_update_idtools.py similarity index 100% rename from src/dashboard/src/main/migrations/0063_update_idtools.py rename to src/archivematica/dashboard/main/migrations/0063_update_idtools.py diff --git a/src/dashboard/src/main/migrations/0064_version_number.py b/src/archivematica/dashboard/main/migrations/0064_version_number.py similarity index 100% rename from src/dashboard/src/main/migrations/0064_version_number.py rename to src/archivematica/dashboard/main/migrations/0064_version_number.py diff --git a/src/dashboard/src/main/migrations/0065_version_number.py b/src/archivematica/dashboard/main/migrations/0065_version_number.py similarity index 100% rename from src/dashboard/src/main/migrations/0065_version_number.py rename to src/archivematica/dashboard/main/migrations/0065_version_number.py diff --git a/src/dashboard/src/main/migrations/0066_archivesspace_base_url.py b/src/archivematica/dashboard/main/migrations/0066_archivesspace_base_url.py similarity index 100% rename from src/dashboard/src/main/migrations/0066_archivesspace_base_url.py rename to src/archivematica/dashboard/main/migrations/0066_archivesspace_base_url.py diff --git a/src/dashboard/src/main/migrations/0067_delete_workflow_models.py b/src/archivematica/dashboard/main/migrations/0067_delete_workflow_models.py similarity index 96% rename from src/dashboard/src/main/migrations/0067_delete_workflow_models.py rename to src/archivematica/dashboard/main/migrations/0067_delete_workflow_models.py index 04d9729518..f8e24ab00c 100644 --- a/src/dashboard/src/main/migrations/0067_delete_workflow_models.py +++ b/src/archivematica/dashboard/main/migrations/0067_delete_workflow_models.py @@ -1,8 +1,9 @@ import uuid -import main.models from django.db import migrations +import archivematica.dashboard.main.models as main_models + class Migration(migrations.Migration): dependencies = [("main", "0066_archivesspace_base_url")] @@ -57,7 +58,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="job", name="microservicechainlink", - field=main.models.UUIDField( + field=main_models.UUIDField( max_length=36, null=True, editable=False, @@ -69,7 +70,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="unitvariable", name="microservicechainlink", - field=main.models.UUIDField( + field=main_models.UUIDField( max_length=36, null=True, editable=False, diff --git a/src/dashboard/src/main/migrations/0068_version_number.py b/src/archivematica/dashboard/main/migrations/0068_version_number.py similarity index 100% rename from src/dashboard/src/main/migrations/0068_version_number.py rename to src/archivematica/dashboard/main/migrations/0068_version_number.py diff --git a/src/dashboard/src/main/migrations/0069_remove_atk.py b/src/archivematica/dashboard/main/migrations/0069_remove_atk.py similarity index 100% rename from src/dashboard/src/main/migrations/0069_remove_atk.py rename to src/archivematica/dashboard/main/migrations/0069_remove_atk.py diff --git a/src/dashboard/src/main/migrations/0070_index_jobs.py b/src/archivematica/dashboard/main/migrations/0070_index_jobs.py similarity index 89% rename from src/dashboard/src/main/migrations/0070_index_jobs.py rename to src/archivematica/dashboard/main/migrations/0070_index_jobs.py index 16fdd3730a..30e9062e42 100644 --- a/src/dashboard/src/main/migrations/0070_index_jobs.py +++ b/src/archivematica/dashboard/main/migrations/0070_index_jobs.py @@ -1,8 +1,9 @@ import uuid -import main.models from django.db import migrations +import archivematica.dashboard.main.models as main_models + class Migration(migrations.Migration): dependencies = [("main", "0069_remove_atk")] @@ -11,7 +12,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="job", name="sipuuid", - field=main.models.UUIDField( + field=main_models.UUIDField( max_length=36, db_column="SIPUUID", db_index=True, default=uuid.uuid4 ), ), diff --git a/src/dashboard/src/main/migrations/0071_index_unitvariables_runsql.py b/src/archivematica/dashboard/main/migrations/0071_index_unitvariables_runsql.py similarity index 100% rename from src/dashboard/src/main/migrations/0071_index_unitvariables_runsql.py rename to src/archivematica/dashboard/main/migrations/0071_index_unitvariables_runsql.py diff --git a/src/dashboard/src/main/migrations/0072_index_files.py b/src/archivematica/dashboard/main/migrations/0072_index_files.py similarity index 100% rename from src/dashboard/src/main/migrations/0072_index_files.py rename to src/archivematica/dashboard/main/migrations/0072_index_files.py diff --git a/src/dashboard/src/main/migrations/0073_index_files_runsql.py b/src/archivematica/dashboard/main/migrations/0073_index_files_runsql.py similarity index 100% rename from src/dashboard/src/main/migrations/0073_index_files_runsql.py rename to src/archivematica/dashboard/main/migrations/0073_index_files_runsql.py diff --git a/src/dashboard/src/main/migrations/0074_version_number.py b/src/archivematica/dashboard/main/migrations/0074_version_number.py similarity index 100% rename from src/dashboard/src/main/migrations/0074_version_number.py rename to src/archivematica/dashboard/main/migrations/0074_version_number.py diff --git a/src/dashboard/src/main/migrations/0075_userprofile_system_emails.py b/src/archivematica/dashboard/main/migrations/0075_userprofile_system_emails.py similarity index 100% rename from src/dashboard/src/main/migrations/0075_userprofile_system_emails.py rename to src/archivematica/dashboard/main/migrations/0075_userprofile_system_emails.py diff --git a/src/dashboard/src/main/migrations/0076_version_number.py b/src/archivematica/dashboard/main/migrations/0076_version_number.py similarity index 100% rename from src/dashboard/src/main/migrations/0076_version_number.py rename to src/archivematica/dashboard/main/migrations/0076_version_number.py diff --git a/src/dashboard/src/main/migrations/0077_uuid_fields.py b/src/archivematica/dashboard/main/migrations/0077_uuid_fields.py similarity index 82% rename from src/dashboard/src/main/migrations/0077_uuid_fields.py rename to src/archivematica/dashboard/main/migrations/0077_uuid_fields.py index d36cb0ecae..85169dad85 100644 --- a/src/dashboard/src/main/migrations/0077_uuid_fields.py +++ b/src/archivematica/dashboard/main/migrations/0077_uuid_fields.py @@ -2,9 +2,10 @@ import uuid -import main.models from django.db import migrations +import archivematica.dashboard.main.models as main_models + class Migration(migrations.Migration): dependencies = [("main", "0076_version_number")] @@ -13,7 +14,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="event", name="event_id", - field=main.models.UUIDField( + field=main_models.UUIDField( db_column="eventIdentifierUUID", null=True, unique=True, @@ -23,7 +24,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="job", name="microservicechainlink", - field=main.models.UUIDField( + field=main_models.UUIDField( blank=True, db_column="MicroServiceChainLinksPK", null=True, @@ -33,19 +34,19 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="siparrange", name="file_uuid", - field=main.models.UUIDField( + field=main_models.UUIDField( blank=True, default=uuid.uuid4, null=True, unique=True ), ), migrations.AlterField( model_name="siparrange", name="transfer_uuid", - field=main.models.UUIDField(blank=True, default=uuid.uuid4, null=True), + field=main_models.UUIDField(blank=True, default=uuid.uuid4, null=True), ), migrations.AlterField( model_name="unitvariable", name="microservicechainlink", - field=main.models.UUIDField( + field=main_models.UUIDField( blank=True, db_column="microServiceChainLink", null=True, diff --git a/src/dashboard/src/main/migrations/0078_username_check.py b/src/archivematica/dashboard/main/migrations/0078_username_check.py similarity index 100% rename from src/dashboard/src/main/migrations/0078_username_check.py rename to src/archivematica/dashboard/main/migrations/0078_username_check.py diff --git a/src/dashboard/src/main/migrations/0079_version_number.py b/src/archivematica/dashboard/main/migrations/0079_version_number.py similarity index 100% rename from src/dashboard/src/main/migrations/0079_version_number.py rename to src/archivematica/dashboard/main/migrations/0079_version_number.py diff --git a/src/dashboard/src/main/migrations/0080_delete_software_agent.py b/src/archivematica/dashboard/main/migrations/0080_delete_software_agent.py similarity index 100% rename from src/dashboard/src/main/migrations/0080_delete_software_agent.py rename to src/archivematica/dashboard/main/migrations/0080_delete_software_agent.py diff --git a/src/dashboard/src/main/migrations/0081_package_status.py b/src/archivematica/dashboard/main/migrations/0081_package_status.py similarity index 100% rename from src/dashboard/src/main/migrations/0081_package_status.py rename to src/archivematica/dashboard/main/migrations/0081_package_status.py diff --git a/src/dashboard/src/main/migrations/0082_use_uuidfield.py b/src/archivematica/dashboard/main/migrations/0082_use_uuidfield.py similarity index 83% rename from src/dashboard/src/main/migrations/0082_use_uuidfield.py rename to src/archivematica/dashboard/main/migrations/0082_use_uuidfield.py index e99246d46d..6c9f71e2cc 100644 --- a/src/dashboard/src/main/migrations/0082_use_uuidfield.py +++ b/src/archivematica/dashboard/main/migrations/0082_use_uuidfield.py @@ -1,9 +1,10 @@ # Generated by Django 1.11.29 on 2023-08-11 16:29 import uuid -import main.models from django.db import migrations +import archivematica.dashboard.main.models as main_models + class Migration(migrations.Migration): dependencies = [ @@ -14,7 +15,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="job", name="jobuuid", - field=main.models.UUIDField( + field=main_models.UUIDField( db_column="jobUUID", default=uuid.uuid4, primary_key=True, @@ -24,56 +25,56 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="levelofdescription", name="id", - field=main.models.UUIDField( + field=main_models.UUIDField( db_column="pk", default=uuid.uuid4, primary_key=True, serialize=False ), ), migrations.AlterField( model_name="metadataappliestotype", name="id", - field=main.models.UUIDField( + field=main_models.UUIDField( db_column="pk", default=uuid.uuid4, primary_key=True, serialize=False ), ), migrations.AlterField( model_name="taxonomy", name="id", - field=main.models.UUIDField( + field=main_models.UUIDField( db_column="pk", default=uuid.uuid4, primary_key=True, serialize=False ), ), migrations.AlterField( model_name="taxonomyterm", name="id", - field=main.models.UUIDField( + field=main_models.UUIDField( db_column="pk", default=uuid.uuid4, primary_key=True, serialize=False ), ), migrations.AlterField( model_name="transfermetadatafield", name="id", - field=main.models.UUIDField( + field=main_models.UUIDField( db_column="pk", default=uuid.uuid4, primary_key=True, serialize=False ), ), migrations.AlterField( model_name="transfermetadatafieldvalue", name="id", - field=main.models.UUIDField( + field=main_models.UUIDField( db_column="pk", default=uuid.uuid4, primary_key=True, serialize=False ), ), migrations.AlterField( model_name="transfermetadataset", name="id", - field=main.models.UUIDField( + field=main_models.UUIDField( db_column="pk", default=uuid.uuid4, primary_key=True, serialize=False ), ), migrations.AlterField( model_name="unitvariable", name="id", - field=main.models.UUIDField( + field=main_models.UUIDField( db_column="pk", default=uuid.uuid4, primary_key=True, serialize=False ), ), diff --git a/src/dashboard/src/main/migrations/0083_unpickle_access_target.py b/src/archivematica/dashboard/main/migrations/0083_unpickle_access_target.py similarity index 100% rename from src/dashboard/src/main/migrations/0083_unpickle_access_target.py rename to src/archivematica/dashboard/main/migrations/0083_unpickle_access_target.py diff --git a/src/dashboard/src/main/migrations/0084_alter_job_index_together.py b/src/archivematica/dashboard/main/migrations/0084_alter_job_index_together.py similarity index 100% rename from src/dashboard/src/main/migrations/0084_alter_job_index_together.py rename to src/archivematica/dashboard/main/migrations/0084_alter_job_index_together.py diff --git a/src/dashboard/src/main/templatetags/__init__.py b/src/archivematica/dashboard/main/migrations/__init__.py similarity index 100% rename from src/dashboard/src/main/templatetags/__init__.py rename to src/archivematica/dashboard/main/migrations/__init__.py diff --git a/src/dashboard/src/main/models.py b/src/archivematica/dashboard/main/models.py similarity index 99% rename from src/dashboard/src/main/models.py rename to src/archivematica/dashboard/main/models.py index a7cf03c3f3..35c847196a 100644 --- a/src/dashboard/src/main/models.py +++ b/src/archivematica/dashboard/main/models.py @@ -32,7 +32,8 @@ from django.db.models.signals import post_save from django.dispatch import receiver from django.utils.translation import gettext_lazy as _ -from version import get_preservation_system_identifier + +from archivematica.archivematicaCommon.version import get_preservation_system_identifier # Core Django, alphabetical by import source # Third party dependencies, alphabetical by import source diff --git a/src/dashboard/src/main/signals.py b/src/archivematica/dashboard/main/signals.py similarity index 93% rename from src/dashboard/src/main/signals.py rename to src/archivematica/dashboard/main/signals.py index 5834dcdaea..c8c2112cf3 100644 --- a/src/dashboard/src/main/signals.py +++ b/src/archivematica/dashboard/main/signals.py @@ -2,10 +2,11 @@ from django.db.models.signals import post_delete from django.db.models.signals import post_save from django.dispatch import receiver -from main.models import RightsStatement -from main.models import RightsStatementRightsGranted from prometheus_client import Counter +from archivematica.dashboard.main.models import RightsStatement +from archivematica.dashboard.main.models import RightsStatementRightsGranted + @receiver(post_delete, sender=RightsStatementRightsGranted) def delete_rights_statement(sender, **kwargs): diff --git a/src/dashboard/src/middleware/__init__.py b/src/archivematica/dashboard/main/templatetags/__init__.py similarity index 100% rename from src/dashboard/src/middleware/__init__.py rename to src/archivematica/dashboard/main/templatetags/__init__.py diff --git a/src/dashboard/src/main/templatetags/active.py b/src/archivematica/dashboard/main/templatetags/active.py similarity index 100% rename from src/dashboard/src/main/templatetags/active.py rename to src/archivematica/dashboard/main/templatetags/active.py diff --git a/src/dashboard/src/main/templatetags/breadcrumb.py b/src/archivematica/dashboard/main/templatetags/breadcrumb.py similarity index 100% rename from src/dashboard/src/main/templatetags/breadcrumb.py rename to src/archivematica/dashboard/main/templatetags/breadcrumb.py diff --git a/src/dashboard/src/main/templatetags/forms.py b/src/archivematica/dashboard/main/templatetags/forms.py similarity index 100% rename from src/dashboard/src/main/templatetags/forms.py rename to src/archivematica/dashboard/main/templatetags/forms.py diff --git a/src/dashboard/src/main/templatetags/job_status.py b/src/archivematica/dashboard/main/templatetags/job_status.py similarity index 94% rename from src/dashboard/src/main/templatetags/job_status.py rename to src/archivematica/dashboard/main/templatetags/job_status.py index 88fb9080b7..244d062edf 100644 --- a/src/dashboard/src/main/templatetags/job_status.py +++ b/src/archivematica/dashboard/main/templatetags/job_status.py @@ -15,7 +15,8 @@ # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . from django.template import Library -from main.models import Job + +from archivematica.dashboard.main.models import Job register = Library() diff --git a/src/dashboard/src/main/templatetags/keyvalue.py b/src/archivematica/dashboard/main/templatetags/keyvalue.py similarity index 100% rename from src/dashboard/src/main/templatetags/keyvalue.py rename to src/archivematica/dashboard/main/templatetags/keyvalue.py diff --git a/src/dashboard/src/main/templatetags/lang.py b/src/archivematica/dashboard/main/templatetags/lang.py similarity index 100% rename from src/dashboard/src/main/templatetags/lang.py rename to src/archivematica/dashboard/main/templatetags/lang.py diff --git a/src/dashboard/src/main/templatetags/user.py b/src/archivematica/dashboard/main/templatetags/user.py similarity index 100% rename from src/dashboard/src/main/templatetags/user.py rename to src/archivematica/dashboard/main/templatetags/user.py diff --git a/src/dashboard/src/main/urls.py b/src/archivematica/dashboard/main/urls.py similarity index 97% rename from src/dashboard/src/main/urls.py rename to src/archivematica/dashboard/main/urls.py index 3a6125ac89..d7444b0b0c 100644 --- a/src/dashboard/src/main/urls.py +++ b/src/archivematica/dashboard/main/urls.py @@ -17,7 +17,8 @@ from django.conf import settings from django.urls import path from django.urls import re_path -from main import views + +from archivematica.dashboard.main import views app_name = "main" urlpatterns = [ diff --git a/src/dashboard/src/main/views.py b/src/archivematica/dashboard/main/views.py similarity index 97% rename from src/dashboard/src/main/views.py rename to src/archivematica/dashboard/main/views.py index ff83043fd7..a0044df6bb 100644 --- a/src/dashboard/src/main/views.py +++ b/src/archivematica/dashboard/main/views.py @@ -14,9 +14,6 @@ # # You should have received a copy of the GNU General Public License # along with Archivematica. If not, see . -from archivematicaFunctions import escape -from components import helpers -from contrib.mcp.client import MCPClient from django.conf import settings as django_settings from django.shortcuts import get_object_or_404 from django.shortcuts import redirect @@ -29,7 +26,11 @@ from django.views.decorators.http import last_modified from django.views.i18n import JavaScriptCatalog from lxml import etree -from main import models + +from archivematica.archivematicaCommon.archivematicaFunctions import escape +from archivematica.dashboard.components import helpers +from archivematica.dashboard.contrib.mcp.client import MCPClient +from archivematica.dashboard.main import models @cache_page(86400, key_prefix="js18n-%s" % get_language()) diff --git a/src/dashboard/src/manage.py b/src/archivematica/dashboard/manage.py similarity index 62% rename from src/dashboard/src/manage.py rename to src/archivematica/dashboard/manage.py index 1bd1b11b71..5d34f6faff 100755 --- a/src/dashboard/src/manage.py +++ b/src/archivematica/dashboard/manage.py @@ -3,7 +3,9 @@ import sys if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.local") + os.environ.setdefault( + "DJANGO_SETTINGS_MODULE", "archivematica.dashboard.settings.local" + ) from django.core.management import execute_from_command_line diff --git a/src/dashboard/src/media/css/archival_storage.css b/src/archivematica/dashboard/media/css/archival_storage.css similarity index 100% rename from src/dashboard/src/media/css/archival_storage.css rename to src/archivematica/dashboard/media/css/archival_storage.css diff --git a/src/dashboard/src/media/css/as_match.css b/src/archivematica/dashboard/media/css/as_match.css similarity index 100% rename from src/dashboard/src/media/css/as_match.css rename to src/archivematica/dashboard/media/css/as_match.css diff --git a/src/dashboard/src/media/css/backbone-file-explorer.css b/src/archivematica/dashboard/media/css/backbone-file-explorer.css similarity index 100% rename from src/dashboard/src/media/css/backbone-file-explorer.css rename to src/archivematica/dashboard/media/css/backbone-file-explorer.css diff --git a/src/dashboard/src/media/css/backlog.css b/src/archivematica/dashboard/media/css/backlog.css similarity index 100% rename from src/dashboard/src/media/css/backlog.css rename to src/archivematica/dashboard/media/css/backlog.css diff --git a/src/dashboard/src/media/css/base.css b/src/archivematica/dashboard/media/css/base.css similarity index 100% rename from src/dashboard/src/media/css/base.css rename to src/archivematica/dashboard/media/css/base.css diff --git a/src/dashboard/src/media/css/directory_picker.css b/src/archivematica/dashboard/media/css/directory_picker.css similarity index 100% rename from src/dashboard/src/media/css/directory_picker.css rename to src/archivematica/dashboard/media/css/directory_picker.css diff --git a/src/dashboard/src/media/css/fonts.css b/src/archivematica/dashboard/media/css/fonts.css similarity index 100% rename from src/dashboard/src/media/css/fonts.css rename to src/archivematica/dashboard/media/css/fonts.css diff --git a/src/dashboard/src/media/css/reset.css b/src/archivematica/dashboard/media/css/reset.css similarity index 100% rename from src/dashboard/src/media/css/reset.css rename to src/archivematica/dashboard/media/css/reset.css diff --git a/src/dashboard/src/media/css/search.css b/src/archivematica/dashboard/media/css/search.css similarity index 100% rename from src/dashboard/src/media/css/search.css rename to src/archivematica/dashboard/media/css/search.css diff --git a/src/dashboard/src/media/css/style.css b/src/archivematica/dashboard/media/css/style.css similarity index 100% rename from src/dashboard/src/media/css/style.css rename to src/archivematica/dashboard/media/css/style.css diff --git a/src/dashboard/src/media/css/transfer_grid.css b/src/archivematica/dashboard/media/css/transfer_grid.css similarity index 100% rename from src/dashboard/src/media/css/transfer_grid.css rename to src/archivematica/dashboard/media/css/transfer_grid.css diff --git a/src/dashboard/src/media/images/1x1-pixel.png b/src/archivematica/dashboard/media/images/1x1-pixel.png similarity index 100% rename from src/dashboard/src/media/images/1x1-pixel.png rename to src/archivematica/dashboard/media/images/1x1-pixel.png diff --git a/src/dashboard/src/media/images/accept.png b/src/archivematica/dashboard/media/images/accept.png similarity index 100% rename from src/dashboard/src/media/images/accept.png rename to src/archivematica/dashboard/media/images/accept.png diff --git a/src/dashboard/src/media/images/ajax-loader.gif b/src/archivematica/dashboard/media/images/ajax-loader.gif similarity index 100% rename from src/dashboard/src/media/images/ajax-loader.gif rename to src/archivematica/dashboard/media/images/ajax-loader.gif diff --git a/src/dashboard/src/media/images/arrow_refresh.png b/src/archivematica/dashboard/media/images/arrow_refresh.png similarity index 100% rename from src/dashboard/src/media/images/arrow_refresh.png rename to src/archivematica/dashboard/media/images/arrow_refresh.png diff --git a/src/dashboard/src/media/images/attach.png b/src/archivematica/dashboard/media/images/attach.png similarity index 100% rename from src/dashboard/src/media/images/attach.png rename to src/archivematica/dashboard/media/images/attach.png diff --git a/src/dashboard/src/media/images/bell.png b/src/archivematica/dashboard/media/images/bell.png similarity index 100% rename from src/dashboard/src/media/images/bell.png rename to src/archivematica/dashboard/media/images/bell.png diff --git a/src/dashboard/src/media/images/bullet_arrow_down.png b/src/archivematica/dashboard/media/images/bullet_arrow_down.png similarity index 100% rename from src/dashboard/src/media/images/bullet_arrow_down.png rename to src/archivematica/dashboard/media/images/bullet_arrow_down.png diff --git a/src/dashboard/src/media/images/bullet_arrow_up.png b/src/archivematica/dashboard/media/images/bullet_arrow_up.png similarity index 100% rename from src/dashboard/src/media/images/bullet_arrow_up.png rename to src/archivematica/dashboard/media/images/bullet_arrow_up.png diff --git a/src/dashboard/src/media/images/bullet_delete.png b/src/archivematica/dashboard/media/images/bullet_delete.png similarity index 100% rename from src/dashboard/src/media/images/bullet_delete.png rename to src/archivematica/dashboard/media/images/bullet_delete.png diff --git a/src/dashboard/src/media/images/bullet_green.png b/src/archivematica/dashboard/media/images/bullet_green.png similarity index 100% rename from src/dashboard/src/media/images/bullet_green.png rename to src/archivematica/dashboard/media/images/bullet_green.png diff --git a/src/dashboard/src/media/images/bullet_orange.png b/src/archivematica/dashboard/media/images/bullet_orange.png similarity index 100% rename from src/dashboard/src/media/images/bullet_orange.png rename to src/archivematica/dashboard/media/images/bullet_orange.png diff --git a/src/dashboard/src/media/images/cancel.png b/src/archivematica/dashboard/media/images/cancel.png similarity index 100% rename from src/dashboard/src/media/images/cancel.png rename to src/archivematica/dashboard/media/images/cancel.png diff --git a/src/dashboard/src/media/images/clock.png b/src/archivematica/dashboard/media/images/clock.png similarity index 100% rename from src/dashboard/src/media/images/clock.png rename to src/archivematica/dashboard/media/images/clock.png diff --git a/src/dashboard/src/media/images/cog.png b/src/archivematica/dashboard/media/images/cog.png similarity index 100% rename from src/dashboard/src/media/images/cog.png rename to src/archivematica/dashboard/media/images/cog.png diff --git a/src/dashboard/src/media/images/delete.png b/src/archivematica/dashboard/media/images/delete.png similarity index 100% rename from src/dashboard/src/media/images/delete.png rename to src/archivematica/dashboard/media/images/delete.png diff --git a/src/dashboard/src/media/images/error.png b/src/archivematica/dashboard/media/images/error.png similarity index 100% rename from src/dashboard/src/media/images/error.png rename to src/archivematica/dashboard/media/images/error.png diff --git a/src/dashboard/src/media/images/favicon.ico b/src/archivematica/dashboard/media/images/favicon.ico similarity index 100% rename from src/dashboard/src/media/images/favicon.ico rename to src/archivematica/dashboard/media/images/favicon.ico diff --git a/src/dashboard/src/media/images/filefolder.png b/src/archivematica/dashboard/media/images/filefolder.png similarity index 100% rename from src/dashboard/src/media/images/filefolder.png rename to src/archivematica/dashboard/media/images/filefolder.png diff --git a/src/dashboard/src/media/images/filefolderopen.png b/src/archivematica/dashboard/media/images/filefolderopen.png similarity index 100% rename from src/dashboard/src/media/images/filefolderopen.png rename to src/archivematica/dashboard/media/images/filefolderopen.png diff --git a/src/dashboard/src/media/images/folder.png b/src/archivematica/dashboard/media/images/folder.png similarity index 100% rename from src/dashboard/src/media/images/folder.png rename to src/archivematica/dashboard/media/images/folder.png diff --git a/src/dashboard/src/media/images/logo_header.png b/src/archivematica/dashboard/media/images/logo_header.png similarity index 100% rename from src/dashboard/src/media/images/logo_header.png rename to src/archivematica/dashboard/media/images/logo_header.png diff --git a/src/dashboard/src/media/images/logo_login.gif b/src/archivematica/dashboard/media/images/logo_login.gif similarity index 100% rename from src/dashboard/src/media/images/logo_login.gif rename to src/archivematica/dashboard/media/images/logo_login.gif diff --git a/src/dashboard/src/media/images/logo_welcome.gif b/src/archivematica/dashboard/media/images/logo_welcome.gif similarity index 100% rename from src/dashboard/src/media/images/logo_welcome.gif rename to src/archivematica/dashboard/media/images/logo_welcome.gif diff --git a/src/dashboard/src/media/images/package_go.png b/src/archivematica/dashboard/media/images/package_go.png similarity index 100% rename from src/dashboard/src/media/images/package_go.png rename to src/archivematica/dashboard/media/images/package_go.png diff --git a/src/dashboard/src/media/images/pencil_add.png b/src/archivematica/dashboard/media/images/pencil_add.png similarity index 100% rename from src/dashboard/src/media/images/pencil_add.png rename to src/archivematica/dashboard/media/images/pencil_add.png diff --git a/src/dashboard/src/media/images/table_edit.png b/src/archivematica/dashboard/media/images/table_edit.png similarity index 100% rename from src/dashboard/src/media/images/table_edit.png rename to src/archivematica/dashboard/media/images/table_edit.png diff --git a/src/dashboard/src/media/images/tick.png b/src/archivematica/dashboard/media/images/tick.png similarity index 100% rename from src/dashboard/src/media/images/tick.png rename to src/archivematica/dashboard/media/images/tick.png diff --git a/src/dashboard/src/media/images/user_comment.png b/src/archivematica/dashboard/media/images/user_comment.png similarity index 100% rename from src/dashboard/src/media/images/user_comment.png rename to src/archivematica/dashboard/media/images/user_comment.png diff --git a/src/dashboard/src/media/images/vcard_edit.png b/src/archivematica/dashboard/media/images/vcard_edit.png similarity index 100% rename from src/dashboard/src/media/images/vcard_edit.png rename to src/archivematica/dashboard/media/images/vcard_edit.png diff --git a/src/dashboard/src/media/images/zoom.png b/src/archivematica/dashboard/media/images/zoom.png similarity index 100% rename from src/dashboard/src/media/images/zoom.png rename to src/archivematica/dashboard/media/images/zoom.png diff --git a/src/dashboard/src/media/images/zoom_out.png b/src/archivematica/dashboard/media/images/zoom_out.png similarity index 100% rename from src/dashboard/src/media/images/zoom_out.png rename to src/archivematica/dashboard/media/images/zoom_out.png diff --git a/src/dashboard/src/media/js/administration/atom_levels_of_description.js b/src/archivematica/dashboard/media/js/administration/atom_levels_of_description.js similarity index 100% rename from src/dashboard/src/media/js/administration/atom_levels_of_description.js rename to src/archivematica/dashboard/media/js/administration/atom_levels_of_description.js diff --git a/src/dashboard/src/media/js/advanced-search-query-creator.js b/src/archivematica/dashboard/media/js/advanced-search-query-creator.js similarity index 100% rename from src/dashboard/src/media/js/advanced-search-query-creator.js rename to src/archivematica/dashboard/media/js/advanced-search-query-creator.js diff --git a/src/dashboard/src/media/js/archival_storage/archival_storage_search.js b/src/archivematica/dashboard/media/js/archival_storage/archival_storage_search.js similarity index 100% rename from src/dashboard/src/media/js/archival_storage/archival_storage_search.js rename to src/archivematica/dashboard/media/js/archival_storage/archival_storage_search.js diff --git a/src/dashboard/src/media/js/backlog/backlog-search.js b/src/archivematica/dashboard/media/js/backlog/backlog-search.js similarity index 100% rename from src/dashboard/src/media/js/backlog/backlog-search.js rename to src/archivematica/dashboard/media/js/backlog/backlog-search.js diff --git a/src/dashboard/src/media/js/contextual_help.js b/src/archivematica/dashboard/media/js/contextual_help.js similarity index 100% rename from src/dashboard/src/media/js/contextual_help.js rename to src/archivematica/dashboard/media/js/contextual_help.js diff --git a/src/dashboard/src/media/js/directory_picker.js b/src/archivematica/dashboard/media/js/directory_picker.js similarity index 100% rename from src/dashboard/src/media/js/directory_picker.js rename to src/archivematica/dashboard/media/js/directory_picker.js diff --git a/src/dashboard/src/media/js/file-explorer.js b/src/archivematica/dashboard/media/js/file-explorer.js similarity index 100% rename from src/dashboard/src/media/js/file-explorer.js rename to src/archivematica/dashboard/media/js/file-explorer.js diff --git a/src/dashboard/src/media/js/file_browser.js b/src/archivematica/dashboard/media/js/file_browser.js similarity index 100% rename from src/dashboard/src/media/js/file_browser.js rename to src/archivematica/dashboard/media/js/file_browser.js diff --git a/src/dashboard/src/media/js/ingest.js b/src/archivematica/dashboard/media/js/ingest.js similarity index 100% rename from src/dashboard/src/media/js/ingest.js rename to src/archivematica/dashboard/media/js/ingest.js diff --git a/src/dashboard/src/media/js/ingest/aip_browser.js b/src/archivematica/dashboard/media/js/ingest/aip_browser.js similarity index 100% rename from src/dashboard/src/media/js/ingest/aip_browser.js rename to src/archivematica/dashboard/media/js/ingest/aip_browser.js diff --git a/src/dashboard/src/media/js/ingest/as_matcher.js b/src/archivematica/dashboard/media/js/ingest/as_matcher.js similarity index 100% rename from src/dashboard/src/media/js/ingest/as_matcher.js rename to src/archivematica/dashboard/media/js/ingest/as_matcher.js diff --git a/src/dashboard/src/media/js/ingest/as_matcher_setup.js b/src/archivematica/dashboard/media/js/ingest/as_matcher_setup.js similarity index 100% rename from src/dashboard/src/media/js/ingest/as_matcher_setup.js rename to src/archivematica/dashboard/media/js/ingest/as_matcher_setup.js diff --git a/src/dashboard/src/media/js/ingest/backlog.js b/src/archivematica/dashboard/media/js/ingest/backlog.js similarity index 100% rename from src/dashboard/src/media/js/ingest/backlog.js rename to src/archivematica/dashboard/media/js/ingest/backlog.js diff --git a/src/dashboard/src/media/js/ingest/metadata_form.js b/src/archivematica/dashboard/media/js/ingest/metadata_form.js similarity index 100% rename from src/dashboard/src/media/js/ingest/metadata_form.js rename to src/archivematica/dashboard/media/js/ingest/metadata_form.js diff --git a/src/dashboard/src/media/js/ingest/normalization_report.js b/src/archivematica/dashboard/media/js/ingest/normalization_report.js similarity index 100% rename from src/dashboard/src/media/js/ingest/normalization_report.js rename to src/archivematica/dashboard/media/js/ingest/normalization_report.js diff --git a/src/dashboard/src/media/js/jobs.js b/src/archivematica/dashboard/media/js/jobs.js similarity index 100% rename from src/dashboard/src/media/js/jobs.js rename to src/archivematica/dashboard/media/js/jobs.js diff --git a/src/dashboard/src/media/js/misc.js b/src/archivematica/dashboard/media/js/misc.js similarity index 76% rename from src/dashboard/src/media/js/misc.js rename to src/archivematica/dashboard/media/js/misc.js index be55b64073..8300db7e75 100644 --- a/src/dashboard/src/media/js/misc.js +++ b/src/archivematica/dashboard/media/js/misc.js @@ -138,4 +138,28 @@ $(document).ready( $(this).closest('.content').hide(); $(this).closest('.preview-help-text').children('.preview').show(); }); + + // Set up the button which allows users to copy the API key to the clipboard. + $('#copy-api-key-button').tooltip().click(function () { + var button = $(this); + navigator.clipboard.writeText($('#api-key').val()).then(function () { + var button_icon = $('#copy-button-icon'); + var icon_original_class = button_icon.data('icon-original-class'); + var icon_clicked_class = button_icon.data('icon-clicked-class'); + + // Update the button icon. + button_icon.removeClass(icon_original_class).addClass(icon_clicked_class).css('color', 'green'); + + // Update the button tooltip. + button.tooltip('option', 'content', button.data('clicked-label')).tooltip('open'); + + // Reset the button after 2 seconds. + setTimeout(function () { + button_icon.removeClass(icon_clicked_class).addClass(icon_original_class).css('color', ''); + button.tooltip('option', 'content', button.data('original-label')).tooltip('close'); + }, 2000); + }).catch(function (err) { + console.error('Failed to copy API key to clipboard: ', err); + }); + }); }); diff --git a/src/dashboard/src/media/js/online.js b/src/archivematica/dashboard/media/js/online.js similarity index 100% rename from src/dashboard/src/media/js/online.js rename to src/archivematica/dashboard/media/js/online.js diff --git a/src/dashboard/src/media/js/repeating-ajax-data.js b/src/archivematica/dashboard/media/js/repeating-ajax-data.js similarity index 100% rename from src/dashboard/src/media/js/repeating-ajax-data.js rename to src/archivematica/dashboard/media/js/repeating-ajax-data.js diff --git a/src/dashboard/src/media/js/rights_edit.js b/src/archivematica/dashboard/media/js/rights_edit.js similarity index 100% rename from src/dashboard/src/media/js/rights_edit.js rename to src/archivematica/dashboard/media/js/rights_edit.js diff --git a/src/dashboard/src/media/js/status.js b/src/archivematica/dashboard/media/js/status.js similarity index 100% rename from src/dashboard/src/media/js/status.js rename to src/archivematica/dashboard/media/js/status.js diff --git a/src/dashboard/src/media/js/transfer.js b/src/archivematica/dashboard/media/js/transfer.js similarity index 100% rename from src/dashboard/src/media/js/transfer.js rename to src/archivematica/dashboard/media/js/transfer.js diff --git a/src/dashboard/src/media/js/transfer/component_directory_select.js b/src/archivematica/dashboard/media/js/transfer/component_directory_select.js similarity index 100% rename from src/dashboard/src/media/js/transfer/component_directory_select.js rename to src/archivematica/dashboard/media/js/transfer/component_directory_select.js diff --git a/src/dashboard/src/media/js/vendor/base64.js b/src/archivematica/dashboard/media/js/vendor/base64.js similarity index 100% rename from src/dashboard/src/media/js/vendor/base64.js rename to src/archivematica/dashboard/media/js/vendor/base64.js diff --git a/src/dashboard/src/media/vendor/backbone.js b/src/archivematica/dashboard/media/vendor/backbone.js similarity index 100% rename from src/dashboard/src/media/vendor/backbone.js rename to src/archivematica/dashboard/media/vendor/backbone.js diff --git a/src/dashboard/src/media/vendor/datatables/css/buttons.bootstrap.min.css b/src/archivematica/dashboard/media/vendor/datatables/css/buttons.bootstrap.min.css similarity index 100% rename from src/dashboard/src/media/vendor/datatables/css/buttons.bootstrap.min.css rename to src/archivematica/dashboard/media/vendor/datatables/css/buttons.bootstrap.min.css diff --git a/src/dashboard/src/media/vendor/datatables/css/jquery.dataTables.css b/src/archivematica/dashboard/media/vendor/datatables/css/jquery.dataTables.css similarity index 100% rename from src/dashboard/src/media/vendor/datatables/css/jquery.dataTables.css rename to src/archivematica/dashboard/media/vendor/datatables/css/jquery.dataTables.css diff --git a/src/dashboard/src/media/vendor/datatables/images/favicon.ico b/src/archivematica/dashboard/media/vendor/datatables/images/favicon.ico similarity index 100% rename from src/dashboard/src/media/vendor/datatables/images/favicon.ico rename to src/archivematica/dashboard/media/vendor/datatables/images/favicon.ico diff --git a/src/dashboard/src/media/vendor/datatables/images/sort_asc.png b/src/archivematica/dashboard/media/vendor/datatables/images/sort_asc.png similarity index 100% rename from src/dashboard/src/media/vendor/datatables/images/sort_asc.png rename to src/archivematica/dashboard/media/vendor/datatables/images/sort_asc.png diff --git a/src/dashboard/src/media/vendor/datatables/images/sort_asc_disabled.png b/src/archivematica/dashboard/media/vendor/datatables/images/sort_asc_disabled.png similarity index 100% rename from src/dashboard/src/media/vendor/datatables/images/sort_asc_disabled.png rename to src/archivematica/dashboard/media/vendor/datatables/images/sort_asc_disabled.png diff --git a/src/dashboard/src/media/vendor/datatables/images/sort_both.png b/src/archivematica/dashboard/media/vendor/datatables/images/sort_both.png similarity index 100% rename from src/dashboard/src/media/vendor/datatables/images/sort_both.png rename to src/archivematica/dashboard/media/vendor/datatables/images/sort_both.png diff --git a/src/dashboard/src/media/vendor/datatables/images/sort_desc.png b/src/archivematica/dashboard/media/vendor/datatables/images/sort_desc.png similarity index 100% rename from src/dashboard/src/media/vendor/datatables/images/sort_desc.png rename to src/archivematica/dashboard/media/vendor/datatables/images/sort_desc.png diff --git a/src/dashboard/src/media/vendor/datatables/images/sort_desc_disabled.png b/src/archivematica/dashboard/media/vendor/datatables/images/sort_desc_disabled.png similarity index 100% rename from src/dashboard/src/media/vendor/datatables/images/sort_desc_disabled.png rename to src/archivematica/dashboard/media/vendor/datatables/images/sort_desc_disabled.png diff --git a/src/dashboard/src/media/vendor/datatables/js/buttons.bootstrap.min.js b/src/archivematica/dashboard/media/vendor/datatables/js/buttons.bootstrap.min.js similarity index 100% rename from src/dashboard/src/media/vendor/datatables/js/buttons.bootstrap.min.js rename to src/archivematica/dashboard/media/vendor/datatables/js/buttons.bootstrap.min.js diff --git a/src/dashboard/src/media/vendor/datatables/js/buttons.colVis.min.js b/src/archivematica/dashboard/media/vendor/datatables/js/buttons.colVis.min.js similarity index 100% rename from src/dashboard/src/media/vendor/datatables/js/buttons.colVis.min.js rename to src/archivematica/dashboard/media/vendor/datatables/js/buttons.colVis.min.js diff --git a/src/dashboard/src/media/vendor/datatables/js/dataTables.buttons.min.js b/src/archivematica/dashboard/media/vendor/datatables/js/dataTables.buttons.min.js similarity index 100% rename from src/dashboard/src/media/vendor/datatables/js/dataTables.buttons.min.js rename to src/archivematica/dashboard/media/vendor/datatables/js/dataTables.buttons.min.js diff --git a/src/dashboard/src/media/vendor/datatables/js/jquery.dataTables.min.js b/src/archivematica/dashboard/media/vendor/datatables/js/jquery.dataTables.min.js similarity index 100% rename from src/dashboard/src/media/vendor/datatables/js/jquery.dataTables.min.js rename to src/archivematica/dashboard/media/vendor/datatables/js/jquery.dataTables.min.js diff --git a/src/dashboard/src/media/vendor/font-awesome/.npmignore b/src/archivematica/dashboard/media/vendor/font-awesome/.npmignore similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/.npmignore rename to src/archivematica/dashboard/media/vendor/font-awesome/.npmignore diff --git a/src/dashboard/src/media/vendor/font-awesome/HELP-US-OUT.txt b/src/archivematica/dashboard/media/vendor/font-awesome/HELP-US-OUT.txt similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/HELP-US-OUT.txt rename to src/archivematica/dashboard/media/vendor/font-awesome/HELP-US-OUT.txt diff --git a/src/dashboard/src/media/vendor/font-awesome/README.md b/src/archivematica/dashboard/media/vendor/font-awesome/README.md similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/README.md rename to src/archivematica/dashboard/media/vendor/font-awesome/README.md diff --git a/src/dashboard/src/media/vendor/font-awesome/css/font-awesome.css b/src/archivematica/dashboard/media/vendor/font-awesome/css/font-awesome.css similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/css/font-awesome.css rename to src/archivematica/dashboard/media/vendor/font-awesome/css/font-awesome.css diff --git a/src/dashboard/src/media/vendor/font-awesome/css/font-awesome.css.map b/src/archivematica/dashboard/media/vendor/font-awesome/css/font-awesome.css.map similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/css/font-awesome.css.map rename to src/archivematica/dashboard/media/vendor/font-awesome/css/font-awesome.css.map diff --git a/src/dashboard/src/media/vendor/font-awesome/css/font-awesome.min.css b/src/archivematica/dashboard/media/vendor/font-awesome/css/font-awesome.min.css similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/css/font-awesome.min.css rename to src/archivematica/dashboard/media/vendor/font-awesome/css/font-awesome.min.css diff --git a/src/dashboard/src/media/vendor/font-awesome/fonts/FontAwesome.otf b/src/archivematica/dashboard/media/vendor/font-awesome/fonts/FontAwesome.otf similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/fonts/FontAwesome.otf rename to src/archivematica/dashboard/media/vendor/font-awesome/fonts/FontAwesome.otf diff --git a/src/dashboard/src/media/vendor/font-awesome/fonts/fontawesome-webfont.eot b/src/archivematica/dashboard/media/vendor/font-awesome/fonts/fontawesome-webfont.eot similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/fonts/fontawesome-webfont.eot rename to src/archivematica/dashboard/media/vendor/font-awesome/fonts/fontawesome-webfont.eot diff --git a/src/dashboard/src/media/vendor/font-awesome/fonts/fontawesome-webfont.svg b/src/archivematica/dashboard/media/vendor/font-awesome/fonts/fontawesome-webfont.svg similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/fonts/fontawesome-webfont.svg rename to src/archivematica/dashboard/media/vendor/font-awesome/fonts/fontawesome-webfont.svg diff --git a/src/dashboard/src/media/vendor/font-awesome/fonts/fontawesome-webfont.ttf b/src/archivematica/dashboard/media/vendor/font-awesome/fonts/fontawesome-webfont.ttf similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/fonts/fontawesome-webfont.ttf rename to src/archivematica/dashboard/media/vendor/font-awesome/fonts/fontawesome-webfont.ttf diff --git a/src/dashboard/src/media/vendor/font-awesome/fonts/fontawesome-webfont.woff b/src/archivematica/dashboard/media/vendor/font-awesome/fonts/fontawesome-webfont.woff similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/fonts/fontawesome-webfont.woff rename to src/archivematica/dashboard/media/vendor/font-awesome/fonts/fontawesome-webfont.woff diff --git a/src/dashboard/src/media/vendor/font-awesome/fonts/fontawesome-webfont.woff2 b/src/archivematica/dashboard/media/vendor/font-awesome/fonts/fontawesome-webfont.woff2 similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/fonts/fontawesome-webfont.woff2 rename to src/archivematica/dashboard/media/vendor/font-awesome/fonts/fontawesome-webfont.woff2 diff --git a/src/dashboard/src/media/vendor/font-awesome/less/animated.less b/src/archivematica/dashboard/media/vendor/font-awesome/less/animated.less similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/less/animated.less rename to src/archivematica/dashboard/media/vendor/font-awesome/less/animated.less diff --git a/src/dashboard/src/media/vendor/font-awesome/less/bordered-pulled.less b/src/archivematica/dashboard/media/vendor/font-awesome/less/bordered-pulled.less similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/less/bordered-pulled.less rename to src/archivematica/dashboard/media/vendor/font-awesome/less/bordered-pulled.less diff --git a/src/dashboard/src/media/vendor/font-awesome/less/core.less b/src/archivematica/dashboard/media/vendor/font-awesome/less/core.less similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/less/core.less rename to src/archivematica/dashboard/media/vendor/font-awesome/less/core.less diff --git a/src/dashboard/src/media/vendor/font-awesome/less/fixed-width.less b/src/archivematica/dashboard/media/vendor/font-awesome/less/fixed-width.less similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/less/fixed-width.less rename to src/archivematica/dashboard/media/vendor/font-awesome/less/fixed-width.less diff --git a/src/dashboard/src/media/vendor/font-awesome/less/font-awesome.less b/src/archivematica/dashboard/media/vendor/font-awesome/less/font-awesome.less similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/less/font-awesome.less rename to src/archivematica/dashboard/media/vendor/font-awesome/less/font-awesome.less diff --git a/src/dashboard/src/media/vendor/font-awesome/less/icons.less b/src/archivematica/dashboard/media/vendor/font-awesome/less/icons.less similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/less/icons.less rename to src/archivematica/dashboard/media/vendor/font-awesome/less/icons.less diff --git a/src/dashboard/src/media/vendor/font-awesome/less/larger.less b/src/archivematica/dashboard/media/vendor/font-awesome/less/larger.less similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/less/larger.less rename to src/archivematica/dashboard/media/vendor/font-awesome/less/larger.less diff --git a/src/dashboard/src/media/vendor/font-awesome/less/list.less b/src/archivematica/dashboard/media/vendor/font-awesome/less/list.less similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/less/list.less rename to src/archivematica/dashboard/media/vendor/font-awesome/less/list.less diff --git a/src/dashboard/src/media/vendor/font-awesome/less/mixins.less b/src/archivematica/dashboard/media/vendor/font-awesome/less/mixins.less similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/less/mixins.less rename to src/archivematica/dashboard/media/vendor/font-awesome/less/mixins.less diff --git a/src/dashboard/src/media/vendor/font-awesome/less/path.less b/src/archivematica/dashboard/media/vendor/font-awesome/less/path.less similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/less/path.less rename to src/archivematica/dashboard/media/vendor/font-awesome/less/path.less diff --git a/src/dashboard/src/media/vendor/font-awesome/less/rotated-flipped.less b/src/archivematica/dashboard/media/vendor/font-awesome/less/rotated-flipped.less similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/less/rotated-flipped.less rename to src/archivematica/dashboard/media/vendor/font-awesome/less/rotated-flipped.less diff --git a/src/dashboard/src/media/vendor/font-awesome/less/screen-reader.less b/src/archivematica/dashboard/media/vendor/font-awesome/less/screen-reader.less similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/less/screen-reader.less rename to src/archivematica/dashboard/media/vendor/font-awesome/less/screen-reader.less diff --git a/src/dashboard/src/media/vendor/font-awesome/less/stacked.less b/src/archivematica/dashboard/media/vendor/font-awesome/less/stacked.less similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/less/stacked.less rename to src/archivematica/dashboard/media/vendor/font-awesome/less/stacked.less diff --git a/src/dashboard/src/media/vendor/font-awesome/less/variables.less b/src/archivematica/dashboard/media/vendor/font-awesome/less/variables.less similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/less/variables.less rename to src/archivematica/dashboard/media/vendor/font-awesome/less/variables.less diff --git a/src/dashboard/src/media/vendor/font-awesome/package.json b/src/archivematica/dashboard/media/vendor/font-awesome/package.json similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/package.json rename to src/archivematica/dashboard/media/vendor/font-awesome/package.json diff --git a/src/dashboard/src/media/vendor/font-awesome/scss/_animated.scss b/src/archivematica/dashboard/media/vendor/font-awesome/scss/_animated.scss similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/scss/_animated.scss rename to src/archivematica/dashboard/media/vendor/font-awesome/scss/_animated.scss diff --git a/src/dashboard/src/media/vendor/font-awesome/scss/_bordered-pulled.scss b/src/archivematica/dashboard/media/vendor/font-awesome/scss/_bordered-pulled.scss similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/scss/_bordered-pulled.scss rename to src/archivematica/dashboard/media/vendor/font-awesome/scss/_bordered-pulled.scss diff --git a/src/dashboard/src/media/vendor/font-awesome/scss/_core.scss b/src/archivematica/dashboard/media/vendor/font-awesome/scss/_core.scss similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/scss/_core.scss rename to src/archivematica/dashboard/media/vendor/font-awesome/scss/_core.scss diff --git a/src/dashboard/src/media/vendor/font-awesome/scss/_fixed-width.scss b/src/archivematica/dashboard/media/vendor/font-awesome/scss/_fixed-width.scss similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/scss/_fixed-width.scss rename to src/archivematica/dashboard/media/vendor/font-awesome/scss/_fixed-width.scss diff --git a/src/dashboard/src/media/vendor/font-awesome/scss/_icons.scss b/src/archivematica/dashboard/media/vendor/font-awesome/scss/_icons.scss similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/scss/_icons.scss rename to src/archivematica/dashboard/media/vendor/font-awesome/scss/_icons.scss diff --git a/src/dashboard/src/media/vendor/font-awesome/scss/_larger.scss b/src/archivematica/dashboard/media/vendor/font-awesome/scss/_larger.scss similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/scss/_larger.scss rename to src/archivematica/dashboard/media/vendor/font-awesome/scss/_larger.scss diff --git a/src/dashboard/src/media/vendor/font-awesome/scss/_list.scss b/src/archivematica/dashboard/media/vendor/font-awesome/scss/_list.scss similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/scss/_list.scss rename to src/archivematica/dashboard/media/vendor/font-awesome/scss/_list.scss diff --git a/src/dashboard/src/media/vendor/font-awesome/scss/_mixins.scss b/src/archivematica/dashboard/media/vendor/font-awesome/scss/_mixins.scss similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/scss/_mixins.scss rename to src/archivematica/dashboard/media/vendor/font-awesome/scss/_mixins.scss diff --git a/src/dashboard/src/media/vendor/font-awesome/scss/_path.scss b/src/archivematica/dashboard/media/vendor/font-awesome/scss/_path.scss similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/scss/_path.scss rename to src/archivematica/dashboard/media/vendor/font-awesome/scss/_path.scss diff --git a/src/dashboard/src/media/vendor/font-awesome/scss/_rotated-flipped.scss b/src/archivematica/dashboard/media/vendor/font-awesome/scss/_rotated-flipped.scss similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/scss/_rotated-flipped.scss rename to src/archivematica/dashboard/media/vendor/font-awesome/scss/_rotated-flipped.scss diff --git a/src/dashboard/src/media/vendor/font-awesome/scss/_screen-reader.scss b/src/archivematica/dashboard/media/vendor/font-awesome/scss/_screen-reader.scss similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/scss/_screen-reader.scss rename to src/archivematica/dashboard/media/vendor/font-awesome/scss/_screen-reader.scss diff --git a/src/dashboard/src/media/vendor/font-awesome/scss/_stacked.scss b/src/archivematica/dashboard/media/vendor/font-awesome/scss/_stacked.scss similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/scss/_stacked.scss rename to src/archivematica/dashboard/media/vendor/font-awesome/scss/_stacked.scss diff --git a/src/dashboard/src/media/vendor/font-awesome/scss/_variables.scss b/src/archivematica/dashboard/media/vendor/font-awesome/scss/_variables.scss similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/scss/_variables.scss rename to src/archivematica/dashboard/media/vendor/font-awesome/scss/_variables.scss diff --git a/src/dashboard/src/media/vendor/font-awesome/scss/font-awesome.scss b/src/archivematica/dashboard/media/vendor/font-awesome/scss/font-awesome.scss similarity index 100% rename from src/dashboard/src/media/vendor/font-awesome/scss/font-awesome.scss rename to src/archivematica/dashboard/media/vendor/font-awesome/scss/font-awesome.scss diff --git a/src/dashboard/src/media/vendor/jquery-ui/images/ui-icons_444444_256x240.png b/src/archivematica/dashboard/media/vendor/jquery-ui/images/ui-icons_444444_256x240.png similarity index 100% rename from src/dashboard/src/media/vendor/jquery-ui/images/ui-icons_444444_256x240.png rename to src/archivematica/dashboard/media/vendor/jquery-ui/images/ui-icons_444444_256x240.png diff --git a/src/dashboard/src/media/vendor/jquery-ui/images/ui-icons_555555_256x240.png b/src/archivematica/dashboard/media/vendor/jquery-ui/images/ui-icons_555555_256x240.png similarity index 100% rename from src/dashboard/src/media/vendor/jquery-ui/images/ui-icons_555555_256x240.png rename to src/archivematica/dashboard/media/vendor/jquery-ui/images/ui-icons_555555_256x240.png diff --git a/src/dashboard/src/media/vendor/jquery-ui/images/ui-icons_777620_256x240.png b/src/archivematica/dashboard/media/vendor/jquery-ui/images/ui-icons_777620_256x240.png similarity index 100% rename from src/dashboard/src/media/vendor/jquery-ui/images/ui-icons_777620_256x240.png rename to src/archivematica/dashboard/media/vendor/jquery-ui/images/ui-icons_777620_256x240.png diff --git a/src/dashboard/src/media/vendor/jquery-ui/images/ui-icons_777777_256x240.png b/src/archivematica/dashboard/media/vendor/jquery-ui/images/ui-icons_777777_256x240.png similarity index 100% rename from src/dashboard/src/media/vendor/jquery-ui/images/ui-icons_777777_256x240.png rename to src/archivematica/dashboard/media/vendor/jquery-ui/images/ui-icons_777777_256x240.png diff --git a/src/dashboard/src/media/vendor/jquery-ui/images/ui-icons_cc0000_256x240.png b/src/archivematica/dashboard/media/vendor/jquery-ui/images/ui-icons_cc0000_256x240.png similarity index 100% rename from src/dashboard/src/media/vendor/jquery-ui/images/ui-icons_cc0000_256x240.png rename to src/archivematica/dashboard/media/vendor/jquery-ui/images/ui-icons_cc0000_256x240.png diff --git a/src/dashboard/src/media/vendor/jquery-ui/images/ui-icons_ffffff_256x240.png b/src/archivematica/dashboard/media/vendor/jquery-ui/images/ui-icons_ffffff_256x240.png similarity index 100% rename from src/dashboard/src/media/vendor/jquery-ui/images/ui-icons_ffffff_256x240.png rename to src/archivematica/dashboard/media/vendor/jquery-ui/images/ui-icons_ffffff_256x240.png diff --git a/src/dashboard/src/media/vendor/jquery-ui/jquery-ui.css b/src/archivematica/dashboard/media/vendor/jquery-ui/jquery-ui.css similarity index 100% rename from src/dashboard/src/media/vendor/jquery-ui/jquery-ui.css rename to src/archivematica/dashboard/media/vendor/jquery-ui/jquery-ui.css diff --git a/src/dashboard/src/media/vendor/jquery-ui/jquery-ui.js b/src/archivematica/dashboard/media/vendor/jquery-ui/jquery-ui.js similarity index 100% rename from src/dashboard/src/media/vendor/jquery-ui/jquery-ui.js rename to src/archivematica/dashboard/media/vendor/jquery-ui/jquery-ui.js diff --git a/src/dashboard/src/media/vendor/jquery.idle-timer.js b/src/archivematica/dashboard/media/vendor/jquery.idle-timer.js similarity index 100% rename from src/dashboard/src/media/vendor/jquery.idle-timer.js rename to src/archivematica/dashboard/media/vendor/jquery.idle-timer.js diff --git a/src/dashboard/src/media/vendor/jquery.inputmask.js b/src/archivematica/dashboard/media/vendor/jquery.inputmask.js similarity index 100% rename from src/dashboard/src/media/vendor/jquery.inputmask.js rename to src/archivematica/dashboard/media/vendor/jquery.inputmask.js diff --git a/src/dashboard/src/media/vendor/jquery.js b/src/archivematica/dashboard/media/vendor/jquery.js similarity index 100% rename from src/dashboard/src/media/vendor/jquery.js rename to src/archivematica/dashboard/media/vendor/jquery.js diff --git a/src/dashboard/src/media/vendor/select2/README.md b/src/archivematica/dashboard/media/vendor/select2/README.md similarity index 100% rename from src/dashboard/src/media/vendor/select2/README.md rename to src/archivematica/dashboard/media/vendor/select2/README.md diff --git a/src/dashboard/src/media/vendor/select2/select2.css b/src/archivematica/dashboard/media/vendor/select2/select2.css similarity index 100% rename from src/dashboard/src/media/vendor/select2/select2.css rename to src/archivematica/dashboard/media/vendor/select2/select2.css diff --git a/src/dashboard/src/media/vendor/select2/select2.js b/src/archivematica/dashboard/media/vendor/select2/select2.js similarity index 100% rename from src/dashboard/src/media/vendor/select2/select2.js rename to src/archivematica/dashboard/media/vendor/select2/select2.js diff --git a/src/dashboard/src/media/vendor/select2/select2.min.js b/src/archivematica/dashboard/media/vendor/select2/select2.min.js similarity index 100% rename from src/dashboard/src/media/vendor/select2/select2.min.js rename to src/archivematica/dashboard/media/vendor/select2/select2.min.js diff --git a/src/dashboard/src/media/vendor/select2/select2.png b/src/archivematica/dashboard/media/vendor/select2/select2.png similarity index 100% rename from src/dashboard/src/media/vendor/select2/select2.png rename to src/archivematica/dashboard/media/vendor/select2/select2.png diff --git a/src/dashboard/src/media/vendor/select2/spinner.gif b/src/archivematica/dashboard/media/vendor/select2/spinner.gif similarity index 100% rename from src/dashboard/src/media/vendor/select2/spinner.gif rename to src/archivematica/dashboard/media/vendor/select2/spinner.gif diff --git a/src/dashboard/src/media/vendor/twitter-bootstrap/.gitignore b/src/archivematica/dashboard/media/vendor/twitter-bootstrap/.gitignore similarity index 100% rename from src/dashboard/src/media/vendor/twitter-bootstrap/.gitignore rename to src/archivematica/dashboard/media/vendor/twitter-bootstrap/.gitignore diff --git a/src/dashboard/src/media/vendor/twitter-bootstrap/css/bootstrap-theme.css b/src/archivematica/dashboard/media/vendor/twitter-bootstrap/css/bootstrap-theme.css similarity index 100% rename from src/dashboard/src/media/vendor/twitter-bootstrap/css/bootstrap-theme.css rename to src/archivematica/dashboard/media/vendor/twitter-bootstrap/css/bootstrap-theme.css diff --git a/src/dashboard/src/media/vendor/twitter-bootstrap/css/bootstrap-theme.css.map b/src/archivematica/dashboard/media/vendor/twitter-bootstrap/css/bootstrap-theme.css.map similarity index 100% rename from src/dashboard/src/media/vendor/twitter-bootstrap/css/bootstrap-theme.css.map rename to src/archivematica/dashboard/media/vendor/twitter-bootstrap/css/bootstrap-theme.css.map diff --git a/src/dashboard/src/media/vendor/twitter-bootstrap/css/bootstrap.css b/src/archivematica/dashboard/media/vendor/twitter-bootstrap/css/bootstrap.css similarity index 100% rename from src/dashboard/src/media/vendor/twitter-bootstrap/css/bootstrap.css rename to src/archivematica/dashboard/media/vendor/twitter-bootstrap/css/bootstrap.css diff --git a/src/dashboard/src/media/vendor/twitter-bootstrap/css/bootstrap.css.map b/src/archivematica/dashboard/media/vendor/twitter-bootstrap/css/bootstrap.css.map similarity index 100% rename from src/dashboard/src/media/vendor/twitter-bootstrap/css/bootstrap.css.map rename to src/archivematica/dashboard/media/vendor/twitter-bootstrap/css/bootstrap.css.map diff --git a/src/dashboard/src/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.eot b/src/archivematica/dashboard/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.eot similarity index 100% rename from src/dashboard/src/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.eot rename to src/archivematica/dashboard/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.eot diff --git a/src/dashboard/src/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.svg b/src/archivematica/dashboard/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.svg similarity index 100% rename from src/dashboard/src/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.svg rename to src/archivematica/dashboard/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.svg diff --git a/src/dashboard/src/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.ttf b/src/archivematica/dashboard/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.ttf similarity index 100% rename from src/dashboard/src/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.ttf rename to src/archivematica/dashboard/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.ttf diff --git a/src/dashboard/src/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.woff b/src/archivematica/dashboard/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.woff similarity index 100% rename from src/dashboard/src/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.woff rename to src/archivematica/dashboard/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.woff diff --git a/src/dashboard/src/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.woff2 b/src/archivematica/dashboard/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.woff2 similarity index 100% rename from src/dashboard/src/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.woff2 rename to src/archivematica/dashboard/media/vendor/twitter-bootstrap/fonts/glyphicons-halflings-regular.woff2 diff --git a/src/dashboard/src/media/vendor/twitter-bootstrap/js/bootstrap.js b/src/archivematica/dashboard/media/vendor/twitter-bootstrap/js/bootstrap.js similarity index 100% rename from src/dashboard/src/media/vendor/twitter-bootstrap/js/bootstrap.js rename to src/archivematica/dashboard/media/vendor/twitter-bootstrap/js/bootstrap.js diff --git a/src/dashboard/src/media/vendor/underscore.js b/src/archivematica/dashboard/media/vendor/underscore.js similarity index 100% rename from src/dashboard/src/media/vendor/underscore.js rename to src/archivematica/dashboard/media/vendor/underscore.js diff --git a/src/dashboard/src/settings/__init__.py b/src/archivematica/dashboard/middleware/__init__.py similarity index 100% rename from src/dashboard/src/settings/__init__.py rename to src/archivematica/dashboard/middleware/__init__.py diff --git a/src/dashboard/src/middleware/common.py b/src/archivematica/dashboard/middleware/common.py similarity index 90% rename from src/dashboard/src/middleware/common.py rename to src/archivematica/dashboard/middleware/common.py index 9f5b6ec0e0..498fc0c2bf 100644 --- a/src/dashboard/src/middleware/common.py +++ b/src/archivematica/dashboard/middleware/common.py @@ -18,7 +18,6 @@ import sys import traceback -import elasticsearch from django.conf import settings from django.http import HttpResponseServerError from django.shortcuts import render @@ -26,6 +25,8 @@ from django.utils.deprecation import MiddlewareMixin from shibboleth.middleware import ShibbolethRemoteUserMiddleware +from archivematica.search.service import SearchServiceError + logger = logging.getLogger("archivematica.dashboard") @@ -58,21 +59,18 @@ def process_exception(self, request, exception): return HttpResponseServerError("Missing template: " + str(exception)) -class ElasticsearchMiddleware(MiddlewareMixin): +class SearchServiceMiddleware(MiddlewareMixin): """ Redirect the user to a friendly error page when an exception related to - Elasticsearch is detected. + the search service is detected. - The goal is to inform the user that the error is related to Elasticsearch - when they are running in production, as this seems to be a common problem, - mainly because users frequently experience Elasticsearch node crashes when - running them with not enough memory. + The goal is to inform the user that the error is related to the search + service when they are running in production, as this seems to be a common + problem, mainly because users frequently experience search service node + crashes when running them with not enough memory. """ - EXCEPTIONS = ( - elasticsearch.ElasticsearchException, - elasticsearch.ImproperlyConfigured, - ) + EXCEPTIONS = (SearchServiceError,) def process_exception(self, request, exception): if settings.DEBUG: @@ -80,7 +78,7 @@ def process_exception(self, request, exception): if isinstance(exception, self.EXCEPTIONS): return render( request, - "elasticsearch_error.html", + "search_service_error.html", {"exception_type": str(type(exception))}, ) diff --git a/src/dashboard/src/middleware/locale.py b/src/archivematica/dashboard/middleware/locale.py similarity index 100% rename from src/dashboard/src/middleware/locale.py rename to src/archivematica/dashboard/middleware/locale.py diff --git a/src/dashboard/src/settings/components/__init__.py b/src/archivematica/dashboard/settings/__init__.py similarity index 100% rename from src/dashboard/src/settings/components/__init__.py rename to src/archivematica/dashboard/settings/__init__.py diff --git a/src/dashboard/src/settings/base.py b/src/archivematica/dashboard/settings/base.py similarity index 83% rename from src/dashboard/src/settings/base.py rename to src/archivematica/dashboard/settings/base.py index 462cb75175..ed104d7b02 100644 --- a/src/dashboard/src/settings/base.py +++ b/src/archivematica/dashboard/settings/base.py @@ -19,15 +19,14 @@ import os from io import StringIO from typing import Any -from typing import Dict -from typing import List -import email_settings -from appconfig import Config -from appconfig import process_search_enabled from django.core.exceptions import ImproperlyConfigured from django.utils.translation import gettext_lazy as _ +from archivematica.archivematicaCommon import email_settings +from archivematica.archivematicaCommon.appconfig import Config +from archivematica.archivematicaCommon.appconfig import process_search_enabled + def _get_settings_from_file(path): try: @@ -168,6 +167,36 @@ def _get_settings_from_file(path): }, "site_url": {"section": "Dashboard", "option": "site_url", "type": "string"}, "time_zone": {"section": "Dashboard", "option": "time_zone", "type": "string"}, + "session_cookie_secure": { + "section": "Dashboard", + "option": "session_cookie_secure", + "type": "boolean", + }, + "session_cookie_httponly": { + "section": "Dashboard", + "option": "session_cookie_httponly", + "type": "boolean", + }, + "session_cookie_samesite": { + "section": "Dashboard", + "option": "session_cookie_samesite", + "type": "string", + }, + "csrf_cookie_secure": { + "section": "Dashboard", + "option": "csrf_cookie_secure", + "type": "boolean", + }, + "csrf_cookie_httponly": { + "section": "Dashboard", + "option": "csrf_cookie_httponly", + "type": "boolean", + }, + "csrf_cookie_samesite": { + "section": "Dashboard", + "option": "csrf_cookie_samesite", + "type": "string", + }, # [Dashboard] (MANDATORY in production) "allowed_hosts": { "section": "Dashboard", @@ -194,7 +223,7 @@ def _get_settings_from_file(path): CONFIG_DEFAULTS = """[Dashboard] shared_directory = /var/archivematica/sharedDirectory/ watch_directory = /var/archivematica/sharedDirectory/watchedDirectories/ -elasticsearch_server = 127.0.0.1:9200 +elasticsearch_server = http://127.0.0.1:9200 elasticsearch_timeout = 10 elasticsearch_max_query_size = 10000 search_enabled = true @@ -219,6 +248,12 @@ def _get_settings_from_file(path): polling_interval = 10 site_url = time_zone = UTC +session_cookie_secure = True +session_cookie_httponly = True +session_cookie_samesite = Strict +csrf_cookie_secure = True +csrf_cookie_httponly = False +csrf_cookie_samesite = Strict [client] user = archivematica @@ -378,7 +413,7 @@ def _get_settings_from_file(path): }, } -TEMPLATES: List[Dict[str, Any]] = [ +TEMPLATES: list[dict[str, Any]] = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [os.path.join(BASE_PATH, "templates")], @@ -392,8 +427,8 @@ def _get_settings_from_file(path): "django.template.context_processors.static", "django.template.context_processors.request", "django.contrib.messages.context_processors.messages", - "main.context_processors.search_enabled", - "main.context_processors.auth_methods", + "archivematica.dashboard.main.context_processors.search_enabled", + "archivematica.dashboard.main.context_processors.auth_methods", ], "debug": DEBUG, }, @@ -407,28 +442,28 @@ def _get_settings_from_file(path): "django.contrib.sessions.middleware.SessionMiddleware", # Automatic language selection is disabled. # See #723 for more details. - "middleware.locale.ForceDefaultLanguageMiddleware", + "archivematica.dashboard.middleware.locale.ForceDefaultLanguageMiddleware", "django.middleware.locale.LocaleMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", - "middleware.common.AJAXSimpleExceptionResponseMiddleware", - "installer.middleware.ConfigurationCheckMiddleware", - "middleware.common.SpecificExceptionErrorPageResponseMiddleware", - "middleware.common.ElasticsearchMiddleware", + "archivematica.dashboard.middleware.common.AJAXSimpleExceptionResponseMiddleware", + "archivematica.dashboard.installer.middleware.ConfigurationCheckMiddleware", + "archivematica.dashboard.middleware.common.SpecificExceptionErrorPageResponseMiddleware", + "archivematica.dashboard.middleware.common.SearchServiceMiddleware", ] AUDIT_LOG_MIDDLEWARE = config.get("audit_log_middleware") if AUDIT_LOG_MIDDLEWARE: - MIDDLEWARE.append("middleware.common.AuditLogMiddleware") + MIDDLEWARE.append("archivematica.dashboard.middleware.common.AuditLogMiddleware") AUTHENTICATION_BACKENDS = ["django.contrib.auth.backends.ModelBackend"] # Import basic authentication settings from component module. -from .components.auth import * +from archivematica.dashboard.settings.components.auth import * -ROOT_URLCONF = "urls" +ROOT_URLCONF = "archivematica.dashboard.urls" INSTALLED_APPS = [ # Django basics @@ -441,12 +476,12 @@ def _get_settings_from_file(path): # Uncomment the next line to enable the admin: # 'django.contrib.admin', # Internal apps - "installer", - "components.accounts", - "main", - "components.mcp", - "components.administration", - "fpr", + "archivematica.dashboard.installer", + "archivematica.dashboard.components.accounts", + "archivematica.dashboard.main", + "archivematica.dashboard.components.mcp", + "archivematica.dashboard.components.administration", + "archivematica.dashboard.fpr", # For REST API "tastypie", "django_forms_bootstrap", @@ -580,33 +615,42 @@ def _get_settings_from_file(path): ALLOW_USER_EDITS = True +SESSION_COOKIE_SECURE = config.get("session_cookie_secure") +SESSION_COOKIE_HTTPONLY = config.get("session_cookie_httponly") +SESSION_COOKIE_SAMESITE = config.get("session_cookie_samesite") +CSRF_COOKIE_SECURE = config.get("csrf_cookie_secure") +CSRF_COOKIE_HTTPONLY = config.get("csrf_cookie_httponly") +CSRF_COOKIE_SAMESITE = config.get("csrf_cookie_samesite") + SHIBBOLETH_AUTHENTICATION = config.get("shibboleth_authentication") if SHIBBOLETH_AUTHENTICATION: ALLOW_USER_EDITS = False INSTALLED_APPS += ["shibboleth"] AUTHENTICATION_BACKENDS += [ - "components.accounts.backends.CustomShibbolethRemoteUserBackend" + "archivematica.dashboard.components.accounts.backends.CustomShibbolethRemoteUserBackend" ] # Insert Shibboleth after the authentication middleware MIDDLEWARE.insert( MIDDLEWARE.index("django.contrib.auth.middleware.AuthenticationMiddleware") + 1, - "middleware.common.CustomShibbolethRemoteUserMiddleware", + "archivematica.dashboard.middleware.common.CustomShibbolethRemoteUserMiddleware", ) TEMPLATES[0]["OPTIONS"]["context_processors"] += [ "shibboleth.context_processors.logout_link" ] - from .components.shibboleth_auth import * + from archivematica.dashboard.settings.components.shibboleth_auth import * LDAP_AUTHENTICATION = config.get("ldap_authentication") if LDAP_AUTHENTICATION: ALLOW_USER_EDITS = False - AUTHENTICATION_BACKENDS.insert(0, "components.accounts.backends.CustomLDAPBackend") + AUTHENTICATION_BACKENDS.insert( + 0, "archivematica.dashboard.components.accounts.backends.CustomLDAPBackend" + ) - from .components.ldap_auth import * + from archivematica.dashboard.settings.components.ldap_auth import * CAS_AUTHENTICATION = config.get("cas_authentication") if CAS_AUTHENTICATION: @@ -623,7 +667,9 @@ def _get_settings_from_file(path): ALLOW_USER_EDITS = False INSTALLED_APPS += ["django_cas_ng"] - AUTHENTICATION_BACKENDS += ["components.accounts.backends.CustomCASBackend"] + AUTHENTICATION_BACKENDS += [ + "archivematica.dashboard.components.accounts.backends.CustomCASBackend" + ] # Insert CAS after the authentication middleware MIDDLEWARE.insert( @@ -631,7 +677,7 @@ def _get_settings_from_file(path): "django_cas_ng.middleware.CASMiddleware", ) - from .components.cas_auth import * + from archivematica.dashboard.settings.components.cas_auth import * OIDC_AUTHENTICATION = config.get("oidc_authentication") if OIDC_AUTHENTICATION: @@ -641,11 +687,11 @@ def _get_settings_from_file(path): ALLOW_USER_EDITS = False OIDC_STORE_ID_TOKEN = True - OIDC_AUTHENTICATE_CLASS = ( - "components.accounts.views.CustomOIDCAuthenticationRequestView" - ) + OIDC_AUTHENTICATE_CLASS = "archivematica.dashboard.components.accounts.views.CustomOIDCAuthenticationRequestView" - AUTHENTICATION_BACKENDS += ["components.accounts.backends.CustomOIDCBackend"] + AUTHENTICATION_BACKENDS += [ + "archivematica.dashboard.components.accounts.backends.CustomOIDCBackend" + ] LOGIN_EXEMPT_URLS.append(r"^oidc") if not OIDC_ALLOW_LOCAL_AUTHENTICATION: @@ -659,16 +705,37 @@ def _get_settings_from_file(path): # Insert OIDC before the redirect to LOGIN_URL MIDDLEWARE.insert( MIDDLEWARE.index("django.contrib.auth.middleware.AuthenticationMiddleware") + 1, - "middleware.common.OidcCaptureQueryParamMiddleware", + "archivematica.dashboard.middleware.common.OidcCaptureQueryParamMiddleware", + ) + + OIDC_USE_SESSION_REFRESH_MIDDLEWARE = os.environ.get( + "OIDC_USE_SESSION_REFRESH_MIDDLEWARE", "false" + ).lower() in ( + "true", + "yes", + "on", + "1", ) - from .components.oidc_auth import * + if OIDC_USE_SESSION_REFRESH_MIDDLEWARE: + OIDC_RENEW_ID_TOKEN_EXPIRY_SECONDS = int( + os.environ.get("OIDC_RENEW_ID_TOKEN_EXPIRY_SECONDS", 60 * 15) + ) + + MIDDLEWARE.insert( + MIDDLEWARE.index("django.contrib.auth.middleware.AuthenticationMiddleware") + + 1, + "mozilla_django_oidc.middleware.SessionRefresh", + ) + + from archivematica.dashboard.settings.components.oidc_auth import * CSP_ENABLED = config.get("csp_enabled") if CSP_ENABLED: + INSTALLED_APPS.append("csp") MIDDLEWARE.insert(0, "csp.middleware.CSPMiddleware") - from .components.csp import * + from archivematica.dashboard.settings.components.csp import * CSP_SETTINGS_FILE = os.environ.get("CSP_SETTINGS_FILE", "") if CSP_SETTINGS_FILE: diff --git a/src/archivematica/dashboard/settings/components/__init__.py b/src/archivematica/dashboard/settings/components/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/dashboard/src/settings/components/auth.py b/src/archivematica/dashboard/settings/components/auth.py similarity index 85% rename from src/dashboard/src/settings/components/auth.py rename to src/archivematica/dashboard/settings/components/auth.py index a77d0f7625..a3d3b5d915 100644 --- a/src/dashboard/src/settings/components/auth.py +++ b/src/archivematica/dashboard/settings/components/auth.py @@ -1,6 +1,6 @@ """Settings for basic user authentication.""" -from settings.base import config +from archivematica.dashboard.settings.base import config AUTH_PASSWORD_VALIDATORS = [ { @@ -30,5 +30,7 @@ ) if not DISABLE_PASSWORD_COMPLEXITY_VALIDATION: AUTH_PASSWORD_VALIDATORS.append( - {"NAME": "components.accounts.validators.PasswordComplexityValidator"} + { + "NAME": "archivematica.dashboard.components.accounts.validators.PasswordComplexityValidator" + } ) diff --git a/src/dashboard/src/settings/components/cas_auth.py b/src/archivematica/dashboard/settings/components/cas_auth.py similarity index 100% rename from src/dashboard/src/settings/components/cas_auth.py rename to src/archivematica/dashboard/settings/components/cas_auth.py diff --git a/src/archivematica/dashboard/settings/components/csp.py b/src/archivematica/dashboard/settings/components/csp.py new file mode 100644 index 0000000000..17f3b1ece4 --- /dev/null +++ b/src/archivematica/dashboard/settings/components/csp.py @@ -0,0 +1,18 @@ +from csp.constants import NONE +from csp.constants import SELF +from csp.constants import UNSAFE_EVAL +from csp.constants import UNSAFE_INLINE + +CONTENT_SECURITY_POLICY = { + "DIRECTIVES": { + "default-src": [NONE], + "script-src": [SELF, UNSAFE_INLINE, UNSAFE_EVAL], + "style-src": [SELF, UNSAFE_INLINE], + "img-src": [SELF, "data:"], + "font-src": [SELF, "data:"], + # for preview file pane in the appraisal tab + "frame-src": [SELF], + # for /status + "connect-src": [SELF], + } +} diff --git a/src/dashboard/src/settings/components/ldap_auth.py b/src/archivematica/dashboard/settings/components/ldap_auth.py similarity index 100% rename from src/dashboard/src/settings/components/ldap_auth.py rename to src/archivematica/dashboard/settings/components/ldap_auth.py diff --git a/src/dashboard/src/settings/components/oidc_auth.py b/src/archivematica/dashboard/settings/components/oidc_auth.py similarity index 59% rename from src/dashboard/src/settings/components/oidc_auth.py rename to src/archivematica/dashboard/settings/components/oidc_auth.py index f092711a29..cd7041c4b8 100644 --- a/src/dashboard/src/settings/components/oidc_auth.py +++ b/src/archivematica/dashboard/settings/components/oidc_auth.py @@ -1,6 +1,9 @@ +import json import os -from archivematicaFunctions import get_oidc_secondary_providers +from archivematica.archivematicaCommon.archivematicaFunctions import ( + get_oidc_secondary_providers, +) OIDC_RP_CLIENT_ID = os.environ.get("OIDC_RP_CLIENT_ID", "") OIDC_RP_CLIENT_SECRET = os.environ.get("OIDC_RP_CLIENT_SECRET", "") @@ -34,19 +37,50 @@ OIDC_OP_JWKS_ENDPOINT = os.environ.get("OIDC_OP_JWKS_ENDPOINT", "") OIDC_OP_LOGOUT_ENDPOINT = os.environ.get("OIDC_OP_LOGOUT_ENDPOINT", "") +OIDC_OP_SET_ROLES_FROM_CLAIMS = os.environ.get( + "OIDC_OP_SET_ROLES_FROM_CLAIMS", "" +).lower() in ( + "true", + "yes", + "on", + "1", +) +OIDC_OP_ROLE_CLAIM_PATH = os.environ.get( + "OIDC_OP_ROLE_CLAIM_PATH", "realm_access.roles" +) +OIDC_ROLE_CLAIM_ADMIN = os.environ.get("OIDC_ROLE_CLAIM_ADMIN", "admin") +OIDC_ROLE_CLAIM_DEFAULT = os.environ.get("OIDC_ROLE_CLAIM_DEFAULT", "default") + +DEFAULT_OIDC_CLAIMS = {"given_name": "first_name", "family_name": "last_name"} + OIDC_SECONDARY_PROVIDER_NAMES = os.environ.get( "OIDC_SECONDARY_PROVIDER_NAMES", "" ).split(",") OIDC_PROVIDER_QUERY_PARAM_NAME = os.environ.get( "OIDC_PROVIDER_QUERY_PARAM_NAME", "secondary" ) -OIDC_PROVIDERS = get_oidc_secondary_providers(OIDC_SECONDARY_PROVIDER_NAMES) +OIDC_PROVIDERS = get_oidc_secondary_providers( + OIDC_SECONDARY_PROVIDER_NAMES, DEFAULT_OIDC_CLAIMS +) if OIDC_OP_LOGOUT_ENDPOINT: - OIDC_OP_LOGOUT_URL_METHOD = "components.accounts.views.get_oidc_logout_url" + OIDC_OP_LOGOUT_URL_METHOD = ( + "archivematica.dashboard.components.accounts.views.get_oidc_logout_url" + ) OIDC_RP_SIGN_ALGO = os.environ.get("OIDC_RP_SIGN_ALGO", "HS256") +OIDC_USE_PKCE = os.environ.get("OIDC_USE_PKCE", "false").lower() in ( + "true", + "yes", + "on", + "1", +) + +OIDC_PKCE_CODE_CHALLENGE_METHOD = os.environ.get( + "OIDC_PKCE_CODE_CHALLENGE_METHOD", "S256" +) + # Username is email address def _get_email(email): @@ -56,7 +90,12 @@ def _get_email(email): OIDC_USERNAME_ALGO = _get_email # map attributes from access token -OIDC_ACCESS_ATTRIBUTE_MAP = {"given_name": "first_name", "family_name": "last_name"} +try: + OIDC_ACCESS_ATTRIBUTE_MAP = json.loads( + os.environ.get("OIDC_ACCESS_ATTRIBUTE_MAP", json.dumps(DEFAULT_OIDC_CLAIMS)) + ) +except json.JSONDecodeError: + OIDC_ACCESS_ATTRIBUTE_MAP = DEFAULT_OIDC_CLAIMS # map attributes from id token OIDC_ID_ATTRIBUTE_MAP = {"email": "email"} diff --git a/src/dashboard/src/settings/components/shibboleth_auth.py b/src/archivematica/dashboard/settings/components/shibboleth_auth.py similarity index 100% rename from src/dashboard/src/settings/components/shibboleth_auth.py rename to src/archivematica/dashboard/settings/components/shibboleth_auth.py diff --git a/src/dashboard/src/settings/local.py b/src/archivematica/dashboard/settings/local.py similarity index 84% rename from src/dashboard/src/settings/local.py rename to src/archivematica/dashboard/settings/local.py index 32a371321e..9f992e26e1 100644 --- a/src/dashboard/src/settings/local.py +++ b/src/archivematica/dashboard/settings/local.py @@ -17,14 +17,17 @@ """Development settings and globals.""" import os +from typing import Any -from .base import * +from archivematica.dashboard.settings.base import * +from archivematica.dashboard.settings.base import BASE_PATH +from archivematica.dashboard.settings.base import TEMPLATES DEBUG = True TEMPLATES[0]["OPTIONS"]["debug"] = True # Disable password validation in local development environment. -AUTH_PASSWORD_VALIDATORS = [] +AUTH_PASSWORD_VALIDATORS: list[dict[str, Any]] = [] # Fixture directories are only configured in local and test environments. # In Django 1.8, if you create a fixture named initial_data.[xml/yaml/json], diff --git a/src/dashboard/src/settings/production.py b/src/archivematica/dashboard/settings/production.py similarity index 89% rename from src/dashboard/src/settings/production.py rename to src/archivematica/dashboard/settings/production.py index a551d023b2..5dedb7450c 100644 --- a/src/dashboard/src/settings/production.py +++ b/src/archivematica/dashboard/settings/production.py @@ -16,7 +16,8 @@ # along with Archivematica. If not, see . """Production settings and globals.""" -from .base import * +from archivematica.dashboard.settings.base import * +from archivematica.dashboard.settings.base import config ALLOWED_HOSTS = config.get("allowed_hosts").split(",") diff --git a/src/dashboard/src/settings/test.py b/src/archivematica/dashboard/settings/test.py similarity index 89% rename from src/dashboard/src/settings/test.py rename to src/archivematica/dashboard/settings/test.py index 82fc6f7848..983e7b2a4d 100644 --- a/src/dashboard/src/settings/test.py +++ b/src/archivematica/dashboard/settings/test.py @@ -16,10 +16,14 @@ # along with Archivematica. If not, see . """Test settings and globals.""" +from typing import Any + import ldap from django_auth_ldap.config import LDAPSearch -from .local import * +from archivematica.dashboard.settings.local import * +from archivematica.dashboard.settings.local import MIDDLEWARE +from archivematica.dashboard.settings.local import STORAGES # Import local settings (base settings + debug + fixture dirs) @@ -66,5 +70,5 @@ AUTH_LDAP_USER_SEARCH = LDAPSearch( "ou=example,o=test", ldap.SCOPE_SUBTREE, "(cn=%(user)s)" ) -AUTH_LDAP_USER_FLAGS_BY_GROUP = {} +AUTH_LDAP_USER_FLAGS_BY_GROUP: dict[str, Any] = {} AUTH_LDAP_USERNAME_SUFFIX = "_ldap" diff --git a/src/dashboard/src/settings/testmysql.py b/src/archivematica/dashboard/settings/testmysql.py similarity index 85% rename from src/dashboard/src/settings/testmysql.py rename to src/archivematica/dashboard/settings/testmysql.py index 9aaaa72340..fe38b20f44 100644 --- a/src/dashboard/src/settings/testmysql.py +++ b/src/archivematica/dashboard/settings/testmysql.py @@ -1,4 +1,4 @@ -from .test import * +from archivematica.dashboard.settings.test import * DATABASES = { "default": { diff --git a/src/dashboard/src/templates/404.html b/src/archivematica/dashboard/templates/404.html similarity index 100% rename from src/dashboard/src/templates/404.html rename to src/archivematica/dashboard/templates/404.html diff --git a/src/dashboard/src/templates/500.html b/src/archivematica/dashboard/templates/500.html similarity index 100% rename from src/dashboard/src/templates/500.html rename to src/archivematica/dashboard/templates/500.html diff --git a/src/dashboard/src/templates/_form.html b/src/archivematica/dashboard/templates/_form.html similarity index 100% rename from src/dashboard/src/templates/_form.html rename to src/archivematica/dashboard/templates/_form.html diff --git a/src/dashboard/src/templates/_formset.html b/src/archivematica/dashboard/templates/_formset.html similarity index 100% rename from src/dashboard/src/templates/_formset.html rename to src/archivematica/dashboard/templates/_formset.html diff --git a/src/dashboard/src/templates/_formset_table.html b/src/archivematica/dashboard/templates/_formset_table.html similarity index 100% rename from src/dashboard/src/templates/_formset_table.html rename to src/archivematica/dashboard/templates/_formset_table.html diff --git a/src/dashboard/src/templates/_fpr_update_button.html b/src/archivematica/dashboard/templates/_fpr_update_button.html similarity index 100% rename from src/dashboard/src/templates/_fpr_update_button.html rename to src/archivematica/dashboard/templates/_fpr_update_button.html diff --git a/src/dashboard/src/templates/_messages.html b/src/archivematica/dashboard/templates/_messages.html similarity index 100% rename from src/dashboard/src/templates/_messages.html rename to src/archivematica/dashboard/templates/_messages.html diff --git a/src/dashboard/src/templates/_pager.html b/src/archivematica/dashboard/templates/_pager.html similarity index 100% rename from src/dashboard/src/templates/_pager.html rename to src/archivematica/dashboard/templates/_pager.html diff --git a/src/dashboard/src/templates/accounts/add.html b/src/archivematica/dashboard/templates/accounts/add.html similarity index 100% rename from src/dashboard/src/templates/accounts/add.html rename to src/archivematica/dashboard/templates/accounts/add.html diff --git a/src/dashboard/src/templates/accounts/edit.html b/src/archivematica/dashboard/templates/accounts/edit.html similarity index 90% rename from src/dashboard/src/templates/accounts/edit.html rename to src/archivematica/dashboard/templates/accounts/edit.html index 32af8b413b..b6ada6145a 100644 --- a/src/dashboard/src/templates/accounts/edit.html +++ b/src/archivematica/dashboard/templates/accounts/edit.html @@ -29,12 +29,12 @@ {% include "_form.html" %} -
-
{{ user|api_key }}
-
- {% include "_form.html" with form=userprofileform %} + {% if return_view %} + + {% endif %} +
{% trans "Cancel" %} diff --git a/src/dashboard/src/templates/accounts/list.html b/src/archivematica/dashboard/templates/accounts/list.html similarity index 100% rename from src/dashboard/src/templates/accounts/list.html rename to src/archivematica/dashboard/templates/accounts/list.html diff --git a/src/dashboard/src/templates/accounts/logged_out.html b/src/archivematica/dashboard/templates/accounts/logged_out.html similarity index 100% rename from src/dashboard/src/templates/accounts/logged_out.html rename to src/archivematica/dashboard/templates/accounts/logged_out.html diff --git a/src/dashboard/src/templates/accounts/login.html b/src/archivematica/dashboard/templates/accounts/login.html similarity index 100% rename from src/dashboard/src/templates/accounts/login.html rename to src/archivematica/dashboard/templates/accounts/login.html diff --git a/src/dashboard/src/templates/accounts/profile.html b/src/archivematica/dashboard/templates/accounts/profile.html similarity index 93% rename from src/dashboard/src/templates/accounts/profile.html rename to src/archivematica/dashboard/templates/accounts/profile.html index ffd87eb158..01db7c2fb8 100644 --- a/src/dashboard/src/templates/accounts/profile.html +++ b/src/archivematica/dashboard/templates/accounts/profile.html @@ -38,10 +38,6 @@ {% include "_form.html" %} -
-
{{ user|api_key }}
-
- {% include "_form.html" with form=userprofileform %}
diff --git a/src/archivematica/dashboard/templates/accounts/user_api_key_alert.html b/src/archivematica/dashboard/templates/accounts/user_api_key_alert.html new file mode 100644 index 0000000000..0f8e67a052 --- /dev/null +++ b/src/archivematica/dashboard/templates/accounts/user_api_key_alert.html @@ -0,0 +1,16 @@ +{% load i18n %} +

{% trans "Make sure to copy the API key now as you will not be able to see it again." %}

+
+{% trans 'Copy' as label_copy %} +{% trans 'Copied!' as label_copied %} +
+ + + + +
diff --git a/src/dashboard/src/templates/administration/api.html b/src/archivematica/dashboard/templates/administration/api.html similarity index 100% rename from src/dashboard/src/templates/administration/api.html rename to src/archivematica/dashboard/templates/administration/api.html diff --git a/src/dashboard/src/templates/administration/atom_levels_of_description.html b/src/archivematica/dashboard/templates/administration/atom_levels_of_description.html similarity index 100% rename from src/dashboard/src/templates/administration/atom_levels_of_description.html rename to src/archivematica/dashboard/templates/administration/atom_levels_of_description.html diff --git a/src/dashboard/src/templates/administration/dips_as_edit.html b/src/archivematica/dashboard/templates/administration/dips_as_edit.html similarity index 100% rename from src/dashboard/src/templates/administration/dips_as_edit.html rename to src/archivematica/dashboard/templates/administration/dips_as_edit.html diff --git a/src/dashboard/src/templates/administration/dips_atom_edit.html b/src/archivematica/dashboard/templates/administration/dips_atom_edit.html similarity index 100% rename from src/dashboard/src/templates/administration/dips_atom_edit.html rename to src/archivematica/dashboard/templates/administration/dips_atom_edit.html diff --git a/src/dashboard/src/templates/administration/general.html b/src/archivematica/dashboard/templates/administration/general.html similarity index 100% rename from src/dashboard/src/templates/administration/general.html rename to src/archivematica/dashboard/templates/administration/general.html diff --git a/src/dashboard/src/templates/administration/handle_config.html b/src/archivematica/dashboard/templates/administration/handle_config.html similarity index 100% rename from src/dashboard/src/templates/administration/handle_config.html rename to src/archivematica/dashboard/templates/administration/handle_config.html diff --git a/src/dashboard/src/templates/administration/language.html b/src/archivematica/dashboard/templates/administration/language.html similarity index 100% rename from src/dashboard/src/templates/administration/language.html rename to src/archivematica/dashboard/templates/administration/language.html diff --git a/src/dashboard/src/templates/administration/locations.html b/src/archivematica/dashboard/templates/administration/locations.html similarity index 100% rename from src/dashboard/src/templates/administration/locations.html rename to src/archivematica/dashboard/templates/administration/locations.html diff --git a/src/dashboard/src/templates/administration/premis_agent.html b/src/archivematica/dashboard/templates/administration/premis_agent.html similarity index 100% rename from src/dashboard/src/templates/administration/premis_agent.html rename to src/archivematica/dashboard/templates/administration/premis_agent.html diff --git a/src/dashboard/src/templates/administration/processing.html b/src/archivematica/dashboard/templates/administration/processing.html similarity index 100% rename from src/dashboard/src/templates/administration/processing.html rename to src/archivematica/dashboard/templates/administration/processing.html diff --git a/src/dashboard/src/templates/administration/processing_edit.html b/src/archivematica/dashboard/templates/administration/processing_edit.html similarity index 100% rename from src/dashboard/src/templates/administration/processing_edit.html rename to src/archivematica/dashboard/templates/administration/processing_edit.html diff --git a/src/dashboard/src/templates/administration/reports/failure_detail.html b/src/archivematica/dashboard/templates/administration/reports/failure_detail.html similarity index 100% rename from src/dashboard/src/templates/administration/reports/failure_detail.html rename to src/archivematica/dashboard/templates/administration/reports/failure_detail.html diff --git a/src/dashboard/src/templates/administration/reports/failures.html b/src/archivematica/dashboard/templates/administration/reports/failures.html similarity index 100% rename from src/dashboard/src/templates/administration/reports/failures.html rename to src/archivematica/dashboard/templates/administration/reports/failures.html diff --git a/src/dashboard/src/templates/administration/sidebar.html b/src/archivematica/dashboard/templates/administration/sidebar.html similarity index 100% rename from src/dashboard/src/templates/administration/sidebar.html rename to src/archivematica/dashboard/templates/administration/sidebar.html diff --git a/src/dashboard/src/templates/administration/taxonomy.html b/src/archivematica/dashboard/templates/administration/taxonomy.html similarity index 100% rename from src/dashboard/src/templates/administration/taxonomy.html rename to src/archivematica/dashboard/templates/administration/taxonomy.html diff --git a/src/dashboard/src/templates/administration/term_detail.html b/src/archivematica/dashboard/templates/administration/term_detail.html similarity index 100% rename from src/dashboard/src/templates/administration/term_detail.html rename to src/archivematica/dashboard/templates/administration/term_detail.html diff --git a/src/dashboard/src/templates/administration/terms.html b/src/archivematica/dashboard/templates/administration/terms.html similarity index 100% rename from src/dashboard/src/templates/administration/terms.html rename to src/archivematica/dashboard/templates/administration/terms.html diff --git a/src/dashboard/src/templates/administration/usage.html b/src/archivematica/dashboard/templates/administration/usage.html similarity index 100% rename from src/dashboard/src/templates/administration/usage.html rename to src/archivematica/dashboard/templates/administration/usage.html diff --git a/src/dashboard/src/templates/administration/version.html b/src/archivematica/dashboard/templates/administration/version.html similarity index 100% rename from src/dashboard/src/templates/administration/version.html rename to src/archivematica/dashboard/templates/administration/version.html diff --git a/src/dashboard/src/templates/appraisal/appraisal.html b/src/archivematica/dashboard/templates/appraisal/appraisal.html similarity index 100% rename from src/dashboard/src/templates/appraisal/appraisal.html rename to src/archivematica/dashboard/templates/appraisal/appraisal.html diff --git a/src/dashboard/src/templates/archival_storage/_archival_storage_search_form.html b/src/archivematica/dashboard/templates/archival_storage/_archival_storage_search_form.html similarity index 100% rename from src/dashboard/src/templates/archival_storage/_archival_storage_search_form.html rename to src/archivematica/dashboard/templates/archival_storage/_archival_storage_search_form.html diff --git a/src/dashboard/src/templates/archival_storage/archival_storage.html b/src/archivematica/dashboard/templates/archival_storage/archival_storage.html similarity index 100% rename from src/dashboard/src/templates/archival_storage/archival_storage.html rename to src/archivematica/dashboard/templates/archival_storage/archival_storage.html diff --git a/src/dashboard/src/templates/archival_storage/view.html b/src/archivematica/dashboard/templates/archival_storage/view.html similarity index 100% rename from src/dashboard/src/templates/archival_storage/view.html rename to src/archivematica/dashboard/templates/archival_storage/view.html diff --git a/src/dashboard/src/templates/backlog/backlog.html b/src/archivematica/dashboard/templates/backlog/backlog.html similarity index 100% rename from src/dashboard/src/templates/backlog/backlog.html rename to src/archivematica/dashboard/templates/backlog/backlog.html diff --git a/src/dashboard/src/templates/delete_request.html b/src/archivematica/dashboard/templates/delete_request.html similarity index 100% rename from src/dashboard/src/templates/delete_request.html rename to src/archivematica/dashboard/templates/delete_request.html diff --git a/src/dashboard/src/templates/forbidden.html b/src/archivematica/dashboard/templates/forbidden.html similarity index 100% rename from src/dashboard/src/templates/forbidden.html rename to src/archivematica/dashboard/templates/forbidden.html diff --git a/src/dashboard/src/templates/ingest/aip_browse.html b/src/archivematica/dashboard/templates/ingest/aip_browse.html similarity index 100% rename from src/dashboard/src/templates/ingest/aip_browse.html rename to src/archivematica/dashboard/templates/ingest/aip_browse.html diff --git a/src/dashboard/src/templates/ingest/as/_search_form.html b/src/archivematica/dashboard/templates/ingest/as/_search_form.html similarity index 100% rename from src/dashboard/src/templates/ingest/as/_search_form.html rename to src/archivematica/dashboard/templates/ingest/as/_search_form.html diff --git a/src/dashboard/src/templates/ingest/as/match.html b/src/archivematica/dashboard/templates/ingest/as/match.html similarity index 100% rename from src/dashboard/src/templates/ingest/as/match.html rename to src/archivematica/dashboard/templates/ingest/as/match.html diff --git a/src/dashboard/src/templates/ingest/as/resource_component.html b/src/archivematica/dashboard/templates/ingest/as/resource_component.html similarity index 100% rename from src/dashboard/src/templates/ingest/as/resource_component.html rename to src/archivematica/dashboard/templates/ingest/as/resource_component.html diff --git a/src/dashboard/src/templates/ingest/as/resource_detail.html b/src/archivematica/dashboard/templates/ingest/as/resource_detail.html similarity index 100% rename from src/dashboard/src/templates/ingest/as/resource_detail.html rename to src/archivematica/dashboard/templates/ingest/as/resource_detail.html diff --git a/src/dashboard/src/templates/ingest/as/resource_list.html b/src/archivematica/dashboard/templates/ingest/as/resource_list.html similarity index 100% rename from src/dashboard/src/templates/ingest/as/resource_list.html rename to src/archivematica/dashboard/templates/ingest/as/resource_list.html diff --git a/src/dashboard/src/templates/ingest/as/review_matches.html b/src/archivematica/dashboard/templates/ingest/as/review_matches.html similarity index 100% rename from src/dashboard/src/templates/ingest/as/review_matches.html rename to src/archivematica/dashboard/templates/ingest/as/review_matches.html diff --git a/src/dashboard/src/templates/ingest/backlog/_search_form.html b/src/archivematica/dashboard/templates/ingest/backlog/_search_form.html similarity index 100% rename from src/dashboard/src/templates/ingest/backlog/_search_form.html rename to src/archivematica/dashboard/templates/ingest/backlog/_search_form.html diff --git a/src/dashboard/src/templates/ingest/detail.html b/src/archivematica/dashboard/templates/ingest/detail.html similarity index 100% rename from src/dashboard/src/templates/ingest/detail.html rename to src/archivematica/dashboard/templates/ingest/detail.html diff --git a/src/dashboard/src/templates/ingest/grid.html b/src/archivematica/dashboard/templates/ingest/grid.html similarity index 100% rename from src/dashboard/src/templates/ingest/grid.html rename to src/archivematica/dashboard/templates/ingest/grid.html diff --git a/src/dashboard/src/templates/ingest/metadata_add_files.html b/src/archivematica/dashboard/templates/ingest/metadata_add_files.html similarity index 100% rename from src/dashboard/src/templates/ingest/metadata_add_files.html rename to src/archivematica/dashboard/templates/ingest/metadata_add_files.html diff --git a/src/dashboard/src/templates/ingest/metadata_edit.html b/src/archivematica/dashboard/templates/ingest/metadata_edit.html similarity index 100% rename from src/dashboard/src/templates/ingest/metadata_edit.html rename to src/archivematica/dashboard/templates/ingest/metadata_edit.html diff --git a/src/dashboard/src/templates/ingest/metadata_event_detail.html b/src/archivematica/dashboard/templates/ingest/metadata_event_detail.html similarity index 100% rename from src/dashboard/src/templates/ingest/metadata_event_detail.html rename to src/archivematica/dashboard/templates/ingest/metadata_event_detail.html diff --git a/src/dashboard/src/templates/ingest/metadata_list.html b/src/archivematica/dashboard/templates/ingest/metadata_list.html similarity index 100% rename from src/dashboard/src/templates/ingest/metadata_list.html rename to src/archivematica/dashboard/templates/ingest/metadata_list.html diff --git a/src/dashboard/src/templates/ingest/microservices.html b/src/archivematica/dashboard/templates/ingest/microservices.html similarity index 100% rename from src/dashboard/src/templates/ingest/microservices.html rename to src/archivematica/dashboard/templates/ingest/microservices.html diff --git a/src/dashboard/src/templates/ingest/normalization_report.html b/src/archivematica/dashboard/templates/ingest/normalization_report.html similarity index 100% rename from src/dashboard/src/templates/ingest/normalization_report.html rename to src/archivematica/dashboard/templates/ingest/normalization_report.html diff --git a/src/dashboard/src/templates/installer/storagesetup.html b/src/archivematica/dashboard/templates/installer/storagesetup.html similarity index 100% rename from src/dashboard/src/templates/installer/storagesetup.html rename to src/archivematica/dashboard/templates/installer/storagesetup.html diff --git a/src/dashboard/src/templates/installer/welcome.html b/src/archivematica/dashboard/templates/installer/welcome.html similarity index 100% rename from src/dashboard/src/templates/installer/welcome.html rename to src/archivematica/dashboard/templates/installer/welcome.html diff --git a/src/dashboard/src/templates/layout.html b/src/archivematica/dashboard/templates/layout.html similarity index 100% rename from src/dashboard/src/templates/layout.html rename to src/archivematica/dashboard/templates/layout.html diff --git a/src/dashboard/src/templates/layout_ajax.html b/src/archivematica/dashboard/templates/layout_ajax.html similarity index 100% rename from src/dashboard/src/templates/layout_ajax.html rename to src/archivematica/dashboard/templates/layout_ajax.html diff --git a/src/dashboard/src/templates/layout_fluid.html b/src/archivematica/dashboard/templates/layout_fluid.html similarity index 100% rename from src/dashboard/src/templates/layout_fluid.html rename to src/archivematica/dashboard/templates/layout_fluid.html diff --git a/src/dashboard/src/templates/main/_tasks_pager.html b/src/archivematica/dashboard/templates/main/_tasks_pager.html similarity index 100% rename from src/dashboard/src/templates/main/_tasks_pager.html rename to src/archivematica/dashboard/templates/main/_tasks_pager.html diff --git a/src/dashboard/src/templates/main/access.html b/src/archivematica/dashboard/templates/main/access.html similarity index 100% rename from src/dashboard/src/templates/main/access.html rename to src/archivematica/dashboard/templates/main/access.html diff --git a/src/dashboard/src/templates/main/tasks.html b/src/archivematica/dashboard/templates/main/tasks.html similarity index 100% rename from src/dashboard/src/templates/main/tasks.html rename to src/archivematica/dashboard/templates/main/tasks.html diff --git a/src/dashboard/src/templates/rights/rights_edit.html b/src/archivematica/dashboard/templates/rights/rights_edit.html similarity index 100% rename from src/dashboard/src/templates/rights/rights_edit.html rename to src/archivematica/dashboard/templates/rights/rights_edit.html diff --git a/src/dashboard/src/templates/rights/rights_grants_edit.html b/src/archivematica/dashboard/templates/rights/rights_grants_edit.html similarity index 100% rename from src/dashboard/src/templates/rights/rights_grants_edit.html rename to src/archivematica/dashboard/templates/rights/rights_grants_edit.html diff --git a/src/dashboard/src/templates/rights/rights_list.html b/src/archivematica/dashboard/templates/rights/rights_list.html similarity index 100% rename from src/dashboard/src/templates/rights/rights_list.html rename to src/archivematica/dashboard/templates/rights/rights_list.html diff --git a/src/dashboard/src/templates/elasticsearch_error.html b/src/archivematica/dashboard/templates/search_service_error.html similarity index 90% rename from src/dashboard/src/templates/elasticsearch_error.html rename to src/archivematica/dashboard/templates/search_service_error.html index 17c816198d..f2b2bb319f 100644 --- a/src/dashboard/src/templates/elasticsearch_error.html +++ b/src/archivematica/dashboard/templates/search_service_error.html @@ -3,7 +3,7 @@ {% block content %} -

{% trans "Elasticsearch error" %}

+

{% trans "Search service error" %}

{% blocktrans with exception_type=exception_type %} diff --git a/src/dashboard/src/templates/simple_confirm.html b/src/archivematica/dashboard/templates/simple_confirm.html similarity index 100% rename from src/dashboard/src/templates/simple_confirm.html rename to src/archivematica/dashboard/templates/simple_confirm.html diff --git a/src/dashboard/src/templates/transfer/component.html b/src/archivematica/dashboard/templates/transfer/component.html similarity index 100% rename from src/dashboard/src/templates/transfer/component.html rename to src/archivematica/dashboard/templates/transfer/component.html diff --git a/src/dashboard/src/templates/transfer/detail.html b/src/archivematica/dashboard/templates/transfer/detail.html similarity index 100% rename from src/dashboard/src/templates/transfer/detail.html rename to src/archivematica/dashboard/templates/transfer/detail.html diff --git a/src/dashboard/src/templates/transfer/grid.html b/src/archivematica/dashboard/templates/transfer/grid.html similarity index 100% rename from src/dashboard/src/templates/transfer/grid.html rename to src/archivematica/dashboard/templates/transfer/grid.html diff --git a/src/dashboard/src/templates/transfer/metadata_edit.html b/src/archivematica/dashboard/templates/transfer/metadata_edit.html similarity index 100% rename from src/dashboard/src/templates/transfer/metadata_edit.html rename to src/archivematica/dashboard/templates/transfer/metadata_edit.html diff --git a/src/dashboard/src/templates/transfer/metadata_list.html b/src/archivematica/dashboard/templates/transfer/metadata_list.html similarity index 100% rename from src/dashboard/src/templates/transfer/metadata_list.html rename to src/archivematica/dashboard/templates/transfer/metadata_list.html diff --git a/src/dashboard/src/templates/transfer/microservices.html b/src/archivematica/dashboard/templates/transfer/microservices.html similarity index 100% rename from src/dashboard/src/templates/transfer/microservices.html rename to src/archivematica/dashboard/templates/transfer/microservices.html diff --git a/src/archivematica/dashboard/urls.py b/src/archivematica/dashboard/urls.py new file mode 100644 index 0000000000..82a003afef --- /dev/null +++ b/src/archivematica/dashboard/urls.py @@ -0,0 +1,72 @@ +# This file is part of Archivematica. +# +# Copyright 2010-2013 Artefactual Systems Inc. +# +# Archivematica is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Archivematica is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Archivematica. If not, see . +from django.conf import settings +from django.urls import include +from django.urls import path +from django.urls import re_path + +urlpatterns = [ + path("mcp/", include("archivematica.dashboard.components.mcp.urls")), + path("installer/", include("archivematica.dashboard.installer.urls")), + path( + "administration/accounts/", + include("archivematica.dashboard.components.accounts.urls"), + ), + path( + "archival-storage/", + include("archivematica.dashboard.components.archival_storage.urls"), + ), + path("fpr/", include("archivematica.dashboard.fpr.urls")), + re_path( + r"^(?Ptransfer|ingest)/", + include("archivematica.dashboard.components.unit.urls"), + ), # URLs common to transfer & ingest + re_path( + r"^transfer/(?P" + settings.UUID_REGEX + ")/rights/", + include("archivematica.dashboard.components.rights.transfer_urls"), + ), + path("transfer/", include("archivematica.dashboard.components.transfer.urls")), + path("appraisal/", include("archivematica.dashboard.components.appraisal.urls")), + re_path( + r"^ingest/(?P" + settings.UUID_REGEX + ")/rights/", + include("archivematica.dashboard.components.rights.ingest_urls"), + ), + path("ingest/", include("archivematica.dashboard.components.ingest.urls")), + path( + "administration/", + include("archivematica.dashboard.components.administration.urls"), + ), + path( + "filesystem/", + include("archivematica.dashboard.components.filesystem_ajax.urls"), + ), + path("api/", include("archivematica.dashboard.components.api.urls")), + path("file/", include("archivematica.dashboard.components.file.urls")), + path("access/", include("archivematica.dashboard.components.access.urls")), + path("backlog/", include("archivematica.dashboard.components.backlog.urls")), + path("", include("archivematica.dashboard.main.urls")), +] + +if settings.PROMETHEUS_ENABLED: + # Include prometheus metrics at /metrics + urlpatterns += [path("", include("django_prometheus.urls"))] + +if settings.OIDC_AUTHENTICATION: + urlpatterns += [path("oidc/", include("mozilla_django_oidc.urls"))] + +if "shibboleth" in settings.INSTALLED_APPS: + urlpatterns += [path("shib/", include("shibboleth.urls"))] diff --git a/src/dashboard/src/wsgi.py b/src/archivematica/dashboard/wsgi.py similarity index 52% rename from src/dashboard/src/wsgi.py rename to src/archivematica/dashboard/wsgi.py index 1e6b3bba7c..ac24fcef82 100644 --- a/src/dashboard/src/wsgi.py +++ b/src/archivematica/dashboard/wsgi.py @@ -3,9 +3,9 @@ from django.core.wsgi import get_wsgi_application django.setup() -import elasticSearchFunctions +from archivematica.search.service import setup_search_service_from_conf application = get_wsgi_application() -# Set up Elasticsearch client -elasticSearchFunctions.setup_reading_from_conf(settings) +# Set up search service +setup_search_service_from_conf(settings) diff --git a/src/archivematica/search/__init__.py b/src/archivematica/search/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/archivematica/search/constants.py b/src/archivematica/search/constants.py new file mode 100644 index 0000000000..56321d4008 --- /dev/null +++ b/src/archivematica/search/constants.py @@ -0,0 +1,45 @@ +STATUS_DELETE_REQUESTED = "DEL_REQ" +STATUS_DELETED = "DELETED" +STATUS_UPLOADED = "UPLOADED" +STATUS_BACKLOG = "backlog" + +AIPS_INDEX = "aips" +AIP_FILES_INDEX = "aipfiles" +TRANSFERS_INDEX = "transfers" +TRANSFER_FILES_INDEX = "transferfiles" + +ES_FIELD_AICID = "AICID" +ES_FIELD_ACCESSION_IDS = "accessionids" +ES_FIELD_AICCOUNT = "countAIPsinAIC" +ES_FIELD_CREATED = "created" +ES_FIELD_ENCRYPTED = "encrypted" +ES_FIELD_FILECOUNT = "file_count" +ES_FIELD_LOCATION = "location" +ES_FIELD_NAME = "name" +ES_FIELD_SIZE = "size" +ES_FIELD_STATUS = "status" +ES_FIELD_UUID = "uuid" + +# Additional Elasticsearch field names +ES_FIELD_AIPUUID = "AIPUUID" +ES_FIELD_FILEUUID = "FILEUUID" +ES_FIELD_SIPUUID = "sipuuid" +ES_FIELD_FILEUUID_LOWER = "fileuuid" + +DEFAULT_TIMEOUT = 10 +# Known indexes. This indexes may be enabled or not based on the SEARCH_ENABLED +# setting. To add a new index, make sure it's related to the setting values in +# the setup functions below, add its name to the following array and create a +# function declaring the index settings and mapping. For example, for an index +# called `tests` the function must be called `_get_tests_index_body`. See the +# functions related to the current known indexes for examples. +INDEXES = [AIPS_INDEX, AIP_FILES_INDEX, TRANSFERS_INDEX, TRANSFER_FILES_INDEX] +# Maximun ES result window. Use the scroll API for a better way to get all +# results or change `index.max_result_window` on each index settings. +MAX_QUERY_SIZE = 10000 +# Maximun amount of fields per index (increased from the ES default of 1000). +TOTAL_FIELDS_LIMIT = 10000 +# Maximum index depth (increased from the ES default of 20). The way the +# `structMap` element from the METS file is parsed may create a big depth +# in documents for AIPs with a big directories hierarchy. +DEPTH_LIMIT = 1000 diff --git a/src/archivematica/search/service.py b/src/archivematica/search/service.py new file mode 100644 index 0000000000..b2d87459fb --- /dev/null +++ b/src/archivematica/search/service.py @@ -0,0 +1,1701 @@ +import logging +import os +import time +from abc import ABC +from abc import abstractmethod +from collections.abc import Generator +from dataclasses import dataclass +from typing import Any +from typing import Literal +from typing import Optional +from typing import TypedDict +from typing import Union + +from elasticsearch import Elasticsearch +from elasticsearch.exceptions import NotFoundError +from elasticsearch.exceptions import RequestError +from elasticsearch.helpers import bulk + +from archivematica.search.constants import AIP_FILES_INDEX +from archivematica.search.constants import AIPS_INDEX +from archivematica.search.constants import DEFAULT_TIMEOUT +from archivematica.search.constants import DEPTH_LIMIT +from archivematica.search.constants import ES_FIELD_ACCESSION_IDS +from archivematica.search.constants import ES_FIELD_AICCOUNT +from archivematica.search.constants import ES_FIELD_AICID +from archivematica.search.constants import ES_FIELD_AIPUUID +from archivematica.search.constants import ES_FIELD_CREATED +from archivematica.search.constants import ES_FIELD_ENCRYPTED +from archivematica.search.constants import ES_FIELD_FILECOUNT +from archivematica.search.constants import ES_FIELD_FILEUUID +from archivematica.search.constants import ES_FIELD_FILEUUID_LOWER +from archivematica.search.constants import ES_FIELD_LOCATION +from archivematica.search.constants import ES_FIELD_NAME +from archivematica.search.constants import ES_FIELD_SIPUUID +from archivematica.search.constants import ES_FIELD_SIZE +from archivematica.search.constants import ES_FIELD_STATUS +from archivematica.search.constants import ES_FIELD_UUID +from archivematica.search.constants import STATUS_BACKLOG +from archivematica.search.constants import STATUS_DELETE_REQUESTED +from archivematica.search.constants import STATUS_UPLOADED +from archivematica.search.constants import TOTAL_FIELDS_LIMIT +from archivematica.search.constants import TRANSFER_FILES_INDEX +from archivematica.search.constants import TRANSFERS_INDEX + +logger = logging.getLogger("archivematica.search") + + +_search_service_instance: Optional["SearchService"] = None + + +@dataclass +class SearchBackendInfo: + """Information about the search backend connection.""" + + name: str + version: str + cluster_name: Optional[str] = None + raw_info: Optional[dict[str, Any]] = None + + +@dataclass +class SortSpec: + """Represents search sort specification with field name and order.""" + + field: str + order: Literal["asc", "desc"] = "asc" + + +class SearchServiceError(Exception): + """Base exception for search service operations.""" + + pass + + +class AIPNotFoundError(SearchServiceError): + """Raised when an AIP cannot be found by UUID.""" + + pass + + +class AIPFileNotFoundError(SearchServiceError): + """Raised when an AIP file cannot be found by UUID.""" + + pass + + +class TransferFileNotFoundError(SearchServiceError): + """Raised when a transfer file cannot be found by UUID.""" + + pass + + +class MultipleResultsError(SearchServiceError): + """Raised when a search expected to return a single result returns multiple.""" + + pass + + +class SearchService(ABC): + """Abstract base class for search services.""" + + @abstractmethod + def delete_transfer(self, transfer_uuid: str) -> None: + """Delete transfer from the search index. + + :param str transfer_uuid: Transfer UUID to delete + """ + pass + + @abstractmethod + def delete_transfer_files(self, transfer_uuids: set[str]) -> None: + """Delete transfer files from the search index. + + :param set[str] transfer_ids: Set of transfer UUIDs to delete files for + """ + pass + + @abstractmethod + def delete_aip(self, aip_uuid: str) -> None: + """Delete AIP from the search index. + + :param str aip_uuid: AIP UUID to delete + """ + pass + + @abstractmethod + def delete_aip_files(self, aip_uuid: str) -> None: + """Delete AIP files from the search index. + + :param str aip_uuid: AIP UUID to delete files for + """ + pass + + @abstractmethod + def mark_transfer_for_deletion(self, transfer_uuid: str) -> None: + """Mark transfer for deletion in the search index. + + :param str transfer_uuid: Transfer UUID to mark for deletion + """ + pass + + @abstractmethod + def unmark_transfer_for_deletion(self, transfer_uuid: str) -> None: + """Unmark transfer for deletion in the search index. + + :param str transfer_uuid: Transfer UUID to unmark for deletion + """ + pass + + @abstractmethod + def mark_aip_for_deletion(self, aip_uuid: str) -> None: + """Mark AIP for deletion in the search index. + + :param str aip_uuid: AIP UUID to mark for deletion + """ + pass + + @abstractmethod + def unmark_aip_for_deletion(self, aip_uuid: str) -> None: + """Unmark AIP for deletion in the search index. + + :param str aip_uuid: AIP UUID to unmark for deletion + """ + pass + + @abstractmethod + def ensure_indexes_exist(self, indexes: list[str]) -> None: + """Ensure the specified search indexes exist. + + Creates any missing indexes with their appropriate settings and mappings. + + :param list[str] indexes: List of index names to ensure exist + """ + pass + + @abstractmethod + def delete_indexes(self, indexes: list[str]) -> None: + """Delete the specified search indexes. + + :param list[str] indexes: List of index names to delete + """ + pass + + @abstractmethod + def update_index_mappings(self, index: str, mappings: dict[str, Any]) -> None: + """Update mappings for a specific index. + + :param str index: Index name to update + :param dict[str, Any] mappings: Mapping properties to apply + """ + pass + + @abstractmethod + def reindex_from_remote( + self, remote_config: dict[str, Any], index_mappings: list[dict[str, str]] + ) -> dict[str, Any]: + """Reindex data from a remote search cluster. + + :param dict[str, Any] remote_config: Remote cluster configuration + :param list[dict[str, str]] index_mappings: Source to destination index mappings + :return: Results of reindex operations + """ + pass + + @abstractmethod + def update_all_documents(self, index: str) -> dict[str, Any]: + """Update all documents in an index. + + Useful for populating new mapping fields after schema changes. + + :param str index: Index name to update + :return: Update operation results + """ + pass + + @abstractmethod + def get_aip_data( + self, uuid: str, fields: Optional[list[str]] = None + ) -> dict[str, Any]: + """Get AIP data by UUID. + + :param str uuid: AIP UUID to retrieve + :param Optional[list[str]] fields: Optional list of fields to return + :return: AIP document data + """ + pass + + @abstractmethod + def get_aipfile_data( + self, uuid: str, fields: Optional[list[str]] = None + ) -> dict[str, Any]: + """Get AIP file data by file UUID. + + :param str uuid: File UUID to retrieve + :param Optional[list[str]] fields: Optional list of fields to return + :return: AIP file document data + """ + pass + + @abstractmethod + def get_aipfile_by_document_id(self, document_id: str) -> dict[str, Any]: + """Get AIP file document by document ID. + + :param str document_id: Document ID + :return: Complete document data including metadata + :raises AIPFileNotFoundError: When document is not found + """ + pass + + @abstractmethod + def get_transfer_file_tags(self, uuid: str) -> list[str]: + """Get tags for a transfer file by file UUID. + + :param str uuid: File UUID to retrieve tags for + :return: List of tags for the file + """ + pass + + @abstractmethod + def get_transfer_file_data(self, uuid: str) -> dict[str, Any]: + """Get transfer file data by file UUID. + + :param str uuid: File UUID to search for + :return: Transfer file document data + """ + pass + + @abstractmethod + def set_transfer_file_tags(self, uuid: str, tags: list[str]) -> None: + """Set tags for a transfer file by file UUID. + + :param str uuid: File UUID to update tags for + :param list[str] tags: List of tags to set (empty list clears tags) + """ + pass + + @abstractmethod + def search_transfer_files( + self, + query: dict[str, Any], + size: Optional[int] = None, + from_: Optional[int] = None, + sort: Optional[SortSpec] = None, + fields: Optional[list[str]] = None, + ) -> dict[str, Any]: + """Search transfer files with optional parameters. + + :param dict[str, Any] query: Search query body + :param Optional[int] size: Optional size limit for results, defaults to max_query_size + :param Optional[int] from_: Optional start index for pagination + :param Optional[SortSpec] sort: Optional sort specification with field and order + :param Optional[list[str]] fields: Optional list of fields to return + :return: Raw search results + """ + pass + + @abstractmethod + def search_transfers( + self, + query: dict[str, Any], + size: Optional[int] = None, + from_: Optional[int] = None, + sort: Optional[SortSpec] = None, + fields: Optional[list[str]] = None, + ) -> dict[str, Any]: + """Search transfers with optional parameters. + + :param dict[str, Any] query: Search query body + :param Optional[int] size: Optional size limit for results, defaults to max_query_size + :param Optional[int] from_: Optional start index for pagination + :param Optional[SortSpec] sort: Optional sort specification with field and order + :param Optional[list[str]] fields: Optional list of fields to return + :return: Raw search results + """ + pass + + @abstractmethod + def search_aips( + self, + query: dict[str, Any], + size: Optional[int] = None, + from_: Optional[int] = None, + sort: Optional[SortSpec] = None, + fields: Optional[list[str]] = None, + ) -> dict[str, Any]: + """Search AIPs with optional parameters. + + :param dict[str, Any] query: Search query body + :param Optional[int] size: Optional size limit for results, defaults to max_query_size + :param Optional[int] from_: Optional start index for pagination + :param Optional[SortSpec] sort: Optional sort specification with field and order + :param Optional[list[str]] fields: Optional list of fields to return + :return: Raw search results + """ + pass + + @abstractmethod + def search_aip_files( + self, + query: dict[str, Any], + size: Optional[int] = None, + from_: Optional[int] = None, + sort: Optional[SortSpec] = None, + fields: Optional[list[str]] = None, + ) -> dict[str, Any]: + """Search AIP files with optional parameters. + + :param dict[str, Any] query: Search query body + :param Optional[int] size: Optional size limit for results, defaults to max_query_size + :param Optional[int] from_: Optional start index for pagination + :param Optional[SortSpec] sort: Optional sort specification with field and order + :param Optional[list[str]] fields: Optional list of fields to return + :return: Raw search results + """ + pass + + @abstractmethod + def count_aip_files(self, query: dict[str, Any]) -> int: + """Count AIP files matching the given query. + + :param dict[str, Any] query: Search query body + :return: Number of AIP files matching the query + """ + pass + + @abstractmethod + def get_cluster_info(self) -> SearchBackendInfo: + """Get search backend cluster information. + + :return: SearchBackendInfo containing cluster information and backend details + :raises SearchServiceError: When unable to connect to search backend + """ + pass + + @abstractmethod + def index_aip( + self, + uuid: str, + aip_stored_path: str, + parser: Any, + name: str, + aip_size: int, + am_version: str, + indexed_at: str, + identifiers: list[dict[str, str]], + aips_in_aic: Optional[int] = None, + encrypted: bool = False, + location: str = "", + dashboard_uuid: str = "", + ) -> int: + """Index AIP and AIP files. + + :param str uuid: UUID of the AIP to index + :param str aip_stored_path: Path on disk where the AIP is located + :param Any parser: AIPMETSParser instance + :param str name: AIP name + :param int aip_size: AIP size in bytes + :param str am_version: Archivematica version + :param str indexed_at: Timestamp when indexing occurred + :param list[dict[str, str]] identifiers: Additional identifiers (MODS, Islandora, etc.) + :param Optional[int] aips_in_aic: Number of AIPs stored in AIC + :param bool encrypted: Whether AIP is encrypted + :param str location: AIP location + :param str dashboard_uuid: Pipeline UUID + :return: 0 if succeeded, 1 otherwise + """ + pass + + @abstractmethod + def index_transfer( + self, + uuid: str, + path: str, + size: int, + transfer_index_data: Any, + pending_deletion: bool = False, + printfn: Any = print, + dashboard_uuid: str = "", + transfer_name: str = "", + accession_id: str = "", + ingest_date: str = "", + ) -> int: + """Index Transfer and Transfer files. + + :param str uuid: UUID of the transfer to index + :param str path: path on disk, including the transfer directory and trailing / + :param int size: size of transfer in bytes + :param Any transfer_index_data: pre-computed transfer file index data + :param bool pending_deletion: whether transfer is pending deletion + :param printfn: optional print function + :param str dashboard_uuid: Pipeline UUID + :param str transfer_name: name of Transfer + :param str accession_id: optional accession ID + :param str ingest_date: date Transfer was indexed + :return: 0 if succeeded, 1 otherwise + """ + pass + + +class TermQuery(TypedDict): + term: dict[str, str] + + +class TermsQuery(TypedDict): + terms: dict[str, list[str]] + + +class DeleteByQueryBody(TypedDict): + query: Union[TermQuery, TermsQuery] + + +class ScriptDict(TypedDict): + source: str + params: dict[str, Union[str, bool, list[str]]] + + +class UpdateByQueryBody(TypedDict): + script: ScriptDict + query: TermQuery + + +class ElasticsearchSearchService(SearchService): + """Elasticsearch implementation of SearchService.""" + + def __init__( + self, + client: Elasticsearch, + transfer_files_index: str = "transferfiles", + aip_files_index: str = "aipfiles", + aips_index: str = "aips", + transfers_index: str = "transfers", + max_query_size: int = 10000, + ) -> None: + """Initialize with Elasticsearch client and configuration. + + :param Elasticsearch client: Elasticsearch client instance + :param str transfer_files_index: Name of the transfer files index + :param str aip_files_index: Name of the AIP files index + :param str aips_index: Name of the AIPs index + :param int max_query_size: Maximum size for Elasticsearch queries + """ + self.client = client + self.transfer_files_index = transfer_files_index + self.aip_files_index = aip_files_index + self.aips_index = aips_index + self.transfers_index = transfers_index + self.max_query_size = max_query_size + + def _escape_slashes(self, value: str) -> str: + """Escape forward slashes for Elasticsearch queries. + + :param str value: value to escape + :return: value with forward slashes escaped + """ + return str(value).replace("/", "\\/") + + def _delete_by_field( + self, index: str, field: str, value: Union[str, set[str]] + ) -> None: + """Delete documents from Elasticsearch index by field value(s). + + :param str index: Elasticsearch index name + :param str field: Field name to query on + :param Union[str, set[str]] value: Single value or set of values to match + """ + if not value: + return + + query: DeleteByQueryBody + if isinstance(value, str): + query = {"query": {"term": {field: self._escape_slashes(value)}}} + else: + escaped_value = [self._escape_slashes(v) for v in value] + query = {"query": {"terms": {field: escaped_value}}} + + self.client.delete_by_query(index=index, body=query) + + def _update_by_field( + self, + index: str, + field: str, + value: str, + update_field: str, + update_value: Union[str, bool, list[str]], + ) -> None: + """Update documents in Elasticsearch index by field value. + + :param str index: Elasticsearch index name + :param str field: Field name to query on + :param str value: Value to match in query field + :param str update_field: Field to update + :param Union[str, bool, list[str]] update_value: Value to set in update field + """ + escaped_value = self._escape_slashes(value) + query = self._build_update_query( + field, escaped_value, update_field, update_value + ) + self.client.update_by_query(index=index, body=query) + + def _build_update_query( + self, + query_field: str, + query_value: str, + update_field: str, + update_value: Union[str, bool, list[str]], + ) -> UpdateByQueryBody: + """Build an update_by_query body for Elasticsearch. + + :param str query_field: Field to query on + :param str query_value: Value to match in query field + :param str update_field: Field to update + :param Union[str, bool, list[str]] update_value: Value to set in update field + :return: Dictionary representing the update query body + """ + return { + "script": { + "source": f"ctx._source.{update_field} = params.value", + "params": {"value": update_value}, + }, + "query": {"term": {query_field: query_value}}, + } + + def _update_field_for_package_and_files( + self, + package_index: str, + files_index: str, + package_uuid_field: str, + package_uuid: str, + field: str, + value: Union[str, bool], + ) -> None: + """Update field for a package and its related files using bulk operations. + + :param str package_index: Name of package index to update + :param str files_index: Name of files index to update + :param str package_uuid_field: Name of ES field for package UUID in files index + :param str package_uuid: UUID of package to update + :param str field: Field in indices to update + :param Union[str, bool] value: Value to set in updated field + """ + self._update_by_field(package_index, ES_FIELD_UUID, package_uuid, field, value) + self._update_by_field( + files_index, package_uuid_field, package_uuid, field, value + ) + + def _update_transfer_pending_deletion( + self, transfer_uuid: str, pending: bool + ) -> None: + """Update pending_deletion field for a transfer and its files. + + :param str transfer_uuid: Transfer UUID to update + :param bool pending: Whether transfer is pending deletion + """ + self._update_field_for_package_and_files( + self.transfers_index, + self.transfer_files_index, + ES_FIELD_SIPUUID, + transfer_uuid, + "pending_deletion", + pending, + ) + + def _update_aip_pending_deletion(self, aip_uuid: str, pending: bool) -> None: + """Update status field for an AIP and its files based on pending deletion. + + :param str aip_uuid: AIP UUID to update + :param bool pending: Whether AIP is pending deletion + """ + status_value = STATUS_DELETE_REQUESTED if pending else STATUS_UPLOADED + self._update_field_for_package_and_files( + self.aips_index, + self.aip_files_index, + ES_FIELD_AIPUUID, + aip_uuid, + ES_FIELD_STATUS, + status_value, + ) + + def delete_transfer(self, transfer_uuid: str) -> None: + """Delete transfer from the Elasticsearch index. + + :param str transfer_uuid: Transfer UUID to delete + """ + self._delete_by_field(self.transfers_index, ES_FIELD_UUID, transfer_uuid) + + def delete_transfer_files(self, transfer_uuids: set[str]) -> None: + """Delete transfer files from the Elasticsearch index. + + :param set[str] transfer_ids: Set of transfer UUIDs to delete files for + """ + self._delete_by_field( + self.transfer_files_index, ES_FIELD_SIPUUID, transfer_uuids + ) + + def delete_aip(self, aip_uuid: str) -> None: + """Delete AIP from the Elasticsearch index. + + :param str aip_uuid: AIP UUID to delete + """ + self._delete_by_field(self.aips_index, ES_FIELD_UUID, aip_uuid) + + def delete_aip_files(self, aip_uuid: str) -> None: + """Delete AIP files from the Elasticsearch index. + + :param str aip_uuid: AIP UUID to delete files for + """ + self._delete_by_field(self.aip_files_index, ES_FIELD_AIPUUID, aip_uuid) + + def mark_transfer_for_deletion(self, transfer_uuid: str) -> None: + """Mark transfer for deletion in the Elasticsearch index. + + :param str transfer_uuid: Transfer UUID to mark for deletion + """ + self._update_transfer_pending_deletion(transfer_uuid, True) + + def unmark_transfer_for_deletion(self, transfer_uuid: str) -> None: + """Unmark transfer for deletion in the Elasticsearch index. + + :param str transfer_uuid: Transfer UUID to unmark for deletion + """ + self._update_transfer_pending_deletion(transfer_uuid, False) + + def mark_aip_for_deletion(self, aip_uuid: str) -> None: + """Mark AIP for deletion in the Elasticsearch index. + + :param str aip_uuid: AIP UUID to mark for deletion + """ + self._update_aip_pending_deletion(aip_uuid, True) + + def unmark_aip_for_deletion(self, aip_uuid: str) -> None: + """Unmark AIP for deletion in the Elasticsearch index. + + :param str aip_uuid: AIP UUID to unmark for deletion + """ + self._update_aip_pending_deletion(aip_uuid, False) + + def ensure_indexes_exist(self, indexes: list[str]) -> None: + """Ensure the specified search indexes exist. + + Creates any missing indexes with their appropriate settings and mappings. + + :param list[str] indexes: List of index names to ensure exist + """ + if not indexes or self.client.indices.exists(index=indexes): + return + + valid_indexes = { + self.aips_index, + self.aip_files_index, + self.transfers_index, + self.transfer_files_index, + } + + for index in indexes: + if index not in valid_indexes: + continue + + self._create_index(index) + + def _create_index(self, index: str) -> None: + """Create an Elasticsearch index with settings and mappings. + + :param str index: Name of the index to create + """ + settings = self._get_index_settings_for_index(index) + mappings = self._get_index_mappings_for_index(index) + + try: + self.client.indices.create( + index=index, + body={"settings": settings, "mappings": mappings}, + ) + except RequestError as e: + if not e.error == "resource_already_exists_exception": + raise + + def _get_index_settings_for_index(self, index: str) -> dict[str, Any]: + """Get the settings for a specific index. + + :param str index: Name of the index + :return: Dictionary containing settings + """ + return self._get_index_settings() + + def _get_index_mappings_for_index(self, index: str) -> dict[str, Any]: + """Get the mapping properties for a specific index. + + :param str index: Name of the index + :return: Dictionary containing mapping properties for the index + """ + if index == self.aips_index: + return self._get_aips_index_mappings() + elif index == self.aip_files_index: + return self._get_aipfiles_index_mappings() + elif index == self.transfers_index: + return self._get_transfers_index_mappings() + elif index == self.transfer_files_index: + return self._get_transferfiles_index_mappings() + else: + raise ValueError(f"Unknown index: {index}") + + def _get_index_settings(self) -> dict[str, Any]: + """Return common settings applied to all Elasticsearch indexes.""" + return { + "index": { + "mapping": { + "total_fields": {"limit": TOTAL_FIELDS_LIMIT}, + "depth": {"limit": DEPTH_LIMIT}, + }, + "analysis": { + "analyzer": { + "file_path_and_name": { + "tokenizer": "char_tokenizer", + "filter": ["lowercase"], + } + }, + "tokenizer": { + "char_tokenizer": { + "type": "char_group", + "tokenize_on_chars": ["-", "_", ".", "/", "\\"], + } + }, + }, + } + } + + def _get_aips_index_mappings(self) -> dict[str, Any]: + """Return field mappings for the AIPs index.""" + return { + "date_detection": False, + "properties": { + ES_FIELD_NAME: { + "type": "text", + "fields": {"raw": {"type": "keyword"}}, + "analyzer": "file_path_and_name", + }, + ES_FIELD_SIZE: {"type": "double"}, + ES_FIELD_UUID: {"type": "keyword"}, + ES_FIELD_ACCESSION_IDS: {"type": "keyword"}, + ES_FIELD_STATUS: {"type": "keyword"}, + ES_FIELD_FILECOUNT: {"type": "integer"}, + ES_FIELD_LOCATION: {"type": "keyword"}, + }, + } + + def _get_aipfiles_index_mappings(self) -> dict[str, Any]: + """Return field mappings for the AIP files index.""" + return { + "date_detection": False, + "properties": { + "sipName": { + "type": "text", + "fields": {"raw": {"type": "keyword"}}, + "analyzer": "file_path_and_name", + }, + ES_FIELD_AIPUUID: {"type": "keyword"}, + ES_FIELD_FILEUUID: {"type": "keyword"}, + "isPartOf": {"type": "keyword"}, + ES_FIELD_AICID: {"type": "keyword"}, + "indexedAt": {"type": "double"}, + "filePath": { + "type": "text", + "fields": {"raw": {"type": "keyword"}}, + "analyzer": "file_path_and_name", + }, + "fileExtension": {"type": "text"}, + "origin": {"type": "text"}, + "identifiers": {"type": "keyword"}, + "accessionid": {"type": "keyword"}, + ES_FIELD_STATUS: {"type": "keyword"}, + }, + } + + def _get_transfers_index_mappings(self) -> dict[str, Any]: + """Return field mappings for the transfers index.""" + return { + "properties": { + ES_FIELD_NAME: { + "type": "text", + "fields": {"raw": {"type": "keyword"}}, + "analyzer": "file_path_and_name", + }, + ES_FIELD_STATUS: {"type": "text"}, + "ingest_date": {"type": "date", "format": "date_optional_time"}, + ES_FIELD_SIZE: {"type": "long"}, + ES_FIELD_FILECOUNT: {"type": "integer"}, + ES_FIELD_UUID: {"type": "keyword"}, + "accessionid": {"type": "keyword"}, + "pending_deletion": {"type": "boolean"}, + } + } + + def _get_transferfiles_index_mappings(self) -> dict[str, Any]: + """Return field mappings for the transfer files index.""" + return { + "properties": { + "filename": { + "type": "text", + "fields": {"raw": {"type": "keyword"}}, + "analyzer": "file_path_and_name", + }, + "relative_path": { + "type": "text", + "analyzer": "file_path_and_name", + }, + ES_FIELD_FILEUUID_LOWER: {"type": "keyword"}, + ES_FIELD_SIPUUID: {"type": "keyword"}, + "accessionid": {"type": "keyword"}, + ES_FIELD_STATUS: {"type": "keyword"}, + "origin": {"type": "keyword"}, + "ingestdate": {"type": "date", "format": "date_optional_time"}, + "modification_date": { + "type": "date", + "format": "date_optional_time", + "ignore_malformed": True, + }, + ES_FIELD_CREATED: {"type": "double"}, + ES_FIELD_SIZE: {"type": "double"}, + "tags": {"type": "keyword"}, + "file_extension": {"type": "keyword"}, + "bulk_extractor_reports": {"type": "keyword"}, + "format": { + "type": "nested", + "properties": { + "puid": {"type": "keyword"}, + "format": {"type": "text"}, + "group": {"type": "text"}, + }, + }, + "pending_deletion": {"type": "boolean"}, + } + } + + def delete_indexes(self, indexes: list[str]) -> None: + """Delete the specified search indexes. + + :param list[str] indexes: List of index names to delete + """ + if not indexes: + return + + try: + self.client.indices.delete(index=indexes) + except NotFoundError: + pass + + def update_index_mappings(self, index: str, mappings: dict[str, Any]) -> None: + """Update mappings for a specific index. + + :param str index: Index name to update + :param dict[str, Any] mappings: Mapping properties to apply + """ + self.client.indices.put_mapping(index=index, body=mappings) + + def reindex_from_remote( + self, remote_config: dict[str, Any], index_mappings: list[dict[str, str]] + ) -> dict[str, Any]: + """Reindex data from a remote Elasticsearch cluster. + + :param dict[str, Any] remote_config: Remote cluster configuration + :param list[dict[str, str]] index_mappings: Source to destination index mappings + :return: Results of reindex operations + """ + results: dict[str, list[dict[str, Any]]] = {"successful": [], "failed": []} + + for mapping in index_mappings: + body = { + "source": { + "remote": remote_config, + "index": mapping["source_index"], + "size": remote_config.get("size", 10), + }, + "dest": {"index": mapping["dest_index"]}, + } + + try: + response = self.client.reindex(body=body) + results["successful"].append( + { + "index": mapping["dest_index"], + "response": response, + } + ) + except Exception as exc: + results["failed"].append( + { + "index": mapping["dest_index"], + "error": str(exc), + } + ) + + return results + + def update_all_documents(self, index: str) -> dict[str, Any]: + """Update all documents in an index using update_by_query. + + Useful for populating new mapping fields after schema changes. + + :param str index: Index name to update + :return: Update operation results + """ + return dict(self.client.update_by_query(index=index)) + + def _search_by_term( + self, + index: str, + field: str, + value: str, + fields: Optional[list[str]] = None, + size: Optional[int] = None, + ) -> dict[str, Any]: + """Execute a term search query. + + :param str index: Elasticsearch index to search + :param str field: Field name to query on + :param str value: Value to search for + :param Optional[list[str]] fields: Optional list of fields to return + :param Optional[int] size: Optional size limit for results + :return: Raw search results from Elasticsearch + """ + query = {"query": {"term": {field: value}}} + return self._search_index(index, query, size=size, fields=fields) + + def _get_single_document_by_field( + self, + index: str, + field: str, + value: str, + fields: Optional[list[str]] = None, + not_found_exception_class: type[SearchServiceError] = SearchServiceError, + error_context: str = "document", + ) -> dict[str, Any]: + """Get a single document by field value with error handling. + + :param str index: Elasticsearch index to search + :param str field: Field name to query on + :param str value: Value to search for + :param Optional[list[str]] fields: Optional list of fields to return + :param type[SearchServiceError] not_found_exception_class: Exception class to raise when not found + :param str error_context: Context description for error messages + :return: Document data + :raises SearchServiceError: When no document is found or multiple documents are found + """ + results = self._search_by_term(index, field, value, fields=fields) + + count = results["hits"]["total"]["value"] + if count == 0: + raise not_found_exception_class( + f"No {error_context} found with {field} {value}" + ) + if count > 1: + raise MultipleResultsError( + f"{count} {error_context}s found with {field} {value}; unable to fetch a single result" + ) + + return dict(results["hits"]["hits"][0]) + + def get_aip_data( + self, uuid: str, fields: Optional[list[str]] = None + ) -> dict[str, Any]: + """Get AIP data by UUID. + + :param str uuid: AIP UUID to retrieve + :param Optional[list[str]] fields: Optional list of fields to return + :return: AIP document data + :raises AIPNotFoundError: When no AIP is found with the given UUID + :raises MultipleResultsError: When multiple AIPs are found with the same UUID + """ + return self._get_single_document_by_field( + index=self.aips_index, + field=ES_FIELD_UUID, + value=uuid, + fields=fields, + not_found_exception_class=AIPNotFoundError, + error_context="AIP", + ) + + def get_aipfile_data( + self, uuid: str, fields: Optional[list[str]] = None + ) -> dict[str, Any]: + """Get AIP file data by file UUID. + + :param str uuid: File UUID to retrieve + :param Optional[list[str]] fields: Optional list of fields to return + :return: AIP file document data + :raises AIPFileNotFoundError: When no file is found with the given UUID + :raises MultipleResultsError: When multiple files are found with the same UUID + """ + return self._get_single_document_by_field( + index=self.aip_files_index, + field=ES_FIELD_FILEUUID, + value=uuid, + fields=fields, + not_found_exception_class=AIPFileNotFoundError, + error_context="AIP file", + ) + + def get_aipfile_by_document_id(self, document_id: str) -> dict[str, Any]: + """Get AIP file document by Elasticsearch document ID. + + :param str document_id: Elasticsearch document ID + :return: Complete document data including metadata + :raises AIPFileNotFoundError: When document is not found + """ + try: + return dict(self.client.get(index=self.aip_files_index, id=document_id)) + except Exception as e: + raise AIPFileNotFoundError( + f"AIP file document {document_id} not found: {e}" + ) + + def get_transfer_file_tags(self, uuid: str) -> list[str]: + """Get tags for a transfer file by file UUID. + + :param str uuid: File UUID to retrieve tags for + :return: List of tags for the file + :raises TransferFileNotFoundError: When no file is found with the given UUID + :raises MultipleResultsError: When multiple files are found with the same UUID + """ + document = self._get_single_document_by_field( + index=self.transfer_files_index, + field=ES_FIELD_FILEUUID_LOWER, + value=uuid, + fields=["tags"], + not_found_exception_class=TransferFileNotFoundError, + error_context="transfer file", + ) + + source = document.get("_source", {}) + tags = source.get("tags", []) + return list(tags) if tags else [] + + def get_transfer_file_data(self, uuid: str) -> dict[str, Any]: + """Get transfer file data by file UUID. + + :param str uuid: File UUID to search for + :return: Transfer file document data + :raises SearchServiceError: When no results found or other search errors occur + """ + results = {} + field = ES_FIELD_FILEUUID_LOWER + + documents = self._search_by_term( + self.transfer_files_index, field, uuid, size=self.max_query_size + ) + + result_count = len(documents["hits"]["hits"]) + if result_count == 0: + raise TransferFileNotFoundError( + f"No transfer file found with fileuuid {uuid}" + ) + elif result_count == 1: + results = documents["hits"]["hits"][0]["_source"] + elif result_count > 1: + # Elasticsearch can rank results for different filenames above the queried file. + # Filter to ensure we only consider exact matches for the UUID. + filtered_results = [ + result + for result in documents["hits"]["hits"] + if result["_source"][field] == uuid + ] + + result_count = len(filtered_results) + if result_count == 1: + results = filtered_results[0]["_source"] + elif result_count > 1: + results = filtered_results[0]["_source"] + elif result_count < 1: + raise TransferFileNotFoundError( + "get_transfer_file_data returned no exact results" + ) + + return results + + def set_transfer_file_tags(self, uuid: str, tags: list[str]) -> None: + """Set tags for a transfer file by file UUID using update_by_query. + + :param str uuid: File UUID to update tags for + :param list[str] tags: List of tags to set (empty list clears tags) + """ + escaped_uuid = self._escape_slashes(uuid) + query = self._build_update_query( + ES_FIELD_FILEUUID_LOWER, escaped_uuid, "tags", tags + ) + self.client.update_by_query(index=self.transfer_files_index, body=query) + + def _search_index( + self, + index: str, + query: dict[str, Any], + size: Optional[int] = None, + from_: Optional[int] = None, + sort: Optional[SortSpec] = None, + fields: Optional[list[str]] = None, + ) -> dict[str, Any]: + """Search an index with optional parameters. + + :param str index: Elasticsearch index name to search + :param dict[str, Any] query: Elasticsearch query body + :param Optional[int] size: Optional size limit for results + :param Optional[int] from_: Optional start index for pagination + :param Optional[SortSpec] sort: Optional sort specification with field and order + :param Optional[list[str]] fields: Optional list of fields to return + :return: Raw search results from Elasticsearch + """ + search_params: dict[str, Any] = { + "index": index, + "body": query, + } + + # Add optional parameters directly to search_params for the Elasticsearch client. + # The client expects these as keyword arguments, not nested in a params dict. + if size is not None: + search_params["size"] = size + if from_ is not None: + search_params["from_"] = from_ + if sort is not None: + search_params["sort"] = [{sort.field: {"order": sort.order}}] + if fields is not None: + search_params["_source"] = fields + + return dict(self.client.search(**search_params)) + + def search_transfer_files( + self, + query: dict[str, Any], + size: Optional[int] = None, + from_: Optional[int] = None, + sort: Optional[SortSpec] = None, + fields: Optional[list[str]] = None, + ) -> dict[str, Any]: + """Search transfer files with optional parameters. + + :param dict[str, Any] query: Elasticsearch query body + :param Optional[int] size: Optional size limit for results, defaults to max_query_size + :param Optional[int] from_: Optional start index for pagination + :param Optional[SortSpec] sort: Optional sort specification with field and order + :param Optional[list[str]] fields: Optional list of fields to return + :return: Raw search results from Elasticsearch + """ + if size is None: + size = self.max_query_size + return self._search_index( + self.transfer_files_index, query, size, from_, sort, fields + ) + + def search_transfers( + self, + query: dict[str, Any], + size: Optional[int] = None, + from_: Optional[int] = None, + sort: Optional[SortSpec] = None, + fields: Optional[list[str]] = None, + ) -> dict[str, Any]: + """Search transfers with optional parameters. + + :param dict[str, Any] query: Elasticsearch query body + :param Optional[int] size: Optional size limit for results, defaults to max_query_size + :param Optional[int] from_: Optional start index for pagination + :param Optional[SortSpec] sort: Optional sort specification with field and order + :param Optional[list[str]] fields: Optional list of fields to return + :return: Raw search results from Elasticsearch + """ + if size is None: + size = self.max_query_size + return self._search_index( + self.transfers_index, query, size, from_, sort, fields + ) + + def search_aips( + self, + query: dict[str, Any], + size: Optional[int] = None, + from_: Optional[int] = None, + sort: Optional[SortSpec] = None, + fields: Optional[list[str]] = None, + ) -> dict[str, Any]: + """Search AIPs with optional parameters. + + :param dict[str, Any] query: Elasticsearch query body + :param Optional[int] size: Optional size limit for results, defaults to max_query_size + :param Optional[int] from_: Optional start index for pagination + :param Optional[SortSpec] sort: Optional sort specification with field and order + :param Optional[list[str]] fields: Optional list of fields to return + :return: Raw search results from Elasticsearch + """ + if size is None: + size = self.max_query_size + return self._search_index(self.aips_index, query, size, from_, sort, fields) + + def search_aip_files( + self, + query: dict[str, Any], + size: Optional[int] = None, + from_: Optional[int] = None, + sort: Optional[SortSpec] = None, + fields: Optional[list[str]] = None, + ) -> dict[str, Any]: + """Search AIP files with optional parameters. + + :param dict[str, Any] query: Elasticsearch query body + :param Optional[int] size: Optional size limit for results, defaults to max_query_size + :param Optional[int] from_: Optional start index for pagination + :param Optional[SortSpec] sort: Optional sort specification with field and order + :param Optional[list[str]] fields: Optional list of fields to return + :return: Raw search results from Elasticsearch + """ + if size is None: + size = self.max_query_size + return self._search_index( + self.aip_files_index, query, size, from_, sort, fields + ) + + def count_aip_files(self, query: dict[str, Any]) -> int: + """Count AIP files matching the given query. + + :param dict[str, Any] query: Elasticsearch query body + :return: Number of AIP files matching the query + """ + try: + result = self.client.count(index=self.aip_files_index, body=query) + return int(result["count"]) + except Exception: + return 0 + + def get_cluster_info(self) -> SearchBackendInfo: + """Get Elasticsearch cluster information. + + :return: SearchBackendInfo containing Elasticsearch cluster information + :raises SearchServiceError: When unable to connect to Elasticsearch + """ + try: + es_info = dict(self.client.info()) + return SearchBackendInfo( + name=es_info["name"], + version=es_info["version"]["number"], + cluster_name=es_info.get("cluster_name"), + raw_info=es_info, + ) + except Exception as err: + raise SearchServiceError(f"Unable to connect to Elasticsearch: {err}") + + def _wait_for_cluster_yellow_status( + self, wait_between_tries: int = 10, max_tries: int = 10 + ) -> None: + """Wait for Elasticsearch cluster to reach yellow status. + + :param int wait_between_tries: Seconds to wait between health checks + :param int max_tries: Maximum number of tries before giving up + """ + health: dict[str, Any] = {} + health["status"] = None + tries = 0 + + while ( + health["status"] != "yellow" + and health["status"] != "green" + and tries < max_tries + ): + tries = tries + 1 + + try: + health = self.client.cluster.health() + except Exception: + health["status"] = None + + if health["status"] != "yellow" and health["status"] != "green": + time.sleep(wait_between_tries) + + def _try_to_index( + self, + data: dict[str, Any], + index: str, + wait_between_tries: int = 10, + max_tries: int = 10, + printfn: Any = print, + ) -> None: + """Try to index data in Elasticsearch with retry logic. + + :param dict[str, Any] data: Data to index + :param str index: Index name to store the document + :param int wait_between_tries: Seconds to wait between retries + :param int max_tries: Maximum number of attempts + :param printfn: Print function for output messages + """ + exception = None + if max_tries < 1: + raise ValueError("max_tries must be 1 or greater") + for _ in range(0, max_tries): + try: + self.client.index(index=index, body=data) + return + except Exception as e: + exception = e + printfn("ERROR: error trying to index.") + printfn(e) + time.sleep(wait_between_tries) + + if exception: + raise exception + + def _bulk_index( + self, generator: Generator[dict[str, Any], None, None], chunk_size: int = 50 + ) -> tuple[int, list[dict[str, Any]]]: + """Perform bulk indexing operation using elasticsearch helpers. + + :param Generator[dict[str, Any], None, None] generator: Generator yielding documents to index + :param int chunk_size: Number of documents to process in each batch + :return: Tuple of (number of documents indexed, list of failed documents) + """ + success, errors = bulk( + self.client, generator, chunk_size=chunk_size, stats_only=False + ) + return success, errors + + def _index_aip_files( + self, + uuid: str, + name: str, + am_version: str, + indexed_at: str, + identifiers: list[dict[str, str]], + dashboard_uuid: str = "", + parser: Any = None, + ) -> tuple[int, list[str]]: + """Index AIP files from AIP with UUID `uuid`. + + :param str uuid: UUID of the AIP being indexed + :param str name: AIP name + :param str am_version: Archivematica version + :param str indexed_at: Timestamp when indexing occurred + :param list[dict[str, str]] identifiers: Additional identifiers + :param str dashboard_uuid: Pipeline UUID + :param Any parser: AIPMETSParser instance + :return: Tuple of (number of files indexed, list of accession numbers) + """ + accession_ids = set() + + def _generator() -> Generator[dict[str, Any], None, None]: + for file_data, file_accession_ids in parser.files(): + file_data.update( + { + "archivematicaVersion": am_version, + "AIPUUID": uuid, + "sipName": name, + "indexedAt": indexed_at, + "isPartOf": parser.is_part_of, + ES_FIELD_AICID: parser.aic_identifier, + "origin": dashboard_uuid, + ES_FIELD_STATUS: STATUS_UPLOADED, + } + ) + file_data["transferMetadata"] = ( + parser.aip_metadata + file_data["transferMetadata"] + ) + file_data["identifiers"] = identifiers + file_data["identifiers"] + accession_ids.update(file_accession_ids) + + yield { + "_op_type": "index", + "_index": self.aip_files_index, + "_source": file_data, + } + + file_count, _ = self._bulk_index(_generator(), chunk_size=50) + return file_count, list(accession_ids) + + def _index_transfer_files( + self, + uuid: str, + path: str, + transfer_name: str, + accession_id: str, + ingest_date: str, + transfer_index_data: Any, + pending_deletion: bool = False, + printfn: Any = print, + dashboard_uuid: str = "", + ) -> int: + """Index files in the Transfer with UUID `uuid` at path `path`. + + :param str uuid: UUID of the Transfer in the DB + :param str path: path on disk, including the transfer directory and trailing / + :param str transfer_name: name of Transfer + :param str accession_id: optional accession ID + :param str ingest_date: date Transfer was indexed + :param Any transfer_index_data: pre-computed transfer file index data + :param bool pending_deletion: whether transfer is pending deletion + :param printfn: optional print function + :param str dashboard_uuid: Pipeline UUID + :return: number of files indexed + """ + + def _generator() -> Generator[dict[str, Any], None, None]: + """Memory-efficient generator that processes pre-filtered files one at a time.""" + for filepath in transfer_index_data.file_paths: + # Gather filesystem metadata (minimal per-file operations) + filename = os.path.basename(filepath) + stripped_path = filepath.replace(path, transfer_name + "/") + file_extension = os.path.splitext(filepath)[1][1:].lower() + size = os.path.getsize(filepath) / (1024 * 1024) # Size in MB + create_time = os.stat(filepath).st_ctime + + # Compute currentlocation for database lookup + currentlocation = "%transferDirectory%" + os.path.relpath( + filepath, path + ).removeprefix("data/") + + # Look up database information from pre-computed cache + file_record = transfer_index_data.files_by_location.get(currentlocation) + if file_record: + file_uuid = str(file_record.uuid) + formats = transfer_index_data.format_cache.get(file_uuid, []) + modification_date = ( + file_record.modificationtime.strftime("%Y-%m-%d") + if file_record.modificationtime + else "" + ) + else: + file_uuid = "" + formats = [] + modification_date = "" + + # Get bulk extractor reports from cache + bulk_extractor_reports = transfer_index_data.bulk_extractor_reports.get( + file_uuid, [] + ) + + index_data = { + "filename": filename, + "relative_path": stripped_path, + ES_FIELD_FILEUUID_LOWER: file_uuid, + ES_FIELD_CREATED: create_time, + "modification_date": modification_date, + ES_FIELD_SIZE: size, + "file_extension": file_extension, + "bulk_extractor_reports": bulk_extractor_reports, + "format": formats, + ES_FIELD_SIPUUID: uuid, + "accessionid": accession_id, + ES_FIELD_STATUS: STATUS_BACKLOG, + "origin": dashboard_uuid, + "ingestdate": ingest_date, + "tags": [], + "pending_deletion": pending_deletion, + } + + printfn( + f"Indexing {index_data['relative_path']} (UUID: {index_data['fileuuid']})" + ) + + yield { + "_op_type": "index", + "_index": self.transfer_files_index, + "_source": index_data, + } + + self._wait_for_cluster_yellow_status() + files_indexed, _ = self._bulk_index(_generator(), chunk_size=50) + + return files_indexed + + def index_aip( + self, + uuid: str, + aip_stored_path: str, + parser: Any, + name: str, + aip_size: int, + am_version: str, + indexed_at: str, + identifiers: list[dict[str, str]], + aips_in_aic: Optional[int] = None, + encrypted: bool = False, + location: str = "", + dashboard_uuid: str = "", + ) -> int: + """Index AIP and AIP files. + + :param str uuid: UUID of the AIP to index + :param str aip_stored_path: Path on disk where the AIP is located + :param Any parser: AIPMETSParser instance + :param str name: AIP name + :param int aip_size: AIP size in bytes + :param str am_version: Archivematica version + :param str indexed_at: Timestamp when indexing occurred + :param list[dict[str, str]] identifiers: Additional identifiers + :param Optional[int] aips_in_aic: Number of AIPs stored in AIC + :param bool encrypted: Whether AIP is encrypted + :param str location: AIP location + :param str dashboard_uuid: Pipeline UUID + :return: 0 if succeeded, 1 otherwise + """ + try: + files_indexed, accession_ids = self._index_aip_files( + uuid=uuid, + name=name, + am_version=am_version, + indexed_at=indexed_at, + identifiers=identifiers, + dashboard_uuid=dashboard_uuid, + parser=parser, + ) + + print(f"Files indexed: {files_indexed}") + print("Indexing AIP ...") + + aip_data = { + ES_FIELD_UUID: uuid, + ES_FIELD_NAME: name, + "filePath": aip_stored_path, + ES_FIELD_SIZE: int(aip_size) / (1024 * 1024), + ES_FIELD_FILECOUNT: files_indexed, + "origin": dashboard_uuid, + ES_FIELD_CREATED: parser.created, + ES_FIELD_AICID: parser.aic_identifier, + "isPartOf": parser.is_part_of, + ES_FIELD_AICCOUNT: aips_in_aic, + "identifiers": identifiers, + "transferMetadata": parser.aip_metadata, + ES_FIELD_ENCRYPTED: encrypted, + ES_FIELD_ACCESSION_IDS: accession_ids, + ES_FIELD_STATUS: STATUS_UPLOADED, + ES_FIELD_LOCATION: location, + } + + self._wait_for_cluster_yellow_status() + self._try_to_index(aip_data, self.aips_index) + + return 0 + except Exception as e: + logger.error(f"Failed to index AIP {uuid}: {e}") + return 1 + + def index_transfer( + self, + uuid: str, + path: str, + size: int, + transfer_index_data: Any, + pending_deletion: bool = False, + printfn: Any = print, + dashboard_uuid: str = "", + transfer_name: str = "", + accession_id: str = "", + ingest_date: str = "", + ) -> int: + """Index Transfer and Transfer files. + + :param str uuid: UUID of the transfer to index + :param str path: path on disk, including the transfer directory and trailing / + :param int size: size of transfer in bytes + :param Any transfer_index_data: pre-computed transfer file index data + :param bool pending_deletion: whether transfer is pending deletion + :param printfn: optional print function + :param str dashboard_uuid: Pipeline UUID + :param str transfer_name: name of Transfer + :param str accession_id: optional accession ID + :param str ingest_date: date Transfer was indexed + :return: 0 if succeeded, 1 otherwise + """ + try: + files_indexed = self._index_transfer_files( + uuid, + path, + transfer_name, + accession_id, + ingest_date, + transfer_index_data, + pending_deletion=pending_deletion, + printfn=printfn, + dashboard_uuid=dashboard_uuid, + ) + + printfn("Files indexed: " + str(files_indexed)) + printfn("Indexing Transfer ...") + + transfer_data = { + ES_FIELD_NAME: transfer_name, + ES_FIELD_STATUS: STATUS_BACKLOG, + "accessionid": accession_id, + "ingest_date": ingest_date, + ES_FIELD_FILECOUNT: files_indexed, + ES_FIELD_SIZE: int(size), + ES_FIELD_UUID: uuid, + "pending_deletion": pending_deletion, + } + + self._wait_for_cluster_yellow_status() + self._try_to_index(transfer_data, self.transfers_index, printfn=printfn) + + return 0 + except Exception as e: + logger.error(f"Failed to index transfer {uuid}: {e}") + return 1 + + +def _create_elasticsearch_client( + hosts: Union[str, list[str], tuple[str, ...]], timeout: int = DEFAULT_TIMEOUT +) -> Elasticsearch: + """Create configured Elasticsearch client. + + :param hosts: Elasticsearch hosts (string, list or tuple) + :param timeout: Connection timeout in seconds + :return: Configured Elasticsearch client + """ + return Elasticsearch(**{"hosts": hosts, "request_timeout": timeout}) + + +def setup_search_service( + hosts: Union[str, list[str], tuple[str, ...]], + timeout: int = DEFAULT_TIMEOUT, + enabled: tuple[str, ...] = (AIPS_INDEX, TRANSFERS_INDEX), +) -> SearchService: + """Initialize and return the search service with Elasticsearch client. + + Also creates the required indexes if they don't exist based on the enabled parameter. + + :param hosts: Elasticsearch hosts (string, list or tuple) + :param timeout: Connection timeout in seconds + :param enabled: Enabled indexes tuple + :return: Configured SearchService instance + """ + global _search_service_instance + + client = _create_elasticsearch_client(hosts, timeout) + _search_service_instance = ElasticsearchSearchService(client) + + indexes = [] + if AIPS_INDEX in enabled: + indexes.extend([AIPS_INDEX, AIP_FILES_INDEX]) + if TRANSFERS_INDEX in enabled: + indexes.extend([TRANSFERS_INDEX, TRANSFER_FILES_INDEX]) + + if indexes: + _search_service_instance.ensure_indexes_exist(indexes) + else: + logger.warning("Setting up the search service without enabled indexes.") + + return _search_service_instance + + +def setup_search_service_from_conf(settings: Any) -> SearchService: + """Initialize search service using Django settings. + + :param settings: Django settings object + :return: Configured SearchService instance + """ + return setup_search_service( + settings.ELASTICSEARCH_SERVER, + settings.ELASTICSEARCH_TIMEOUT, + settings.SEARCH_ENABLED, + ) + + +def get_search_service_instance() -> Optional["SearchService"]: + """Get the current search service instance if initialized. + + :return: SearchService instance or None if not initialized + """ + return _search_service_instance diff --git a/src/archivematica/search/utils.py b/src/archivematica/search/utils.py new file mode 100644 index 0000000000..c47487a15c --- /dev/null +++ b/src/archivematica/search/utils.py @@ -0,0 +1,17 @@ +def augment_raw_search_results(raw_results): + """Normalize search results response. + + This function takes JSON returned by an ES query and returns the source + document for each result. + + :param raw_results: the raw JSON result from an elastic search query + :return: JSON result simplified, with document_id set + """ + modifiedResults = [] + + for item in raw_results["hits"]["hits"]: + clone = item["_source"].copy() + clone["document_id"] = item["_id"] + modifiedResults.append(clone) + + return modifiedResults diff --git a/src/archivematica/settings/__init__.py b/src/archivematica/settings/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/archivematica/settings/testmysql.py b/src/archivematica/settings/testmysql.py new file mode 100644 index 0000000000..b740a02b15 --- /dev/null +++ b/src/archivematica/settings/testmysql.py @@ -0,0 +1,18 @@ +import os + +from archivematica.MCPClient.settings.testmysql import * +from archivematica.MCPServer.settings.testmysql import * +from archivematica.dashboard.settings.testmysql import * + +DATABASES = { + "default": { + "ENGINE": os.environ.get("TEST_DB_ENGINE", "django.db.backends.mysql"), + "NAME": os.environ.get("TEST_DB_NAME", "ARCHIVEMATICATEST"), + "USER": os.environ.get("TEST_DB_USER", "archivematica"), + "PASSWORD": os.environ.get("TEST_DB_PASSWORD", "demo"), + "HOST": os.environ.get("TEST_DB_HOST", "mysql"), + "PORT": os.environ.get("TEST_DB_PORT", "3306"), + "CONN_MAX_AGE": 600, + "TEST": {"NAME": f"test_{os.environ.get('TEST_DB_NAME', 'ARCHIVEMATICATEST')}"}, + } +} diff --git a/src/archivematicaCommon/lib/elasticSearchFunctions.py b/src/archivematicaCommon/lib/elasticSearchFunctions.py deleted file mode 100644 index 76acbc83a2..0000000000 --- a/src/archivematicaCommon/lib/elasticSearchFunctions.py +++ /dev/null @@ -1,1565 +0,0 @@ -# This file is part of Archivematica. -# -# Copyright 2010-2012 Artefactual Systems Inc. -# -# Archivematica is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Archivematica is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Archivematica. If not, see . -import calendar -import copy -import datetime -import logging -import os -import re -import sys -import time - -import namespaces as ns -import version -from archivematicaFunctions import get_dashboard_uuid -from django.db.models import Min -from django.db.models import Q -from elasticsearch import Elasticsearch -from elasticsearch import ImproperlyConfigured -from elasticsearch.helpers import bulk -from externals import xmltodict -from lxml import etree -from main.models import File -from main.models import Identifier -from main.models import Transfer - -logger = logging.getLogger("archivematica.common") - -STATUS_DELETE_REQUESTED = "DEL_REQ" -STATUS_DELETED = "DELETED" -STATUS_UPLOADED = "UPLOADED" - -AIPS_INDEX = "aips" -AIP_FILES_INDEX = "aipfiles" -TRANSFERS_INDEX = "transfers" -TRANSFER_FILES_INDEX = "transferfiles" - -ES_FIELD_AICID = "AICID" -ES_FIELD_ACCESSION_IDS = "accessionids" -ES_FIELD_AICCOUNT = "countAIPsinAIC" -ES_FIELD_CREATED = "created" -ES_FIELD_ENCRYPTED = "encrypted" -ES_FIELD_FILECOUNT = "file_count" -ES_FIELD_LOCATION = "location" -ES_FIELD_NAME = "name" -ES_FIELD_SIZE = "size" -ES_FIELD_STATUS = "status" -ES_FIELD_UUID = "uuid" - - -class ElasticsearchError(Exception): - """Not operational errors.""" - - pass - - -class EmptySearchResultError(ElasticsearchError): - pass - - -class TooManyResultsError(ElasticsearchError): - pass - - -_es_hosts = None -_es_client = None -DEFAULT_TIMEOUT = 10 -# Known indexes. This indexes may be enabled or not based on the SEARCH_ENABLED -# setting. To add a new index, make sure it's related to the setting values in -# the setup functions below, add its name to the following array and create a -# function declaring the index settings and mapping. For example, for an index -# called `tests` the function must be called `_get_tests_index_body`. See the -# functions related to the current known indexes for examples. -INDEXES = [AIPS_INDEX, AIP_FILES_INDEX, TRANSFERS_INDEX, TRANSFER_FILES_INDEX] -# A doc type is still required in ES 6.x but it's limited to one per index. -# It will be removed in ES 7.x, so we'll use the same for all indexes. -DOC_TYPE = "_doc" -# Maximun ES result window. Use the scroll API for a better way to get all -# results or change `index.max_result_window` on each index settings. -MAX_QUERY_SIZE = 10000 -# Maximun amount of fields per index (increased from the ES default of 1000). -TOTAL_FIELDS_LIMIT = 10000 -# Maximum index depth (increased from the ES default of 20). The way the -# `structMap` element from the METS file is parsed may create a big depth -# in documents for AIPs with a big directories hierarchy. -DEPTH_LIMIT = 1000 - - -def setup(hosts, timeout=DEFAULT_TIMEOUT, enabled=(AIPS_INDEX, TRANSFERS_INDEX)): - """Initialize and share the Elasticsearch client. - - Share it as the attribute _es_client in the current module. An additional - attribute _es_hosts is defined containing the Elasticsearch hosts (expected - types are: string, list or tuple). Also, the existence of the enabled - indexes is checked to be able to create the required indexes on the fly if - they don't exist. - """ - global _es_hosts - global _es_client - - _es_hosts = hosts - _es_client = Elasticsearch( - **{"hosts": _es_hosts, "timeout": timeout, "dead_timeout": 2} - ) - - # TODO: Do we really need to be able to create the indexes - # on the fly? Could we force to run the rebuild commands and - # avoid doing this on each setup? - indexes = [] - if AIPS_INDEX in enabled: - indexes.extend([AIPS_INDEX, AIP_FILES_INDEX]) - if TRANSFERS_INDEX in enabled: - indexes.extend([TRANSFERS_INDEX, TRANSFER_FILES_INDEX]) - if len(indexes) > 0: - create_indexes_if_needed(_es_client, indexes) - else: - logger.warning( - "Setting up the Elasticsearch client " "without enabled indexes." - ) - - -def setup_reading_from_conf(settings): - setup( - settings.ELASTICSEARCH_SERVER, - settings.ELASTICSEARCH_TIMEOUT, - settings.SEARCH_ENABLED, - ) - - -def get_client(): - """Obtain the current Elasticsearch client. - - If undefined, an exception will be raised. This function also checks the - integrity of the indexes expected by this application and populate them - when they cannot be found. - """ - if not _es_client: - raise ImproperlyConfigured( - "The Elasticsearch client has not been set up yet. Please call setup() first." - ) - return _es_client - - -def _wait_for_cluster_yellow_status(client, wait_between_tries=10, max_tries=10): - health = {} - health["status"] = None - tries = 0 - - # Wait for either yellow or green status - while ( - health["status"] != "yellow" - and health["status"] != "green" - and tries < max_tries - ): - tries = tries + 1 - - try: - health = client.cluster.health() - except Exception: - print("ERROR: failed health check.") - health["status"] = None - - # Sleep if cluster not healthy - if health["status"] != "yellow" and health["status"] != "green": - print("Cluster not in yellow or green state... waiting to retry.") - time.sleep(wait_between_tries) - - -def _get_sip_identifiers(uuid): - # Also index Directory identifiers so the AIP can be found through them - return list( - Identifier.objects.filter(Q(sip=uuid) | Q(directory__sip=uuid)).values_list( - "value", flat=True - ) - ) - - -def _get_file_identifiers(uuid): - return list(Identifier.objects.filter(file=uuid).values_list("value", flat=True)) - - -# -------------- -# CREATE INDEXES -# -------------- - - -def create_indexes_if_needed(client, indexes): - """Checks if the indexes passed exist in the client. - - Otherwise, creates the missing ones with their settings and mappings. - """ - if client.indices.exists(index=",".join(indexes)): - logger.info("All indexes already created.") - return - for index in indexes: - if index not in INDEXES: - logger.warning('Index "%s" not recognized. Skipping.', index) - continue - # Call get index body functions below for each index - body = getattr(sys.modules[__name__], "_get_%s_index_body" % index)() - logger.info('Creating "%s" index ...', index) - client.indices.create(index, body=body, ignore=400) - logger.info("Index created.") - - -def _get_aips_index_body(): - """Get settings and mappings for `aips` index. - - The mapping is dynamic and not a full representation of the final documents. - For example, the AIP directories AMD and DMD sections are parsed from the - METS file and added to a `transferMetadata` field. - """ - return { - "settings": _get_index_settings(), - "mappings": { - DOC_TYPE: { - "date_detection": False, - "properties": { - ES_FIELD_NAME: { - "type": "text", - "fields": {"raw": {"type": "keyword"}}, - "analyzer": "file_path_and_name", - }, - ES_FIELD_SIZE: {"type": "double"}, - ES_FIELD_UUID: {"type": "keyword"}, - ES_FIELD_ACCESSION_IDS: {"type": "keyword"}, - ES_FIELD_STATUS: {"type": "keyword"}, - ES_FIELD_FILECOUNT: {"type": "integer"}, - ES_FIELD_LOCATION: {"type": "keyword"}, - }, - } - }, - } - - -def _get_aipfiles_index_body(): - """Get settings and mappings for `aipfiles` index. - - The mapping is dynamic and not a full representation of the final documents. - For example, the files AMD and DMD sections are parsed from the METS file - and added to a `METS` field. - """ - return { - "settings": _get_index_settings(), - "mappings": { - DOC_TYPE: { - "date_detection": False, - "properties": { - "sipName": { - "type": "text", - "fields": {"raw": {"type": "keyword"}}, - "analyzer": "file_path_and_name", - }, - "AIPUUID": {"type": "keyword"}, - "FILEUUID": {"type": "keyword"}, - "isPartOf": {"type": "keyword"}, - ES_FIELD_AICID: {"type": "keyword"}, - "indexedAt": {"type": "double"}, - "filePath": { - "type": "text", - "fields": {"raw": {"type": "keyword"}}, - "analyzer": "file_path_and_name", - }, - "fileExtension": {"type": "text"}, - "origin": {"type": "text"}, - "identifiers": {"type": "keyword"}, - "accessionid": {"type": "keyword"}, - ES_FIELD_STATUS: {"type": "keyword"}, - }, - } - }, - } - - -def _get_transfers_index_body(): - return { - "settings": _get_index_settings(), - "mappings": { - DOC_TYPE: { - "properties": { - ES_FIELD_NAME: { - "type": "text", - "fields": {"raw": {"type": "keyword"}}, - "analyzer": "file_path_and_name", - }, - ES_FIELD_STATUS: {"type": "text"}, - "ingest_date": {"type": "date", "format": "dateOptionalTime"}, - ES_FIELD_SIZE: {"type": "long"}, - ES_FIELD_FILECOUNT: {"type": "integer"}, - ES_FIELD_UUID: {"type": "keyword"}, - "accessionid": {"type": "keyword"}, - "pending_deletion": {"type": "boolean"}, - } - } - }, - } - - -def _get_transferfiles_index_body(): - return { - "settings": _get_index_settings(), - "mappings": { - DOC_TYPE: { - "properties": { - "filename": { - "type": "text", - "fields": {"raw": {"type": "keyword"}}, - "analyzer": "file_path_and_name", - }, - "relative_path": {"type": "text", "analyzer": "file_path_and_name"}, - "fileuuid": {"type": "keyword"}, - "sipuuid": {"type": "keyword"}, - "accessionid": {"type": "keyword"}, - ES_FIELD_STATUS: {"type": "keyword"}, - "origin": {"type": "keyword"}, - "ingestdate": {"type": "date", "format": "dateOptionalTime"}, - # METS.xml files in transfers sent to backlog will have '' - # as their modification_date value. This can cause a - # failure in certain cases, see: - # https://github.com/artefactual/archivematica/issues/719. - # For this reason, we specify the type and format here and - # ignore malformed values like ''. - "modification_date": { - "type": "date", - "format": "dateOptionalTime", - "ignore_malformed": True, - }, - ES_FIELD_CREATED: {"type": "double"}, - ES_FIELD_SIZE: {"type": "double"}, - "tags": {"type": "keyword"}, - "file_extension": {"type": "keyword"}, - "bulk_extractor_reports": {"type": "keyword"}, - "format": { - "type": "nested", - "properties": { - "puid": {"type": "keyword"}, - "format": {"type": "text"}, - "group": {"type": "text"}, - }, - }, - "pending_deletion": {"type": "boolean"}, - } - } - }, - } - - -def _get_index_settings(): - """Returns a dictionary with the settings used in all indexes.""" - return { - "index": { - "mapping": { - "total_fields": {"limit": TOTAL_FIELDS_LIMIT}, - "depth": {"limit": DEPTH_LIMIT}, - }, - "analysis": { - "analyzer": { - # Use the char_group tokenizer to split paths and filenames, - # including file extensions, which avoids the overhead of - # the pattern tokenizer. - "file_path_and_name": { - "tokenizer": "char_tokenizer", - "filter": ["lowercase"], - } - }, - "tokenizer": { - "char_tokenizer": { - "type": "char_group", - "tokenize_on_chars": ["-", "_", ".", "/", "\\"], - } - }, - }, - } - } - - -# --------------- -# INDEX RESOURCES -# --------------- - - -def index_aip_and_files( - client, - uuid, - aip_stored_path, - mets_staging_path, - name, - aip_size, - aips_in_aic=None, - identifiers=None, - encrypted=False, - location="", - printfn=print, -): - """Index AIP and AIP files with UUID `uuid` at path `path`. - - :param client: The ElasticSearch client. - :param uuid: The UUID of the AIP we're indexing. - :param aip_stored_path: path on disk where the AIP is located. - :param mets_staging_path: path on disk where the AIP METS file is located. - :param name: AIP name. - :param aip_size: AIP size. - :param aips_in_aic: optional number of AIPs stored in AIC. - :param identifiers: optional additional identifiers (MODS, Islandora, etc.). - :param encrypted: optional AIP encrypted boolean (defaults to `False`). - :param printfn: optional print funtion. - :return: 0 is succeded, 1 otherwise. - """ - # Stop if METS file is not at staging path. - error_message = None - if not os.path.exists(mets_staging_path): - error_message = "METS file does not exist at: " + mets_staging_path - if error_message: - logger.error(error_message) - printfn(error_message, file=sys.stderr) - return 1 - - tree = etree.parse(mets_staging_path) - _remove_tool_output_from_mets(tree) - root = tree.getroot() - # Extract AIC identifier, other specially-indexed information - aic_identifier = None - is_part_of = None - dublincore = ns.xml_find_premis( - root, "mets:dmdSec/mets:mdWrap/mets:xmlData/dcterms:dublincore" - ) - if dublincore is not None: - aip_type = ns.xml_findtext_premis( - dublincore, "dc:type" - ) or ns.xml_findtext_premis(dublincore, "dcterms:type") - if aip_type == "Archival Information Collection": - aic_identifier = ns.xml_findtext_premis( - dublincore, "dc:identifier" - ) or ns.xml_findtext_premis(dublincore, "dcterms:identifier") - is_part_of = ns.xml_findtext_premis(dublincore, "dcterms:isPartOf") - - # Pull the create time from the METS header. - # Old METS did not use `metsHdr`. - created = time.time() - mets_hdr = ns.xml_find_premis(root, "mets:metsHdr") - if mets_hdr is not None: - mets_created_attr = mets_hdr.get("CREATEDATE") - if mets_created_attr: - try: - created = calendar.timegm( - time.strptime(mets_created_attr, "%Y-%m-%dT%H:%M:%S") - ) - except ValueError: - printfn("Failed to parse METS CREATEDATE: %s" % (mets_created_attr)) - - if identifiers is None: - identifiers = [] - identifiers += _get_sip_identifiers(uuid) - - aip_metadata = _get_aip_metadata(root) - - printfn("AIP UUID: " + uuid) - printfn("Indexing AIP files ...") - - (files_indexed, accession_ids) = _index_aip_files( - client=client, - uuid=uuid, - mets=root, - name=name, - identifiers=identifiers, - aip_metadata=aip_metadata, - ) - - printfn("Files indexed: " + str(files_indexed)) - printfn("Indexing AIP ...") - - aip_data = { - ES_FIELD_UUID: uuid, - ES_FIELD_NAME: name, - "filePath": aip_stored_path, - ES_FIELD_SIZE: int(aip_size) / (1024 * 1024), - ES_FIELD_FILECOUNT: files_indexed, - "origin": get_dashboard_uuid(), - ES_FIELD_CREATED: created, - ES_FIELD_AICID: aic_identifier, - "isPartOf": is_part_of, - ES_FIELD_AICCOUNT: aips_in_aic, - "identifiers": identifiers, - "transferMetadata": aip_metadata, - ES_FIELD_ENCRYPTED: encrypted, - ES_FIELD_ACCESSION_IDS: accession_ids, - ES_FIELD_STATUS: STATUS_UPLOADED, - ES_FIELD_LOCATION: location, - } - - _wait_for_cluster_yellow_status(client) - _try_to_index(client, aip_data, AIPS_INDEX, printfn=printfn) - printfn("Done.") - - return 0 - - -def _index_aip_files(client, uuid, mets, name, identifiers=None, aip_metadata=None): - """Index AIP files from AIP with UUID `uuid` and METS at path `mets_path`. - - :param client: The ElasticSearch client. - :param uuid: The UUID of the AIP we're indexing. - :param mets: root Element of the METS document. - :param name: AIP name. - :param identifiers: optional additional identifiers (MODS, Islandora, etc.). - :param aip_metadata: list with the descriptive and administrative metadata - of each directory in the AIP - :return: number of files indexed, list of accession numbers - """ - - # Extract isPartOf (for AIPs) or identifier (for AICs) from DublinCore. - dublincore = ns.xml_find_premis( - mets, "mets:dmdSec/mets:mdWrap/mets:xmlData/dcterms:dublincore" - ) - aic_identifier = None - is_part_of = None - if dublincore is not None: - aip_type = ns.xml_findtext_premis( - dublincore, "dc:type" - ) or ns.xml_findtext_premis(dublincore, "dcterms:type") - if aip_type == "Archival Information Collection": - aic_identifier = ns.xml_findtext_premis( - dublincore, "dc:identifier" - ) or ns.xml_findtext_premis(dublincore, "dcterms:identifier") - elif aip_type == "Archival Information Package": - is_part_of = ns.xml_findtext_premis(dublincore, "dcterms:isPartOf") - - if identifiers is None: - identifiers = [] - - if aip_metadata is None: - aip_metadata = [] - - # Use a set to ensure accession numbers are unique without needing to - # iterate through the list each time to check. - accession_ids = set() - - # Establish the structure to be indexed for each file item. - fileData = { - "archivematicaVersion": version.get_version(), - "AIPUUID": uuid, - "sipName": name, - "FILEUUID": "", - "indexedAt": time.time(), - "filePath": "", - "fileExtension": "", - "isPartOf": is_part_of, - ES_FIELD_AICID: aic_identifier, - "METS": {"dmdSec": {}, "amdSec": {}}, - "origin": get_dashboard_uuid(), - "accessionid": "", - ES_FIELD_STATUS: STATUS_UPLOADED, - } - - # Index all files in a fileGrup with USE='original' or USE='metadata'. - original_files = ns.xml_findall_premis( - mets, "mets:fileSec/mets:fileGrp[@USE='original']/mets:file" - ) - metadata_files = ns.xml_findall_premis( - mets, "mets:fileSec/mets:fileGrp[@USE='metadata']/mets:file" - ) - files = original_files + metadata_files - - def _generator(): - # Index AIC METS file if it exists. - for file_ in files: - # Make a deep copy of dict, not a copy of dict contents. - indexData = fileData.copy() - - # Get file UUID. If an ADMID exists, look in the amdSec for the UUID, - # otherwise parse it out of the file ID. - # 'Original' files have ADMIDs, 'Metadata' files do not. - admID = file_.attrib.get("ADMID", None) - if admID is None: - # Parse UUID from the file ID. - fileUUID = None - uuix_regex = r"\w{8}-?\w{4}-?\w{4}-?\w{4}-?\w{12}" - uuids = re.findall(uuix_regex, file_.attrib["ID"]) - # Multiple UUIDs may be returned - if they are all identical, - # use that UUID, otherwise use None. - # To determine all UUIDs are identical, use the size of the set. - if len(set(uuids)) == 1: - fileUUID = uuids[0] - else: - amdSec = _get_amdSec(admID, mets) - fileUUID = _get_file_uuid(amdSec) - accession_id = _get_accession_number(amdSec) - if accession_id is not None: - indexData["accessionid"] = accession_id - accession_ids.add(accession_id) - - # Index amdSec information. - xml = etree.tostring(amdSec, encoding="utf8") - indexData["METS"]["amdSec"] = _normalize_dict(xmltodict.parse(xml)) - - indexData["FILEUUID"] = fileUUID - - file_metadata = [] - - # Get the parent division for the file pointer by searching the - # physical structural map section (structMap). - file_id = file_.attrib.get("ID", None) - file_pointer_division = ns.xml_find_premis( - mets, - f"mets:structMap[@TYPE='physical']//mets:fptr[@FILEID='{file_id}']/..", - ) - if file_pointer_division is not None: - descriptive_metadata = _get_file_metadata(file_pointer_division, mets) - if descriptive_metadata: - file_metadata.append(descriptive_metadata) - # If the parent division has a DMDID attribute then index - # its data from the descriptive metadata section (dmdSec). - dmd_section_id = file_pointer_division.attrib.get("DMDID", None) - if dmd_section_id is not None: - # dmd_section_id can contain one id (e.g., "dmdSec_2") or - # more than one (e.g., "dmdSec_2 dmdSec_3", when a file - # has both DC and non-DC metadata). - # Attempt to index only the DC dmdSec if available. - for dmd_section_id_item in dmd_section_id.split(): - dmd_section_info = ns.xml_find_premis( - mets, - f"mets:dmdSec[@ID='{dmd_section_id_item}']/mets:mdWrap[@MDTYPE='DC']/mets:xmlData", - ) - if dmd_section_info is not None: - xml = etree.tostring(dmd_section_info, encoding="utf8") - data = _normalize_dict(xmltodict.parse(xml)) - indexData["METS"]["dmdSec"] = data - break - - indexData["transferMetadata"] = aip_metadata + file_metadata - - # Get file path from FLocat and extension. - filePath = ns.xml_find_premis(file_, "mets:FLocat").attrib[ - "{http://www.w3.org/1999/xlink}href" - ] - indexData["filePath"] = filePath - _, fileExtension = os.path.splitext(filePath) - if fileExtension: - indexData["fileExtension"] = fileExtension[1:].lower() - - indexData["identifiers"] = identifiers + _get_file_identifiers(fileUUID) - - yield { - "_op_type": "index", - "_index": AIP_FILES_INDEX, - "_type": DOC_TYPE, - "_source": indexData, - } - - # Reset fileData['METS']['amdSec'] and fileData['METS']['dmdSec'], - # since they are updated in the loop above. - # See http://stackoverflow.com/a/3975388 for explanation. - fileData["METS"]["amdSec"] = {} - fileData["METS"]["dmdSec"] = {} - - # Number of docs (chunk_size) defaults to 500 which is probably too big as - # we're potentially dealing with large documents (full amdSec embedded). - # It should be revisited once we make documents smaller. - bulk(client, _generator(), chunk_size=50) - - file_count = len(files) - accession_ids_list = list(accession_ids) - - return (file_count, accession_ids_list) - - -def index_transfer_and_files( - client, uuid, path, size, pending_deletion=False, printfn=print -): - """Indexes Transfer and Transfer files with UUID `uuid` at path `path`. - - :param client: The ElasticSearch client. - :param uuid: The UUID of the transfer we're indexing. - :param path: path on disk, including the transfer directory and a - trailing / but not including objects/. - :param size: size of transfer in bytes. - :param printfn: optional print funtion. - :return: 0 is succeded, 1 otherwise. - """ - # Stop if Transfer does not exist - if not os.path.exists(path): - error_message = "Transfer does not exist at: " + path - logger.error(error_message) - printfn(error_message, file=sys.stderr) - return 1 - - # Default status of a transfer file document in the index. - status = "backlog" - - transfer_name, accession_id, ingest_date = "", "", str(datetime.date.today()) - try: - transfer = Transfer.objects.get(uuid=uuid) - except Transfer.DoesNotExist: - pass - else: - transfer_name = transfer.currentlocation.split("/")[-2] - if transfer.accessionid: - accession_id = transfer.accessionid - # It doesn't seem that Archivematica records the ingestion date - # associated with the Transfer but we can look at the earliest file - # entry instead - as long as there is a match which may not always be - # the case. - dt = File.objects.filter(transfer=transfer).aggregate(Min("enteredsystem"))[ - "enteredsystem__min" - ] - if dt: - ingest_date = str(dt.date()) - - printfn("Transfer UUID: " + uuid) - printfn("Indexing Transfer files ...") - files_indexed = _index_transfer_files( - client, - uuid, - path, - transfer_name, - accession_id, - ingest_date, - pending_deletion=pending_deletion, - status=status, - printfn=printfn, - ) - - printfn("Files indexed: " + str(files_indexed)) - printfn("Indexing Transfer ...") - - transfer_data = { - ES_FIELD_NAME: transfer_name, - ES_FIELD_STATUS: status, - "accessionid": accession_id, - "ingest_date": ingest_date, - ES_FIELD_FILECOUNT: files_indexed, - ES_FIELD_SIZE: int(size), - ES_FIELD_UUID: uuid, - "pending_deletion": pending_deletion, - } - - _wait_for_cluster_yellow_status(client) - _try_to_index(client, transfer_data, TRANSFERS_INDEX, printfn=printfn) - printfn("Done.") - - return 0 - - -def _index_transfer_files( - client, - uuid, - path, - transfer_name, - accession_id, - ingest_date, - status="", - pending_deletion=False, - printfn=print, -): - """Indexes files in the Transfer with UUID `uuid` at path `path`. - - :param client: ElasticSearch client. - :param uuid: UUID of the Transfer in the DB. - :param path: path on disk, including the transfer directory and a - trailing / but not including objects/. - :param transfer_name: name of Transfer - :param accession_id: optional accession ID - :param ingest_date: date Transfer was indexed - :param status: optional Transfer status. - :param printfn: optional print funtion. - :return: number of files indexed. - """ - files_indexed = 0 - - # Some files should not be indexed. - # This should match the basename of the file. - ignore_files = ["processingMCP.xml"] - - # Get dashboard UUID - dashboard_uuid = get_dashboard_uuid() - - for filepath in _list_files_in_dir(path): - if os.path.isfile(filepath): - # We need to account for the possibility of dealing with a BagIt - # transfer package - the new default in Archivematica. - # The BagIt is created when the package is sent to backlog hence - # the locations in the database do not reflect the BagIt paths. - # Strip the "data/" part when looking up the file entry. - stripped_path = re.sub(r"^data/", "", os.path.relpath(filepath, path)) - currentlocation = "%transferDirectory%" + stripped_path - try: - f = File.objects.get( - currentlocation=currentlocation.encode(), transfer_id=uuid - ) - file_uuid = str(f.uuid) - formats = _get_file_formats(f) - bulk_extractor_reports = _list_bulk_extractor_reports(path, file_uuid) - if f.modificationtime is not None: - modification_date = f.modificationtime.strftime("%Y-%m-%d") - else: - modification_date = "" - except File.DoesNotExist: - file_uuid, modification_date = "", "" - formats = [] - bulk_extractor_reports = [] - - # Get file path info - stripped_path = filepath.replace(path, transfer_name + "/") - file_extension = os.path.splitext(filepath)[1][1:].lower() - filename = os.path.basename(filepath) - # Size in megabytes - size = os.path.getsize(filepath) / (1024 * 1024) - create_time = os.stat(filepath).st_ctime - - if filename not in ignore_files: - printfn(f"Indexing {stripped_path} (UUID: {file_uuid})") - - # TODO: Index Backlog Location UUID? - indexData = { - "filename": filename, - "relative_path": stripped_path, - "fileuuid": file_uuid, - "sipuuid": uuid, - "accessionid": accession_id, - ES_FIELD_STATUS: status, - "origin": dashboard_uuid, - "ingestdate": ingest_date, - ES_FIELD_CREATED: create_time, - "modification_date": modification_date, - ES_FIELD_SIZE: size, - "tags": [], - "file_extension": file_extension, - "bulk_extractor_reports": bulk_extractor_reports, - "format": formats, - "pending_deletion": pending_deletion, - } - - _wait_for_cluster_yellow_status(client) - _try_to_index(client, indexData, TRANSFER_FILES_INDEX, printfn=printfn) - - files_indexed = files_indexed + 1 - else: - printfn(f"Skipping indexing {stripped_path}") - - return files_indexed - - -def _try_to_index( - client, data, index, wait_between_tries=10, max_tries=10, printfn=print -): - exception = None - if max_tries < 1: - raise ValueError("max_tries must be 1 or greater") - for _ in range(0, max_tries): - try: - client.index(body=data, index=index, doc_type=DOC_TYPE) - return - except Exception as e: - exception = e - printfn("ERROR: error trying to index.") - printfn(e) - time.sleep(wait_between_tries) - - # If indexing did not succeed after max_tries is already complete, - # reraise the Elasticsearch exception to aid in debugging. - if exception: - raise exception - - -# ---------------- -# INDEXING HELPERS -# ---------------- - - -def _remove_tool_output_from_mets(doc): - """Remove tool output from a METS ElementTree. - - Given an ElementTree object, removes all objectsCharacteristicsExtensions - elements. This modifies the existing document in-place; it does not return - a new document. This helps index METS files, which might otherwise get too - large to be usable. - """ - root = doc.getroot() - - # Remove tool output nodes - toolNodes = ns.xml_findall_premis( - root, - "mets:amdSec/mets:techMD/mets:mdWrap/mets:xmlData/premis:object/premis:objectCharacteristics/premis:objectCharacteristicsExtension", - ) - - for parent in toolNodes: - parent.clear() - - print("Removed FITS output from METS.") - - -def _get_directories_with_metadata(container): - """Return Directory entries with metadata sections. - - Check if the entry references dmdSec (descriptive) or amdSec - (administrative) metadata sections. - """ - return ns.xml_xpath_premis( - container, './/mets:div[@TYPE="Directory"][@DMDID or @ADMID]' - ) - - -def _get_descriptive_section_metadata(dmdSec): - """Get dublin core and custom descriptive metadata.""" - result = [] - # look for dublincore terms in the dmdSec - result += ns.xml_findall_premis( - dmdSec, 'mets:mdWrap[@MDTYPE="DC"]/mets:xmlData/dcterms:dublincore' - ) - # look for non dublincore (custom) metadata - result += ns.xml_findall_premis(dmdSec, 'mets:mdWrap[@MDTYPE="OTHER"]/mets:xmlData') - return result - - -def _combine_elements(elements): - """Serialize all the elements as a JSON compatible string. - - Combine the elements contents into a single container and parse it - with xmltodict. - """ - # wrap the data from all metadata elements in a container - container = etree.Element("container") - for element in elements: - for child in element: - # add a copy to not modify the METS file in place - container.append(copy.deepcopy(child)) - # parse the container with xmltodict ignoring element attributes - return ( - xmltodict.parse( - etree.tostring(container, encoding="utf8"), xml_attribs=False - ).get("container") - or {} - ) - - -def _get_relative_path_element(directory): - """Build an element with the relative path of the directory.""" - TAG = "__DIRECTORY_LABEL__" - result = etree.Element("container") - path = etree.SubElement(result, TAG) - path.text = directory.attrib.get("LABEL", "") - return result - - -def _get_latest_dmd_secs(dmd_id, doc): - # Build a mapping of dmdSec by metadata type. - dmd_secs = {} - for id in dmd_id.split(): - dmd_sec = ns.xml_find_premis(doc, f'mets:dmdSec[@ID="{id}"]') - if not dmd_sec: - continue - # Use mdWrap MDTYPE and OTHERMDTYPE to generate a unique key. - md_wrap = ns.xml_find_premis(dmd_sec, "mets:mdWrap") - if not md_wrap: - continue - mdtype_key = md_wrap.attrib.get("MDTYPE") - # Ignore PREMIS:OBJECT. - if mdtype_key == "PREMIS:OBJECT": - continue - other_mdtype = md_wrap.attrib.get("OTHERMDTYPE") - if other_mdtype: - mdtype_key += "_" + other_mdtype - if mdtype_key not in dmd_secs: - dmd_secs[mdtype_key] = [] - dmd_secs[mdtype_key].append(dmd_sec) - # Get only the latest dmdSec by type. - final_dmd_secs = [] - for _, secs in dmd_secs.items(): - latest_date = "" - latest_sec = None - for sec in secs: - date = sec.attrib.get("CREATED", "") - if not latest_date or date > latest_date: - latest_date = date - latest_sec = sec - # Do not add latest dmdSec if it's deleted. - if latest_sec and latest_sec.attrib.get("STATUS", "") != "deleted": - final_dmd_secs.append(latest_sec) - return final_dmd_secs - - -def _get_file_metadata(file_pointer_division, doc): - """Get descriptive metadata for a file pointer. - - There are various types of metadata elements extracted: dublin core - and custom (non dublin core), both set through the metadata/metadata.csv - file; and metadata added through the source-metadata.csv files and - related XML files. - - These types are parsed and combined into a single dictionary of - metadata attributes. - """ - result = {} - elements_with_metadata = [] - dmd_id = file_pointer_division.attrib.get("DMDID") - if not dmd_id: - return result - for dmd_sec in _get_latest_dmd_secs(dmd_id, doc): - elements_with_metadata += _get_descriptive_section_metadata(dmd_sec) - if elements_with_metadata: - result = _combine_elements(elements_with_metadata) - return _normalize_dict(result) - - -def _get_directory_metadata(directory, doc): - """Get descriptive or administrive metadata for a directory. - - There are three types of metadata elements extracted: - - 1. Dublin core, set through the metadata/metadata.csv file or the - transfer/ingest metadata form in the dashboard - 2. Custom (non dublin core), set through the metadata/metadata.csv - file - 3. Bag/disk image metadata, set through the bag-info.txt file or - the disk image metadata form in the dashboard - 4. Custom XML metadata, set through the source-metadata.csv files - and related XML files - - These types are parsed and combined into a single dictionary of - metadata attributes. A marker element with the label of the - Directory entry is added to the result. - """ - result = {} - elements_with_metadata = [] - dmd_id = directory.attrib.get("DMDID") - if dmd_id: - for dmd_sec in _get_latest_dmd_secs(dmd_id, doc): - elements_with_metadata += _get_descriptive_section_metadata(dmd_sec) - for ADMID in directory.attrib.get("ADMID", "").split(): - amd_sec = ns.xml_find_premis(doc, f'mets:amdSec[@ID="{ADMID}"]') - if amd_sec is not None: - # look for bag/disk image metadata - elements_with_metadata += ns.xml_findall_premis( - amd_sec, "mets:sourceMD/mets:mdWrap/mets:xmlData/transfer_metadata" - ) - if elements_with_metadata: - # add an attribute with the relative path of the Directory entry - elements_with_metadata.append(_get_relative_path_element(directory)) - result = _combine_elements(elements_with_metadata) - return _normalize_dict(result) - - -def _get_aip_metadata(doc): - """Get metadata about the directories in the AIP. - - Given a doc representing a METS file, look for Directory entries - that have descriptive or administrative metadata in the physical - structMap and return dictionaries with metadata attributes for - each directory. - """ - result = [] - physical_struct_map = ns.xml_find_premis(doc, 'mets:structMap[@TYPE="physical"]') - if physical_struct_map is not None: - for directory in _get_directories_with_metadata(physical_struct_map): - directory_metadata = _get_directory_metadata(directory, doc) - if directory_metadata: - result.append(directory_metadata) - return result - - -def _normalize_dict(data): - """Normalize dictionary from xmltodict for ES indexing. - - Because an XML element may contain text value or other elements, conversion - to a dict can result in different value types for the same key. This causes - problems in the Elasticsearch index as it expects consistent types. This - function recurses a dict and appends "_dict" to the keys with a dict value. - """ - new = {} - for key, value in data.items(): - dict_key = key + "_dict" - if isinstance(value, dict): - new[dict_key] = _normalize_dict(value) - elif isinstance(value, list): - for item in value: - new_key = key - if isinstance(item, dict): - new_key = dict_key - item = _normalize_dict(item) - if new_key not in new: - new[new_key] = [] - new[new_key].append(item) - else: - new[key] = value - return new - - -def _get_file_formats(f): - formats = [] - fields = [ - "format_version__pronom_id", - "format_version__description", - "format_version__format__group__description", - ] - for puid, fmt, group in f.fileformatversion_set.all().values_list(*fields): - formats.append({"puid": puid, "format": fmt, "group": group}) - - return formats - - -def _list_bulk_extractor_reports(transfer_path, file_uuid): - reports = [] - log_path = os.path.join(transfer_path, "data", "logs", "bulk-" + file_uuid) - - if not os.path.isdir(log_path): - return reports - for report in ["telephone", "ccn", "ccn_track2", "pii"]: - path = os.path.join(log_path, report + ".txt") - if os.path.isfile(path) and os.path.getsize(path) > 0: - reports.append(report) - - return reports - - -def _list_files_in_dir(path, filepaths=None): - if filepaths is None: - filepaths = [] - - # Define entries - for file in os.listdir(path): - child_path = os.path.join(path, file) - filepaths.append(child_path) - - # If entry is a directory, recurse - if os.path.isdir(child_path) and os.access(child_path, os.R_OK): - _list_files_in_dir(child_path, filepaths) - - # Return fully traversed data - return filepaths - - -def _get_amdSec(admID, doc): - """Get amdSec with given admID. - - :param admID: admID. - :param doc: ElementTree object. - :return: amdSec. - """ - return ns.xml_find_premis(doc, f"mets:amdSec[@ID='{admID}']") - - -def _get_file_uuid(amdSec): - """Get UUID of a file from amdSec. - - :param amdSec: amdSec. - :return: File UUID. - """ - return ns.xml_findtext_premis( - amdSec, - "mets:techMD/mets:mdWrap/mets:xmlData/premis:object/premis:objectIdentifier/premis:objectIdentifierValue", - ) - - -def _get_accession_number(amdSec): - """Get accession number associated with a file from amdSec. - - Look for a entry within file's amdSec that has a - of "registration". Return the text value (with leading - "accession#" stripped out) from the . - - If no matching entry is found, return None. - - :param amdSec: amdSec. - :return: Accession number or None. - """ - registration_detail_notes = ns.xml_xpath_premis( - amdSec, - ".//premis:event[premis:eventType='registration']/premis:eventOutcomeInformation/premis:eventOutcomeDetail/premis:eventOutcomeDetailNote", - ) - if not registration_detail_notes: - return None - detail_text = registration_detail_notes[0].text - ACCESSION_PREFIX = "accession#" - return detail_text[len(ACCESSION_PREFIX) :] - - -# ------- -# QUERIES -# ------- - - -def search_all_results(client, body, index): - """Performs client.search with the size set to MAX_QUERY_SIZE. - - By default search_raw returns only 10 results. Since we usually want all - results, this is a wrapper that fetches MAX_QUERY_SIZE results and logs a - warning if more results were available. - """ - if isinstance(index, list): - index = ",".join(index) - - results = client.search(body=body, index=index, size=MAX_QUERY_SIZE) - - if results["hits"]["total"] > MAX_QUERY_SIZE: - logger.warning( - "Number of items in backlog (%s) exceeds maximum amount " "fetched (%s)", - results["hits"]["total"], - MAX_QUERY_SIZE, - ) - return results - - -def get_aip_data(client, uuid, fields=None): - search_params = { - "body": {"query": {"term": {ES_FIELD_UUID: uuid}}}, - "index": AIPS_INDEX, - } - - if fields: - search_params["_source"] = fields - - aips = client.search(**search_params) - - return aips["hits"]["hits"][0] - - -def get_aipfile_data(client, uuid, fields=None): - search_params = { - "body": {"query": {"term": {"FILEUUID": uuid}}}, - "index": AIP_FILES_INDEX, - } - - if fields: - search_params["_source"] = fields - - aipfiles = client.search(**search_params) - - return aipfiles["hits"]["hits"][0] - - -def _document_ids_from_field_query(client, index, field, value): - document_ids = [] - - # Escape /'s with \\ - searchvalue = str(value).replace("/", "\\/") - query = {"query": {"term": {field: searchvalue}}} - documents = search_all_results(client, body=query, index=index) - - if len(documents["hits"]["hits"]) > 0: - document_ids = [d["_id"] for d in documents["hits"]["hits"]] - - return document_ids - - -def _document_id_from_field_query(client, index, field, value): - document_id = None - ids = _document_ids_from_field_query(client, index, field, value) - if len(ids) == 1: - document_id = ids[0] - return document_id - - -def get_file_tags(client, uuid): - """Gets the tags of a given file by its UUID. - - Retrieve the complete set of tags for the file with the fileuuid `uuid`. - Returns a list of zero or more strings. - - :param Elasticsearch client: Elasticsearch client - :param str uuid: A file UUID. - """ - query = {"query": {"term": {"fileuuid": uuid}}} - - results = client.search(body=query, index=TRANSFER_FILES_INDEX, _source="tags") - - count = results["hits"]["total"] - if count == 0: - raise EmptySearchResultError(f"No matches found for file with UUID {uuid}") - if count > 1: - raise TooManyResultsError( - f"{count} matches found for file with UUID {uuid}; unable to fetch a single result" - ) - - result = results["hits"]["hits"][0] - # File has no tags - if "_source" not in result: - return [] - return result["_source"]["tags"] - - -def set_file_tags(client, uuid, tags): - """Updates the file(s) with the fileuuid `uuid` to the provided value(s). - - :param Elasticsearch client: Elasticsearch client - :param str uuid: A file UUID. - :param list tags: A list of zero or more tags. - Passing an empty list clears the file's tags. - """ - document_ids = _document_ids_from_field_query( - client, TRANSFER_FILES_INDEX, "fileuuid", uuid - ) - - count = len(document_ids) - if count == 0: - raise EmptySearchResultError(f"No matches found for file with UUID {uuid}") - if count > 1: - raise TooManyResultsError( - f"{count} matches found for file with UUID {uuid}; unable to fetch a single result" - ) - - body = {"doc": {"tags": tags}} - client.update( - body=body, index=TRANSFER_FILES_INDEX, doc_type=DOC_TYPE, id=document_ids[0] - ) - return True - - -def get_transfer_file_info(client, field, value): - """ - Get transferfile information from ElasticSearch with query field = value. - """ - logger.debug("get_transfer_file_info: field: %s, value: %s", field, value) - results = {} - query = {"query": {"term": {field: value}}} - documents = search_all_results(client, body=query, index=TRANSFER_FILES_INDEX) - result_count = len(documents["hits"]["hits"]) - if result_count == 1: - results = documents["hits"]["hits"][0]["_source"] - elif result_count > 1: - # Elasticsearch was sometimes ranking results for a different filename above - # the actual file being queried for; in that case only consider results - # where the value is an actual precise match. - filtered_results = [ - results - for results in documents["hits"]["hits"] - if results["_source"][field] == value - ] - - result_count = len(filtered_results) - if result_count == 1: - results = filtered_results[0]["_source"] - if result_count > 1: - results = filtered_results[0]["_source"] - logger.warning( - "get_transfer_file_info returned %s results for query %s: %s (using first result)", - result_count, - field, - value, - ) - elif result_count < 1: - logger.error( - "get_transfer_file_info returned no exact results for query %s: %s", - field, - value, - ) - raise ElasticsearchError("get_transfer_file_info returned no exact results") - - logger.debug("get_transfer_file_info: results: %s", results) - return results - - -# ------- -# DELETES -# ------- - - -def remove_backlog_transfer(client, uuid): - _delete_matching_documents(client, TRANSFERS_INDEX, ES_FIELD_UUID, uuid) - - -def remove_backlog_transfer_files(client, uuid): - _remove_transfer_files(client, uuid, "transfer") - - -def remove_sip_transfer_files(client, uuid): - _remove_transfer_files(client, uuid, "sip") - - -def _remove_transfer_files(client, uuid, unit_type=None): - if unit_type == "transfer": - transfers = {uuid} - else: - condition = Q(transfer_id=uuid) | Q(sip_id=uuid) - transfers = { - f[0] for f in File.objects.filter(condition).values_list("transfer_id") - } - - if len(transfers) > 0: - for transfer in transfers: - files = _document_ids_from_field_query( - client, TRANSFER_FILES_INDEX, "sipuuid", transfer - ) - if len(files) > 0: - for file_id in files: - client.delete( - index=TRANSFER_FILES_INDEX, doc_type=DOC_TYPE, id=file_id - ) - else: - if not unit_type: - unit_type = "transfer or SIP" - logger.warning("No transfers found for %s %s", unit_type, uuid) - - -def delete_aip(client, uuid): - _delete_matching_documents(client, AIPS_INDEX, ES_FIELD_UUID, uuid) - - -def delete_aip_files(client, uuid): - _delete_matching_documents(client, AIP_FILES_INDEX, "AIPUUID", uuid) - - -def _delete_matching_documents(client, index, field, value): - """Deletes all documents in index where field = value - - :param Elasticsearch client: Elasticsearch client - :param str index: Name of the index. E.g. 'aips' - :param str field: Field to query when deleting. E.g. 'uuid' - :param str value: Value of the field to query when deleting. E.g. 'cd0bb626-cf27-4ca3-8a77-f14496b66f04' - """ - query = {"query": {"term": {field: value}}} - logger.info("Deleting with query %s", query) - results = client.delete_by_query(index=index, body=query) - logger.info("Deleted by query %s", results) - - -# ------- -# UPDATES -# ------- - - -def _update_field(client, index, uuid, field, value): - document_id = _document_id_from_field_query(client, index, ES_FIELD_UUID, uuid) - - if document_id is None: - logger.error(f"Unable to find document with UUID {uuid} in index {index}") - return - - client.update( - body={"doc": {field: value}}, index=index, doc_type=DOC_TYPE, id=document_id - ) - - -def mark_aip_stored(client, uuid): - _update_field(client, AIPS_INDEX, uuid, ES_FIELD_STATUS, STATUS_UPLOADED) - - -def mark_aip_deletion_requested(client, uuid): - """Update AIP package and file indices to reflect AIP deletion request. - - :param client: ES client. - :param uuid: AIP UUID. - :return: None. - """ - _update_field_for_package_and_files( - client, AIPS_INDEX, AIP_FILES_INDEX, "AIPUUID", uuid, ES_FIELD_STATUS, "DEL_REQ" - ) - - -def mark_backlog_deletion_requested(client, uuid): - """Update transfer package and file indices to reflect backlog deletion request. - - :param client: ES client. - :param uuid: Transfer UUID. - :return: None. - """ - _update_field_for_package_and_files( - client, - TRANSFERS_INDEX, - TRANSFER_FILES_INDEX, - "sipuuid", - uuid, - "pending_deletion", - True, - ) - - -def revert_aip_deletion_request(client, uuid): - """Update AIP indices to reflect rejected deletion request - - :param client: ES client. - :param uuid: AIP UUID. - :returns: None. - """ - _update_field_for_package_and_files( - client, - AIPS_INDEX, - AIP_FILES_INDEX, - "AIPUUID", - uuid, - ES_FIELD_STATUS, - STATUS_UPLOADED, - ) - - -def revert_backlog_deletion_request(client, uuid): - """Update transfer indices to reflect rejected deletion request - - :param client: ES client. - :param uuid: Transfer UUID. - :returns: None. - """ - _update_field_for_package_and_files( - client, - TRANSFERS_INDEX, - TRANSFER_FILES_INDEX, - "sipuuid", - uuid, - "pending_deletion", - False, - ) - - -def _update_field_for_package_and_files( - client, package_index, files_index, package_uuid_field, package_uuid, field, value -): - """Update the specified field for a package and its related files - - :param client: ES client. - :param package_index: Name of package index to update. - :param files_index: Name of files index to update. - :param package_uuid_field: Name of ES field for package UUID in package_index. - :param package_uuid: UUID of package to update. - :param field: Field in indices to update. - :param value: Value to set in updated field. - :return: None. - """ - _update_field(client, package_index, package_uuid, field, value) - - files = _document_ids_from_field_query( - client, files_index, package_uuid_field, package_uuid - ) - for file_id in files: - client.update( - body={"doc": {field: value}}, - index=files_index, - doc_type=DOC_TYPE, - id=file_id, - ) - - -# --------------- -# RESULTS HELPERS -# --------------- - - -def augment_raw_search_results(raw_results): - """Normalize search results response. - - This function takes JSON returned by an ES query and returns the source - document for each result. - - :param raw_results: the raw JSON result from an elastic search query - :return: JSON result simplified, with document_id set - """ - modifiedResults = [] - - for item in raw_results["hits"]["hits"]: - clone = item["_source"].copy() - clone["document_id"] = item["_id"] - modifiedResults.append(clone) - - return modifiedResults diff --git a/src/archivematicaCommon/lib/externals/fiwalk_plugins/ficonfig.txt b/src/archivematicaCommon/lib/externals/fiwalk_plugins/ficonfig.txt deleted file mode 100644 index 9546270ae9..0000000000 --- a/src/archivematicaCommon/lib/externals/fiwalk_plugins/ficonfig.txt +++ /dev/null @@ -1,5 +0,0 @@ -# -# Configuration file for fiwalk -# - -* dgi python /usr/lib/archivematica/archivematicaCommon/externals/fiwalk_plugins/pronom_ident.py diff --git a/src/archivematicaCommon/lib/externals/fiwalk_plugins/pronom_ident.py b/src/archivematicaCommon/lib/externals/fiwalk_plugins/pronom_ident.py deleted file mode 100755 index b9dd56fce8..0000000000 --- a/src/archivematicaCommon/lib/externals/fiwalk_plugins/pronom_ident.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env python -""" -pronom-ident.py - Identify a bitstream against PRONOM; uses fido -""" - -# https://github.com/anarchivist/fiwalk-dgi/blob/master/python/pronom_ident.py -# Author anarchivist -import os -import sys -import time -from optparse import OptionParser - -from fido import fido - - -class FiwalkFido(fido.Fido): - """docstring for FiwalkFido""" - - def __init__(self, **kwargs): - fido.Fido.__init__(self, kwargs) - self.handle_matches = self.parse_matches - - def identify_file(self, filename): - """Identify the type of @param filename.""" - self.current_file = filename - try: - t0 = time.clock() - f = open(filename, "rb") - size = os.stat(filename)[6] - self.current_filesize = size - bofbuffer, eofbuffer, __ = self.get_buffers(f, size, seekable=True) - matches = self.match_formats(bofbuffer, eofbuffer) - # from here is also repeated in walk_zip - # we should make this uniform in next version! - # - # filesize is made conditional because files with 0 bytes - # are falsely characterised being 'rtf' - # in these cases we try to match the extension instead - if len(matches) > 0 and self.current_filesize > 0: - return self.handle_matches( - filename, matches, time.clock() - t0, "signature" - ) - elif len(matches) == 0 or self.current_filesize == 0: - matches = self.match_extensions(filename) - return self.handle_matches( - filename, matches, time.clock() - t0, "extension" - ) - # till here matey! - if self.zip: - self.identify_contents(filename, type=self.container_type(matches)) - except OSError: - # print >> sys.stderr, "FIDO: Error in identify_file: Path is {0}".format(filename) - sys.stderr.write(f"FIDO: Error in identify_file: Path is {filename}\n") - - def parse_matches(self, fullname, matches, delta_t, matchtype=""): - out = {} - out["pronomSoftware"] = "fido " + fido.version - out["pronomTotalMatches"] = len(matches) - if len(matches) == 0: - out["pronomMatchType"] = "fail" - else: - i = 0 - for f, s in matches: - i += 1 - out["pronomMatchType"] = matchtype - out["pronomPuid"] = self.get_puid(f) - out["pronomFormatName"] = f.find("name").text - out["pronomSignatureName"] = s.find("name").text - mime = f.find("mime") - out["pronomFormatMimeType"] = mime.text if mime is not None else None - version = f.find("version") - out["pronomFormatVersion"] = ( - version.text if version is not None else None - ) - alias = f.find("alias") - out["pronomFormatAlias"] = alias.text if alias is not None else None - return out - - -def pronom_ident(fn): - f = FiwalkFido(quiet=True) - return f.identify_file(fn) - - -def main(): - parser = OptionParser() - opts, args = parser.parse_args() - - if len(args) < 1: - parser.print_help() - exit(-1) - - filename = args[0] - out = pronom_ident(filename) - - for k, v in out.items(): - if v is not None: - print(k + ": " + str(v)) - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/src/dashboard/src/components/accounts/backends.py b/src/dashboard/src/components/accounts/backends.py deleted file mode 100644 index 38cda4608d..0000000000 --- a/src/dashboard/src/components/accounts/backends.py +++ /dev/null @@ -1,118 +0,0 @@ -import json - -from components.helpers import generate_api_key -from django.conf import settings -from django.core.exceptions import ImproperlyConfigured -from django_auth_ldap.backend import LDAPBackend -from django_cas_ng.backends import CASBackend -from josepy.jws import JWS -from mozilla_django_oidc.auth import OIDCAuthenticationBackend -from shibboleth.backends import ShibbolethRemoteUserBackend - - -class CustomShibbolethRemoteUserBackend(ShibbolethRemoteUserBackend): - def configure_user(self, user): - generate_api_key(user) - return user - - -class CustomCASBackend(CASBackend): - def configure_user(self, user): - generate_api_key(user) - # If CAS_AUTOCONFIGURE_EMAIL and CAS_EMAIL_DOMAIN settings are - # configured, add an email address for this user, using rule - # username@domain. - if settings.CAS_AUTOCONFIGURE_EMAIL and settings.CAS_EMAIL_DOMAIN: - user.email = f"{user.username}@{settings.CAS_EMAIL_DOMAIN}" - user.save() - return user - - -class CustomLDAPBackend(LDAPBackend): - """Append a usernamed suffix to LDAP users, if configured""" - - def ldap_to_django_username(self, username): - return username.rstrip(settings.AUTH_LDAP_USERNAME_SUFFIX) - - def django_to_ldap_username(self, username): - return username + settings.AUTH_LDAP_USERNAME_SUFFIX - - -class CustomOIDCBackend(OIDCAuthenticationBackend): - """ - Provide OpenID Connect authentication - """ - - def get_settings(self, attr, *args): - if attr in [ - "OIDC_RP_CLIENT_ID", - "OIDC_RP_CLIENT_SECRET", - "OIDC_OP_AUTHORIZATION_ENDPOINT", - "OIDC_OP_TOKEN_ENDPOINT", - "OIDC_OP_USER_ENDPOINT", - "OIDC_OP_JWKS_ENDPOINT", - "OIDC_OP_LOGOUT_ENDPOINT", - ]: - # Retrieve the request object stored in the instance. - request = getattr(self, "request", None) - - if request: - provider_name = request.session.get("providername") - - if provider_name and provider_name in settings.OIDC_PROVIDERS: - provider_settings = settings.OIDC_PROVIDERS.get(provider_name, {}) - value = provider_settings.get(attr) - - if value is None: - raise ImproperlyConfigured( - f"Setting {attr} for provider {provider_name} not found" - ) - return value - - # If request is None or provider_name session var is not set or attr is - # not in the list, call the superclass's get_settings method. - return OIDCAuthenticationBackend.get_settings(attr, *args) - - def authenticate(self, request, **kwargs): - self.request = request - self.OIDC_RP_CLIENT_ID = self.get_settings("OIDC_RP_CLIENT_ID") - self.OIDC_RP_CLIENT_SECRET = self.get_settings("OIDC_RP_CLIENT_SECRET") - self.OIDC_OP_TOKEN_ENDPOINT = self.get_settings("OIDC_OP_TOKEN_ENDPOINT") - self.OIDC_OP_USER_ENDPOINT = self.get_settings("OIDC_OP_USER_ENDPOINT") - self.OIDC_OP_JWKS_ENDPOINT = self.get_settings("OIDC_OP_JWKS_ENDPOINT") - - return super().authenticate(request, **kwargs) - - def get_userinfo(self, access_token, id_token, verified_id): - """ - Extract user details from JSON web tokens - These map to fields on the user field. - """ - - def decode_token(token): - sig = JWS.from_compact(token.encode("utf-8")) - payload = sig.payload.decode("utf-8") - return json.loads(payload) - - access_info = decode_token(access_token) - id_info = decode_token(id_token) - - info = {} - - for oidc_attr, user_attr in settings.OIDC_ACCESS_ATTRIBUTE_MAP.items(): - if oidc_attr in access_info: - info.setdefault(user_attr, access_info[oidc_attr]) - - for oidc_attr, user_attr in settings.OIDC_ID_ATTRIBUTE_MAP.items(): - if oidc_attr in id_info: - info.setdefault(user_attr, id_info[oidc_attr]) - - return info - - def create_user(self, user_info): - user = super().create_user(user_info) - for attr, value in user_info.items(): - setattr(user, attr, value) - user.save() - generate_api_key(user) - return user diff --git a/src/dashboard/src/fpr/south_migrations/0001_initial.py b/src/dashboard/src/fpr/south_migrations/0001_initial.py deleted file mode 100644 index b816629256..0000000000 --- a/src/dashboard/src/fpr/south_migrations/0001_initial.py +++ /dev/null @@ -1,799 +0,0 @@ -from typing import Any -from typing import Dict - -from south.db import db -from south.v2 import SchemaMigration - - -class Migration(SchemaMigration): - def forwards(self, orm): - # Adding model 'Agent' - db.create_table( - "Agent", - ( - ( - "uuid", - self.gf("django.db.models.fields.CharField")( - max_length=36, primary_key=True, db_column="uuid" - ), - ), - ( - "agentIdentifierType", - self.gf("django.db.models.fields.CharField")(max_length=100), - ), - ( - "agentIdentifierValue", - self.gf("django.db.models.fields.CharField")(max_length=100), - ), - ( - "agentName", - self.gf("django.db.models.fields.CharField")(max_length=100), - ), - ( - "agentType", - self.gf("django.db.models.fields.CharField")(max_length=100), - ), - ( - "clientIP", - self.gf("django.db.models.fields.CharField")(max_length=100), - ), - ), - ) - db.send_create_signal("fpr", ["Agent"]) - - # Adding model 'CommandType' - db.create_table( - "CommandType", - ( - ( - "uuid", - self.gf("django.db.models.fields.CharField")( - max_length=36, primary_key=True, db_column="pk" - ), - ), - ( - "replaces", - self.gf("django.db.models.fields.CharField")( - max_length=50, null=True, db_column="replaces" - ), - ), - ( - "type", - self.gf("django.db.models.fields.TextField")(db_column="type"), - ), - ( - "lastmodified", - self.gf("django.db.models.fields.DateTimeField")( - db_column="lastModified" - ), - ), - ( - "enabled", - self.gf("django.db.models.fields.IntegerField")( - default=1, null=True, db_column="enabled" - ), - ), - ), - ) - db.send_create_signal("fpr", ["CommandType"]) - - # Adding model 'Command' - db.create_table( - "Command", - ( - ( - "uuid", - self.gf("django.db.models.fields.CharField")( - max_length=36, primary_key=True, db_column="pk" - ), - ), - ( - "commandUsage", - self.gf("django.db.models.fields.CharField")(max_length=15), - ), - ( - "commandType", - self.gf("django.db.models.fields.CharField")(max_length=36), - ), - ( - "verificationCommand", - self.gf("django.db.models.fields.CharField")( - max_length=36, null=True - ), - ), - ( - "eventDetailCommand", - self.gf("django.db.models.fields.CharField")( - max_length=36, null=True - ), - ), - ( - "supportedBy", - self.gf("django.db.models.fields.CharField")( - max_length=36, null=True, db_column="supportedBy" - ), - ), - ( - "command", - self.gf("django.db.models.fields.TextField")(db_column="command"), - ), - ( - "outputLocation", - self.gf("django.db.models.fields.TextField")( - null=True, db_column="outputLocation" - ), - ), - ( - "description", - self.gf("django.db.models.fields.TextField")( - db_column="description" - ), - ), - ( - "outputFileFormat", - self.gf("django.db.models.fields.TextField")( - null=True, db_column="outputFileFormat" - ), - ), - ( - "replaces", - self.gf("django.db.models.fields.CharField")( - max_length=36, null=True, db_column="replaces" - ), - ), - ( - "lastmodified", - self.gf("django.db.models.fields.DateTimeField")( - null=True, db_column="lastModified" - ), - ), - ( - "enabled", - self.gf("django.db.models.fields.IntegerField")( - default=1, null=True, db_column="enabled" - ), - ), - ), - ) - db.send_create_signal("fpr", ["Command"]) - - # Adding model 'CommandsSupportedBy' - db.create_table( - "CommandsSupportedBy", - ( - ( - "uuid", - self.gf("django.db.models.fields.CharField")( - max_length=36, primary_key=True, db_column="pk" - ), - ), - ( - "description", - self.gf("django.db.models.fields.TextField")( - null=True, db_column="description" - ), - ), - ( - "replaces", - self.gf("django.db.models.fields.CharField")( - max_length=36, null=True, db_column="replaces" - ), - ), - ( - "lastmodified", - self.gf("django.db.models.fields.DateTimeField")( - db_column="lastModified" - ), - ), - ( - "enabled", - self.gf("django.db.models.fields.IntegerField")( - default=1, null=True, db_column="enabled" - ), - ), - ), - ) - db.send_create_signal("fpr", ["CommandsSupportedBy"]) - - # Adding model 'FileIDType' - db.create_table( - "FileIDType", - ( - ( - "uuid", - self.gf("django.db.models.fields.CharField")( - max_length=36, primary_key=True, db_column="pk" - ), - ), - ( - "description", - self.gf("django.db.models.fields.TextField")( - null=True, db_column="description" - ), - ), - ( - "replaces", - self.gf("django.db.models.fields.CharField")( - max_length=50, null=True, db_column="replaces" - ), - ), - ( - "lastmodified", - self.gf("django.db.models.fields.DateTimeField")( - db_column="lastModified" - ), - ), - ( - "enabled", - self.gf("django.db.models.fields.IntegerField")( - default=1, null=True, db_column="enabled" - ), - ), - ), - ) - db.send_create_signal("fpr", ["FileIDType"]) - - # Adding model 'FileID' - db.create_table( - "FileID", - ( - ( - "uuid", - self.gf("django.db.models.fields.CharField")( - max_length=36, primary_key=True, db_column="pk" - ), - ), - ( - "description", - self.gf("django.db.models.fields.TextField")( - db_column="description" - ), - ), - ( - "validpreservationformat", - self.gf("django.db.models.fields.IntegerField")( - default=0, null=True, db_column="validPreservationFormat" - ), - ), - ( - "validaccessformat", - self.gf("django.db.models.fields.IntegerField")( - default=0, null=True, db_column="validAccessFormat" - ), - ), - ( - "fileidtype", - self.gf("django.db.models.fields.CharField")( - max_length=36, null=True, db_column="fileidtype_id" - ), - ), - ( - "replaces", - self.gf("django.db.models.fields.CharField")( - max_length=36, null=True, db_column="replaces" - ), - ), - ( - "lastmodified", - self.gf("django.db.models.fields.DateTimeField")( - db_column="lastModified" - ), - ), - ( - "enabled", - self.gf("django.db.models.fields.IntegerField")( - default=1, null=True, db_column="enabled" - ), - ), - ), - ) - db.send_create_signal("fpr", ["FileID"]) - - # Adding model 'CommandClassification' - db.create_table( - "CommandClassification", - ( - ( - "uuid", - self.gf("django.db.models.fields.CharField")( - max_length=36, primary_key=True, db_column="pk" - ), - ), - ( - "classification", - self.gf("django.db.models.fields.TextField")( - null=True, db_column="classification" - ), - ), - ( - "replaces", - self.gf("django.db.models.fields.CharField")( - max_length=50, null=True, db_column="replaces" - ), - ), - ( - "lastmodified", - self.gf("django.db.models.fields.DateTimeField")( - db_column="lastModified" - ), - ), - ( - "enabled", - self.gf("django.db.models.fields.IntegerField")( - default=1, null=True, db_column="enabled" - ), - ), - ), - ) - db.send_create_signal("fpr", ["CommandClassification"]) - - # Adding model 'CommandRelationship' - db.create_table( - "CommandRelationship", - ( - ( - "uuid", - self.gf("django.db.models.fields.CharField")( - max_length=36, primary_key=True, db_column="pk" - ), - ), - ( - "commandClassification", - self.gf("django.db.models.fields.CharField")(max_length=36), - ), - ( - "command", - self.gf("django.db.models.fields.CharField")( - max_length=36, null=True - ), - ), - ( - "fileID", - self.gf("django.db.models.fields.CharField")( - max_length=36, null=True - ), - ), - ( - "replaces", - self.gf("django.db.models.fields.CharField")( - max_length=36, null=True - ), - ), - ( - "lastmodified", - self.gf("django.db.models.fields.DateTimeField")( - db_column="lastModified" - ), - ), - ( - "enabled", - self.gf("django.db.models.fields.IntegerField")( - default=1, null=True, db_column="enabled" - ), - ), - ), - ) - db.send_create_signal("fpr", ["CommandRelationship"]) - - # Adding model 'FileIDsBySingleID' - db.create_table( - "FileIDsBySingleID", - ( - ( - "uuid", - self.gf("django.db.models.fields.CharField")( - max_length=36, primary_key=True, db_column="pk" - ), - ), - ( - "fileID", - self.gf("django.db.models.fields.CharField")( - max_length=36, null=True - ), - ), - ("id", self.gf("django.db.models.fields.TextField")(db_column="id")), - ( - "tool", - self.gf("django.db.models.fields.TextField")(db_column="tool"), - ), - ( - "toolVersion", - self.gf("django.db.models.fields.TextField")( - null=True, db_column="toolVersion" - ), - ), - ( - "replaces", - self.gf("django.db.models.fields.CharField")( - max_length=50, null=True, db_column="replaces" - ), - ), - ( - "lastmodified", - self.gf("django.db.models.fields.DateTimeField")( - db_column="lastModified" - ), - ), - ( - "enabled", - self.gf("django.db.models.fields.IntegerField")( - default=1, null=True, db_column="enabled" - ), - ), - ), - ) - db.send_create_signal("fpr", ["FileIDsBySingleID"]) - - def backwards(self, orm): - # Deleting model 'Agent' - db.delete_table("Agent") - - # Deleting model 'CommandType' - db.delete_table("CommandType") - - # Deleting model 'Command' - db.delete_table("Command") - - # Deleting model 'CommandsSupportedBy' - db.delete_table("CommandsSupportedBy") - - # Deleting model 'FileIDType' - db.delete_table("FileIDType") - - # Deleting model 'FileID' - db.delete_table("FileID") - - # Deleting model 'CommandClassification' - db.delete_table("CommandClassification") - - # Deleting model 'CommandRelationship' - db.delete_table("CommandRelationship") - - # Deleting model 'FileIDsBySingleID' - db.delete_table("FileIDsBySingleID") - - models: Dict[str, Any] = { - "fpr.agent": { - "Meta": {"object_name": "Agent", "db_table": "u'Agent'"}, - "agentIdentifierType": ( - "django.db.models.fields.CharField", - [], - {"max_length": "100"}, - ), - "agentIdentifierValue": ( - "django.db.models.fields.CharField", - [], - {"max_length": "100"}, - ), - "agentName": ( - "django.db.models.fields.CharField", - [], - {"max_length": "100"}, - ), - "agentType": ( - "django.db.models.fields.CharField", - [], - {"max_length": "100"}, - ), - "clientIP": ( - "django.db.models.fields.CharField", - [], - {"max_length": "100"}, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "primary_key": "True", "db_column": "'uuid'"}, - ), - }, - "fpr.command": { - "Meta": {"object_name": "Command", "db_table": "u'Command'"}, - "command": ( - "django.db.models.fields.TextField", - [], - {"db_column": "'command'"}, - ), - "commandType": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36"}, - ), - "commandUsage": ( - "django.db.models.fields.CharField", - [], - {"max_length": "15"}, - ), - "description": ( - "django.db.models.fields.TextField", - [], - {"db_column": "'description'"}, - ), - "enabled": ( - "django.db.models.fields.IntegerField", - [], - {"default": "1", "null": "True", "db_column": "'enabled'"}, - ), - "eventDetailCommand": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True"}, - ), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"null": "True", "db_column": "'lastModified'"}, - ), - "outputFileFormat": ( - "django.db.models.fields.TextField", - [], - {"null": "True", "db_column": "'outputFileFormat'"}, - ), - "outputLocation": ( - "django.db.models.fields.TextField", - [], - {"null": "True", "db_column": "'outputLocation'"}, - ), - "replaces": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True", "db_column": "'replaces'"}, - ), - "supportedBy": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True", "db_column": "'supportedBy'"}, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "primary_key": "True", "db_column": "'pk'"}, - ), - "verificationCommand": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True"}, - ), - }, - "fpr.commandclassification": { - "Meta": { - "object_name": "CommandClassification", - "db_table": "u'CommandClassification'", - }, - "classification": ( - "django.db.models.fields.TextField", - [], - {"null": "True", "db_column": "'classification'"}, - ), - "enabled": ( - "django.db.models.fields.IntegerField", - [], - {"default": "1", "null": "True", "db_column": "'enabled'"}, - ), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"db_column": "'lastModified'"}, - ), - "replaces": ( - "django.db.models.fields.CharField", - [], - {"max_length": "50", "null": "True", "db_column": "'replaces'"}, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "primary_key": "True", "db_column": "'pk'"}, - ), - }, - "fpr.commandrelationship": { - "Meta": { - "object_name": "CommandRelationship", - "db_table": "u'CommandRelationship'", - }, - "command": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True"}, - ), - "commandClassification": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36"}, - ), - "enabled": ( - "django.db.models.fields.IntegerField", - [], - {"default": "1", "null": "True", "db_column": "'enabled'"}, - ), - "fileID": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True"}, - ), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"db_column": "'lastModified'"}, - ), - "replaces": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True"}, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "primary_key": "True", "db_column": "'pk'"}, - ), - }, - "fpr.commandssupportedby": { - "Meta": { - "object_name": "CommandsSupportedBy", - "db_table": "u'CommandsSupportedBy'", - }, - "description": ( - "django.db.models.fields.TextField", - [], - {"null": "True", "db_column": "'description'"}, - ), - "enabled": ( - "django.db.models.fields.IntegerField", - [], - {"default": "1", "null": "True", "db_column": "'enabled'"}, - ), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"db_column": "'lastModified'"}, - ), - "replaces": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True", "db_column": "'replaces'"}, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "primary_key": "True", "db_column": "'pk'"}, - ), - }, - "fpr.commandtype": { - "Meta": {"object_name": "CommandType", "db_table": "u'CommandType'"}, - "enabled": ( - "django.db.models.fields.IntegerField", - [], - {"default": "1", "null": "True", "db_column": "'enabled'"}, - ), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"db_column": "'lastModified'"}, - ), - "replaces": ( - "django.db.models.fields.CharField", - [], - {"max_length": "50", "null": "True", "db_column": "'replaces'"}, - ), - "type": ("django.db.models.fields.TextField", [], {"db_column": "'type'"}), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "primary_key": "True", "db_column": "'pk'"}, - ), - }, - "fpr.fileid": { - "Meta": {"object_name": "FileID", "db_table": "u'FileID'"}, - "description": ( - "django.db.models.fields.TextField", - [], - {"db_column": "'description'"}, - ), - "enabled": ( - "django.db.models.fields.IntegerField", - [], - {"default": "1", "null": "True", "db_column": "'enabled'"}, - ), - "fileidtype": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True", "db_column": "'fileidtype_id'"}, - ), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"db_column": "'lastModified'"}, - ), - "replaces": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True", "db_column": "'replaces'"}, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "primary_key": "True", "db_column": "'pk'"}, - ), - "validaccessformat": ( - "django.db.models.fields.IntegerField", - [], - {"default": "0", "null": "True", "db_column": "'validAccessFormat'"}, - ), - "validpreservationformat": ( - "django.db.models.fields.IntegerField", - [], - { - "default": "0", - "null": "True", - "db_column": "'validPreservationFormat'", - }, - ), - }, - "fpr.fileidsbysingleid": { - "Meta": { - "object_name": "FileIDsBySingleID", - "db_table": "u'FileIDsBySingleID'", - }, - "enabled": ( - "django.db.models.fields.IntegerField", - [], - {"default": "1", "null": "True", "db_column": "'enabled'"}, - ), - "fileID": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True"}, - ), - "id": ("django.db.models.fields.TextField", [], {"db_column": "'id'"}), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"db_column": "'lastModified'"}, - ), - "replaces": ( - "django.db.models.fields.CharField", - [], - {"max_length": "50", "null": "True", "db_column": "'replaces'"}, - ), - "tool": ("django.db.models.fields.TextField", [], {"db_column": "'tool'"}), - "toolVersion": ( - "django.db.models.fields.TextField", - [], - {"null": "True", "db_column": "'toolVersion'"}, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "primary_key": "True", "db_column": "'pk'"}, - ), - }, - "fpr.fileidtype": { - "Meta": {"object_name": "FileIDType", "db_table": "u'FileIDType'"}, - "description": ( - "django.db.models.fields.TextField", - [], - {"null": "True", "db_column": "'description'"}, - ), - "enabled": ( - "django.db.models.fields.IntegerField", - [], - {"default": "1", "null": "True", "db_column": "'enabled'"}, - ), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"db_column": "'lastModified'"}, - ), - "replaces": ( - "django.db.models.fields.CharField", - [], - {"max_length": "50", "null": "True", "db_column": "'replaces'"}, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "primary_key": "True", "db_column": "'pk'"}, - ), - }, - } - - complete_apps = ["fpr"] diff --git a/src/dashboard/src/fpr/south_migrations/0002_api_v2.py b/src/dashboard/src/fpr/south_migrations/0002_api_v2.py deleted file mode 100644 index ebb878a3ed..0000000000 --- a/src/dashboard/src/fpr/south_migrations/0002_api_v2.py +++ /dev/null @@ -1,1318 +0,0 @@ -from typing import Any -from typing import Dict - -from south.db import db -from south.v2 import SchemaMigration - - -class Migration(SchemaMigration): - def forwards(self, orm): - # Adding model 'FPRule' - db.create_table( - "fpr_fprule", - ( - ("id", self.gf("django.db.models.fields.AutoField")(primary_key=True)), - ( - "replaces", - self.gf("django.db.models.fields.related.ForeignKey")( - to=orm["fpr.FPRule"], to_field="uuid", null=True, blank=True - ), - ), - ( - "enabled", - self.gf("django.db.models.fields.BooleanField")(default=True), - ), - ( - "lastmodified", - self.gf("django.db.models.fields.DateTimeField")( - auto_now_add=True, blank=True - ), - ), - ( - "uuid", - self.gf("django.db.models.fields.CharField")( - unique=True, max_length=36, blank=True - ), - ), - ( - "purpose", - self.gf("django.db.models.fields.CharField")(max_length=32), - ), - ( - "command", - self.gf("django.db.models.fields.related.ForeignKey")( - to=orm["fpr.FPCommand"], to_field="uuid" - ), - ), - ( - "format", - self.gf("django.db.models.fields.related.ForeignKey")( - to=orm["fpr.FormatVersion"], to_field="uuid" - ), - ), - ( - "count_attempts", - self.gf("django.db.models.fields.IntegerField")(default=0), - ), - ( - "count_okay", - self.gf("django.db.models.fields.IntegerField")(default=0), - ), - ( - "count_not_okay", - self.gf("django.db.models.fields.IntegerField")(default=0), - ), - ), - ) - db.send_create_signal("fpr", ["FPRule"]) - - # Adding model 'FPTool' - db.create_table( - "fpr_fptool", - ( - ("id", self.gf("django.db.models.fields.AutoField")(primary_key=True)), - ( - "uuid", - self.gf("django.db.models.fields.CharField")( - unique=True, max_length=36, blank=True - ), - ), - ( - "description", - self.gf("django.db.models.fields.CharField")(max_length=256), - ), - ( - "version", - self.gf("django.db.models.fields.CharField")(max_length=64), - ), - ( - "enabled", - self.gf("django.db.models.fields.BooleanField")(default=True), - ), - ( - "slug", - self.gf("autoslug.fields.AutoSlugField")( - unique_with=(), max_length=50, populate_from="_slug" - ), - ), - ), - ) - db.send_create_signal("fpr", ["FPTool"]) - - # Adding model 'FormatVersion' - db.create_table( - "fpr_formatversion", - ( - ("id", self.gf("django.db.models.fields.AutoField")(primary_key=True)), - ( - "replaces", - self.gf("django.db.models.fields.related.ForeignKey")( - to=orm["fpr.FormatVersion"], - to_field="uuid", - null=True, - blank=True, - ), - ), - ( - "enabled", - self.gf("django.db.models.fields.BooleanField")(default=True), - ), - ( - "lastmodified", - self.gf("django.db.models.fields.DateTimeField")( - auto_now_add=True, blank=True - ), - ), - ( - "uuid", - self.gf("django.db.models.fields.CharField")( - unique=True, max_length=36, blank=True - ), - ), - ( - "format", - self.gf("django.db.models.fields.related.ForeignKey")( - related_name="version_set", - to_field="uuid", - null=True, - to=orm["fpr.Format"], - ), - ), - ( - "version", - self.gf("django.db.models.fields.CharField")( - max_length=10, null=True, blank=True - ), - ), - ( - "pronom_id", - self.gf("django.db.models.fields.CharField")( - max_length=32, null=True, blank=True - ), - ), - ( - "description", - self.gf("django.db.models.fields.CharField")( - max_length=128, null=True, blank=True - ), - ), - ( - "access_format", - self.gf("django.db.models.fields.BooleanField")(default=False), - ), - ( - "preservation_format", - self.gf("django.db.models.fields.BooleanField")(default=False), - ), - ( - "slug", - self.gf("autoslug.fields.AutoSlugField")( - unique_with=("format",), - max_length=50, - populate_from="description", - ), - ), - ), - ) - db.send_create_signal("fpr", ["FormatVersion"]) - - # Adding model 'IDRule' - db.create_table( - "fpr_idrule", - ( - ("id", self.gf("django.db.models.fields.AutoField")(primary_key=True)), - ( - "replaces", - self.gf("django.db.models.fields.related.ForeignKey")( - to=orm["fpr.IDRule"], to_field="uuid", null=True, blank=True - ), - ), - ( - "enabled", - self.gf("django.db.models.fields.BooleanField")(default=True), - ), - ( - "lastmodified", - self.gf("django.db.models.fields.DateTimeField")( - auto_now_add=True, blank=True - ), - ), - ( - "uuid", - self.gf("django.db.models.fields.CharField")( - unique=True, max_length=36, blank=True - ), - ), - ( - "command", - self.gf("django.db.models.fields.related.ForeignKey")( - to=orm["fpr.IDCommand"], to_field="uuid" - ), - ), - ( - "format", - self.gf("django.db.models.fields.related.ForeignKey")( - to=orm["fpr.FormatVersion"], to_field="uuid" - ), - ), - ("command_output", self.gf("django.db.models.fields.TextField")()), - ), - ) - db.send_create_signal("fpr", ["IDRule"]) - - # Adding model 'IDTool' - db.create_table( - "fpr_idtool", - ( - ("id", self.gf("django.db.models.fields.AutoField")(primary_key=True)), - ( - "uuid", - self.gf("django.db.models.fields.CharField")( - unique=True, max_length=36, blank=True - ), - ), - ( - "description", - self.gf("django.db.models.fields.CharField")(max_length=256), - ), - ( - "version", - self.gf("django.db.models.fields.CharField")(max_length=64), - ), - ( - "enabled", - self.gf("django.db.models.fields.BooleanField")(default=True), - ), - ( - "slug", - self.gf("autoslug.fields.AutoSlugField")( - unique_with=(), max_length=50, populate_from="_slug" - ), - ), - ), - ) - db.send_create_signal("fpr", ["IDTool"]) - - # Adding model 'IDCommand' - db.create_table( - "fpr_idcommand", - ( - ("id", self.gf("django.db.models.fields.AutoField")(primary_key=True)), - ( - "replaces", - self.gf("django.db.models.fields.related.ForeignKey")( - to=orm["fpr.IDCommand"], to_field="uuid", null=True, blank=True - ), - ), - ( - "enabled", - self.gf("django.db.models.fields.BooleanField")(default=True), - ), - ( - "lastmodified", - self.gf("django.db.models.fields.DateTimeField")( - auto_now_add=True, blank=True - ), - ), - ( - "uuid", - self.gf("django.db.models.fields.CharField")( - unique=True, max_length=36, blank=True - ), - ), - ( - "description", - self.gf("django.db.models.fields.CharField")(max_length=256), - ), - ("config", self.gf("django.db.models.fields.CharField")(max_length=4)), - ("script", self.gf("django.db.models.fields.TextField")()), - ( - "script_type", - self.gf("django.db.models.fields.CharField")(max_length=16), - ), - ( - "tool", - self.gf("django.db.models.fields.related.ForeignKey")( - to=orm["fpr.IDTool"], to_field="uuid", null=True, blank=True - ), - ), - ), - ) - db.send_create_signal("fpr", ["IDCommand"]) - - # Adding model 'FormatGroup' - db.create_table( - "fpr_formatgroup", - ( - ("id", self.gf("django.db.models.fields.AutoField")(primary_key=True)), - ( - "uuid", - self.gf("django.db.models.fields.CharField")( - unique=True, max_length=36, blank=True - ), - ), - ( - "description", - self.gf("django.db.models.fields.CharField")(max_length=128), - ), - ( - "slug", - self.gf("autoslug.fields.AutoSlugField")( - unique_with=(), max_length=50, populate_from="description" - ), - ), - ), - ) - db.send_create_signal("fpr", ["FormatGroup"]) - - # Adding model 'Format' - db.create_table( - "fpr_format", - ( - ("id", self.gf("django.db.models.fields.AutoField")(primary_key=True)), - ( - "uuid", - self.gf("django.db.models.fields.CharField")( - unique=True, max_length=36, blank=True - ), - ), - ( - "description", - self.gf("django.db.models.fields.CharField")(max_length=128), - ), - ( - "group", - self.gf("django.db.models.fields.related.ForeignKey")( - to=orm["fpr.FormatGroup"], to_field="uuid", null=True - ), - ), - ( - "slug", - self.gf("autoslug.fields.AutoSlugField")( - unique_with=(), max_length=50, populate_from="description" - ), - ), - ), - ) - db.send_create_signal("fpr", ["Format"]) - - # Adding model 'FPCommand' - db.create_table( - "fpr_fpcommand", - ( - ("id", self.gf("django.db.models.fields.AutoField")(primary_key=True)), - ( - "replaces", - self.gf("django.db.models.fields.related.ForeignKey")( - to=orm["fpr.FPCommand"], to_field="uuid", null=True, blank=True - ), - ), - ( - "enabled", - self.gf("django.db.models.fields.BooleanField")(default=True), - ), - ( - "lastmodified", - self.gf("django.db.models.fields.DateTimeField")( - auto_now_add=True, blank=True - ), - ), - ( - "uuid", - self.gf("django.db.models.fields.CharField")( - unique=True, max_length=36, blank=True - ), - ), - ( - "tool", - self.gf("django.db.models.fields.related.ForeignKey")( - to=orm["fpr.FPTool"], to_field="uuid", null=True - ), - ), - ( - "description", - self.gf("django.db.models.fields.CharField")(max_length=256), - ), - ("command", self.gf("django.db.models.fields.TextField")()), - ( - "script_type", - self.gf("django.db.models.fields.CharField")(max_length=16), - ), - ( - "output_location", - self.gf("django.db.models.fields.TextField")(null=True, blank=True), - ), - ( - "output_format", - self.gf("django.db.models.fields.related.ForeignKey")( - to=orm["fpr.FormatVersion"], - to_field="uuid", - null=True, - blank=True, - ), - ), - ( - "command_usage", - self.gf("django.db.models.fields.CharField")(max_length=16), - ), - ( - "verification_command", - self.gf("django.db.models.fields.related.ForeignKey")( - blank=True, - related_name="+", - to_field="uuid", - null=True, - to=orm["fpr.FPCommand"], - ), - ), - ( - "event_detail_command", - self.gf("django.db.models.fields.related.ForeignKey")( - blank=True, - related_name="+", - to_field="uuid", - null=True, - to=orm["fpr.FPCommand"], - ), - ), - ), - ) - db.send_create_signal("fpr", ["FPCommand"]) - - # Adding field 'FileID.format' - db.add_column( - "FileID", - "format", - self.gf("django.db.models.fields.related.ForeignKey")( - to=orm["fpr.FormatVersion"], to_field="uuid", null=True - ), - keep_default=False, - ) - - # Changing field 'FileIDsBySingleID.replaces' - db.alter_column( - "FileIDsBySingleID", - "replaces", - self.gf("django.db.models.fields.CharField")( - max_length=36, null=True, db_column="replaces" - ), - ) - - # Changing field 'CommandClassification.replaces' - db.alter_column( - "CommandClassification", - "replaces", - self.gf("django.db.models.fields.CharField")( - max_length=36, null=True, db_column="replaces" - ), - ) - - # Changing field 'FileIDType.replaces' - db.alter_column( - "FileIDType", - "replaces", - self.gf("django.db.models.fields.CharField")( - max_length=36, null=True, db_column="replaces" - ), - ) - - # Changing field 'CommandType.replaces' - db.alter_column( - "CommandType", - "replaces", - self.gf("django.db.models.fields.CharField")( - max_length=36, null=True, db_column="replaces" - ), - ) - - def backwards(self, orm): - # Deleting model 'FPRule' - db.delete_table("fpr_fprule") - - # Deleting model 'FPTool' - db.delete_table("fpr_fptool") - - # Deleting model 'FormatVersion' - db.delete_table("fpr_formatversion") - - # Deleting model 'IDRule' - db.delete_table("fpr_idrule") - - # Deleting model 'IDTool' - db.delete_table("fpr_idtool") - - # Deleting model 'IDCommand' - db.delete_table("fpr_idcommand") - - # Deleting model 'FormatGroup' - db.delete_table("fpr_formatgroup") - - # Deleting model 'Format' - db.delete_table("fpr_format") - - # Deleting model 'FPCommand' - db.delete_table("fpr_fpcommand") - - # Deleting field 'FileID.format' - db.delete_column("FileID", "format_id") - - # Changing field 'FileIDsBySingleID.replaces' - db.alter_column( - "FileIDsBySingleID", - "replaces", - self.gf("django.db.models.fields.CharField")( - max_length=50, null=True, db_column="replaces" - ), - ) - - # Changing field 'CommandClassification.replaces' - db.alter_column( - "CommandClassification", - "replaces", - self.gf("django.db.models.fields.CharField")( - max_length=50, null=True, db_column="replaces" - ), - ) - - # Changing field 'FileIDType.replaces' - db.alter_column( - "FileIDType", - "replaces", - self.gf("django.db.models.fields.CharField")( - max_length=50, null=True, db_column="replaces" - ), - ) - - # Changing field 'CommandType.replaces' - db.alter_column( - "CommandType", - "replaces", - self.gf("django.db.models.fields.CharField")( - max_length=50, null=True, db_column="replaces" - ), - ) - - models: Dict[str, Any] = { - "fpr.agent": { - "Meta": {"object_name": "Agent", "db_table": "u'Agent'"}, - "agentIdentifierType": ( - "django.db.models.fields.CharField", - [], - {"max_length": "100"}, - ), - "agentIdentifierValue": ( - "django.db.models.fields.CharField", - [], - {"max_length": "100"}, - ), - "agentName": ( - "django.db.models.fields.CharField", - [], - {"max_length": "100"}, - ), - "agentType": ( - "django.db.models.fields.CharField", - [], - {"max_length": "100"}, - ), - "clientIP": ( - "django.db.models.fields.CharField", - [], - {"max_length": "100"}, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "primary_key": "True", "db_column": "'uuid'"}, - ), - }, - "fpr.command": { - "Meta": {"object_name": "Command", "db_table": "u'Command'"}, - "command": ( - "django.db.models.fields.TextField", - [], - {"db_column": "'command'"}, - ), - "commandType": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36"}, - ), - "commandUsage": ( - "django.db.models.fields.CharField", - [], - {"max_length": "15"}, - ), - "description": ( - "django.db.models.fields.TextField", - [], - {"db_column": "'description'"}, - ), - "enabled": ( - "django.db.models.fields.IntegerField", - [], - {"default": "1", "null": "True", "db_column": "'enabled'"}, - ), - "eventDetailCommand": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True"}, - ), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"null": "True", "db_column": "'lastModified'"}, - ), - "outputFileFormat": ( - "django.db.models.fields.TextField", - [], - {"null": "True", "db_column": "'outputFileFormat'"}, - ), - "outputLocation": ( - "django.db.models.fields.TextField", - [], - {"null": "True", "db_column": "'outputLocation'"}, - ), - "replaces": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True", "db_column": "'replaces'"}, - ), - "supportedBy": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True", "db_column": "'supportedBy'"}, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "primary_key": "True", "db_column": "'pk'"}, - ), - "verificationCommand": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True"}, - ), - }, - "fpr.commandclassification": { - "Meta": { - "object_name": "CommandClassification", - "db_table": "u'CommandClassification'", - }, - "classification": ( - "django.db.models.fields.TextField", - [], - {"null": "True", "db_column": "'classification'"}, - ), - "enabled": ( - "django.db.models.fields.IntegerField", - [], - {"default": "1", "null": "True", "db_column": "'enabled'"}, - ), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"db_column": "'lastModified'"}, - ), - "replaces": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True", "db_column": "'replaces'"}, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "primary_key": "True", "db_column": "'pk'"}, - ), - }, - "fpr.commandrelationship": { - "Meta": { - "object_name": "CommandRelationship", - "db_table": "u'CommandRelationship'", - }, - "command": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True"}, - ), - "commandClassification": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36"}, - ), - "enabled": ( - "django.db.models.fields.IntegerField", - [], - {"default": "1", "null": "True", "db_column": "'enabled'"}, - ), - "fileID": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True"}, - ), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"db_column": "'lastModified'"}, - ), - "replaces": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True"}, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "primary_key": "True", "db_column": "'pk'"}, - ), - }, - "fpr.commandssupportedby": { - "Meta": { - "object_name": "CommandsSupportedBy", - "db_table": "u'CommandsSupportedBy'", - }, - "description": ( - "django.db.models.fields.TextField", - [], - {"null": "True", "db_column": "'description'"}, - ), - "enabled": ( - "django.db.models.fields.IntegerField", - [], - {"default": "1", "null": "True", "db_column": "'enabled'"}, - ), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"db_column": "'lastModified'"}, - ), - "replaces": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True", "db_column": "'replaces'"}, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "primary_key": "True", "db_column": "'pk'"}, - ), - }, - "fpr.commandtype": { - "Meta": {"object_name": "CommandType", "db_table": "u'CommandType'"}, - "enabled": ( - "django.db.models.fields.IntegerField", - [], - {"default": "1", "null": "True", "db_column": "'enabled'"}, - ), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"db_column": "'lastModified'"}, - ), - "replaces": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True", "db_column": "'replaces'"}, - ), - "type": ("django.db.models.fields.TextField", [], {"db_column": "'type'"}), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "primary_key": "True", "db_column": "'pk'"}, - ), - }, - "fpr.fileid": { - "Meta": {"object_name": "FileID", "db_table": "u'FileID'"}, - "description": ( - "django.db.models.fields.TextField", - [], - {"db_column": "'description'"}, - ), - "enabled": ( - "django.db.models.fields.IntegerField", - [], - {"default": "1", "null": "True", "db_column": "'enabled'"}, - ), - "fileidtype": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True", "db_column": "'fileidtype_id'"}, - ), - "format": ( - "django.db.models.fields.related.ForeignKey", - [], - { - "to": "orm['fpr.FormatVersion']", - "to_field": "'uuid'", - "null": "True", - }, - ), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"db_column": "'lastModified'"}, - ), - "replaces": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True", "db_column": "'replaces'"}, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "primary_key": "True", "db_column": "'pk'"}, - ), - "validaccessformat": ( - "django.db.models.fields.IntegerField", - [], - {"default": "0", "null": "True", "db_column": "'validAccessFormat'"}, - ), - "validpreservationformat": ( - "django.db.models.fields.IntegerField", - [], - { - "default": "0", - "null": "True", - "db_column": "'validPreservationFormat'", - }, - ), - }, - "fpr.fileidsbysingleid": { - "Meta": { - "object_name": "FileIDsBySingleID", - "db_table": "u'FileIDsBySingleID'", - }, - "enabled": ( - "django.db.models.fields.IntegerField", - [], - {"default": "1", "null": "True", "db_column": "'enabled'"}, - ), - "fileID": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True"}, - ), - "id": ("django.db.models.fields.TextField", [], {"db_column": "'id'"}), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"db_column": "'lastModified'"}, - ), - "replaces": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True", "db_column": "'replaces'"}, - ), - "tool": ("django.db.models.fields.TextField", [], {"db_column": "'tool'"}), - "toolVersion": ( - "django.db.models.fields.TextField", - [], - {"null": "True", "db_column": "'toolVersion'"}, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "primary_key": "True", "db_column": "'pk'"}, - ), - }, - "fpr.fileidtype": { - "Meta": {"object_name": "FileIDType", "db_table": "u'FileIDType'"}, - "description": ( - "django.db.models.fields.TextField", - [], - {"null": "True", "db_column": "'description'"}, - ), - "enabled": ( - "django.db.models.fields.IntegerField", - [], - {"default": "1", "null": "True", "db_column": "'enabled'"}, - ), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"db_column": "'lastModified'"}, - ), - "replaces": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "null": "True", "db_column": "'replaces'"}, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"max_length": "36", "primary_key": "True", "db_column": "'pk'"}, - ), - }, - "fpr.format": { - "Meta": {"ordering": "['group', 'description']", "object_name": "Format"}, - "description": ( - "django.db.models.fields.CharField", - [], - {"max_length": "128"}, - ), - "group": ( - "django.db.models.fields.related.ForeignKey", - [], - {"to": "orm['fpr.FormatGroup']", "to_field": "'uuid'", "null": "True"}, - ), - "id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}), - "slug": ( - "autoslug.fields.AutoSlugField", - [], - { - "unique_with": "()", - "max_length": "50", - "populate_from": "'description'", - }, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"unique": "True", "max_length": "36", "blank": "True"}, - ), - }, - "fpr.formatgroup": { - "Meta": {"ordering": "['description']", "object_name": "FormatGroup"}, - "description": ( - "django.db.models.fields.CharField", - [], - {"max_length": "128"}, - ), - "id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}), - "slug": ( - "autoslug.fields.AutoSlugField", - [], - { - "unique_with": "()", - "max_length": "50", - "populate_from": "'description'", - }, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"unique": "True", "max_length": "36", "blank": "True"}, - ), - }, - "fpr.formatversion": { - "Meta": { - "ordering": "['format', 'description']", - "object_name": "FormatVersion", - }, - "access_format": ( - "django.db.models.fields.BooleanField", - [], - {"default": "False"}, - ), - "description": ( - "django.db.models.fields.CharField", - [], - {"max_length": "128", "null": "True", "blank": "True"}, - ), - "enabled": ( - "django.db.models.fields.BooleanField", - [], - {"default": "True"}, - ), - "format": ( - "django.db.models.fields.related.ForeignKey", - [], - { - "related_name": "'version_set'", - "to_field": "'uuid'", - "null": "True", - "to": "orm['fpr.Format']", - }, - ), - "id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"auto_now_add": "True", "blank": "True"}, - ), - "preservation_format": ( - "django.db.models.fields.BooleanField", - [], - {"default": "False"}, - ), - "pronom_id": ( - "django.db.models.fields.CharField", - [], - {"max_length": "32", "null": "True", "blank": "True"}, - ), - "replaces": ( - "django.db.models.fields.related.ForeignKey", - [], - { - "to": "orm['fpr.FormatVersion']", - "to_field": "'uuid'", - "null": "True", - "blank": "True", - }, - ), - "slug": ( - "autoslug.fields.AutoSlugField", - [], - { - "unique_with": "('format',)", - "max_length": "50", - "populate_from": "'description'", - }, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"unique": "True", "max_length": "36", "blank": "True"}, - ), - "version": ( - "django.db.models.fields.CharField", - [], - {"max_length": "10", "null": "True", "blank": "True"}, - ), - }, - "fpr.fpcommand": { - "Meta": {"ordering": "['description']", "object_name": "FPCommand"}, - "command": ("django.db.models.fields.TextField", [], {}), - "command_usage": ( - "django.db.models.fields.CharField", - [], - {"max_length": "16"}, - ), - "description": ( - "django.db.models.fields.CharField", - [], - {"max_length": "256"}, - ), - "enabled": ( - "django.db.models.fields.BooleanField", - [], - {"default": "True"}, - ), - "event_detail_command": ( - "django.db.models.fields.related.ForeignKey", - [], - { - "blank": "True", - "related_name": "'+'", - "to_field": "'uuid'", - "null": "True", - "to": "orm['fpr.FPCommand']", - }, - ), - "id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"auto_now_add": "True", "blank": "True"}, - ), - "output_format": ( - "django.db.models.fields.related.ForeignKey", - [], - { - "to": "orm['fpr.FormatVersion']", - "to_field": "'uuid'", - "null": "True", - "blank": "True", - }, - ), - "output_location": ( - "django.db.models.fields.TextField", - [], - {"null": "True", "blank": "True"}, - ), - "replaces": ( - "django.db.models.fields.related.ForeignKey", - [], - { - "to": "orm['fpr.FPCommand']", - "to_field": "'uuid'", - "null": "True", - "blank": "True", - }, - ), - "script_type": ( - "django.db.models.fields.CharField", - [], - {"max_length": "16"}, - ), - "tool": ( - "django.db.models.fields.related.ForeignKey", - [], - {"to": "orm['fpr.FPTool']", "to_field": "'uuid'", "null": "True"}, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"unique": "True", "max_length": "36", "blank": "True"}, - ), - "verification_command": ( - "django.db.models.fields.related.ForeignKey", - [], - { - "blank": "True", - "related_name": "'+'", - "to_field": "'uuid'", - "null": "True", - "to": "orm['fpr.FPCommand']", - }, - ), - }, - "fpr.fprule": { - "Meta": {"object_name": "FPRule"}, - "command": ( - "django.db.models.fields.related.ForeignKey", - [], - {"to": "orm['fpr.FPCommand']", "to_field": "'uuid'"}, - ), - "count_attempts": ( - "django.db.models.fields.IntegerField", - [], - {"default": "0"}, - ), - "count_not_okay": ( - "django.db.models.fields.IntegerField", - [], - {"default": "0"}, - ), - "count_okay": ( - "django.db.models.fields.IntegerField", - [], - {"default": "0"}, - ), - "enabled": ( - "django.db.models.fields.BooleanField", - [], - {"default": "True"}, - ), - "format": ( - "django.db.models.fields.related.ForeignKey", - [], - {"to": "orm['fpr.FormatVersion']", "to_field": "'uuid'"}, - ), - "id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"auto_now_add": "True", "blank": "True"}, - ), - "purpose": ("django.db.models.fields.CharField", [], {"max_length": "32"}), - "replaces": ( - "django.db.models.fields.related.ForeignKey", - [], - { - "to": "orm['fpr.FPRule']", - "to_field": "'uuid'", - "null": "True", - "blank": "True", - }, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"unique": "True", "max_length": "36", "blank": "True"}, - ), - }, - "fpr.fptool": { - "Meta": {"object_name": "FPTool"}, - "description": ( - "django.db.models.fields.CharField", - [], - {"max_length": "256"}, - ), - "enabled": ( - "django.db.models.fields.BooleanField", - [], - {"default": "True"}, - ), - "id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}), - "slug": ( - "autoslug.fields.AutoSlugField", - [], - {"unique_with": "()", "max_length": "50", "populate_from": "'_slug'"}, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"unique": "True", "max_length": "36", "blank": "True"}, - ), - "version": ("django.db.models.fields.CharField", [], {"max_length": "64"}), - }, - "fpr.idcommand": { - "Meta": {"ordering": "['description']", "object_name": "IDCommand"}, - "config": ("django.db.models.fields.CharField", [], {"max_length": "4"}), - "description": ( - "django.db.models.fields.CharField", - [], - {"max_length": "256"}, - ), - "enabled": ( - "django.db.models.fields.BooleanField", - [], - {"default": "True"}, - ), - "id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"auto_now_add": "True", "blank": "True"}, - ), - "replaces": ( - "django.db.models.fields.related.ForeignKey", - [], - { - "to": "orm['fpr.IDCommand']", - "to_field": "'uuid'", - "null": "True", - "blank": "True", - }, - ), - "script": ("django.db.models.fields.TextField", [], {}), - "script_type": ( - "django.db.models.fields.CharField", - [], - {"max_length": "16"}, - ), - "tool": ( - "django.db.models.fields.related.ForeignKey", - [], - { - "to": "orm['fpr.IDTool']", - "to_field": "'uuid'", - "null": "True", - "blank": "True", - }, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"unique": "True", "max_length": "36", "blank": "True"}, - ), - }, - "fpr.idrule": { - "Meta": {"object_name": "IDRule"}, - "command": ( - "django.db.models.fields.related.ForeignKey", - [], - {"to": "orm['fpr.IDCommand']", "to_field": "'uuid'"}, - ), - "command_output": ("django.db.models.fields.TextField", [], {}), - "enabled": ( - "django.db.models.fields.BooleanField", - [], - {"default": "True"}, - ), - "format": ( - "django.db.models.fields.related.ForeignKey", - [], - {"to": "orm['fpr.FormatVersion']", "to_field": "'uuid'"}, - ), - "id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}), - "lastmodified": ( - "django.db.models.fields.DateTimeField", - [], - {"auto_now_add": "True", "blank": "True"}, - ), - "replaces": ( - "django.db.models.fields.related.ForeignKey", - [], - { - "to": "orm['fpr.IDRule']", - "to_field": "'uuid'", - "null": "True", - "blank": "True", - }, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"unique": "True", "max_length": "36", "blank": "True"}, - ), - }, - "fpr.idtool": { - "Meta": {"object_name": "IDTool"}, - "description": ( - "django.db.models.fields.CharField", - [], - {"max_length": "256"}, - ), - "enabled": ( - "django.db.models.fields.BooleanField", - [], - {"default": "True"}, - ), - "id": ("django.db.models.fields.AutoField", [], {"primary_key": "True"}), - "slug": ( - "autoslug.fields.AutoSlugField", - [], - {"unique_with": "()", "max_length": "50", "populate_from": "'_slug'"}, - ), - "uuid": ( - "django.db.models.fields.CharField", - [], - {"unique": "True", "max_length": "36", "blank": "True"}, - ), - "version": ("django.db.models.fields.CharField", [], {"max_length": "64"}), - }, - } - - complete_apps = ["fpr"] diff --git a/src/dashboard/src/fpr/south_migrations/rawdump.sql b/src/dashboard/src/fpr/south_migrations/rawdump.sql deleted file mode 100644 index a985ea3eb6..0000000000 --- a/src/dashboard/src/fpr/south_migrations/rawdump.sql +++ /dev/null @@ -1,970 +0,0 @@ -LOCK TABLES `CommandType` WRITE; -/*!40000 ALTER TABLE `CommandType` DISABLE KEYS */; -INSERT INTO `CommandType` (`pk`, `type`, `replaces`, `lastModified`) VALUES ('a5fa09c6-9a8d-40b9-aee2-2641160f969e','bashScript',NULL,'2012-10-02 00:24:58'); -INSERT INTO `CommandType` (`pk`, `type`, `replaces`, `lastModified`) VALUES ('ee6869f7-3ac0-44ed-a5ca-50f0f81cd7c1','pythonScript',NULL,'2012-10-02 00:24:58'); -INSERT INTO `CommandType` (`pk`, `type`, `replaces`, `lastModified`) VALUES ('f3f2dae8-54ed-40b4-8f20-cffb249262cf','command',NULL,'2012-10-02 00:24:58'); -/*!40000 ALTER TABLE `CommandType` ENABLE KEYS */; -UNLOCK TABLES; - -SET FOREIGN_KEY_CHECKS=0; -LOCK TABLES `Command` WRITE; -/*!40000 ALTER TABLE `Command` DISABLE KEYS */; -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('03f67b09-a8e7-44e2-b876-5786d7da09c7','eventDetail','a5fa09c6-9a8d-40b9-aee2-2641160f969e',NULL,'echo program=\\\"ffmpeg\\\"\\; version=\\\"`ffmpeg 2>&1 | grep --ignore-case \"FFmpeg version\"`\\\"','',NULL,'Get event detail text for ffmpeg extraction','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-12-19 01:54:26',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('0889b85d-e891-4d18-8e89-33b02783ba7a','eventDetail','a5fa09c6-9a8d-40b9-aee2-2641160f969e',NULL,'echo program=\\\"openOffice\\\"\\; version=\\\"\\\" ','',NULL,'openOffice event detail','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('0fd7935a-ed0d-4f67-aa25-1b44684f6aca','eventDetail','a5fa09c6-9a8d-40b9-aee2-2641160f969e',NULL,'echo program=\\\"convert\\\"\\; version=\\\"`convert -version | grep Version:`\\\"','',NULL,'convert event detail','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('1628571b-c2cd-4822-afdb-53561400c7c4','command','f3f2dae8-54ed-40b4-8f20-cffb249262cf','a9111dc0-edc9-47f8-85e5-ad4013971361','convert \"%fileFullName%\" -thumbnail 100x100 -layers merge\n \"%outputDirectory%%postfix%.jpg\"','%outputDirectory%%postfix%.jpg','0fd7935a-ed0d-4f67-aa25-1b44684f6aca','Transcoding to jpg thumbnail with convert','ca278b07-8137-4a75-b84f-9b10994ed006','JPEG File Interchange Format',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('1c9728c0-16d3-4db0-81c3-7a51068caa1c','command','a5fa09c6-9a8d-40b9-aee2-2641160f969e','67432cf4-fd33-4943-a1e7-4b4a8c27bb1d','mkdir \"%outputDirectory%\" && readpst -r \"%inputFile%\" -o \"%outputDirectory%\"','%outputDirectory%','42e7fdc3-42c6-4715-b79a-e900df916cab','Extracting pst file','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('1db2deea-01dd-43e5-9f27-dc9ed02acdfe','eventDetail','a5fa09c6-9a8d-40b9-aee2-2641160f969e',NULL,'echo program=\\\"inkscape\\\"\\; version=\\\"`inkscape -V`\\\" ','',NULL,'inkscape event detail','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('299390fd-24f2-4ed5-9bb6-e270b9e2f64f','command','f3f2dae8-54ed-40b4-8f20-cffb249262cf',NULL,'7z x -bd -o\"%outputDirectory%\" \"%inputFile%\"','%outputDirectory%','914fe3e5-fa45-47c6-858d-e11bc71f5b76','Extracting 7zip compatable file.','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('42e7fdc3-42c6-4715-b79a-e900df916cab','eventDetail','a5fa09c6-9a8d-40b9-aee2-2641160f969e',NULL,'echo program=\\\"readpst\\\"\\; version=\\\"`readpst -V`\\\"','',NULL,'readpst event detail','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('45a2f264-37d7-4881-b28c-25d628bf2823','eventDetail','a5fa09c6-9a8d-40b9-aee2-2641160f969e',NULL,'echo program=\\\"Ghostscript\\\"\\; version=\\\"`gs --version`\\\" ','',NULL,'Ghostscript event detail','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('520f976f-476d-4ec6-be1c-7c448c5c074d','command','a5fa09c6-9a8d-40b9-aee2-2641160f969e','a9111dc0-edc9-47f8-85e5-ad4013971361','ffmpeg -i \"%fileFullName%\" -ac 2 -ab 192000 \"%outputDirectory%%prefix%%fileName%%postfix%.mp3\"','%outputDirectory%%prefix%%fileName%%postfix%.mp3','03f67b09-a8e7-44e2-b876-5786d7da09c7','Transcoding to mp3 with ffmpeg','ca278b07-8137-4a75-b84f-9b10994ed006','MPEG 1/2 Audio Layer 3',NULL,'2012-12-19 01:54:26',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('53d34372-8805-4adf-b940-6c05b835a94f','command','a5fa09c6-9a8d-40b9-aee2-2641160f969e','a9111dc0-edc9-47f8-85e5-ad4013971361','outputExtension=\"pdf\"\nddirname=\"`dirname \"%fileFullName%\"`\"\ndbasename=\"`basename \"%fileFullName%\"`\" \ncd \"$ddirname\"\noutputFile=\"%outputDirectory%%prefix%%fileName%%postfix%.${outputExtension}\"\n\nflock -x /var/lock/documentConversion.lock -c \"soffice --nodefault --nologo --headless --convert-to $outputExtension \\\"${dbasename}\\\"\"\necho The exit code was $?\nset -e\n\necho mv \"%fileDirectory%%fileName%.${outputExtension}\" \"$outputFile\"\nmv \"%fileDirectory%%fileName%.${outputExtension}\" \"$outputFile\"\n','%outputDirectory%%prefix%%fileName%%postfix%.pdf','0889b85d-e891-4d18-8e89-33b02783ba7a','Transcoding to pdf with openOffice','ca278b07-8137-4a75-b84f-9b10994ed006','Acrobat PDF/A - Portable Document Format',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('5c319663-42f6-4a46-b957-f730c5f3ae88','eventDetail','f3f2dae8-54ed-40b4-8f20-cffb249262cf',NULL,'echo \"/usr/lib/archivematica/transcoder/transcoderScripts/\" \"%fileFullName%\" \"%outputDirectory%%prefix%%fileName%%postfix%.mbox\"','',NULL,'Transcoding maildir to mbox event detail','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('64c450b4-135c-46d1-a9aa-3f9b15671677','command','a5fa09c6-9a8d-40b9-aee2-2641160f969e','a9111dc0-edc9-47f8-85e5-ad4013971361','sudo /usr/bin/inkscape \"%fileFullName%\" --export-plain-svg=\"%outputDirectory%%prefix%%fileName%%postfix%.svg\"\n sudo chmod 777 \"%outputDirectory%%prefix%%fileName%%postfix%.svg\"','%outputDirectory%%prefix%%fileName%%postfix%.svg','1db2deea-01dd-43e5-9f27-dc9ed02acdfe','Transcoding to plain svg with inkscape','ca278b07-8137-4a75-b84f-9b10994ed006','Scalable Vector Graphics',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('665e6bf4-0c6d-432a-a0c6-04050e1cfae5','command','a5fa09c6-9a8d-40b9-aee2-2641160f969e',NULL,'exitCode=0\nfilesFound=0\nfunction checkDirectory {\n cd \"$1\"\n for f in *; do \n if [ -d \"$f\" ] ; then\n checkDirectory \"$f\" || exitCode=1\n else\n if [ -s \"$f\" ]; then\n filesFound=1\n else\n echo \"0 byte file: $f\"\n exitCode=2\n fi\n fi\n done\n}\n\ncheckDirectory \"%outputLocation%\"\nif [ $filesFound -eq 0 ] ; then\n exit 3\nelse\n exit $exitCode\nfi','',NULL,'Standard directory verification command','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('67432cf4-fd33-4943-a1e7-4b4a8c27bb1d','verification','ee6869f7-3ac0-44ed-a5ca-50f0f81cd7c1',NULL,'#!/usr/bin/python\n#\n\nimport os\ndirectory = \"%outputLocation%\"\nextension = \".mbox\"\n\ndef appendExtensionToFilesWithNoExtension(directory, extension, baseDirectory=\"\"):\n for dirname, dirnames, filenames in os.walk(directory):\n for filename in filenames:\n if filename.rfind(\".\") == -1:\n newName = os.path.join(os.path.dirname(filename), \"0-\" + os.path.basename(filename)) + extension\n n=1\n while os.path.exists(os.path.join(dirname, newName)):\n newName = os.path.join(os.path.dirname(filename), \"0-\" + os.path.basename(filename)) + n.__str__() + extension\n n+=1\n print \"Renaming: \", filename, \" -> \" + newName\n os.rename(os.path.join(dirname, filename), os.path.join(dirname, newName))\n\nappendExtensionToFilesWithNoExtension(directory, extension, baseDirectory=directory)\n','',NULL,'append .mbox extension to files without extensions','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('6957fdac-a1ed-470f-89f7-fb00be42ea13','command','f3f2dae8-54ed-40b4-8f20-cffb249262cf','a9111dc0-edc9-47f8-85e5-ad4013971361','convert \"%fileFullName%\" -sampling-factor 4:4:4 -quality 60 -layers merge\n \"%outputDirectory%%prefix%%fileName%%postfix%.jpg\"','%outputDirectory%%prefix%%fileName%%postfix%.jpg','0fd7935a-ed0d-4f67-aa25-1b44684f6aca','Transcoding to jpg with convert','ca278b07-8137-4a75-b84f-9b10994ed006','JPEG File Interchange Format',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('69b4ac0a-6e3a-4eaa-bf85-12fb5489a41d','command','a5fa09c6-9a8d-40b9-aee2-2641160f969e','a9111dc0-edc9-47f8-85e5-ad4013971361','outputExtension=\"odp\"\nddirname=\"`dirname \"%fileFullName%\"`\"\ndbasename=\"`basename \"%fileFullName%\"`\" \ncd \"$ddirname\"\noutputFile=\"%outputDirectory%%prefix%%fileName%%postfix%.${outputExtension}\"\n\nflock -x /var/lock/documentConversion.lock -c \"soffice --nodefault --nologo --headless --convert-to $outputExtension \\\"${dbasename}\\\"\"\necho The exit code was $?\nset -e\necho mv \"%fileDirectory%%fileName%.${outputExtension}\" \"$outputFile\"\nmv \"%fileDirectory%%fileName%.${outputExtension}\" \"$outputFile\"\n','%outputDirectory%%prefix%%fileName%%postfix%.odp','0889b85d-e891-4d18-8e89-33b02783ba7a','Transcoding to ODP with openOffice','ca278b07-8137-4a75-b84f-9b10994ed006','OpenDocument Presentation',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('70547204-50b1-4e45-8d36-276fe4510728','command','a5fa09c6-9a8d-40b9-aee2-2641160f969e','a9111dc0-edc9-47f8-85e5-ad4013971361','outputExtension=\"ods\"\nddirname=\"`dirname \"%fileFullName%\"`\"\ndbasename=\"`basename \"%fileFullName%\"`\" \ncd \"$ddirname\"\noutputFile=\"%outputDirectory%%prefix%%fileName%%postfix%.${outputExtension}\"\n\nflock -x /var/lock/documentConversion.lock -c \"soffice --nodefault --nologo --headless --convert-to $outputExtension \\\"${dbasename}\\\"\"\necho The exit code was $?\nset -e\n\necho mv \"%fileDirectory%%fileName%.${outputExtension}\" \"$outputFile\"\nmv \"%fileDirectory%%fileName%.${outputExtension}\" \"$outputFile\"\n','%outputDirectory%%prefix%%fileName%%postfix%.ods','0889b85d-e891-4d18-8e89-33b02783ba7a','Transcoding to ODS with openOffice','ca278b07-8137-4a75-b84f-9b10994ed006','OpenDocument Spreadsheet',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('76fd899e-cd5a-4da3-8752-45c43fc18656','command','a5fa09c6-9a8d-40b9-aee2-2641160f969e','a9111dc0-edc9-47f8-85e5-ad4013971361','#!/bin/bash\n\ninputFile=\"%fileFullName%\"\noutputFile=\"%outputDirectory%%prefix%%fileName%%postfix%.mkv\"\naudioCodec=\"pcm_s16le\"\nvideoCodec=\"ffv1\"\n\ncommand=\"ffmpeg -vsync passthrough -i \\\"${inputFile}\\\" -map 0:v -map 0:a \"\ncommand=\"${command} -vcodec ${videoCodec} -g 1 \"\ncommand=\"${command} -acodec ${audioCodec}\"\n\n\ncommand=\"${command} ${outputFile}\"\n\necho $command\neval $command\n','%outputDirectory%%prefix%%fileName%%postfix%.mkv','77f73d63-7d82-4b61-8dec-6626c3dc224a','Transcoding to mkv with ffmpeg','ca278b07-8137-4a75-b84f-9b10994ed006','FF Video Codec 1 / Pulse Code Modulation in Matroska Media Container',NULL,'2012-12-19 01:54:26',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('77f73d63-7d82-4b61-8dec-6626c3dc224a','eventDetail','a5fa09c6-9a8d-40b9-aee2-2641160f969e',NULL,'echo program=\\\"ffmpeg\\\"\\; version=\\\"`ffmpeg 2>&1 | grep --ignore-case \"FFmpeg version\"`\\\"','',NULL,'Get event detail text for ffmpeg extraction','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-12-19 01:54:26',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('814febd9-945b-4580-8381-9b146fd042d9','command','f3f2dae8-54ed-40b4-8f20-cffb249262cf',NULL,'cp -R \"/var/archivematica/sharedDirectory/sharedMicroServiceTasksConfigs/transcoder/defaultIcons/default.jpg\" \"%outputDirectory%%postfix%.jpg\"','%outputDirectory%%postfix%.jpg',NULL,'Using default thumbnail.','ca278b07-8137-4a75-b84f-9b10994ed006','JPEG File Interchange Format',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('90db809f-268c-4b26-988b-b44c103ac655','command','f3f2dae8-54ed-40b4-8f20-cffb249262cf','ef3ea000-0c3c-4cae-adc2-aa2a6ccbffce','\"/usr/lib/archivematica/transcoder/transcoderScripts/archivematicaMaildirToMbox.py\" \"%fileFullName%\" \"%outputDirectory%%prefix%%fileName%%postfix%.mbox\"','%outputDirectory%%prefix%%fileName%%postfix%.mbox','5c319663-42f6-4a46-b957-f730c5f3ae88','Transcoding maildir to mbox','ca278b07-8137-4a75-b84f-9b10994ed006','MBox',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('914fe3e5-fa45-47c6-858d-e11bc71f5b76','eventDetail','a5fa09c6-9a8d-40b9-aee2-2641160f969e',NULL,'echo program=\\\"7z\\\"\\; version=\\\"`7z | grep Version`\\\"','',NULL,'Get event detail text for 7z extraction','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('9dc10fcb-5809-4c0b-917a-02517ef4d1c6','command','a5fa09c6-9a8d-40b9-aee2-2641160f969e','a9111dc0-edc9-47f8-85e5-ad4013971361','ffmpeg -i \"%fileFullName%\" \"%outputDirectory%%prefix%%fileName%%postfix%.wav\"','%outputDirectory%%prefix%%fileName%%postfix%.wav','03f67b09-a8e7-44e2-b876-5786d7da09c7','Transcoding to wav with ffmpeg','ca278b07-8137-4a75-b84f-9b10994ed006','Waveform Audio',NULL,'2012-12-19 01:54:26',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('a1921355-58c2-4d6f-9535-a6d227a0be16','command','f3f2dae8-54ed-40b4-8f20-cffb249262cf','a9111dc0-edc9-47f8-85e5-ad4013971361','convert \"%fileFullName%\" -resize 70% \"%outputDirectory%%prefix%%fileName%%postfix%.jpg\"','%outputDirectory%%prefix%%fileName%%postfix%.jpg','0fd7935a-ed0d-4f67-aa25-1b44684f6aca','Transcoding to jpg with convert','ca278b07-8137-4a75-b84f-9b10994ed006','JPEG File Interchange Format',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('a34ddc9b-c922-4bb6-8037-bbe713332175','command','f3f2dae8-54ed-40b4-8f20-cffb249262cf','a9111dc0-edc9-47f8-85e5-ad4013971361','convert \"%fileFullName%\" +compress \"%outputDirectory%%prefix%%fileName%%postfix%.tif\"','%outputDirectory%%prefix%%fileName%%postfix%.tif','0fd7935a-ed0d-4f67-aa25-1b44684f6aca','Transcoding to tif with convert','ca278b07-8137-4a75-b84f-9b10994ed006','Tagged Image File Format',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('a74cbffb-281b-49ee-8e96-fbac2ab54785','command','f3f2dae8-54ed-40b4-8f20-cffb249262cf','a9111dc0-edc9-47f8-85e5-ad4013971361','ps2pdf \"%fileFullName%\" \"%outputDirectory%%prefix%%fileName%%postfix%.pdf\"','%outputDirectory%%prefix%%fileName%%postfix%.pdf','ebe5a085-6039-4c8d-9953-beed5b03c778','Transcoding to pdf with ps2pdf','ca278b07-8137-4a75-b84f-9b10994ed006','Acrobat PDF/A - Portable Document Format',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('a90025a4-9e8b-44b9-a267-9580c7c5460b','command','a5fa09c6-9a8d-40b9-aee2-2641160f969e','b812bf73-7f58-4fa8-84d3-415e2f56daa8','mkdir \"%outputDirectory%\" && readpst -r \"%inputFile%\" -o \"%outputDirectory%\" -S','%outputDirectory%','42e7fdc3-42c6-4715-b79a-e900df916cab','Extracting pst file','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('a9111dc0-edc9-47f8-85e5-ad4013971361','verification','f3f2dae8-54ed-40b4-8f20-cffb249262cf',NULL,'test -s \"%outputLocation%\"','',NULL,'Standard verification command (non zero filesize)','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('ab17d28b-3f9a-45e2-b6da-fb19cf759c13','command','a5fa09c6-9a8d-40b9-aee2-2641160f969e','a9111dc0-edc9-47f8-85e5-ad4013971361','outputExtension=\"odt\"\nddirname=\"`dirname \"%fileFullName%\"`\"\ndbasename=\"`basename \"%fileFullName%\"`\" \ncd \"$ddirname\"\noutputFile=\"%outputDirectory%%prefix%%fileName%%postfix%.${outputExtension}\"\n\nflock -x /var/lock/documentConversion.lock -c \"soffice --nodefault --nologo --headless --convert-to $outputExtension \\\"${dbasename}\\\"\"\necho The exit code was $?\nset -e\n\necho mv \"%fileDirectory%%fileName%.${outputExtension}\" \"$outputFile\"\nmv \"%fileDirectory%%fileName%.${outputExtension}\" \"$outputFile\"\n','%outputDirectory%%prefix%%fileName%%postfix%.odt','0889b85d-e891-4d18-8e89-33b02783ba7a','Transcoding to ODT with openOffice','ca278b07-8137-4a75-b84f-9b10994ed006','OpenDocument Text',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('b3b3b726-acef-4d58-82e4-ef8875d654ce','command','f3f2dae8-54ed-40b4-8f20-cffb249262cf',NULL,'echo Not including \"%inputFile%\" in DIP.','%inputFile%',NULL,'Don\'t include in DIP.','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('b812bf73-7f58-4fa8-84d3-415e2f56daa8','verification','ee6869f7-3ac0-44ed-a5ca-50f0f81cd7c1',NULL,'#!/usr/bin/python\n#\n\nimport os\ndirectory = \"%outputLocation%\"\nextension = \".mboxi\"\n\ndef appendExtensionToFilesWithNoExtension(directory, extension, baseDirectory=\"\"):\n for dirname, dirnames, filenames in os.walk(directory):\n for filename in filenames:\n if filename.rfind(\".\") == -1:\n newName = filename + extension\n n=1\n while os.path.exists(os.path.join(dirname, newName)):\n newName = newName = filename + n.__str__() + extension\n n+=1\n print \"Renaming: \", filename, \" -> \" + newName\n os.rename(os.path.join(dirname, filename), os.path.join(dirname, newName))\n\nappendExtensionToFilesWithNoExtension(directory, extension, baseDirectory=directory)\n','',NULL,'append .mboxi extension to files without extensions','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','command','a5fa09c6-9a8d-40b9-aee2-2641160f969e','a9111dc0-edc9-47f8-85e5-ad4013971361','ffmpeg -i \"%fileFullName%\" -vcodec libx264 -pix_fmt yuv420p -preset medium -crf 18 \"%outputDirectory%%prefix%%fileName%%postfix%.mp4\"','%outputDirectory%%prefix%%fileName%%postfix%.mp4','77f73d63-7d82-4b61-8dec-6626c3dc224a','Transcoding to mp4 with ffmpeg','ca278b07-8137-4a75-b84f-9b10994ed006','MPEG-4 Media File',NULL,'2012-12-19 01:54:26',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('c072c3c8-ba0e-480b-b0ee-2f27c6c0653e','eventDetail','a5fa09c6-9a8d-40b9-aee2-2641160f969e',NULL,'echo program=\\\"unrar-nonfree\\\"\\; version=\\\"`unrar-nonfree | grep \'UNRAR\'`\\\"','',NULL,'Get event detail text for unrar extraction','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('d6a33093-85d5-4088-83e1-b7a774a826bd','command','f3f2dae8-54ed-40b4-8f20-cffb249262cf','a9111dc0-edc9-47f8-85e5-ad4013971361','gs -dPDFA -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -dPDFACompatibilityPolicy=1 -sOutputFile=\"%outputDirectory%%prefix%%fileName%%postfix%.pdf\" \"%fileFullName%\"','%outputDirectory%%prefix%%fileName%%postfix%.pdf','45a2f264-37d7-4881-b28c-25d628bf2823','Transcoding to pdfa with Ghostscript','ca278b07-8137-4a75-b84f-9b10994ed006','Acrobat PDF/A - Portable Document Format',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('d91f0637-b2f6-4492-ac1b-23c155102a3e','command','f3f2dae8-54ed-40b4-8f20-cffb249262cf','a9111dc0-edc9-47f8-85e5-ad4013971361','inkscape -z \"%fileFullName%\" --export-pdf=\"%outputDirectory%%prefix%%fileName%%postfix%.pdf\"','%outputDirectory%%prefix%%fileName%%postfix%.pdf','1db2deea-01dd-43e5-9f27-dc9ed02acdfe','Transcoding to pdf with inkscape','ca278b07-8137-4a75-b84f-9b10994ed006','Acrobat PDF/A - Portable Document Format',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('dc949cb7-ad68-44ba-a795-d2a43a18164f','command','f3f2dae8-54ed-40b4-8f20-cffb249262cf',NULL,'cp -R \"%inputFile%\" \"%outputDirectory%%prefix%%fileName%%postfix%%fileExtensionWithDot%\"','%outputDirectory%%prefix%%fileName%%postfix%%fileExtensionWithDot%',NULL,'Copying file to access directory.','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('dd4b3428-134f-4e51-870c-69e71ad58a21','command','a5fa09c6-9a8d-40b9-aee2-2641160f969e',NULL,'mkdir \"%outputDirectory%\" && unrar-nonfree x \"%inputFile%\" \"%outputDirectory%\"','%outputDirectory%','c072c3c8-ba0e-480b-b0ee-2f27c6c0653e','Extracting unrar-nonfree compatable file.','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('e96b787d-57f6-41a4-9693-d0634bfacacb','command','f3f2dae8-54ed-40b4-8f20-cffb249262cf','a9111dc0-edc9-47f8-85e5-ad4013971361','ps2pdf -dEPSCrop -dPDFA \"%fileFullName%\" \"%outputDirectory%%prefix%%fileName%%postfix%.pdf\"','%outputDirectory%%prefix%%fileName%%postfix%.pdf','ebe5a085-6039-4c8d-9953-beed5b03c778','Transcoding to pdf with ps2pdf','ca278b07-8137-4a75-b84f-9b10994ed006','Acrobat PDF/A - Portable Document Format',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('ebe5a085-6039-4c8d-9953-beed5b03c778','eventDetail','a5fa09c6-9a8d-40b9-aee2-2641160f969e',NULL,'echo program=\\\"ps2pdf\\\"\\; program=\\\"Ghostscript\\\"\\; version=\\\"`gs --version`\\\" ','',NULL,'ps2pdf event detail','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); -INSERT INTO `Command` (`pk`, `commandUsage`, `commandType`, `verificationCommand`, `command`, `outputLocation`, `eventDetailCommand`, `description`, `supportedBy`, `outputFileFormat`, `replaces`, `lastModified`, `enabled`) VALUES ('ef3ea000-0c3c-4cae-adc2-aa2a6ccbffce','verification','f3f2dae8-54ed-40b4-8f20-cffb249262cf',NULL,'test -f \"%outputLocation%\"','',NULL,'Standard verification command (file exists only)','ca278b07-8137-4a75-b84f-9b10994ed006','',NULL,'2012-10-02 03:24:59',1); - -/*!40000 ALTER TABLE `Command` ENABLE KEYS */; -UNLOCK TABLES; - - -LOCK TABLES `FileIDType` WRITE; -/*!40000 ALTER TABLE `FileIDType` DISABLE KEYS */; -INSERT INTO `FileIDType` (`pk`, `description`, `replaces`, `lastModified`, `enabled`) VALUES ('076cce1b-9b46-4343-a193-11c2662c9e02','FileIDByFitsFileUtilityFormat',NULL,'2013-03-11 22:54:24',1); -INSERT INTO `FileIDType` (`pk`, `description`, `replaces`, `lastModified`, `enabled`) VALUES ('16ae42ff-1018-4815-aac8-cceacd8d88a8','Identified by file extension.',NULL,'2013-03-11 22:54:24',1); -INSERT INTO `FileIDType` (`pk`, `description`, `replaces`, `lastModified`, `enabled`) VALUES ('1d8f3bb3-da8a-4ef6-bac7-b65942df83fc','FileIDByTika',NULL,'2013-03-11 22:54:24',1); -INSERT INTO `FileIDType` (`pk`, `description`, `replaces`, `lastModified`, `enabled`) VALUES ('237d393f-aba2-44ae-b61c-76232d383883','FileIDByFitsFileUtilityMimetype',NULL,'2013-03-11 22:54:24',1); -INSERT INTO `FileIDType` (`pk`, `description`, `replaces`, `lastModified`, `enabled`) VALUES ('8e39a076-d359-4c60-b6f4-38f7ae6adcdf','FileIDByFitsFfidentMimetype',NULL,'2013-03-11 22:54:24',1); -INSERT INTO `FileIDType` (`pk`, `description`, `replaces`, `lastModified`, `enabled`) VALUES ('9ffdc6e8-f25a-4e5b-aaca-02769c4e7b7f','FileIDByMediaInfo',NULL,'2013-03-11 22:54:24',1); -INSERT INTO `FileIDType` (`pk`, `description`, `replaces`, `lastModified`, `enabled`) VALUES ('ac5d97dc-df9e-48b2-81c5-4a8b044355fa','FileIDByFitsDROIDIdentificationPUID',NULL,'2013-03-11 22:54:24',1); -INSERT INTO `FileIDType` (`pk`, `description`, `replaces`, `lastModified`, `enabled`) VALUES ('afdbee13-eec5-4182-8c6c-f5638ee290f3','FileIDByFIDO',NULL,'2013-03-11 22:54:24',1); -INSERT INTO `FileIDType` (`pk`, `description`, `replaces`, `lastModified`, `enabled`) VALUES ('b0bcccfb-04bc-4daa-a13c-77c23c2bda85','FileIDByFitsJhoveFormat',NULL,'2013-03-11 22:54:24',1); -INSERT INTO `FileIDType` (`pk`, `description`, `replaces`, `lastModified`, `enabled`) VALUES ('c26227f7-fca8-4d98-9d8e-cfab86a2dd0a','FileIDByFitsFitsMimetype',NULL,'2013-03-11 22:54:24',1); -INSERT INTO `FileIDType` (`pk`, `description`, `replaces`, `lastModified`, `enabled`) VALUES ('cff7437f-20c6-440a-b801-37c647da2cf1','FileIDByFitsFitsFormat',NULL,'2013-03-11 22:54:24',1); -INSERT INTO `FileIDType` (`pk`, `description`, `replaces`, `lastModified`, `enabled`) VALUES ('dc551ed2-9d2f-4c8a-acce-760fb740af48','FileIDByFitsJhoveMimeType',NULL,'2013-03-11 22:54:24',1); -INSERT INTO `FileIDType` (`pk`, `description`, `replaces`, `lastModified`, `enabled`) VALUES ('f794555f-50ad-4fd4-9eab-67bc47c431ab','FileIDByFitsDROIDMimeType',NULL,'2013-03-11 22:54:24',1); -/*!40000 ALTER TABLE `FileIDType` ENABLE KEYS */; -UNLOCK TABLES; - -LOCK TABLES `FileID` WRITE; -/*!40000 ALTER TABLE `FileID` DISABLE KEYS */; -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('00ee1251-c1ec-49cf-95e7-f0c037d23f43','PUID=\"fmt/419\", FormatName=\"Adobe Illustrator\", FormatVersion=\"4.0\", FormatRisk=\"\", Extension=\"ai,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:30',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('01a2674c-850c-46ab-a768-e8593baf9404','A .psd file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('01c97b84-76e0-4b94-9e3b-1e670bd9468b','video/x-ms-wmv',0,0,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:18:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('0204d3a6-d566-4f90-b298-578eaf6c99f6','PUID=\"fmt/3\", FormatName=\"Graphics Interchange Format\", FormatVersion=\"1987a\", FormatRisk=\"\", Extension=\"gif,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:17',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('0205b77b-4396-4931-95b8-d896a123b0e0','A .m2v file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('02246977-bef6-46da-98c2-de7522111899','A .gif file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('03bb2a7e-47bd-44fa-ab1d-797370bb1ead','PDF',0,1,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-15 21:32:21',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('03bbfaaf-124c-4454-8082-50e0b3d23e57','A .pct file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('05e0d0e9-f51e-45d6-86e5-283f8eeaa00d','PUID=\"x-fmt/390\", FormatName=\"Exchangeable Image File Format (Compressed)\", FormatVersion=\"2.1\", FormatRisk=\"\", Extension=\"jpg,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:15',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('061ec789-c1e9-40af-83b4-a6b0897267d8','audio/x-ms-wma',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:39',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('065a6379-da8d-4d90-8a6e-8286b31e850f','image/svg+xml',1,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:38',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('06b8d06a-aac4-4d09-b0b4-fe67d70faf14','JPEG File Interchange Format',0,1,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:43',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('06e64ff4-3de9-40f6-a9c0-bcf0e5aba5c4','Adobe Photoshop Image, 800 x 533, RGB, 3x 8-bit channels',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:26:57',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('074d4c3a-c9d7-4751-b2eb-72e0cb45f69c','image/png',1,0,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:18:14',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('075bcd44-b926-4a45-b8a1-f75220f5c903','Rich Text Format',1,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:31',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('07cc11b2-5316-4648-a1fb-71d28d9e2200','text/html',1,1,'dc551ed2-9d2f-4c8a-acce-760fb740af48',NULL,'2012-10-16 18:18:19',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('07e990d9-5b60-4cb1-8e83-40e25dcdcdae','A .erf file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('08255d3a-bd85-434e-97a8-14f98b0c4ec8','data',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-11 23:10:30',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('085d00ca-f8af-43e5-9ef9-6d21dec5aff8','Windows Bitmap',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:41',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('090ff58a-f772-455b-9743-2615e17b6aa8','image/jpeg',0,1,'8e39a076-d359-4c60-b6f4-38f7ae6adcdf',NULL,'2012-10-16 18:18:05',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('095e1f7a-815c-449b-ae7e-b5d96e434f2d','Apple QuickTime movie (fast start)',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-11 23:10:31',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('0a01c5ed-7e05-4dc8-8082-48fe61bab13d','image/bmp',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:40',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('0a779ddf-8b63-48fb-a46d-7e2bf32a8b04','A .xls file',1,1,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('0bd0540c-db99-47e2-b63b-12be1c6eafcf','A .pdf file',0,1,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('0c2ad7a0-83b5-4f83-aed9-ee6ad99510b3','Microsoft ASF',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:29',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('0ce828c7-e884-40be-9a6a-51b53cb349c5','PUID=\"x-fmt/396\", FormatName=\"Exchangeable Image File Format (Audio)\", FormatVersion=\"2.2\", FormatRisk=\"\", Extension=\"wav,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:18',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('0cea8124-51f7-49ed-822a-b6b842673c62','Macromedia FLV',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:35',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('0d1861e3-823d-4508-8de4-26bae086efb2','image/jpeg',0,1,'dc551ed2-9d2f-4c8a-acce-760fb740af48',NULL,'2012-10-16 18:18:20',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('0d2065a9-dce6-41d9-8488-e71063bb0e0e','PUID=\"fmt/147\", FormatName=\"Acrobat PDF/X - Portable Document Format - Exchange 2:2003\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:28',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('0f42eabd-4da2-48ed-aa30-f76b1b6d94b7','fmt/149',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-10-16 21:33:04',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('0f919e47-d406-4e9c-96c2-0a372c818f73','Targa image data - RGB 1419 x 1001',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-18 22:57:15',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('0f965b92-8f6c-4a52-bffd-a28da459fdf9','image/x-canon-cr2',0,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 18:27:02',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('1087d1e9-3dff-4b6e-b1e2-625141bbf814','PUID=\"fmt/107\", FormatName=\"Macromedia Flash\", FormatVersion=\"4\", FormatRisk=\"\", Extension=\"swf,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:34',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('10d24277-054c-4c20-9774-9cac31b22b42','A .odt file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('114db91c-503f-43c2-a83e-c2de6b817e47','fmt/48',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-10-16 21:32:57',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('120836eb-b7e7-42dc-95f1-1470b8000dec','Microsoft PowerPoint 2007+',1,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:30',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('123481b3-aca7-41ec-97d7-7e6601395d4a','PUID=\"fmt/124\", FormatName=\"Encapsulated PostScript File Format\", FormatVersion=\"3\", FormatRisk=\"\", Extension=\"eps,epsf,ps,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:21',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('15212ba7-3a43-418b-a3b7-d8095c2b77ff','text/plain; charset=US-ASCII',1,0,'dc551ed2-9d2f-4c8a-acce-760fb740af48',NULL,'2012-10-16 17:55:58',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('1567b10b-1474-4df5-a6df-ce3b8a92ee97','Targa image data - RGB 1419 x 1001',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-18 22:57:04',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('15aceaf6-cf02-4aab-baeb-0f2402e41125','GIF image data, version 89a, 640 x 320',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:26:58',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('16d79ccc-4f77-44d7-8b25-054915b289c5','PUID=\"fmt/53\", FormatName=\"Rich Text Format\", FormatVersion=\"1.8\", FormatRisk=\"\", Extension=\"rtf,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:29',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('16d9ada5-08b6-48cc-b4f8-723327803581','PUID=\"fmt/19\", FormatName=\"Acrobat PDF 1.5 - Portable Document Format\", FormatVersion=\"1.5\", FormatRisk=\"\", Extension=\"pdf,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:25',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('17d1046c-71bd-4c90-9291-6744e5f2fa38','Waveform Audio',1,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:42',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('180a7231-0444-44c9-8371-81825a2be339','image/x-raw',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:35',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('1887c956-1e50-4bae-8cb5-3a5a68d2ef80','video/quicktime',0,0,'8e39a076-d359-4c60-b6f4-38f7ae6adcdf',NULL,'2012-10-15 21:30:49',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('18d00a54-6b22-4434-8563-e66f80431e9d','A .ac3 file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('1984ade9-588b-4817-8460-9dfea763cd23','PUID=\"fmt/1\", FormatName=\"Broadcast WAVE\", FormatVersion=\"0\", FormatRisk=\"\", Extension=\"wav,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:18',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('19d7d449-99c3-4a3d-98cb-428665cf25d1','A .3fr file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('1b801662-0446-4bf5-bbd5-b093b27a1cef','image/svg+xml',1,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 18:18:02',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('1d0b3087-b43f-412f-8e10-2063b912ed81','PUID=\"fmt/143\", FormatName=\"Waveform Audio (WAVEFORMATEXTENSIBLE)\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"wav,wave,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:19',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('1d21c442-22b6-490d-b84a-07311da373c1','PUID=\"fmt/276\", FormatName=\"Acrobat PDF 1.7 - Portable Document Format\", FormatVersion=\"1.7\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:25',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('1d283460-e24c-4cbb-8476-2a66df560456','application/octet-stream',0,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 17:55:52',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('1d31b54e-1745-4b37-9468-d35e8e317d05','PNG image data, 757 x 957, 8-bit/color RGB, non-interlaced',1,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:18:00',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('1db0ee9f-a668-45f2-ad79-50e395a9e171','Microsoft PowerPoint 2007+',1,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-11 23:10:30',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('1ddbfa77-0390-4905-aeda-58d2984db924','PUID=\"fmt/106\", FormatName=\"Macromedia Flash\", FormatVersion=\"3\", FormatRisk=\"\", Extension=\"swf,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:34',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('1ded295a-3065-4da7-8ba7-42edef36a1db','Windows Media Video',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:29',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('1f8df48d-ce76-49e4-80ed-cc0d0ce65d2a','PUID=\"x-fmt/203\", FormatName=\"WordPerfect for Windows Document\", FormatVersion=\"5.2\", FormatRisk=\"\", Extension=\"w52,wp,wpd,wp5,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:14',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('1f968676-81a2-4cd4-8d3d-ee86342e5a57','fmt/47',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-10-16 21:32:57',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('1fd534f6-139f-41ed-80dd-2b6b7fee6768','PUID=\"fmt/145\", FormatName=\"Acrobat PDF/X - Portable Document Format - Exchange 1:2001\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:27',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('20109b15-39c8-4d10-bac5-617ae2850e22','x-fmt/135',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-10-16 21:32:58',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('209d95fb-25be-471f-902d-dd08fe5be83f','PUID=\"fmt/354\", FormatName=\"Acrobat PDF/A - Portable Document Format\", FormatVersion=\"1b\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:25',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('21749616-c014-4e81-84d5-f90af82532b7','RAF EXIF',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:33',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('2184c88b-bbe4-41a1-9a21-281f5cf55433','PUID=\"fmt/480\", FormatName=\"Acrobat PDF/A - Portable Document Format\", FormatVersion=\"3b\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:26',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('21b07ca7-b9e9-490a-a4db-39081a306862','fmt/49',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-10-16 21:32:58',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('23dca992-ea19-4731-8e96-20739509851c','PUID=\"fmt/402\", FormatName=\"Truevision TGA Bitmap\", FormatVersion=\"2.0\", FormatRisk=\"\", Extension=\"tga,icb,vda,vst,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:15',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('23e376ee-554b-4ac0-b34a-86031f77e526','audio/mpeg',0,0,'8e39a076-d359-4c60-b6f4-38f7ae6adcdf',NULL,'2012-10-15 21:30:50',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('24237983-b248-4d0d-b1d2-04ca8e469198','Portable Network Graphics',1,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:36',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('24950d29-e3c6-408e-89e4-28f689239a13','audio/x-flac',0,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 18:27:00',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('24b9aa78-7055-4776-910d-da124b1863f9','KDC EXIF',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:41',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('257c711a-6d38-4b8c-8c54-6c25dc5a4d43','PUID=\"fmt/104\", FormatName=\"Macromedia Flash\", FormatVersion=\"1\", FormatRisk=\"\", Extension=\"swf,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:33',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('268c57bc-edfc-4da7-b01a-c1ebb2438022','fmt/131',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-10-16 21:32:58',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('26a45d64-5e4d-4afc-8297-265f1a4f86bc','Microsoft Excel 2007+',1,1,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-11 23:10:31',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('26fcd232-8685-40ea-a1b3-e845662d9a51','PUID=\"fmt/437\", FormatName=\"Digital Negative Format (DNG)\", FormatVersion=\"1.2\", FormatRisk=\"\", Extension=\"dng,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:20',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('274e16c7-1b68-498b-8290-e339d4197136','MPEG',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:40',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('2797e915-0097-40f6-ba05-ecf94ab34dc5','A .arw file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('27d6e866-3b9d-487b-b3b7-5a1bc4e72a3f','text/html',1,1,'8e39a076-d359-4c60-b6f4-38f7ae6adcdf',NULL,'2012-10-16 18:18:03',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('27f6b2b1-0c93-4b79-9f2e-7f10639d8987','application/msword',1,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:36',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('288aaae9-9513-4bf3-99c3-b5fd26ed85af','A .nef file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('2898de0d-0bae-45ec-a915-05d92d1df637','TIFF image data, little-endian',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:26:58',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('2aab64ea-9bbd-4715-a6bf-ad3ee5ea7072','WAVE',1,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-15 21:32:22',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('2b6c2412-b978-4478-8d09-b91ed4c6dc6a','PUID=\"fmt/11\", FormatName=\"Portable Network Graphics\", FormatVersion=\"1.0\", FormatRisk=\"\", Extension=\"png,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:21',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('2bb14a8d-7eac-4c93-9060-726b4acefbc9','video/quicktime',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:39',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('2c0fbd65-f7ba-452d-bd93-a40b371e2a68','Audio Interchange File Format',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:33',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('2c2c85ee-1437-4beb-82fb-0c7fca51c49a','PUID=\"fmt/38\", FormatName=\"Microsoft Word for Windows Document\", FormatVersion=\"2.0\", FormatRisk=\"\", Extension=\"doc,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:33',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('2c3058ab-a357-4c26-9157-8f11028e63bb','PUID=\"fmt/215\", FormatName=\"Microsoft Powerpoint for Windows\", FormatVersion=\"2007 onwards\", FormatRisk=\"\", Extension=\"pptx,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:14',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('2f652af9-abd4-4048-ab58-0873a3912d95','PUID=\"fmt/353\", FormatName=\"Tagged Image File Format\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"tif,tiff,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:29',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('30ad24b9-3d4a-4cf8-9986-f45c12210aea','PUID=\"fmt/6\", FormatName=\"Waveform Audio\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"wav,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:19',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('3128ebc0-b3bc-4a96-b420-d64327e099ff','PUID=\"x-fmt/386\", FormatName=\"MPEG-2 Video Format\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"mpeg,mpg,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:14',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('315e3d93-22df-4993-955a-73d30a11f7d7','Targa image data - Mono - RLE 128 x 128',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:17:59',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('31868203-8848-4644-92c7-27307c5e5ba0','image/vnd.adobe.photoshop',0,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 18:27:00',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('31a79d37-ac13-4d6f-ba7d-49c9493959c9','PUID=\"fmt/152\", FormatName=\"Digital Negative Format (DNG)\", FormatVersion=\"1.1\", FormatRisk=\"\", Extension=\"dng,tif,tiff,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:20',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('32846451-1dbe-44c0-b599-cfd3bdc73b0e','application/pdf',0,1,'8e39a076-d359-4c60-b6f4-38f7ae6adcdf',NULL,'2012-10-15 21:30:49',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('33da3559-86b0-4db0-8e3f-7339155cc559','audio/x-aiff',0,0,'dc551ed2-9d2f-4c8a-acce-760fb740af48',NULL,'2012-10-16 17:55:59',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('34c42eda-b9e6-4a35-b483-a791bd04718d','Foveon X3F raw image data, version 2.1, 2268x1512',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:42',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('350b7514-7b5c-4b83-913f-b56122423dc5','text/rtf',1,0,'8e39a076-d359-4c60-b6f4-38f7ae6adcdf',NULL,'2012-10-15 21:30:50',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('35b9e3c0-9670-4775-9e9a-cf56d973440b','Windows Media Audio',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:37',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('35c9fd79-c9b4-4a0c-84dd-6a9a6a05453a','ARW EXIF',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:42',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('3651eff9-0c05-4077-8f26-a9dfb915465a','TIFF',1,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-16 18:18:17',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('369eeb06-b520-4566-b9c5-1f7a2277a450','bytestream',1,1,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-15 21:32:21',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('36e43181-0b2d-45d7-939f-491cdd04d7b3','PUID=\"x-fmt/129\", FormatName=\"Microsoft Word for Macintosh Document\", FormatVersion=\"X\", FormatRisk=\"\", Extension=\"doc,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:31',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('37384927-53f5-4ac7-8425-94a110c081ce','image/tiff',1,0,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:18:14',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('37b9babb-af84-4da8-9c2a-2c03087de2e4','application/vnd.oasis.opendocument.text',1,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:34',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('3840a1dc-df0f-4a41-a3df-6eb612491571','PDF document, version 1.6',0,1,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-11 23:10:30',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('38903006-89e2-4d59-b0d5-dbf8614500c4','PUID=\"fmt/18\", FormatName=\"Acrobat PDF 1.4 - Portable Document Format\", FormatVersion=\"1.4\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:25',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('38de9fcc-4200-462e-a4d3-70350fc6b7c9','PUID=\"x-fmt/391\", FormatName=\"Exchangeable Image File Format (Compressed)\", FormatVersion=\"2.2\", FormatRisk=\"\", Extension=\"jpg,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:15',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('3b8ba73b-9cbd-4a6c-b9ae-6fbd9c5f5104','ATSC A/52 aka AC-3 aka Dolby Digital stream, 48 kHz,, complete main (CM) 3 front/2 rear, LFE on,, 448 kbit/s not Dolby Surround encoded',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:48',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('3bc97a96-a507-4c9c-8033-d7f212a9badb','PUID=\"fmt/441\", FormatName=\"Windows Media Video 9 Advanced Profile (WVC1)\", FormatVersion=\"9 Advanced Profile (WVC1)\", FormatRisk=\"\", Extension=\"wmv,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:16',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('3cdd3f9e-a093-4c0d-8aef-ef7ba86f765b','image/gif',0,0,'dc551ed2-9d2f-4c8a-acce-760fb740af48',NULL,'2012-10-16 18:18:20',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('3da83571-5d77-4edb-9782-859138ac1ed3','application/vnd.ms-powerpoint',1,0,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:04:55',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('3dfac762-1463-4914-a5c3-00d751159b52','TIFF EXIF',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:47',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('3ffcbbac-7b49-433b-8ffa-275ff81aba00','TIFF image data, big-endian',1,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:17:59',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4021ed21-c101-42d4-83a8-3ff714eccb17','PUID=\"x-fmt/44\", FormatName=\"WordPerfect for MS-DOS/Windows Document\", FormatVersion=\"6.0\", FormatRisk=\"\", Extension=\"doc,wpd,wp6,wp,w60,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:14',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('40259ade-4e78-47c9-a8b8-d66e2aa121af','PUID=\"x-fmt/276\", FormatName=\"Microsoft Word for MS-DOS Document\", FormatVersion=\"5.5\", FormatRisk=\"\", Extension=\"doc,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:32',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('40597064-f02e-4bc6-9efa-b2d214925b73','A maildir email file',1,1,NULL,NULL,'2012-10-02 00:24:56',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('40e2177a-2219-445b-bd97-56f2ebacfe62','Portable Document Format',1,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:35',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('40f0c13e-f8ef-4a50-94cd-f1a2d95ca886','A .cr2 file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4133c4d3-979c-4ef0-80f1-13ebc7553ac0','Thumbnail normalization failed',0,0,NULL,NULL,'2012-10-02 00:24:56',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4137d40c-0130-4277-b726-61bc76f4fdef','application/msword',1,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 17:55:51',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('41f460dc-3272-497f-bed4-77e67d459612','application/pdf',0,1,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 17:55:52',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('420813eb-03e3-4a3a-97ca-84894f08af88','fmt/9',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-10-16 21:33:00',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('435bcdbe-75b2-44d2-94ef-2710edcf9eb3','PUID=\"fmt/91\", FormatName=\"Scalable Vector Graphics\", FormatVersion=\"1.0\", FormatRisk=\"\", Extension=\"svg,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:19',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('438ae824-987a-4641-9767-9291b26fbc1e','A .odg file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('44da7e78-68c9-41ca-8441-26f882a463c4','PUID=\"x-fmt/88\", FormatName=\"Microsoft Powerpoint Presentation\", FormatVersion=\"4.0\", FormatRisk=\"\", Extension=\"ppt,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:16',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('454214be-1ae6-4bdc-8396-a4355bdbc618','A .avi file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('45984389-977d-4064-8d46-c0df6e791880','image/jpeg',0,1,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-18 22:57:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4648baa2-24e0-4ebd-a31a-2417e4175ad3','PUID=\"fmt/138\", FormatName=\"OpenDocument Presentation\", FormatVersion=\"1.0\", FormatRisk=\"\", Extension=\"odp,otp,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:33',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4690e672-6b5f-42ac-9cf8-d0a3bafeeaa9','JPEG image data, JFIF standard 1.01',0,1,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:26:58',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('473e1574-954b-4b54-a641-8dda3659ddc1','PUID=\"fmt/179\", FormatName=\"Microsoft PowerPoint for Macintosh\", FormatVersion=\"4.0\", FormatRisk=\"\", Extension=\"ppt,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:16',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('477b16c1-28b7-4685-9391-51bbaca936b7','text/plain',1,1,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 18:27:01',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('47cd293b-5789-4084-8e9b-6d0404e15a77','PUID=\"fmt/420\", FormatName=\"Adobe Illustrator\", FormatVersion=\"5.0 / 5.5\", FormatRisk=\"\", Extension=\"ai,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:30',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('480b7fb6-f10b-4631-97f4-f40e0385cdca','Quicktime',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:39',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4818b5a3-6975-483a-b2a2-7be4de3fbc31','unrar-nonfreeCompatable',0,0,NULL,NULL,'2012-10-02 00:24:56',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('48ddd2b7-7841-4731-a28b-5ebe84a0ff8b','image/gif',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:42',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4953b2ca-d1f2-4905-a8d4-bf95abd54930','application/octet-stream',1,0,'dc551ed2-9d2f-4c8a-acce-760fb740af48',NULL,'2012-10-16 17:55:58',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4988d669-fe70-404c-8e0b-c297d2eb9ad2','PUID=\"fmt/423\", FormatName=\"Adobe Illustrator\", FormatVersion=\"7.0\", FormatRisk=\"\", Extension=\"ai,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:30',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('49f8cfb5-afac-44ca-8d47-b408881954e0','PDF document, version 1.4',0,1,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-11 23:10:30',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4aa31d07-d726-499f-b3ec-afe2995abedb','A .mp3 file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4ac74345-b1d9-47d8-bed7-3f41b2bdbc36','A .wav file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4ae6d5e4-1cd1-481e-8469-eae98f9adb36','PUID=\"x-fmt/367\", FormatName=\"Truevision TGA Bitmap\", FormatVersion=\"1.0\", FormatRisk=\"\", Extension=\"tga,icb,vda,vst,afi,bpx,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:15',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4bbcd493-2035-4567-80e8-218dd24201e7','Macintosh PICT Image',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:48',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4bc4b72c-8183-4a14-8066-47d7b0d2a827','fmt/10',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-10-16 21:33:00',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4bcc5156-f3c7-4a5f-a99d-006eb61c4ba1','(Corel/WP)',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:30',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4c451f8c-9af3-468a-ae57-f4ce393eba80','HTML',1,1,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-16 18:18:17',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4c9c49f4-ca45-40bc-b7a2-119bfa76cf23','A .kdc file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4cd6ff3b-2508-47ac-9d5b-2fcdb61ab1cd','PUID=\"fmt/421\", FormatName=\"Adobe Illustrator\", FormatVersion=\"Japan\", FormatRisk=\"\", Extension=\"ai,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:31',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4d2748e9-7233-4106-b7a1-83e7cfdbff24','image/vnd.adobe.photoshop',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:39',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4d399247-7598-403d-bb17-8d9230bacb0d','image/jp2',1,0,'dc551ed2-9d2f-4c8a-acce-760fb740af48',NULL,'2012-10-16 18:18:19',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4d82500c-bfb5-4ddd-bb9e-40733cbe72be','A .orf file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4e5c2d3d-0802-432b-9e52-2a98e8e18e8d','image/x-olympus-orf',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:39',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4e6f9641-ba13-4855-9385-be92209a245e','PUID=\"x-fmt/385\", FormatName=\"MPEG-1 Video Format\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"mpeg,mpg,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:14',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4f31deac-0130-4dc8-9a6b-b2ac3bbc343f','PUID=\"fmt/417\", FormatName=\"Adobe Illustrator\", FormatVersion=\"88\", FormatRisk=\"\", Extension=\"ai,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:30',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4f982c49-6962-463b-ae20-1b5494fc7671','image/x-canon-crw',0,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 18:27:03',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('4fb85f9a-831d-49ff-a554-4c80a792232b','Olympus ORF raw image data, little-endian',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:38',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('51f80b0a-7a4b-4c68-ac7a-b41ef9c9aaba','application/vnd.ms-asf',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:36',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('52b25353-f40f-453c-bb26-854d2cf29b7f','PUID=\"fmt/133\", FormatName=\"Windows Media Video\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"asf,wmv,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:16',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('53b462db-42b5-41e6-94fa-a98974bcfd5b','Quicktime video',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:39',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('53bff439-5ff6-4fe9-a87a-8745da64b5e6','Microsoft Office Document',1,1,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-11 23:10:30',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('54762702-b427-4b75-95a3-faa17a90d1b5','PUID=\"x-fmt/270\", FormatName=\"OS/2 Bitmap\", FormatVersion=\"2.0\", FormatRisk=\"\", Extension=\"bmp,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:22',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('54e14752-4a89-413a-9ac1-dbb519f61e2a','PUID=\"fmt/40\", FormatName=\"Microsoft Word Document\", FormatVersion=\"97-2003\", FormatRisk=\"\", Extension=\"doc,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:31',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('573c6a20-1076-4234-a728-0bce8405f097','Advanced Systems Format',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:34',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('574474c0-6023-4a99-ac26-301495d79194','Do not include in archivematica DIP',0,1,NULL,NULL,'2012-10-02 00:24:56',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('57eeece7-6b2a-4dad-9684-b05c96a57988','GIF',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-16 18:18:18',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('580f3cbb-7604-4775-8776-2dcfc5651139','PUID=\"fmt/39\", FormatName=\"Microsoft Word Document\", FormatVersion=\"6.0/95\", FormatRisk=\"\", Extension=\"doc,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:31',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('58a160ea-d3a2-4bef-8238-ae36f4e9a013','Microsoft Word',1,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:34',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('58e8909c-4c51-489e-bae0-410d82191d6c','A .xlsx file',1,1,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('59160936-d126-49a2-a275-86f9359c0e89','video/x-ms-asf',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:33',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('599cb14f-dbc5-4f45-8e94-24fdb881fbbf','NEF EXIF',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:41',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('5abb795d-02e4-4d63-87cb-61fbc7b70be8','Scalable Vector Graphics (SVG)',1,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:36',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('5b3a71d1-5a3b-46a4-a558-a785a10ca262','PUID=\"x-fmt/392\", FormatName=\"JP2 (JPEG 2000 part 1)\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"jp2,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:29',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('5b553136-e75f-4512-b988-689db706fd50','PUID=\"fmt/422\", FormatName=\"Adobe Illustrator\", FormatVersion=\"6.0\", FormatRisk=\"\", Extension=\"ai,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:30',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('5c4a8eeb-3536-4b43-8eb7-33ee6472c63c','MOV',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:39',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('5c998398-3340-4d36-8b34-2cbe7eca1010','PUID=\"fmt/4\", FormatName=\"Graphics Interchange Format\", FormatVersion=\"1989a\", FormatRisk=\"\", Extension=\"gif,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:17',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('5cad3529-600c-4b7f-aa68-375a5dc9912e','PUID=\"fmt/92\", FormatName=\"Scalable Vector Graphics\", FormatVersion=\"1.1\", FormatRisk=\"\", Extension=\"svg,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:19',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('5cb9e13e-9abe-4258-9c94-f79c6136f577','image/x-x3f',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:41',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('5dd411c8-9ce3-4293-9862-11d526b32725','application/vnd.ms-excel',1,1,'8e39a076-d359-4c60-b6f4-38f7ae6adcdf',NULL,'2012-10-15 21:30:49',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('5df9d93b-5e4d-41a3-89b5-301bf009ee9a','CR2 EXIF',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:47',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('5e7fbaac-1441-46e6-aedf-c65763e9fbf1','ERF EXIF',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:46',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('5ebc8427-124d-4615-83fb-4326749745e3','Microsoft ASF',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-11 23:10:29',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('5f43abcb-ddeb-439a-a80f-fb62dd2f042e','A .flv file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('5f46c34d-7a26-47eb-b977-1be976205050','audio/x-wave',1,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:40',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('5fc9e48c-f29d-4a98-b381-a8cccafc02af','PUID=\"x-fmt/20\", FormatName=\"Adobe Illustrator\", FormatVersion=\"1.0 / 1.1\", FormatRisk=\"\", Extension=\"ai,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:29',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('5ff09d4f-217a-4610-8495-7089859d1452','image/svg+xml',1,0,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:18:14',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('61137b8e-803f-4f04-835c-1718670cebc1','PUID=\"fmt/14\", FormatName=\"Acrobat PDF 1.0 - Portable Document Format\", FormatVersion=\"1.0\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:24',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('61629eaa-2dd4-47d9-9f7b-3ff94db4482a','PUID=\"x-fmt/274\", FormatName=\"Microsoft Word for MS-DOS Document\", FormatVersion=\"4.0\", FormatRisk=\"\", Extension=\"doc,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:32',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('61d45a28-5271-48e8-810d-c83b4bf6b24f','image/x-canon-crw',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:43',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('62ad9422-842f-437e-b388-620abbbfe876','Encapsulated PostScript File Format',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:37',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('62be2403-d1ee-4d70-bab4-28bfd161f8e0','application/octet-stream',0,0,'8e39a076-d359-4c60-b6f4-38f7ae6adcdf',NULL,'2012-10-15 21:30:49',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('632a5374-bc44-4d75-a9f6-e53edb87e316','image/tiff',1,0,'8e39a076-d359-4c60-b6f4-38f7ae6adcdf',NULL,'2012-10-16 18:18:03',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('65b74b06-6bda-433b-b993-f09810cdcbca','audio/x-aiff',0,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 17:55:51',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('66120b2d-0c89-4304-815b-6c8cec94647b','image/jpeg',0,1,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 18:27:01',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('66be432b-20a6-4dbc-8d52-5857f559c40d','PUID=\"fmt/202\", FormatName=\"Nikon Digital SLR Camera Raw Image File\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"nef,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:20',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('66ff18ac-f825-49be-8b4f-d48ead99278a','Microsoft Word 2007+',1,1,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-11 23:10:31',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('675e8fdb-67e9-41a8-a505-823cd8348cb9','text/plain',1,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:34',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('67621d17-25ea-4cef-863d-1431b3946640','A .jp2 file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('68d7dce7-17c4-4407-b648-83efa8870d3e','Foveon X3F raw image data, version 2.1, 2268x1512',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:26:58',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('6a609c8f-29e9-4c04-9586-6d395def0275','Microsoft Excel 2007+',1,1,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:41',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('6a7c543e-1d68-4ab1-8ab0-4898d0d204e4','application/pdf',0,1,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:04:54',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('6a939182-d012-48ce-b495-5648eaf06e1c','PUID=\"fmt/37\", FormatName=\"Microsoft Word for Windows Document\", FormatVersion=\"1.0\", FormatRisk=\"\", Extension=\"doc,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:32',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('6ba6316f-7372-4dfc-a573-9db78af2b3ed','Access Defaults',0,0,NULL,NULL,'2012-10-02 00:24:56',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('6c1064bb-f40f-4413-84e5-d40270f6c96b','application/rtf, text/rtf',1,0,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:04:54',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('6ce73de0-e573-4f2f-9a54-28c440210c2c','fmt/8',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-10-16 21:32:59',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('6e0f164c-6f87-4f2a-9ebe-23089c33cc46','PUID=\"fmt/41\", FormatName=\"Raw JPEG Stream\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"jpe,jpg,jpeg,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:15',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('6f4e7eff-7204-468e-bfb2-2943688d282f','Macromedia Flash Video',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:17:59',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('705e9509-6393-481f-8dd5-f4753abcfead','ATSC A/52 aka AC-3 aka Dolby Digital stream, 48 kHz,, complete main (CM) 3 front/2 rear, LFE on,, 448 kbit/s reserved Dolby Surround mode',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:26:57',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('736176b4-73b4-48fa-803c-0846a5f0760e','x-fmt/111',1,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-10-16 21:33:04',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('73d4a167-f8c1-4466-8f1e-10ce9ab0851f','RIFF (little-endian) data, WAVE audio, Microsoft PCM, 16 bit, mono 44100 Hz',1,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-11 23:10:30',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('73e92bb3-23a0-4fc7-a7e6-4da8b0984506','Flash Video (FLV)',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:34',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('74455ff3-647d-4d29-a71a-be28fa97a289','image/png',1,0,'8e39a076-d359-4c60-b6f4-38f7ae6adcdf',NULL,'2012-10-16 18:18:04',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('74fa9f99-573c-4c76-964b-4456ac234715','PUID=\"x-fmt/275\", FormatName=\"Microsoft Word for MS-DOS Document\", FormatVersion=\"5.0\", FormatRisk=\"\", Extension=\"doc,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:32',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('78640195-eaae-405f-b3bc-304b806977f9','JPEG 2000 JP2',1,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:33',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('78b2cb5f-7b0f-450e-95fe-dd2e8de13770','PUID=\"fmt/144\", FormatName=\"Acrobat PDF/X - Portable Document Format - Exchange 1:1999\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:27',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('79103108-9ea9-4ed0-a777-37af444867b6','DOS EPS Binary File Postscript starts at byte 32 length 159320 TIFF starts at byte 159352 length 881762',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:26:57',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('799d35d1-0125-4d6b-8e92-df7cad508426','PC bitmap, Windows 3.x format, 799 x 599 x 24',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:26:58',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('7a0ab3c1-8ab0-4aac-b064-bff7e6e88f64','PUID=\"fmt/115\", FormatName=\"Windows Bitmap\", FormatVersion=\"2.0\", FormatRisk=\"\", Extension=\"bmp,dib,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:22',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('7a171a9b-49d8-45c7-bb5f-52d9c3157de0','A .giff file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('7a8f358d-9b31-4d29-b258-7d668db64631','PUID=\"x-fmt/389\", FormatName=\"Exchangeable Image File Format (Audio)\", FormatVersion=\"2.1\", FormatRisk=\"\", Extension=\"wav,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:18',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('7abfb7c5-3f55-466e-bd43-1f886e6a884c','PUID=\"fmt/192\", FormatName=\"Kodak Digital Camera Raw Image File\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"dcr,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('7b2ab804-b73e-4700-96ca-61ebc9c3576e','Tagged Image File Format',1,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:35',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('7c5095aa-a9c0-46d7-a67d-e95d628edda8','image/png',1,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 18:18:02',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('7d02f6c1-eab0-418a-a633-fba5abce7f8c','PUID=\"fmt/136\", FormatName=\"OpenDocument Text\", FormatVersion=\"1.0\", FormatRisk=\"\", Extension=\"odt,ott,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:17',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('7efc3875-3bd2-4615-9b1f-83d7fc8131d0','PUID=\"x-fmt/273\", FormatName=\"Microsoft Word for MS-DOS Document\", FormatVersion=\"3.0\", FormatRisk=\"\", Extension=\"doc,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:32',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('7ff7e6cf-5a65-4e2f-9fc3-6f702374b470','A .wbk file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('805785fd-4001-4ef7-9d10-1ef52fcbc9c0','Microsoft Office Document Microsoft Word Document',1,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-11 23:10:31',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('808520c8-c588-40a0-9c32-8518110c0e33','XML',1,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-16 18:18:17',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('82bc2ee5-86e9-4bcb-bb65-90fb1b656754','fmt/96',1,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-10-16 21:32:59',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('82f879a7-fdda-4b3f-a79f-55c1605a85ce','image/jpeg',0,1,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:41',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('8309414c-b081-4342-b20b-eea126a97e26','application/vnd.ms-powerpoint',1,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 17:55:50',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('833952ec-7936-4a3e-979a-a2b705c31a9d','PUID=\"x-fmt/393\", FormatName=\"WordPerfect for MS-DOS Document\", FormatVersion=\"5.0\", FormatRisk=\"\", Extension=\"wp,wp5,wpd,w50,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:14',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('833cc7c2-c155-45b6-b927-98fdb20ae86f','video/quicktime',0,0,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:04:54',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('83e32dd9-e3ac-4953-8f8e-756ac56d4480','PUID=\"fmt/109\", FormatName=\"Macromedia Flash\", FormatVersion=\"6\", FormatRisk=\"\", Extension=\"swf,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:35',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('8479526a-fdab-44af-97de-920bea73237e','image/png',1,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:38',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('84bcc45a-f840-4645-9ae3-1f480ab74589','audio/x-aiff',0,0,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:04:54',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('84c9fb23-9a6c-4fad-832e-0cc3de838d70','video/x-flv',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:36',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('84e0b908-bd9e-4442-ae34-bc9743b43348','text/html',1,1,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:18:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('8533521f-82c8-408f-9499-194e45d50154','image/x-canon-cr2',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:42',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('860d9543-4d04-4be9-9fed-ff1e35648d28','PUID=\"x-fmt/382\", FormatName=\"Macromedia FLV\", FormatVersion=\"1\", FormatRisk=\"\", Extension=\"flv,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:31',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('8612b1de-9825-4cb9-b9bb-0b3b97bbe6b6','PUID=\"fmt/141\", FormatName=\"Waveform Audio (PCMWAVEFORMAT)\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"wav,wav,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:19',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('86ce68f3-b264-4e49-a44e-c58144703700','audio/mpeg',0,0,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:04:55',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('875a88e8-3fd0-48e9-a184-5f62459a468f','PUID=\"fmt/180\", FormatName=\"Microsoft PowerPoint for Macintosh\", FormatVersion=\"98\", FormatRisk=\"\", Extension=\"ppt,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:16',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('881214c5-642f-4a87-b4a5-3c8b6b8436d2','A .mp4 file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('88bb6c02-f537-4879-89dc-8e5e7e257156','Plain text',1,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:31',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('88f61e4e-ce58-4bc3-9bc8-91d6c3cfcabe','PUID=\"fmt/116\", FormatName=\"Windows Bitmap\", FormatVersion=\"3.0\", FormatRisk=\"\", Extension=\"dib,bmp,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:23',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('8a0b7119-633e-4f74-a499-8b5535158860','image/x-olympus-orf',0,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 18:27:00',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('8a0cc11d-d1ec-4e0d-a895-ac037c9d94f8','A .pef file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('8aa97c5e-1cf5-4b1b-a20f-58e21598a5fd','RIFF (little-endian) data, WAVE audio, Microsoft PCM, 16 bit, stereo 44100 Hz',1,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:26:59',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('8c334402-efc2-4585-958c-540cedf32936','PUID=\"fmt/114\", FormatName=\"Windows Bitmap\", FormatVersion=\"1.0\", FormatRisk=\"\", Extension=\"ddb,bmp,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:23',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('8cacccb8-d2d5-46d4-a3f5-805ae1fc68cd','PUID=\"fmt/119\", FormatName=\"Windows Bitmap\", FormatVersion=\"5.0\", FormatRisk=\"\", Extension=\"bmp,dib,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:23',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('8d6f16a1-d41d-4f6b-9da6-fa06ce5ebe54','PUID=\"fmt/2\", FormatName=\"Broadcast WAVE\", FormatVersion=\"1\", FormatRisk=\"\", Extension=\"wav,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:18',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('8d8ad28c-4bf2-4258-a7f1-d2c04df7b125','JPEG 2000 image data',1,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:17:58',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('8ef30615-ebf0-4877-a521-64457b598158','A .archivematicaMaildir file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('8f063f36-f64c-479e-b37a-2491e99feb6a','image/bmp',0,0,'8e39a076-d359-4c60-b6f4-38f7ae6adcdf',NULL,'2012-10-16 18:18:05',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('8f34c0fe-0d12-4dda-bef0-fb952990cb71','Plain text',1,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-11 23:10:29',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('9086cec8-a28e-4723-9f31-e2e68e173c6a','A .dng file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('9118731f-b1bb-46ed-9d6c-1d070a5e7bf6','Targa image data - Mono - RLE 128 x 128',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:36',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('92526fe4-93e8-48c0-ab29-be50bc832bf5','A .aiff file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('92ba8d67-0170-4f39-8f29-316b44522112','A .mov file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('92ec6955-b6ca-4acb-b8d4-a475efcd8c73','PUID=\"fmt/16\", FormatName=\"Acrobat PDF 1.2 - Portable Document Format\", FormatVersion=\"1.2\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:24',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('93020f07-2a16-450e-b0fc-e03446952c02','PUID=\"fmt/20\", FormatName=\"Acrobat PDF 1.6 - Portable Document Format\", FormatVersion=\"1.6\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:25',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('931ba3fb-e82b-467a-a349-0320feac9a97','application/vnd.ms-asf',0,0,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:04:54',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('937e5e30-e054-4e65-972e-9c5b4b4f8f5f','PUID=\"fmt/17\", FormatName=\"Acrobat PDF 1.3 - Portable Document Format\", FormatVersion=\"1.3\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:25',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('9548e247-c91d-4e39-99b1-26456cc7ce22','PUID=\"fmt/105\", FormatName=\"Macromedia Flash\", FormatVersion=\"2\", FormatRisk=\"\", Extension=\"swf,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:34',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('95d261ff-13dd-4c42-8c83-f69a7b1cd6ba','image/tiff',1,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:37',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('9621ea57-5c9c-48dd-8a7f-1a7dc3f7e107','PUID=\"fmt/118\", FormatName=\"Windows Bitmap\", FormatVersion=\"4.0\", FormatRisk=\"\", Extension=\"bmp,dib,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:22',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('976dc56c-c92a-4253-ac88-f69cae251794','image/gif',0,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 18:27:01',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('9796ae62-3ee9-445c-a7d6-867fa5fae36d','A .mpeg file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('97f8b89c-5aed-4c41-97ec-72516fe0df51','PUID=\"fmt/292\", FormatName=\"OpenDocument Presentation\", FormatVersion=\"1.1\", FormatRisk=\"\", Extension=\"odp,otp,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:33',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('983398c1-423c-4e88-99be-da3ad56d2d7c','AIFF',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-15 21:32:21',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('983636ce-8bfb-4e06-b5f5-a2994af8ccc9','ORF EXIF',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:38',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('9862f025-b370-4ed0-96a1-0d81813ac256','A .raf file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('99818e34-6fe4-455c-81df-cca3b0e70932','Canon CR2 raw image data, version 2.0',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:47',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('99c1ab3a-dc50-4186-8828-df989f8d3d50','A .pptx file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('9a8285cc-f383-4a1b-bf64-60749f065603','fmt/7',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-10-16 21:32:59',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('9c1ef1bf-7ac0-431e-bfaa-0490b199d2c3','A .tga file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('9d14b564-3571-4a71-81a0-9b937c61df41','PUID=\"x-fmt/398\", FormatName=\"Exchangeable Image File Format (Compressed)\", FormatVersion=\"2.0\", FormatRisk=\"\", Extension=\"jpg,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:15',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('9e447d46-e845-4abb-b562-e73b3a716ab4','audio/x-aiff',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:35',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('9e55df2f-6153-4d2b-a156-863b1c0148e2','video/quicktime',0,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 17:55:52',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('9f11626e-4664-49d6-9604-5c2ee9f4f299','A .x3f file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('9f66ca0d-34eb-4820-9679-0704fecb73f6','A .wpd file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('9f97ee78-98ae-4a3e-9989-50ca598fee65','video/x-flv',0,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 18:18:01',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('9ff3de86-38cd-4c4a-816d-048f95ec4f51','A .mkv file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a010a604-78c2-4656-846b-d265b95ae771','PUID=\"fmt/191\", FormatName=\"Sony RAW Image File\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"arw,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a0af674f-4e72-496b-bf4e-4f126eab7372','AIFF',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:32',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a14579f5-81f1-4d0e-b1f2-a63c7c0c94af','PUID=\"fmt/478\", FormatName=\"Acrobat PDF/A - Portable Document Format\", FormatVersion=\"2u\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:26',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a1bd442c-44f7-431a-9eb3-cfb962872c31','Free Lossless Audio Codec',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:37',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a1e102fe-de95-4fc5-9bff-a1cc7058e1cd','A .wmv file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a1f09e48-b4d7-426f-b186-b7bdd258f0f3','Microsoft Excel Format',1,1,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:42',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a269e764-da36-445a-b6a4-e4cc3fac4f88','PUID=\"fmt/481\", FormatName=\"Acrobat PDF/A - Portable Document Format\", FormatVersion=\"3u\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:27',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a30af6e0-195d-46a8-a46a-cc1682d04896','image/tiff',1,0,'dc551ed2-9d2f-4c8a-acce-760fb740af48',NULL,'2012-10-16 18:18:19',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a359b55f-70af-43c8-a6d1-42f0801c0155','PUID=\"fmt/293\", FormatName=\"OpenDocument Presentation\", FormatVersion=\"1.2\", FormatRisk=\"\", Extension=\"odp,otp,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:33',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a3698ee9-8770-41d4-b4a2-c8cbafb972bc','PUID=\"fmt/42\", FormatName=\"JPEG File Interchange Format\", FormatVersion=\"1.00\", FormatRisk=\"\", Extension=\"jpeg,jpe,jpg,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:15',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a3797587-2fde-418a-a73c-62541ca30275','audio/x-wave',1,0,'dc551ed2-9d2f-4c8a-acce-760fb740af48',NULL,'2012-10-16 17:55:59',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a38ef10a-1dab-4643-b93c-dec589951c70','PUID=\"fmt/158\", FormatName=\"Acrobat PDF/X - Portable Document Format - Exchange 3:2002\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:28',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a3f41b86-152e-4feb-878a-c83d486b57cf','ASCII',1,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-15 21:32:22',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a487dde7-8bc6-475c-9b7c-35cd163c5845','fmt/46',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-10-16 21:32:57',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a5b94581-b1b5-40c4-975c-0ea11870c237','IFF data, AIFF audio',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-11 23:10:30',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a5cdb910-3eea-4f59-9319-d2c51b6658fd','PUID=\"fmt/110\", FormatName=\"Macromedia Flash\", FormatVersion=\"7\", FormatRisk=\"\", Extension=\"swf,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:35',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a6a4884a-f8e5-4909-905b-df8825040cef','image/x-x3f',0,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 18:27:01',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a86b0683-25af-4e39-9aa3-e0e4a1e7b395','FLAC audio bitstream data, 24 bit, 6 channels, 96 kHz, 28369692 samples',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:26:56',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a973a41b-d90a-4081-b0ac-6eeef52d2893','text/xml',1,0,'dc551ed2-9d2f-4c8a-acce-760fb740af48',NULL,'2012-10-16 18:18:19',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('a974079d-0865-49ef-afd8-0ec91174d323','PUID=\"x-fmt/394\", FormatName=\"WordPerfect for MS-DOS/Windows Document\", FormatVersion=\"5.1\", FormatRisk=\"\", Extension=\"wp5,wpd,w51,wp,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:14',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('aa3515c1-bd03-4555-9e98-723ca14f74a9','A .eps file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('aa9e07d4-53ce-45c0-b72f-dfbbcb40829f','A .mpg file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('aaf1a292-b2e7-4624-8119-a6805bc6702c','A .jpg file',0,1,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('aaf970b7-31ef-40f0-a493-187298baa1b1','PUID=\"x-fmt/384\", FormatName=\"Quicktime\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"mov,qtm,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('abc707f1-48f6-4e0c-acba-67c818ccdc85','PUID=\"fmt/148\", FormatName=\"Acrobat PDF/X - Portable Document Format - Exchange 3:2003\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:28',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('adac68c6-1483-430c-ab2e-b594660ae4d6','Audio file with ID3 version 2.3.0, contains: MPEG ADTS, layer III, v1, 128 kbps, 44.1 kHz, JntStereo',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-11 23:10:29',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('adfa26bc-e276-4d69-85f3-63c8bd046d3f','A .bmp file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('aed7e281-17ad-4834-aa9e-96fdc19ea2f1','Hypertext Markup Language',1,1,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:34',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('af5504e8-0bdb-4e97-8dea-08892fb928ca','A .odp file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('afea3d12-379b-47ef-a395-0e162e37d90b','image/gif',0,0,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:18:16',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b0bfcd0e-3194-4292-b78a-cea6a356a4d8','A .mboxi file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b155a460-4722-4dcf-81fa-3a4c65557cef','fmt/111',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-10-16 21:32:58',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b155afa2-038e-42dc-beee-f89e5a3ce41f','A .tif file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b339460f-c9b6-43a2-a78d-9d8aaea9c8f2','PUID=\"fmt/157\", FormatName=\"Acrobat PDF/X - Portable Document Format - Exchange 1a:2001\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:27',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b3df6777-cc46-4d03-8766-9c1eb0893ee9','A .xml file',1,1,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b43d2871-487f-4d31-aaa1-e9bea8dd86fa','PUID=\"fmt/355\", FormatName=\"Rich Text Format\", FormatVersion=\"1.9\", FormatRisk=\"\", Extension=\"rtf,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:29',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b47ed36b-9e71-4516-ba3a-cb4c91718d8c','JPEG 2000',1,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-16 18:18:17',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b50f44c8-1d3d-4d0b-b17d-9ab0005ee224','Canon CR2 raw image data, version 2.0',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:26:59',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b5d57def-9d34-4851-89f9-62b4646ad605','ATSC A/52 aka AC-3 aka Dolby Digital stream, 48 kHz,, complete main (CM) 3 front/2 rear, LFE on,, 448 kbit/s not Dolby Surround encoded',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:26:59',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b60d9ce6-7f94-4c2d-b1b0-04d74644b48b','A .ods file',1,1,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b63fd011-1df4-4478-86df-da93f3fc1955','PUID=\"fmt/112\", FormatName=\"Still Picture Interchange File Format\", FormatVersion=\"1.0\", FormatRisk=\"\", Extension=\"spf,jpg,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:15',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b7463782-e37e-447e-90cd-2703deeb21e2','text/html',1,1,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 18:18:01',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b7996d48-fbd6-4f41-9b0e-30ab67feb511','text/rtf',1,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:35',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b7b89065-c83b-48f0-bf23-136c6c092dfd','application/vnd.ms-excel',1,1,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:40',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b7c6ba3f-b139-4451-b0ce-2e534a088d56','PUID=\"fmt/44\", FormatName=\"JPEG File Interchange Format\", FormatVersion=\"1.02\", FormatRisk=\"\", Extension=\"jpg,jpe,jpeg,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:15',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b83c3a3d-7feb-4677-b38c-1ecd5118c3f4','Microsoft Powerpoint Presentation',1,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:40',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b8668b4d-abbf-4a4e-8646-0640f2baa787','PDF document, version 1.5',1,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:17:59',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b8863dbf-f999-4c0f-889f-ccc18b6a6e71','video/x-ms-asf',0,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 17:55:51',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b89940da-f3c3-4dad-9eb6-86bd2ba380ab','A .ai file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b8b6dfa3-beca-4667-a279-cf78d610a0b6','video/x-flv',0,0,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:18:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('b95d9944-b9f3-4dba-bd83-86c6fe3a9ca0','PUID=\"fmt/108\", FormatName=\"Macromedia Flash\", FormatVersion=\"5\", FormatRisk=\"\", Extension=\"swf,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:34',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('badbed99-3113-45e7-bf87-40bf5354f88d','A .wma file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('baecde96-ebee-426c-bc77-3ec8dc653261','A .jpeg file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('bbfbbed7-11fa-4cb1-9819-89284ff7d80a','text/rtf',1,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 17:55:51',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('bc0a4930-5a62-4e38-92ae-c7b0b024fecd','A .log file',1,1,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('bc6c161a-3f61-4890-9492-90ad25567b6b','PUID=\"fmt/290\", FormatName=\"OpenDocument Text\", FormatVersion=\"1.1\", FormatRisk=\"\", Extension=\"odt,ott,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:17',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('bc9e29c0-cf88-497c-a9da-76956e4dc08e','PUID=\"fmt/199\", FormatName=\"MPEG-4 Media File\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"mp4,m4v,m4a,f4v,f4a,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:17',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('bc9e9d1b-9112-4336-ac8e-c2c0b614777b','A .tiff file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('bd3002cb-5404-4590-9481-c62d5d638bf6','fmt/189',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-10-16 21:32:56',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('bd4305e4-73dc-41e8-a63c-11dabe86bad4','text/html',1,1,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:36',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('bdb7edf8-255d-4fe5-a457-50e93fad0211','PUID=\"fmt/95\", FormatName=\"Acrobat PDF/A - Portable Document Format\", FormatVersion=\"1a\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:25',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('bfefbf36-c506-403d-896f-3a2eac356e0c','audio/x-wav',1,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 17:55:52',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('bff92855-162c-4d4e-abb3-0db3388cf7ae','A .mbox file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('c02e25ee-16f0-4020-80f7-8c378c398dc4','PUID=\"fmt/122\", FormatName=\"Encapsulated PostScript File Format\", FormatVersion=\"1.2\", FormatRisk=\"\", Extension=\"eps,epsf,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:21',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('c0540086-8d61-4684-b8a8-ed11594c5abf','application/rtf, text/rtf',1,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:35',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('c0971b7e-06eb-4d4b-856b-5ffb7d2843d6','image/jp2',1,0,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:18:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('c219f204-5418-4f45-b148-8216d2a15e88','A .dcr file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('c24abbeb-3318-4ec0-ab40-f272a26800ad','PUID=\"x-fmt/2\", FormatName=\"Microsoft Word for Macintosh Document\", FormatVersion=\"6.0\", FormatRisk=\"\", Extension=\"doc,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:31',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('c285590a-914f-4c36-a0a2-624fa25d028d','Canon CIFF raw image data, version 1.2',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:48',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('c458b319-6779-4426-a1a1-b01c0cb46194','JPEG',0,1,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-16 18:18:18',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('c57451a7-13f9-4483-94af-5290f7a2bd87','application/vnd.ms-excel',1,1,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 17:55:53',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('c5e493af-195e-4b64-9331-4f716a16eac9','A .pst file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('c687dcf9-2069-4027-8c00-98f4b66fc2e2','A .rtf file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('c789e8b6-9d94-47f4-9be5-5a118cd13c0d','PUID=\"fmt/182\", FormatName=\"Microsoft PowerPoint for Macintosh\", FormatVersion=\"v.X\", FormatRisk=\"\", Extension=\"ppt,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:16',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('c79e680d-ce6f-44bb-aa6f-91ac2e94dedb','PUID=\"fmt/43\", FormatName=\"JPEG File Interchange Format\", FormatVersion=\"1.01\", FormatRisk=\"\", Extension=\"jpg,jpe,jpeg,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:15',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('c82bc722-a02c-42d5-b894-e45baa4badb2','application/msword',1,0,'8e39a076-d359-4c60-b6f4-38f7ae6adcdf',NULL,'2012-10-15 21:30:49',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('c8e9d29a-13ca-4f03-9d2c-5834667f54ea','(Corel/WP)',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:17:58',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('c90cae9f-04e4-48d5-8c7f-ddaed3bc64ac','PUID=\"fmt/414\", FormatName=\"Audio Interchange File Format\", FormatVersion=\"1.2\", FormatRisk=\"\", Extension=\"aif,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:33',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('c9d79a74-a58f-4c71-8977-1129c10f3a5d','application/zip',1,1,'8e39a076-d359-4c60-b6f4-38f7ae6adcdf',NULL,'2012-10-15 21:30:49',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('cabd97ec-12fc-442a-9952-54a9250c6e8c','image/bmp',0,0,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:18:16',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('cb2d39d3-9f00-41c1-b0ef-99d5c57a900c','ATSC A/52 aka AC-3 aka Dolby Digital stream, 48 kHz,, complete main (CM) 3 front/2 rear, LFE on,, 448 kbit/s reserved Dolby Surround mode',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:40',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('cba89043-1ac9-4e3c-a140-2ac74377c3b0','A .mrw file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('cba8d14c-a8a8-4676-b1d2-3cd726528825','PUID=\"fmt/13\", FormatName=\"Portable Network Graphics\", FormatVersion=\"1.2\", FormatRisk=\"\", Extension=\"png,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:21',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('cc23bcb7-221b-4ef0-98ab-40726b5aee4b','PUID=\"fmt/5\", FormatName=\"Audio/Video Interleaved Format\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"avi,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:29',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('ce23cf3b-6ee1-4392-9d11-e088cde97864','A .swf file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('ce25ed99-6365-4cc9-a8f7-1b0f8428d271','Access normalization failed',0,0,NULL,NULL,'2012-10-02 00:24:56',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('d189342e-a551-4f77-a45c-6f3482e3dce3','Canon CIFF raw image data, version 1.2',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:26:59',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('d2941b3f-38e2-491d-947b-f6e07802595c','image/gif',0,0,'8e39a076-d359-4c60-b6f4-38f7ae6adcdf',NULL,'2012-10-16 18:18:06',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('d3e84d02-ec80-459d-bba2-2517d9732c3b','PUID=\"fmt/341\", FormatName=\"Macintosh PICT Image\", FormatVersion=\"2.0\", FormatRisk=\"\", Extension=\"pct,pict,pic,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:17',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('d407dc6e-52ae-4fec-a1f6-9708d87af938','application/octet-stream',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:34',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('d4e1477a-ed22-48a3-b1f0-751e5bfa6b5a','PUID=\"fmt/12\", FormatName=\"Portable Network Graphics\", FormatVersion=\"1.1\", FormatRisk=\"\", Extension=\"png,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:21',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('d4e6b67d-331c-428b-964f-8303ca7eafe5','text/plain',1,1,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:18:16',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('d5719ffd-c663-44dd-946e-0818c31629a6','PUID=\"fmt/126\", FormatName=\"Microsoft Powerpoint Presentation\", FormatVersion=\"97-2002\", FormatRisk=\"\", Extension=\"ppt,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:17',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('d63f2b5e-736c-4b85-ba35-ec6cc58be2a2','PUID=\"fmt/477\", FormatName=\"Acrobat PDF/A - Portable Document Format\", FormatVersion=\"2b\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:26',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('d6d087d6-ace3-49ad-882d-67267cd29721','PUID=\"fmt/132\", FormatName=\"Windows Media Audio\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"wma,asf,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:28',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('d72473f6-d239-448b-acfc-c3f177f65aa2','PUID=\"x-fmt/397\", FormatName=\"Exchangeable Image File Format (Audio)\", FormatVersion=\"2.0\", FormatRisk=\"\", Extension=\"wav,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:18',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('d80f1e2e-a75b-4492-8f7b-e541e3980fb7','application/pdf',1,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:37',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('d82eff8d-ade2-4c27-a5f9-afe1fedd6b10','PUID=\"fmt/52\", FormatName=\"Rich Text Format\", FormatVersion=\"1.7\", FormatRisk=\"\", Extension=\"rtf,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:29',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('d95f4962-a8cd-4357-a0bc-4f2ffd408f2d','PUID=\"x-fmt/80\", FormatName=\"Macintosh PICT Image\", FormatVersion=\"1.0\", FormatRisk=\"\", Extension=\"pct,pict,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:17',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('daf70773-a281-4620-9c75-73350150512e','PUID=\"fmt/117\", FormatName=\"Windows Bitmap\", FormatVersion=\"3.0 NT\", FormatRisk=\"\", Extension=\"dib,bmp,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:23',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('db10b9f5-1c41-4638-bf7b-e8f5c214903c','PUID=\"fmt/479\", FormatName=\"Acrobat PDF/A - Portable Document Format\", FormatVersion=\"3a\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:26',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('dbad8e78-b7af-4039-8aa2-b501c42b7eeb','PUID=\"fmt/438\", FormatName=\"Digital Negative Format (DNG)\", FormatVersion=\"1.3\", FormatRisk=\"\", Extension=\"dng,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:20',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('dc4be07b-03cc-4d6d-95e5-2bd6f49b8b8b','audio/x-wav',1,0,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:04:55',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('dcc00ef2-f22c-4796-87e5-f8b728a6a0dd','PUID=\"fmt/436\", FormatName=\"Digital Negative Format (DNG)\", FormatVersion=\"1.0\", FormatRisk=\"\", Extension=\"dng,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:20',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('dcd1f9aa-d544-4ff7-a90a-d7dafc162897','CRW',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:49',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('dee6f452-c658-46e1-9fbc-f3dd3727479c','A .docx file',1,1,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e01e5dc0-37da-451a-9644-22309e5cb21e','audio/mpeg',0,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 17:55:52',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e0c40516-1727-47b5-89cd-ff8e36764442','A .HTML file',1,1,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e0e6ab6e-dd08-4b2a-9267-8ed7500655f6','text/xml',1,0,'8e39a076-d359-4c60-b6f4-38f7ae6adcdf',NULL,'2012-10-16 18:18:04',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e10c64e0-105a-44f5-83da-a4a10379d802','PUID=\"fmt/134\", FormatName=\"MPEG 1/2 Audio Layer 3\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"mp3,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:20',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e112c95a-5d2e-4b07-89fb-8775e48514d5','PUID=\"fmt/146\", FormatName=\"Acrobat PDF/X - Portable Document Format - Exchange 1a:2003\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:28',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e2421d0b-edfe-44fc-b404-f88f4e3ac522','SVG Scalable Vector Graphics image',1,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:18:00',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e2e6315f-7469-410d-8532-628d933f1508','PUID=\"x-fmt/92\", FormatName=\"Adobe Photoshop\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"psd,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:31',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e326db2c-7da9-40c4-81a2-613c979efcbf','audio/x-ms-wma',0,0,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:18:15',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e342d908-5dd2-4ea3-9291-288339829a8c','video/mpeg',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:40',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e3bb38be-25d8-4831-b583-84c20ee371a9','A .css file',1,1,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e45b3f8d-6116-48a5-b671-031cc0f9ab6b','7ZipCompatable',0,0,NULL,NULL,'2012-10-02 00:24:56',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e4c15807-3cb1-4e86-a659-03b56b3e1fb5','A .aif file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e4f2ee35-9d5c-4d2a-a251-0bbe5979d6d1','application/postscript',0,0,'f794555f-50ad-4fd4-9eab-67bc47c431ab',NULL,'2012-10-16 18:18:15',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e549810e-8a45-4d3b-9f7a-97ce7f11e853','Adobe Photoshop Image, 800 x 533, RGB, 3x 8-bit channels',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:38',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e5b5b610-e9dd-471f-b2c9-08455a23ddfe','application/pdf',0,1,'dc551ed2-9d2f-4c8a-acce-760fb740af48',NULL,'2012-10-16 17:55:59',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e5ebd621-9c56-4ab6-a698-75a89b044102','PUID=\"fmt/142\", FormatName=\"Waveform Audio (WAVEFORMATEX)\", FormatVersion=\"\", FormatRisk=\"\", Extension=\"wav,wave,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:19',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e65c2ac3-db3d-412e-ad84-021efeca4991','A .mxf file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e793325f-df0f-4e64-9ef9-cf1b08bf29ee','PUID=\"fmt/125\", FormatName=\"Microsoft Powerpoint Presentation\", FormatVersion=\"95\", FormatRisk=\"\", Extension=\"ppt,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:17',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e7c2b9b8-8c8f-42f7-86c6-2011cc2678a4','A .doc file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e7cb481d-e08b-4389-9819-e6bd6e12295c','A .ppt file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e88a46ca-5372-4314-a51c-ec83ac9ef4b4','audio/mpeg',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:41',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('e8e6949d-f0ae-4001-b8d0-91bfd45a1b47','image/jp2',1,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:35',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('eaf3f239-4e9b-42bd-b618-2cad78793b36','A .crw file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('eb10b29a-689c-45ca-9ea5-d92c275d0258','Olympus ORF raw image data, little-endian',0,0,'076cce1b-9b46-4343-a193-11c2662c9e02',NULL,'2012-10-16 18:26:57',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('eb216a3d-72a4-459b-8686-6aebab26bbb7','A .svg file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('ebc1e69b-0209-4abe-b22e-6d5660fab576','PUID=\"fmt/15\", FormatName=\"Acrobat PDF 1.1 - Portable Document Format\", FormatVersion=\"1.1\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:24',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('ec0a162a-90d3-4731-90c8-6c7ef3396f5c','PUID=\"fmt/45\", FormatName=\"Rich Text Format\", FormatVersion=\"1.0-1.4\", FormatRisk=\"\", Extension=\"rtf,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:28',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('ed15e649-7843-494a-a100-fc2571193f02','OpenDocument Text',1,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:30',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('ed5607ba-10de-4e96-902b-8eaf4dacf249','application/vnd.ms-powerpoint',1,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:34',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('ee907131-8b73-4dab-9978-97fa820b01fe','A .png file',1,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('ef02e784-10d9-4ff1-b4ea-b0e4636ba2b6','PUID=\"fmt/418\", FormatName=\"Adobe Illustrator\", FormatVersion=\"3.0 / 3.2\", FormatRisk=\"\", Extension=\"ai,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:29',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('f048d680-916b-4631-a19e-2b4c292f010e','PUID=\"fmt/291\", FormatName=\"OpenDocument Text\", FormatVersion=\"1.2\", FormatRisk=\"\", Extension=\"odt,ott,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:18',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('f1abb661-2418-4d3a-8d41-aef1a0f57bdc','EPS EXIF',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:37',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('f3fa999a-ba70-482a-a660-f732d27a882e','Apple QuickTime movie (fast start)',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:38',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('f520ade0-4fe7-4000-8bb5-061a222cb923','A .txt file',1,1,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('f869b28f-c712-48b5-b095-27dc19c94d09','video/x-ms-wmv',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:34',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('f86d6829-9a80-4348-9869-36b11517e74e','MPEG 1/2 Audio Layer 3',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:42',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('f991f719-4fe8-43d7-b032-2e4a52de8d53','image/tiff',1,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 18:18:01',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('f99c8abb-3364-44d6-b2da-bbcaad58e2ce','A .xsl file',1,1,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('fa1980ea-578a-44f0-9cca-fd9b80f52af2','Graphics Interchange Format',0,0,'b0bcccfb-04bc-4daa-a13c-77c23c2bda85',NULL,'2012-10-17 23:13:45',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('fb06ea33-20d5-4a3b-96fe-a7f654e63c03','audio/x-flac',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:38',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('fb5fca7c-0a75-4a94-8dc2-0dba6d66747f','image/x-ms-bmp',0,0,'237d393f-aba2-44ae-b61c-76232d383883',NULL,'2012-10-16 18:27:00',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('fbae3a6d-5b98-4974-888a-85bf48b793fe','PUID=\"fmt/181\", FormatName=\"Microsoft PowerPoint for Macintosh\", FormatVersion=\"2001\", FormatRisk=\"\", Extension=\"ppt,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:16',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('fbcf2ae7-4893-4771-833d-8eee20d91f3b','A .raw file',0,0,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('fc865a65-2cae-4062-a63d-0c6a54877c0e','application/postscript',0,0,'c26227f7-fca8-4d98-9d8e-cfab86a2dd0a',NULL,'2012-10-17 23:15:39',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('fd394578-5b76-4eb9-b40f-f116ec11a09e','PUID=\"fmt/476\", FormatName=\"Acrobat PDF/A - Portable Document Format\", FormatVersion=\"2a\", FormatRisk=\"\", Extension=\"pdf,\"',0,1,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:26',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('fe2dfde9-d4b8-4e1f-bcc0-262ec7710c3c','A .cvs file',1,1,'16ae42ff-1018-4815-aac8-cceacd8d88a8',NULL,'2012-10-09 23:16:13',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('fedd6582-993f-498c-86c3-991e758a795f','PUID=\"fmt/50\", FormatName=\"Rich Text Format\", FormatVersion=\"1.5-1.6\", FormatRisk=\"\", Extension=\"rtf,\"',1,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:28',1); -INSERT INTO `FileID` (`pk`, `description`, `validPreservationFormat`, `validAccessFormat`, `fileidtype_id`, `replaces`, `lastModified`, `enabled`) VALUES ('ff33f793-b071-4362-a13d-18225470ea98','PUID=\"fmt/123\", FormatName=\"Encapsulated PostScript File Format\", FormatVersion=\"2.0\", FormatRisk=\"\", Extension=\"eps,epsf,\"',0,0,'ac5d97dc-df9e-48b2-81c5-4a8b044355fa',NULL,'2012-11-09 23:17:21',1); -/*!40000 ALTER TABLE `FileID` ENABLE KEYS */; -UNLOCK TABLES; - -INSERT INTO `CommandClassification` (`pk`, `classification`, `replaces`, `lastModified`) VALUES ('27c2969b-b6a0-441d-888d-85292b692064','thumbnail',NULL,'2012-10-02 00:24:57'); -INSERT INTO `CommandClassification` (`pk`, `classification`, `replaces`, `lastModified`) VALUES ('3141bc6f-7f77-4809-9244-116b235e7330','access',NULL,'2012-10-02 00:24:57'); -INSERT INTO `CommandClassification` (`pk`, `classification`, `replaces`, `lastModified`) VALUES ('3d1b570f-f500-4b3c-bbbc-4c58aad05c27','preservation',NULL,'2012-10-02 00:24:57'); -INSERT INTO `CommandClassification` (`pk`, `classification`, `replaces`, `lastModified`) VALUES ('5934dd0b-9f7c-4091-8607-47f519f5c095','extract',NULL,'2012-10-02 00:24:57'); - -LOCK TABLES `CommandRelationship` WRITE; -/*!40000 ALTER TABLE `CommandRelationship` DISABLE KEYS */; -INSERT INTO `CommandRelationship` (`pk`, `commandClassification`, `command`, `fileId`, `replaces`, `lastModified`) VALUES ('005ccb08-212f-4f38-a570-8124054fbbaa','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','e326db2c-7da9-40c4-81a2-613c979efcbf',NULL,'2012-10-17 01:18:15'),('00e17703-53c5-4349-9f34-7b62f45f8de6','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','4953b2ca-d1f2-4905-a8d4-bf95abd54930',NULL,'2012-10-17 00:55:59'),('00f81dcf-8b45-4bf2-acfc-9b8a023c5015','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','a5cdb910-3eea-4f59-9319-d2c51b6658fd',NULL,'2012-11-10 07:17:35'),('022e019a-c910-4318-b414-c6c0b97c32b8','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','74fa9f99-573c-4c76-964b-4456ac234715',NULL,'2012-11-10 07:17:32'),('02b1e102-f4f4-44c6-baf5-887403dde742','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','f3fa999a-ba70-482a-a660-f732d27a882e',NULL,'2012-10-18 06:13:39'),('0432646a-83ea-4411-b873-6b4e47f53835','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','afea3d12-379b-47ef-a395-0e162e37d90b',NULL,'2012-10-17 01:18:16'),('044b0127-10a0-417e-9ece-308252f7211b','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','79103108-9ea9-4ed0-a777-37af444867b6',NULL,'2012-10-17 01:26:57'),('0496cb64-3e26-420a-81e6-c6b39c83e580','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','95d261ff-13dd-4c42-8c83-f69a7b1cd6ba',NULL,'2012-10-18 06:15:37'),('04984e2e-27f1-49f5-bbc4-1a3f8fc02c38','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','573c6a20-1076-4234-a728-0bce8405f097',NULL,'2012-10-18 06:13:34'),('04a475bc-53b9-4cf2-b2a0-dfd3123ca434','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','705e9509-6393-481f-8dd5-f4753abcfead',NULL,'2012-10-17 01:26:58'),('04aadda9-2bcc-4adb-b4b9-63a38d348801','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','a30af6e0-195d-46a8-a46a-cc1682d04896',NULL,'2012-10-17 01:18:19'),('04b41cc3-3b8c-4681-bdd4-d595f63a1502','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','a487dde7-8bc6-475c-9b7c-35cd163c5845',NULL,'2012-10-17 04:32:57'),('05218f18-5400-4dda-a96c-ce1c25e8c930','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','1567b10b-1474-4df5-a6df-ce3b8a92ee97',NULL,'2012-10-19 05:57:04'),('05321f60-fcbd-44a6-92cc-0591862e72df','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','f86d6829-9a80-4348-9869-36b11517e74e',NULL,'2012-10-18 06:13:42'),('05766805-3412-42af-bdc0-5ffd5383b197','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','c789e8b6-9d94-47f4-9be5-5a118cd13c0d',NULL,'2012-11-10 07:17:16'),('05df4917-f3b3-41b4-876e-9ba92b852730','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','369eeb06-b520-4566-b9c5-1f7a2277a450',NULL,'2012-10-17 01:18:17'),('07153824-b553-4db8-884d-a4669c927ee9','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','8479526a-fdab-44af-97de-920bea73237e',NULL,'2012-10-18 06:15:38'),('07621790-06a8-49cf-be93-822072279885','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','21749616-c014-4e81-84d5-f90af82532b7',NULL,'2012-10-18 06:13:33'),('07a22467-a6a2-4621-8e6c-cf80b8d58c8a','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','08255d3a-bd85-434e-97a8-14f98b0c4ec8',NULL,'2012-10-17 01:26:59'),('088d506d-d71a-42fd-bcfb-c95bfaf2dfa5','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','881214c5-642f-4a87-b4a5-3c8b6b8436d2',NULL,'2012-10-02 07:24:59'),('08b8152a-10cf-4ace-8ac8-8a422b29eef2','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','480b7fb6-f10b-4631-97f4-f40e0385cdca',NULL,'2012-10-18 06:13:39'),('08d37a19-7939-49e6-ade6-3ea0220ad88b','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','0f919e47-d406-4e9c-96c2-0a372c818f73',NULL,'2012-10-19 05:57:15'),('08ff0f76-ea48-4310-aa74-e4d09e6ecfdb','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','dcd1f9aa-d544-4ff7-a90a-d7dafc162897',NULL,'2012-10-18 06:13:49'),('097e8e92-ba61-4d86-a945-666766f0eaf9','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','f3fa999a-ba70-482a-a660-f732d27a882e',NULL,'2012-10-18 06:13:39'),('098dd2f9-8440-4d18-b37f-e4e2ab859498','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','4d2748e9-7233-4106-b7a1-83e7cfdbff24',NULL,'2012-10-18 06:15:39'),('0a0b5dc9-661f-477e-8acb-c45ae4ab7062','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','32846451-1dbe-44c0-b599-cfd3bdc73b0e',NULL,'2012-11-21 06:37:15'),('0aea272d-5ba8-4599-ae27-2a773335ee86','3141bc6f-7f77-4809-9244-116b235e7330','d91f0637-b2f6-4492-ac1b-23c155102a3e','e0e6ab6e-dd08-4b2a-9267-8ed7500655f6',NULL,'2012-11-21 06:37:15'),('0b34951f-715f-4572-9903-5578baabedf2','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','fbcf2ae7-4893-4771-833d-8eee20d91f3b',NULL,'2012-10-02 07:24:59'),('0bb6dfd3-b9ff-422e-a651-567bd6ef71f1','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','b47ed36b-9e71-4516-ba3a-cb4c91718d8c',NULL,'2012-10-17 01:18:17'),('0c459459-21d7-4d79-b84b-c127301c9d37','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','4c9c49f4-ca45-40bc-b7a2-119bfa76cf23',NULL,'2012-10-02 07:24:59'),('0caae796-cb9b-4e2f-bfd5-59f267eff991','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','9862f025-b370-4ed0-96a1-0d81813ac256',NULL,'2012-10-02 07:24:59'),('0e27288b-1d52-4f3f-a53e-f1bdd8973f33','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','420813eb-03e3-4a3a-97ca-84894f08af88',NULL,'2012-10-17 04:33:02'),('0e472be8-84f9-4e1c-98d6-8e1ce5aeabee','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','f991f719-4fe8-43d7-b032-2e4a52de8d53',NULL,'2012-10-17 01:18:02'),('0e5fe48d-6579-49a0-843e-40433219dd98','3141bc6f-7f77-4809-9244-116b235e7330','d91f0637-b2f6-4492-ac1b-23c155102a3e','435bcdbe-75b2-44d2-94ef-2710edcf9eb3',NULL,'2012-10-17 04:33:01'),('0e961118-9d44-4cec-ad68-5ece6cc7fa1a','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','e112c95a-5d2e-4b07-89fb-8775e48514d5',NULL,'2012-11-10 07:17:28'),('0ea97891-93a8-4ac8-b453-e9fcb1094f2d','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','9118731f-b1bb-46ed-9d6c-1d070a5e7bf6',NULL,'2012-10-18 06:13:36'),('0ebadba7-4eb9-4937-aa5f-42dcddbe3388','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','59160936-d126-49a2-a275-86f9359c0e89',NULL,'2012-10-18 06:15:36'),('0f764862-d980-4838-a6b0-d0de7d659333','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','e10c64e0-105a-44f5-83da-a4a10379d802',NULL,'2012-10-17 04:33:05'),('0fbfd7bc-dde6-4435-90ea-640547bdff2e','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','7b2ab804-b73e-4700-96ca-61ebc9c3576e',NULL,'2012-10-18 06:13:41'),('1008bda5-2d9d-47f9-a05f-01e7e0716ab9','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','1ddbfa77-0390-4905-aeda-58d2984db924',NULL,'2012-11-10 07:17:34'),('10dd2122-74cf-4e5d-adaf-6bfb650a5458','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','d95f4962-a8cd-4357-a0bc-4f2ffd408f2d',NULL,'2012-10-17 04:33:05'),('11422576-5eba-4b51-aad5-b9e31c70ff48','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','976dc56c-c92a-4253-ac88-f69cae251794',NULL,'2012-10-17 01:27:02'),('11c8d32c-e555-41a7-abba-9640fada41ca','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','d407dc6e-52ae-4fec-a1f6-9708d87af938',NULL,'2012-10-18 06:15:40'),('1328a142-2dc3-4dbe-848e-3166dba0c3e5','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','a269e764-da36-445a-b6a4-e4cc3fac4f88',NULL,'2012-11-10 07:17:27'),('1387adf6-657c-4a8a-9c6f-89b14cc0d7b0','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','2b6c2412-b978-4478-8d09-b91ed4c6dc6a',NULL,'2012-10-17 04:33:00'),('13e648a7-4e57-4e54-a0c7-e0e8a502c3ec','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','3651eff9-0c05-4077-8f26-a9dfb915465a',NULL,'2012-10-17 01:18:18'),('144ed0a5-ed7b-4543-8092-f1d4b8acca02','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','a0af674f-4e72-496b-bf4e-4f126eab7372',NULL,'2012-10-18 06:13:33'),('14929c16-4cc7-4ce4-b809-54868020a7cc','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','4690e672-6b5f-42ac-9cf8-d0a3bafeeaa9',NULL,'2012-10-17 01:26:58'),('149d0f47-1538-4832-885e-7287c3241fed','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','e4f2ee35-9d5c-4d2a-a251-0bbe5979d6d1',NULL,'2012-10-17 01:18:15'),('14ba299c-8e37-4407-bb9e-c0fd1ffab13d','3141bc6f-7f77-4809-9244-116b235e7330','e96b787d-57f6-41a4-9693-d0634bfacacb','369eeb06-b520-4566-b9c5-1f7a2277a450',NULL,'2012-10-17 01:18:18'),('14ea0110-1a05-47a9-9a66-75881cd88490','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','cc23bcb7-221b-4ef0-98ab-40726b5aee4b',NULL,'2012-11-10 07:17:29'),('152c53d0-546c-4c9d-8e17-7cca83c20b28','3141bc6f-7f77-4809-9244-116b235e7330','d91f0637-b2f6-4492-ac1b-23c155102a3e','e2421d0b-edfe-44fc-b404-f88f4e3ac522',NULL,'2012-10-17 01:18:00'),('1535c347-9e09-4282-9cc5-b14971303b45','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','0f919e47-d406-4e9c-96c2-0a372c818f73',NULL,'2012-10-19 05:57:15'),('154f81f6-d830-4cc1-9fe1-3335a0718d43','3141bc6f-7f77-4809-9244-116b235e7330','a74cbffb-281b-49ee-8e96-fbac2ab54785','4f31deac-0130-4dc8-9a6b-b2ac3bbc343f',NULL,'2012-11-10 07:17:30'),('15e340bd-72fa-4ba1-b7ad-1702df0b0f1f','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','b155a460-4722-4dcf-81fa-3a4c65557cef',NULL,'2012-10-17 04:32:58'),('15e54d71-1fbe-40cc-b3ac-6485878457c4','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','c285590a-914f-4c36-a0a2-624fa25d028d',NULL,'2012-10-18 06:13:48'),('15f2e4cc-ce68-423e-baaf-9cb095d16576','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','8d8ad28c-4bf2-4258-a7f1-d2c04df7b125',NULL,'2012-10-17 01:17:59'),('15fbe8c4-1bc2-4d5d-b2fa-9db597dd99f3','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','180a7231-0444-44c9-8371-81825a2be339',NULL,'2012-10-18 06:15:35'),('160ab252-75e9-4cab-9577-2b6292c9bceb','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','adfa26bc-e276-4d69-85f3-63c8bd046d3f',NULL,'2013-01-05 07:02:50'),('160b833d-4424-434d-892e-06f349c89146','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','b8b6dfa3-beca-4667-a279-cf78d610a0b6',NULL,'2012-10-17 01:18:13'),('16124a22-522f-4bd1-8a96-758b823aea83','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','3651eff9-0c05-4077-8f26-a9dfb915465a',NULL,'2012-10-17 01:18:17'),('1656da41-2aa7-4549-9537-cf173e526397','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','51f80b0a-7a4b-4c68-ac7a-b41ef9c9aaba',NULL,'2012-10-18 06:15:36'),('168457f7-e929-401f-a6b6-82a94363763f','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','9118731f-b1bb-46ed-9d6c-1d070a5e7bf6',NULL,'2012-10-18 06:13:36'),('17416d8a-df62-47c7-b0d7-4b8bbff9b5a0','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','daf70773-a281-4620-9c75-73350150512e',NULL,'2012-11-10 07:17:23'),('17ab8559-00c2-4bd1-8c3d-a5fac515a1a0','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','274e16c7-1b68-498b-8290-e339d4197136',NULL,'2012-10-18 06:13:40'),('1886c600-80c7-4e1b-801f-8b1925df0db0','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','a359b55f-70af-43c8-a6d1-42f0801c0155',NULL,'2012-11-10 07:17:33'),('18c9ae2f-1488-4b0c-800d-19f6e4b226c6','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','62be2403-d1ee-4d70-bab4-28bfd161f8e0',NULL,'2012-11-21 06:37:15'),('18d3ae40-bd3f-4a7e-9e5c-331a864060d9','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','badbed99-3113-45e7-bf87-40bf5354f88d',NULL,'2012-10-02 07:24:59'),('18d91784-c60c-427e-a64f-1fa79d557810','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','92ba8d67-0170-4f39-8f29-316b44522112',NULL,'2012-10-02 07:24:59'),('18e49104-5c65-472b-9a05-2b478a63a073','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','62be2403-d1ee-4d70-bab4-28bfd161f8e0',NULL,'2012-11-21 06:37:15'),('193b956c-fbc2-4e8d-8384-2e3c9bd2c15e','3141bc6f-7f77-4809-9244-116b235e7330','b3b3b726-acef-4d58-82e4-ef8875d654ce','b0bfcd0e-3194-4292-b78a-cea6a356a4d8',NULL,'2012-10-02 07:24:59'),('19581229-5fa8-43d6-905a-0e873c2d6d1b','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','95d261ff-13dd-4c42-8c83-f69a7b1cd6ba',NULL,'2012-10-18 06:15:40'),('199a5fad-35ba-482a-b760-dc631be40384','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','65b74b06-6bda-433b-b993-f09810cdcbca',NULL,'2012-10-17 00:55:51'),('19ffd84b-ceef-4d72-8f97-3f52659fb27a','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','8d6f16a1-d41d-4f6b-9da6-fa06ce5ebe54',NULL,'2012-11-10 07:17:18'),('1a0b956f-0ec1-44c1-90c8-5dad38c606fe','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','937e5e30-e054-4e65-972e-9c5b4b4f8f5f',NULL,'2012-11-10 07:17:25'),('1a568224-f6a0-4806-8c89-1763c0e93027','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','6f4e7eff-7204-468e-bfb2-2943688d282f',NULL,'2012-10-17 01:17:59'),('1a7154cf-bdf0-4425-8172-454a4cb3c608','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','833cc7c2-c155-45b6-b927-98fdb20ae86f',NULL,'2012-10-17 01:04:54'),('1a9a9bcb-7f60-4e9f-9b1b-5a863bd5d676','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','d407dc6e-52ae-4fec-a1f6-9708d87af938',NULL,'2012-10-18 06:15:38'),('1aa6f89b-1235-46eb-8082-0c3e7060d260','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','0c2ad7a0-83b5-4f83-aed9-ee6ad99510b3',NULL,'2012-10-18 06:13:29'),('1b3eff03-d413-43f6-adf4-9c9e866c6da7','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','b43d2871-487f-4d31-aaa1-e9bea8dd86fa',NULL,'2012-11-10 07:17:29'),('1be21d30-dbe7-4020-bc7f-18044959a5ee','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','9086cec8-a28e-4723-9f31-e2e68e173c6a',NULL,'2012-10-02 07:24:59'),('1c4365f5-17a1-49fe-a2ea-5b8fc5e0cac7','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','d2941b3f-38e2-491d-947b-f6e07802595c',NULL,'2012-11-21 06:37:15'),('1dd2099a-ed85-4900-a3aa-0d870365d44c','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','ce23cf3b-6ee1-4392-9d11-e088cde97864',NULL,'2012-10-02 07:24:59'),('1f0bac1b-ef44-457e-9c31-54aa64d43b8a','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','cba89043-1ac9-4e3c-a140-2ac74377c3b0',NULL,'2012-10-02 07:24:59'),('1f28a34a-61ca-4f96-900d-1747fb02d9e2','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','d63f2b5e-736c-4b85-ba35-ec6cc58be2a2',NULL,'2012-11-10 07:17:26'),('1fb93a40-b118-498e-b9ae-647ebfd03ab1','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','82f879a7-fdda-4b3f-a79f-55c1605a85ce',NULL,'2012-10-18 06:15:42'),('1fe3366d-adf4-4a29-84b6-81cf935b7ae7','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','86ce68f3-b264-4e49-a44e-c58144703700',NULL,'2012-10-17 01:04:55'),('1feac350-33dc-4b2f-8b98-34ec6643e13b','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','dcc00ef2-f22c-4796-87e5-f8b728a6a0dd',NULL,'2012-11-10 07:17:20'),('20539d55-6bc3-4155-bc5e-4bb91838199b','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','1d31b54e-1745-4b37-9468-d35e8e317d05',NULL,'2012-10-17 01:18:00'),('20797682-64ea-418a-84a3-283a34344167','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','5cb9e13e-9abe-4258-9c94-f79c6136f577',NULL,'2012-10-18 06:15:41'),('20ad2ae7-ddba-4dd0-900b-445521f62dac','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','fc865a65-2cae-4062-a63d-0c6a54877c0e',NULL,'2012-10-18 06:15:39'),('20e94d3b-45cd-42e6-9036-abeaa6f7768e','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','315e3d93-22df-4993-955a-73d30a11f7d7',NULL,'2012-10-17 01:17:59'),('20f37e74-23f2-48de-a3ed-5ca04c44120c','5934dd0b-9f7c-4091-8607-47f519f5c095','1c9728c0-16d3-4db0-81c3-7a51068caa1c','c5e493af-195e-4b64-9331-4f716a16eac9',NULL,'2012-10-02 07:24:59'),('214b2af6-fc8c-495b-b6bb-5ed3a641a78e','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','bdb7edf8-255d-4fe5-a457-50e93fad0211',NULL,'2012-11-10 07:17:25'),('216d494f-90bd-4957-a7f2-644699cd2636','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','4bcc5156-f3c7-4a5f-a99d-006eb61c4ba1',NULL,'2012-10-18 06:13:31'),('21844439-e82d-4329-97a5-b982399018bf','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','59160936-d126-49a2-a275-86f9359c0e89',NULL,'2012-10-18 06:15:36'),('21f2e375-495e-416d-b371-79a97fcf8a38','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','88f61e4e-ce58-4bc3-9bc8-91d6c3cfcabe',NULL,'2012-10-17 04:33:03'),('221f7b01-2c6b-40f0-b3ac-a35235ef93d6','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','4d2748e9-7233-4106-b7a1-83e7cfdbff24',NULL,'2012-10-18 06:15:39'),('22535c1a-8672-4cb7-b4c8-f7952773a58a','3141bc6f-7f77-4809-9244-116b235e7330','a74cbffb-281b-49ee-8e96-fbac2ab54785','5b553136-e75f-4512-b988-689db706fd50',NULL,'2012-11-10 07:17:30'),('2265a4f7-4e31-4746-a702-ba4ed2f4090c','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','1087d1e9-3dff-4b6e-b1e2-625141bbf814',NULL,'2012-11-10 07:17:34'),('228278af-11ca-456b-a658-4cb603e11978','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','d407dc6e-52ae-4fec-a1f6-9708d87af938',NULL,'2012-10-18 06:15:38'),('23c3c94a-b029-4cce-b80d-f939dc1e98f0','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','e7c2b9b8-8c8f-42f7-86c6-2011cc2678a4',NULL,'2012-12-13 05:41:55'),('24a8db76-c08f-458f-bd7d-c7046639dd11','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','369eeb06-b520-4566-b9c5-1f7a2277a450',NULL,'2012-10-17 01:18:18'),('258b90af-eca6-4a08-b1c7-584a3e539984','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','40259ade-4e78-47c9-a8b8-d66e2aa121af',NULL,'2012-11-10 07:17:32'),('25a2ce1e-e3a6-4c5a-97a1-54a58f9b4b21','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','adac68c6-1483-430c-ab2e-b594660ae4d6',NULL,'2012-10-12 06:10:29'),('25ec0f09-98f0-4aad-bf27-aa56315c0d88','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','a1e102fe-de95-4fc5-9bff-a1cc7058e1cd',NULL,'2012-10-02 07:24:59'),('2669a2db-94cb-42d5-af0d-6068f3be7202','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','e549810e-8a45-4d3b-9f7a-97ce7f11e853',NULL,'2012-10-18 06:13:38'),('26737641-50eb-4263-aba4-2ccadd84b822','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','d189342e-a551-4f77-a45c-6f3482e3dce3',NULL,'2012-10-17 01:26:59'),('27ec83cb-456f-4053-865e-2e478aaf5206','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','b5d57def-9d34-4851-89f9-62b4646ad605',NULL,'2012-10-17 01:26:59'),('27febcd5-1a1b-4257-be4d-dee9ae5959d5','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','03bbfaaf-124c-4454-8082-50e0b3d23e57',NULL,'2012-10-02 07:24:59'),('27ff07c7-a565-479b-b695-49c9bd33bc8b','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','92ec6955-b6ca-4acb-b8d4-a475efcd8c73',NULL,'2012-11-10 07:17:24'),('28461679-496e-47db-8d53-26b021fb490d','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','66120b2d-0c89-4304-815b-6c8cec94647b',NULL,'2012-10-17 01:27:01'),('2886ae01-89ab-4572-9ef5-f3ca047f95cd','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','e01e5dc0-37da-451a-9644-22309e5cb21e',NULL,'2012-10-17 00:55:52'),('291e67c7-a628-4d3e-89e9-91be995168be','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','51f80b0a-7a4b-4c68-ac7a-b41ef9c9aaba',NULL,'2012-10-18 06:15:42'),('2958fdb6-8c5f-49dc-99a8-1f73cf5ef8b6','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','8a0cc11d-d1ec-4e0d-a895-ac037c9d94f8',NULL,'2012-10-02 07:24:59'),('297ca89d-2d05-4277-b359-d2427f4b7b22','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','86ce68f3-b264-4e49-a44e-c58144703700',NULL,'2012-10-17 01:18:16'),('29ae437b-fd54-4c76-ab8e-0adda93970c3','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','88bb6c02-f537-4879-89dc-8e5e7e257156',NULL,'2012-10-18 06:13:31'),('29cf8c74-5bba-4778-b39e-e7d0ab1ecfd0','3141bc6f-7f77-4809-9244-116b235e7330','a74cbffb-281b-49ee-8e96-fbac2ab54785','03bb2a7e-47bd-44fa-ab1d-797370bb1ead',NULL,'2012-10-17 01:18:17'),('29f7848c-3118-440c-b182-8c7084dc90f9','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','120836eb-b7e7-42dc-95f1-1470b8000dec',NULL,'2012-10-18 06:13:30'),('2a4e657b-f3cf-4c07-b3ff-46f106c49c3a','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','435bcdbe-75b2-44d2-94ef-2710edcf9eb3',NULL,'2012-10-17 04:33:01'),('2a6031aa-247f-427d-bcea-3490fe38afc3','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','bd3002cb-5404-4590-9481-c62d5d638bf6',NULL,'2012-10-17 04:32:56'),('2ae64e63-eb3c-4dbb-b2c4-045649aae19e','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','1d283460-e24c-4cbb-8476-2a66df560456',NULL,'2012-10-17 01:18:02'),('2b66444d-f00a-4bc3-b289-d4f2d36f1fe8','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','d407dc6e-52ae-4fec-a1f6-9708d87af938',NULL,'2012-10-18 06:15:38'),('2bdfab41-575f-4d8c-9949-24a01a0471d3','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','01c97b84-76e0-4b94-9e3b-1e670bd9468b',NULL,'2012-10-17 01:18:13'),('2bf3175a-0ee7-466f-a331-dbc871888130','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','24237983-b248-4d0d-b1d2-04ca8e469198',NULL,'2012-10-18 06:13:36'),('2bfd2630-e769-4617-8eef-d06080d988d8','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','baecde96-ebee-426c-bc77-3ec8dc653261',NULL,'2012-10-02 07:24:59'),('2c753d1d-0889-4b86-964f-591cc5ac0ca1','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','16d79ccc-4f77-44d7-8b25-054915b289c5',NULL,'2012-10-17 04:33:04'),('2c98c9e4-c0f7-420a-bcc8-56e03c5b7974','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','fedd6582-993f-498c-86c3-991e758a795f',NULL,'2012-11-10 07:17:29'),('2da25c12-863a-4f48-8118-6ee170ae1eb0','3141bc6f-7f77-4809-9244-116b235e7330','e96b787d-57f6-41a4-9693-d0634bfacacb','62ad9422-842f-437e-b388-620abbbfe876',NULL,'2012-10-18 06:13:37'),('2e5339fd-85f0-4139-bbf7-d9936492f0a7','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','209d95fb-25be-471f-902d-dd08fe5be83f',NULL,'2012-11-10 07:17:25'),('2e7125ec-0926-44fa-9a95-40f6d5586dda','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','15aceaf6-cf02-4aab-baeb-0f2402e41125',NULL,'2012-10-17 01:26:59'),('2e743e60-1573-4684-932f-6cebae2eaf51','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','f86d6829-9a80-4348-9869-36b11517e74e',NULL,'2012-10-18 06:13:42'),('2ea443cc-23bf-4767-b59c-30e76f73c543','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','983398c1-423c-4e88-99be-da3ad56d2d7c',NULL,'2012-10-16 04:32:22'),('2f242e5e-31ca-4393-a1fc-e26ab5d22646','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','a30af6e0-195d-46a8-a46a-cc1682d04896',NULL,'2012-10-17 01:18:19'),('2f384d1a-d795-47a0-9815-e3b4518b006f','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','799d35d1-0125-4d6b-8e92-df7cad508426',NULL,'2012-10-17 01:26:58'),('2f52ecb3-1d2f-4c1d-b7d2-f12ba62034fd','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','21b07ca7-b9e9-490a-a4db-39081a306862',NULL,'2012-10-17 04:32:58'),('2fb55e38-bbed-4da0-bad5-447025e2fbf9','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','b155afa2-038e-42dc-beee-f89e5a3ce41f',NULL,'2013-01-05 07:02:50'),('3017f9eb-b2a6-46de-9638-3cffc0936892','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','9e447d46-e845-4abb-b562-e73b3a716ab4',NULL,'2012-10-18 06:15:35'),('3020dca3-8984-4053-85a1-107efe2d0364','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','37b9babb-af84-4da8-9c2a-2c03087de2e4',NULL,'2012-10-18 06:15:34'),('302eaad4-7748-4609-a9cc-fd4d2aae536d','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','420813eb-03e3-4a3a-97ca-84894f08af88',NULL,'2012-10-17 04:33:00'),('3048eb16-116e-4e80-a018-2e555e3033ac','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','9a8285cc-f383-4a1b-bf64-60749f065603',NULL,'2012-10-17 04:33:02'),('306868e3-127b-4bd1-9d46-f407ec883b59','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','7b2ab804-b73e-4700-96ca-61ebc9c3576e',NULL,'2012-10-18 06:13:36'),('30692241-6ba2-4863-841c-5a1c3e402b03','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','92526fe4-93e8-48c0-ab29-be50bc832bf5',NULL,'2012-10-02 07:24:59'),('30b9cc98-05dc-49cf-9a54-ea0baeaa4313','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','23dca992-ea19-4731-8e96-20739509851c',NULL,'2012-11-10 07:17:15'),('31622348-6e1e-4f2f-b139-dc29f74cea49','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','599cb14f-dbc5-4f45-8e94-24fdb881fbbf',NULL,'2012-10-18 06:13:41'),('31b658d1-5622-4718-8f43-5137f03b9c87','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','62be2403-d1ee-4d70-bab4-28bfd161f8e0',NULL,'2012-11-21 06:37:15'),('31bb80ad-794c-48a2-99d3-fcb2e616222f','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','4ae6d5e4-1cd1-481e-8469-eae98f9adb36',NULL,'2012-10-17 04:33:00'),('31f580e9-3d54-414c-a203-64b525b35523','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','40f0c13e-f8ef-4a50-94cd-f1a2d95ca886',NULL,'2012-10-02 07:24:59'),('327f9451-7e2b-4fd8-a3b6-b69e7af30328','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','580f3cbb-7604-4775-8776-2dcfc5651139',NULL,'2012-11-10 07:17:31'),('32d67c6d-0975-4389-ade6-b29a69c85bad','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','4953b2ca-d1f2-4905-a8d4-bf95abd54930',NULL,'2012-10-17 01:18:19'),('32d9daf9-e794-4eba-92e3-a643cb6262ed','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','bc9e9d1b-9112-4336-ac8e-c2c0b614777b',NULL,'2012-10-02 07:24:59'),('332d076c-057b-4f28-8479-3526e345dd26','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','10d24277-054c-4c20-9774-9cac31b22b42',NULL,'2012-10-02 07:24:59'),('338f94cc-0ded-4129-bdb7-13b349679991','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','0f965b92-8f6c-4a52-bffd-a28da459fdf9',NULL,'2012-10-17 01:27:03'),('3443c0ed-6eeb-479a-887f-cbcfd2638474','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','78b2cb5f-7b0f-450e-95fe-dd2e8de13770',NULL,'2012-11-10 07:17:27'),('34d3bf8d-d704-4ade-8ea9-91409aa80d16','3141bc6f-7f77-4809-9244-116b235e7330','dc949cb7-ad68-44ba-a795-d2a43a18164f','6ba6316f-7372-4dfc-a573-9db78af2b3ed',NULL,'2013-01-05 07:02:50'),('352c79e5-90ad-4eb2-82de-4e4f901685fa','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','4648baa2-24e0-4ebd-a31a-2417e4175ad3',NULL,'2012-11-10 07:17:33'),('353659c1-f664-4fd5-9496-1e13b5bc9dab','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','c219f204-5418-4f45-b148-8216d2a15e88',NULL,'2012-10-02 07:24:59'),('35578541-2946-4a48-a769-7c493dc8d508','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','1ded295a-3065-4da7-8ba7-42edef36a1db',NULL,'2012-10-18 06:13:29'),('35d403c7-da1b-4805-922e-eb0e28eacda6','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','8cacccb8-d2d5-46d4-a3f5-805ae1fc68cd',NULL,'2012-11-10 07:17:23'),('364c6353-f200-403a-88fc-6bbc60df80df','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','2184c88b-bbe4-41a1-9a21-281f5cf55433',NULL,'2012-11-10 07:17:26'),('37271457-06e0-41e9-a0ea-5a48e508274d','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','d2941b3f-38e2-491d-947b-f6e07802595c',NULL,'2012-11-21 06:37:15'),('372b5bcd-89e7-4cb9-a0ae-ec154c382527','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','315e3d93-22df-4993-955a-73d30a11f7d7',NULL,'2012-10-17 01:17:59'),('37315e65-c380-4531-b73a-900c715dc4ca','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','075bcd44-b926-4a45-b8a1-f75220f5c903',NULL,'2012-10-18 06:13:32'),('373c315c-c922-4af0-a82a-1f9e9fb21297','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','8f063f36-f64c-479e-b37a-2491e99feb6a',NULL,'2012-11-21 06:37:15'),('378d7215-c54e-46f1-b6f7-bf6badde35d1','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','4d82500c-bfb5-4ddd-bb9e-40733cbe72be',NULL,'2012-10-02 07:24:59'),('37a67bd8-214c-478d-8c4e-eba6a4b45905','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','095e1f7a-815c-449b-ae7e-b5d96e434f2d',NULL,'2012-10-12 06:10:31'),('380bc9fc-7aba-441f-978d-ed4620e66618','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','aaf1a292-b2e7-4624-8119-a6805bc6702c',NULL,'2013-01-05 07:02:50'),('385c2749-f719-47ab-90b9-85df76fe8599','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','9f11626e-4664-49d6-9604-5c2ee9f4f299',NULL,'2012-10-02 07:24:59'),('385c8489-1d29-4620-b86e-b20aadbc95e6','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','dbad8e78-b7af-4039-8aa2-b501c42b7eeb',NULL,'2012-11-10 07:17:20'),('386fb7e7-ba3a-44e6-beb3-eb86372f4e1f','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','31868203-8848-4644-92c7-27307c5e5ba0',NULL,'2012-10-17 01:27:00'),('387a9cfc-5f0f-4387-8c7e-c595735cb442','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','4fb85f9a-831d-49ff-a554-4c80a792232b',NULL,'2012-10-18 06:13:38'),('38c11b7e-6699-47c4-ac17-736029d05cb7','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','1ddbfa77-0390-4905-aeda-58d2984db924',NULL,'2012-11-10 07:17:34'),('38f08986-484a-427a-b2ea-507b15722c93','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','31a79d37-ac13-4d6f-ba7d-49c9493959c9',NULL,'2012-11-10 07:17:20'),('38fff51d-9377-4847-be85-f3bcb7f4bc57','3141bc6f-7f77-4809-9244-116b235e7330','e96b787d-57f6-41a4-9693-d0634bfacacb','fc865a65-2cae-4062-a63d-0c6a54877c0e',NULL,'2012-10-18 06:15:39'),('39433733-15fd-4ad6-85a6-b3fc68b94818','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','4d399247-7598-403d-bb17-8d9230bacb0d',NULL,'2012-10-17 01:18:19'),('39656572-91ff-48bf-8ed2-962653f90ae1','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','53bff439-5ff6-4fe9-a87a-8745da64b5e6',NULL,'2012-10-12 06:10:30'),('3a446915-a1ac-4706-9a92-2244d8f098a8','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','8479526a-fdab-44af-97de-920bea73237e',NULL,'2012-10-18 06:15:38'),('3a58f65d-afd1-425e-a9af-6b54eee2691f','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','35c9fd79-c9b4-4a0c-84dd-6a9a6a05453a',NULL,'2012-10-18 06:13:42'),('3a66f468-87fa-4589-8f55-2a09982f5f72','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','8533521f-82c8-408f-9499-194e45d50154',NULL,'2012-10-18 06:15:42'),('3a85647b-4456-4485-8e13-d5178df1a9eb','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','123481b3-aca7-41ec-97d7-7e6601395d4a',NULL,'2012-10-17 04:33:01'),('3a9e84ec-8d72-4b0c-8b40-55a6aad546c6','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','ab17d28b-3f9a-45e2-b6da-fb19cf759c13','a974079d-0865-49ef-afd8-0ec91174d323',NULL,'2012-11-10 07:17:14'),('3ab592dc-f203-496e-9834-5758fd049e35','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','3651eff9-0c05-4077-8f26-a9dfb915465a',NULL,'2012-10-17 01:18:17'),('3b97490c-339c-4e11-a0b9-c9ab93625539','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','1d0b3087-b43f-412f-8e10-2063b912ed81',NULL,'2012-11-10 07:17:19'),('3b97994e-5e53-4905-9c27-ad6a4152cb19','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','632a5374-bc44-4d75-a9f6-e53edb87e316',NULL,'2012-11-21 06:37:15'),('3c4589ca-115a-429d-8f86-b6b9af324622','3141bc6f-7f77-4809-9244-116b235e7330','e96b787d-57f6-41a4-9693-d0634bfacacb','aa3515c1-bd03-4555-9e98-723ca14f74a9',NULL,'2013-01-05 07:02:50'),('3c84588b-2bfb-454a-8131-c77942d99e86','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','369eeb06-b520-4566-b9c5-1f7a2277a450',NULL,'2012-10-16 04:32:21'),('3cc51efd-794e-486b-95fb-eb4fdcc44cca','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','fbcf2ae7-4893-4771-833d-8eee20d91f3b',NULL,'2012-10-02 07:24:59'),('3d10277a-5ca4-48cc-a254-7d1e04350264','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','0f42eabd-4da2-48ed-aa30-f76b1b6d94b7',NULL,'2012-10-17 04:33:04'),('3d33cae3-a09e-4d61-af0b-8756a4b899d0','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','84c9fb23-9a6c-4fad-832e-0cc3de838d70',NULL,'2012-10-18 06:15:37'),('3d955fdb-7708-4ae4-9810-eec4bf84c2f3','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','fb5fca7c-0a75-4a94-8dc2-0dba6d66747f',NULL,'2012-10-17 01:27:01'),('3dff549d-ae61-48fc-a11d-db5afaf64e26','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','15212ba7-3a43-418b-a3b7-d8095c2b77ff',NULL,'2012-10-17 00:55:58'),('3e3d94ad-54db-4938-8615-e320c8debe85','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','d3e84d02-ec80-459d-bba2-2517d9732c3b',NULL,'2012-11-10 07:17:17'),('3ebb77ac-c953-4f50-af10-05df787490e5','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','54762702-b427-4b75-95a3-faa17a90d1b5',NULL,'2012-11-10 07:17:22'),('3ec20363-19b4-4cb6-9efc-209f7e0fcf9c','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','805785fd-4001-4ef7-9d10-1ef52fcbc9c0',NULL,'2012-10-12 06:10:31'),('3f0f9655-ad95-4a8d-a4b2-d14ddc2c34be','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','e0e6ab6e-dd08-4b2a-9267-8ed7500655f6',NULL,'2012-11-21 06:37:15'),('3f6b067a-4cb6-4136-9a66-bc07d76623f0','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','0d1861e3-823d-4508-8de4-26bae086efb2',NULL,'2012-10-17 01:18:20'),('3f8302e6-f952-442b-b6c9-7cc500cca045','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','37384927-53f5-4ac7-8425-94a110c081ce',NULL,'2012-10-17 01:18:14'),('3f9b0789-92a9-4c64-b359-13a22357202f','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','aa3515c1-bd03-4555-9e98-723ca14f74a9',NULL,'2013-01-05 07:02:50'),('3fce7420-7ba2-440c-8349-f469e8ba7cac','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','369eeb06-b520-4566-b9c5-1f7a2277a450',NULL,'2012-10-17 01:18:17'),('3fd44655-2050-4640-a42c-34215fabc3b0','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','bc6c161a-3f61-4890-9492-90ad25567b6b',NULL,'2012-11-10 07:17:18'),('4113a10a-4b0b-44dc-9938-d16db4c264da','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','573c6a20-1076-4234-a728-0bce8405f097',NULL,'2012-10-18 06:13:47'),('4140787d-230f-410d-9311-6fba190ba799','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','274e16c7-1b68-498b-8290-e339d4197136',NULL,'2012-10-18 06:13:40'),('414c044c-f6e0-44ec-bda6-ce002011b6c5','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','38903006-89e2-4d59-b0d5-dbf8614500c4',NULL,'2012-10-17 04:33:01'),('41d59166-c408-4a47-bb4d-79beafecc48a','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','1f968676-81a2-4cd4-8d3d-ee86342e5a57',NULL,'2012-10-17 04:32:57'),('43420e1a-9d5f-4451-a643-c55e11964d8d','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','7a171a9b-49d8-45c7-bb5f-52d9c3157de0',NULL,'2012-10-02 07:24:59'),('4373da33-9d64-43e8-ba28-8452acfc8c91','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','adfa26bc-e276-4d69-85f3-63c8bd046d3f',NULL,'2013-01-05 07:02:50'),('444f692f-a951-425b-a9c4-b3b08ec12dbb','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','4ae6d5e4-1cd1-481e-8469-eae98f9adb36',NULL,'2012-10-17 04:33:00'),('44640874-804b-43bc-8e23-02d80deaa7f5','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','baecde96-ebee-426c-bc77-3ec8dc653261',NULL,'2012-10-02 07:24:59'),('44ce3c60-c156-4f8d-856e-3b87d240e432','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','fa1980ea-578a-44f0-9cca-fd9b80f52af2',NULL,'2012-10-18 06:13:46'),('44e47868-729e-429d-81c1-3a6a937d80bd','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','473e1574-954b-4b54-a641-8dda3659ddc1',NULL,'2012-11-10 07:17:16'),('44fdc6fd-55e2-4df7-9299-43241a2e7afe','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','02246977-bef6-46da-98c2-de7522111899',NULL,'2013-01-05 07:02:50'),('450e0358-8a48-41f4-aed2-fe7c6efac1d0','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','d3e84d02-ec80-459d-bba2-2517d9732c3b',NULL,'2012-11-10 07:17:17'),('450e62c2-82fa-426c-b077-db22ff0d8d5a','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','dcd1f9aa-d544-4ff7-a90a-d7dafc162897',NULL,'2012-10-18 06:13:49'),('4525c5f0-7c2a-4f69-a922-1901ee79732e','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','48ddd2b7-7841-4731-a28b-5ebe84a0ff8b',NULL,'2012-10-18 06:15:42'),('45262c25-854d-4c50-a186-1b6f1967df82','27c2969b-b6a0-441d-888d-85292b692064','814febd9-945b-4580-8381-9b146fd042d9','6ba6316f-7372-4dfc-a573-9db78af2b3ed',NULL,'2013-01-05 07:51:02'),('46397729-1992-476d-92fa-755edf7d3c46','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','3dfac762-1463-4914-a5c3-00d751159b52',NULL,'2012-10-18 06:13:47'),('463a7e78-e0f8-4d0e-ba6c-2d4b02552b2c','27c2969b-b6a0-441d-888d-85292b692064','814febd9-945b-4580-8381-9b146fd042d9','4133c4d3-979c-4ef0-80f1-13ebc7553ac0',NULL,'2012-10-02 07:24:59'),('463bd1c9-7726-4d19-9d8f-c7bf7d1a456d','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','d5719ffd-c663-44dd-946e-0818c31629a6',NULL,'2012-10-17 04:33:02'),('464faad7-eab7-45cc-aee7-ca062aab7b0c','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','ab17d28b-3f9a-45e2-b6da-fb19cf759c13','4bcc5156-f3c7-4a5f-a99d-006eb61c4ba1',NULL,'2012-10-18 06:13:31'),('46559f01-6955-4592-a978-7ebdb86ebbeb','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','8f063f36-f64c-479e-b37a-2491e99feb6a',NULL,'2012-11-21 06:37:15'),('46c8bf07-677e-4888-8be3-a831e26a15a1','3141bc6f-7f77-4809-9244-116b235e7330','e96b787d-57f6-41a4-9693-d0634bfacacb','f1abb661-2418-4d3a-8d41-aef1a0f57bdc',NULL,'2012-10-18 06:13:37'),('46dfdd44-8724-46a9-ad58-5217185791cb','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','976dc56c-c92a-4253-ac88-f69cae251794',NULL,'2012-10-17 01:27:01'),('478b514d-2d99-4acb-8447-16c401088179','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','74455ff3-647d-4d29-a71a-be28fa97a289',NULL,'2012-11-21 06:37:15'),('47a296c9-096c-4311-90f4-b79f57e0b739','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','ab17d28b-3f9a-45e2-b6da-fb19cf759c13','c8e9d29a-13ca-4f03-9d2c-5834667f54ea',NULL,'2012-10-17 01:17:58'),('48023675-bce9-4b18-8081-ea17f0484c23','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','b50f44c8-1d3d-4d0b-b17d-9ab0005ee224',NULL,'2012-10-17 01:26:59'),('480f84db-255d-4553-9c2a-8d2c1ce42ffe','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','e5b5b610-e9dd-471f-b2c9-08455a23ddfe',NULL,'2012-10-17 00:55:59'),('48454fb9-60a0-49bf-86b1-32dd8f3fc8e2','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','c0971b7e-06eb-4d4b-856b-5ffb7d2843d6',NULL,'2012-10-17 01:18:13'),('48742063-b3d8-4609-9b42-2be13b0cadb6','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','c82bc722-a02c-42d5-b894-e45baa4badb2',NULL,'2012-10-16 04:30:49'),('4888a9a7-27a9-4ee2-b12e-68c687e0e5d4','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','61d45a28-5271-48e8-810d-c83b4bf6b24f',NULL,'2012-10-18 06:15:43'),('48c3b3b2-283f-40a6-85d5-a97257967897','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','73e92bb3-23a0-4fc7-a7e6-4da8b0984506',NULL,'2012-10-18 06:13:34'),('490d983d-4653-408d-920a-cc048b2933da','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','257c711a-6d38-4b8c-8c54-6c25dc5a4d43',NULL,'2012-11-10 07:17:34'),('4ad74917-0ee9-4243-87a5-5f445cf7ba22','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','83e32dd9-e3ac-4953-8f8e-756ac56d4480',NULL,'2012-11-10 07:17:35'),('4b55ad67-39b5-44d9-b40b-4c4c2eb16fb4','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','5e7fbaac-1441-46e6-aedf-c65763e9fbf1',NULL,'2012-10-18 06:13:47'),('4bb853a7-51ce-4796-bfad-da4d2cd9b9ef','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','aa9e07d4-53ce-45c0-b72f-dfbbcb40829f',NULL,'2012-10-02 07:24:59'),('4bc08d71-e06a-4b0b-bc08-636227bd061e','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','c458b319-6779-4426-a1a1-b01c0cb46194',NULL,'2012-10-17 01:18:18'),('4bfc19f3-4e92-406d-a9a0-7fbcd98c41f3','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','8cacccb8-d2d5-46d4-a3f5-805ae1fc68cd',NULL,'2012-11-10 07:17:23'),('4c3b0fd8-2a9a-45eb-9317-7d815ae681c9','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','c219f204-5418-4f45-b148-8216d2a15e88',NULL,'2012-10-02 07:24:59'),('4c62336b-4ca7-4ea1-ad12-d941689a3583','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','6ce73de0-e573-4f2f-9a54-28c440210c2c',NULL,'2012-10-17 04:33:00'),('4c711428-b84d-4f6a-82f0-46e30c351b67','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','7c5095aa-a9c0-46d7-a67d-e95d628edda8',NULL,'2012-10-17 01:18:02'),('4c802e9f-7e6e-4713-b1f8-a6ec0ad5408b','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','3b8ba73b-9cbd-4a6c-b9ae-6fbd9c5f5104',NULL,'2012-10-18 06:13:48'),('4cb85d5a-405f-4d14-bb1c-f8deede43397','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','e5ebd621-9c56-4ab6-a698-75a89b044102',NULL,'2012-11-10 07:17:19'),('4d021b90-14ae-44be-8ae3-e460b024f17a','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','2aab64ea-9bbd-4715-a6bf-ad3ee5ea7072',NULL,'2012-10-16 04:32:22'),('4d3af35e-e99e-40ff-bf39-8dca1f3b5452','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','4953b2ca-d1f2-4905-a8d4-bf95abd54930',NULL,'2012-10-17 00:55:59'),('4d40d067-9e3c-400f-8d19-4ce6a1b53d16','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','99818e34-6fe4-455c-81df-cca3b0e70932',NULL,'2012-10-18 06:13:47'),('4d782998-67e6-44cb-a289-1765453e7d33','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','a38ef10a-1dab-4643-b93c-dec589951c70',NULL,'2012-11-10 07:17:28'),('4ef49c18-149c-4f57-b6a3-072be12c32f8','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','41f460dc-3272-497f-bed4-77e67d459612',NULL,'2012-10-17 00:55:52'),('4f0e84bb-cdf1-454c-b5a2-5bed2308c19b','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','ff33f793-b071-4362-a13d-18225470ea98',NULL,'2012-11-10 07:17:21'),('4f399515-126c-4e88-a5ac-12ceb6c8b8b7','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','20109b15-39c8-4d10-bac5-617ae2850e22',NULL,'2012-10-17 04:32:58'),('4f7134c4-387f-4a5b-a1fa-077dc92954ea','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','86ce68f3-b264-4e49-a44e-c58144703700',NULL,'2012-10-17 01:18:16'),('4fa368dc-ceaf-4261-a5cc-c04f10bb76c5','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','66be432b-20a6-4dbc-8d52-5857f559c40d',NULL,'2012-11-10 07:17:21'),('5015627d-ee04-4a4e-8b1e-91d1b43cb00e','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','9f97ee78-98ae-4a3e-9989-50ca598fee65',NULL,'2012-10-17 01:18:01'),('50282008-83ba-49c1-8c81-488f75d88969','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','9ff3de86-38cd-4c4a-816d-048f95ec4f51',NULL,'2012-10-02 07:24:59'),('51a6067a-7e58-475d-9e75-0a4a354a9cea','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','0205b77b-4396-4931-95b8-d896a123b0e0',NULL,'2012-10-02 07:24:59'),('528790b9-61ac-4666-a6d3-d0fd6061f8ef','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','1d283460-e24c-4cbb-8476-2a66df560456',NULL,'2012-10-17 00:55:52'),('541e68b2-068a-40c0-a2e7-947633c8ae86','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','40f0c13e-f8ef-4a50-94cd-f1a2d95ca886',NULL,'2012-10-02 07:24:59'),('542c8529-ce29-48bf-9799-b3b34625c2ff','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','e8e6949d-f0ae-4001-b8d0-91bfd45a1b47',NULL,'2012-10-18 06:15:36'),('544aec1b-2162-409d-92fc-88dcbde575eb','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','bc9e9d1b-9112-4336-ac8e-c2c0b614777b',NULL,'2012-10-02 07:24:59'),('547fb52d-7f1f-46df-91a8-447265413577','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','9796ae62-3ee9-445c-a7d6-867fa5fae36d',NULL,'2012-10-02 07:24:59'),('54bdfb92-2060-485b-a338-95d5ef9ec02e','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','5f43abcb-ddeb-439a-a80f-fb62dd2f042e',NULL,'2012-10-02 07:24:59'),('54e0a7af-c438-493f-b656-fb9bb7212171','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','82f879a7-fdda-4b3f-a79f-55c1605a85ce',NULL,'2012-10-18 06:15:42'),('553ebe80-cb56-42c8-8573-882eac065c3a','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','d6d087d6-ace3-49ad-882d-67267cd29721',NULL,'2012-10-17 04:33:01'),('5553419e-974d-4f93-b0e5-f3703beec2a4','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','350b7514-7b5c-4b83-913f-b56122423dc5',NULL,'2012-10-16 04:30:50'),('55c7c9e2-ddd7-43f9-8b35-cbe8f6474774','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','67621d17-25ea-4cef-863d-1431b3946640',NULL,'2013-01-05 07:02:50'),('55e9b625-98d1-469c-bb2c-1554f0d779a7','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','cba89043-1ac9-4e3c-a140-2ac74377c3b0',NULL,'2012-10-02 07:24:59'),('56753555-594e-4149-a1e3-87f327b1a7ab','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','08255d3a-bd85-434e-97a8-14f98b0c4ec8',NULL,'2012-10-17 01:26:59'),('56c7a2f2-ad0f-465c-b671-5deb22a0d814','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','d2941b3f-38e2-491d-947b-f6e07802595c',NULL,'2012-11-21 06:37:15'),('578dac8c-6756-48ec-ae73-ec3cb421a9df','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','9c1ef1bf-7ac0-431e-bfaa-0490b199d2c3',NULL,'2013-01-05 07:02:50'),('5799f974-014c-42e9-8b19-9c406f9c2f54','3141bc6f-7f77-4809-9244-116b235e7330','e96b787d-57f6-41a4-9693-d0634bfacacb','e4f2ee35-9d5c-4d2a-a251-0bbe5979d6d1',NULL,'2012-10-17 01:18:15'),('57ccba32-331b-49a2-a5e9-0c25373b2d49','3141bc6f-7f77-4809-9244-116b235e7330','dc949cb7-ad68-44ba-a795-d2a43a18164f','ce25ed99-6365-4cc9-a8f7-1b0f8428d271',NULL,'2012-10-02 07:24:59'),('583fb1e2-d623-4f1d-9f3a-0c88cd6f128f','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','52b25353-f40f-453c-bb26-854d2cf29b7f',NULL,'2012-10-17 04:32:56'),('58a531b2-65ab-481b-83ef-a4a312c4dddb','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','58a160ea-d3a2-4bef-8238-ae36f4e9a013',NULL,'2012-10-18 06:13:34'),('58b5081d-4e2a-4f13-a675-63bc113ecbe9','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','e88a46ca-5372-4314-a51c-ec83ac9ef4b4',NULL,'2012-10-18 06:15:41'),('594ac222-1f9a-47d6-8677-2d0ab03adead','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','0cea8124-51f7-49ed-822a-b6b842673c62',NULL,'2012-10-18 06:13:35'),('5955d80e-ddba-46ec-9bfa-0bd772b86b8c','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','bc9e29c0-cf88-497c-a9da-76956e4dc08e',NULL,'2012-11-10 07:17:17'),('5996298f-f215-426c-973a-104e5b85078b','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','ce23cf3b-6ee1-4392-9d11-e088cde97864',NULL,'2012-10-02 07:24:59'),('59c59b7f-ff59-4f8d-bb69-c8f8d0885d4e','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','085d00ca-f8af-43e5-9ef9-6d21dec5aff8',NULL,'2012-10-18 06:13:41'),('5a1263eb-3af1-4da9-9bd2-4da7a684bc2b','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','b155afa2-038e-42dc-beee-f89e5a3ce41f',NULL,'2013-01-05 07:02:50'),('5a358ca3-9980-49e1-9fc8-e2adeaed85d1','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','d95f4962-a8cd-4357-a0bc-4f2ffd408f2d',NULL,'2012-10-17 04:33:05'),('5a99f3f0-7daf-43b0-9de4-69f625f998af','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','fbae3a6d-5b98-4974-888a-85bf48b793fe',NULL,'2012-11-10 07:17:16'),('5aac5ee8-f95b-4191-9fbc-0f65f2c26bae','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','16d9ada5-08b6-48cc-b4f8-723327803581',NULL,'2012-11-10 07:17:25'),('5ae7157f-867d-4223-9467-546bd307db8f','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','2797e915-0097-40f6-ba05-ecf94ab34dc5',NULL,'2012-10-02 07:24:59'),('5b1afdc6-4805-457a-8d4a-53df5fa730ba','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','454214be-1ae6-4bdc-8396-a4355bdbc618',NULL,'2012-10-02 07:24:59'),('5b99b744-22ca-4cd1-9186-a8df3773ee7e','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','9c1ef1bf-7ac0-431e-bfaa-0490b199d2c3',NULL,'2013-01-05 07:02:50'),('5b9a9b0c-2e79-4895-bcfe-f5238cb091b9','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','97f8b89c-5aed-4c41-97ec-72516fe0df51',NULL,'2012-11-10 07:17:33'),('5bbf5eb3-d70d-4a5d-863a-ec2c2e444271','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','03bbfaaf-124c-4454-8082-50e0b3d23e57',NULL,'2012-10-02 07:24:59'),('5bddccdd-5540-4585-9ce6-60b7cd3a377d','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','2bb14a8d-7eac-4c93-9060-726b4acefbc9',NULL,'2012-10-18 06:15:40'),('5be1ab97-a65a-4376-bb3d-0ea213f0264e','3141bc6f-7f77-4809-9244-116b235e7330','d91f0637-b2f6-4492-ac1b-23c155102a3e','808520c8-c588-40a0-9c32-8518110c0e33',NULL,'2012-10-17 01:18:18'),('5cfcaea6-ee58-4a13-9983-a99aa61b94c0','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','1984ade9-588b-4817-8460-9dfea763cd23',NULL,'2012-11-10 07:17:18'),('5d31fa7f-47dc-4c7e-94d3-eb449f3c2a60','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','315e3d93-22df-4993-955a-73d30a11f7d7',NULL,'2012-10-17 01:17:59'),('5d369df4-52f2-49c3-856c-791c67169423','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','dbad8e78-b7af-4039-8aa2-b501c42b7eeb',NULL,'2012-11-10 07:17:20'),('5d7ea504-091a-4f96-b821-324f1de67216','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','36e43181-0b2d-45d7-939f-491cdd04d7b3',NULL,'2012-11-10 07:17:32'),('5dfe9c1a-187b-4505-8b56-11b68e7bd214','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','34c42eda-b9e6-4a35-b483-a791bd04718d',NULL,'2012-10-18 06:13:43'),('5e9ffc6e-0457-4c3f-a877-49d5f387d36b','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','6a7c543e-1d68-4ab1-8ab0-4898d0d204e4',NULL,'2012-10-17 01:04:54'),('5ed5d7e4-fdd4-489c-86d0-16debcbea4b6','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','4953b2ca-d1f2-4905-a8d4-bf95abd54930',NULL,'2012-10-17 00:55:59'),('5eea2c0c-b01f-4229-b9f3-1a0362020b81','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','5c998398-3340-4d36-8b34-2cbe7eca1010',NULL,'2012-10-17 04:33:05'),('5f8bcd7b-98af-4a96-b019-0dbbf143b0c5','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','ab17d28b-3f9a-45e2-b6da-fb19cf759c13','4021ed21-c101-42d4-83a8-3ff714eccb17',NULL,'2012-10-17 04:32:56'),('602a3ecb-26b6-4ab6-ae79-ae29d489ad6d','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','8c334402-efc2-4585-958c-540cedf32936',NULL,'2012-11-10 07:17:23'),('61e20e1d-e2d9-4a73-9f20-49b15ef0effe','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','73d4a167-f8c1-4466-8f1e-10ce9ab0851f',NULL,'2012-10-12 06:10:31'),('6218ab72-3e73-4a1b-a0aa-1befa28f13b2','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','5c998398-3340-4d36-8b34-2cbe7eca1010',NULL,'2012-10-17 04:33:05'),('62223a4f-8e0a-47ee-8594-00ed642db598','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','b5d57def-9d34-4851-89f9-62b4646ad605',NULL,'2012-10-17 01:26:59'),('62467f2c-fa65-4013-80de-00aa13b937e7','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','31868203-8848-4644-92c7-27307c5e5ba0',NULL,'2012-10-17 01:27:00'),('629f5bb6-8890-4c18-8588-3791bc3b4e65','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','1d21c442-22b6-490d-b84a-07311da373c1',NULL,'2012-11-10 07:17:25'),('63259e8a-340d-4cc6-938e-a80abb1e645b','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','8c334402-efc2-4585-958c-540cedf32936',NULL,'2012-11-10 07:17:23'),('63526eab-1461-4131-817c-1547717255ef','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','b95d9944-b9f3-4dba-bd83-86c6fe3a9ca0',NULL,'2012-11-10 07:17:35'),('636adabf-5df8-4ce0-9d99-e90bc4ba1eff','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','daf70773-a281-4620-9c75-73350150512e',NULL,'2012-11-10 07:17:24'),('63907c8a-51e9-467e-b6f2-c88cca86565f','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','5ebc8427-124d-4615-83fb-4326749745e3',NULL,'2012-10-12 06:10:31'),('63b28ee7-4015-4bc9-ae2a-672dff655a4d','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','d407dc6e-52ae-4fec-a1f6-9708d87af938',NULL,'2012-10-18 06:15:40'),('6415ac63-aed2-45ef-9ed7-4bef5c161f81','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','8612b1de-9825-4cb9-b9bb-0b3b97bbe6b6',NULL,'2012-11-10 07:17:19'),('64c560dc-16e2-4102-b260-730b2c84e7ef','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','35b9e3c0-9670-4775-9e9a-cf56d973440b',NULL,'2012-10-18 06:13:38'),('652ee2f3-4b3d-4f6e-a1f4-58852f7495c7','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','62be2403-d1ee-4d70-bab4-28bfd161f8e0',NULL,'2012-11-21 06:37:15'),('65835b3f-6ea9-47d6-90b4-5ea44586ad26','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','4953b2ca-d1f2-4905-a8d4-bf95abd54930',NULL,'2012-10-17 01:18:19'),('66121dd1-57e5-4661-bec6-092d3cc01b56','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','48ddd2b7-7841-4731-a28b-5ebe84a0ff8b',NULL,'2012-10-18 06:15:42'),('6633308c-3533-4e7b-97d2-13aa9efbb347','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','a0af674f-4e72-496b-bf4e-4f126eab7372',NULL,'2012-10-18 06:13:32'),('665fe589-6851-4f33-8dba-70cfedfe2b91','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','4bbcd493-2035-4567-80e8-218dd24201e7',NULL,'2012-10-18 06:13:48'),('66a6c2a4-4c79-4fb7-b23d-749798937a49','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','2c0fbd65-f7ba-452d-bd93-a40b371e2a68',NULL,'2012-10-18 06:13:33'),('67614a14-9944-43aa-9b4e-3a1880bd02b4','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','45984389-977d-4064-8d46-c0df6e791880',NULL,'2012-10-19 05:57:13'),('67daa613-04d4-4075-81f9-50e5b4179afe','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','9621ea57-5c9c-48dd-8a7f-1a7dc3f7e107',NULL,'2012-11-10 07:17:22'),('68bf2dfb-2cad-4aac-ba39-7989039dc75e','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','1567b10b-1474-4df5-a6df-ce3b8a92ee97',NULL,'2012-10-19 05:57:04'),('6977f221-ce07-4309-b36d-b9ddd8c1ee39','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','9f11626e-4664-49d6-9604-5c2ee9f4f299',NULL,'2012-10-02 07:24:59'),('69ec4a3a-e424-4b7b-b523-9997afd1e947','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','afea3d12-379b-47ef-a395-0e162e37d90b',NULL,'2012-10-17 01:18:16'),('69efca90-9c54-4118-bb7c-e9596d6d43df','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','15aceaf6-cf02-4aab-baeb-0f2402e41125',NULL,'2012-10-17 01:26:59'),('6a21f45c-f7fa-4e0a-af7b-67a45505a9b0','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','3cdd3f9e-a093-4c0d-8aef-ef7ba86f765b',NULL,'2012-10-17 01:18:20'),('6a7b9fa2-d15a-4189-86a3-6f320efa052d','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','1d283460-e24c-4cbb-8476-2a66df560456',NULL,'2012-10-17 01:27:00'),('6aa46d42-d1ed-4071-9790-885597ec92e3','3141bc6f-7f77-4809-9244-116b235e7330','e96b787d-57f6-41a4-9693-d0634bfacacb','4953b2ca-d1f2-4905-a8d4-bf95abd54930',NULL,'2012-10-17 01:18:19'),('6b0ad244-494d-4438-a2d9-06f02d87755e','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','3ffcbbac-7b49-433b-8ffa-275ff81aba00',NULL,'2012-10-17 01:26:58'),('6b1fe249-95d6-41e9-a716-b8064c6b4ed8','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','268c57bc-edfc-4da7-b01a-c1ebb2438022',NULL,'2012-10-17 04:32:59'),('6b56c9af-5540-424e-b2b1-a82fff2f51ec','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','5f46c34d-7a26-47eb-b977-1be976205050',NULL,'2012-10-18 06:15:41'),('6c0d6cf5-59bb-45ba-b95b-5cca3a7e3084','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','01a2674c-850c-46ab-a768-e8593baf9404',NULL,'2012-10-02 07:24:59'),('6c30a13c-e23d-4814-aaa8-f7d877f6509c','3141bc6f-7f77-4809-9244-116b235e7330','a74cbffb-281b-49ee-8e96-fbac2ab54785','5fc9e48c-f29d-4a98-b381-a8cccafc02af',NULL,'2012-11-10 07:17:29'),('6cb9fe9c-9c8c-47d2-aa22-c8f5059bf5fc','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','24237983-b248-4d0d-b1d2-04ca8e469198',NULL,'2012-10-18 06:13:36'),('6d1652e4-8dcf-498d-b9c8-e05bd32cbcb0','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','085d00ca-f8af-43e5-9ef9-6d21dec5aff8',NULL,'2012-10-18 06:13:42'),('6d3dc4c6-dd9c-48d3-af79-e6fbfb1e9c33','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','3ffcbbac-7b49-433b-8ffa-275ff81aba00',NULL,'2012-10-17 01:17:59'),('6d790cb1-d7cd-48a6-a05e-746890064618','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','454214be-1ae6-4bdc-8396-a4355bdbc618',NULL,'2012-10-02 07:24:59'),('6dbfd612-300d-4ee1-8db5-cc0bed7f7f9b','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','573c6a20-1076-4234-a728-0bce8405f097',NULL,'2012-10-18 06:13:48'),('6eb28044-6258-4319-8f68-88f92b3fc1a6','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','78640195-eaae-405f-b3bc-304b806977f9',NULL,'2012-10-18 06:13:34'),('6f2afda5-503c-4db3-8cdb-4ebadedfe1a0','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','e10c64e0-105a-44f5-83da-a4a10379d802',NULL,'2012-10-17 04:33:04'),('6f2b67e5-1658-49a3-83a3-776e54771c8b','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','833952ec-7936-4a3e-979a-a2b705c31a9d',NULL,'2012-11-10 07:17:14'),('6ffb8bd2-c682-4089-b5ca-44d218438d4a','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','7a0ab3c1-8ab0-4aac-b064-bff7e6e88f64',NULL,'2012-11-10 07:17:22'),('700d305a-b0bf-4c08-89e2-1cfec77219f2','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','fa1980ea-578a-44f0-9cca-fd9b80f52af2',NULL,'2012-10-18 06:13:45'),('710f4f1c-7bba-47b2-935e-1dafa8c8b101','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','84bcc45a-f840-4645-9ae3-1f480ab74589',NULL,'2012-10-17 01:04:54'),('720d2a8f-dddf-4f1f-8771-438a55d4bab2','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','599cb14f-dbc5-4f45-8e94-24fdb881fbbf',NULL,'2012-10-18 06:13:41'),('725b81c8-db6e-4452-980c-63262d2f8efa','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','23e376ee-554b-4ac0-b34a-86031f77e526',NULL,'2012-10-16 04:30:50'),('72fcc6a6-bd10-4ae8-866e-57e44038fcbc','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','db10b9f5-1c41-4638-bf7b-e8f5c214903c',NULL,'2012-11-10 07:17:26'),('731047c5-7220-43c9-b600-af12df014d65','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','931ba3fb-e82b-467a-a349-0320feac9a97',NULL,'2012-10-17 01:04:54'),('7330137b-0bd4-4ceb-a719-5f9da7fae2f0','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','a973a41b-d90a-4081-b0ac-6eeef52d2893',NULL,'2012-10-17 01:18:19'),('7332502a-063b-4045-b939-5f4bc033099e','3141bc6f-7f77-4809-9244-116b235e7330','a74cbffb-281b-49ee-8e96-fbac2ab54785','d80f1e2e-a75b-4492-8f7b-e541e3980fb7',NULL,'2012-10-18 06:15:37'),('73b34d01-545c-4986-bb6f-d9bc6ce0f0c7','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','eb216a3d-72a4-459b-8686-6aebab26bbb7',NULL,'2013-01-05 07:02:50'),('73b48315-54c1-43aa-b15c-799da16825b1','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','1d283460-e24c-4cbb-8476-2a66df560456',NULL,'2012-10-17 00:55:52'),('75337495-ea0c-40e7-affd-65a41b4d7422','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','4ae6d5e4-1cd1-481e-8469-eae98f9adb36',NULL,'2012-10-17 04:33:00'),('7551672d-ecbc-4001-9771-472f57c07745','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','d4e1477a-ed22-48a3-b1f0-751e5bfa6b5a',NULL,'2012-11-10 07:17:21'),('75839015-87e4-43ea-a5bd-7cb209e62abb','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','37384927-53f5-4ac7-8425-94a110c081ce',NULL,'2012-10-17 01:18:14'),('7613db8a-279c-4e77-a8b8-e74f4b14864d','3141bc6f-7f77-4809-9244-116b235e7330','a74cbffb-281b-49ee-8e96-fbac2ab54785','b8668b4d-abbf-4a4e-8646-0640f2baa787',NULL,'2012-10-17 01:17:59'),('76b943d1-e2ed-419c-a9b9-f6b09e34efc3','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','5c998398-3340-4d36-8b34-2cbe7eca1010',NULL,'2012-10-17 04:33:05'),('76ce950e-8fd3-42fb-941a-f0910d188c63','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','86ce68f3-b264-4e49-a44e-c58144703700',NULL,'2012-10-17 01:04:55'),('7731ac3d-b1e8-4b24-ae40-dd630c43da27','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','4953b2ca-d1f2-4905-a8d4-bf95abd54930',NULL,'2012-10-17 01:18:18'),('7758daa3-cfa0-4cd3-abab-10f126b3e1fa','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','9a8285cc-f383-4a1b-bf64-60749f065603',NULL,'2012-10-17 04:32:59'),('7773bda3-05aa-4cc7-bf8c-2e63f2800e3d','3141bc6f-7f77-4809-9244-116b235e7330','e96b787d-57f6-41a4-9693-d0634bfacacb','62be2403-d1ee-4d70-bab4-28bfd161f8e0',NULL,'2012-11-21 06:37:15'),('7789250a-49dc-4233-870e-e5f4b40e349e','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','9621ea57-5c9c-48dd-8a7f-1a7dc3f7e107',NULL,'2012-11-10 07:17:23'),('782ccbeb-ddc2-4b77-86a0-a712fbc108b7','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','62be2403-d1ee-4d70-bab4-28bfd161f8e0',NULL,'2012-11-21 06:37:15'),('784d480e-f192-4a5b-82c0-9fe500a5f788','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','95d261ff-13dd-4c42-8c83-f69a7b1cd6ba',NULL,'2012-10-18 06:15:37'),('785bdc45-a27f-4deb-a090-26fd8af8211e','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','b8b6dfa3-beca-4667-a279-cf78d610a0b6',NULL,'2012-10-17 01:18:13'),('789fbc83-04cd-41b8-a20c-bfcbc9758106','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','2bb14a8d-7eac-4c93-9060-726b4acefbc9',NULL,'2012-10-18 06:15:39'),('78ac25a2-276c-45fc-a87f-e6687907e8dc','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','065a6379-da8d-4d90-8a6e-8286b31e850f',NULL,'2012-10-18 06:15:38'),('78ea0176-8554-4d38-94d4-3729ef01aac6','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','27f6b2b1-0c93-4b79-9f2e-7f10639d8987',NULL,'2012-10-18 06:15:36'),('799465c6-5e3c-4afa-a95c-b9933be613fe','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','e793325f-df0f-4e64-9ef9-cf1b08bf29ee',NULL,'2012-11-10 07:17:17'),('79bd22a6-7eb5-4d30-a51e-c08f257a7ca5','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','f991f719-4fe8-43d7-b032-2e4a52de8d53',NULL,'2012-10-17 01:18:02'),('79f676b5-e64e-4bc9-84bd-ca2d664790df','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','aa9e07d4-53ce-45c0-b72f-dfbbcb40829f',NULL,'2012-10-02 07:24:59'),('7a12451e-1e50-4871-bf2d-aed0a92da31b','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','02246977-bef6-46da-98c2-de7522111899',NULL,'2013-01-05 07:02:50'),('7a17e715-d160-43a1-b492-6cf0a49c32fb','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','e7cb481d-e08b-4389-9819-e6bd6e12295c',NULL,'2012-11-21 06:37:15'),('7a5d8438-1d85-4714-8142-91fd1a8fdd6b','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','d3e84d02-ec80-459d-bba2-2517d9732c3b',NULL,'2012-11-10 07:17:17'),('7a7aaeb3-f59a-48f9-9da0-086caff8a206','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','d189342e-a551-4f77-a45c-6f3482e3dce3',NULL,'2012-10-17 01:26:59'),('7ae413f2-e1a2-4afd-bcd6-b471038fe4a7','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','84c9fb23-9a6c-4fad-832e-0cc3de838d70',NULL,'2012-10-18 06:15:37'),('7b22508c-172c-4520-83ce-e34bcc728083','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','5cb9e13e-9abe-4258-9c94-f79c6136f577',NULL,'2012-10-18 06:15:41'),('7b4db543-c2c6-455a-a27d-64e50bb88bdf','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','61629eaa-2dd4-47d9-9f7b-3ff94db4482a',NULL,'2012-11-10 07:17:32'),('7bdcee53-08ef-468a-9603-f70858d71ae5','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','875a88e8-3fd0-48e9-a184-5f62459a468f',NULL,'2012-11-10 07:17:16'),('7be5ab1c-c69a-4fe5-a48a-23dfcde99dbc','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','e342d908-5dd2-4ea3-9291-288339829a8c',NULL,'2012-10-18 06:15:40'),('7bf74d74-6b37-44fc-aa0d-e70f39158d74','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','976dc56c-c92a-4253-ac88-f69cae251794',NULL,'2012-10-17 01:27:02'),('7c59dc04-a096-47ea-975f-8ddf97bd4106','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','99818e34-6fe4-455c-81df-cca3b0e70932',NULL,'2012-10-18 06:13:47'),('7c6158a7-fc1d-4820-9fbe-e8c40ac6bac2','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','53b462db-42b5-41e6-94fa-a98974bcfd5b',NULL,'2012-10-18 06:13:39'),('7cfd956a-85ac-45c5-b8e2-f8a6fdff2648','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','23e376ee-554b-4ac0-b34a-86031f77e526',NULL,'2012-10-16 04:30:50'),('7d34971a-ac4d-422c-b80d-f75faba289c2','5934dd0b-9f7c-4091-8607-47f519f5c095','299390fd-24f2-4ed5-9bb6-e270b9e2f64f','e45b3f8d-6116-48a5-b671-031cc0f9ab6b',NULL,'2012-12-07 03:33:04'),('7d55b95f-d506-4f56-b1be-31a7942ad2b6','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','62be2403-d1ee-4d70-bab4-28bfd161f8e0',NULL,'2012-11-21 06:37:15'),('7d5a141f-7ea2-433f-9a6e-d51df5a48803','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','badbed99-3113-45e7-bf87-40bf5354f88d',NULL,'2012-10-02 07:24:59'),('7db3c2ca-bb41-4bdb-9631-a9f8eb38498d','3141bc6f-7f77-4809-9244-116b235e7330','d91f0637-b2f6-4492-ac1b-23c155102a3e','065a6379-da8d-4d90-8a6e-8286b31e850f',NULL,'2012-10-18 06:15:38'),('7dc7f5b1-1558-4947-a0de-07ba8f2669d4','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','3cdd3f9e-a093-4c0d-8aef-ef7ba86f765b',NULL,'2012-10-17 01:18:20'),('7e295ddf-5ac1-49a1-a81d-93768eba98bc','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','b7996d48-fbd6-4f41-9b0e-30ab67feb511',NULL,'2012-10-18 06:15:35'),('7e5a43cd-87f6-4256-a61e-9553b5edb4a0','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','6ce73de0-e573-4f2f-9a54-28c440210c2c',NULL,'2012-10-17 04:33:02'),('7e64dfb8-4f3a-49fb-8810-a0c0dfa110fa','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','8f34c0fe-0d12-4dda-bef0-fb952990cb71',NULL,'2012-10-12 06:10:29'),('7e7e0805-6316-4ff7-81d8-1552bf6863ff','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','4bbcd493-2035-4567-80e8-218dd24201e7',NULL,'2012-10-18 06:13:48'),('7ea41d67-c5f4-4cde-b99b-bb4f14877088','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','4ac74345-b1d9-47d8-bed7-3f41b2bdbc36',NULL,'2012-10-02 07:24:59'),('7f34ef0a-c49b-4bb8-aad2-f472d2b33c17','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','fb5fca7c-0a75-4a94-8dc2-0dba6d66747f',NULL,'2012-10-17 01:27:01'),('7f475cbd-17db-4fc3-9a1a-6957279ece01','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','92ba8d67-0170-4f39-8f29-316b44522112',NULL,'2012-10-02 07:24:59'),('7f4b32a1-4339-4e37-b874-b4b96bd341d5','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','fb5fca7c-0a75-4a94-8dc2-0dba6d66747f',NULL,'2012-10-17 01:27:01'),('7f6c3a67-e3ae-488e-b3a5-61cb21db6225','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','5df9d93b-5e4d-41a3-89b5-301bf009ee9a',NULL,'2012-10-18 06:13:47'),('7f7c6c85-ce1f-48da-a641-dd77c3346ee9','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','1f8df48d-ce76-49e4-80ed-cc0d0ce65d2a',NULL,'2012-11-10 07:17:14'),('7f94adce-caaa-403a-ba0e-6140aac65231','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','d407dc6e-52ae-4fec-a1f6-9708d87af938',NULL,'2012-10-18 06:15:34'),('7fb7335a-c72a-4bc9-aeeb-eca362adb4e7','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','adac68c6-1483-430c-ab2e-b594660ae4d6',NULL,'2012-10-12 06:10:29'),('800d0249-e5ed-4606-b215-9209743a9ae1','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','18d00a54-6b22-4434-8563-e66f80431e9d',NULL,'2012-10-02 07:24:59'),('8032c5df-f2ea-49f5-866c-582b5067b787','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','6f4e7eff-7204-468e-bfb2-2943688d282f',NULL,'2012-10-17 01:17:59'),('8077515b-71dd-4741-9e57-34a49a647abd','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','99c1ab3a-dc50-4186-8828-df989f8d3d50',NULL,'2012-12-19 06:54:45'),('815c3275-8053-4784-878c-6b3083dac4b2','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','68d7dce7-17c4-4407-b648-83efa8870d3e',NULL,'2012-10-17 01:26:58'),('8228c808-5068-4da1-813b-529cbedc54b4','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','4021ed21-c101-42d4-83a8-3ff714eccb17',NULL,'2012-10-17 04:32:56'),('8233e840-0d1f-466c-ac11-5d59be1e2a4d','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','f869b28f-c712-48b5-b095-27dc19c94d09',NULL,'2012-10-18 06:15:34'),('82782038-7472-4566-8afd-fcbd090226bb','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','1b801662-0446-4bf5-bbd5-b093b27a1cef',NULL,'2012-10-17 01:18:02'),('8295d964-8c51-4742-810c-ee685424ca76','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','7abfb7c5-3f55-466e-bd43-1f886e6a884c',NULL,'2012-11-10 07:17:13'),('82b25d8c-da57-431d-818d-45acd9f2d7ad','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','4690e672-6b5f-42ac-9cf8-d0a3bafeeaa9',NULL,'2012-10-17 01:26:58'),('82bc13e8-96c4-4ff8-a91e-86bafb60068e','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','06b8d06a-aac4-4d09-b0b4-fe67d70faf14',NULL,'2012-10-18 06:13:45'),('8309ec2e-4f6d-4351-a85b-0e4a24f0ca23','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','b83c3a3d-7feb-4677-b38c-1ecd5118c3f4',NULL,'2012-10-18 06:13:40'),('831508b9-a42d-467e-9128-12dfed902cc2','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','57eeece7-6b2a-4dad-9684-b05c96a57988',NULL,'2012-10-17 01:18:18'),('83528ac8-3b2b-449d-82ee-b48fc43540f3','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','6c1064bb-f40f-4413-84e5-d40270f6c96b',NULL,'2012-10-17 01:04:54'),('83b534da-5d4b-4783-9907-1dd566b12a57','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','19d7d449-99c3-4a3d-98cb-428665cf25d1',NULL,'2012-10-02 07:24:59'),('83cfd228-6b80-4bda-84ab-2becc2798182','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','bc9e29c0-cf88-497c-a9da-76956e4dc08e',NULL,'2012-11-10 07:17:17'),('8428ee12-91dd-463a-863a-139907a14ed6','5934dd0b-9f7c-4091-8607-47f519f5c095','dd4b3428-134f-4e51-870c-69e71ad58a21','4818b5a3-6975-483a-b2a2-7be4de3fbc31',NULL,'2012-10-02 07:24:59'),('843872a2-e687-42aa-a8f2-2e6b8ed23ebb','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','0205b77b-4396-4931-95b8-d896a123b0e0',NULL,'2012-10-02 07:24:59'),('84ee6558-a351-4214-8e5d-8a07209b17ae','3141bc6f-7f77-4809-9244-116b235e7330','b3b3b726-acef-4d58-82e4-ef8875d654ce','574474c0-6023-4a99-ac26-301495d79194',NULL,'2012-10-02 07:24:59'),('855e353e-d026-4068-a849-417550806f05','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','cc23bcb7-221b-4ef0-98ab-40726b5aee4b',NULL,'2012-11-10 07:17:29'),('85d38dce-c306-42f3-83ab-a5e5ae54310b','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','ab17d28b-3f9a-45e2-b6da-fb19cf759c13','833952ec-7936-4a3e-979a-a2b705c31a9d',NULL,'2012-11-10 07:17:14'),('867dfbfd-4ed0-4042-bee0-389acfb07d03','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','52b25353-f40f-453c-bb26-854d2cf29b7f',NULL,'2012-10-17 04:32:56'),('86b36fe7-0342-416b-9d0d-816e403f23be','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','983636ce-8bfb-4e06-b5f5-a2994af8ccc9',NULL,'2012-10-18 06:13:38'),('86b858d8-b867-4846-b3ff-83e2e141e967','3141bc6f-7f77-4809-9244-116b235e7330','a74cbffb-281b-49ee-8e96-fbac2ab54785','e5b5b610-e9dd-471f-b2c9-08455a23ddfe',NULL,'2012-10-17 01:18:19'),('86d587ed-9c3b-473d-988d-0692788d33e8','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','420813eb-03e3-4a3a-97ca-84894f08af88',NULL,'2012-10-17 04:33:00'),('86fdabeb-2b7c-4b3e-9315-033bf1aead11','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','35b9e3c0-9670-4775-9e9a-cf56d973440b',NULL,'2012-10-18 06:13:38'),('8732c0d6-d9a2-4625-9153-572a3fc2df16','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','bbfbbed7-11fa-4cb1-9819-89284ff7d80a',NULL,'2012-10-17 00:55:51'),('8789ab6d-424a-4a4f-852f-f927ca0fcff6','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','cba8d14c-a8a8-4676-b1d2-3cd726528825',NULL,'2012-11-10 07:17:22'),('87c0e805-c71a-4083-91d8-e04b9d1936bc','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','53b462db-42b5-41e6-94fa-a98974bcfd5b',NULL,'2012-10-18 06:13:40'),('88d63709-5154-40b0-8363-3677ae51952e','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','369eeb06-b520-4566-b9c5-1f7a2277a450',NULL,'2012-10-16 04:32:21'),('8902304b-c1a0-486e-a3a6-684699312536','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','4d2748e9-7233-4106-b7a1-83e7cfdbff24',NULL,'2012-10-18 06:15:39'),('894d9942-b6ee-48ad-8811-e7d2daf6be4c','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','3ffcbbac-7b49-433b-8ffa-275ff81aba00',NULL,'2012-10-17 01:17:59'),('89fa08d3-c8aa-4735-a314-0f94cd9c68a8','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','c285590a-914f-4c36-a0a2-624fa25d028d',NULL,'2012-10-18 06:13:48'),('8a24dd43-e75e-4829-a57a-12d30fb75ab3','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','f1abb661-2418-4d3a-8d41-aef1a0f57bdc',NULL,'2012-10-18 06:13:37'),('8af04291-77e8-4302-9558-c36ef7c30b93','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','3dfac762-1463-4914-a5c3-00d751159b52',NULL,'2012-10-18 06:13:47'),('8c9509c1-4426-4280-8eb7-252e23341734','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','ed5607ba-10de-4e96-902b-8eaf4dacf249',NULL,'2012-10-18 06:15:34'),('8ce7afbc-0a52-4a9f-b982-123fafddfe61','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','b8863dbf-f999-4c0f-889f-ccc18b6a6e71',NULL,'2012-10-17 00:55:53'),('8d1333d4-48d2-4a26-81db-aef054f3d900','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','1ded295a-3065-4da7-8ba7-42edef36a1db',NULL,'2012-10-18 06:13:29'),('8d5a91d4-b3fd-4d3d-8575-633eca54e2af','3141bc6f-7f77-4809-9244-116b235e7330','a74cbffb-281b-49ee-8e96-fbac2ab54785','16d9ada5-08b6-48cc-b4f8-723327803581',NULL,'2012-10-17 04:32:59'),('8d5f2a73-fe1a-4fdf-9251-ac7577098245','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','7a0ab3c1-8ab0-4aac-b064-bff7e6e88f64',NULL,'2012-11-10 07:17:22'),('8d76bbc0-8f45-4682-b422-f9d1e9215706','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','adfa26bc-e276-4d69-85f3-63c8bd046d3f',NULL,'2013-01-05 07:02:50'),('8d7c66be-3913-457a-ab7d-6fe5229f4876','3141bc6f-7f77-4809-9244-116b235e7330','d91f0637-b2f6-4492-ac1b-23c155102a3e','5abb795d-02e4-4d63-87cb-61fbc7b70be8',NULL,'2012-10-18 06:13:37'),('8d7e8180-ff06-43d5-8404-be25fe7cdc55','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','a30af6e0-195d-46a8-a46a-cc1682d04896',NULL,'2012-10-17 01:18:19'),('8d95f8e4-abd7-45b4-9279-caec11d2291a','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','b8863dbf-f999-4c0f-889f-ccc18b6a6e71',NULL,'2012-10-17 00:55:51'),('8dd465b8-f3ee-43f9-9bc9-ad44d802435d','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','5c4a8eeb-3536-4b43-8eb7-33ee6472c63c',NULL,'2012-10-18 06:13:39'),('8e2237ca-1677-4982-9fe3-e3b06920a393','3141bc6f-7f77-4809-9244-116b235e7330','a74cbffb-281b-49ee-8e96-fbac2ab54785','41f460dc-3272-497f-bed4-77e67d459612',NULL,'2012-10-17 01:18:01'),('8f3aab8c-ca7d-4f67-bdc2-d6c2e6807255','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','aaf970b7-31ef-40f0-a493-187298baa1b1',NULL,'2012-10-17 04:33:02'),('8f5d3548-057c-49ec-bc6f-51ff26f45643','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','08255d3a-bd85-434e-97a8-14f98b0c4ec8',NULL,'2012-10-12 06:10:30'),('8fa93b16-0532-4a92-b4dd-13cddbaaf34f','3141bc6f-7f77-4809-9244-116b235e7330','d91f0637-b2f6-4492-ac1b-23c155102a3e','5cad3529-600c-4b7f-aa68-375a5dc9912e',NULL,'2012-11-10 07:17:20'),('8ff223d0-208a-4bd7-a52e-8ad0837f432c','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','3bc97a96-a507-4c9c-8033-d7f212a9badb',NULL,'2012-11-10 07:17:16'),('8ffbae3e-24fa-460e-840a-e7407cc3684b','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','288aaae9-9513-4bf3-99c3-b5fd26ed85af',NULL,'2012-10-02 07:24:59'),('8ffbaeb3-87ea-404a-808f-37fa585cc092','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','54e14752-4a89-413a-9ac1-dbb519f61e2a',NULL,'2012-11-10 07:17:31'),('90305915-e27f-4372-b469-d7a97d60bbb3','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','480b7fb6-f10b-4631-97f4-f40e0385cdca',NULL,'2012-10-18 06:13:39'),('906de2a3-d112-474e-b0cf-1e5139067aa2','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','0c2ad7a0-83b5-4f83-aed9-ee6ad99510b3',NULL,'2012-10-18 06:13:34'),('90844def-e35d-4e1d-a231-a1be5485ba59','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','e10c64e0-105a-44f5-83da-a4a10379d802',NULL,'2012-10-17 04:33:05'),('911e3eae-1005-4a88-b324-4c6a2656bbda','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','1fd534f6-139f-41ed-80dd-2b6b7fee6768',NULL,'2012-11-10 07:17:27'),('913ef119-d8fa-4e18-884b-4d5bbddf69c7','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','2797e915-0097-40f6-ba05-ecf94ab34dc5',NULL,'2012-10-02 07:24:59'),('919b855f-c89a-4d09-85d7-1cc9da7b977f','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','573c6a20-1076-4234-a728-0bce8405f097',NULL,'2012-10-18 06:13:34'),('921d12ba-37b9-4e64-bd4a-04b8c2759ca4','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','1db0ee9f-a668-45f2-ad79-50e395a9e171',NULL,'2012-10-12 06:10:30'),('92a0b44c-4fb4-4c2f-8490-23365e578974','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','06e64ff4-3de9-40f6-a9c0-bcf0e5aba5c4',NULL,'2012-10-17 01:26:57'),('92ea14a6-3ef1-4546-9ec6-eebafb5a7205','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','4bbcd493-2035-4567-80e8-218dd24201e7',NULL,'2012-10-18 06:13:48'),('92f35636-3c2a-4894-8e4b-123d06082829','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','4bc4b72c-8183-4a14-8066-47d7b0d2a827',NULL,'2012-10-17 04:33:02'),('931afde0-8a80-481e-940b-985f53719108','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','07e990d9-5b60-4cb1-8e83-40e25dcdcdae',NULL,'2012-10-02 07:24:59'),('93ae048b-fce9-4f35-8ecf-3392ae80e3d3','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','23dca992-ea19-4731-8e96-20739509851c',NULL,'2012-11-10 07:17:15'),('93aecac3-9ab6-4b40-a579-2a9229cab628','3141bc6f-7f77-4809-9244-116b235e7330','e96b787d-57f6-41a4-9693-d0634bfacacb','123481b3-aca7-41ec-97d7-7e6601395d4a',NULL,'2012-10-17 04:33:01'),('9449ad16-3528-476e-9fe8-eafd9d72b5cf','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','074d4c3a-c9d7-4751-b2eb-72e0cb45f69c',NULL,'2012-10-17 01:18:14'),('95225644-6afb-41c1-a6d8-619c44bef8cc','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','5ebc8427-124d-4615-83fb-4326749745e3',NULL,'2012-10-12 06:10:29'),('9542abc3-b611-42c7-aca3-e9ab5bd0ebd4','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','2c3058ab-a357-4c26-9157-8f11028e63bb',NULL,'2012-11-10 07:17:14'),('95a93190-8a9f-41a8-acb9-b817b27024b4','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','66be432b-20a6-4dbc-8d52-5857f559c40d',NULL,'2012-11-10 07:17:21'),('95e74733-a04b-4551-8cef-7d8a4313e13f','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','6a939182-d012-48ce-b495-5648eaf06e1c',NULL,'2012-11-10 07:17:33'),('95e87a08-4c4c-4497-b1e3-465ea36fbfce','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','1887c956-1e50-4bae-8cb5-3a5a68d2ef80',NULL,'2012-10-16 04:30:49'),('9600b136-6028-44ce-972d-81de005c690f','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','c8e9d29a-13ca-4f03-9d2c-5834667f54ea',NULL,'2012-10-17 01:17:58'),('96f4d696-d2e6-4b99-8102-6cb8667cca18','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','7efc3875-3bd2-4615-9b1f-83d7fc8131d0',NULL,'2012-11-10 07:17:32'),('972d0dcb-2f51-4c4b-b903-61c1f5d88b43','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','860d9543-4d04-4be9-9fed-ff1e35648d28',NULL,'2012-10-17 04:32:59'),('97807527-189b-4aaf-b928-8a601c2ecaae','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','17d1046c-71bd-4c90-9291-6744e5f2fa38',NULL,'2012-10-18 06:13:42'),('97852b80-4746-4384-b8c7-5890d7157971','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','a3f41b86-152e-4feb-878a-c83d486b57cf',NULL,'2012-10-16 04:32:22'),('9889648d-1508-406c-87d5-c4fd8c79c14e','3141bc6f-7f77-4809-9244-116b235e7330','90db809f-268c-4b26-988b-b44c103ac655','8ef30615-ebf0-4877-a521-64457b598158',NULL,'2012-10-02 07:24:59'),('989e5ac0-1a3b-4639-9162-98b78c56a2fe','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','02246977-bef6-46da-98c2-de7522111899',NULL,'2013-01-05 07:02:50'),('98bdbcfb-cac3-490f-a6e0-d6a9626456bc','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','d4e1477a-ed22-48a3-b1f0-751e5bfa6b5a',NULL,'2012-11-10 07:17:21'),('98bdd72a-4867-4a3c-b3e8-f7e620e5be8c','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','dc4be07b-03cc-4d6d-95e5-2bd6f49b8b8b',NULL,'2012-10-17 01:04:55'),('98ec9e0a-3a33-4900-95a2-e7f79aecef01','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','f86d6829-9a80-4348-9869-36b11517e74e',NULL,'2012-10-18 06:13:48'),('9938ac11-b96e-4a97-be3f-8a80d11d49f3','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','aaf1a292-b2e7-4624-8119-a6805bc6702c',NULL,'2013-01-05 07:02:50'),('99a966d7-6195-488c-83e3-a42bf27479e1','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','61137b8e-803f-4f04-835c-1718670cebc1',NULL,'2012-11-10 07:17:24'),('99ae41cc-3f84-4fd8-9465-10a6577098ae','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','a6a4884a-f8e5-4909-905b-df8825040cef',NULL,'2012-10-17 01:27:01'),('99bf3316-dbc8-4a4e-9c5a-11d3164f8026','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','369eeb06-b520-4566-b9c5-1f7a2277a450',NULL,'2012-10-16 04:32:22'),('9a69467b-7510-453f-879d-e95bf305d6a9','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','0a01c5ed-7e05-4dc8-8082-48fe61bab13d',NULL,'2012-10-18 06:15:40'),('9ad92801-31f8-4c30-a429-1f6262804f69','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','9548e247-c91d-4e39-99b1-26456cc7ce22',NULL,'2012-11-10 07:17:34'),('9b8248ba-9b7b-4965-aa73-9a82b15a957f','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','7abfb7c5-3f55-466e-bd43-1f886e6a884c',NULL,'2012-11-10 07:17:13'),('9bbcac01-e742-43bb-877e-a2935411c886','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','cabd97ec-12fc-442a-9952-54a9250c6e8c',NULL,'2012-10-17 01:18:16'),('9c1aac62-f712-44a6-9fec-37618ad4f409','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','08255d3a-bd85-434e-97a8-14f98b0c4ec8',NULL,'2012-10-17 01:17:58'),('9c1c9864-8e5a-40e9-8465-2b2fd8847fad','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','59160936-d126-49a2-a275-86f9359c0e89',NULL,'2012-10-18 06:15:34'),('9c5fa409-5302-49e2-be7a-296f681ed8ba','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','57eeece7-6b2a-4dad-9684-b05c96a57988',NULL,'2012-10-17 01:18:18'),('9c73f3ff-3ab6-4165-ae86-967746076dc3','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','9086cec8-a28e-4723-9f31-e2e68e173c6a',NULL,'2012-10-02 07:24:59'),('9ca85780-974a-417b-a399-295420c0cff8','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','d72473f6-d239-448b-acfc-c3f177f65aa2',NULL,'2012-11-10 07:17:18'),('9cf741ad-1cce-4b8d-aa60-35303be10b7b','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','799d35d1-0125-4d6b-8e92-df7cad508426',NULL,'2012-10-17 01:26:58'),('9d19cccd-6447-4b6d-ba3d-d9bdf7c2f62a','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','bfefbf36-c506-403d-896f-3a2eac356e0c',NULL,'2012-10-17 00:55:52'),('9d28cda9-eb95-48b9-81be-b88841712fb1','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','c687dcf9-2069-4027-8c00-98f4b66fc2e2',NULL,'2012-11-21 06:37:15'),('9d4cb0ce-21b2-4049-926e-dd84934a0734','3141bc6f-7f77-4809-9244-116b235e7330','d91f0637-b2f6-4492-ac1b-23c155102a3e','eb216a3d-72a4-459b-8686-6aebab26bbb7',NULL,'2013-01-05 07:02:50'),('9dea4de3-96bb-40c2-bdf7-67714b3298e5','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','daf70773-a281-4620-9c75-73350150512e',NULL,'2012-11-10 07:17:24'),('9e35f2bf-b56e-4786-9d8c-65942555dbad','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','6ce73de0-e573-4f2f-9a54-28c440210c2c',NULL,'2012-10-17 04:32:59'),('9e69143b-1773-46ce-8630-859a438823d7','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','57eeece7-6b2a-4dad-9684-b05c96a57988',NULL,'2012-10-17 01:18:18'),('9e6921a1-cde7-47dd-b1a5-37df8aef29f2','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','1d283460-e24c-4cbb-8476-2a66df560456',NULL,'2012-10-17 01:18:01'),('9e75e80a-b70e-42ca-9a38-d3bc3f5d2e04','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','e2e6315f-7469-410d-8532-628d933f1508',NULL,'2012-10-17 04:33:02'),('9f732852-d9d1-4f5a-abb5-0358290a6abe','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','62be2403-d1ee-4d70-bab4-28bfd161f8e0',NULL,'2012-11-21 06:37:15'),('9fff3cb0-d49e-4115-b9bd-05ffb554f2e5','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','9e55df2f-6153-4d2b-a156-863b1c0148e2',NULL,'2012-10-17 00:55:52'),('a000b0b0-244b-4ee1-9972-51f04044e490','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','2898de0d-0bae-45ec-a915-05d92d1df637',NULL,'2012-10-17 01:26:58'),('a00b22f1-a5f4-474f-b77b-910f3a7fe8d0','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','78640195-eaae-405f-b3bc-304b806977f9',NULL,'2012-10-18 06:13:34'),('a0c5c2bb-e678-49d9-9833-7918f11d6b73','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','ab17d28b-3f9a-45e2-b6da-fb19cf759c13','7ff7e6cf-5a65-4e2f-9fc3-6f702374b470',NULL,'2012-10-02 07:24:59'),('a0ec60dc-8b77-4b91-9b80-106911995d01','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','03bbfaaf-124c-4454-8082-50e0b3d23e57',NULL,'2012-10-02 07:24:59'),('a13b4500-a4b3-4991-937c-1d6f84933f84','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','7b2ab804-b73e-4700-96ca-61ebc9c3576e',NULL,'2012-10-18 06:13:35'),('a13c324a-44f6-49d0-ade7-5f55efc9eea3','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','8aa97c5e-1cf5-4b1b-a20f-58e21598a5fd',NULL,'2012-10-17 01:26:59'),('a17c0167-739c-4636-8b66-e3ad0a52c7e5','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','01c97b84-76e0-4b94-9e3b-1e670bd9468b',NULL,'2012-10-17 01:18:13'),('a1f88ffb-d7ad-4a93-8505-fe07d26b09dd','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','931ba3fb-e82b-467a-a349-0320feac9a97',NULL,'2012-10-17 01:04:55'),('a24e0f96-36f8-4ba0-ac52-6f55fd81841c','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','c24abbeb-3318-4ec0-ab40-f272a26800ad',NULL,'2012-11-10 07:17:31'),('a2cf25ba-366e-41b2-8afd-2a13e8d9936c','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','62be2403-d1ee-4d70-bab4-28bfd161f8e0',NULL,'2012-11-21 06:37:15'),('a350ec04-198e-4a46-a125-519ff1e3d868','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','19d7d449-99c3-4a3d-98cb-428665cf25d1',NULL,'2012-10-02 07:24:59'),('a3b4f130-b4be-49ee-a871-40dc9f7a8aee','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','ab17d28b-3f9a-45e2-b6da-fb19cf759c13','62be2403-d1ee-4d70-bab4-28bfd161f8e0',NULL,'2012-11-21 06:37:15'),('a4cf9433-b0b5-4891-a16b-a96d6d279eef','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','4e5c2d3d-0802-432b-9e52-2a98e8e18e8d',NULL,'2012-10-18 06:15:39'),('a52df418-89b7-4f86-bc3f-d29827a16492','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','881214c5-639f-4a87-b4a5-3c8b6b8436d2',NULL,'2012-10-02 07:24:59'),('a593ec92-eacd-4587-8cf3-770a9226cd6f','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','268c57bc-edfc-4da7-b01a-c1ebb2438022',NULL,'2012-10-17 04:32:59'),('a5badc7d-c8cf-4e74-998e-652e8fc8dced','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','f86d6829-9a80-4348-9869-36b11517e74e',NULL,'2012-10-18 06:13:48'),('a5d862d5-125d-4e5a-8b2f-7e5716201f15','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','e01e5dc0-37da-451a-9644-22309e5cb21e',NULL,'2012-10-17 00:55:53'),('a5f835e6-c6d8-4ba6-a08a-5f1bf523313d','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','03bb2a7e-47bd-44fa-ab1d-797370bb1ead',NULL,'2012-10-16 04:32:21'),('a5fe454a-70a6-4461-8fe4-a660f04887d9','5934dd0b-9f7c-4091-8607-47f519f5c095','a90025a4-9e8b-44b9-a267-9580c7c5460b','c5e493af-195e-4b64-9331-4f716a16eac9',NULL,'2012-10-02 07:24:59'),('a66dc580-3107-4655-b0ab-9cbf84240847','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','b8863dbf-f999-4c0f-889f-ccc18b6a6e71',NULL,'2012-10-17 00:55:53'),('a6cec38d-8488-46d0-908a-a1e846df388d','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','33da3559-86b0-4db0-8e3f-7339155cc559',NULL,'2012-10-17 00:55:59'),('a6d1191b-d805-4f92-be3d-f9412bc4a970','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','5df9d93b-5e4d-41a3-89b5-301bf009ee9a',NULL,'2012-10-18 06:13:47'),('a7fd0033-ad64-45a1-8398-1cb02056526b','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','4aa31d07-d726-499f-b3ec-afe2995abedb',NULL,'2012-10-02 07:24:59'),('a955ab0e-c1bd-4ea9-8e9a-f7493e2c14e9','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','c9d79a74-a58f-4c71-8977-1129c10f3a5d',NULL,'2012-10-16 04:30:50'),('a98f90c0-d274-4c38-9ac7-e260af25afb4','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','06e64ff4-3de9-40f6-a9c0-bcf0e5aba5c4',NULL,'2012-10-17 01:26:57'),('aa067fe5-ca47-4746-8103-d7c91356b881','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','30ad24b9-3d4a-4cf8-9986-f45c12210aea',NULL,'2012-10-17 04:33:03'),('aa27e81c-db91-4960-80ae-5ff6df3d2a99','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','1d283460-e24c-4cbb-8476-2a66df560456',NULL,'2012-10-17 01:27:00'),('aa3838ae-4612-46e7-bedb-aaec9478156e','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','1d283460-e24c-4cbb-8476-2a66df560456',NULL,'2012-10-17 01:18:01'),('aa9363e5-53b0-471d-84c0-7fb239263269','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','9a8285cc-f383-4a1b-bf64-60749f065603',NULL,'2012-10-17 04:32:59'),('aac646d7-b643-4655-93c0-921ca7d3bfba','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','931ba3fb-e82b-467a-a349-0320feac9a97',NULL,'2012-10-17 01:04:54'),('ab4b4d8a-6aa8-4b5e-81b7-7455c94c527e','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','0a01c5ed-7e05-4dc8-8082-48fe61bab13d',NULL,'2012-10-18 06:15:40'),('ab7de0db-a13c-4f61-9084-29b6547f53fe','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','b47ed36b-9e71-4516-ba3a-cb4c91718d8c',NULL,'2012-10-17 01:18:17'),('ab9df566-7bd4-46e7-a51b-eea22ededce9','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','eaf3f239-4e9b-42bd-b618-2cad78793b36',NULL,'2012-10-02 07:24:59'),('abd6d986-39a8-490a-bbb8-2e4a873a1a8e','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','4f982c49-6962-463b-ae20-1b5494fc7671',NULL,'2012-10-17 01:27:03'),('abf3fa20-8aa5-4a7c-9b4d-2fa8f30647d7','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','e4c15807-3cb1-4e86-a659-03b56b3e1fb5',NULL,'2012-10-02 07:24:59'),('ac5034fe-563d-41f3-b80f-b39258db5be1','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','84bcc45a-f840-4645-9ae3-1f480ab74589',NULL,'2012-10-17 01:04:54'),('acd78426-4224-4578-b2cf-976ad25ac788','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','ebc1e69b-0209-4abe-b22e-6d5660fab576',NULL,'2012-11-10 07:17:24'),('acf85022-3160-4f39-b4af-a2d2ee22edd0','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','cb2d39d3-9f00-41c1-b0ef-99d5c57a900c',NULL,'2012-10-18 06:13:40'),('ad35da19-6807-498f-afab-884ded63e4f9','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','4137d40c-0130-4277-b726-61bc76f4fdef',NULL,'2012-10-17 00:55:52'),('ad3e3a53-0cda-46f7-aa17-d5e3701cfb75','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','1d283460-e24c-4cbb-8476-2a66df560456',NULL,'2012-10-17 01:18:01'),('ad6105ef-ef6b-4a73-902b-1df9e80e837e','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','93020f07-2a16-450e-b0fc-e03446952c02',NULL,'2012-10-17 04:33:04'),('ad6cae68-4537-41b3-bc49-9e7c837ecc58','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','288aaae9-9513-4bf3-99c3-b5fd26ed85af',NULL,'2012-10-02 07:24:59'),('ad71dfd5-624d-4cde-90b6-f29a7698c965','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','a6a4884a-f8e5-4909-905b-df8825040cef',NULL,'2012-10-17 01:27:01'),('adcea1b8-1c31-4441-a924-b897831bd759','3141bc6f-7f77-4809-9244-116b235e7330','a74cbffb-281b-49ee-8e96-fbac2ab54785','00ee1251-c1ec-49cf-95e7-f0c037d23f43',NULL,'2012-11-10 07:17:30'),('ae99e470-5c3f-46c4-be27-8f6bf7d3e707','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','4953b2ca-d1f2-4905-a8d4-bf95abd54930',NULL,'2012-10-17 01:18:19'),('ae9e5663-5795-44c1-b03e-17e659638f61','3141bc6f-7f77-4809-9244-116b235e7330','a74cbffb-281b-49ee-8e96-fbac2ab54785','6a7c543e-1d68-4ab1-8ab0-4898d0d204e4',NULL,'2012-10-17 01:18:14'),('aef413ef-62d4-405a-887e-b6d0339e7ce9','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','bc9e9d1b-9112-4336-ac8e-c2c0b614777b',NULL,'2012-10-02 07:24:59'),('af752dec-a607-44e9-9dd9-c49ef0ec7e34','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','8533521f-82c8-408f-9499-194e45d50154',NULL,'2012-10-18 06:15:42'),('b03ae32f-dc8b-4365-84b8-5d2cda600f40','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','51f80b0a-7a4b-4c68-ac7a-b41ef9c9aaba',NULL,'2012-10-18 06:15:36'),('b04fae89-1307-4e35-aca1-b544bb76451c','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','35c9fd79-c9b4-4a0c-84dd-6a9a6a05453a',NULL,'2012-10-18 06:13:42'),('b05cf453-abb3-4fa5-b0e4-68f53cce95bf','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','5ebc8427-124d-4615-83fb-4326749745e3',NULL,'2012-10-12 06:10:31'),('b1a246ef-7739-4679-8264-c1e98c551760','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','67621d17-25ea-4cef-863d-1431b3946640',NULL,'2013-01-05 07:02:50'),('b2870513-62a2-4a8d-81de-5f1b88dbb6f1','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','983398c1-423c-4e88-99be-da3ad56d2d7c',NULL,'2012-10-16 04:32:21'),('b352b2e6-31eb-4a0f-8b2b-57805e210ad8','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','9548e247-c91d-4e39-99b1-26456cc7ce22',NULL,'2012-11-10 07:17:34'),('b3610e83-0b77-4d92-9388-a4697f78a341','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','4f982c49-6962-463b-ae20-1b5494fc7671',NULL,'2012-10-17 01:27:03'),('b38dc22c-8504-4b3e-afbd-bf6c818f6c3c','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','7b2ab804-b73e-4700-96ca-61ebc9c3576e',NULL,'2012-10-18 06:13:36'),('b3ae5bd3-88ad-4de7-b4c2-62d2c1eef9fd','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','369eeb06-b520-4566-b9c5-1f7a2277a450',NULL,'2012-10-16 04:32:22'),('b3c63fef-ee0f-4e46-9d2c-2b8d59d77630','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','34c42eda-b9e6-4a35-b483-a791bd04718d',NULL,'2012-10-18 06:13:43'),('b40db546-938e-449c-ac4d-b3c27b52780a','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','e549810e-8a45-4d3b-9f7a-97ce7f11e853',NULL,'2012-10-18 06:13:38'),('b440608f-fd3d-466c-8446-8fb3a217db56','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','a5cdb910-3eea-4f59-9319-d2c51b6658fd',NULL,'2012-11-10 07:17:35'),('b48a0068-5dc3-4454-8450-6bccd9c1ebce','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','e88a46ca-5372-4314-a51c-ec83ac9ef4b4',NULL,'2012-10-18 06:15:42'),('b4ab22b6-3829-49cc-ac2a-384fd1c31038','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','4c9c49f4-ca45-40bc-b7a2-119bfa76cf23',NULL,'2012-10-02 07:24:59'),('b4b7ab19-2836-46c1-a7f1-c36eb85daf48','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','f048d680-916b-4631-a19e-2b4c292f010e',NULL,'2012-11-10 07:17:18'),('b53321de-fb89-4112-a01a-4df6d522f4f2','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','d80f1e2e-a75b-4492-8f7b-e541e3980fb7',NULL,'2012-10-18 06:15:39'),('b57c0591-cb9e-4005-a05d-99bb3855e1f8','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','06e64ff4-3de9-40f6-a9c0-bcf0e5aba5c4',NULL,'2012-10-17 01:26:57'),('b5ab1809-a30d-4979-9911-f520b0b5f6e0','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','9862f025-b370-4ed0-96a1-0d81813ac256',NULL,'2012-10-02 07:24:59'),('b6430b4d-205e-4126-b515-be556d11160a','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','180a7231-0444-44c9-8371-81825a2be339',NULL,'2012-10-18 06:15:35'),('b6552d4d-d02f-41c1-acb2-ee099a9a02be','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','4fb85f9a-831d-49ff-a554-4c80a792232b',NULL,'2012-10-18 06:13:38'),('b6634d6a-c880-4a57-93f6-3c1006a949a1','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','a30af6e0-195d-46a8-a46a-cc1682d04896',NULL,'2012-10-17 01:18:19'),('b70dda0d-7d43-4b7e-911e-45ac833f0474','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','0d1861e3-823d-4508-8de4-26bae086efb2',NULL,'2012-10-17 01:18:20'),('b7817d7d-7cab-4873-bd36-1bf8de8f8811','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','b95d9944-b9f3-4dba-bd83-86c6fe3a9ca0',NULL,'2012-11-10 07:17:35'),('b7a2b291-33be-4f84-bda1-59fa6a905a02','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','3cdd3f9e-a093-4c0d-8aef-ef7ba86f765b',NULL,'2012-10-17 01:18:20'),('b7a59434-7159-4a17-874a-173e11c4ab32','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','26fcd232-8685-40ea-a1b3-e845662d9a51',NULL,'2012-11-10 07:17:20'),('b82aeea6-631a-412e-a159-baac1e50fc48','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','074d4c3a-c9d7-4751-b2eb-72e0cb45f69c',NULL,'2012-10-17 01:18:14'),('b8861735-8843-49ef-9608-32c47079421e','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','aaf970b7-31ef-40f0-a493-187298baa1b1',NULL,'2012-10-17 04:33:02'),('b8de84f3-62c6-4628-8225-36091f8c2530','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','8309414c-b081-4342-b20b-eea126a97e26',NULL,'2012-10-17 00:55:51'),('b938ed9b-fddb-4596-a586-0deb85a370a8','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','d6d087d6-ace3-49ad-882d-67267cd29721',NULL,'2012-10-17 04:33:01'),('b9f00571-24b8-4d3b-b9df-8953228b7265','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','0f42eabd-4da2-48ed-aa30-f76b1b6d94b7',NULL,'2012-10-17 04:33:04'),('ba04958b-c721-4efc-8c08-7dac2e5f2a16','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','1087d1e9-3dff-4b6e-b1e2-625141bbf814',NULL,'2012-11-10 07:17:34'),('ba0f52cc-7502-4440-907f-4a5b808b7c66','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','1887c956-1e50-4bae-8cb5-3a5a68d2ef80',NULL,'2012-10-16 04:30:49'),('ba171eaa-14c1-4ac5-b662-447a48a014fc','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','abc707f1-48f6-4e0c-acba-67c818ccdc85',NULL,'2012-11-10 07:17:28'),('ba8b4eb4-ed9f-4763-a88f-8d7cfcb258fb','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','1d283460-e24c-4cbb-8476-2a66df560456',NULL,'2012-10-17 01:27:00'),('ba8fc177-7253-4727-85bc-5f6122f36bab','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','ab17d28b-3f9a-45e2-b6da-fb19cf759c13','d407dc6e-52ae-4fec-a1f6-9708d87af938',NULL,'2012-10-18 06:15:34'),('ba939c9d-fc24-4df5-bf98-3f34c5d2071d','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','eaf3f239-4e9b-42bd-b618-2cad78793b36',NULL,'2012-10-02 07:24:59'),('baad37d3-ac4d-480c-8a87-de764937d7cc','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','931ba3fb-e82b-467a-a349-0320feac9a97',NULL,'2012-10-17 01:04:55'),('bab00a17-687f-4c0a-b0c6-b1c434d81b6c','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','7a171a9b-49d8-45c7-bb5f-52d9c3157de0',NULL,'2012-10-02 07:24:59'),('bab6a10f-fda7-4b4c-ab4c-bb8894056277','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','eb10b29a-689c-45ca-9ea5-d92c275d0258',NULL,'2012-10-17 01:26:57'),('bad7c18d-f4c2-4ad7-9496-16c9083e7bec','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','e342d908-5dd2-4ea3-9291-288339829a8c',NULL,'2012-10-18 06:15:40'),('bb5fce41-7926-4146-9e60-8c0cc7de92c2','3141bc6f-7f77-4809-9244-116b235e7330','d91f0637-b2f6-4492-ac1b-23c155102a3e','1b801662-0446-4bf5-bbd5-b093b27a1cef',NULL,'2012-10-17 01:18:02'),('bb6646cd-c75d-4c46-b5d6-521a05c0a612','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','92526fe4-93e8-48c0-ab29-be50bc832bf5',NULL,'2012-10-02 07:24:59'),('bb739d73-f628-41d0-ba7e-be12673c9d7c','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','ab17d28b-3f9a-45e2-b6da-fb19cf759c13','1f8df48d-ce76-49e4-80ed-cc0d0ce65d2a',NULL,'2012-11-10 07:17:14'),('bb7e0128-d894-43a8-8a35-8eb4c6b5cc99','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','44da7e78-68c9-41ca-8441-26f882a463c4',NULL,'2012-11-10 07:17:17'),('bbe339e6-723f-49c0-9538-e9f1ef4dd538','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','0cea8124-51f7-49ed-822a-b6b842673c62',NULL,'2012-10-18 06:13:35'),('bc1872de-9787-4cda-a863-61a7bd3aa21a','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','cabd97ec-12fc-442a-9952-54a9250c6e8c',NULL,'2012-10-17 01:18:16'),('bd369d95-b686-4904-9c28-5c5e04fc5e76','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','73e92bb3-23a0-4fc7-a7e6-4da8b0984506',NULL,'2012-10-18 06:13:35'),('bd723b9f-8ad3-47b8-8a35-19861cbea436','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','0f919e47-d406-4e9c-96c2-0a372c818f73',NULL,'2012-10-19 05:57:15'),('bda4d788-69d5-43b1-8188-c55f9dff6504','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','f869b28f-c712-48b5-b095-27dc19c94d09',NULL,'2012-10-18 06:15:34'),('bde6aea4-0f8c-4bf6-bcb2-e8f89c3b1d03','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','62be2403-d1ee-4d70-bab4-28bfd161f8e0',NULL,'2012-11-21 06:37:15'),('be46024e-11ed-437a-bb7d-ec80ef7f3a7d','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','061ec789-c1e9-40af-83b4-a6b0897267d8',NULL,'2012-10-18 06:15:39'),('beafae6c-f9ad-4d67-98ce-095b5d3784ae','3141bc6f-7f77-4809-9244-116b235e7330','a74cbffb-281b-49ee-8e96-fbac2ab54785','b89940da-f3c3-4dad-9eb6-86bd2ba380ab',NULL,'2013-01-05 07:02:50'),('bfd892ba-e785-46a9-9f92-179f24f09db4','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','f991f719-4fe8-43d7-b032-2e4a52de8d53',NULL,'2012-10-17 01:27:00'),('bfda4a32-cc8b-4e1b-8fe9-6dcf708ffdd7','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','ee907131-8b73-4dab-9978-97fa820b01fe',NULL,'2013-01-05 07:02:50'),('bfe09702-16b1-4103-8012-57d1872855c7','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','af5504e8-0bdb-4e97-8dea-08892fb928ca',NULL,'2012-10-02 07:24:59'),('bfe1189d-0afc-4cdb-9750-c281353583a5','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','8d8ad28c-4bf2-4258-a7f1-d2c04df7b125',NULL,'2012-10-17 01:17:59'),('c002a501-dde7-4476-b3ec-dcecb1d76305','3141bc6f-7f77-4809-9244-116b235e7330','a74cbffb-281b-49ee-8e96-fbac2ab54785','32846451-1dbe-44c0-b599-cfd3bdc73b0e',NULL,'2012-11-21 06:37:15'),('c0c3378e-7388-4403-94b9-af48845a7424','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','54762702-b427-4b75-95a3-faa17a90d1b5',NULL,'2012-11-10 07:17:22'),('c0d6df16-b9ff-476e-ad3c-c2be65e57f01','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','061ec789-c1e9-40af-83b4-a6b0897267d8',NULL,'2012-10-18 06:15:39'),('c0db6f3e-abcf-4c24-b200-e4f35b75611d','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','ab17d28b-3f9a-45e2-b6da-fb19cf759c13','4953b2ca-d1f2-4905-a8d4-bf95abd54930',NULL,'2012-10-17 01:18:18'),('c0ffb52b-c1a1-4fa1-86f7-905a71601aab','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','9118731f-b1bb-46ed-9d6c-1d070a5e7bf6',NULL,'2012-10-18 06:13:36'),('c116bdf2-eeaa-4772-88da-a0cd4fa58579','3141bc6f-7f77-4809-9244-116b235e7330','a74cbffb-281b-49ee-8e96-fbac2ab54785','4988d669-fe70-404c-8e0b-c297d2eb9ad2',NULL,'2012-11-10 07:17:30'),('c168bcbb-f9dc-42ab-8605-364ed5d06697','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','705e9509-6393-481f-8dd5-f4753abcfead',NULL,'2012-10-17 01:26:57'),('c186d9a0-8e1b-4530-b945-4da2f1963b2a','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','8a0cc11d-d1ec-4e0d-a895-ac037c9d94f8',NULL,'2012-10-02 07:24:59'),('c2042398-40ae-4a77-838b-32774f5f0250','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','9f97ee78-98ae-4a3e-9989-50ca598fee65',NULL,'2012-10-17 01:18:01'),('c214459a-095a-4ff9-9e05-d48ddb730381','3141bc6f-7f77-4809-9244-116b235e7330','e96b787d-57f6-41a4-9693-d0634bfacacb','ff33f793-b071-4362-a13d-18225470ea98',NULL,'2012-11-10 07:17:21'),('c23c04e8-8ccb-4ea6-b7a3-98ffc6a0f2b8','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','62be2403-d1ee-4d70-bab4-28bfd161f8e0',NULL,'2012-11-21 06:37:15'),('c3931afc-6cf7-4f95-b8f5-811174b816b1','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','a14579f5-81f1-4d0e-b1f2-a63c7c0c94af',NULL,'2012-11-10 07:17:26'),('c3a0e55e-49ed-4c62-a84e-cffa71c3fcd8','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','51f80b0a-7a4b-4c68-ac7a-b41ef9c9aaba',NULL,'2012-10-18 06:15:42'),('c3daaccc-dc33-4813-93b3-87e3c69fd328','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','4aa31d07-d726-499f-b3ec-afe2995abedb',NULL,'2012-10-02 07:24:59'),('c467fbbc-3145-4094-82f7-637d4e27c056','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','31a79d37-ac13-4d6f-ba7d-49c9493959c9',NULL,'2012-11-10 07:17:20'),('c583a438-c961-48f3-8606-d3b85ff3b153','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','ee907131-8b73-4dab-9978-97fa820b01fe',NULL,'2013-01-05 07:02:50'),('c5aa0e46-4a34-4cea-b021-94a9c79bc8a8','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','31868203-8848-4644-92c7-27307c5e5ba0',NULL,'2012-10-17 01:27:00'),('c5b5141d-b229-417f-a948-1f0113d76ef1','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','0d2065a9-dce6-41d9-8488-e71063bb0e0e',NULL,'2012-11-10 07:17:28'),('c5d76260-9d73-4b57-a9da-7a2264319f4f','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','5abb795d-02e4-4d63-87cb-61fbc7b70be8',NULL,'2012-10-18 06:13:37'),('c5deed01-1463-46f5-ad82-cffd66a825e7','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','c79e680d-ce6f-44bb-aa6f-91ac2e94dedb',NULL,'2012-10-19 05:57:13'),('c5e10a73-d033-4510-bd75-51461fa03133','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','dcc00ef2-f22c-4796-87e5-f8b728a6a0dd',NULL,'2012-11-10 07:17:20'),('c61b4aec-a187-4c3f-a761-46bda76c7606','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','7ff7e6cf-5a65-4e2f-9fc3-6f702374b470',NULL,'2012-10-02 07:24:59'),('c636c83e-8daa-4871-945d-6745bbb82084','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','24b9aa78-7055-4776-910d-da124b1863f9',NULL,'2012-10-18 06:13:41'),('c66a7567-aaf9-4a20-a34f-c149809c82c6','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','085d00ca-f8af-43e5-9ef9-6d21dec5aff8',NULL,'2012-10-18 06:13:42'),('c67a474d-a5db-4ab3-8eaf-979bdb514b5b','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','4bc4b72c-8183-4a14-8066-47d7b0d2a827',NULL,'2012-10-17 04:33:00'),('c6bbb8a8-4496-476a-8954-36c0ad1949b5','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','08255d3a-bd85-434e-97a8-14f98b0c4ec8',NULL,'2012-10-17 01:17:58'),('c7363744-e8ec-46a5-ae0c-1134b3a069db','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','799d35d1-0125-4d6b-8e92-df7cad508426',NULL,'2012-10-17 01:26:58'),('c7d6dae6-83af-42d6-8200-528a6316020d','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','9ff3de86-38cd-4c4a-816d-048f95ec4f51',NULL,'2012-10-02 07:24:59'),('c8b9dc7b-f8ec-442f-a17a-892c5af9f999','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','08255d3a-bd85-434e-97a8-14f98b0c4ec8',NULL,'2012-10-12 06:10:30'),('c92bd606-edf8-4be3-93b3-f4719ab1c338','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','e2e6315f-7469-410d-8532-628d933f1508',NULL,'2012-10-17 04:33:02'),('c9637721-c282-47ab-99c3-2e6547bfe484','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','40e2177a-2219-445b-bd97-56f2ebacfe62',NULL,'2012-10-18 06:13:37'),('c99fae8d-bb56-4a10-a833-2b2b90f6f64e','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','983636ce-8bfb-4e06-b5f5-a2994af8ccc9',NULL,'2012-10-18 06:13:38'),('ca0bae49-550f-4482-aca1-0c568980f356','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','0ce828c7-e884-40be-9a6a-51b53cb349c5',NULL,'2012-11-10 07:17:19'),('cb133f47-bac5-4975-be2d-cc7822227f0c','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','5b3a71d1-5a3b-46a4-a558-a785a10ca262',NULL,'2012-10-17 04:32:58'),('cb354ef7-0331-4d79-b532-dec5ced387b0','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','4d82500c-bfb5-4ddd-bb9e-40733cbe72be',NULL,'2012-10-02 07:24:59'),('cb73efed-848e-4fcd-ab84-58726ddf27bb','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','07e990d9-5b60-4cb1-8e83-40e25dcdcdae',NULL,'2012-10-02 07:24:59'),('cbde7ffb-360c-44a9-8ef0-ed85eb30d687','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','b8863dbf-f999-4c0f-889f-ccc18b6a6e71',NULL,'2012-10-17 00:55:51'),('cbf632b8-6d02-40bb-a5ff-c3d7943693fa','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','9e55df2f-6153-4d2b-a156-863b1c0148e2',NULL,'2012-10-17 00:55:52'),('cd3f01b6-132c-4287-9c6a-0a845b5732ca','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','cba8d14c-a8a8-4676-b1d2-3cd726528825',NULL,'2012-11-10 07:17:22'),('cd8545e4-ed0b-42b1-8913-fb6cde0d5079','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','ec0a162a-90d3-4731-90c8-6c7ef3396f5c',NULL,'2012-10-17 04:32:57'),('ce11dddf-9c9a-4b43-bc74-86de7a53a07b','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','9e447d46-e845-4abb-b562-e73b3a716ab4',NULL,'2012-10-18 06:15:35'),('ce9c443a-2d51-494e-a296-939722833b5d','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','369eeb06-b520-4566-b9c5-1f7a2277a450',NULL,'2012-10-17 01:18:17'),('ce9dca63-c4e2-463d-bae7-81af2b497c2f','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','c458b319-6779-4426-a1a1-b01c0cb46194',NULL,'2012-10-17 01:18:18'),('cef67349-f2c5-4ac0-83a7-38eff130cb4e','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','fa1980ea-578a-44f0-9cca-fd9b80f52af2',NULL,'2012-10-18 06:13:46'),('cef70ced-3b1b-4b51-a2cd-14ce3ef7c892','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','5f43abcb-ddeb-439a-a80f-fb62dd2f042e',NULL,'2012-10-02 07:24:59'),('cf9ba72d-e353-4416-a8f8-2197047b211c','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','fd394578-5b76-4eb9-b40f-f116ec11a09e',NULL,'2012-11-10 07:17:26'),('cfb3ecf0-991a-4f81-b40a-b86fcc4229e1','3141bc6f-7f77-4809-9244-116b235e7330','d91f0637-b2f6-4492-ac1b-23c155102a3e','a973a41b-d90a-4081-b0ac-6eeef52d2893',NULL,'2012-10-17 01:18:19'),('d00f34f9-fa7a-4978-9bc1-e92c2f648c71','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','b339460f-c9b6-43a2-a78d-9d8aaea9c8f2',NULL,'2012-11-10 07:17:27'),('d03df513-e933-4137-a32b-e207ec8c9680','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','8c334402-efc2-4585-958c-540cedf32936',NULL,'2012-11-10 07:17:23'),('d09d91a5-f9b1-4862-ad4f-789bf785be43','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','61d45a28-5271-48e8-810d-c83b4bf6b24f',NULL,'2012-10-18 06:15:43'),('d0a16995-1f00-4f05-8848-8baad8b23aba','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','e88a46ca-5372-4314-a51c-ec83ac9ef4b4',NULL,'2012-10-18 06:15:42'),('d1a62f4a-e954-404b-b491-b62a4869d11f','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','d95f4962-a8cd-4357-a0bc-4f2ffd408f2d',NULL,'2012-10-17 04:33:05'),('d23d5976-f590-49fa-8660-43f89d5f49cd','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','a974079d-0865-49ef-afd8-0ec91174d323',NULL,'2012-11-10 07:17:14'),('d24828bc-d3a5-4f63-b3c7-7c4bd29e51e0','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','7a171a9b-49d8-45c7-bb5f-52d9c3157de0',NULL,'2012-10-02 07:24:59'),('d264d6ba-bf94-4522-9c94-461d8a195167','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','833cc7c2-c155-45b6-b927-98fdb20ae86f',NULL,'2012-10-17 01:04:54'),('d307a274-1781-416b-ada6-b9716b41f39f','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','e8e6949d-f0ae-4001-b8d0-91bfd45a1b47',NULL,'2012-10-18 06:15:36'),('d3937b4b-389e-4cd4-b644-57a45e3f1676','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','cb2d39d3-9f00-41c1-b0ef-99d5c57a900c',NULL,'2012-10-18 06:13:40'),('d3ffc829-3264-4f2d-869e-a7ac245f7043','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','5b3a71d1-5a3b-46a4-a558-a785a10ca262',NULL,'2012-10-17 04:32:58'),('d410d9b1-94b1-48f5-9efd-672e546121d4','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','3651eff9-0c05-4077-8f26-a9dfb915465a',NULL,'2012-10-17 01:18:17'),('d4180635-3669-415e-966b-ecfb8dc0b653','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','2c2c85ee-1437-4beb-82fb-0c7fca51c49a',NULL,'2012-11-10 07:17:33'),('d4bf7f40-d875-4187-9b92-c8d6bc6c9644','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','3b8ba73b-9cbd-4a6c-b9ae-6fbd9c5f5104',NULL,'2012-10-18 06:13:48'),('d4c57ce9-edce-418f-8465-4849ffe951f6','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','5cad3529-600c-4b7f-aa68-375a5dc9912e',NULL,'2012-11-10 07:17:20'),('d59994f3-e547-4103-a5c1-96a97868ff1c','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','9a8285cc-f383-4a1b-bf64-60749f065603',NULL,'2012-10-17 04:32:59'),('d65f17cf-adee-46db-ae60-bddf3db29ee0','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','420813eb-03e3-4a3a-97ca-84894f08af88',NULL,'2012-10-17 04:33:00'),('d7254336-83ed-4c89-a4af-cee2fe43027f','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','257c711a-6d38-4b8c-8c54-6c25dc5a4d43',NULL,'2012-11-10 07:17:34'),('d7b00caf-3014-4a41-8446-883d01c38cb5','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','06b8d06a-aac4-4d09-b0b4-fe67d70faf14',NULL,'2012-10-18 06:13:45'),('d841335c-ce92-43ec-8579-b164cbfd4b4e','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','66120b2d-0c89-4304-815b-6c8cec94647b',NULL,'2012-10-17 01:27:01'),('d8720dc1-3c04-4a4f-97ba-fd8c69b3a8e8','3141bc6f-7f77-4809-9244-116b235e7330','a74cbffb-281b-49ee-8e96-fbac2ab54785','40e2177a-2219-445b-bd97-56f2ebacfe62',NULL,'2012-10-18 06:13:35'),('d8b1cda5-7e73-4b2e-b9ab-d65954474921','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','8f063f36-f64c-479e-b37a-2491e99feb6a',NULL,'2012-11-21 06:37:15'),('d8fef6ca-b5da-433d-a9b7-93e6b3d4c680','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','ab17d28b-3f9a-45e2-b6da-fb19cf759c13','369eeb06-b520-4566-b9c5-1f7a2277a450',NULL,'2012-10-17 01:18:17'),('da46d971-584a-4881-a0fb-fb2824db4da1','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','4bc4b72c-8183-4a14-8066-47d7b0d2a827',NULL,'2012-10-17 04:33:00'),('db04e52e-93e1-4eeb-84cf-53850532e4ae','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','e2e6315f-7469-410d-8532-628d933f1508',NULL,'2012-10-17 04:33:01'),('db097570-ffa7-4ea7-b7bd-6691a497a8a4','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','18d00a54-6b22-4434-8563-e66f80431e9d',NULL,'2012-10-02 07:24:59'),('db3b772c-acff-456d-9e25-d285ed33ab48','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','4953b2ca-d1f2-4905-a8d4-bf95abd54930',NULL,'2012-10-17 01:18:19'),('dbe51385-ab5f-456c-8570-8bd878f55f22','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','a010a604-78c2-4656-846b-d265b95ae771',NULL,'2012-11-10 07:17:14'),('dc67f301-d64d-4f09-8f3b-40a1d4e02b1b','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','54762702-b427-4b75-95a3-faa17a90d1b5',NULL,'2012-11-10 07:17:22'),('dce52ebe-9e73-49a2-b180-7165f1892f50','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','15aceaf6-cf02-4aab-baeb-0f2402e41125',NULL,'2012-10-17 01:26:59'),('dd3413d9-54e9-4817-8f57-43d462d35b9e','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','5ff09d4f-217a-4610-8495-7089859d1452',NULL,'2012-10-17 01:18:14'),('dd39ec0e-b8c8-4dff-b8d3-96468b1848ce','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','33da3559-86b0-4db0-8e3f-7339155cc559',NULL,'2012-10-17 00:55:59'),('dd52d00e-dc01-4292-bca9-bc48e0d8a9b1','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','e10c64e0-105a-44f5-83da-a4a10379d802',NULL,'2012-10-17 04:33:04'),('dd6b72f3-6152-4964-8c0b-2f6dcfeab071','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','4e5c2d3d-0802-432b-9e52-2a98e8e18e8d',NULL,'2012-10-18 06:15:39'),('dd936cf5-a97c-4587-8ee9-7756d52f7853','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','4953b2ca-d1f2-4905-a8d4-bf95abd54930',NULL,'2012-10-17 00:55:58'),('dde03552-30d6-4957-bb50-5eaa1ccab517','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','06f8bd90-7731-479e-ab53-f56c4c95db80',NULL,'2012-10-13 06:42:48'),('dde74aed-0572-4049-91c5-95514533b002','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','369eeb06-b520-4566-b9c5-1f7a2277a450',NULL,'2012-10-17 01:18:17'),('de731b52-ce20-4e41-b2c7-dc0ca68c2597','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','24b9aa78-7055-4776-910d-da124b1863f9',NULL,'2012-10-18 06:13:41'),('dee94080-e629-4786-8541-46a3e74ad826','3141bc6f-7f77-4809-9244-116b235e7330','a74cbffb-281b-49ee-8e96-fbac2ab54785','47cd293b-5789-4084-8e9b-6d0404e15a77',NULL,'2012-11-10 07:17:30'),('df58c34a-9e46-47e2-8ad4-1e7784f035e8','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','a5b94581-b1b5-40c4-975c-0ea11870c237',NULL,'2012-10-12 06:10:30'),('df87cea5-c8d7-42f8-a8b4-2c4d5e99ac07','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','afea3d12-379b-47ef-a395-0e162e37d90b',NULL,'2012-10-17 01:18:16'),('dfdac623-c73d-45d2-a3b6-732a331f6bb7','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','9c1ef1bf-7ac0-431e-bfaa-0490b199d2c3',NULL,'2013-01-05 07:02:50'),('dfe9a466-a289-40c6-be37-29aa1fca64be','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','9621ea57-5c9c-48dd-8a7f-1a7dc3f7e107',NULL,'2012-11-10 07:17:22'),('e00fd21d-41d0-4d2a-9156-038ef19a4abd','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','65b74b06-6bda-433b-b993-f09810cdcbca',NULL,'2012-10-17 00:55:51'),('e0ac28f5-8480-4443-880a-311d046d7e23','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','808520c8-c588-40a0-9c32-8518110c0e33',NULL,'2012-10-17 01:18:18'),('e11b0e80-d28a-47d8-a2e0-85ae571ff33f','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','2c0fbd65-f7ba-452d-bd93-a40b371e2a68',NULL,'2012-10-18 06:13:33'),('e18c1382-31ea-45be-ba86-94e074f44f3a','3141bc6f-7f77-4809-9244-116b235e7330','e96b787d-57f6-41a4-9693-d0634bfacacb','1d283460-e24c-4cbb-8476-2a66df560456',NULL,'2012-10-17 01:27:00'),('e19e53a5-9f3f-492f-9c5b-f761f399a7f2','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','1567b10b-1474-4df5-a6df-ce3b8a92ee97',NULL,'2012-10-19 05:57:04'),('e2160ecb-a245-4b65-92f3-f360a7c4d9e4','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','5e7fbaac-1441-46e6-aedf-c65763e9fbf1',NULL,'2012-10-18 06:13:47'),('e2d36fd5-0e6a-4650-8f50-d8f0f8b4a017','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','8cacccb8-d2d5-46d4-a3f5-805ae1fc68cd',NULL,'2012-11-10 07:17:23'),('e34df219-4ed4-4d6e-862f-a105bb3810a8','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','83e32dd9-e3ac-4953-8f8e-756ac56d4480',NULL,'2012-11-10 07:17:35'),('e43b2308-8809-4379-9d14-74ef648830d7','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','45984389-977d-4064-8d46-c0df6e791880',NULL,'2012-10-19 05:57:13'),('e4458c56-0b80-428d-b95d-ab762b28b7a6','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','b155afa2-038e-42dc-beee-f89e5a3ce41f',NULL,'2013-01-05 07:02:50'),('e4885958-7e2a-4daf-83db-d2c18a456f27','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','68d7dce7-17c4-4407-b648-83efa8870d3e',NULL,'2012-10-17 01:26:58'),('e4b81413-36ca-411d-90c8-b30df261920d','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','3ffcbbac-7b49-433b-8ffa-275ff81aba00',NULL,'2012-10-17 01:17:59'),('e4c1dc9c-bdb8-41ec-980f-eab7f9226f9a','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','e4c15807-3cb1-4e86-a659-03b56b3e1fb5',NULL,'2012-10-02 07:24:59'),('e4cce67c-cf77-42b8-9689-a9742270c725','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','21749616-c014-4e81-84d5-f90af82532b7',NULL,'2012-10-18 06:13:33'),('e4fcea82-c052-4b70-983a-a04a543bf52a','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','b50f44c8-1d3d-4d0b-b17d-9ab0005ee224',NULL,'2012-10-17 01:26:59'),('e51fa409-4594-4c15-afbb-3488c709de93','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','7a0ab3c1-8ab0-4aac-b064-bff7e6e88f64',NULL,'2012-11-10 07:17:22'),('e55ee0a5-5f40-42d1-aa35-b7046ddad8b3','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','a3797587-2fde-418a-a73c-62541ca30275',NULL,'2012-10-17 00:55:59'),('e5c55989-cbc0-4c71-94ab-c2365c9894e7','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','ab17d28b-3f9a-45e2-b6da-fb19cf759c13','9f66ca0d-34eb-4820-9679-0704fecb73f6',NULL,'2012-10-02 07:24:59'),('e5c9d1fc-55bc-4642-9ffe-f63e406d7744','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','06f8bd90-7731-479e-ab53-f56c4c95db80',NULL,'2012-10-13 06:42:48'),('e5db7c98-9726-4f91-86be-4ae08571d05e','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','e88a46ca-5372-4314-a51c-ec83ac9ef4b4',NULL,'2012-10-18 06:15:41'),('e61b126f-f747-4a9d-a459-a0a63b1ce812','3141bc6f-7f77-4809-9244-116b235e7330','b3b3b726-acef-4d58-82e4-ef8875d654ce','c5e493af-195e-4b64-9331-4f716a16eac9',NULL,'2012-10-02 07:24:59'),('e6351083-46f5-4e57-8df0-465264cada29','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','59160936-d126-49a2-a275-86f9359c0e89',NULL,'2012-10-18 06:15:34'),('e675f4db-2927-4214-bdc1-f2aae737f674','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','3da83571-5d77-4edb-9782-859138ac1ed3',NULL,'2012-10-17 01:04:55'),('e679ba29-5084-4899-9e60-c64ce77fc129','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','095e1f7a-815c-449b-ae7e-b5d96e434f2d',NULL,'2012-10-12 06:10:31'),('e740e108-1a5f-4c0b-92a1-06f7d787f7f2','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','c79e680d-ce6f-44bb-aa6f-91ac2e94dedb',NULL,'2012-10-19 05:57:13'),('e825052e-0128-44f7-b874-97a7fc7ff32a','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','0f965b92-8f6c-4a52-bffd-a28da459fdf9',NULL,'2012-10-17 01:27:03'),('e8605ebc-ca01-4e4f-924a-ac5a885d8292','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','c02e25ee-16f0-4020-80f7-8c378c398dc4',NULL,'2012-11-10 07:17:21'),('e87389ed-ae39-49f5-8796-d8296a2c94c1','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','114db91c-503f-43c2-a83e-c2de6b817e47',NULL,'2012-10-17 04:32:58'),('e95b2b31-5731-4ce7-b927-d08513f6a4c1','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','88f61e4e-ce58-4bc3-9bc8-91d6c3cfcabe',NULL,'2012-10-17 04:33:03'),('e9b38318-f496-47ed-a23f-3b6749b943a3','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','8a0b7119-633e-4f74-a499-8b5535158860',NULL,'2012-10-17 01:27:00'),('e9cb44f2-97d1-4d6b-bbc9-9955dbecddf5','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','3bc97a96-a507-4c9c-8033-d7f212a9badb',NULL,'2012-11-10 07:17:16'),('ea9574f6-10a2-4c11-89b9-deff6bcfa136','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','9f66ca0d-34eb-4820-9679-0704fecb73f6',NULL,'2012-10-02 07:24:59'),('eab75cdd-50cd-4bcb-99f7-f900ad572a2b','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','01a2674c-850c-46ab-a768-e8593baf9404',NULL,'2012-10-02 07:24:59'),('eb14f61d-546e-4cf0-b471-5b2773c03c05','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','4d399247-7598-403d-bb17-8d9230bacb0d',NULL,'2012-10-17 01:18:19'),('eb1cfe5e-1261-4ac6-8596-2f443f84d198','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','f991f719-4fe8-43d7-b032-2e4a52de8d53',NULL,'2012-10-17 01:18:02'),('eb377c40-2583-4966-8759-1fd10a78d660','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','e326db2c-7da9-40c4-81a2-613c979efcbf',NULL,'2012-10-17 01:18:15'),('eb589ed3-503e-4412-8044-e4f4e811d3f7','3141bc6f-7f77-4809-9244-116b235e7330','e96b787d-57f6-41a4-9693-d0634bfacacb','c02e25ee-16f0-4020-80f7-8c378c398dc4',NULL,'2012-11-10 07:17:21'),('eb63b3ba-a588-46a9-b72d-1f778c281614','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','4bc4b72c-8183-4a14-8066-47d7b0d2a827',NULL,'2012-10-17 04:33:00'),('eb693c0e-8844-46fe-88cd-6639f03cff7b','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','0c2ad7a0-83b5-4f83-aed9-ee6ad99510b3',NULL,'2012-10-18 06:13:34'),('ebf41562-4bed-4eba-9b2c-4cd3688f5f85','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','a010a604-78c2-4656-846b-d265b95ae771',NULL,'2012-11-10 07:17:14'),('ec8ca2e6-3580-48bb-a0a9-dbed4a718ba5','3141bc6f-7f77-4809-9244-116b235e7330','e96b787d-57f6-41a4-9693-d0634bfacacb','79103108-9ea9-4ed0-a777-37af444867b6',NULL,'2012-10-17 01:26:57'),('ec8d63f1-3fc7-401c-b7c8-1027d5ae17c1','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','632a5374-bc44-4d75-a9f6-e53edb87e316',NULL,'2012-11-21 06:37:15'),('eda4c911-81c9-42c8-b936-afdd184353d1','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','7d02f6c1-eab0-418a-a633-fba5abce7f8c',NULL,'2012-11-10 07:17:17'),('edc01ead-f89c-4637-8526-bfe303c2e1b8','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','e549810e-8a45-4d3b-9f7a-97ce7f11e853',NULL,'2012-10-18 06:13:38'),('edc5102f-f93c-4d51-a74b-739f2645158a','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','eb10b29a-689c-45ca-9ea5-d92c275d0258',NULL,'2012-10-17 01:26:57'),('eddecae5-9422-46f3-a115-70d65374e316','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','0bd0540c-db99-47e2-b63b-12be1c6eafcf',NULL,'2012-12-13 05:41:55'),('ee331274-9838-4bfd-86cc-2a093229621a','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','62ad9422-842f-437e-b388-620abbbfe876',NULL,'2012-10-18 06:13:37'),('ee589368-49c7-4c10-9ba7-d71e98ca3ae4','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','95d261ff-13dd-4c42-8c83-f69a7b1cd6ba',NULL,'2012-10-18 06:15:37'),('ee8e0da7-17fd-4d7a-9a2d-ad117742c666','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','26fcd232-8685-40ea-a1b3-e845662d9a51',NULL,'2012-11-10 07:17:20'),('ee92c445-e1ea-490a-ae9b-0f4077aae25b','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','2b6c2412-b978-4478-8d09-b91ed4c6dc6a',NULL,'2012-10-17 04:33:00'),('eea1e9cd-02f0-41c9-a1f2-de479ce5abb5','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','48ddd2b7-7841-4731-a28b-5ebe84a0ff8b',NULL,'2012-10-18 06:15:42'),('eea37c20-720a-433b-b898-4b633cf46601','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','d82eff8d-ade2-4c27-a5f9-afe1fedd6b10',NULL,'2012-10-17 04:33:03'),('eecbf307-6f7a-42da-b715-3f2f8c27e195','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','369eeb06-b520-4566-b9c5-1f7a2277a450',NULL,'2012-10-16 04:32:21'),('eee0e1bf-a83a-4b42-ae17-90aed2122f6a','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','37384927-53f5-4ac7-8425-94a110c081ce',NULL,'2012-10-17 01:18:16'),('ef71b3f4-e724-447d-9155-ce8038016aa3','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','268c57bc-edfc-4da7-b01a-c1ebb2438022',NULL,'2012-10-17 04:33:05'),('f03f6665-1a75-425b-9abd-a8b5800563c6','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','a5b94581-b1b5-40c4-975c-0ea11870c237',NULL,'2012-10-12 06:10:30'),('f0b34de4-ef6b-41b0-864b-6469af6c295d','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','23dca992-ea19-4731-8e96-20739509851c',NULL,'2012-11-10 07:17:15'),('f15b930e-5ad4-49f5-a76e-90f04e2c8e0e','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','090ff58a-f772-455b-9743-2615e17b6aa8',NULL,'2012-11-21 06:37:15'),('f15ed09a-2439-43cc-ba7f-663b534eb495','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','49f8cfb5-afac-44ca-8d47-b408881954e0',NULL,'2012-10-12 06:10:30'),('f204fbec-9c62-4bdf-b3fa-3ced615b2fdd','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','7c5095aa-a9c0-46d7-a67d-e95d628edda8',NULL,'2012-10-17 01:18:02'),('f2ea1037-21b0-4013-9265-b60ef2adf2f3','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','2898de0d-0bae-45ec-a915-05d92d1df637',NULL,'2012-10-17 01:26:58'),('f3124cec-d5a4-40a0-a319-6ebaff24f33e','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','0a01c5ed-7e05-4dc8-8082-48fe61bab13d',NULL,'2012-10-18 06:15:40'),('f3e0e982-1dd6-4366-be58-eb284c3e55af','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','268c57bc-edfc-4da7-b01a-c1ebb2438022',NULL,'2012-10-17 04:33:05'),('f4142bcf-9ab6-41f2-8e36-4b44ce6f1b34','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','d6a33093-85d5-4088-83e1-b7a774a826bd','3840a1dc-df0f-4a41-a3df-6eb612491571',NULL,'2012-10-12 06:10:30'),('f4b47fb2-7431-4469-a442-6a3679890154','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','cabd97ec-12fc-442a-9952-54a9250c6e8c',NULL,'2012-10-17 01:18:16'),('f57e5e1a-2119-4f04-9ca2-7133089b99c1','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','74455ff3-647d-4d29-a71a-be28fa97a289',NULL,'2012-11-21 06:37:15'),('f5e50daa-f19f-4a9e-b6f5-6bf8c344ddb5','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','c0971b7e-06eb-4d4b-856b-5ffb7d2843d6',NULL,'2012-10-17 01:18:13'),('f697bca1-9eb9-4e2e-a37b-60f8e066c6b4','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','5ebc8427-124d-4615-83fb-4326749745e3',NULL,'2012-10-12 06:10:29'),('f69d174e-3be8-4fb9-bb02-b4fe49242a8f','3141bc6f-7f77-4809-9244-116b235e7330','d91f0637-b2f6-4492-ac1b-23c155102a3e','5ff09d4f-217a-4610-8495-7089859d1452',NULL,'2012-10-17 01:18:15'),('f77bb023-2770-4be3-a5df-34b1c7ce2514','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','1d283460-e24c-4cbb-8476-2a66df560456',NULL,'2012-10-17 00:55:52'),('f7b65c87-6e89-4d04-aec4-01a05917a43d','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','a1e102fe-de95-4fc5-9bff-a1cc7058e1cd',NULL,'2012-10-02 07:24:59'),('f8ca25f2-d271-49d8-80dd-2889dfb7eda0','3141bc6f-7f77-4809-9244-116b235e7330','a74cbffb-281b-49ee-8e96-fbac2ab54785','ef02e784-10d9-4ff1-b4ea-b0e4636ba2b6',NULL,'2012-11-10 07:17:30'),('f9745e55-b227-487e-b41d-d6bcc7228c2a','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','860d9543-4d04-4be9-9fed-ff1e35648d28',NULL,'2012-10-17 04:32:59'),('fa27685c-d412-47d6-8cd1-fa0adc9beef9','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','01a2674c-850c-46ab-a768-e8593baf9404',NULL,'2012-10-02 07:24:59'),('fac42ff8-e742-48f7-ac2f-591d58fa5924','3141bc6f-7f77-4809-9244-116b235e7330','a1921355-58c2-4d6f-9535-a6d227a0be16','632a5374-bc44-4d75-a9f6-e53edb87e316',NULL,'2012-11-21 06:37:15'),('fb25756e-898d-4ea0-9f4d-0b3d61ed184c','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','64c450b4-135c-46d1-a9aa-3f9b15671677','e2421d0b-edfe-44fc-b404-f88f4e3ac522',NULL,'2012-10-17 01:18:00'),('fbede329-c8e8-4813-b2e1-1e174e22a36c','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','c0540086-8d61-4684-b8a8-ed11594c5abf',NULL,'2012-10-18 06:15:35'),('fbf5bcc2-4741-42b0-ab9c-09e5422024bb','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','9dc10fcb-5809-4c0b-917a-02517ef4d1c6','4953b2ca-d1f2-4905-a8d4-bf95abd54930',NULL,'2012-10-17 00:55:59'),('fc3d022b-3de5-4e05-a8fc-0784dbc6d974','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','a34ddc9b-c922-4bb6-8037-bbe713332175','632a5374-bc44-4d75-a9f6-e53edb87e316',NULL,'2012-11-21 06:37:15'),('fc954c0e-5e41-4da5-bd7e-b5e5e5b0cc4a','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','ab17d28b-3f9a-45e2-b6da-fb19cf759c13','1d283460-e24c-4cbb-8476-2a66df560456',NULL,'2012-10-17 01:18:01'),('fcb4380d-629c-4e8b-919c-9fd213e1c83e','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','ed15e649-7843-494a-a100-fc2571193f02',NULL,'2012-10-18 06:13:30'),('fd03e85e-45f4-4a3b-9566-dcced71ddac6','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','20109b15-39c8-4d10-bac5-617ae2850e22',NULL,'2012-10-17 04:32:58'),('fd3d2b07-c103-45ba-84a8-b65ec36e9c77','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','0c2ad7a0-83b5-4f83-aed9-ee6ad99510b3',NULL,'2012-10-18 06:13:29'),('fd76ecf2-0eb7-4beb-8a04-7a344832a5be','3d1b570f-f500-4b3c-bbbc-4c58aad05c27','76fd899e-cd5a-4da3-8752-45c43fc18656','9796ae62-3ee9-445c-a7d6-867fa5fae36d',NULL,'2012-10-02 07:24:59'),('fd9c93cb-3363-4fad-9735-49aaa5b877ea','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','8a0b7119-633e-4f74-a499-8b5535158860',NULL,'2012-10-17 01:27:00'),('fd9f78fc-2916-4331-a3ec-c6367f358a72','3141bc6f-7f77-4809-9244-116b235e7330','a74cbffb-281b-49ee-8e96-fbac2ab54785','4cd6ff3b-2508-47ac-9d5b-2fcdb61ab1cd',NULL,'2012-11-10 07:17:31'),('fddc163f-621a-4ccf-bdf8-8183983e506d','3141bc6f-7f77-4809-9244-116b235e7330','b3b3b726-acef-4d58-82e4-ef8875d654ce','40597064-f02e-4bc6-9efa-b2d214925b73',NULL,'2012-10-02 07:24:59'),('fe0242c1-b6e7-4138-afef-b7560b9de587','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','88f61e4e-ce58-4bc3-9bc8-91d6c3cfcabe',NULL,'2012-10-17 04:33:02'),('fe074dcb-e45c-4a9e-a52a-aef93e402d4e','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','06f8bd90-7731-479e-ab53-f56c4c95db80',NULL,'2012-10-13 06:42:47'),('fe8031d8-03bf-488f-a6b0-ed8fc44c50c6','3141bc6f-7f77-4809-9244-116b235e7330','6957fdac-a1ed-470f-89f7-fb00be42ea13','6ce73de0-e573-4f2f-9a54-28c440210c2c',NULL,'2012-10-17 04:33:00'),('fe881f60-e2e6-40e7-bbc3-d54588e2ec8f','3141bc6f-7f77-4809-9244-116b235e7330','53d34372-8805-4adf-b940-6c05b835a94f','675e8fdb-67e9-41a8-a505-823cd8348cb9',NULL,'2012-10-18 06:15:34'),('fed3ffba-cc26-426c-83bc-d42b0b71a686','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','37384927-53f5-4ac7-8425-94a110c081ce',NULL,'2012-10-17 01:18:14'),('fee0554c-549b-4b93-a62c-ce1393abc8de','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','1d31b54e-1745-4b37-9468-d35e8e317d05',NULL,'2012-10-17 01:18:00'),('ff7fcb5c-5ab5-49c2-a0f0-f7612b526d69','3141bc6f-7f77-4809-9244-116b235e7330','520f976f-476d-4ec6-be1c-7c448c5c074d','7a8f358d-9b31-4d29-b258-7d668db64631',NULL,'2012-11-10 07:17:18'),('ff905955-0428-4f8e-b91b-0463a0337c4e','27c2969b-b6a0-441d-888d-85292b692064','1628571b-c2cd-4822-afdb-53561400c7c4','090ff58a-f772-455b-9743-2615e17b6aa8',NULL,'2012-11-21 06:37:15'),('ff98e0ef-473f-4733-9704-00772c51ecea','3141bc6f-7f77-4809-9244-116b235e7330','bb5a6da2-4b89-4f8c-96e3-ca36c55d3337','5c4a8eeb-3536-4b43-8eb7-33ee6472c63c',NULL,'2012-10-18 06:13:39'); -/*!40000 ALTER TABLE `CommandRelationship` ENABLE KEYS */; -UNLOCK TABLES; -LOCK TABLES `FileIDsBySingleID` WRITE; -/*!40000 ALTER TABLE `FileIDsBySingleID` DISABLE KEYS */; -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('00cfad8d-b4bc-4cad-a5dd-e1160905573c','88bb6c02-f537-4879-89dc-8e5e7e257156','Plain text',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('01db40ed-980c-4c24-9d87-be078a2adc47','21b07ca7-b9e9-490a-a4db-39081a306862','fmt/49',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:04'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('02bcfb5e-ef06-4bc7-8e47-0ced2adffc0f','24b9aa78-7055-4776-910d-da124b1863f9','KDC EXIF',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('03ea1222-339b-445a-80f4-fc9a5fc12a68','c24abbeb-3318-4ec0-ab40-f272a26800ad','x-fmt/2',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:31'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('048dd909-34f8-475d-9f87-4db3c67e2615','4137d40c-0130-4277-b726-61bc76f4fdef','application/msword',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:09'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('0493044d-0ed6-4069-8f42-9f6ca8b21974','a359b55f-70af-43c8-a6d1-42f0801c0155','fmt/293',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:33'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('05109d04-3d50-4e83-bd27-33394b24e563','54762702-b427-4b75-95a3-faa17a90d1b5','x-fmt/270',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:22'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('05175d02-cb07-4d5b-96fe-6279a30b6249','d2941b3f-38e2-491d-947b-f6e07802595c','image/gif',NULL,'FileIDsByFitsFfidentMimetype','0.2','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('05e6b62e-e65e-4630-8723-0a4cd12a763f','15aceaf6-cf02-4aab-baeb-0f2402e41125','GIF image data, version 89a, 640 x 320',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('06e12b77-19cf-4f8e-a07e-e5795c968c96','4021ed21-c101-42d4-83a8-3ff714eccb17','x-fmt/44',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:04'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('06ecb656-652d-4be3-b1ae-73e90f7c7a5e','937e5e30-e054-4e65-972e-9c5b4b4f8f5f','fmt/17',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:25'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('072180c4-2275-4f8c-ab5a-56b79b283f54','4d82500c-bfb5-4ddd-bb9e-40733cbe72be','orf',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('07e470a4-9186-4811-8186-2e8c66f0de0f','5df9d93b-5e4d-41a3-89b5-301bf009ee9a','CR2 EXIF',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('07ec7018-2604-4495-9b44-98b5f0f8125c','02246977-bef6-46da-98c2-de7522111899','gif',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('0824f2e1-b8ab-475e-9c42-9047922ef588','599cb14f-dbc5-4f45-8e94-24fdb881fbbf','NEF EXIF',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('08732870-6a9b-4ad4-ac97-b2badfce9570','6f4e7eff-7204-468e-bfb2-2943688d282f','Macromedia Flash Video',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('08effc83-379e-4ab6-964b-f00bdbcb3f2b','a1f09e48-b4d7-426f-b186-b7bdd258f0f3','Microsoft Excel Format',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('08fdff90-cbfe-4aca-ba07-bc4b04b38bfe','dcd1f9aa-d544-4ff7-a90a-d7dafc162897','CRW',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('0910460b-fde6-44a5-9f5e-c783c3f31935','53bff439-5ff6-4fe9-a87a-8745da64b5e6','Microsoft Office Document',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('09351452-e937-427f-8eeb-ca4ca281466f','52b25353-f40f-453c-bb26-854d2cf29b7f','fmt/133',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:04'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('0a5160e5-9488-428c-9182-e8d4ed09c8dd','53b462db-42b5-41e6-94fa-a98974bcfd5b','Quicktime video',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('0a850d28-91a9-48d6-aea8-b4171dbf6bdf','061ec789-c1e9-40af-83b4-a6b0897267d8','audio/x-ms-wma',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:13'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('0a9a13dc-715b-451c-b2ef-bafd22e5e879','0cea8124-51f7-49ed-822a-b6b842673c62','Macromedia FLV',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('0b870553-f0fc-453f-8ef7-86ed187f5fcd','03bbfaaf-124c-4454-8082-50e0b3d23e57','pct',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('0b9fd719-58b7-44e9-b7e1-774aadb2a8c3','7efc3875-3bd2-4615-9b1f-83d7fc8131d0','x-fmt/273',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:32'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('0c5515c5-0ad6-4085-a966-3853ee4512d5','4d399247-7598-403d-bb17-8d9230bacb0d','image/jp2',NULL,'FileIDsByFitsJhoveMimeType','1.5','2012-10-26 18:38:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('0cb0eac1-7146-42b1-ae5b-3aef53a81b99','ed5607ba-10de-4e96-902b-8eaf4dacf249','application/vnd.ms-powerpoint',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:13'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('0cb9cdda-c1c0-442d-9f51-d47e385027da','b155a460-4722-4dcf-81fa-3a4c65557cef','fmt/111',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:04'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('0ceb728d-68ad-4848-a98e-a723b17ab792','e4f2ee35-9d5c-4d2a-a251-0bbe5979d6d1','application/postscript',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('0d4c93bc-80a3-40f8-9a05-8e22b9b15c8d','cb2d39d3-9f00-41c1-b0ef-99d5c57a900c','ATSC A/52 aka AC-3 aka Dolby Digital stream, 48 kHz,, complete main (CM) 3 front/2 rear, LFE on,, 448 kbit/s reserved Dolby Surround mode',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('0ecaeb44-3633-4616-8d4e-fa7f2909b689','4bc4b72c-8183-4a14-8066-47d7b0d2a827','fmt/10',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:04'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('0ee88860-132d-42c4-ba0f-1a149fcbb131','6ce73de0-e573-4f2f-9a54-28c440210c2c','fmt/8',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:04'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('0f0f4d25-3841-471b-965b-c57951c5aa20','36e43181-0b2d-45d7-939f-491cdd04d7b3','x-fmt/129',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:31'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('0f11fcd7-9035-441d-afbb-abf96b9a53b8','8d6f16a1-d41d-4f6b-9da6-fa06ce5ebe54','fmt/2',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:18'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('0fd44cb8-2da0-4163-9ad8-e24f8ab4a0f0','bdb7edf8-255d-4fe5-a457-50e93fad0211','fmt/95',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:25'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('10565386-3e60-45ab-8084-a8aa1cfdf5d7','799d35d1-0125-4d6b-8e92-df7cad508426','PC bitmap, Windows 3.x format, 799 x 599 x 24',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('10d4a50b-b5ad-4e7e-ab63-099c1c239003','0f42eabd-4da2-48ed-aa30-f76b1b6d94b7','fmt/149',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:04'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('1173ad57-8d50-4e1c-bace-e703e82b01e6','0f919e47-d406-4e9c-96c2-0a372c818f73','Targa image data - RGB 1419 x 1001',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('1315c1b3-1913-4bea-bcf4-a2f6621f8d1b','bd3002cb-5404-4590-9481-c62d5d638bf6','fmt/189',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:04'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('1451a098-e696-4a93-92b3-e90df08bd497','a14579f5-81f1-4d0e-b1f2-a63c7c0c94af','fmt/478',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:26'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('14688790-9072-4fb3-9612-ae58ad865d76','aa3515c1-bd03-4555-9e98-723ca14f74a9','eps',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('14c5a5f5-2514-43ce-a33a-8704dde30cf5','3dfac762-1463-4914-a5c3-00d751159b52','TIFF EXIF',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('1595f156-7097-4026-87ad-36473ef71c9b','45984389-977d-4064-8d46-c0df6e791880','image/jpeg',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('15d5af2e-0bad-4190-a0ec-d767b3e46b41','0205b77b-4396-4931-95b8-d896a123b0e0','m2v',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('15debe56-18ee-4aa6-b1c8-9e4e7e38f339','6a939182-d012-48ce-b495-5648eaf06e1c','fmt/37',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:33'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('15ec791c-0e8b-466e-948f-de32356cd77b','6a609c8f-29e9-4c04-9586-6d395def0275','Microsoft Excel 2007+',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('16804138-397d-4f1a-ba16-674fd01f1f0e','62be2403-d1ee-4d70-bab4-28bfd161f8e0','application/octet-stream',NULL,'FileIDsByFitsFfidentMimetype','0.2','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('17198e18-05ef-4159-b2f2-5d6a493a67a0','120836eb-b7e7-42dc-95f1-1470b8000dec','Microsoft PowerPoint 2007+',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('1a6ea6ba-7126-47ea-8306-59b25d13d2ff','438ae824-987a-4641-9767-9291b26fbc1e','odg',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('1affb692-f036-468f-b478-f376a96f04e2','095e1f7a-815c-449b-ae7e-b5d96e434f2d','Apple QuickTime movie (fast start)',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('1b00d40f-2900-4c54-ae9c-1a963aa8ef7c','bfefbf36-c506-403d-896f-3a2eac356e0c','audio/x-wav',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:09'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('1b412c29-6cb2-4bfe-9920-972d5d1858ab','4f31deac-0130-4dc8-9a6b-b2ac3bbc343f','fmt/417',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('1b530f99-503a-49a0-a75b-f53bbd619d9e','bc6c161a-3f61-4890-9492-90ad25567b6b','fmt/290',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:18'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('1b68cc35-cb3b-43e6-b492-10c623bb9eb2','dc4be07b-03cc-4d6d-95e5-2bd6f49b8b8b','audio/x-wav',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('1c710166-a1d1-40c1-b8fb-fab711a7b2bb','fbae3a6d-5b98-4974-888a-85bf48b793fe','fmt/181',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('1e3b4fa1-d600-4175-b7b9-99471dcce0d9','95d261ff-13dd-4c42-8c83-f69a7b1cd6ba','image/tiff',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:13'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('1e4e293b-8f85-4780-9821-e725d8db622f','c90cae9f-04e4-48d5-8c7f-ddaed3bc64ac','fmt/414',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:33'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('1f000949-b429-4782-909d-cd0dc7211cbb','4bbcd493-2035-4567-80e8-218dd24201e7','Macintosh PICT Image',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('2071274c-2fd9-4033-aab8-16cfe2463741','3128ebc0-b3bc-4a96-b420-d64327e099ff','x-fmt/386',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:14'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('2161c0a4-ceb6-4482-9b0f-18741939acf6','baecde96-ebee-426c-bc77-3ec8dc653261','jpeg',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('236a6305-6278-425b-8b0c-5c9e94596406','e326db2c-7da9-40c4-81a2-613c979efcbf','audio/x-ms-wma',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('23c48165-5b31-4fbc-9b43-d328a1f5c610','9f11626e-4664-49d6-9604-5c2ee9f4f299','x3f',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('249a2695-b5a7-418d-922c-f57cdd7ff174','a487dde7-8bc6-475c-9b7c-35cd163c5845','fmt/46',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:04'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('249ef230-d60f-4b30-b289-9ed6dd192da7','0a779ddf-8b63-48fb-a46d-7e2bf32a8b04','xls',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('24cf0f8d-be07-4353-b5cd-adc5752029a5','fc865a65-2cae-4062-a63d-0c6a54877c0e','application/postscript',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:13'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('24e1cbe3-28dc-40f1-bc95-9475ea0702bf','f86d6829-9a80-4348-9869-36b11517e74e','MPEG 1/2 Audio Layer 3',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('25bfdf9d-1031-44a6-bb62-671df0aa7c51','37b9babb-af84-4da8-9c2a-2c03087de2e4','application/vnd.oasis.opendocument.text',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:13'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('26be78f5-7ef7-4c7a-aa06-1ac3d9c9611c','01c97b84-76e0-4b94-9e3b-1e670bd9468b','video/x-ms-wmv',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('26dbbf82-5e71-4405-9494-e33b79a0e1dc','af5504e8-0bdb-4e97-8dea-08892fb928ca','odp',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('26fcf182-9a6f-4ff5-86e0-c4f54c051a0a','61629eaa-2dd4-47d9-9f7b-3ff94db4482a','x-fmt/274',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:32'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('27689ddd-7b9a-474c-9639-98fdf0f189cb','21749616-c014-4e81-84d5-f90af82532b7','RAF EXIF',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('27ebf3c7-fcd9-4af8-ae74-fd3e2e70cad3','99c1ab3a-dc50-4186-8828-df989f8d3d50','pptx',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('28a55813-24a1-41b3-bc0a-44d2590bf031','d407dc6e-52ae-4fec-a1f6-9708d87af938','application/octet-stream',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:13'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('28a8b142-d45e-4fa2-b771-74c20f28cc83','afea3d12-379b-47ef-a395-0e162e37d90b','image/gif',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('29f06bb1-7da0-4ba9-ab68-07761adc3e58','881214c5-642f-4a87-b4a5-3c8b6b8436d2','mp4',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('2a213953-6ce9-4f8c-8070-14e69aea66b7','c79e680d-ce6f-44bb-aa6f-91ac2e94dedb','fmt/43',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:04'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('2a2b09a1-d345-40f1-868e-0508111c90ec','4aa31d07-d726-499f-b3ec-afe2995abedb','mp3',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('2a61b455-1daa-44a0-befc-ce344d9551c4','01a2674c-850c-46ab-a768-e8593baf9404','psd',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('2b864240-7a70-4548-bfca-f8f89d7c531e','1fd534f6-139f-41ed-80dd-2b6b7fee6768','fmt/145',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:27'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('2d85a057-ea3e-4e95-b2ab-01a845c8fb4a','5abb795d-02e4-4d63-87cb-61fbc7b70be8','Scalable Vector Graphics (SVG)',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('2daa5b6a-ecdf-41ac-aaf3-9dd165d015df','e01e5dc0-37da-451a-9644-22309e5cb21e','audio/mpeg',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:09'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('2dd25c6d-73e1-41a6-b7c6-6b71737a5973','c285590a-914f-4c36-a0a2-624fa25d028d','Canon CIFF raw image data, version 1.2',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('2e582b0d-3e0d-4d3d-9865-9857fde66e8c','03bb2a7e-47bd-44fa-ab1d-797370bb1ead','PDF',NULL,'FileIDsByFitsJhoveFormat','1.5','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('2ec87b00-7aff-4faa-87c9-367930092050','b3df6777-cc46-4d03-8766-9c1eb0893ee9','xml',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('30007b4f-14b5-495c-9d96-f285aaf4c8fb','d63f2b5e-736c-4b85-ba35-ec6cc58be2a2','fmt/477',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:26'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('302b65bc-05bd-4af4-b9d7-f563a6e374d5','8479526a-fdab-44af-97de-920bea73237e','image/png',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:13'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('30eb12e7-fb1c-430b-b7fa-15bdd19405e6','ebc1e69b-0209-4abe-b22e-6d5660fab576','fmt/15',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:24'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('3158b6b9-d236-43a2-8aa6-1b9800d39c3d','5cb9e13e-9abe-4258-9c94-f79c6136f577','image/x-x3f',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:14'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('31841d63-2d28-4b19-8ebf-c733b201381f','e4c15807-3cb1-4e86-a659-03b56b3e1fb5','aif',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('334d255a-921d-4c77-a975-daa085deecff','075bcd44-b926-4a45-b8a1-f75220f5c903','Rich Text Format',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('33e37fe8-42c3-4c2e-a988-900963aa7c94','34c42eda-b9e6-4a35-b483-a791bd04718d','Foveon X3F raw image data, version 2.1, 2268x1512',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('33e57fbd-dd11-4243-843a-9b79d5b71c55','35b9e3c0-9670-4775-9e9a-cf56d973440b','Windows Media Audio',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('342a18bd-cb87-4f15-9968-0f91aab9833b','d189342e-a551-4f77-a45c-6f3482e3dce3','Canon CIFF raw image data, version 1.2',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('344c95d4-300d-41d5-8fcd-2d46b3cbdf32','a269e764-da36-445a-b6a4-e4cc3fac4f88','fmt/481',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:27'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('34c5ae07-ddb7-4037-b247-9e1671c4549e','ce23cf3b-6ee1-4392-9d11-e088cde97864','swf',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('34de2fbe-5914-42a3-86be-a99d06322c3a','51f80b0a-7a4b-4c68-ac7a-b41ef9c9aaba','application/vnd.ms-asf',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:14'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('35694c56-9aad-4647-b374-f3cf70eb1ef8','66120b2d-0c89-4304-815b-6c8cec94647b','image/jpeg',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:09'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('35d3bc97-0325-4708-a156-35d950a39235','b0bfcd0e-3194-4292-b78a-cea6a356a4d8','mboxi',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('35f916f0-171a-4ee1-931f-0a0763331c1d','9621ea57-5c9c-48dd-8a7f-1a7dc3f7e107','fmt/118',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:22'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('35fb7091-3ba3-4cd5-bf75-7f392bc6cbdb','3bc97a96-a507-4c9c-8033-d7f212a9badb','fmt/441',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('36ff33b5-ece1-4a3d-8861-e9d7260a5f67','e793325f-df0f-4e64-9ef9-cf1b08bf29ee','fmt/125',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:17'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('387ffd3d-d264-4caf-b728-72aa678e6be2','6a7c543e-1d68-4ab1-8ab0-4898d0d204e4','application/pdf',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('391223a3-25b0-4b20-b938-0cb2dfb4d6d2','e7c2b9b8-8c8f-42f7-86c6-2011cc2678a4','doc',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('3c26c5e7-4f3b-467a-8098-3ad9931ed577','5ebc8427-124d-4615-83fb-4326749745e3','Microsoft ASF',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('3c687bef-9cbc-4ed2-8592-884efa615819','983398c1-423c-4e88-99be-da3ad56d2d7c','AIFF',NULL,'FileIDsByFitsJhoveFormat','1.5','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('3cddfa18-cb5d-4a1c-ae6e-45f6b3114703','65b74b06-6bda-433b-b993-f09810cdcbca','audio/x-aiff',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:09'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('3d5e64a2-7978-42e5-a6bc-a21187bbe254','eb10b29a-689c-45ca-9ea5-d92c275d0258','Olympus ORF raw image data, little-endian',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('3d6e7cab-fd40-4813-b100-095eeb252059','b8668b4d-abbf-4a4e-8646-0640f2baa787','PDF document, version 1.5',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('3d875715-46cb-4e1e-a167-4f096b607d4f','61d45a28-5271-48e8-810d-c83b4bf6b24f','image/x-canon-crw',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:14'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('3e60d025-4f0e-4ee1-9002-c8747f982765','9e447d46-e845-4abb-b562-e73b3a716ab4','audio/x-aiff',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:14'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('3f9316e2-71e6-4c0e-9fbb-83d06dbc30ff','632a5374-bc44-4d75-a9f6-e53edb87e316','image/tiff',NULL,'FileIDsByFitsFfidentMimetype','0.2','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('4116dbe6-b432-4630-b786-864f2d8b30c8','07e990d9-5b60-4cb1-8e83-40e25dcdcdae','erf',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('42869764-0620-4dd0-bb74-c1c60bf58d39','5fc9e48c-f29d-4a98-b381-a8cccafc02af','x-fmt/20',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('42cd210e-869c-4c94-bc5e-12ad18bef12d','1ddbfa77-0390-4905-aeda-58d2984db924','fmt/106',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:34'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('4361ab17-09b5-470a-810f-d3f3f329fde9','074d4c3a-c9d7-4751-b2eb-72e0cb45f69c','image/png',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('438f8297-c922-422d-ad78-d4f58e8a4a67','420813eb-03e3-4a3a-97ca-84894f08af88','fmt/9',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('43ff10c3-0a66-4488-8a0c-509e813b5256','5c4a8eeb-3536-4b43-8eb7-33ee6472c63c','MOV',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('4496e165-8e75-4429-8c37-890227a08e95','ef02e784-10d9-4ff1-b4ea-b0e4636ba2b6','fmt/418',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('44b4a764-c709-445f-b333-b11220964063','10d24277-054c-4c20-9774-9cac31b22b42','odt',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('4538c76c-a3c8-4c60-a3d6-fa4affb2c3d1','8c334402-efc2-4585-958c-540cedf32936','fmt/114',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:23'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('45e47eba-3e95-4108-a7fa-e6a68ee7ea06','e2421d0b-edfe-44fc-b404-f88f4e3ac522','SVG Scalable Vector Graphics image',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('466fbbd9-616a-4e95-a0cf-d4fd703eeb75','d82eff8d-ade2-4c27-a5f9-afe1fedd6b10','fmt/52',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('470dcb96-0077-4737-9e4a-d734908a5852','79103108-9ea9-4ed0-a777-37af444867b6','DOS EPS Binary File Postscript starts at byte 32 length 159320 TIFF starts at byte 159352 length 881762',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('47847a60-45ba-4e56-80b3-e940e1a05db1','84bcc45a-f840-4645-9ae3-1f480ab74589','audio/x-aiff',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('4839eae2-551e-4961-a861-5a85c100612b','288aaae9-9513-4bf3-99c3-b5fd26ed85af','nef',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('48848bc9-44e5-4b00-b87d-9d4b015576bc','e10c64e0-105a-44f5-83da-a4a10379d802','fmt/134',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('48bf63ca-a4af-42a1-8ea7-5af705662011','2898de0d-0bae-45ec-a915-05d92d1df637','TIFF image data, little-endian',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('490e9bde-ff9f-46a0-b661-722efc13e22e','9118731f-b1bb-46ed-9d6c-1d070a5e7bf6','Targa image data - Mono - RLE 128 x 128',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('4a1ce2bb-a707-4a8a-abc7-429b8a662b35','59160936-d126-49a2-a275-86f9359c0e89','video/x-ms-asf',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:14'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('4a9cb021-67ee-40b5-ac70-e10f99b76b23','e2e6315f-7469-410d-8532-628d933f1508','x-fmt/92',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('4af68905-08d1-4bec-8fad-fe8024053e3b','4c9c49f4-ca45-40bc-b7a2-119bfa76cf23','kdc',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('4b6de988-f8b9-49b7-8677-711aa73682a4','8f34c0fe-0d12-4dda-bef0-fb952990cb71','Plain text',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('4bd59917-59ec-4aa7-8a1d-333769bc6183','b7c6ba3f-b139-4451-b0ce-2e534a088d56','fmt/44',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('4d0b6832-0bf0-44da-bcee-e4952d1dd1bb','d95f4962-a8cd-4357-a0bc-4f2ffd408f2d','x-fmt/80',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('4de7d193-996f-4eb2-9a33-4e08cf2dd467','9086cec8-a28e-4723-9f31-e2e68e173c6a','dng',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('4e996919-6d88-4a4b-880d-5b810705d40b','8612b1de-9825-4cb9-b9bb-0b3b97bbe6b6','fmt/141',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:19'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('4ec79527-6356-42af-87a0-b21192464190','4cd6ff3b-2508-47ac-9d5b-2fcdb61ab1cd','fmt/421',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:31'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('4f48511c-5241-41e1-9818-b7f484da6bb4','7abfb7c5-3f55-466e-bd43-1f886e6a884c','fmt/192',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:13'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('4f6cbeeb-856a-4c12-9fec-2759c3efdbd4','bc9e9d1b-9112-4336-ac8e-c2c0b614777b','tiff',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('50877889-aab9-47d8-8fbe-7d68fe5e91a8','4c451f8c-9af3-468a-ae57-f4ce393eba80','HTML',NULL,'FileIDsByFitsJhoveFormat','1.5','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('517cf3c4-dbea-487a-a2e4-a3d4868fea1a','9a8285cc-f383-4a1b-bf64-60749f065603','fmt/7',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('51a3a233-1cd0-40d6-ae8e-78f4b449bdd7','06b8d06a-aac4-4d09-b0b4-fe67d70faf14','JPEG File Interchange Format',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('5201ef8b-bc95-4cd0-862f-32597f09d206','3cdd3f9e-a093-4c0d-8aef-ef7ba86f765b','image/gif',NULL,'FileIDsByFitsJhoveMimeType','1.5','2012-10-26 18:38:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('5238abe1-28cd-4d3b-b35a-ae241a6f35c3','d72473f6-d239-448b-acfc-c3f177f65aa2','x-fmt/397',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:18'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('5286cc46-aca2-4227-9898-b3fb38d4ceed','5f43abcb-ddeb-439a-a80f-fb62dd2f042e','flv',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('533f8057-e56d-402d-85c4-6808cde014d3','a973a41b-d90a-4081-b0ac-6eeef52d2893','text/xml',NULL,'FileIDsByFitsJhoveMimeType','1.5','2012-10-26 18:38:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('54d62544-2a54-46b9-a081-6b0380ede966','40259ade-4e78-47c9-a8b8-d66e2aa121af','x-fmt/276',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:32'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('55bd6657-d247-42fe-8441-d6f1e3ded242','875a88e8-3fd0-48e9-a184-5f62459a468f','fmt/180',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('55d42b3e-f9e4-49ce-a300-68c534076945','eb216a3d-72a4-459b-8686-6aebab26bbb7','svg',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('566dbf38-31df-464c-a657-07a3259763dc','bbfbbed7-11fa-4cb1-9819-89284ff7d80a','text/rtf',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:09'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('568c4a9c-1c0e-43a0-9378-19c9886b0c80','99818e34-6fe4-455c-81df-cca3b0e70932','Canon CR2 raw image data, version 2.0',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:11'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('570ba6a0-bdbe-4ce0-b45f-b647aad273aa','7a8f358d-9b31-4d29-b258-7d668db64631','x-fmt/389',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:18'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('5715b2ca-3677-4459-bd0e-44138705142b','4f982c49-6962-463b-ae20-1b5494fc7671','image/x-canon-crw',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('572d4940-de04-4c0f-8de8-628b0f521ad2','24237983-b248-4d0d-b1d2-04ca8e469198','Portable Network Graphics',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('577530e0-26ac-41cf-9275-e0624c6ba9b5','61137b8e-803f-4f04-835c-1718670cebc1','fmt/14',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:24'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('57dbdda5-c48d-434c-bce2-31abd992b45d','c219f204-5418-4f45-b148-8216d2a15e88','dcr',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('592df4a5-4835-46d9-8bcc-fea6a772a204','e342d908-5dd2-4ea3-9291-288339829a8c','video/mpeg',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:14'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('5c4875f3-1da0-475d-89af-c672ec07dd86','369eeb06-b520-4566-b9c5-1f7a2277a450','bytestream',NULL,'FileIDsByFitsJhoveFormat','1.5','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('5db2324c-f553-4c41-ba74-ada9ed5eafb9','daf70773-a281-4620-9c75-73350150512e','fmt/117',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:23'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('5f4fb4ac-2707-4bd0-b0c2-b6e286af3e7b','1d0b3087-b43f-412f-8e10-2063b912ed81','fmt/143',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:19'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('5f81e440-41be-4bf5-8a7e-4084df92c5f8','8f063f36-f64c-479e-b37a-2491e99feb6a','image/bmp',NULL,'FileIDsByFitsFfidentMimetype','0.2','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('5f91605a-074c-439f-af3f-91bc423e5023','d4e1477a-ed22-48a3-b1f0-751e5bfa6b5a','fmt/12',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:21'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('5f9d4ccb-bf73-44f9-9d17-bbc0fb208ff7','6c1064bb-f40f-4413-84e5-d40270f6c96b','application/rtf, text/rtf',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('603661a6-306c-4eb7-ac42-7146cbb498bf','1f8df48d-ce76-49e4-80ed-cc0d0ce65d2a','x-fmt/203',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:14'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('605c65fb-5eae-4f47-83cf-0b07f5d57630','9c1ef1bf-7ac0-431e-bfaa-0490b199d2c3','tga',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('60c520ac-ddd2-4747-9e84-6810579cf414','41f460dc-3272-497f-bed4-77e67d459612','application/pdf',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('61218cb5-186b-468e-b6d6-6bc2cc25b2c3','5b3a71d1-5a3b-46a4-a558-a785a10ca262','x-fmt/392',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('6130cc11-2638-4ec7-b908-26d21cd96099','580f3cbb-7604-4775-8776-2dcfc5651139','fmt/39',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:31'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('62690593-79b9-4b43-a558-a5547292ffbd','b8863dbf-f999-4c0f-889f-ccc18b6a6e71','video/x-ms-asf',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('635b4877-6d55-42b3-b4a0-c0897c2023d4','32846451-1dbe-44c0-b599-cfd3bdc73b0e','application/pdf',NULL,'FileIDsByFitsFfidentMimetype','0.2','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('6454b91b-a492-4a8b-9afc-79a3f2b45002','58a160ea-d3a2-4bef-8238-ae36f4e9a013','Microsoft Word',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('64901097-0060-4970-b016-031f2c981182','8533521f-82c8-408f-9499-194e45d50154','image/x-canon-cr2',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:14'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('64ede5d8-a169-4090-b174-e6131cb7588f','15212ba7-3a43-418b-a3b7-d8095c2b77ff','text/plain; charset=US-ASCII',NULL,'FileIDsByFitsJhoveMimeType','1.5','2012-10-26 18:38:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('654da8ca-bfdb-4d28-8d69-7940cc2e9f23','48ddd2b7-7841-4731-a28b-5ebe84a0ff8b','image/gif',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:14'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('65dc107f-e2ab-4d82-8bdd-41cf69bb47a1','a38ef10a-1dab-4643-b93c-dec589951c70','fmt/158',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('65f493b0-397f-4671-9529-bd410759d4c7','f048d680-916b-4631-a19e-2b4c292f010e','fmt/291',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:18'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('670a10a6-5252-4bf2-8918-aa74b6299d33','68d7dce7-17c4-4407-b648-83efa8870d3e','Foveon X3F raw image data, version 2.1, 2268x1512',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('681943dc-fa51-4928-be5b-2e498616a5d1','0c2ad7a0-83b5-4f83-aed9-ee6ad99510b3','Microsoft ASF',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('6867abc3-4bba-47a6-a6eb-64f893872f70','92526fe4-93e8-48c0-ab29-be50bc832bf5','aiff',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('6889c62f-121c-457c-a617-6a93227bf4cc','983636ce-8bfb-4e06-b5f5-a2994af8ccc9','ORF EXIF',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('68bbc28d-055a-400c-8f2f-c29eb122c3b4','92ba8d67-0170-4f39-8f29-316b44522112','mov',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('6a23f98c-22cc-4e27-bf30-301a96fa6429','0f965b92-8f6c-4a52-bffd-a28da459fdf9','image/x-canon-cr2',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('6ab4ac19-e851-4ecb-b8eb-3b8cd48cb2c5','2aab64ea-9bbd-4715-a6bf-ad3ee5ea7072','WAVE',NULL,'FileIDsByFitsJhoveFormat','1.5','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('6b5f73b1-d0e4-4118-b3da-ccef52e6a502','9f66ca0d-34eb-4820-9679-0704fecb73f6','wpd',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('6b6dc773-9afe-46a2-b84e-3932c21cfe71','7a0ab3c1-8ab0-4aac-b064-bff7e6e88f64','fmt/115',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:22'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('6bccb910-0a66-4e5b-bd13-fd5aeddc3fd4','e549810e-8a45-4d3b-9f7a-97ce7f11e853','Adobe Photoshop Image, 800 x 533, RGB, 3x 8-bit channels',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('6d473c49-1c64-4be7-8428-331f5de0bb2f','3da83571-5d77-4edb-9782-859138ac1ed3','application/vnd.ms-powerpoint',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('6d64c940-81f9-41df-851f-a5aad69dd970','860d9543-4d04-4be9-9fed-ff1e35648d28','x-fmt/382',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('6dc078d4-2d47-4739-a613-a31521601595','26a45d64-5e4d-4afc-8297-265f1a4f86bc','Microsoft Excel 2007+',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('6ddd9abf-cc24-481e-a4ed-95b879271d9c','f99c8abb-3364-44d6-b2da-bbcaad58e2ce','xsl',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('6e0e82c8-3ee5-429c-a1db-fcf535f8cd7c','f991f719-4fe8-43d7-b032-2e4a52de8d53','image/tiff',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('6f2d8b01-d05b-4dbb-a64f-53e9feb2cc1b','88f61e4e-ce58-4bc3-9bc8-91d6c3cfcabe','fmt/116',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('6f9d85e9-6176-4d05-a997-04ab02aaa1e3','3651eff9-0c05-4077-8f26-a9dfb915465a','TIFF',NULL,'FileIDsByFitsJhoveFormat','1.5','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('71b1aace-dce0-429b-81a9-34b72fef951d','74455ff3-647d-4d29-a71a-be28fa97a289','image/png',NULL,'FileIDsByFitsFfidentMimetype','0.2','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('71d1d681-4c27-416e-a54c-b9c55352eaeb','aaf1a292-b2e7-4624-8119-a6805bc6702c','jpg',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('71eec4b6-8be4-4d3f-91cb-24a8891dc60d','8a0cc11d-d1ec-4e0d-a895-ac037c9d94f8','pef',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('726f3b41-8d8c-4146-9dc0-53ed124ef6f5','cba89043-1ac9-4e3c-a140-2ac74377c3b0','mrw',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('72d36e83-dddc-4f8e-8d76-3bb869acdc22','480b7fb6-f10b-4631-97f4-f40e0385cdca','Quicktime',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('731f81d5-a8a1-4fbc-ad9a-db6c7f6e13e1','9e55df2f-6153-4d2b-a156-863b1c0148e2','video/quicktime',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('747f08af-58df-4b27-b40c-3c78c64396dd','cba8d14c-a8a8-4676-b1d2-3cd726528825','fmt/13',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:22'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('759cb63e-0434-4070-9ef0-edec412b1591','675e8fdb-67e9-41a8-a505-823cd8348cb9','text/plain',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:14'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('772df9f6-60dc-49a8-b465-e52720c17c2a','257c711a-6d38-4b8c-8c54-6c25dc5a4d43','fmt/104',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:34'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('77b3c9da-90a8-4ad8-a6bb-a3742959035d','b7463782-e37e-447e-90cd-2703deeb21e2','text/html',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('7857ceaf-2c7a-41db-8133-ad74130f67a2','f520ade0-4fe7-4000-8bb5-061a222cb923','txt',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('78731108-55cb-47e3-8b2c-95036d4532b9','d5719ffd-c663-44dd-946e-0818c31629a6','fmt/126',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('788dd63f-da3b-4421-89b1-764cad6e50a5','b339460f-c9b6-43a2-a78d-9d8aaea9c8f2','fmt/157',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:27'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('78af54c2-d2e0-4fa4-a67e-c5cb2a3d513e','c5e493af-195e-4b64-9331-4f716a16eac9','pst',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('790ace69-333a-4d40-bb71-5b254d98eaf5','b8b6dfa3-beca-4667-a279-cf78d610a0b6','video/x-flv',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('79267853-99a8-4f0e-aa37-cf21141d53b3','b63fd011-1df4-4478-86df-da93f3fc1955','fmt/112',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('797fde6c-48c4-42d7-802f-f99eda4f7158','1db0ee9f-a668-45f2-ad79-50e395a9e171','Microsoft PowerPoint 2007+',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('7a547d8a-bed0-4ac3-b7e8-18f608a23738','8309414c-b081-4342-b20b-eea126a97e26','application/vnd.ms-powerpoint',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('7aa434d9-6d19-4613-939d-83bddd902fdf','cabd97ec-12fc-442a-9952-54a9250c6e8c','image/bmp',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('7adbd693-754a-46cf-b8b3-01b0b58857c6','e0e6ab6e-dd08-4b2a-9267-8ed7500655f6','text/xml',NULL,'FileIDsByFitsFfidentMimetype','0.2','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('7b3d1009-1879-4de8-978f-8615cb18f2b9','2c0fbd65-f7ba-452d-bd93-a40b371e2a68','Audio Interchange File Format',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('7b3d2084-d73b-4553-a67f-6f870c36bb5d','4e6f9641-ba13-4855-9385-be92209a245e','x-fmt/385',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:14'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('7b3e2599-05e9-43c3-b8c6-c182835b3a41','38de9fcc-4200-462e-a4d3-70350fc6b7c9','x-fmt/391',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('7bcfc7ea-394d-46d4-a292-17f51c06147e','2184c88b-bbe4-41a1-9a21-281f5cf55433','fmt/480',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:26'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('7c5637c8-d87e-40ef-b2f9-82fa087ccae3','4ae6d5e4-1cd1-481e-8469-eae98f9adb36','x-fmt/367',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('7c79e05c-e596-4a6a-8040-87055ecf8aef','123481b3-aca7-41ec-97d7-7e6601395d4a','fmt/124',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('7cc247a2-13bb-4960-8e5f-28f9d59c28ed','40f0c13e-f8ef-4a50-94cd-f1a2d95ca886','cr2',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('7ec27a59-6f0d-4aaf-9935-779934709485','2c3058ab-a357-4c26-9157-8f11028e63bb','fmt/215',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:14'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('7f51b5a9-0db3-45d6-b262-4dab55a8777e','1b801662-0446-4bf5-bbd5-b093b27a1cef','image/svg+xml',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('7f81e609-c058-4025-8029-62e6dcfe050b','f3fa999a-ba70-482a-a660-f732d27a882e','Apple QuickTime movie (fast start)',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('7ff5b9c4-fefd-474e-bea8-e91c07de1356','5f46c34d-7a26-47eb-b977-1be976205050','audio/x-wave',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:14'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('801f3529-7e4f-4253-a510-c2ad36c98c0e','33da3559-86b0-4db0-8e3f-7339155cc559','audio/x-aiff',NULL,'FileIDsByFitsJhoveMimeType','1.5','2012-10-26 18:38:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('807cfe9b-01bf-4a78-ab7d-417009858dfa','abc707f1-48f6-4e0c-acba-67c818ccdc85','fmt/148',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('8155817b-84b3-4ce8-af2b-5e7d7d126896','78640195-eaae-405f-b3bc-304b806977f9','JPEG 2000 JP2',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('81c4493d-259e-4987-9b87-f4a86c00dd69','477b16c1-28b7-4685-9391-51bbaca936b7','text/plain',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('81ef0156-e0c4-4c4c-ade6-584c740fb532','315e3d93-22df-4993-955a-73d30a11f7d7','Targa image data - Mono - RLE 128 x 128',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('82cfd645-247f-4be5-9b32-2156c10a3038','e0c40516-1727-47b5-89cd-ff8e36764442','HTML',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('830972cb-3313-4881-9d7e-b03e22b1cefd','268c57bc-edfc-4da7-b01a-c1ebb2438022','fmt/131',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('83d61f3f-d30e-4889-bb46-0278baa997fd','44da7e78-68c9-41ca-8441-26f882a463c4','x-fmt/88',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('849868bc-b1b0-4e59-bec3-4e356dcd1d6a','31868203-8848-4644-92c7-27307c5e5ba0','image/vnd.adobe.photoshop',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('84e756b5-b9ea-4f72-95d5-0a0ccb351754','e112c95a-5d2e-4b07-89fb-8775e48514d5','fmt/146',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('86b464f8-a299-4d80-844c-6588d00c1028','4988d669-fe70-404c-8e0b-c297d2eb9ad2','fmt/423',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('87387469-26b1-4e96-9b0e-644340afe63a','eaf3f239-4e9b-42bd-b618-2cad78793b36','crw',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('87e5ac50-a4b7-4716-8131-126bb1138ac4','7b2ab804-b73e-4700-96ca-61ebc9c3576e','Tagged Image File Format',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('89d8f8c4-ad23-4407-a4d6-06a1b08ede83','c57451a7-13f9-4483-94af-5290f7a2bd87','application/vnd.ms-excel',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('8b565e70-c271-4828-b46b-d4ef0f323f76','a86b0683-25af-4e39-9aa3-e0e4a1e7b395','FLAC audio bitstream data, 24 bit, 6 channels, 96 kHz, 28369692 samples',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('8c5c7d8e-6684-425c-b6c2-030ca601774a','5b553136-e75f-4512-b988-689db706fd50','fmt/422',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('8db037df-85ba-46e8-bd4c-cd63f294fd45','fb5fca7c-0a75-4a94-8dc2-0dba6d66747f','image/x-ms-bmp',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('8ddf2d8d-6c75-4035-948d-f6bbf8a8cf2d','dbad8e78-b7af-4039-8aa2-b501c42b7eeb','fmt/438',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:20'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('8ea428c7-de8e-41e5-95ba-030ca6f11b40','e8e6949d-f0ae-4001-b8d0-91bfd45a1b47','image/jp2',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('8f30f15d-e49b-4734-a367-e7e2b6cd8b1c','58e8909c-4c51-489e-bae0-410d82191d6c','xlsx',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('902c76c1-abb0-4d2d-84a6-50c4ec3b1fc9','17d1046c-71bd-4c90-9291-6744e5f2fa38','Waveform Audio',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('906181d3-894a-4294-a695-5454f17baab5','4fb85f9a-831d-49ff-a554-4c80a792232b','Olympus ORF raw image data, little-endian',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('909c148a-b4b7-4ff6-8349-6dcc1911dcd3','0a01c5ed-7e05-4dc8-8082-48fe61bab13d','image/bmp',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('90daba15-4988-4e4d-b590-493928c43c89','73e92bb3-23a0-4fc7-a7e6-4da8b0984506','Flash Video (FLV)',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('91104401-41e5-4063-adb1-51a18485f64a','84e0b908-bd9e-4442-ae34-bc9743b43348','text/html',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('92751e84-b688-4efc-b8af-a87090da8e42','c789e8b6-9d94-47f4-9be5-5a118cd13c0d','fmt/182',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9320d70d-7f58-45b9-b582-18967846c4db','7ff7e6cf-5a65-4e2f-9fc3-6f702374b470','wbk',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9392ded7-03f5-43b1-a1fc-e325cf9a892b','aaf970b7-31ef-40f0-a493-187298baa1b1','x-fmt/384',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('939f1276-016c-40ac-bd01-cb7f30005e48','37384927-53f5-4ac7-8425-94a110c081ce','image/tiff',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('93b5b338-da0c-4d32-b052-1dba459f0e01','6e0f164c-6f87-4f2a-9ebe-23089c33cc46','fmt/41',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('947a78a1-5b28-4470-8f9a-95afe4cd7a4d','4e5c2d3d-0802-432b-9e52-2a98e8e18e8d','image/x-olympus-orf',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('94f3dc1e-c678-47ff-9e18-c332a4a641ba','38903006-89e2-4d59-b0d5-dbf8614500c4','fmt/18',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('95249cd2-60f3-4d61-962d-6f8b9eea4fae','fa1980ea-578a-44f0-9cca-fd9b80f52af2','Graphics Interchange Format',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9635837d-0823-45fb-9938-48ba7113676f','808520c8-c588-40a0-9c32-8518110c0e33','XML',NULL,'FileIDsByFitsJhoveFormat','1.5','2012-10-26 18:38:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('97533006-c9f0-4243-aa2d-704beeecc07f','92ec6955-b6ca-4acb-b8d4-a475efcd8c73','fmt/16',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:24'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('996cdf47-7db7-421e-96c0-05c32fa1a894','f1abb661-2418-4d3a-8d41-aef1a0f57bdc','EPS EXIF',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9a9b541c-2b1e-43dc-8b9b-027b38d7f34a','8aa97c5e-1cf5-4b1b-a20f-58e21598a5fd','RIFF (little-endian) data, WAVE audio, Microsoft PCM, 16 bit, stereo 44100 Hz',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9abbc75d-c527-421b-87b7-a92e6d55e869','350b7514-7b5c-4b83-913f-b56122423dc5','text/rtf',NULL,'FileIDsByFitsFfidentMimetype','0.2','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9b8222f9-71ba-45ee-8b0e-ac135a5cb134','19d7d449-99c3-4a3d-98cb-428665cf25d1','3fr',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9bafb34c-cb02-4baa-a043-9c9a4966d879','a3f41b86-152e-4feb-878a-c83d486b57cf','ASCII',NULL,'FileIDsByFitsJhoveFormat','1.5','2012-10-26 18:38:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9bc15328-5734-4986-9daa-a3e0a1795d9d','ff33f793-b071-4362-a13d-18225470ea98','fmt/123',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:21'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9bd0aa5a-c11b-4418-b37a-21084af0022c','5ff09d4f-217a-4610-8495-7089859d1452','image/svg+xml',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9c2975f6-f6a7-4638-8323-c71a17ce360f','209d95fb-25be-471f-902d-dd08fe5be83f','fmt/354',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:25'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9c53f1f5-a9c5-443a-8105-b4b0375ff801','c9d79a74-a58f-4c71-8977-1129c10f3a5d','application/zip',NULL,'FileIDsByFitsFfidentMimetype','0.2','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9c73425f-7f42-434e-892a-9325743c5bf6','2b6c2412-b978-4478-8d09-b91ed4c6dc6a','fmt/11',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9c79449a-d058-4d75-bdcd-08386082b425','e3bb38be-25d8-4831-b583-84c20ee371a9','css',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9cab3ec0-de73-471e-9601-122b740f4268','1567b10b-1474-4df5-a6df-ce3b8a92ee97','Targa image data - RGB 1419 x 1001',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9d20a54f-bc24-4f1e-bd80-ca7261f1d997','435bcdbe-75b2-44d2-94ef-2710edcf9eb3','fmt/91',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9d52b1e8-18e9-4946-9171-92b2e2823119','a010a604-78c2-4656-846b-d265b95ae771','fmt/191',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:13'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9e34397f-27a2-4efb-92d8-b197e0f6a790','35c9fd79-c9b4-4a0c-84dd-6a9a6a05453a','ARW EXIF',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9e39145f-fe6b-45a4-b746-fd70d4a802b8','05e0d0e9-f51e-45d6-86e5-283f8eeaa00d','x-fmt/390',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9e6a168a-a740-47a1-997a-7af3f9e18a16','085d00ca-f8af-43e5-9ef9-6d21dec5aff8','Windows Bitmap',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9f1897dd-3b62-4eff-ad10-8b02272da021','7a171a9b-49d8-45c7-bb5f-52d9c3157de0','giff',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('9f927013-88d4-4243-917d-3e52275bb47c','b60d9ce6-7f94-4c2d-b1b0-04d74644b48b','ods',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('a001fba6-80a5-40d2-8e05-0ed3f08e7198','fbcf2ae7-4893-4771-833d-8eee20d91f3b','raw',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('a0874400-997f-4755-81e2-e57856c471d4','9548e247-c91d-4e39-99b1-26456cc7ce22','fmt/105',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:34'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('a205da36-de61-4ea9-8a18-60e72c7d31f7','0204d3a6-d566-4f90-b298-578eaf6c99f6','fmt/3',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:17'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('a2175deb-c922-449d-a28f-367dedf966e0','0bd0540c-db99-47e2-b63b-12be1c6eafcf','pdf',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('a383dd7b-d76d-492e-b646-68afc6814f40','fedd6582-993f-498c-86c3-991e758a795f','fmt/50',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('a5228b65-8e02-45ba-934a-44484de14b6f','274e16c7-1b68-498b-8290-e339d4197136','MPEG',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('a6f578e9-e12a-4dbd-8f47-5ef48b6ecf46','83e32dd9-e3ac-4953-8f8e-756ac56d4480','fmt/109',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:35'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('a7810d01-caba-4d9c-8771-f708e58c44d1','ec0a162a-90d3-4731-90c8-6c7ef3396f5c','fmt/45',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('a7c3be20-cf5c-487f-a6fc-a2b9767ab5b3','e7cb481d-e08b-4389-9819-e6bd6e12295c','ppt',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('a7f0fac0-1fdf-44f0-8284-45f93415db02','c0971b7e-06eb-4d4b-856b-5ffb7d2843d6','image/jp2',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('a8ce0153-c919-4bc6-8e8c-cf65e2fe960e','b7b89065-c83b-48f0-bf23-136c6c092dfd','application/vnd.ms-excel',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('a9779e0b-d9cd-4844-8aba-700569642d78','86ce68f3-b264-4e49-a44e-c58144703700','audio/mpeg',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('a9cae264-5383-46b4-9859-1d84226dbe8a','a1bd442c-44f7-431a-9eb3-cfb962872c31','Free Lossless Audio Codec',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('aaa63846-4464-484d-90b4-8262406ec9c8','40e2177a-2219-445b-bd97-56f2ebacfe62','Portable Document Format',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('ab65998e-db6b-4d1b-bc51-fa7888e438f3','26fcd232-8685-40ea-a1b3-e845662d9a51','fmt/437',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:20'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('abf5ae65-61a4-4e22-b4ad-cae80b0a59fa','fe2dfde9-d4b8-4e1f-bcc0-262ec7710c3c','cvs',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('abf5ddf2-9b49-4a32-bbfe-abc4846bee17','cc23bcb7-221b-4ef0-98ab-40726b5aee4b','fmt/5',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('ac12344e-50c8-41fe-8ee7-3926e20a2ad7','97f8b89c-5aed-4c41-97ec-72516fe0df51','fmt/292',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:33'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('adce6348-fd3c-4b9b-bef9-7701cbab6f61','23dca992-ea19-4731-8e96-20739509851c','fmt/402',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('adf88e20-21f9-4a9d-bbd1-a396841fcbc7','78b2cb5f-7b0f-450e-95fe-dd2e8de13770','fmt/144',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:27'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('aecb2b92-f272-4d99-bba1-8b7481c0ae2e','b89940da-f3c3-4dad-9eb6-86bd2ba380ab','ai',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('aee31b46-dcc2-4322-bcd5-d36cabeef14d','a6a4884a-f8e5-4909-905b-df8825040cef','image/x-x3f',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('af13d090-a605-483a-b95f-46518a56db56','fd394578-5b76-4eb9-b40f-f116ec11a09e','fmt/476',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:26'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('af72f326-ce34-4547-a94d-4325c34ed123','b83c3a3d-7feb-4677-b38c-1ecd5118c3f4','Microsoft Powerpoint Presentation',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('af81e5fa-3245-4be9-a767-87b37003a1e2','4953b2ca-d1f2-4905-a8d4-bf95abd54930','application/octet-stream',NULL,'FileIDsByFitsJhoveMimeType','1.5','2012-10-26 18:38:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('afca678a-9df4-4a10-94e3-e4aacca3417a','090ff58a-f772-455b-9743-2615e17b6aa8','image/jpeg',NULL,'FileIDsByFitsFfidentMimetype','0.2','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('affe18c5-9912-4e91-891d-cac13f4f0c6c','b7996d48-fbd6-4f41-9b0e-30ab67feb511','text/rtf',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('b1328ae0-5d12-4953-8b3b-6ae56de178e0','9862f025-b370-4ed0-96a1-0d81813ac256','raf',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('b19c2f1e-c781-4990-b8a1-c1bf4c2be633','833cc7c2-c155-45b6-b927-98fdb20ae86f','video/quicktime',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('b2c2bece-5eeb-4831-a50f-8b449374951e','8cacccb8-d2d5-46d4-a3f5-805ae1fc68cd','fmt/119',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:23'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('b2e20b54-64f7-4e49-b034-837919ce0bb6','0ce828c7-e884-40be-9a6a-51b53cb349c5','x-fmt/396',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:19'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('b3cd697d-90bf-490e-a8be-b4a3d58c4237','4ac74345-b1d9-47d8-bed7-3f41b2bdbc36','wav',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('b44f87bf-8f25-4019-ad50-680bbee42284','e88a46ca-5372-4314-a51c-ec83ac9ef4b4','audio/mpeg',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('b4f87e53-3783-480c-91cf-fca02b85b2b4','dcc00ef2-f22c-4796-87e5-f8b728a6a0dd','fmt/436',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:20'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('b504f1fd-dee3-4cff-81a0-74dce82b07da','b155afa2-038e-42dc-beee-f89e5a3ce41f','tif',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('b7b51c13-6373-495c-a152-f18dcbbb67a0','805785fd-4001-4ef7-9d10-1ef52fcbc9c0','Microsoft Office Document Microsoft Word Document',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('b8add022-38a6-4802-ac4b-3fcfe2bef6a1','c0540086-8d61-4684-b8a8-ed11594c5abf','application/rtf, text/rtf',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('b8c6c0e0-f111-488f-bdac-844c2506eb43','74fa9f99-573c-4c76-964b-4456ac234715','x-fmt/275',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:32'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('b8f3453c-d5d3-4fc1-b482-dd9575e513b1','4d2748e9-7233-4106-b7a1-83e7cfdbff24','image/vnd.adobe.photoshop',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('b9a65325-a5e1-49cb-ace8-94e7b0641fa3','736176b4-73b4-48fa-803c-0846a5f0760e','x-fmt/111',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('ba97e868-9eec-42ca-9604-263bdbb1e239','54e14752-4a89-413a-9ac1-dbb519f61e2a','fmt/40',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:31'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('baeb83ef-d279-4def-9637-f412b44e1e3b','66be432b-20a6-4dbc-8d52-5857f559c40d','fmt/202',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:20'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('bb07eb12-52ff-458a-8dc3-d848144e2fea','9d14b564-3571-4a71-81a0-9b937c61df41','x-fmt/398',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('bb980afe-3e73-4b8f-8337-9053bdd42da9','5e7fbaac-1441-46e6-aedf-c65763e9fbf1','ERF EXIF',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('bbf1f1d3-12df-46be-9b56-d1eeb0b1dd59','30ad24b9-3d4a-4cf8-9986-f45c12210aea','fmt/6',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('bce14e4c-e86b-4373-a2bb-1af1d97cdc4f','2797e915-0097-40f6-ba05-ecf94ab34dc5','arw',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('be1eea63-aa11-43e6-a03e-def5e023c8ce','4648baa2-24e0-4ebd-a31a-2417e4175ad3','fmt/138',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:33'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('bef9b2ee-cd13-4ab5-8bce-831eae30b9f0','82bc2ee5-86e9-4bcb-bb65-90fb1b656754','fmt/96',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('bfa47c06-24a7-40c3-8e86-7fe4a3a1c92e','976dc56c-c92a-4253-ac88-f69cae251794','image/gif',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c03df361-6d86-4f8b-8731-9213af904675','454214be-1ae6-4bdc-8396-a4355bdbc618','avi',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c05bba0d-89c0-45a2-9d9c-462a008ccd9a','9f97ee78-98ae-4a3e-9989-50ca598fee65','video/x-flv',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c0907124-9bbb-4c4c-acd2-23ced541c2e8','db10b9f5-1c41-4638-bf7b-e8f5c214903c','fmt/479',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:26'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c156775d-faae-4187-b4a0-d03b14c0c3dd','2f652af9-abd4-4048-ab58-0873a3912d95','fmt/353',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c1d164ab-b07e-4038-93f8-e1eb822940c5','5cad3529-600c-4b7f-aa68-375a5dc9912e','fmt/92',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:20'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c1ffa5fe-fd3d-4e00-8ec3-77de38c136cb','e65c2ac3-db3d-412e-ad84-021efeca4991','mxf',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c2731679-09cc-488b-abfd-03fd02f2cac4','e5ebd621-9c56-4ab6-a698-75a89b044102','fmt/142',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:19'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c27a3385-2481-4ff6-9fef-02356c0c6b26','833952ec-7936-4a3e-979a-a2b705c31a9d','x-fmt/393',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:14'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c2ee62ad-739d-444b-9990-16daf92b5489','065a6379-da8d-4d90-8a6e-8286b31e850f','image/svg+xml',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c361a014-1b23-47ec-a7c1-547ac92fcfc5','06e64ff4-3de9-40f6-a9c0-bcf0e5aba5c4','Adobe Photoshop Image, 800 x 533, RGB, 3x 8-bit channels',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c3b1399d-7805-4f4f-a2b4-150b78c73434','8ef30615-ebf0-4877-a521-64457b598158','archivematicaMaildir',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c3d86e43-811a-405d-bed7-60b602cf583a','20109b15-39c8-4d10-bac5-617ae2850e22','x-fmt/135',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:05'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c3f52040-8b58-4eee-8474-af08329cc3eb','16d9ada5-08b6-48cc-b4f8-723327803581','fmt/19',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c417b1bd-cb89-4181-801e-9a49de3f3d56','ed15e649-7843-494a-a100-fc2571193f02','OpenDocument Text',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:12'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c434a7c4-a57e-4340-85c4-54b50fbd9ffc','5c998398-3340-4d36-8b34-2cbe7eca1010','fmt/4',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c75a23a6-719a-49b1-88c4-b3dd75e71c3a','adfa26bc-e276-4d69-85f3-63c8bd046d3f','bmp',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c7c22951-2dc1-4754-ae52-0126a7d96a76','1984ade9-588b-4817-8460-9dfea763cd23','fmt/1',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:18'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c814a813-484b-4feb-9e6d-53d8d1074416','c82bc722-a02c-42d5-b894-e45baa4badb2','application/msword',NULL,'FileIDsByFitsFfidentMimetype','0.2','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c9a33459-3689-4c1b-b28d-857d8aca5f72','18d00a54-6b22-4434-8563-e66f80431e9d','ac3',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c9a98c00-f517-43c4-9e10-a959d8d4aaf5','a0af674f-4e72-496b-bf4e-4f126eab7372','AIFF',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:13'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('c9aaae39-ec08-4bfb-b7e9-bddc8c35ee52','1f968676-81a2-4cd4-8d3d-ee86342e5a57','fmt/47',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('ca9ac21e-9fde-44c8-82da-df7195a51c59','a1e102fe-de95-4fc5-9bff-a1cc7058e1cd','wmv',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('cb0c0155-e724-4a26-8a29-04aa28d9e30a','93020f07-2a16-450e-b0fc-e03446952c02','fmt/20',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('cc560c97-164d-4db2-bffe-327bd5906dac','27d6e866-3b9d-487b-b3b7-5a1bc4e72a3f','text/html',NULL,'FileIDsByFitsFfidentMimetype','0.2','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('cc85467c-170b-47a7-b6a7-6348f6439203','adac68c6-1483-430c-ab2e-b594660ae4d6','Audio file with ID3 version 2.3.0, contains: MPEG ADTS, layer III, v1, 128 kbps, 44.1 kHz, JntStereo',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('cdde137f-298d-4434-b337-6451043b45b8','1087d1e9-3dff-4b6e-b1e2-625141bbf814','fmt/107',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:34'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('cde32a4d-0361-4bb4-a9e4-7244defe1ee5','7c5095aa-a9c0-46d7-a67d-e95d628edda8','image/png',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('ce020c32-cddb-4044-b998-c858e95f534f','d4e6b67d-331c-428b-964f-8303ca7eafe5','text/plain',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('cf68f326-4d13-4260-a6d8-da04183a1285','5dd411c8-9ce3-4293-9862-11d526b32725','application/vnd.ms-excel',NULL,'FileIDsByFitsFfidentMimetype','0.2','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('cf9fffe6-a604-4e29-b8c8-624a6dd7f58b','a5cdb910-3eea-4f59-9319-d2c51b6658fd','fmt/110',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:35'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('d02019ea-cf04-4a53-81dc-a426e39cf584','49f8cfb5-afac-44ca-8d47-b408881954e0','PDF document, version 1.4',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:08'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('d05cd3c8-8d12-4367-a61d-9ba672b8f334','b5d57def-9d34-4851-89f9-62b4646ad605','ATSC A/52 aka AC-3 aka Dolby Digital stream, 48 kHz,, complete main (CM) 3 front/2 rear, LFE on,, 448 kbit/s not Dolby Surround encoded',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:09'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('d070f329-11bb-41f3-b765-21afebb49454','c8e9d29a-13ca-4f03-9d2c-5834667f54ea','(Corel/WP)',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:09'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('d1795fac-cac1-4d33-bf85-44804d7c358d','705e9509-6393-481f-8dd5-f4753abcfead','ATSC A/52 aka AC-3 aka Dolby Digital stream, 48 kHz,, complete main (CM) 3 front/2 rear, LFE on,, 448 kbit/s reserved Dolby Surround mode',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:09'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('d2951e93-993c-43bb-b92a-281baf3dfd15','c458b319-6779-4426-a1a1-b01c0cb46194','JPEG',NULL,'FileIDsByFitsJhoveFormat','1.5','2012-10-26 18:38:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('d9f5b15f-5b7f-4401-8e33-1ace1bfd89ee','2c2c85ee-1437-4beb-82fb-0c7fca51c49a','fmt/38',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:33'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('db29f895-f041-4e21-a04f-7cedb371f5e9','a5b94581-b1b5-40c4-975c-0ea11870c237','IFF data, AIFF audio',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:09'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('dc21fbbe-bd7b-4ed5-819e-abb59315a64a','d80f1e2e-a75b-4492-8f7b-e541e3980fb7','application/pdf',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('dccfbbf3-095d-46e3-8881-335d4d0bac59','24950d29-e3c6-408e-89e4-28f689239a13','audio/x-flac',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('dce65bd3-c4bf-47c9-a019-7d6f5afd4a68','b95d9944-b9f3-4dba-bd83-86c6fe3a9ca0','fmt/108',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:35'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('dcf4f23f-bc15-40f4-bf01-4887cee51591','3b8ba73b-9cbd-4a6c-b9ae-6fbd9c5f5104','ATSC A/52 aka AC-3 aka Dolby Digital stream, 48 kHz,, complete main (CM) 3 front/2 rear, LFE on,, 448 kbit/s not Dolby Surround encoded',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:13'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('dcfcf6ab-9941-4f9e-af60-1e3298b18f64','a3797587-2fde-418a-a73c-62541ca30275','audio/x-wave',NULL,'FileIDsByFitsJhoveMimeType','1.5','2012-10-26 18:38:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('ddd17cc4-0e64-4c74-8135-1abb870cb9fd','badbed99-3113-45e7-bf87-40bf5354f88d','wma',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('dde8ed0a-8f39-4b2e-b7f2-cc87532067ef','c687dcf9-2069-4027-8c00-98f4b66fc2e2','rtf',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:31'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('de6ab06e-ba9d-42e7-a132-bfe164f936b3','bff92855-162c-4d4e-abb3-0db3388cf7ae','mbox',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:31'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('df1c13cd-14c3-4462-ad59-6652d6f9307d','931ba3fb-e82b-467a-a349-0320feac9a97','application/vnd.ms-asf',NULL,'FileIDsByFitsDROIDMimeType','3.0','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('df64e500-ed0e-4b4f-9960-b1a7320b7eae','73d4a167-f8c1-4466-8f1e-10ce9ab0851f','RIFF (little-endian) data, WAVE audio, Microsoft PCM, 16 bit, mono 44100 Hz',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:09'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('e0275ab2-c7f1-41cb-9705-80cd6150054a','67621d17-25ea-4cef-863d-1431b3946640','jp2',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:31'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('e03def2e-7468-41ac-9bb4-b854654869c2','573c6a20-1076-4234-a728-0bce8405f097','Advanced Systems Format',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:13'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('e0c8fbcf-e938-4cd0-a7c8-ab2816956ec3','b50f44c8-1d3d-4d0b-b17d-9ab0005ee224','Canon CR2 raw image data, version 2.0',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:09'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('e29774f1-505e-4af6-a795-ec7d8bd96352','114db91c-503f-43c2-a83e-c2de6b817e47','fmt/48',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('e2f7fe17-acd1-448f-8bc7-a7a8417e13ed','07cc11b2-5316-4648-a1fb-71d28d9e2200','text/html',NULL,'FileIDsByFitsJhoveMimeType','1.5','2012-10-26 18:38:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('e32a67c3-46db-49a8-9ce0-9a1023ad6fa8','ee907131-8b73-4dab-9978-97fa820b01fe','png',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:31'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('e3415c88-e712-4ce1-bda4-91ff3aa4c2a2','180a7231-0444-44c9-8371-81825a2be339','image/x-raw',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('e4ea5231-1faf-457f-b77d-f543268ad414','3ffcbbac-7b49-433b-8ffa-275ff81aba00','TIFF image data, big-endian',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:09'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('e54d00e9-a0cf-43a2-9a20-6a071bb4457d','c02e25ee-16f0-4020-80f7-8c378c398dc4','fmt/122',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:21'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('e5d84aaa-c4a5-402c-905b-e054c5eeedff','aa9e07d4-53ce-45c0-b72f-dfbbcb40829f','mpg',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:31'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('e67fa329-a2cd-493c-8085-8d0167791231','62ad9422-842f-437e-b388-620abbbfe876','Encapsulated PostScript File Format',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:13'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('e701b402-7c1f-47a2-aa45-4261eb67d888','0d1861e3-823d-4508-8de4-26bae086efb2','image/jpeg',NULL,'FileIDsByFitsJhoveMimeType','1.5','2012-10-26 18:38:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('e72bcaa6-647f-47b0-bdd9-107a3db35675','82f879a7-fdda-4b3f-a79f-55c1605a85ce','image/jpeg',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('e8399bd8-9e6e-418b-a969-5fe431207bdb','b47ed36b-9e71-4516-ba3a-cb4c91718d8c','JPEG 2000',NULL,'FileIDsByFitsJhoveFormat','1.5','2012-10-26 18:38:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('e84a052f-da5a-47f2-8a64-242c1fbbc8df','3840a1dc-df0f-4a41-a3df-6eb612491571','PDF document, version 1.6',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:09'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('e88430d4-6407-4e91-ae1a-7108c57769a0','a30af6e0-195d-46a8-a46a-cc1682d04896','image/tiff',NULL,'FileIDsByFitsJhoveMimeType','1.5','2012-10-26 18:38:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('ea00dd73-206b-4f28-a76f-1ef8e5b32c15','31a79d37-ac13-4d6f-ba7d-49c9493959c9','fmt/152',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:20'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('ea902620-31ff-4379-80a2-b1afeb466b66','dee6f452-c658-46e1-9fbc-f3dd3727479c','docx',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:31'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('ec157914-e2bb-40f9-8968-f164e363ca54','bc9e29c0-cf88-497c-a9da-76956e4dc08e','fmt/199',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:17'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('ec17c792-4490-4d6a-a4b1-9c26d05126fe','08255d3a-bd85-434e-97a8-14f98b0c4ec8','data',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:09'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('ecb48bda-53e6-407c-8e78-dbddbd60ae8e','84c9fb23-9a6c-4fad-832e-0cc3de838d70','video/x-flv',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('ed10d0c0-4571-477b-be69-01a87770e299','1d31b54e-1745-4b37-9468-d35e8e317d05','PNG image data, 757 x 957, 8-bit/color RGB, non-interlaced',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:09'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('ed88fae6-cdc4-4eaa-9cfa-2de614257b1e','2bb14a8d-7eac-4c93-9060-726b4acefbc9','video/quicktime',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('edec2684-c9bd-4953-ab98-d8bb599b53ec','1d21c442-22b6-490d-b84a-07311da373c1','fmt/276',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:25'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('ee1c53a2-2441-4772-8cfb-bc728ba60640','a974079d-0865-49ef-afd8-0ec91174d323','x-fmt/394',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:14'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('ee41d139-b990-4804-bb2d-3d59cdca7430','473e1574-954b-4b54-a641-8dda3659ddc1','fmt/179',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('ee8f4a3a-ed87-4340-ae09-f8c22e3f98d2','bc0a4930-5a62-4e38-92ae-c7b0b024fecd','log',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:31'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('eedef6be-7192-4944-99fd-c3d4ce407438','4bcc5156-f3c7-4a5f-a99d-006eb61c4ba1','(Corel/WP)',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:13'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('f090f89a-9d8d-45ae-9600-79ba554fd79e','aed7e281-17ad-4834-aa9e-96fdc19ea2f1','Hypertext Markup Language',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:13'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('f0aaaa49-50ed-44d0-8bc2-f825ac2319ad','9796ae62-3ee9-445c-a7d6-867fa5fae36d','mpeg',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:31'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('f2060591-d7f5-46ad-99a4-9faa80448f13','fb06ea33-20d5-4a3b-96fe-a7f654e63c03','audio/x-flac',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('f39b2933-253f-4df9-a2d9-843f2171d706','9ff3de86-38cd-4c4a-816d-048f95ec4f51','mkv',NULL,'FileIDsByExtension',NULL,'2012-10-26 21:29:31'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('f3bda0f0-e89d-4fdf-b489-9fa10f2c1e81','b43d2871-487f-4d31-aaa1-e9bea8dd86fa','fmt/355',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:29'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('f40a1fdc-b317-490c-9041-f26fa46f628a','16d79ccc-4f77-44d7-8b25-054915b289c5','fmt/53',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('f507b1b5-e73f-449d-8e4f-d3ca83836433','1d283460-e24c-4cbb-8476-2a66df560456','application/octet-stream',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('f520a00e-e7df-4198-8754-6db5dc4b26e3','27f6b2b1-0c93-4b79-9f2e-7f10639d8987','application/msword',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('f530f208-979c-443f-8238-ebe5db677e79','d6d087d6-ace3-49ad-882d-67267cd29721','fmt/132',NULL,'FITS DROID PUID','3.0','2012-10-26 18:38:06'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('f5b78a9e-e9ef-4a63-9f4f-21684a582700','00ee1251-c1ec-49cf-95e7-f0c037d23f43','fmt/419',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('f6533f8b-420c-4ffb-b0e0-33148e5d28dc','66ff18ac-f825-49be-8b4f-d48ead99278a','Microsoft Word 2007+',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:09'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('f65b9fc0-c3bf-45bd-bd11-1bcd4fbcd06c','f869b28f-c712-48b5-b095-27dc19c94d09','video/x-ms-wmv',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('f6db522b-a24c-40a1-a024-d8dcfa1b24da','a3698ee9-8770-41d4-b4a2-c8cbafb972bc','fmt/42',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('f7df1f58-8879-4339-a73c-9f79e01a700e','8a0b7119-633e-4f74-a499-8b5535158860','image/x-olympus-orf',NULL,'FileIDsByFitsFileUtilityMimetype','5.09','2012-10-26 18:38:10'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('f865aaaf-e488-4a65-a0d2-b565360793ad','e5b5b610-e9dd-471f-b2c9-08455a23ddfe','application/pdf',NULL,'FileIDsByFitsJhoveMimeType','1.5','2012-10-26 18:38:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('f94c9682-b329-4c98-90b3-02f49966c809','0d2065a9-dce6-41d9-8488-e71063bb0e0e','fmt/147',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:28'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('f9a95f1c-b780-46fc-96fa-8d84b1f87850','57eeece7-6b2a-4dad-9684-b05c96a57988','GIF',NULL,'FileIDsByFitsJhoveFormat','1.5','2012-10-26 18:38:16'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('f9eeaece-f5d0-47e6-85fb-c3902b77dfd5','7d02f6c1-eab0-418a-a633-fba5abce7f8c','fmt/136',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:17'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('fa646395-1abb-473b-8e9d-9f138217a0e5','bd4305e4-73dc-41e8-a63c-11dabe86bad4','text/html',NULL,'FileIDsByFitsFitsMimetype','0.6.1','2012-10-26 18:38:15'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('fda218b4-5a96-49dc-9385-7a149516ceac','8d8ad28c-4bf2-4258-a7f1-d2c04df7b125','JPEG 2000 image data',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:09'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('fe009c40-6cb8-44d2-a23b-40346526335c','1887c956-1e50-4bae-8cb5-3a5a68d2ef80','video/quicktime',NULL,'FileIDsByFitsFfidentMimetype','0.2','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('fe2d7af4-2753-4a35-ba32-9bacc580c3b5','47cd293b-5789-4084-8e9b-6d0404e15a77','fmt/420',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:30'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('fe74de1e-1787-4429-beee-77d3873ff959','23e376ee-554b-4ac0-b34a-86031f77e526','audio/mpeg',NULL,'FileIDsByFitsFfidentMimetype','0.2','2012-10-26 18:38:07'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('fee3875c-d933-4311-82c1-f891caaaa0e9','1ded295a-3065-4da7-8ba7-42edef36a1db','Windows Media Video',NULL,'FileIDsByFitsFitsFormat','0.6.1','2012-10-26 18:38:13'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('ff734dca-1810-41c1-8240-6ae7774b2f9e','4690e672-6b5f-42ac-9cf8-d0a3bafeeaa9','JPEG image data, JFIF standard 1.01',NULL,'FileIDsByFitsFileUtilityFormat','5.09','2012-10-26 18:38:09'); -INSERT INTO `FileIDsBySingleID` (`pk`, `fileID`, `id`, `replaces`, `tool`, `toolVersion`, `lastModified`) VALUES ('ffabb349-cd89-4d3d-917f-dd18185e35c5','d3e84d02-ec80-459d-bba2-2517d9732c3b','fmt/341',NULL,'FITS DROID PUID','3.0','2012-11-09 23:17:17'); -/*!40000 ALTER TABLE `FileIDsBySingleID` ENABLE KEYS */; -set FOREIGN_KEY_CHECKS=1; -UNLOCK TABLES; - - -/* Task #1935 -Remove default access normalization for pptx */ -INSERT INTO CommandRelationship (pk, commandClassification, command, fileID, enabled, replaces, lastModified) -VALUES ('38099d05-afa7-4fb2-9257-3e158388b529', '3141bc6f-7f77-4809-9244-116b235e7330', '53d34372-8805-4adf-b940-6c05b835a94f', '99c1ab3a-dc50-4186-8828-df989f8d3d50', FALSE, '8077515b-71dd-4741-9e57-34a49a647abd', NOW()); - - -/*Bug #4825 -Remove command relationships for non positive file id matches -https://projects.artefactual.com/issues/4825*/ -DELETE FROM CommandRelationship USING CommandRelationship JOIN FileID ON CommandRelationship.fileID = FileID.pk WHERE FileID.description = 'application/octet-stream' OR FileID.description = 'bytestream' OR FileID.description='data'; diff --git a/src/dashboard/src/fpr/south_migrations/recreateData.sh b/src/dashboard/src/fpr/south_migrations/recreateData.sh deleted file mode 100755 index 5f76356c60..0000000000 --- a/src/dashboard/src/fpr/south_migrations/recreateData.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash - -# This file is part of Archivematica. -# -# Copyright 2010-2013 Artefactual Systems Inc. -# -# Archivematica is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Archivematica is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Archivematica. If not, see . - - -database="$FPR_DB_NAME" -origDir="`pwd`" -currentDir="$(dirname $0)" -username="$FPR_DB_USER" -dbpassword="$FPR_DB_PASSWORD" -dbhost="$FPR_DB_HOST" - -if [ ! -z "$dbpassword" ] ; then - dbpassword="-p${dbpassword}" -else - dbpassword="" -fi - -cd "$currentDir" -set +e -echo "Removing existing entries" -mysql $dbhost -u $username "${dbpassword}" --execute="DELETE FROM Command;" $database -mysql $dbhost -u $username "${dbpassword}" --execute="DELETE FROM CommandClassification;" $database -mysql $dbhost -u $username "${dbpassword}" --execute="DELETE FROM CommandRelationship;" $database -mysql $dbhost -u $username "${dbpassword}" --execute="DELETE FROM CommandType;" $database -#mysql $dbhost -u $username "${dbpassword}" --execute="DELETE FROM CommandsSupportedBy;" $database -#mysql $dbhost -u $username "${dbpassword}" --execute="DELETE FROM DefaultCommandsForClassification;" $database -mysql $dbhost -u $username "${dbpassword}" --execute="DELETE FROM FileID;" $database -#mysql $dbhost -u $username "${dbpassword}" --execute="DELETE FROM FileIDGroupMember;" $database -mysql $dbhost -u $username "${dbpassword}" --execute="DELETE FROM FileIDType;" $database -mysql $dbhost -u $username "${dbpassword}" --execute="DELETE FROM FileIDsBySingleID;" $database -#mysql $dbhost -u $username "${dbpassword}" --execute="DELETE FROM $database.Group;" $database -#mysql $dbhost -u $username "${dbpassword}" --execute="DELETE FROM SubGroup;" $database -set -e -mysql $dbhost -u $username "${dbpassword}" --execute="source ./rawdump.sql" $database - -cd "$origDir" -dbpassword="" - diff --git a/src/dashboard/src/main/management/commands/update_elasticsearch_mappings.py b/src/dashboard/src/main/management/commands/update_elasticsearch_mappings.py deleted file mode 100644 index ce75324deb..0000000000 --- a/src/dashboard/src/main/management/commands/update_elasticsearch_mappings.py +++ /dev/null @@ -1,76 +0,0 @@ -"""Update Elasticsearch mappings for Archivematica 1.12 - -This command updates the Elasticsearch mappings for the aips and -aipfiles indices for Archivematica 1.12 to enable sorting on all fields -displayed in the new Archival Storage DataTable and populates the new -filePath.raw subfield in the aipfiles index using an Update By Query. - -Execution example: - -./manage.py update_elasticsearch_mappings -""" - -import sys - -import elasticSearchFunctions as es -from django.conf import settings -from elasticsearch import ElasticsearchException -from main.management.commands import DashboardCommand - - -class Command(DashboardCommand): - help = __doc__ - - def handle(self, *args, **options): - # Check that the AIPs index is enabled before proceeding. - if es.AIPS_INDEX not in settings.SEARCH_ENABLED: - self.error( - "The AIPs indexes are not enabled. Please, make sure to " - "set the *_SEARCH_ENABLED environment variables to `true` " - "to enable the AIPs and Transfers indexes, or to `aips` " - "to only enable the AIPs indexes." - ) - sys.exit(1) - - try: - es.setup_reading_from_conf(settings) - es_client = es.get_client() - except ElasticsearchException: - self.error("Error: Elasticsearch may not be running.") - sys.exit(1) - - # Update the AIPs index mappings. - es_client.indices.put_mapping( - index=es.AIPS_INDEX, - doc_type=es.DOC_TYPE, - body={ - "properties": { - es.ES_FIELD_ACCESSION_IDS: {"type": "keyword"}, - es.ES_FIELD_STATUS: {"type": "keyword"}, - es.ES_FIELD_FILECOUNT: {"type": "integer"}, - es.ES_FIELD_LOCATION: {"type": "keyword"}, - } - }, - ) - - # Update the AIP files index mapping. - es_client.indices.put_mapping( - index=es.AIP_FILES_INDEX, - doc_type=es.DOC_TYPE, - body={ - "properties": { - "accessionid": {"type": "keyword"}, - es.ES_FIELD_STATUS: {"type": "keyword"}, - "filePath": { - "type": "text", - "analyzer": "file_path_and_name", - "fields": {"raw": {"type": "keyword"}}, - }, - } - }, - ) - - # Perform an update by query on the aipfiles index to populate - # the filePath.raw subfield from existing text values. We do - # not specify a query to ensure that all documents are updated. - es_client.update_by_query(es.AIP_FILES_INDEX) diff --git a/src/dashboard/src/settings/components/csp.py b/src/dashboard/src/settings/components/csp.py deleted file mode 100644 index 6a007f6924..0000000000 --- a/src/dashboard/src/settings/components/csp.py +++ /dev/null @@ -1,11 +0,0 @@ -CSP_DEFAULT_SRC = ["'none'"] -CSP_SCRIPT_SRC = ["'self'", "'unsafe-inline'", "'unsafe-eval'"] -CSP_STYLE_SRC = ["'self'", "'unsafe-inline'"] -CSP_IMG_SRC = ["'self'", "data:"] -CSP_FONT_SRC = ["'self'", "data:"] - -# for preview file pane in the appraisal tab -CSP_FRAME_SRC = ["'self'"] - -# for /status -CSP_CONNECT_SRC = ["'self'"] diff --git a/src/dashboard/src/templates/elasticsearch_not_found.html b/src/dashboard/src/templates/elasticsearch_not_found.html deleted file mode 100644 index 2c30ce9682..0000000000 --- a/src/dashboard/src/templates/elasticsearch_not_found.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends "layout.html" %} -{% load i18n %} - -{% block content %} - -

{% trans "ElasticSearch server issue" %}

- -

{{ status }}

- -
-

{% trans "Sorry! The page that you are trying to view is not available." %}

-

{% trans "Please contact an administrator." %}

-
- -{% endblock %} diff --git a/src/dashboard/src/urls.py b/src/dashboard/src/urls.py deleted file mode 100644 index fc7dba8228..0000000000 --- a/src/dashboard/src/urls.py +++ /dev/null @@ -1,59 +0,0 @@ -# This file is part of Archivematica. -# -# Copyright 2010-2013 Artefactual Systems Inc. -# -# Archivematica is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Archivematica is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Archivematica. If not, see . -from django.conf import settings -from django.urls import include -from django.urls import path -from django.urls import re_path - -urlpatterns = [ - path("mcp/", include("components.mcp.urls")), - path("installer/", include("installer.urls")), - path("administration/accounts/", include("components.accounts.urls")), - path("archival-storage/", include("components.archival_storage.urls")), - path("fpr/", include("fpr.urls")), - re_path( - r"^(?Ptransfer|ingest)/", include("components.unit.urls") - ), # URLs common to transfer & ingest - re_path( - r"^transfer/(?P" + settings.UUID_REGEX + ")/rights/", - include("components.rights.transfer_urls"), - ), - path("transfer/", include("components.transfer.urls")), - path("appraisal/", include("components.appraisal.urls")), - re_path( - r"^ingest/(?P" + settings.UUID_REGEX + ")/rights/", - include("components.rights.ingest_urls"), - ), - path("ingest/", include("components.ingest.urls")), - path("administration/", include("components.administration.urls")), - path("filesystem/", include("components.filesystem_ajax.urls")), - path("api/", include("components.api.urls")), - path("file/", include("components.file.urls")), - path("access/", include("components.access.urls")), - path("backlog/", include("components.backlog.urls")), - path("", include("main.urls")), -] - -if settings.PROMETHEUS_ENABLED: - # Include prometheus metrics at /metrics - urlpatterns += [path("", include("django_prometheus.urls"))] - -if settings.OIDC_AUTHENTICATION: - urlpatterns += [path("oidc/", include("mozilla_django_oidc.urls"))] - -if "shibboleth" in settings.INSTALLED_APPS: - urlpatterns += [path("shib/", include("shibboleth.urls"))] diff --git a/tests/MCPClient/conftest.py b/tests/MCPClient/conftest.py index 244772bcf5..e3cf3600fe 100644 --- a/tests/MCPClient/conftest.py +++ b/tests/MCPClient/conftest.py @@ -1,11 +1,13 @@ +import importlib.resources import pathlib import pytest import pytest_django -from client.job import Job from django.utils import timezone -from fpr import models as fprmodels -from main import models + +from archivematica.dashboard.fpr import models as fprmodels +from archivematica.dashboard.main import models +from archivematica.MCPClient.client.job import Job @pytest.fixture(autouse=True) @@ -14,10 +16,7 @@ def set_xml_catalog_files(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv( "XML_CATALOG_FILES", str( - pathlib.Path(__file__).parent.parent.parent - / "src" - / "MCPClient" - / "lib" + importlib.resources.files("archivematica.MCPClient") / "assets" / "catalog" / "catalog.xml" @@ -270,6 +269,6 @@ def settings( shared_directory_path: pathlib.Path, ) -> pytest_django.fixtures.SettingsWrapper: settings.SHARED_DIRECTORY = f"{shared_directory_path}/" - settings.PROCESSING_DIRECTORY = f'{shared_directory_path / "currentlyProcessing"}/' + settings.PROCESSING_DIRECTORY = f"{shared_directory_path / 'currentlyProcessing'}/" return settings diff --git a/tests/MCPClient/test_antivirus.py b/tests/MCPClient/test_antivirus.py index 23ea0647a7..b42b29855c 100644 --- a/tests/MCPClient/test_antivirus.py +++ b/tests/MCPClient/test_antivirus.py @@ -1,61 +1,42 @@ -"""Tests for the archivematica_clamscan.py client script.""" +"""Tests for the antivirus.py client script.""" from collections import OrderedDict from collections import namedtuple from unittest import mock -import archivematica_clamscan import pytest +from clamav_client.scanner import ClamdScanner +from clamav_client.scanner import ClamscanScanner +from clamav_client.scanner import Scanner +from clamav_client.scanner import ScanResult -from . import test_antivirus_clamdscan +from archivematica.MCPClient.clientScripts.antivirus import create_scanner +from archivematica.MCPClient.clientScripts.antivirus import scan_file -def test_get_scanner(settings): - """Test that get_scanner returns the correct instance of antivirus - per the user's configuration. Test return of clamdscanner by default.""" - - # Ensure that environment settings are available to the mock classes. - test_antivirus_clamdscan.setup_clamdscanner(settings) - - # Testing to ensure clamscanner is returned when explicitly set. - settings.CLAMAV_CLIENT_BACKEND = "clamscanner" - scanner = archivematica_clamscan.get_scanner() - assert isinstance(scanner, archivematica_clamscan.ClamScanner) - - # Testing to ensure that clamdscanner is returned when explicitly set. - settings.CLAMAV_CLIENT_BACKEND = "clamdscanner" - scanner = archivematica_clamscan.get_scanner() - assert isinstance(scanner, archivematica_clamscan.ClamdScanner) - - # Testing to ensure that clamdscanner is the default returned scanner. - settings.CLAMAV_CLIENT_BACKEND = "fprot" - scanner = archivematica_clamscan.get_scanner() - assert isinstance(scanner, archivematica_clamscan.ClamdScanner) - - # Testing to ensure that clamdscanner is the default returned scanner when - # the user configures an empty string. - settings.CLAMAV_CLIENT_BACKEND = "" - scanner = archivematica_clamscan.get_scanner() - assert isinstance(scanner, archivematica_clamscan.ClamdScanner) - - # Testing to ensure that clamdscanner is returned when the environment - # hasn't been configured appropriately and None is returned. - settings.CLAMAV_CLIENT_BACKEND = None - scanner = archivematica_clamscan.get_scanner() - assert isinstance(scanner, archivematica_clamscan.ClamdScanner) - - # Testing to ensure that clamdscanner is returned when another variable - # type is specified, e.g. in this instance, an integer. - settings.CLAMAV_CLIENT_BACKEND = 10 - scanner = archivematica_clamscan.get_scanner() - assert isinstance(scanner, archivematica_clamscan.ClamdScanner) +@pytest.mark.parametrize( + "backend_setting, expected_scanner_class", + [ + ("clamscanner", ClamscanScanner), + ("clamdscanner", ClamdScanner), + ("fprot", ClamdScanner), # Default when unknown backend. + ("", ClamdScanner), # Default when empty string. + (None, ClamdScanner), # Default when None. + (10, ClamdScanner), # Default when non-string. + ], +) +def test_create_scanner(backend_setting, expected_scanner_class, settings): + """Test that create_scanner returns the correct instance of antivirus + per the user's configuration.""" + settings.CLAMAV_CLIENT_BACKEND = backend_setting + scanner = create_scanner() + assert isinstance(scanner, expected_scanner_class) args = OrderedDict() args["file_uuid"] = "ec26199f-72a4-4fd8-a94a-29144b02ddd8" args["path"] = "/path" args["date"] = "2019-12-01" -args["task_uuid"] = "c380e94e-7a7b-4ab8-aa72-ec0644cc3f5d" class FileMock: @@ -63,24 +44,42 @@ def __init__(self, size): self.size = size -class ScannerMock(archivematica_clamscan.ScannerBase): - PROGRAM = "Mock" +class ScanResultMock(ScanResult): + def __init__(self, filename, state, details, err, passed): + super().__init__(filename=filename, state=state, details=details, err=err) + self._passed_override = passed + + @property + def passed(self) -> bool: + return self._passed_override - def __init__(self, should_except=False, passed=False): + +class ScannerMock(Scanner): + _program = "ClamAV (clamd)" + _command = "mock" + + def __init__(self, *, should_except: bool = False, passed: bool = False): + super().__init__() self.should_except = should_except self.passed = passed - def scan(self, path): + def scan(self, path: str) -> ScanResultMock: if self.should_except: raise Exception("Something really bad happened!") - return self.passed, None, None + return ScanResultMock( + filename=path, + state="OK", + details="details", + err=None, + passed=self.passed, + ) - def version_attrs(self): - return ("version", "virus_definitions") + def _get_version(self) -> str: + return "ClamAV 0.103.11/27400/Mon Sep 16 10:52:36 2024" def setup_test_scan_file_mocks( - get_scanner, + create_scanner, file_already_scanned_mock, file_objects_get, file_already_scanned=False, @@ -96,25 +95,25 @@ def setup_test_scan_file_mocks( scanner=ScannerMock(should_except=scanner_should_except, passed=scanner_passed), ) - get_scanner.return_value = deps.scanner + create_scanner.return_value = deps.scanner return deps -@mock.patch("archivematica_clamscan.file_already_scanned") -@mock.patch("main.models.File.objects.get") -@mock.patch("archivematica_clamscan.get_scanner") +@mock.patch("archivematica.MCPClient.clientScripts.antivirus.file_already_scanned") +@mock.patch("archivematica.dashboard.main.models.File.objects.get") +@mock.patch("archivematica.MCPClient.clientScripts.antivirus.create_scanner") def test_scan_file_already_scanned( - get_scanner, file_objects_get, file_already_scanned_mock + create_scanner, file_objects_get, file_already_scanned_mock ): deps = setup_test_scan_file_mocks( - get_scanner, + create_scanner, file_already_scanned_mock, file_objects_get, file_already_scanned=True, ) - exit_code = archivematica_clamscan.scan_file([], **dict(args)) + exit_code = scan_file([], **dict(args)) assert exit_code == 0 deps.file_already_scanned.assert_called_once_with(args["file_uuid"]) @@ -170,11 +169,11 @@ def test_scan_file_already_scanned( ), ], ) -@mock.patch("archivematica_clamscan.file_already_scanned") -@mock.patch("main.models.File.objects.get") -@mock.patch("archivematica_clamscan.get_scanner") +@mock.patch("archivematica.MCPClient.clientScripts.antivirus.file_already_scanned") +@mock.patch("archivematica.dashboard.main.models.File.objects.get") +@mock.patch("archivematica.MCPClient.clientScripts.antivirus.create_scanner") def test_scan_file( - get_scanner, + create_scanner, file_objects_get, file_already_scanned_mock, setup_kwargs, @@ -183,7 +182,7 @@ def test_scan_file( settings, ): setup_test_scan_file_mocks( - get_scanner, file_already_scanned_mock, file_objects_get, **setup_kwargs + create_scanner, file_already_scanned_mock, file_objects_get, **setup_kwargs ) # Here the user configurable thresholds for maimum file size, and maximum @@ -195,11 +194,11 @@ def test_scan_file( event_queue = [] - ret = archivematica_clamscan.scan_file(event_queue, **dict(args)) + ret = scan_file(event_queue, **dict(args)) # The integer returned by scan_file() is going to be used as the exit code - # of the archivematica_clamscan.py script which is important for the AM - # workflow in order to control what to do next. + # of the antivirus.py script which is important for the AM workflow in order + # to control what to do next. assert exit_code == ret # A side effect of scan_file() is to queue an event to be created in the diff --git a/tests/MCPClient/test_antivirus_clamdscan.py b/tests/MCPClient/test_antivirus_clamdscan.py deleted file mode 100644 index 5bf353a8ca..0000000000 --- a/tests/MCPClient/test_antivirus_clamdscan.py +++ /dev/null @@ -1,225 +0,0 @@ -"""Tests for the archivematica_clamscan.py client script.""" - -import errno -from collections import namedtuple -from unittest import mock - -import archivematica_clamscan -from clamd import BufferTooLongError -from clamd import ClamdNetworkSocket -from clamd import ClamdUnixSocket -from clamd import ConnectionError - - -def setup_clamdscanner( - settings, addr="/var/run/clamav/clamd.ctl", timeout=10, stream=False -): - settings.CLAMAV_SERVER = addr - settings.CLAMAV_CLIENT_TIMEOUT = timeout - settings.CLAMAV_PASS_BY_STREAM = stream - - return archivematica_clamscan.ClamdScanner() - - -def test_clamdscanner_version_props(settings): - scanner = setup_clamdscanner(settings) - with mock.patch.object( - scanner, - "version_attrs", - return_value=("ClamAV 0.99.2", "23992/Fri Oct 27 05:04:12 2017"), - ): - assert scanner.program() == "ClamAV (clamd)" - assert scanner.version() == "ClamAV 0.99.2" - assert scanner.virus_definitions() == "23992/Fri Oct 27 05:04:12 2017" - - -def test_clamdscanner_version_attrs(settings): - scanner = setup_clamdscanner(settings, addr="/var/run/clamav/clamd.ctl") - with mock.patch.object( - scanner.client, - "version", - return_value="ClamAV 0.99.2/23992/Fri Oct 27 05:04:12 2017", - ) as version: - assert scanner.version_attrs() == ( - "ClamAV 0.99.2", - "23992/Fri Oct 27 05:04:12 2017", - ) - version.assert_called_once() - - -def test_clamdscanner_get_client(settings): - scanner = setup_clamdscanner(settings, addr="/var/run/clamav/clamd.ctl") - assert isinstance(scanner.client, ClamdUnixSocket) - - scanner = setup_clamdscanner(settings, addr="127.0.0.1:1234", timeout=15.5) - assert isinstance(scanner.client, ClamdNetworkSocket) - assert scanner.client.host == "127.0.0.1" - assert scanner.client.port == 1234 - assert scanner.client.timeout == 15.5 - - -def test_clamdscanner_scan(settings): - OKAY_RET = ("OK", None) - ERROR_RET = ("ERROR", "Permission denied") - FOUND_RET = ("FOUND", "Eicar-Test-Signature") - - def patch(pass_by_stream, pass_by_reference, scanner, ret=OKAY_RET, excepts=False): - """Patch the scanner function and enable testing of exceptions raised - by clamdscanner that we want to control. excepts can take an argument - of True to pass a generic exception. excepts can also take an exception - as an argument for better granularity. - """ - pass_by_stream.return_value = {"stream": ret} - pass_by_reference.return_value = {"/file": ret} - deps = namedtuple("deps", ["pass_by_stream", "pass_by_reference"])( - pass_by_stream=pass_by_stream, - pass_by_reference=pass_by_reference, - ) - if excepts is not False: - e = excepts - if excepts is True: - e = Exception("Testing an unmanaged exception.") - deps.pass_by_stream.side_effect = e - deps.pass_by_reference.side_effect = e - return deps - - scanner = setup_clamdscanner(settings, stream=False) - - with mock.patch.object( - scanner, "pass_by_stream" - ) as pass_by_stream, mock.patch.object( - scanner, "pass_by_reference" - ) as pass_by_reference: - deps = patch(pass_by_stream, pass_by_reference, scanner, ret=OKAY_RET) - passed, state, details = scanner.scan("/file") - assert passed is True - assert state == "OK" - assert details is None - deps.pass_by_stream.assert_not_called() - deps.pass_by_reference.assert_called_once() - - scanner = setup_clamdscanner(settings, stream=True) - with mock.patch.object( - scanner, "pass_by_stream" - ) as pass_by_stream, mock.patch.object( - scanner, "pass_by_reference" - ) as pass_by_reference: - deps = patch(pass_by_stream, pass_by_reference, scanner, ret=OKAY_RET) - passed, state, details = scanner.scan("/file") - assert passed is True - assert state == "OK" - assert details is None - deps.pass_by_stream.assert_called_once() - deps.pass_by_reference.assert_not_called() - - with mock.patch.object( - scanner, "pass_by_stream" - ) as pass_by_stream, mock.patch.object( - scanner, "pass_by_reference" - ) as pass_by_reference: - patch(pass_by_stream, pass_by_reference, scanner, ret=ERROR_RET) - passed, state, details = scanner.scan("/file") - assert passed is False - assert state == "ERROR" - assert details == "Permission denied" - - with mock.patch.object( - scanner, "pass_by_stream" - ) as pass_by_stream, mock.patch.object( - scanner, "pass_by_reference" - ) as pass_by_reference: - patch(pass_by_stream, pass_by_reference, scanner, ret=FOUND_RET) - passed, state, details = scanner.scan("/file") - assert passed is False - assert state == "FOUND" - assert details == "Eicar-Test-Signature" - - with mock.patch.object( - scanner, "pass_by_stream" - ) as pass_by_stream, mock.patch.object( - scanner, "pass_by_reference" - ) as pass_by_reference: - # Testing a generic Exception returned by the clamdscan micorservice. - patch(pass_by_stream, pass_by_reference, scanner, ret=OKAY_RET, excepts=True) - passed, state, details = scanner.scan("/file") - assert passed is False - assert state is None - assert details is None - - with mock.patch.object( - scanner, "pass_by_stream" - ) as pass_by_stream, mock.patch.object( - scanner, "pass_by_reference" - ) as pass_by_reference: - # Testing a generic IOError that is not a broken pipe error that we're - # expecting to be able to manage from clamdscan. - patch( - pass_by_stream, - pass_by_reference, - scanner, - ret=OKAY_RET, - excepts=OSError("Testing a generic IO Error"), - ) - passed, state, details = scanner.scan("/file") - assert passed is False - assert state is None - assert details is None - - with mock.patch.object( - scanner, "pass_by_stream" - ) as pass_by_stream, mock.patch.object( - scanner, "pass_by_reference" - ) as pass_by_reference: - # Broken pipe is a known error from the clamd library. - brokenpipe_error = OSError("Testing a broken pipe error") - brokenpipe_error.errno = errno.EPIPE - patch( - pass_by_stream, - pass_by_reference, - scanner, - ret=OKAY_RET, - excepts=brokenpipe_error, - ) - passed, state, details = scanner.scan("/file") - assert passed is None - assert state is None - assert details is None - - with mock.patch.object( - scanner, "pass_by_stream" - ) as pass_by_stream, mock.patch.object( - scanner, "pass_by_reference" - ) as pass_by_reference: - # The INSTREAM size limit error is known to us; test it here. - instream_error = BufferTooLongError("INSTREAM size limit exceeded. ERROR.") - patch( - pass_by_stream, - pass_by_reference, - scanner, - ret=OKAY_RET, - excepts=instream_error, - ) - passed, state, details = scanner.scan("/file") - assert passed is None - assert state is None - assert details is None - - with mock.patch.object( - scanner, "pass_by_stream" - ) as pass_by_stream, mock.patch.object( - scanner, "pass_by_reference" - ) as pass_by_reference: - # The clamd library can return a further error code here, and we we test it - # to make sure that if it does, it is managed. - connection_error = ConnectionError("Error while reading from socket.") - patch( - pass_by_stream, - pass_by_reference, - scanner, - ret=OKAY_RET, - excepts=connection_error, - ) - passed, state, details = scanner.scan("/file") - assert passed is None - assert state is None - assert details is None diff --git a/tests/MCPClient/test_antivirus_clamscan.py b/tests/MCPClient/test_antivirus_clamscan.py deleted file mode 100644 index ac4d71ea42..0000000000 --- a/tests/MCPClient/test_antivirus_clamscan.py +++ /dev/null @@ -1,78 +0,0 @@ -"""Tests for the archivematica_clamscan.py client script.""" - -import subprocess -from unittest import mock - -import archivematica_clamscan -import pytest - - -@pytest.mark.parametrize( - "version, want", - [ - ( - "ClamAV 0.99.2/23992/Fri Oct 27 05:04:12 2017", - ("ClamAV 0.99.2", "23992/Fri Oct 27 05:04:12 2017"), - ), - ("ClamAV 0.99.2", ("ClamAV 0.99.2", None)), - ("Unexpected value", (None, None)), - ], -) -def test_clamav_version_parts(version, want): - got = archivematica_clamscan.clamav_version_parts(version) - assert got == want - - -def setup_clamscanner(): - return archivematica_clamscan.ClamScanner() - - -def test_clamscanner_version_props(): - scanner = setup_clamscanner() - with mock.patch.object( - scanner, - "version_attrs", - return_value=("ClamAV 0.99.2", "23992/Fri Oct 27 05:04:12 2017"), - ): - assert scanner.program() == "ClamAV (clamscan)" - assert scanner.version() == "ClamAV 0.99.2" - assert scanner.virus_definitions() == "23992/Fri Oct 27 05:04:12 2017" - - -def test_clamscanner_version_attrs(settings): - scanner = setup_clamscanner() - with mock.patch.object( - scanner, "_call", return_value="ClamAV 0.99.2/23992/Fri Oct 27 05:04:12 2017" - ) as mock_call: - assert scanner.version_attrs() == ( - "ClamAV 0.99.2", - "23992/Fri Oct 27 05:04:12 2017", - ) - mock_call.assert_called_once_with("-V") - - -def test_clamscanner_scan(settings): - scanner = setup_clamscanner() - with mock.patch.object( - scanner, "_call", return_value="Output of clamscan" - ) as mock_call: - # User configured thresholds need to be sent through to clamscanner and - # executed as part of the call to it. - settings.CLAMAV_CLIENT_MAX_FILE_SIZE = 20 - settings.CLAMAV_CLIENT_MAX_SCAN_SIZE = 20 - - max_file_size = "--max-filesize=%dM" % settings.CLAMAV_CLIENT_MAX_FILE_SIZE - max_scan_size = "--max-scansize=%dM" % settings.CLAMAV_CLIENT_MAX_SCAN_SIZE - - assert scanner.scan("/file") == (True, "OK", None) - mock_call.assert_called_once_with(max_file_size, max_scan_size, "/file") - - mock_call.side_effect = subprocess.CalledProcessError( - 1, "clamscan", "Output of clamscan" - ) - assert scanner.scan("/file") == (False, "FOUND", None) - - mock_call.side_effect = subprocess.CalledProcessError( - 2, "clamscan", "Output of clamscan" - ) - assert scanner.scan("/file") == (False, "ERROR", None) diff --git a/tests/MCPClient/test_archivematicaCreateMETSMetadataCSV.py b/tests/MCPClient/test_archivematicaCreateMETSMetadataCSV.py index 81d80a8347..23e10b1474 100755 --- a/tests/MCPClient/test_archivematicaCreateMETSMetadataCSV.py +++ b/tests/MCPClient/test_archivematicaCreateMETSMetadataCSV.py @@ -5,7 +5,9 @@ from unittest import mock -from archivematicaCreateMETSMetadataCSV import parseMetadataCSV +from archivematica.MCPClient.clientScripts.archivematicaCreateMETSMetadataCSV import ( + parseMetadataCSV, +) content_when_value_has_empty_strings = """ filename,dc.title,dc.type,dc.type,Other metadata diff --git a/tests/MCPClient/test_archivematicaCreateMETSMetadataXML.py b/tests/MCPClient/test_archivematicaCreateMETSMetadataXML.py index b0a4799865..04867c3128 100644 --- a/tests/MCPClient/test_archivematicaCreateMETSMetadataXML.py +++ b/tests/MCPClient/test_archivematicaCreateMETSMetadataXML.py @@ -12,10 +12,13 @@ import metsrw import pytest import requests -from archivematicaCreateMETSMetadataXML import process_xml_metadata from importlib_metadata import version from lxml.etree import parse -from main.models import File + +from archivematica.dashboard.main.models import File +from archivematica.MCPClient.clientScripts.archivematicaCreateMETSMetadataXML import ( + process_xml_metadata, +) METADATA_DIR = Path("objects") / "metadata" TRANSFER_METADATA_DIR = METADATA_DIR / "transfers" / "transfer_a" @@ -127,7 +130,7 @@ def _make_mock_mets(metadata_file_uuids=None): @pytest.fixture def insert_into_events_mock(): with mock.patch( - "archivematicaCreateMETSMetadataXML.insertIntoEvents", + "archivematica.MCPClient.clientScripts.archivematicaCreateMETSMetadataXML.insertIntoEvents", return_value="fake_event", ) as result: yield result @@ -136,7 +139,7 @@ def insert_into_events_mock(): @pytest.fixture def create_event_mock(): with mock.patch( - "archivematicaCreateMETSMetadataXML.createmets2.createEvent", + "archivematica.MCPClient.clientScripts.archivematicaCreateMETSMetadataXML.createmets2.createEvent", return_value="fake_element", ) as result: yield result @@ -180,7 +183,7 @@ def __call__(self, *args, **kwargs): return parse(*args, **kwargs) with mock.patch( - "archivematicaCreateMETSMetadataXML.etree.parse", + "archivematica.MCPClient.clientScripts.archivematicaCreateMETSMetadataXML.etree.parse", mock_parse(), ): yield diff --git a/tests/MCPClient/test_archivematicaCreateMETSRights.py b/tests/MCPClient/test_archivematicaCreateMETSRights.py index 06baa79813..a2f335a1e9 100644 --- a/tests/MCPClient/test_archivematicaCreateMETSRights.py +++ b/tests/MCPClient/test_archivematicaCreateMETSRights.py @@ -1,12 +1,16 @@ -import archivematicaCreateMETSRights import pytest -from create_mets_v2 import MetsState -from main import models -from namespaces import NSMAP + +from archivematica.archivematicaCommon.namespaces import NSMAP +from archivematica.dashboard.main import models +from archivematica.MCPClient.clientScripts import archivematicaCreateMETSRights +from archivematica.MCPClient.clientScripts.create_mets_v2 import MetsState @pytest.fixture() def rights_statement(db, sip_file): + models.MetadataAppliesToType.objects.get_or_create( + pk="7f04d9d4-92c2-44a5-93dc-b7bfdf0c1f17", description="File" + ) statement = models.RightsStatement.objects.create( metadataappliestotype=models.MetadataAppliesToType.objects.get( id=models.MetadataAppliesToType.FILE_TYPE diff --git a/tests/MCPClient/test_archivematicaCreateMETSRightsDspaceMDRef.py b/tests/MCPClient/test_archivematicaCreateMETSRightsDspaceMDRef.py index 4f309f4456..ba35b8e8d2 100644 --- a/tests/MCPClient/test_archivematicaCreateMETSRightsDspaceMDRef.py +++ b/tests/MCPClient/test_archivematicaCreateMETSRightsDspaceMDRef.py @@ -1,14 +1,15 @@ import pathlib from unittest import mock -import namespaces as ns import pytest -from archivematicaCreateMETSRightsDspaceMDRef import ( + +from archivematica.archivematicaCommon import namespaces as ns +from archivematica.dashboard.main import models +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts.archivematicaCreateMETSRightsDspaceMDRef import ( archivematicaCreateMETSRightsDspaceMDRef, ) -from client.job import Job -from create_mets_v2 import MetsState -from main import models +from archivematica.MCPClient.clientScripts.create_mets_v2 import MetsState @pytest.fixture @@ -142,7 +143,7 @@ def test_archivematicaCreateMETSRightsDspaceMDRef(transfer_data): @pytest.mark.django_db @mock.patch( - "archivematicaCreateMETSRightsDspaceMDRef.createMDRefDMDSec", + "archivematica.MCPClient.clientScripts.archivematicaCreateMETSRightsDspaceMDRef.createMDRefDMDSec", side_effect=Exception("error"), ) def test_archivematicaCreateMETSRightsDspaceMDRef_handle_exceptions( diff --git a/tests/MCPClient/test_assign_file_uuids.py b/tests/MCPClient/test_assign_file_uuids.py index 6f8000ee8f..293395509a 100644 --- a/tests/MCPClient/test_assign_file_uuids.py +++ b/tests/MCPClient/test_assign_file_uuids.py @@ -2,9 +2,10 @@ from unittest import mock import pytest -from assign_file_uuids import call -from client.job import Job -from main import models + +from archivematica.dashboard.main import models +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts.assign_file_uuids import call @pytest.fixture @@ -28,7 +29,7 @@ def test_call_creates_transfer_files_and_events(sip_directory_path, transfer): job = mock.MagicMock( spec=Job, args=[ - "assign_file_uuids.py", + "archivematica.MCPClient.clientScripts.assign_file_uuids.py", "--transferUUID", str(transfer.uuid), "--sipDirectory", @@ -56,10 +57,10 @@ def test_call_creates_transfer_files_and_events(sip_directory_path, transfer): @pytest.mark.parametrize( "job_arguments", [ - (["assign_file_uuids.py"]), + (["archivematica.MCPClient.clientScripts.assign_file_uuids.py"]), ( [ - "assign_file_uuids.py", + "archivematica.MCPClient.clientScripts.assign_file_uuids.py", "--transferUUID", str(uuid.uuid4()), "--sipUUID", @@ -86,8 +87,10 @@ def test_call_validates_job_arguments(job_arguments): @pytest.mark.django_db @mock.patch("metsrw.METSDocument.fromfile") -@mock.patch("assign_file_uuids.find_mets_file") -@mock.patch("assign_file_uuids.get_file_info_from_mets") +@mock.patch("archivematica.MCPClient.clientScripts.assign_file_uuids.find_mets_file") +@mock.patch( + "archivematica.MCPClient.clientScripts.assign_file_uuids.get_file_info_from_mets" +) def test_call_updates_transfer_file_on_reingest( get_file_info_from_mets, find_mets_file, @@ -108,7 +111,7 @@ def test_call_updates_transfer_file_on_reingest( job = mock.MagicMock( spec=Job, args=[ - "assign_file_uuids.py", + "archivematica.MCPClient.clientScripts.assign_file_uuids.py", "--transferUUID", str(transfer.uuid), "--sipDirectory", diff --git a/tests/MCPClient/test_assign_uuids_to_directories.py b/tests/MCPClient/test_assign_uuids_to_directories.py index f28b3e3451..cc3d155b6a 100755 --- a/tests/MCPClient/test_assign_uuids_to_directories.py +++ b/tests/MCPClient/test_assign_uuids_to_directories.py @@ -1,9 +1,10 @@ from unittest import mock import pytest -from assign_uuids_to_directories import main -from client.job import Job -from main import models + +from archivematica.dashboard.main import models +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts.assign_uuids_to_directories import main @pytest.fixture diff --git a/tests/MCPClient/test_characterize_file.py b/tests/MCPClient/test_characterize_file.py index 5267971b0d..3a70ee66ac 100644 --- a/tests/MCPClient/test_characterize_file.py +++ b/tests/MCPClient/test_characterize_file.py @@ -1,20 +1,23 @@ from unittest import mock -import characterize_file import pytest -from client.job import Job -from fpr import models as fprmodels -from main import models + +from archivematica.dashboard.fpr import models as fprmodels +from archivematica.dashboard.main import models +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts import characterize_file @pytest.fixture def rule_with_xml_output_format( format_version: fprmodels.FormatVersion, ) -> fprmodels.FPRule: + output_format_version, _ = fprmodels.FormatVersion.objects.get_or_create( + pronom_id="fmt/101", format=format_version.format + ) + return fprmodels.FPRule.objects.create( - command=fprmodels.FPCommand.objects.create( - output_format=fprmodels.FormatVersion.objects.get(pronom_id="fmt/101") - ), + command=fprmodels.FPCommand.objects.create(output_format=output_format_version), format=format_version, purpose="characterization", ) @@ -80,7 +83,7 @@ def test_job_succeeds_if_no_characterization_rules_exist( ["bashScript", "pythonScript"], ids=["bashScript", "pythonScript"], ) -@mock.patch("characterize_file.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.characterize_file.executeOrRun") def test_job_executes_command( execute_or_run: mock.Mock, script_type: str, @@ -118,7 +121,7 @@ def test_job_executes_command( @pytest.mark.django_db -@mock.patch("characterize_file.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.characterize_file.executeOrRun") def test_job_fails_if_command_fails( execute_or_run: mock.Mock, sip_file: models.File, @@ -153,9 +156,11 @@ def test_job_fails_if_command_fails( @pytest.mark.django_db -@mock.patch("characterize_file.insertIntoFPCommandOutput") -@mock.patch("characterize_file.etree") -@mock.patch("characterize_file.executeOrRun") +@mock.patch( + "archivematica.MCPClient.clientScripts.characterize_file.insertIntoFPCommandOutput" +) +@mock.patch("archivematica.MCPClient.clientScripts.characterize_file.etree") +@mock.patch("archivematica.MCPClient.clientScripts.characterize_file.executeOrRun") def test_job_saves_valid_xml_command_output( execute_or_run: mock.Mock, etree: mock.Mock, @@ -196,7 +201,7 @@ def test_job_saves_valid_xml_command_output( @pytest.mark.django_db -@mock.patch("characterize_file.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.characterize_file.executeOrRun") def test_job_fails_with_invalid_xml_command_output( execute_or_run: mock.Mock, sip_file: models.File, diff --git a/tests/MCPClient/test_check_for_access_directory.py b/tests/MCPClient/test_check_for_access_directory.py index abcb291ece..9f9df9ad5a 100644 --- a/tests/MCPClient/test_check_for_access_directory.py +++ b/tests/MCPClient/test_check_for_access_directory.py @@ -1,11 +1,12 @@ from unittest import mock -import check_for_access_directory import pytest -from client.job import Job from django.utils import timezone -from main.models import Event -from main.models import File + +from archivematica.dashboard.main.models import Event +from archivematica.dashboard.main.models import File +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts import check_for_access_directory @pytest.mark.django_db diff --git a/tests/MCPClient/test_create_aic_mets.py b/tests/MCPClient/test_create_aic_mets.py index b0c8ec0062..4e8d8db47d 100644 --- a/tests/MCPClient/test_create_aic_mets.py +++ b/tests/MCPClient/test_create_aic_mets.py @@ -1,13 +1,11 @@ -import os import uuid from unittest import mock -import create_aic_mets -import namespaces as ns import pytest from lxml import etree -THIS_DIR = os.path.dirname(os.path.abspath(__file__)) +from archivematica.archivematicaCommon import namespaces as ns +from archivematica.MCPClient.clientScripts import create_aic_mets def extract_file_mock(aip_uuid, mets_in_aip, mets_path): @@ -16,16 +14,21 @@ def extract_file_mock(aip_uuid, mets_in_aip, mets_path): @pytest.mark.django_db -@mock.patch("fileOperations.addFileToSIP") -@mock.patch("databaseFunctions.insertIntoDerivations") -@mock.patch("storageService.extract_file", side_effect=extract_file_mock) +@mock.patch("archivematica.archivematicaCommon.fileOperations.addFileToSIP") +@mock.patch("archivematica.archivematicaCommon.databaseFunctions.insertIntoDerivations") @mock.patch( - "create_mets_v2.getDublinCore", + "archivematica.archivematicaCommon.storageService.extract_file", + side_effect=extract_file_mock, +) +@mock.patch( + "archivematica.MCPClient.clientScripts.create_mets_v2.getDublinCore", return_value=etree.Element( ns.dctermsBNS + "dublincore", nsmap={"dcterms": ns.dctermsNS, "dc": ns.dcNS} ), ) -@mock.patch("create_mets_v2.SIPMetadataAppliesToType") +@mock.patch( + "archivematica.MCPClient.clientScripts.create_mets_v2.SIPMetadataAppliesToType" +) def test_create_aic_mets( SIPMetadataAppliesToType, getDublinCore, diff --git a/tests/MCPClient/test_create_aip_mets.py b/tests/MCPClient/test_create_aip_mets.py index a56eb73e3c..5111bb744e 100644 --- a/tests/MCPClient/test_create_aip_mets.py +++ b/tests/MCPClient/test_create_aip_mets.py @@ -1,5 +1,6 @@ import collections import csv +import importlib.resources import os import pathlib import random @@ -7,18 +8,18 @@ import tempfile import unittest from pathlib import Path -from typing import List -import archivematicaCreateMETSMetadataCSV -import archivematicaCreateMETSRights -import create_mets_v2 -import namespaces as ns import pytest -from client.job import Job from django.test import TestCase from lxml import etree -from main.models import RightsStatement -from version import get_preservation_system_identifier + +from archivematica.archivematicaCommon import namespaces as ns +from archivematica.archivematicaCommon.version import get_preservation_system_identifier +from archivematica.dashboard.main.models import RightsStatement +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts import archivematicaCreateMETSMetadataCSV +from archivematica.MCPClient.clientScripts import archivematicaCreateMETSRights +from archivematica.MCPClient.clientScripts import create_mets_v2 THIS_DIR = pathlib.Path(__file__).parent @@ -30,7 +31,7 @@ class TestNormativeStructMap(TestCase): """Test creation of normative structMap.""" - fixture_files: List[str] = [] + fixture_files: list[str] = [] fixtures = [os.path.join(THIS_DIR, "fixtures", p) for p in fixture_files] @pytest.fixture(autouse=True) @@ -753,8 +754,11 @@ class TestCustomStructMap(TestCase): os.path.join("custom_structmaps", "model", "files.json"), ] fixtures = [os.path.join(THIS_DIR, "fixtures", p) for p in fixture_files] - mets_xsd_path = os.path.abspath( - os.path.join(THIS_DIR, "../../src/MCPClient/lib/assets/mets/mets.xsd") + mets_xsd_path = ( + importlib.resources.files("archivematica.MCPClient") + / "assets" + / "mets" + / "mets.xsd" ) @pytest.fixture(autouse=True) @@ -864,18 +868,20 @@ def test_create_file_sec(self): ) fnames = [label.attrib["LABEL"] for label in labels] uuids = [fptr.attrib["FILEID"] for fptr in fptrs] - assert isinstance( - self.structmap_div_element, etree._Element - ), "createFileSec didn't return an XML structure to work with" + assert isinstance(self.structmap_div_element, etree._Element), ( + "createFileSec didn't return an XML structure to work with" + ) assert self.count_dir_objects(self.objects_dir) == ( len(fnames) and len(uuids) ), "Count of objects on disk and in fileSec do not match." - assert ( - self.state.fileNameToFileID is not None - ), "fileNameToFileID mapping hasn't been generated" - assert ( - self.count_dir_objects(self.objects_dir) == len(self.state.fileNameToFileID) - ), "State hasn't been generated for all objects on disk, duplicate names may not be counted for" + assert self.state.fileNameToFileID is not None, ( + "fileNameToFileID mapping hasn't been generated" + ) + assert self.count_dir_objects(self.objects_dir) == len( + self.state.fileNameToFileID + ), ( + "State hasn't been generated for all objects on disk, duplicate names may not be counted for" + ) def test_get_included_structmap_invalid_mets(self): """Integration test ensuring that it is possible for the mets @@ -894,7 +900,9 @@ def test_get_included_structmap_invalid_mets(self): try: self.validate_mets(self.mets_xsd_path, broken_structmap_path) except etree.DocumentInvalid: - assert True, "Expecting a validation error so that we know validation is working correctly" + assert True, ( + "Expecting a validation error so that we know validation is working correctly" + ) def test_get_included_structmap_valid_mets(self): """Test the valid output of custom structmaps in create_mets_v2.""" @@ -977,20 +985,20 @@ def test_get_included_structmap_valid_mets(self): == f"structmap_{self.state.globalStructMapCounter}" ), "structmap id is incorrect" else: - assert ( - custom_structmap.attrib["ID"].lower() == res.structmap_id - ), "structmap id hasn't been maintained" + assert custom_structmap.attrib["ID"].lower() == res.structmap_id, ( + "structmap id hasn't been maintained" + ) fids = custom_structmap.xpath( "//*[@FILEID]", namespaces={"mets:": ns.metsNS} ) assert len(fids) == res.replaced_count, "Count of FILEIDs is incorrect" - assert len({fid.attrib["FILEID"] for fid in fids}) == len( - res.files - ), "Uneven replacement of IDs for files in structmap" + assert len({fid.attrib["FILEID"] for fid in fids}) == len(res.files), ( + "Uneven replacement of IDs for files in structmap" + ) for fileid in [fid.attrib["FILEID"] for fid in fids]: - assert fileid in list( - self.state.fileNameToFileID.values() - ), "Expected FILEID not in returned structmap" + assert fileid in list(self.state.fileNameToFileID.values()), ( + "Expected FILEID not in returned structmap" + ) def test_get_included_structmap_incomplete_mets(self): """Test the output of custom structmaps in create_mets_v2 where the @@ -1024,9 +1032,9 @@ def test_get_included_structmap_incomplete_mets(self): state=self.state, custom_structmap=res.structmap_name, ) - assert ( - custom_structmap == [] - ), f"Return from include_custom_structmap should be an empty array: {custom_structmap}" - assert ( - self.state.error_accumulator.error_count == 1 - ), "error counter should be incremented on error" + assert custom_structmap == [], ( + f"Return from include_custom_structmap should be an empty array: {custom_structmap}" + ) + assert self.state.error_accumulator.error_count == 1, ( + "error counter should be incremented on error" + ) diff --git a/tests/MCPClient/test_create_dataverse_mets.py b/tests/MCPClient/test_create_dataverse_mets.py index f19327091e..993cce609b 100644 --- a/tests/MCPClient/test_create_dataverse_mets.py +++ b/tests/MCPClient/test_create_dataverse_mets.py @@ -4,10 +4,11 @@ import os from collections import namedtuple -import convert_dataverse_structure import metsrw import pytest -from client.job import Job + +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts import convert_dataverse_structure # List of Dataverse metadata fixtures. We use a named-tuple to provide some # structure to this index so that we can keep track of information regarding @@ -202,9 +203,9 @@ def test_generated_mets_(self, fixture, tmpdir): except metsrw.MetsError: pytest.fail(f"Could not parse mets {mets_path}") - assert ( - len(mets.all_files()) == fixture.all_file_count - ), f"File count incorrect: '{len(mets.all_files())}', expected '{fixture.all_file_count}'" + assert len(mets.all_files()) == fixture.all_file_count, ( + f"File count incorrect: '{len(mets.all_files())}', expected '{fixture.all_file_count}'" + ) dir_counter = 0 item_counter = 0 @@ -214,12 +215,12 @@ def test_generated_mets_(self, fixture, tmpdir): if file_.type == "Item": item_counter += 1 - assert ( - dir_counter == fixture.dir_count - ), f"Directory count incorrect: '{dir_counter}', expected: '{fixture.dir_count}'" - assert ( - item_counter == fixture.item_count - ), f"Item count incorrect: '{item_counter}', expected: '{fixture.item_count}'" + assert dir_counter == fixture.dir_count, ( + f"Directory count incorrect: '{dir_counter}', expected: '{fixture.dir_count}'" + ) + assert item_counter == fixture.item_count, ( + f"Item count incorrect: '{item_counter}', expected: '{fixture.item_count}'" + ) @pytest.mark.parametrize( "fixture", [dv_1, dv_2, dv_3, dv_4, dv_5, dv_6, dv_7, dv_8] @@ -254,9 +255,9 @@ def test_mets_content(self, fixture, tmpdir): # is expected. title = mets_root.findall(".//ddi:titl", namespace) assert len(title) == 1 - assert ( - title[0].text == fixture.ddi_title - ), f"DDI title: '{title[0].text}' is not what was expected: '{fixture.ddi_title}'" + assert title[0].text == fixture.ddi_title, ( + f"DDI title: '{title[0].text}' is not what was expected: '{fixture.ddi_title}'" + ) # Test that we have a single PID and that it matches what is expected. pid = mets_root.findall(".//ddi:IDNo", namespace) @@ -264,21 +265,21 @@ def test_mets_content(self, fixture, tmpdir): # Agency is synonymous with Type in our use of the PID Agency value. # N.B. The agency providing the persistent identifier. pid_agency = pid[0].get("agency") - assert ( - pid_agency == fixture.pid_type - ), f"PID type: '{pid_agency}' is not what was expected: '{fixture.pid_type}'" - assert ( - pid[0].text == fixture.pid_value - ), f"PID value: '{pid[0].text}' is not what was expected: '{fixture.pid_value}" + assert pid_agency == fixture.pid_type, ( + f"PID type: '{pid_agency}' is not what was expected: '{fixture.pid_type}'" + ) + assert pid[0].text == fixture.pid_value, ( + f"PID value: '{pid[0].text}' is not what was expected: '{fixture.pid_value}" + ) # Title is used in three other locations in the METS. Make sure that # they are found as well. for map_ in mets_root.findall("{http://www.loc.gov/METS/}structMap"): for entry in map_: if entry.get("Type") == "Directory" and entry.get("DMDID") is not None: - assert ( - entry.get("LABEL") == fixture.ddi_title - ), "Title not found in METS struct maps where expected" + assert entry.get("LABEL") == fixture.ddi_title, ( + "Title not found in METS struct maps where expected" + ) # Test that the MDRef count is what is expected. refs = mets_root.findall(".//{http://www.loc.gov/METS/}mdRef") diff --git a/tests/MCPClient/test_create_mets_v2.py b/tests/MCPClient/test_create_mets_v2.py index 1fd62bc338..445920249b 100644 --- a/tests/MCPClient/test_create_mets_v2.py +++ b/tests/MCPClient/test_create_mets_v2.py @@ -1,20 +1,32 @@ +import pathlib import uuid from contextlib import ExitStack as does_not_raise from unittest import mock import pytest -from create_mets_v2 import createDMDIDsFromCSVMetadata -from create_mets_v2 import main +import pytest_django from lxml import etree -from main.models import DublinCore -from main.models import File -from main.models import MetadataAppliesToType -from main.models import SIPArrange -from namespaces import NSMAP + +from archivematica.archivematicaCommon.namespaces import NSMAP +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import DublinCore +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import MetadataAppliesToType +from archivematica.dashboard.main.models import SIPArrange +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts.create_mets_v2 import ( + createDMDIDsFromCSVMetadata, +) +from archivematica.MCPClient.clientScripts.create_mets_v2 import main -@mock.patch("create_mets_v2.createDmdSecsFromCSVParsedMetadata", return_value=[]) -def test_createDMDIDsFromCSVMetadata_finds_non_ascii_paths(dmd_secs_creator_mock): +@mock.patch( + "archivematica.MCPClient.clientScripts.create_mets_v2.createDmdSecsFromCSVParsedMetadata", + return_value=[], +) +def test_createDMDIDsFromCSVMetadata_finds_non_ascii_paths( + dmd_secs_creator_mock: mock.Mock, +) -> None: state_mock = mock.Mock( **{ "CSV_METADATA": { @@ -38,7 +50,7 @@ def test_createDMDIDsFromCSVMetadata_finds_non_ascii_paths(dmd_secs_creator_mock @pytest.fixture() -def objects_path(sip_directory_path): +def objects_path(sip_directory_path: pathlib.Path) -> pathlib.Path: objects_path = sip_directory_path / "objects" objects_path.mkdir() @@ -46,7 +58,7 @@ def objects_path(sip_directory_path): @pytest.fixture() -def empty_dir_path(objects_path): +def empty_dir_path(objects_path: pathlib.Path) -> pathlib.Path: empty_dir_path = objects_path / "empty_dir" empty_dir_path.mkdir() @@ -54,7 +66,9 @@ def empty_dir_path(objects_path): @pytest.fixture() -def metadata_csv(sip, sip_directory_path, objects_path): +def metadata_csv( + sip: SIP, sip_directory_path: pathlib.Path, objects_path: pathlib.Path +) -> File: (objects_path / "metadata").mkdir() metadata_csv = objects_path / "metadata" / "metadata.csv" metadata_csv.write_text("Filename,dc.title\nobjects/file1,File 1") @@ -80,7 +94,11 @@ def metadata_csv(sip, sip_directory_path, objects_path): @pytest.fixture() -def sip_dublincore(sip): +def sip_dublincore(sip: SIP) -> DublinCore: + MetadataAppliesToType.objects.get_or_create( + pk="3e48343d-e2d2-4956-aaa3-b54d26eb9761", description="SIP" + ) + return DublinCore.objects.create( metadataappliestotype_id=MetadataAppliesToType.SIP_TYPE, metadataappliestoidentifier=sip.pk, @@ -91,7 +109,7 @@ def sip_dublincore(sip): @pytest.fixture() -def file_path(objects_path): +def file_path(objects_path: pathlib.Path) -> pathlib.Path: file_path = objects_path / "file1" file_path.write_text("Hello world") @@ -99,7 +117,9 @@ def file_path(objects_path): @pytest.fixture() -def sip_file(sip_file, sip_directory_path, file_path): +def sip_file( + sip_file: File, sip_directory_path: pathlib.Path, file_path: pathlib.Path +) -> File: sip_file.originallocation = ( f"%transferDirectory%{file_path.relative_to(sip_directory_path)}".encode() ) @@ -112,7 +132,9 @@ def sip_file(sip_file, sip_directory_path, file_path): @pytest.mark.django_db -def test_simple_mets(mcp_job, sip_directory_path, sip, sip_file): +def test_simple_mets( + mcp_job: Job, sip_directory_path: pathlib.Path, sip: SIP, sip_file: File +) -> None: mets_path = sip_directory_path / f"METS.{sip.uuid}.xml" main( mcp_job, @@ -145,8 +167,12 @@ def test_simple_mets(mcp_job, sip_directory_path, sip, sip_file): @pytest.mark.django_db def test_aip_mets_includes_dublincore( - mcp_job, sip_directory_path, sip, sip_dublincore, sip_file -): + mcp_job: Job, + sip_directory_path: pathlib.Path, + sip: SIP, + sip_dublincore: DublinCore, + sip_file: File, +) -> None: mets_path = sip_directory_path / f"METS.{sip.uuid}.xml" main( mcp_job, @@ -180,8 +206,12 @@ def test_aip_mets_includes_dublincore( @pytest.mark.django_db def test_aip_mets_includes_dublincore_via_metadata_csv( - mcp_job, sip_directory_path, sip, sip_file, metadata_csv -): + mcp_job: Job, + sip_directory_path: pathlib.Path, + sip: SIP, + sip_file: File, + metadata_csv: File, +) -> None: mets_path = sip_directory_path / f"METS.{sip.uuid}.xml" main( mcp_job, @@ -211,8 +241,13 @@ def test_aip_mets_includes_dublincore_via_metadata_csv( @pytest.mark.django_db def test_aip_mets_normative_directory_structure( - mcp_job, sip_directory_path, sip, sip_file, metadata_csv, empty_dir_path -): + mcp_job: Job, + sip_directory_path: pathlib.Path, + sip: SIP, + sip_file: File, + metadata_csv: File, + empty_dir_path: pathlib.Path, +) -> None: mets_path = sip_directory_path / f"METS.{sip.uuid}.xml" main( mcp_job, @@ -262,18 +297,20 @@ def test_aip_mets_normative_directory_structure( (None, [], does_not_raise()), ], ) -@mock.patch("create_mets_v2.archivematicaCreateMETSMetadataXML.process_xml_metadata") +@mock.patch( + "archivematica.MCPClient.clientScripts.create_mets_v2.archivematicaCreateMETSMetadataXML.process_xml_metadata" +) def test_xml_validation_fail_on_error( - process_xml_metadata, - settings, - mcp_job, - sip_directory_path, - sip, - sip_file, - fail_on_error, - errors, - expectation, -): + process_xml_metadata: mock.Mock, + settings: pytest_django.fixtures.SettingsWrapper, + mcp_job: Job, + sip_directory_path: pathlib.Path, + sip: SIP, + sip_file: File, + fail_on_error: bool, + errors: list[str], + expectation: does_not_raise, +) -> None: mock_mets = mock.Mock( **{ "serialize.return_value": etree.Element("tag"), @@ -301,7 +338,7 @@ def test_xml_validation_fail_on_error( @pytest.fixture -def arranged_sip_path(tmp_path): +def arranged_sip_path(tmp_path: pathlib.Path) -> pathlib.Path: sip_path = tmp_path / "sip" sip_path.mkdir() @@ -309,7 +346,7 @@ def arranged_sip_path(tmp_path): @pytest.fixture -def create_arrangement(sip, arranged_sip_path): +def create_arrangement(sip: SIP, arranged_sip_path: pathlib.Path) -> None: # Create the directory structure representing the new arrangement. objects_path = arranged_sip_path / "objects" objects_path.mkdir() @@ -351,8 +388,8 @@ def create_arrangement(sip, arranged_sip_path): @pytest.mark.django_db def test_structmap_is_created_from_sip_arrangement( - mcp_job, create_arrangement, arranged_sip_path, sip -): + mcp_job: Job, create_arrangement: None, arranged_sip_path: pathlib.Path, sip: SIP +) -> None: mets_path = f"{arranged_sip_path}/METS.{sip.uuid}.xml" main( @@ -391,3 +428,172 @@ def test_structmap_is_created_from_sip_arrangement( assert file3_div.attrib["TYPE"] == "File" assert subdir_second_div.attrib["TYPE"] == "Subseries" assert file4_div.attrib["TYPE"] == "File" + + +@pytest.fixture +def bag_path(sip_directory_path: pathlib.Path, sip: SIP) -> pathlib.Path: + result = ( + sip_directory_path / "logs" / "transfers" / str(sip.uuid) / "logs" / "BagIt" + ) + result.mkdir(parents=True) + (result / "bag-info.txt").touch() + + return result + + +@pytest.mark.django_db +@mock.patch("archivematica.MCPClient.clientScripts.create_mets_v2.Bag") +@pytest.mark.parametrize( + "info", + [ + {"Bagging-Date": "2025-01-08", "Payload-Oxum": "0.2"}, + {}, + ], + ids=[ + "populated", + "empty", + ], +) +def test_bag_metadata_is_recorded_in_a_amdsec( + bag_class: mock.Mock, + info: dict[str, str], + mcp_job: Job, + sip_directory_path: pathlib.Path, + sip: SIP, + sip_file: File, + bag_path: pathlib.Path, +) -> None: + bag_class.return_value = mock.Mock(info=info) + mets_path = sip_directory_path / f"METS.{sip.uuid}.xml" + + main( + mcp_job, + sipType="SIP", + baseDirectoryPath=str(sip_directory_path), + XMLFile=str(mets_path), + sipUUID=sip.pk, + includeAmdSec=False, + createNormativeStructmap=False, + ) + + mets_xml = etree.parse(mets_path.open()) + transfer_metadata = mets_xml.xpath( + ".//mets:amdSec//transfer_metadata/*", + namespaces=NSMAP, + ) + assert {e.tag: e.text for e in transfer_metadata} == info + + +@pytest.fixture() +def transfer_metadata_xml_path(sip: SIP, sip_directory_path: pathlib.Path) -> File: + metadata_dir_path = sip_directory_path / "objects" / "metadata" / "transfers" + metadata_dir_path.mkdir(parents=True) + + result = metadata_dir_path / "transfer_metadata.xml" + result.touch() + + return result + + +@pytest.fixture() +def transfer_metadata_xml( + sip: SIP, sip_directory_path: pathlib.Path, transfer_metadata_xml_path: pathlib.Path +) -> File: + return File.objects.create( + sip=sip, + currentlocation=f"%SIPDirectory%{transfer_metadata_xml_path.relative_to(sip_directory_path)}".encode(), + filegrpuse="metadata", + ) + + +@pytest.mark.django_db +def test_transfer_metadata_xml_is_recorded_in_a_amdsec( + mcp_job: Job, + sip_directory_path: pathlib.Path, + sip: SIP, + sip_file: File, + transfer_metadata_xml_path: pathlib.Path, + transfer_metadata_xml: File, +) -> None: + info = {"test": "foobar"} + for tag, value in info.items(): + transfer_metadata_xml_path.write_text(f"<{tag}>{value}") + mets_path = sip_directory_path / f"METS.{sip.uuid}.xml" + + main( + mcp_job, + sipType="SIP", + baseDirectoryPath=str(sip_directory_path), + XMLFile=str(mets_path), + sipUUID=sip.pk, + includeAmdSec=False, + createNormativeStructmap=False, + ) + + mets_xml = etree.parse(mets_path.open()) + transfer_metadata = mets_xml.xpath( + ".//mets:amdSec//mets:xmlData/*", + namespaces=NSMAP, + ) + assert {e.tag: e.text for e in transfer_metadata} == info + + +@pytest.fixture() +def source_metadata_xml_path(sip: SIP, sip_directory_path: pathlib.Path) -> File: + metadata_dir_path = ( + sip_directory_path / "objects" / "metadata" / "transfers" / "sourceMD" + ) + metadata_dir_path.mkdir(parents=True) + + result = metadata_dir_path / "file.xml" + result.touch() + + return result + + +@pytest.fixture() +def source_metadata_xml( + sip: SIP, sip_directory_path: pathlib.Path, source_metadata_xml_path: pathlib.Path +) -> File: + return File.objects.create( + sip=sip, + currentlocation=f"%SIPDirectory%{source_metadata_xml_path.relative_to(sip_directory_path)}".encode(), + filegrpuse="metadata", + ) + + +@pytest.mark.django_db +def test_source_metadata_xml_is_recorded_in_a_amdsec( + mcp_job: Job, + sip_directory_path: pathlib.Path, + sip: SIP, + sip_file: File, + source_metadata_xml_path: pathlib.Path, + source_metadata_xml: File, +) -> None: + mets_path = sip_directory_path / f"METS.{sip.uuid}.xml" + + main( + mcp_job, + sipType="SIP", + baseDirectoryPath=str(sip_directory_path), + XMLFile=str(mets_path), + sipUUID=sip.pk, + includeAmdSec=False, + createNormativeStructmap=False, + ) + + mets_xml = etree.parse(mets_path.open()) + transfer_metadata = mets_xml.xpath( + ".//mets:amdSec//mets:mdRef", + namespaces=NSMAP, + ) + assert len(transfer_metadata) == 1 + assert transfer_metadata[0].attrib == { + f"{{{NSMAP['xlink']}}}href": str( + source_metadata_xml_path.relative_to(sip_directory_path) + ), + "MDTYPE": "OTHER", + "LOCTYPE": "OTHER", + "OTHERLOCTYPE": "SYSTEM", + } diff --git a/tests/MCPClient/test_create_transfer_mets.py b/tests/MCPClient/test_create_transfer_mets.py index 3342a1a5c6..628fbe672a 100644 --- a/tests/MCPClient/test_create_transfer_mets.py +++ b/tests/MCPClient/test_create_transfer_mets.py @@ -4,40 +4,53 @@ import metsrw import pytest -from create_transfer_mets import FSEntriesTree -from create_transfer_mets import agent_to_premis -from create_transfer_mets import dir_obj_to_premis -from create_transfer_mets import event_to_premis -from create_transfer_mets import file_obj_to_premis -from create_transfer_mets import rights_to_premis -from create_transfer_mets import write_mets from django.db.models import prefetch_related_objects from lxml import etree -from main.models import Agent -from main.models import DashboardSetting -from main.models import Directory -from main.models import Event -from main.models import File -from main.models import FPCommandOutput -from main.models import MetadataAppliesToType -from main.models import RightsStatement -from main.models import RightsStatementCopyright -from main.models import RightsStatementCopyrightDocumentationIdentifier -from main.models import RightsStatementCopyrightNote -from main.models import RightsStatementLicense -from main.models import RightsStatementLicenseDocumentationIdentifier -from main.models import RightsStatementLicenseNote -from main.models import RightsStatementOtherRightsDocumentationIdentifier -from main.models import RightsStatementOtherRightsInformation -from main.models import RightsStatementOtherRightsInformationNote -from main.models import RightsStatementRightsGranted -from main.models import RightsStatementRightsGrantedNote -from main.models import RightsStatementRightsGrantedRestriction -from main.models import RightsStatementStatuteDocumentationIdentifier -from main.models import RightsStatementStatuteInformation -from main.models import RightsStatementStatuteInformationNote from metsrw.plugins.premisrw import PREMIS_3_0_NAMESPACES -from version import get_preservation_system_identifier + +from archivematica.archivematicaCommon.version import get_preservation_system_identifier +from archivematica.dashboard.main.models import Agent +from archivematica.dashboard.main.models import DashboardSetting +from archivematica.dashboard.main.models import Directory +from archivematica.dashboard.main.models import Event +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import FPCommandOutput +from archivematica.dashboard.main.models import MetadataAppliesToType +from archivematica.dashboard.main.models import RightsStatement +from archivematica.dashboard.main.models import RightsStatementCopyright +from archivematica.dashboard.main.models import ( + RightsStatementCopyrightDocumentationIdentifier, +) +from archivematica.dashboard.main.models import RightsStatementCopyrightNote +from archivematica.dashboard.main.models import RightsStatementLicense +from archivematica.dashboard.main.models import ( + RightsStatementLicenseDocumentationIdentifier, +) +from archivematica.dashboard.main.models import RightsStatementLicenseNote +from archivematica.dashboard.main.models import ( + RightsStatementOtherRightsDocumentationIdentifier, +) +from archivematica.dashboard.main.models import RightsStatementOtherRightsInformation +from archivematica.dashboard.main.models import ( + RightsStatementOtherRightsInformationNote, +) +from archivematica.dashboard.main.models import RightsStatementRightsGranted +from archivematica.dashboard.main.models import RightsStatementRightsGrantedNote +from archivematica.dashboard.main.models import RightsStatementRightsGrantedRestriction +from archivematica.dashboard.main.models import ( + RightsStatementStatuteDocumentationIdentifier, +) +from archivematica.dashboard.main.models import RightsStatementStatuteInformation +from archivematica.dashboard.main.models import RightsStatementStatuteInformationNote +from archivematica.MCPClient.clientScripts.create_transfer_mets import FSEntriesTree +from archivematica.MCPClient.clientScripts.create_transfer_mets import agent_to_premis +from archivematica.MCPClient.clientScripts.create_transfer_mets import dir_obj_to_premis +from archivematica.MCPClient.clientScripts.create_transfer_mets import event_to_premis +from archivematica.MCPClient.clientScripts.create_transfer_mets import ( + file_obj_to_premis, +) +from archivematica.MCPClient.clientScripts.create_transfer_mets import rights_to_premis +from archivematica.MCPClient.clientScripts.create_transfer_mets import write_mets PREMIS_NAMESPACES = PREMIS_3_0_NAMESPACES @@ -149,6 +162,9 @@ def fpcommand_output(db, fprule_characterization, file_obj): @pytest.fixture() def basic_rights_statement(db, file_obj): + MetadataAppliesToType.objects.get_or_create( + pk="7f04d9d4-92c2-44a5-93dc-b7bfdf0c1f17", description="File" + ) rights = RightsStatement.objects.create( metadataappliestotype_id="7f04d9d4-92c2-44a5-93dc-b7bfdf0c1f17", metadataappliestoidentifier=file_obj.uuid, diff --git a/tests/MCPClient/test_dip_generation_helper.py b/tests/MCPClient/test_dip_generation_helper.py index 9895d3555c..c8b4707687 100644 --- a/tests/MCPClient/test_dip_generation_helper.py +++ b/tests/MCPClient/test_dip_generation_helper.py @@ -1,9 +1,10 @@ #!/usr/bin/env python from unittest import mock -import dip_generation_helper import pytest -from main.models import ArchivesSpaceDIPObjectResourcePairing + +from archivematica.dashboard.main.models import ArchivesSpaceDIPObjectResourcePairing +from archivematica.MCPClient.clientScripts import dip_generation_helper def strip_sip_directory(location): @@ -67,7 +68,7 @@ def test_no_files_in_db(tmp_path, sip_file): @pytest.mark.django_db @mock.patch( - "dip_generation_helper.create_archivesspace_client", + "archivematica.MCPClient.clientScripts.dip_generation_helper.create_archivesspace_client", return_value=mock.Mock( **{ "find_by_id.return_value": [ diff --git a/tests/MCPClient/test_email_fail_report.py b/tests/MCPClient/test_email_fail_report.py index 2377be3f96..8670b85975 100644 --- a/tests/MCPClient/test_email_fail_report.py +++ b/tests/MCPClient/test_email_fail_report.py @@ -1,10 +1,11 @@ from smtplib import SMTPException from unittest import mock -import email_fail_report import pytest from django.core import mail +from archivematica.MCPClient.clientScripts import email_fail_report + def fake_send_email_with_exception( subject, @@ -79,10 +80,15 @@ def test_send_email_err(monkeypatch): ) @pytest.mark.django_db @mock.patch("argparse.ArgumentParser") -@mock.patch("email_fail_report.get_emails_from_dashboard_users") -@mock.patch("email_fail_report.get_content_for", return_value="my report") -@mock.patch("email_fail_report.send_email") -@mock.patch("email_fail_report.store_report") +@mock.patch( + "archivematica.MCPClient.clientScripts.email_fail_report.get_emails_from_dashboard_users" +) +@mock.patch( + "archivematica.MCPClient.clientScripts.email_fail_report.get_content_for", + return_value="my report", +) +@mock.patch("archivematica.MCPClient.clientScripts.email_fail_report.send_email") +@mock.patch("archivematica.MCPClient.clientScripts.email_fail_report.store_report") def test_report_is_always_stored( store_report_mock, send_email_mock, diff --git a/tests/MCPClient/test_ensure_no_mutable_globals.py b/tests/MCPClient/test_ensure_no_mutable_globals.py index aa3124af1d..8e6d95ab6d 100755 --- a/tests/MCPClient/test_ensure_no_mutable_globals.py +++ b/tests/MCPClient/test_ensure_no_mutable_globals.py @@ -22,7 +22,8 @@ import django import prometheus_client -from client.loader import get_supported_modules + +from archivematica.MCPClient.client.loader import get_supported_modules django.setup() @@ -92,7 +93,9 @@ def analyze_module(module_name): methods. """ global2modules_funcs_2 = {} - module = importlib.import_module("clientScripts." + module_name) + module = importlib.import_module( + "archivematica.MCPClient.clientScripts." + module_name + ) for attr in dir(module): val = getattr(module, attr) if attr.startswith("__"): @@ -153,8 +156,8 @@ def test_ensure_no_mutable_globals(): config_path = ( pathlib.Path(__file__).parent.parent.parent / "src" + / "archivematica" / "MCPClient" - / "lib" / "archivematicaClientModules" ) diff --git a/tests/MCPClient/test_extract_contents.py b/tests/MCPClient/test_extract_contents.py index 397fba1b45..608c5d519d 100644 --- a/tests/MCPClient/test_extract_contents.py +++ b/tests/MCPClient/test_extract_contents.py @@ -1,10 +1,11 @@ import pathlib from unittest import mock -import extract_contents import pytest -from client.job import Job -from main import models + +from archivematica.dashboard.main import models +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts import extract_contents @pytest.mark.django_db @@ -169,7 +170,7 @@ def test_job_fails_if_file_has_been_extracted_already( ] assert len(job.pyprint.mock_calls) == len(expected_pyprint_calls) # This uses any_order because we do not control the order in which the - # jo iterates the files in the transfer. + # job iterates the files in the transfer. job.pyprint.assert_has_calls(expected_pyprint_calls, any_order=True) @@ -188,7 +189,10 @@ def transfer_file_path(transfer_directory_path, transfer_file): @pytest.mark.django_db -@mock.patch("extract_contents.executeOrRun", return_value=(-1, "", "error!")) +@mock.patch( + "archivematica.MCPClient.clientScripts.extract_contents.executeOrRun", + return_value=(-1, "", "error!"), +) def test_job_fails_if_extraction_command_fails( execute_or_run, transfer, @@ -237,7 +241,10 @@ def test_job_fails_if_extraction_command_fails( @pytest.mark.django_db -@mock.patch("extract_contents.executeOrRun", return_value=(0, "success!", "")) +@mock.patch( + "archivematica.MCPClient.clientScripts.extract_contents.executeOrRun", + return_value=(0, "success!", ""), +) def test_job_deletes_compressed_file( execute_or_run, transfer, @@ -301,7 +308,7 @@ def test_job_deletes_compressed_file( @pytest.mark.django_db -@mock.patch("extract_contents.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.extract_contents.executeOrRun") def test_job_assign_uuids_to_extracted_files_and_directories( execute_or_run, transfer, @@ -393,7 +400,7 @@ def execute_or_run_side_effect(*args, **kwargs): f"Command to execute is: {fpcommand.command}", f"Assigning UUID {extracted_directory.uuid} to directory path {extracted_directory.currentlocation.decode()}", ] - assert job.pyprint.mock_calls == [ + expected_pyprint_calls = [ mock.call(f"Deleting?: {delete}", file=mock.ANY), mock.call( "Extracted contents from", @@ -415,10 +422,17 @@ def execute_or_run_side_effect(*args, **kwargs): file=mock.ANY, ), ] + assert len(job.pyprint.mock_calls) == len(expected_pyprint_calls) + # This uses any_order because we do not control the order in which the + # job iterates the files in the transfer. + job.pyprint.assert_has_calls(expected_pyprint_calls, any_order=True) @pytest.mark.django_db -@mock.patch("extract_contents.executeOrRun", return_value=(0, "success", "")) +@mock.patch( + "archivematica.MCPClient.clientScripts.extract_contents.executeOrRun", + return_value=(0, "success", ""), +) def test_job_uses_replacement_keys_in_bash_command( execute_or_run, transfer, diff --git a/tests/MCPClient/test_filename_change.py b/tests/MCPClient/test_filename_change.py index 3f8127a47a..2c49c3b2b8 100644 --- a/tests/MCPClient/test_filename_change.py +++ b/tests/MCPClient/test_filename_change.py @@ -2,19 +2,21 @@ import shutil import uuid -import change_names -import change_object_names import pytest -from client.job import Job from django.test import TestCase -from main.models import Agent -from main.models import Directory -from main.models import Event -from main.models import File -from main.models import Transfer -from main.models import UnitVariable from pytest_django.asserts import assertQuerysetEqual +from archivematica.archivematicaCommon.version import get_full_version +from archivematica.dashboard.main.models import Agent +from archivematica.dashboard.main.models import Directory +from archivematica.dashboard.main.models import Event +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import Transfer +from archivematica.dashboard.main.models import UnitVariable +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts import change_names +from archivematica.MCPClient.clientScripts import change_object_names + THIS_DIR = os.path.dirname(os.path.abspath(__file__)) # This uses the same name as the pytest fixture in conftest and it can be @@ -139,8 +141,9 @@ def is_uuid(uuid_): def verify_event_details(event): assert ( - 'prohibited characters removed: program="change_names"; ' 'version="1.10.' - ) in event.event_detail + event.event_detail + == f'prohibited characters removed: program="change_names"; version="{get_full_version()}"' + ) assertQuerysetEqual( event.agents.all(), [ @@ -178,6 +181,16 @@ def admin_agent(self, user): identifiertype="Archivematica user pk", ) + @pytest.fixture(autouse=True) + def organization_agent(self): + return Agent.objects.get_or_create( + pk=2, + agenttype="organization", + identifiervalue="ORG", + name="Your Organization Name Here", + identifiertype="repository code", + ) + @pytest.fixture(autouse=True) def microservice_unitvars(self, admin_agent): UnitVariable.objects.create( @@ -311,9 +324,25 @@ def test_change_name_raises_valueerror_on_empty_string(): change_names.change_name("") +@pytest.fixture +def organization_agent(): + return Agent.objects.get_or_create( + pk=2, + agenttype="organization", + identifiervalue="ORG", + name="Your Organization Name Here", + identifiertype="repository code", + ) + + @pytest.mark.django_db def test_change_transfer_with_multiple_files( - monkeypatch, tmp_path, transfer, subdir_path, multiple_transfer_file_objs + monkeypatch, + tmp_path, + transfer, + subdir_path, + multiple_transfer_file_objs, + organization_agent, ): monkeypatch.setattr(change_object_names.NameChanger, "BATCH_SIZE", 10) diff --git a/tests/MCPClient/test_fpr_commands.py b/tests/MCPClient/test_fpr_commands.py new file mode 100644 index 0000000000..cfe10e79fa --- /dev/null +++ b/tests/MCPClient/test_fpr_commands.py @@ -0,0 +1,139 @@ +import re +from typing import TypedDict + +import pytest + +from archivematica.archivematicaCommon.executeOrRunSubProcess import executeOrRun +from archivematica.dashboard.fpr.models import FPCommand + + +class QueryFilters(TypedDict): + command_usage: str + description: str + + +class EventDetailResult(TypedDict): + programs: list[str] + version: str + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "expected_programs,expected_version_pattern,cmd,filters", + [ + ( + ["7z"], + # The event detail command extracts different lines depending on the 7z version. + # Older versions report the version on a line starting with "p7zip Version" + # while more recent versions use a line starting with "7-Zip". + r"(^p7zip Version|^7-Zip)", + 'echo program=\\"7z\\"\\; version=\\"`7z | awk \'NR==3 && /^p7zip Version/ {print; exit} NR==2 {line2=$0} NR==3 {print line2 $0}\'`\\"', + { + "command_usage": "event_detail", + "description": "Get event detail text for 7z extraction", + }, + ), + ( + ["convert"], + "^Version: ImageMagick", + 'echo program=\\"convert\\"\\; version=\\"`convert -version | grep Version:`\\"', + { + "command_usage": "event_detail", + "description": "convert event detail", + }, + ), + ( + ["ffmpeg"], + r"^ffmpeg version", + 'echo program=\\"ffmpeg\\"\\; version=\\"`ffmpeg 2>&1 | grep --ignore-case "FFmpeg version"`\\"', + { + "command_usage": "event_detail", + "description": "Get event detail text for ffmpeg extraction", + }, + ), + ( + ["ps2pdf", "Ghostscript"], + r"^\d+\.\d+\.\d+", + 'echo program=\\"ps2pdf\\"\\; program=\\"Ghostscript\\"\\; version=\\"`gs --version`\\" ', + { + "command_usage": "event_detail", + "description": "ps2pdf event detail", + }, + ), + ( + ["Ghostscript"], + r"^\d+\.\d+\.\d+", + 'echo program=\\"Ghostscript\\"\\; version=\\"`gs --version`\\" ', + { + "command_usage": "event_detail", + "description": "Ghostscript event detail", + }, + ), + ( + ["inkscape"], + r"^Inkscape", + 'echo program=\\"inkscape\\"\\; version=\\"`inkscape -V`\\" ', + { + "command_usage": "event_detail", + "description": "inkscape event detail", + }, + ), + pytest.param( + ["unrar-nonfree"], + "^UNRAR", + 'echo program=\\"unrar-nonfree\\"\\; version=\\"`unrar-nonfree | grep \'UNRAR\'`\\"', + { + "command_usage": "event_detail", + "description": "Get event detail text for unrar extraction", + }, + marks=pytest.mark.skip( + reason="Skipping because unrar-nonfree is not installed by default in Archivematica" + ), + ), + ( + ["readpst"], + r"^ReadPST / LibPST", + 'echo program=\\"readpst\\"\\; version=\\"`readpst -V`\\"', + { + "command_usage": "event_detail", + "description": "readpst event detail", + }, + ), + ], + ids=[ + "7z", + "convert", + "ffmpeg", + "ps2pdf", + "Ghostscript", + "inkscape", + "unrar-nonfree", + "readpst", + ], +) +def test_event_detail_command_returns_tool_version( + expected_programs: list[str], + expected_version_pattern: str, + cmd: str, + filters: QueryFilters, +) -> None: + command, _ = FPCommand.active.get_or_create( + script_type="bashScript", command=cmd, **filters + ) + + _, output, _ = executeOrRun(command.script_type, command.command) + + result: EventDetailResult = {"programs": [], "version": ""} + + for match in re.finditer( + r'program="(?P.*?)";|version="(?P.*?)"', output + ): + program = match.group("program") + version = match.group("version") + if program is not None: + result["programs"].append(program) + if version is not None: + result["version"] = version + + assert result["programs"] == expected_programs + assert re.search(expected_version_pattern, result["version"]) is not None diff --git a/tests/MCPClient/test_has_packages.py b/tests/MCPClient/test_has_packages.py index 4372f8bc71..479583123d 100644 --- a/tests/MCPClient/test_has_packages.py +++ b/tests/MCPClient/test_has_packages.py @@ -1,11 +1,12 @@ import pathlib from unittest import mock -import has_packages import pytest -from client.job import Job -from fpr import models as fprmodels -from main import models + +from archivematica.dashboard.fpr import models as fprmodels +from archivematica.dashboard.main import models +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts import has_packages @pytest.fixture diff --git a/tests/MCPClient/test_identify_file_format.py b/tests/MCPClient/test_identify_file_format.py index 2c61a2f72e..413be19e07 100644 --- a/tests/MCPClient/test_identify_file_format.py +++ b/tests/MCPClient/test_identify_file_format.py @@ -1,11 +1,12 @@ import pathlib from unittest import mock -import identify_file_format import pytest -from client.job import Job -from fpr import models as fprmodels -from main import models + +from archivematica.dashboard.fpr import models as fprmodels +from archivematica.dashboard.main import models +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts import identify_file_format @pytest.fixture @@ -25,7 +26,7 @@ def sip_file_path( def job(sip_file: models.File, sip_file_path: pathlib.Path) -> mock.Mock: return mock.Mock( args=[ - "identify_file_format.py", + "archivematica.MCPClient.clientScripts.identify_file_format.py", "True", str(sip_file_path), str(sip_file.uuid), @@ -81,7 +82,7 @@ def test_job_fails_if_identification_command_does_not_exist(job: mock.Mock) -> N @pytest.mark.django_db -@mock.patch("identify_file_format.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.identify_file_format.executeOrRun") def test_job_fails_if_format_identification_command_fails( execute_or_run: mock.Mock, job: mock.Mock, @@ -100,7 +101,7 @@ def test_job_fails_if_format_identification_command_fails( @pytest.mark.django_db -@mock.patch("identify_file_format.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.identify_file_format.executeOrRun") def test_job_fails_if_identification_rule_does_not_exist( execute_or_run: mock.Mock, job: mock.Mock, @@ -134,7 +135,7 @@ def test_job_fails_if_identification_rule_does_not_exist( @pytest.mark.django_db -@mock.patch("identify_file_format.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.identify_file_format.executeOrRun") def test_job_fails_if_multiple_identification_rules_exist( execute_or_run: mock.Mock, job: mock.Mock, @@ -175,7 +176,7 @@ def test_job_fails_if_multiple_identification_rules_exist( @pytest.mark.django_db -@mock.patch("identify_file_format.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.identify_file_format.executeOrRun") def test_job_fails_if_format_version_does_not_exist( execute_or_run: mock.Mock, job: mock.Mock, @@ -204,11 +205,15 @@ def test_job_fails_if_format_version_does_not_exist( @pytest.mark.django_db -@mock.patch("identify_file_format.executeOrRun", return_value=(0, "success!", "")) +@mock.patch( + "archivematica.MCPClient.clientScripts.identify_file_format.executeOrRun", + return_value=(0, "success!", ""), +) def test_job_saves_unit_variable_indicating_file_format_identification_use( execute_or_run: mock.Mock, job: mock.Mock, sip: models.SIP, + idcommand: fprmodels.IDCommand, ) -> None: identify_file_format.call([job]) @@ -216,14 +221,14 @@ def test_job_saves_unit_variable_indicating_file_format_identification_use( models.UnitVariable.objects.filter( unituuid=sip.pk, variable="replacementDict", - variablevalue="{'%IDCommand%': 'True'}", + variablevalue='{"%IDCommand%": "True"}', ).count() == 1 ) @pytest.mark.django_db -@mock.patch("identify_file_format.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.identify_file_format.executeOrRun") def test_job_adds_file_format_version( execute_or_run: mock.Mock, job: mock.Mock, @@ -260,13 +265,14 @@ def test_job_adds_file_format_version( @pytest.mark.django_db -@mock.patch("identify_file_format.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.identify_file_format.executeOrRun") def test_job_updates_file_format_version( execute_or_run: mock.Mock, job: mock.Mock, sip_file: models.File, format: fprmodels.Format, sip_file_format_version: models.FileFormatVersion, + idcommand: fprmodels.IDCommand, ) -> None: command_output = "fmt/111" execute_or_run.return_value = (0, command_output, "") @@ -288,7 +294,7 @@ def test_job_updates_file_format_version( @pytest.mark.django_db -@mock.patch("identify_file_format.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.identify_file_format.executeOrRun") def test_job_adds_successful_format_identification_data( execute_or_run: mock.Mock, job: mock.Mock, @@ -329,7 +335,7 @@ def test_job_adds_successful_format_identification_data( @pytest.mark.django_db -@mock.patch("identify_file_format.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.identify_file_format.executeOrRun") def test_job_falls_back_to_identification_rule_if_format_version_does_not_exist( execute_or_run: mock.Mock, job: mock.Mock, diff --git a/tests/MCPClient/test_job.py b/tests/MCPClient/test_job.py index 0efe69d581..63732b3cd7 100644 --- a/tests/MCPClient/test_job.py +++ b/tests/MCPClient/test_job.py @@ -1,9 +1,6 @@ -import os from uuid import uuid4 -from client.job import Job - -THIS_DIR = os.path.dirname(os.path.realpath(__file__)) +from archivematica.MCPClient.client.job import Job TEXT = "‘你好‘" diff --git a/tests/MCPClient/test_json_conversion.py b/tests/MCPClient/test_json_conversion.py index 580ad55705..2d0db19144 100644 --- a/tests/MCPClient/test_json_conversion.py +++ b/tests/MCPClient/test_json_conversion.py @@ -1,8 +1,8 @@ import json import os -import json_metadata_to_csv -from client.job import Job +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts import json_metadata_to_csv JSON = '[{"dc.title": "This is a test item", "filename": "objects/test.txt"}]' CSV = "filename,dc.title\nobjects/test.txt,This is a test item\n" diff --git a/tests/MCPClient/test_load_labels_from_csv.py b/tests/MCPClient/test_load_labels_from_csv.py index 7cab4aa97a..ca43e8132d 100644 --- a/tests/MCPClient/test_load_labels_from_csv.py +++ b/tests/MCPClient/test_load_labels_from_csv.py @@ -1,10 +1,11 @@ import pathlib from unittest import mock -import load_labels_from_csv import pytest -from client.job import Job -from main import models + +from archivematica.dashboard.main import models +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts import load_labels_from_csv LABEL = "my file" OBJECTS_DIRECTORY = "%transferDirectory%objects" diff --git a/tests/MCPClient/test_load_premis_events_from_xml.py b/tests/MCPClient/test_load_premis_events_from_xml.py index 0bebb7d6d5..a8978e72a0 100644 --- a/tests/MCPClient/test_load_premis_events_from_xml.py +++ b/tests/MCPClient/test_load_premis_events_from_xml.py @@ -1,23 +1,27 @@ -import os +import importlib.resources import pathlib import sys import uuid from unittest import mock -import load_premis_events_from_xml import pytest from lxml import etree -from main.models import Agent -from main.models import Event -from main.models import File + +from archivematica.dashboard.main.models import Agent +from archivematica.dashboard.main.models import Event +from archivematica.dashboard.main.models import File +from archivematica.MCPClient.clientScripts import load_premis_events_from_xml THIS_DIR = pathlib.Path(__file__).parent @pytest.fixture() def xsd_path(): - return os.path.abspath( - os.path.join(THIS_DIR, "../../src/MCPClient/lib/assets/premis/premis.xsd") + return ( + importlib.resources.files("archivematica.MCPClient") + / "assets" + / "premis" + / "premis.xsd" ) @@ -229,9 +233,11 @@ def test_get_premis_element_children_identifiers(): ids=["original_name_as_string", "original_name_as_empty_string"], ) @mock.patch("metsrw.plugins.premisrw.premis_to_data") -@mock.patch("load_premis_events_from_xml.PREMISFile") @mock.patch( - "load_premis_events_from_xml.get_premis_element_children_identifiers", + "archivematica.MCPClient.clientScripts.load_premis_events_from_xml.PREMISFile" +) +@mock.patch( + "archivematica.MCPClient.clientScripts.load_premis_events_from_xml.get_premis_element_children_identifiers", return_value=set(), ) def test_file_element_factory( @@ -253,7 +259,7 @@ def test_file_element_factory( @mock.patch("metsrw.plugins.premisrw.premis_to_data") @mock.patch("metsrw.plugins.premisrw.PREMISAgent") @mock.patch( - "load_premis_events_from_xml.get_premis_element_children_identifiers", + "archivematica.MCPClient.clientScripts.load_premis_events_from_xml.get_premis_element_children_identifiers", return_value=set(), ) def test_agent_element_factory( @@ -288,7 +294,7 @@ def test_agent_element_factory( @mock.patch("metsrw.plugins.premisrw.premis_to_data") @mock.patch("metsrw.plugins.premisrw.PREMISEvent") @mock.patch( - "load_premis_events_from_xml.get_premis_element_children_identifiers", + "archivematica.MCPClient.clientScripts.load_premis_events_from_xml.get_premis_element_children_identifiers", return_value=set(), ) def test_event_element_factory( @@ -353,7 +359,7 @@ def test_event_element_factory( ), ) @mock.patch( - "load_premis_events_from_xml.get_premis_element_children_identifiers", + "archivematica.MCPClient.clientScripts.load_premis_events_from_xml.get_premis_element_children_identifiers", return_value=set(), ) def test_event_element_factory_prints_datetime_error( @@ -424,7 +430,10 @@ def test_event_element_factory_with_no_event_outcome_detail(): def test_get_or_create_agents(): mock_agent_model = mock.Mock(**{"objects.get_or_create.return_value": (None, None)}) - with mock.patch("load_premis_events_from_xml.Agent", mock_agent_model): + with mock.patch( + "archivematica.MCPClient.clientScripts.load_premis_events_from_xml.Agent", + mock_agent_model, + ): agents = [ {"identifier": ("type", "value"), "name": "agent1", "type": "agenttype"} ] diff --git a/tests/MCPClient/test_move_or_merge.py b/tests/MCPClient/test_move_or_merge.py index 1370af839f..07a8273663 100644 --- a/tests/MCPClient/test_move_or_merge.py +++ b/tests/MCPClient/test_move_or_merge.py @@ -1,5 +1,6 @@ import pytest -from move_or_merge import move_or_merge + +from archivematica.MCPClient.clientScripts.move_or_merge import move_or_merge @pytest.fixture diff --git a/tests/MCPClient/test_move_to_backlog.py b/tests/MCPClient/test_move_to_backlog.py index cfecc7004e..125e93d657 100644 --- a/tests/MCPClient/test_move_to_backlog.py +++ b/tests/MCPClient/test_move_to_backlog.py @@ -1,14 +1,26 @@ import uuid import metsrw -import move_to_backlog import pytest from lxml import etree -from main.models import Agent + +from archivematica.dashboard.main.models import Agent +from archivematica.MCPClient.clientScripts import move_to_backlog + + +@pytest.fixture +def organization_agent(): + return Agent.objects.get_or_create( + pk=2, + agenttype="organization", + identifiervalue="ORG", + name="Your Organization Name Here", + identifiertype="repository code", + ) @pytest.mark.django_db -def test_premis_event_data(transfer): +def test_premis_event_data(transfer, organization_agent): event_id, event_type, created_at = ( str(uuid.uuid4()), "placement in backlog", @@ -55,7 +67,7 @@ def test_premis_event_data(transfer): @pytest.mark.django_db -def test_transfer_agents(transfer): +def test_transfer_agents(transfer, organization_agent): ret = move_to_backlog._transfer_agents(transfer.uuid) assert len(ret) == 2 diff --git a/tests/MCPClient/test_normalize.py b/tests/MCPClient/test_normalize.py index 025a34d5a0..fd8d8e101d 100644 --- a/tests/MCPClient/test_normalize.py +++ b/tests/MCPClient/test_normalize.py @@ -1,18 +1,18 @@ import argparse import pathlib import uuid -from typing import Mapping +from collections.abc import Mapping +from collections.abc import Sequence from typing import Optional -from typing import Sequence -from typing import Tuple from unittest import mock -import normalize import pytest import pytest_django -from client.job import Job -from fpr import models as fprmodels -from main import models + +from archivematica.dashboard.fpr import models as fprmodels +from archivematica.dashboard.main import models +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts import normalize @pytest.mark.django_db @@ -384,7 +384,8 @@ def default_preservation_rule( @pytest.mark.django_db @mock.patch( - "transcoder.CommandLinker", return_value=mock.Mock(**{"execute.return_value": 0}) + "archivematica.MCPClient.clientScripts.transcoder.CommandLinker", + return_value=mock.Mock(**{"execute.return_value": 0}), ) def test_normalization_falls_back_to_default_rule( command_linker: mock.Mock, @@ -443,7 +444,8 @@ def test_normalization_falls_back_to_default_rule( @pytest.mark.django_db @mock.patch( - "transcoder.CommandLinker", return_value=mock.Mock(**{"execute.return_value": 0}) + "archivematica.MCPClient.clientScripts.transcoder.CommandLinker", + return_value=mock.Mock(**{"execute.return_value": 0}), ) def test_normalization_finds_rule_by_file_format_version( command_linker: mock.Mock, @@ -499,7 +501,8 @@ def test_normalization_finds_rule_by_file_format_version( @pytest.mark.django_db @mock.patch("os.makedirs", side_effect=OSError("error!")) @mock.patch( - "transcoder.CommandLinker", return_value=mock.Mock(**{"execute.return_value": 0}) + "archivematica.MCPClient.clientScripts.transcoder.CommandLinker", + return_value=mock.Mock(**{"execute.return_value": 0}), ) def test_normalization_fails_if_thumbnail_directory_cannot_be_created( command_linker: mock.Mock, @@ -555,7 +558,7 @@ def fprule_thumbnail(fprule_thumbnail: fprmodels.FPRule) -> fprmodels.FPRule: @pytest.mark.django_db -@mock.patch("transcoder.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.transcoder.executeOrRun") def test_normalization_copies_generated_thumbnail_to_shared_thumbnails_directory( execute_or_run: mock.Mock, sip: models.SIP, @@ -579,7 +582,7 @@ def execute_or_run_side_effect( arguments: Optional[Sequence[str]] = None, env_updates: Optional[Mapping[str, str]] = None, capture_output: bool = True, - ) -> Tuple[int, str, str]: + ) -> tuple[int, str, str]: """Mock thumbnail generation by creating a new temporary file.""" parser = argparse.ArgumentParser() parser.add_argument("--output-directory", required=True) @@ -681,7 +684,7 @@ def fprule_default_thumbnail( @pytest.mark.django_db -@mock.patch("transcoder.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.transcoder.executeOrRun") def test_normalization_fallbacks_to_default_thumbnail_rule_if_initial_command_fails( execute_or_run: mock.Mock, sip: models.SIP, @@ -707,7 +710,7 @@ def execute_or_run_side_effect( arguments: Optional[Sequence[str]] = None, env_updates: Optional[Mapping[str, str]] = None, capture_output: bool = True, - ) -> Tuple[int, str, str]: + ) -> tuple[int, str, str]: """Mock a complex normalization command that initially fails and then falls back to its default rule and also runs its own verification and event detail commands. @@ -832,7 +835,10 @@ def fpcommand_access( @pytest.mark.django_db -@mock.patch("transcoder.executeOrRun", return_value=(-1, "", "error!")) +@mock.patch( + "archivematica.MCPClient.clientScripts.transcoder.executeOrRun", + return_value=(-1, "", "error!"), +) def test_normalization_fails_if_fallback_default_rule_does_not_exist( execute_or_run: mock.Mock, sip: models.SIP, diff --git a/tests/MCPClient/test_parse_dataverse.py b/tests/MCPClient/test_parse_dataverse.py index bdc0deebfd..5df213c9cb 100644 --- a/tests/MCPClient/test_parse_dataverse.py +++ b/tests/MCPClient/test_parse_dataverse.py @@ -4,10 +4,13 @@ import os import metsrw -import parse_dataverse_mets as parse_dataverse -from client.job import Job from django.test import TestCase -from main import models + +from archivematica.dashboard.main import models +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts import ( + parse_dataverse_mets as parse_dataverse, +) THIS_DIR = os.path.dirname(os.path.abspath(__file__)) diff --git a/tests/MCPClient/test_parse_external_mets.py b/tests/MCPClient/test_parse_external_mets.py index 74a91d4ae6..50344f2fa5 100644 --- a/tests/MCPClient/test_parse_external_mets.py +++ b/tests/MCPClient/test_parse_external_mets.py @@ -1,9 +1,10 @@ import os import shutil -import parse_external_mets import pytest -from main import models + +from archivematica.dashboard.main import models +from archivematica.MCPClient.clientScripts import parse_external_mets THIS_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -60,6 +61,9 @@ def test_mets_cannot_parse(mcp_job, transfer_directory_path): def test_mets_is_parsed(db, mcp_job, transfer_directory_path): + models.MetadataAppliesToType.objects.get_or_create( + pk="3e48343d-e2d2-4956-aaa3-b54d26eb9761", description="SIP" + ) exit_code = parse_external_mets.main( mcp_job, "a2f1f249-7bd4-4f52-8f1a-84319cb1b6d3", str(transfer_directory_path) ) diff --git a/tests/MCPClient/test_parse_mets_to_db.py b/tests/MCPClient/test_parse_mets_to_db.py index 2e6ed4e538..7da10c2d77 100644 --- a/tests/MCPClient/test_parse_mets_to_db.py +++ b/tests/MCPClient/test_parse_mets_to_db.py @@ -2,13 +2,14 @@ import uuid from unittest import mock -import fpr -import parse_mets_to_db import pytest -from client.job import Job from django.test import TestCase from lxml import etree -from main import models + +from archivematica.dashboard import fpr +from archivematica.dashboard.main import models +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts import parse_mets_to_db THIS_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -666,8 +667,8 @@ def test_insert_file_info(self): @pytest.mark.django_db -@mock.patch("parse_mets_to_db.os") -@mock.patch("parse_mets_to_db.etree") +@mock.patch("archivematica.MCPClient.clientScripts.parse_mets_to_db.os") +@mock.patch("archivematica.MCPClient.clientScripts.parse_mets_to_db.etree") def test_main_sets_aip_reingest_type(etree, os, sip): job = None assert not models.SIP.objects.filter(uuid=sip.uuid, sip_type="AIP-REIN").exists() @@ -678,8 +679,8 @@ def test_main_sets_aip_reingest_type(etree, os, sip): @pytest.mark.django_db -@mock.patch("parse_mets_to_db.os") -@mock.patch("parse_mets_to_db.etree") +@mock.patch("archivematica.MCPClient.clientScripts.parse_mets_to_db.os") +@mock.patch("archivematica.MCPClient.clientScripts.parse_mets_to_db.etree") def test_main_unsets_partial_reingest_flag(etree, os, sip): job = None sip.set_partial_reingest() diff --git a/tests/MCPClient/test_pid_components.py b/tests/MCPClient/test_pid_components.py index fe248f6114..10801948ef 100644 --- a/tests/MCPClient/test_pid_components.py +++ b/tests/MCPClient/test_pid_components.py @@ -10,17 +10,18 @@ from itertools import chain from unittest import mock -import bind_pid -import bind_pids -import create_mets_v2 -import namespaces as ns import pytest -from main.models import SIP -from main.models import DashboardSetting -from main.models import Directory -from main.models import File -from pid_declaration import DeclarePIDs -from pid_declaration import DeclarePIDsException + +from archivematica.archivematicaCommon import namespaces as ns +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import DashboardSetting +from archivematica.dashboard.main.models import Directory +from archivematica.dashboard.main.models import File +from archivematica.MCPClient.clientScripts import bind_pid +from archivematica.MCPClient.clientScripts import bind_pids +from archivematica.MCPClient.clientScripts import create_mets_v2 +from archivematica.MCPClient.clientScripts.pid_declaration import DeclarePIDs +from archivematica.MCPClient.clientScripts.pid_declaration import DeclarePIDsException THIS_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -237,15 +238,17 @@ def test_bind_pids_no_config( processing configuration but no handle server information configured. """ DashboardSetting.objects.filter(scope="handle").delete() - assert ( - bind_pids.main(mcp_job, None, None) == 1 - ), "Incorrect return value for bind_pids with incomplete configuration." + assert bind_pids.main(mcp_job, None, None) == 1, ( + "Incorrect return value for bind_pids with incomplete configuration." + ) assert caplog.records[0].message.startswith(INCOMPLETE_CONFIG_MSG) @pytest.mark.django_db -@mock.patch("bind_pids._get_unique_acc_no") -@mock.patch("bind_pids._validate_handle_server_config") +@mock.patch("archivematica.MCPClient.clientScripts.bind_pids._get_unique_acc_no") +@mock.patch( + "archivematica.MCPClient.clientScripts.bind_pids._validate_handle_server_config" +) def test_bind_pids( _get_unique_acc_no, _validate_handle_server_config, @@ -272,9 +275,9 @@ def test_bind_pids( bind_pids.main(mcp_job, str(sip.uuid), "") sip_mdl = SIP.objects.filter(uuid=sip.uuid).first() - assert len(sip_mdl.identifiers.all()) == len( - BOUND_IDENTIFIER_TYPES - ), "Number of SIP identifiers is greater than anticipated" + assert len(sip_mdl.identifiers.all()) == len(BOUND_IDENTIFIER_TYPES), ( + "Number of SIP identifiers is greater than anticipated" + ) dirs = Directory.objects.filter(sip=sip).all() # At time of writing, the Bind PIDs functions only binds to the content # folders and the SIP itself, it doesn't bind to the metadata or @@ -287,9 +290,9 @@ def test_bind_pids( pid_types = [] for pid in bound: pid_types.append(pid[0]) - assert ( - "hdl" in pid_types - ), "An expected hdl persistent identifier isn't in the result set" + assert "hdl" in pid_types, ( + "An expected hdl persistent identifier isn't in the result set" + ) assert "URI" in pid_types, "An expected URI isn't in the result set" bound_hdl = f"{BOUND_HDL}{mdl.pk}" bound_uri = f"{BOUND_URI}{mdl.pk}" @@ -350,16 +353,16 @@ def test_bind_pid( "METS.xml", "identifiers.json", ] - assert len(files) is len( - package_files - ), "Number of files returned from package is incorrect" + assert len(files) is len(package_files), ( + "Number of files returned from package is incorrect" + ) for file_ in files: bind_pid.main(mcp_job, file_.pk) for file_mdl in files: bound = {idfr.type: idfr.value for idfr in file_mdl.identifiers.all()} - assert ( - "hdl" in bound - ), "An expected hdl persistent identifier isn't in the result set" + assert "hdl" in bound, ( + "An expected hdl persistent identifier isn't in the result set" + ) assert "URI" in bound, "An expected URI isn't in the result set" bound_hdl = f"{BOUND_HDL}{file_mdl.pk}" bound_uri = f"{BOUND_URI}{file_mdl.pk}" @@ -378,9 +381,9 @@ def test_bind_pid( id_values = [item.text for item in id_value] identifiers_dict = dict(list(zip(id_types, id_values))) for key in identifiers_dict.keys(): - assert key in chain( - TRADITIONAL_IDENTIFIERS, BOUND_IDENTIFIER_TYPES - ), "Identifier type not in expected schemes list" + assert key in chain(TRADITIONAL_IDENTIFIERS, BOUND_IDENTIFIER_TYPES), ( + "Identifier type not in expected schemes list" + ) assert bound_hdl in list(identifiers_dict.values()) assert bound_uri in list(identifiers_dict.values()) @@ -413,8 +416,11 @@ def test_bind_pid_no_settings( THIS_DIR, "fixtures", "pid_declaration", "identifiers.json" ), ) -@mock.patch("bind_pids._get_unique_acc_no") -@mock.patch("bind_pids._validate_handle_server_config", return_value=None) +@mock.patch("archivematica.MCPClient.clientScripts.bind_pids._get_unique_acc_no") +@mock.patch( + "archivematica.MCPClient.clientScripts.bind_pids._validate_handle_server_config", + return_value=None, +) def test_pid_declaration( _validate_handle_server_config, _get_unique_acc_no, @@ -448,9 +454,9 @@ def test_pid_declaration( for mdl in chain((sip_mdl,), files, dir_mdl): bound = {idfr.type: idfr.value for idfr in mdl.identifiers.all()} assert len(bound) == 2, "Number of identifiers is incorrect" - assert set(bound.keys()) == set( - DECLARED_IDENTIFIER_TYPES - ), "Returned keys are not in expected list" + assert set(bound.keys()) == set(DECLARED_IDENTIFIER_TYPES), ( + "Returned keys are not in expected list" + ) for key, value in bound.items(): assert value, "Returned an empty value for an identifier" if key == PID_EXID: @@ -468,12 +474,12 @@ def test_pid_declaration( id_value = dir_dmd_sec.xpath( "//premis:objectIdentifierValue", namespaces={"premis": ns.premisNS} ) - assert len(id_type) == len( - all_identifier_types - ), "Identifier type count is incorrect" - assert len(id_value) == len( - all_identifier_types - ), "Identifier value count is incorrect" + assert len(id_type) == len(all_identifier_types), ( + "Identifier type count is incorrect" + ) + assert len(id_value) == len(all_identifier_types), ( + "Identifier value count is incorrect" + ) for key, value in dict(list(zip(id_type, id_value))).items(): if key == PID_EXID: assert example_uri in value, "Example URI not preserved" @@ -489,12 +495,12 @@ def test_pid_declaration( id_value = file_level_premis.xpath( "//premis:objectIdentifierValue", namespaces={"premis": ns.premisNS} ) - assert len(id_type) == len( - all_identifier_types - ), "Identifier type count is incorrect" - assert len(id_value) == len( - all_identifier_types - ), "Identifier value count is incorrect" + assert len(id_type) == len(all_identifier_types), ( + "Identifier type count is incorrect" + ) + assert len(id_value) == len(all_identifier_types), ( + "Identifier value count is incorrect" + ) for key, value in dict(list(zip(id_type, id_value))).items(): if key == PID_EXID: assert example_uri in value, "Example URI not preserved" @@ -514,11 +520,11 @@ def test_pid_declaration_exceptions( DeclarePIDs(mcp_job).pid_declaration( unit_uuid="eb6b860e-611c-45c8-8d3e-b9396ed6c751", sip_directory="" ) - assert ( - "No identifiers.json file found" in mcp_job.get_stderr().strip() - ), "Expecting no identifiers.json file, but got something else" + assert "No identifiers.json file found" in mcp_job.get_stderr().strip(), ( + "Expecting no identifiers.json file, but got something else" + ) with mock.patch( - "pid_declaration.DeclarePIDs._retrieve_identifiers_path", + "archivematica.MCPClient.clientScripts.pid_declaration.DeclarePIDs._retrieve_identifiers_path", return_value=os.path.join( THIS_DIR, "fixtures", "pid_declaration", "bad_identifiers.json" ), @@ -527,6 +533,6 @@ def test_pid_declaration_exceptions( DeclarePIDs(mcp_job).pid_declaration(unit_uuid="", sip_directory="") except DeclarePIDsException as err: json_error = "Expecting value: line 15 column 1 (char 336)" - assert json_error in str( - err - ), "Error message something other than anticipated for invalid JSON" + assert json_error in str(err), ( + "Error message something other than anticipated for invalid JSON" + ) diff --git a/tests/MCPClient/test_policy_check.py b/tests/MCPClient/test_policy_check.py index c414e8f9c6..49e799038c 100644 --- a/tests/MCPClient/test_policy_check.py +++ b/tests/MCPClient/test_policy_check.py @@ -3,11 +3,12 @@ import uuid from unittest import mock -import policy_check import pytest -from client.job import Job -from fpr import models as fprmodels -from main import models + +from archivematica.dashboard.fpr import models as fprmodels +from archivematica.dashboard.main import models +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts import policy_check @pytest.fixture() @@ -79,7 +80,7 @@ def access_file_format_version( @pytest.mark.django_db -@mock.patch("policy_check.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.policy_check.executeOrRun") def test_policy_checker_succeeds_if_rules_exist( execute_or_run: mock.Mock, sip_file: models.File, @@ -139,7 +140,7 @@ def test_policy_checker_succeeds_if_rules_exist( @pytest.mark.django_db @mock.patch( - "policy_check.executeOrRun", + "archivematica.MCPClient.clientScripts.policy_check.executeOrRun", return_value=( 1, json.dumps( @@ -181,7 +182,7 @@ def test_policy_checker_warns_if_rules_do_not_exist( @pytest.mark.django_db @mock.patch( - "policy_check.executeOrRun", + "archivematica.MCPClient.clientScripts.policy_check.executeOrRun", return_value=( 1, json.dumps( @@ -223,7 +224,7 @@ def test_policy_checker_warns_if_file_does_not_exist( @pytest.mark.django_db -@mock.patch("policy_check.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.policy_check.executeOrRun") def test_policy_checker_fails_if_rule_command_fails( execute_or_run: mock.Mock, sip_file: models.File, @@ -266,7 +267,7 @@ def test_policy_checker_fails_if_rule_command_fails( @pytest.mark.django_db -@mock.patch("policy_check.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.policy_check.executeOrRun") def test_policy_checker_fails_if_event_outcome_information_in_output_is_not_pass( execute_or_run: mock.Mock, sip_file: models.File, @@ -311,7 +312,7 @@ def test_policy_checker_fails_if_event_outcome_information_in_output_is_not_pass @pytest.mark.django_db -@mock.patch("policy_check.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.policy_check.executeOrRun") def test_policy_checker_verifies_file_type_is_preservation( execute_or_run: mock.Mock, derivation_for_preservation: models.Derivation, @@ -356,7 +357,7 @@ def test_policy_checker_verifies_file_type_is_preservation( @pytest.mark.django_db @mock.patch( - "policy_check.executeOrRun", + "archivematica.MCPClient.clientScripts.policy_check.executeOrRun", return_value=( 0, json.dumps( @@ -399,7 +400,7 @@ def test_policy_checker_fails_if_file_is_not_preservation_derivative( @pytest.mark.django_db -@mock.patch("policy_check.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.policy_check.executeOrRun") def test_policy_checker_verifies_file_type_is_access( execute_or_run: mock.Mock, derivation_for_access: models.Derivation, @@ -443,7 +444,7 @@ def test_policy_checker_verifies_file_type_is_access( @pytest.mark.django_db -@mock.patch("policy_check.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.policy_check.executeOrRun") def test_policy_checker_fails_if_file_is_not_access_derivative( execute_or_run: mock.Mock, access_file: models.File, @@ -487,7 +488,7 @@ def test_policy_checker_fails_if_file_is_not_access_derivative( @pytest.mark.django_db -@mock.patch("policy_check.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.policy_check.executeOrRun") def test_policy_checker_saves_policy_check_result_into_logs_directory( execute_or_run: mock.Mock, preservation_file: models.File, @@ -544,7 +545,7 @@ def test_policy_checker_saves_policy_check_result_into_logs_directory( @pytest.mark.django_db -@mock.patch("policy_check.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.policy_check.executeOrRun") def test_policy_checker_saves_policy_check_result_into_submission_documentation_directory( execute_or_run: mock.Mock, sip_file: models.File, @@ -598,7 +599,7 @@ def test_policy_checker_saves_policy_check_result_into_submission_documentation_ @pytest.mark.django_db -@mock.patch("policy_check.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.policy_check.executeOrRun") def test_policy_checker_checks_manually_normalized_access_derivative_file( execute_or_run: mock.Mock, transfer: models.Transfer, diff --git a/tests/MCPClient/test_post_store_aip_hook.py b/tests/MCPClient/test_post_store_aip_hook.py index b137a3718b..6060f7a107 100644 --- a/tests/MCPClient/test_post_store_aip_hook.py +++ b/tests/MCPClient/test_post_store_aip_hook.py @@ -1,10 +1,17 @@ import pathlib +import uuid from unittest import mock -import post_store_aip_hook import pytest -from client.job import Job -from main import models +import pytest_django + +from archivematica.dashboard.components import helpers +from archivematica.dashboard.main import models +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import Transfer +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts import post_store_aip_hook @pytest.fixture() @@ -19,6 +26,22 @@ def archivesspace_components(sip): ) +@pytest.fixture() +def archivesspace_setting(): + models.DashboardSetting.objects.set_dict( + "upload-archivesspace_v0.0", + { + "base_url": "http://foobar.tld", + "user": "user", + "passwd": "12345", + "repository": "5", + "use_statement": "", + "xlink_show": "", + "xlink_actuate": "", + }, + ) + + @pytest.mark.django_db def test_no_archivesspace(sip, archivesspace_components, mcp_job): """It should abort if no ArchivesSpaceDigitalObject found.""" @@ -29,7 +52,7 @@ def test_no_archivesspace(sip, archivesspace_components, mcp_job): @pytest.mark.django_db @mock.patch( - "storageService.get_file_info", + "archivematica.archivematicaCommon.storageService.get_file_info", return_value=[{"misc_attributes": {}}], ) def test_no_dspace(get_file_info, sip, mcp_job): @@ -40,7 +63,7 @@ def test_no_dspace(get_file_info, sip, mcp_job): @pytest.mark.django_db @mock.patch( - "storageService.get_file_info", + "archivematica.archivematicaCommon.storageService.get_file_info", return_value=[{"misc_attributes": {"handle": "123456789/41"}}], ) @mock.patch( @@ -68,7 +91,13 @@ def test_no_dspace(get_file_info, sip, mcp_job): ), ) def test_dspace_handle_to_archivesspace( - requests_get, requests_post, get_file_info, sip, archivesspace_components, mcp_job + requests_get, + requests_post, + get_file_info, + sip, + archivesspace_components, + mcp_job, + archivesspace_setting, ): """It should send the DSpace handle to ArchivesSpace.""" rc = post_store_aip_hook.dspace_handle_to_archivesspace(mcp_job, sip.uuid) @@ -104,3 +133,118 @@ def test_post_store_hook_deletes_transfer_directory( # The transfer directory is returned and has been deleted assert result == str(transfer_path) assert not transfer_path.exists() + + +@pytest.fixture +def storage_service_url() -> str: + value = "https://ss.example.com" + helpers.set_setting("storage_service_url", value) + + return value + + +@pytest.mark.django_db +@mock.patch("requests.Session.request") +def test_call_updates_storage_service_content( + request: mock.Mock, + dashboard_uuid: uuid.UUID, + storage_service_url: str, + transfer: models.Transfer, + sip: models.SIP, + sip_file: models.File, + mcp_job: Job, + settings: pytest_django.fixtures.SettingsWrapper, + caplog: pytest.LogCaptureFixture, +) -> None: + # Disable ES indexing. + settings.SEARCH_ENABLED = "aips" + + arrangement = models.SIPArrange.objects.create( + file_uuid=sip_file.uuid, transfer_uuid=transfer.uuid + ) + mcp_job.args = ["post_store_aip_hook", str(sip.uuid)] + + post_store_aip_hook.call([mcp_job]) + + assert mcp_job.get_exit_code() == 0 + + arrangement.refresh_from_db() + assert arrangement.aip_created + + assert [r.message for r in caplog.records] == [ + "Skipping indexing: Transfers indexing is currently disabled." + ] + + assert mcp_job.get_stdout().splitlines() == [ + f"Checking if transfer {transfer.uuid} is fully stored...", + f"Transfer {transfer.uuid} fully stored, sending delete request to storage service, deleting from transfer backlog", + f"SIP {sip.uuid} not associated with an ArchivesSpace component", + ] + assert mcp_job.get_stderr().splitlines() == [] + + # Verify Storage Service calls. + request.assert_has_calls( + [ + # Delete backlog transfer. + mock.call( + "POST", + f"{storage_service_url}/api/v2/file/{transfer.uuid}/delete_aip/", + data=None, + json={ + "event_reason": "All files in Transfer are now in AIPs.", + "pipeline": str(dashboard_uuid), + "user_email": "archivematica system", + "user_id": 0, + }, + ), + # Trigger Storage Service callback. + mock.call( + "GET", + f"{storage_service_url}/api/v2/file/{sip.uuid}/send_callback/post_store/", + allow_redirects=True, + ), + ], + any_order=True, + ) + + +@pytest.fixture +def test_transfer() -> Transfer: + transfer_id = str(uuid.uuid4()) + return Transfer.objects.create(uuid=transfer_id, currentlocation="") + + +@pytest.fixture +def test_sip() -> SIP: + sip_id = str(uuid.uuid4()) + return SIP.objects.create(uuid=sip_id, currentpath="") + + +@pytest.fixture +def test_file(test_transfer: Transfer, test_sip: SIP) -> File: + return File.objects.create( + transfer_id=test_transfer.uuid, + sip_id=test_sip.uuid, + currentlocation=b"test/location", + originallocation=b"test/original", + ) + + +@pytest.mark.django_db +def test_find_transfer_ids_by_unit_uuid_returns_transfer_ids_when_given_sip_uuid( + test_file: File, +) -> None: + result = post_store_aip_hook.find_transfer_ids_by_unit_uuid(str(test_file.sip_id)) + result_strs = {str(r) for r in result} + assert result_strs == {str(test_file.transfer_id)} + + +@pytest.mark.django_db +def test_find_transfer_ids_by_unit_uuid_returns_transfer_ids_when_given_transfer_uuid( + test_file: File, +) -> None: + result = post_store_aip_hook.find_transfer_ids_by_unit_uuid( + str(test_file.transfer_id) + ) + result_strs = {str(r) for r in result} + assert result_strs == {str(test_file.transfer_id)} diff --git a/tests/MCPClient/test_reingest_mets.py b/tests/MCPClient/test_reingest_mets.py index b3a4d99fa7..f56a667561 100644 --- a/tests/MCPClient/test_reingest_mets.py +++ b/tests/MCPClient/test_reingest_mets.py @@ -4,16 +4,17 @@ import unittest from pathlib import Path -import archivematicaCreateMETSReingest import metsrw -from client.job import Job from django.core.management import call_command from django.test import TestCase from lxml import etree -from main import models -from namespaces import NSMAP -from namespaces import nsmap_for_premis2 -from version import get_preservation_system_identifier + +from archivematica.archivematicaCommon.namespaces import NSMAP +from archivematica.archivematicaCommon.namespaces import nsmap_for_premis2 +from archivematica.archivematicaCommon.version import get_preservation_system_identifier +from archivematica.dashboard.main import models +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts import archivematicaCreateMETSReingest THIS_DIR = os.path.dirname(os.path.abspath(__file__)) FIXTURES_DIR = os.path.join(THIS_DIR, "fixtures") diff --git a/tests/MCPClient/test_restructure_dip_for_content_dm_upload.py b/tests/MCPClient/test_restructure_dip_for_content_dm_upload.py index 8ae5bfdcbc..0bbb39aa8b 100644 --- a/tests/MCPClient/test_restructure_dip_for_content_dm_upload.py +++ b/tests/MCPClient/test_restructure_dip_for_content_dm_upload.py @@ -2,7 +2,8 @@ import shutil import pytest -import restructure_dip_for_content_dm_upload + +from archivematica.MCPClient.clientScripts import restructure_dip_for_content_dm_upload THIS_DIR = os.path.dirname(os.path.abspath(__file__)) diff --git a/tests/MCPClient/test_rights_from_csv.py b/tests/MCPClient/test_rights_from_csv.py index 3c2ecf529f..056edd6422 100644 --- a/tests/MCPClient/test_rights_from_csv.py +++ b/tests/MCPClient/test_rights_from_csv.py @@ -1,9 +1,10 @@ import os -import rights_from_csv -from client.job import Job from django.test import TestCase -from main import models + +from archivematica.dashboard.main import models +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts import rights_from_csv THIS_DIR = os.path.dirname(os.path.abspath(__file__)) diff --git a/tests/MCPClient/test_store_file_modification.py b/tests/MCPClient/test_store_file_modification.py index f6639e7f14..e1a9e83b6d 100644 --- a/tests/MCPClient/test_store_file_modification.py +++ b/tests/MCPClient/test_store_file_modification.py @@ -2,11 +2,12 @@ import shutil import tempfile -import store_file_modification_dates from django.test import TestCase from django.test import override_settings from django.utils.timezone import get_current_timezone -from main import models + +from archivematica.dashboard.main import models +from archivematica.MCPClient.clientScripts import store_file_modification_dates THIS_DIR = os.path.dirname(os.path.abspath(__file__)) diff --git a/tests/MCPClient/test_transcribe_file.py b/tests/MCPClient/test_transcribe_file.py index 4f0c6bdb21..fee7027a19 100644 --- a/tests/MCPClient/test_transcribe_file.py +++ b/tests/MCPClient/test_transcribe_file.py @@ -1,12 +1,14 @@ import pathlib +import uuid from unittest import mock import pytest import pytest_django -import transcribe_file -from client.job import Job -from fpr import models as fprmodels -from main import models + +from archivematica.dashboard.fpr import models as fprmodels +from archivematica.dashboard.main import models +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts import transcribe_file EXECUTE_OR_RUN_STDOUT = "Hello" @@ -61,7 +63,10 @@ def preservation_file_format_version( @pytest.mark.django_db -@mock.patch("transcribe_file.executeOrRun", return_value=(0, EXECUTE_OR_RUN_STDOUT, "")) +@mock.patch( + "archivematica.MCPClient.clientScripts.transcribe_file.executeOrRun", + return_value=(0, EXECUTE_OR_RUN_STDOUT, ""), +) def test_main( execute_or_run: mock.Mock, sip_file: models.File, @@ -252,7 +257,7 @@ def test_main_if_fprule_is_disabled( @pytest.mark.django_db -@mock.patch("transcribe_file.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.transcribe_file.executeOrRun") def test_main_if_command_is_not_bash_script( execute_or_run: mock.Mock, fpcommand: fprmodels.FPCommand, @@ -283,3 +288,26 @@ def test_main_if_command_is_not_bash_script( # Ensure the arguments passed is a list. execute_or_run_args = call_kwargs["arguments"] assert isinstance(execute_or_run_args, list) + + +@pytest.mark.django_db +def test_call_handles_invalid_job_arguments() -> None: + task_uuid = uuid.uuid4() + # None in the file_uuid represents a task on a file that has been + # read by the MCPServer from the file system and not from the database. + file_uuid = "None" + job = mock.Mock( + args=[ + "transcribe_file", + str(task_uuid), + str(file_uuid), + ], + spec=Job, + ) + job.JobContext = mock.MagicMock() + + transcribe_file.call([job]) + + job.print_error.assert_called_once_with( + f"argument file_uuid: invalid UUID value: '{file_uuid}'" + ) diff --git a/tests/MCPClient/test_upload_archivesspace.py b/tests/MCPClient/test_upload_archivesspace.py index b8acf38ae5..242a85f5e6 100644 --- a/tests/MCPClient/test_upload_archivesspace.py +++ b/tests/MCPClient/test_upload_archivesspace.py @@ -4,7 +4,8 @@ from unittest import mock import pytest -import upload_archivesspace + +from archivematica.MCPClient.clientScripts import upload_archivesspace def test_recursive_file_gen(tmpdir): @@ -25,7 +26,7 @@ def test_get_files_from_dip_finds_files(tmpdir): assert sorted(result) == [str(object1), str(object2)] -@mock.patch("upload_archivesspace.logger") +@mock.patch("archivematica.MCPClient.clientScripts.upload_archivesspace.logger") def test_get_files_from_dip_with_empty_dip_location(logger, tmpdir): dip = tmpdir.mkdir("mydip") with pytest.raises(ValueError): @@ -35,7 +36,7 @@ def test_get_files_from_dip_with_empty_dip_location(logger, tmpdir): @mock.patch( - "upload_archivesspace.ArchivesSpaceDIPObjectResourcePairing.objects.filter", + "archivematica.MCPClient.clientScripts.upload_archivesspace.ArchivesSpaceDIPObjectResourcePairing.objects.filter", return_value=[ mock.Mock(fileuuid="1", resourceid="myresource"), mock.Mock(fileuuid="2", resourceid="myresource"), @@ -48,7 +49,9 @@ def test_get_pairs(filter_mock): filter_mock.assert_called_once_with(dipuuid=dip_uuid) -@mock.patch("upload_archivesspace.ArchivesSpaceDIPObjectResourcePairing.objects.filter") +@mock.patch( + "archivematica.MCPClient.clientScripts.upload_archivesspace.ArchivesSpaceDIPObjectResourcePairing.objects.filter" +) def test_delete_pairs(filter_mock): dip_uuid = "somedipuuid" queryset_mock = mock.Mock() @@ -63,8 +66,8 @@ def test_delete_pairs(filter_mock): ["http://some/uri/", "http://some/uri"], ids=["uri_with_trailing_slash", "uri_with_no_trailing_slash"], ) -@mock.patch("upload_archivesspace.mets_file") -@mock.patch("upload_archivesspace.get_pairs") +@mock.patch("archivematica.MCPClient.clientScripts.upload_archivesspace.mets_file") +@mock.patch("archivematica.MCPClient.clientScripts.upload_archivesspace.get_pairs") def test_upload_to_archivesspace_adds_trailing_slash_to_uri( get_pairs, mest_file, db, uri ): @@ -117,9 +120,9 @@ def test_upload_to_archivesspace_adds_trailing_slash_to_uri( ], ids=["with_restrictions", "with_access_conditions", "with_use_conditions"], ) -@mock.patch("upload_archivesspace.get_pairs") -@mock.patch("upload_archivesspace.logger") -@mock.patch("upload_archivesspace.mets_file") +@mock.patch("archivematica.MCPClient.clientScripts.upload_archivesspace.get_pairs") +@mock.patch("archivematica.MCPClient.clientScripts.upload_archivesspace.logger") +@mock.patch("archivematica.MCPClient.clientScripts.upload_archivesspace.mets_file") def test_upload_to_archivesspace_gets_mets_if_needed( mets_file_mock, logger, get_pairs, params ): @@ -147,9 +150,9 @@ def test_upload_to_archivesspace_gets_mets_if_needed( ) -@mock.patch("upload_archivesspace.get_pairs") -@mock.patch("upload_archivesspace.logger") -@mock.patch("upload_archivesspace.mets_file") +@mock.patch("archivematica.MCPClient.clientScripts.upload_archivesspace.get_pairs") +@mock.patch("archivematica.MCPClient.clientScripts.upload_archivesspace.logger") +@mock.patch("archivematica.MCPClient.clientScripts.upload_archivesspace.mets_file") def test_upload_to_archivesspace_logs_files_with_no_pairs( mets_file, logger, get_pairs, db ): @@ -176,9 +179,9 @@ def test_upload_to_archivesspace_logs_files_with_no_pairs( assert not success -@mock.patch("upload_archivesspace.get_pairs") -@mock.patch("upload_archivesspace.logger") -@mock.patch("upload_archivesspace.mets_file") +@mock.patch("archivematica.MCPClient.clientScripts.upload_archivesspace.get_pairs") +@mock.patch("archivematica.MCPClient.clientScripts.upload_archivesspace.logger") +@mock.patch("archivematica.MCPClient.clientScripts.upload_archivesspace.mets_file") def test_upload_to_archivesspace_when_upload_fails(mets_file, logger, get_pairs, db): file1_uuid = uuid.uuid4() file2_uuid = uuid.uuid4() @@ -212,7 +215,7 @@ def fail_video_upload(*args, **kwargs): @mock.patch( - "upload_archivesspace.get_parser", + "archivematica.MCPClient.clientScripts.upload_archivesspace.get_parser", return_value=mock.Mock( **{ "parse_args.return_value": mock.Mock( @@ -237,9 +240,16 @@ def fail_video_upload(*args, **kwargs): } ), ) -@mock.patch("upload_archivesspace.ArchivesSpaceClient") -@mock.patch("upload_archivesspace.get_files_from_dip", return_value=[]) -@mock.patch("upload_archivesspace.upload_to_archivesspace") +@mock.patch( + "archivematica.MCPClient.clientScripts.upload_archivesspace.ArchivesSpaceClient" +) +@mock.patch( + "archivematica.MCPClient.clientScripts.upload_archivesspace.get_files_from_dip", + return_value=[], +) +@mock.patch( + "archivematica.MCPClient.clientScripts.upload_archivesspace.upload_to_archivesspace" +) def test_call( upload_to_archivesspace, get_files_from_dip_mock, @@ -282,10 +292,16 @@ def test_call( ], ids=["no_files_found", "unknown_error_raised"], ) -@mock.patch("upload_archivesspace.get_parser") -@mock.patch("upload_archivesspace.ArchivesSpaceClient") -@mock.patch("upload_archivesspace.get_files_from_dip") -@mock.patch("upload_archivesspace.upload_to_archivesspace") +@mock.patch("archivematica.MCPClient.clientScripts.upload_archivesspace.get_parser") +@mock.patch( + "archivematica.MCPClient.clientScripts.upload_archivesspace.ArchivesSpaceClient" +) +@mock.patch( + "archivematica.MCPClient.clientScripts.upload_archivesspace.get_files_from_dip" +) +@mock.patch( + "archivematica.MCPClient.clientScripts.upload_archivesspace.upload_to_archivesspace" +) def test_call_when_files_from_dip_cant_be_retrieved( upload_to_archivesspace, get_files_from_dip, @@ -303,10 +319,17 @@ def test_call_when_files_from_dip_cant_be_retrieved( upload_to_archivesspace.assert_not_called() -@mock.patch("upload_archivesspace.get_parser") -@mock.patch("upload_archivesspace.ArchivesSpaceClient") -@mock.patch("upload_archivesspace.get_files_from_dip") -@mock.patch("upload_archivesspace.upload_to_archivesspace", return_value=False) +@mock.patch("archivematica.MCPClient.clientScripts.upload_archivesspace.get_parser") +@mock.patch( + "archivematica.MCPClient.clientScripts.upload_archivesspace.ArchivesSpaceClient" +) +@mock.patch( + "archivematica.MCPClient.clientScripts.upload_archivesspace.get_files_from_dip" +) +@mock.patch( + "archivematica.MCPClient.clientScripts.upload_archivesspace.upload_to_archivesspace", + return_value=False, +) def test_call_when_not_all_files_can_be_paired( upload_to_archivesspace, get_files_from_dip, client_factory, get_parser, db ): diff --git a/tests/MCPClient/test_upload_qubit.py b/tests/MCPClient/test_upload_qubit.py index 696f046a14..65c2d3d504 100644 --- a/tests/MCPClient/test_upload_qubit.py +++ b/tests/MCPClient/test_upload_qubit.py @@ -1,11 +1,9 @@ -import os from unittest import mock import pytest -import upload_qubit -from main import models -THIS_DIR = os.path.dirname(os.path.abspath(__file__)) +from archivematica.dashboard.main import models +from archivematica.MCPClient.clientScripts import upload_qubit @pytest.fixture diff --git a/tests/MCPClient/test_validate_file.py b/tests/MCPClient/test_validate_file.py index 86d36c0d1b..ceda2d499d 100644 --- a/tests/MCPClient/test_validate_file.py +++ b/tests/MCPClient/test_validate_file.py @@ -6,10 +6,11 @@ import pytest import pytest_django -import validate_file -from client.job import Job -from fpr import models as fprmodels -from main import models + +from archivematica.dashboard.fpr import models as fprmodels +from archivematica.dashboard.main import models +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts import validate_file @pytest.fixture @@ -59,7 +60,7 @@ def preservation_derivation( @pytest.mark.django_db @mock.patch( - "validate_file.executeOrRun", + "archivematica.MCPClient.clientScripts.validate_file.executeOrRun", return_value=( 0, json.dumps( @@ -83,7 +84,7 @@ def test_job_warns_if_preservation_derivative_sip_does_not_exist( sip_uuid = uuid.uuid4() job = mock.Mock( args=[ - "validate_file.py", + "archivematica.MCPClient.clientScripts.validate_file.py", preservation_file.currentlocation.decode(), str(preservation_file.uuid), str(sip_uuid), @@ -105,7 +106,7 @@ def test_job_warns_if_preservation_derivative_sip_does_not_exist( @pytest.mark.django_db @mock.patch( - "validate_file.executeOrRun", + "archivematica.MCPClient.clientScripts.validate_file.executeOrRun", return_value=( 0, json.dumps( @@ -129,7 +130,7 @@ def test_job_warns_if_preservation_derivative_sip_logs_directory_does_not_exist( ) -> None: job = mock.Mock( args=[ - "validate_file.py", + "archivematica.MCPClient.clientScripts.validate_file.py", preservation_file.currentlocation.decode(), str(preservation_file.uuid), str(sip.uuid), @@ -155,7 +156,7 @@ def test_job_warns_if_preservation_derivative_sip_logs_directory_does_not_exist( [("pass", "successful"), ("partial pass", "partially successful")], ids=["pass", "partial-pass"], ) -@mock.patch("validate_file.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.validate_file.executeOrRun") def test_job_succeeds_with_passing_validation_outcome( execute_or_run: mock.Mock, outcome_information: str, @@ -179,7 +180,7 @@ def test_job_succeeds_with_passing_validation_outcome( ) job = mock.Mock( args=[ - "validate_file.py", + "archivematica.MCPClient.clientScripts.validate_file.py", preservation_file.currentlocation.decode(), str(preservation_file.uuid), str(sip.uuid), @@ -223,7 +224,7 @@ def test_job_succeeds_with_passing_validation_outcome( @pytest.mark.django_db -@mock.patch("validate_file.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.validate_file.executeOrRun") def test_job_saves_command_output_as_preservation_logs( execute_or_run: mock.Mock, sip: models.SIP, @@ -248,7 +249,7 @@ def test_job_saves_command_output_as_preservation_logs( ) job = mock.Mock( args=[ - "validate_file.py", + "archivematica.MCPClient.clientScripts.validate_file.py", preservation_file.currentlocation.decode(), str(preservation_file.uuid), str(sip.uuid), @@ -273,7 +274,7 @@ def test_job_saves_command_output_as_preservation_logs( @pytest.mark.django_db @mock.patch( - "validate_file.executeOrRun", + "archivematica.MCPClient.clientScripts.validate_file.executeOrRun", return_value=( 0, json.dumps( @@ -306,7 +307,7 @@ def test_job_falls_back_to_default_validation_rule( ) job = mock.Mock( args=[ - "validate_file.py", + "archivematica.MCPClient.clientScripts.validate_file.py", preservation_file.currentlocation.decode(), str(preservation_file.uuid), str(sip.uuid), @@ -347,7 +348,7 @@ def test_job_skips_validation_if_rules_do_not_exist( job = mock.Mock( args=[ - "validate_file.py", + "archivematica.MCPClient.clientScripts.validate_file.py", sip_file.currentlocation.decode(), str(sip_file.uuid), str(sip.uuid), @@ -384,7 +385,7 @@ def test_job_skips_validation_if_file_is_not_a_derivative( job = mock.Mock( args=[ - "validate_file.py", + "archivematica.MCPClient.clientScripts.validate_file.py", preservation_file.currentlocation.decode(), str(preservation_file.uuid), str(sip.uuid), @@ -405,7 +406,7 @@ def test_job_skips_validation_if_file_is_not_a_derivative( @pytest.mark.django_db -@mock.patch("validate_file.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.validate_file.executeOrRun") def test_job_fails_if_rule_command_fails( execute_or_run: mock.Mock, sip: models.SIP, @@ -420,7 +421,7 @@ def test_job_fails_if_rule_command_fails( execute_or_run.return_value = (exit_status, "", stderr) job = mock.Mock( args=[ - "validate_file.py", + "archivematica.MCPClient.clientScripts.validate_file.py", preservation_file.currentlocation.decode(), str(preservation_file.uuid), str(sip.uuid), @@ -442,7 +443,7 @@ def test_job_fails_if_rule_command_fails( @pytest.mark.django_db -@mock.patch("validate_file.executeOrRun") +@mock.patch("archivematica.MCPClient.clientScripts.validate_file.executeOrRun") def test_job_fails_with_non_passing_validation_outcome( execute_or_run: mock.Mock, sip: models.SIP, @@ -461,7 +462,7 @@ def test_job_fails_with_non_passing_validation_outcome( execute_or_run.return_value = (0, stdout, "") job = mock.Mock( args=[ - "validate_file.py", + "archivematica.MCPClient.clientScripts.validate_file.py", preservation_file.currentlocation.decode(), str(preservation_file.uuid), str(sip.uuid), diff --git a/tests/MCPClient/test_verify_checksum.py b/tests/MCPClient/test_verify_checksum.py index 94cf4dde88..4f204d512b 100644 --- a/tests/MCPClient/test_verify_checksum.py +++ b/tests/MCPClient/test_verify_checksum.py @@ -26,14 +26,18 @@ from unittest import mock import pytest -from client.job import Job -from main.models import Event -from main.models import File -from verify_checksum import Hashsum -from verify_checksum import NoHashCommandAvailable -from verify_checksum import PREMISFailure -from verify_checksum import get_file_queryset -from verify_checksum import write_premis_event_per_file + +from archivematica.dashboard.main.models import Agent +from archivematica.dashboard.main.models import Event +from archivematica.dashboard.main.models import File +from archivematica.MCPClient.client.job import Job +from archivematica.MCPClient.clientScripts.verify_checksum import Hashsum +from archivematica.MCPClient.clientScripts.verify_checksum import NoHashCommandAvailable +from archivematica.MCPClient.clientScripts.verify_checksum import PREMISFailure +from archivematica.MCPClient.clientScripts.verify_checksum import get_file_queryset +from archivematica.MCPClient.clientScripts.verify_checksum import ( + write_premis_event_per_file, +) class TestHashsum: @@ -75,9 +79,9 @@ def test_valid_initialisation(self, fixture): configured to work with the file path provided. """ if fixture[1]: - assert isinstance( - Hashsum(fixture[0]), Hashsum - ), "Hashsum object not instantiated correctly" + assert isinstance(Hashsum(fixture[0]), Hashsum), ( + "Hashsum object not instantiated correctly" + ) else: with pytest.raises(NoHashCommandAvailable): Hashsum(fixture[0]) @@ -99,9 +103,9 @@ def test_provenance_string(self): with mock.patch.object( hashsum, "_call", return_value=version_string ) as mock_call: - assert ( - hashsum.version() == "md5sum (GNU coreutils) 8.28" - ), "Hashsum version retrieved is incorrect" + assert hashsum.version() == "md5sum (GNU coreutils) 8.28", ( + "Hashsum version retrieved is incorrect" + ) mock_call.assert_called_once_with("--version") expected_provenance = 'program="md5sum -c --strict metadata/checksum.md5"; version="md5sum (GNU coreutils) 8.28"' with mock.patch.object( @@ -110,9 +114,9 @@ def test_provenance_string(self): (hashsum.COMMAND,) + ("-c", "--strict", hash_file), ): provenance_output = hashsum.get_command_detail() - assert ( - provenance_output == expected_provenance - ), f"Provenance output is incorrect: {provenance_output}" + assert provenance_output == expected_provenance, ( + f"Provenance output is incorrect: {provenance_output}" + ) def test_provenance_string_no_command(self): """When nothing has happened, e.g. the checksums haven't been validated @@ -146,10 +150,11 @@ def test_compare_hashes_failed(self): "sha256: objects/nested/ファイル3.bin: FAILED\n" "sha256: objects/readonly.file: FAILED open or read" ) - with mock.patch.object( - hashsum, "_call", return_value=output_string - ) as mock_call, mock.patch.object( - hashsum, "count_and_compare_lines", return_value=True + with ( + mock.patch.object( + hashsum, "_call", return_value=output_string + ) as mock_call, + mock.patch.object(hashsum, "count_and_compare_lines", return_value=True), ): mock_call.side_effect = subprocess.CalledProcessError( returncode=1, cmd=toolname, output=output_string @@ -159,9 +164,9 @@ def test_compare_hashes_failed(self): "-c", "--strict", hash_file, transfer_dir=objects_dir ) assert ret == 1, self.assert_return_value.format(ret) - assert ( - job.get_stderr().strip() == exception_string - ), self.assert_exception_string + assert job.get_stderr().strip() == exception_string, ( + self.assert_exception_string + ) def test_compare_hashes_with_bad_files(self): """Ensure that the formatting of errors is consistent if improperly @@ -186,10 +191,11 @@ def test_compare_hashes_with_bad_files(self): "sha1: comparison exited with status: 1. Please check the formatting of the checksums or integrity of the files.\n" "sha1: sha1sum: WARNING: 1 line is improperly formatted" ) - with mock.patch.object( - hashsum, "_call", return_value=no_proper_output - ) as mock_call, mock.patch.object( - hashsum, "count_and_compare_lines", return_value=True + with ( + mock.patch.object( + hashsum, "_call", return_value=no_proper_output + ) as mock_call, + mock.patch.object(hashsum, "count_and_compare_lines", return_value=True), ): mock_call.side_effect = subprocess.CalledProcessError( returncode=1, cmd=toolname, output=no_proper_output @@ -198,9 +204,9 @@ def test_compare_hashes_with_bad_files(self): mock_call.assert_called_once_with( "-c", "--strict", hash_file, transfer_dir=objects_dir ) - assert ( - job.get_stderr().strip() == except_string_no_proper_out - ), self.assert_exception_string + assert job.get_stderr().strip() == except_string_no_proper_out, ( + self.assert_exception_string + ) assert ret == 1, self.assert_return_value.format(ret) # Flush job.error as it isn't flushed automatically. job.error = "" @@ -211,9 +217,9 @@ def test_compare_hashes_with_bad_files(self): returncode=1, cmd="sha1sum", output=improper_formatting ) ret = hashsum.compare_hashes("") - assert ( - job.get_stderr().strip() == except_string_improper_format - ), self.assert_exception_string + assert job.get_stderr().strip() == except_string_improper_format, ( + self.assert_exception_string + ) mock_call.assert_called_once_with( "-c", "--strict", hash_file, transfer_dir=objects_dir ) @@ -226,10 +232,9 @@ def test_line_comparison_fail(self): hash_file = "metadata/checksum.sha1" hashsum = self.setup_hashsum(hash_file, Job("stub", "stub", ["", ""])) toolname = "sha1sum" - with mock.patch.object( - hashsum, "_call", return_value=None - ) as mock_call, mock.patch.object( - hashsum, "count_and_compare_lines", return_value=False + with ( + mock.patch.object(hashsum, "_call", return_value=None) as mock_call, + mock.patch.object(hashsum, "count_and_compare_lines", return_value=False), ): mock_call.side_effect = subprocess.CalledProcessError( returncode=1, cmd=toolname, output=None @@ -251,9 +256,9 @@ def test_line_comparison_fail(self): ) def test_get_ext(self, fixture): """get_ext helps to format usefully.""" - assert ( - Hashsum.get_ext(fixture[0]) == fixture[1] - ), "Incorrect extension returned from Hashsum" + assert Hashsum.get_ext(fixture[0]) == fixture[1], ( + "Incorrect extension returned from Hashsum" + ) @staticmethod def test_decode_and_version_string(): @@ -264,18 +269,26 @@ def test_decode_and_version_string(): b"sha256sum (GNU coreutils) 8.28\n" b"Copyright (C) 2017 Free Software Foundation, Inc." ) - assert Hashsum._decode(version_string)[ - 0 - ], "Version string incorrectly decoded by Hashsum" - assert ( - Hashsum._decode(version_string)[0] == "sha256sum (GNU coreutils) 8.28" - ), "Invalid version string decoded by Hashsum" + assert Hashsum._decode(version_string)[0], ( + "Version string incorrectly decoded by Hashsum" + ) + assert Hashsum._decode(version_string)[0] == "sha256sum (GNU coreutils) 8.28", ( + "Invalid version string decoded by Hashsum" + ) @pytest.mark.django_db def test_write_premis_event_to_db(self, transfer, transfer_file): """Test that the microservice job connects to the database as anticipated, writes its data, and that data can then be retrieved. """ + Agent.objects.get_or_create( + pk=2, + agenttype="organization", + identifiervalue="ORG", + name="Your Organization Name Here", + identifiertype="repository code", + ) + # Values the job will write. algorithms = ["md5", "sha512", "sha1"] event_type = "fixity check" @@ -305,21 +318,21 @@ def test_write_premis_event_to_db(self, transfer, transfer_file): event_detail = f"{algorithm}: {detail}" write_premis_event_per_file(file_objs_queryset, package_uuid, event_detail) file_uuids = File.objects.filter(**kwargs).values_list("uuid") - assert ( - file_uuids - ), "Files couldn't be retrieved for the transfer from the database" + assert file_uuids, ( + "Files couldn't be retrieved for the transfer from the database" + ) event_algorithms = [] for uuid_ in file_uuids: events = Event.objects.filter(file_uuid=uuid_, event_type=event_type) - assert len(events) == len( - algorithms - ), f"Length of the event objects is not '1', it is: {len(events)}" - assert ( - events[0].event_outcome == event_outcome - ), f"Event outcome retrieved from the database is incorrect: {events[0].event_outcome}" - assert ( - detail in events[0].event_detail - ), f"Event detail retrieved from the database is incorrect: {events[0].event_detail}" + assert len(events) == len(algorithms), ( + f"Length of the event objects is not '1', it is: {len(events)}" + ) + assert events[0].event_outcome == event_outcome, ( + f"Event outcome retrieved from the database is incorrect: {events[0].event_outcome}" + ) + assert detail in events[0].event_detail, ( + f"Event detail retrieved from the database is incorrect: {events[0].event_detail}" + ) # Ensure that UUID creation has happened as anticipated. Will raise # a TypeError if otherwise. (events[0].event_id) @@ -334,26 +347,26 @@ def test_write_premis_event_to_db(self, transfer, transfer_file): idtypes.append(agent.identifiertype) agentnames.append(agent.name) agenttypes.append(agent.agenttype) - assert set(idvalues) == set( - identifier_values - ), "agent identifier values returned don't match" - assert set(idtypes) == set( - identifier_types - ), "agent type values returned don't match" - assert set(agentnames) == set( - agent_names - ), "agent name values returned don't match" + assert set(idvalues) == set(identifier_values), ( + "agent identifier values returned don't match" + ) + assert set(idtypes) == set(identifier_types), ( + "agent type values returned don't match" + ) + assert set(agentnames) == set(agent_names), ( + "agent name values returned don't match" + ) assert set(agenttypes) == set(agent_types), "agent types don't match" - assert ( - _agent_count == number_of_expected_agents - ), f"Number of agents is incorrect: {_agent_count} expected: {number_of_expected_agents}" + assert _agent_count == number_of_expected_agents, ( + f"Number of agents is incorrect: {_agent_count} expected: {number_of_expected_agents}" + ) # Collect the different checksum algorithms written to ensure they # were all written independently in the function. for event in events: event_algorithms.append(event.event_detail.split(":", 1)[0]) - assert set(event_algorithms) == set( - algorithms - ), "No all algorithms written to PREMIS events" + assert set(event_algorithms) == set(algorithms), ( + "No all algorithms written to PREMIS events" + ) @pytest.mark.django_db def test_get_file_obj_queryset(self, transfer, transfer_file): diff --git a/tests/MCPClient/upload-qubit-test b/tests/MCPClient/upload-qubit-test index 7c4d3176a6..719cbb842f 100755 --- a/tests/MCPClient/upload-qubit-test +++ b/tests/MCPClient/upload-qubit-test @@ -1,7 +1,7 @@ #!/bin/bash # upload-qubit script location -UPLOAD_QUBIT_PATH="${HOME}/archivematica/src/MCPClient/lib/clientScripts/upload-qubit.py" +UPLOAD_QUBIT_PATH="${HOME}/archivematica/src/archivematica/MCPClient/clientScripts/upload-qubit.py" # Qubit URL and login URL="http://localhost/ica-atom/index.php/sword/deposit/archivematica" diff --git a/tests/MCPServer/test_gearman.py b/tests/MCPServer/test_gearman.py index 92abcbf11f..a02f6f7d1a 100644 --- a/tests/MCPServer/test_gearman.py +++ b/tests/MCPServer/test_gearman.py @@ -4,9 +4,10 @@ import gearman import pytest -from server.jobs import Job -from server.tasks import GearmanTaskBackend -from server.tasks import Task + +from archivematica.MCPServer.server.jobs import Job +from archivematica.MCPServer.server.tasks import GearmanTaskBackend +from archivematica.MCPServer.server.tasks import Task class MockJob(Job): @@ -58,9 +59,15 @@ def format_gearman_response(task_results): return response -@mock.patch("server.tasks.backends.gearman_backend.MCPGearmanClient") -@mock.patch("server.tasks.GearmanTaskBackend.TASK_BATCH_SIZE", 1) -@mock.patch("server.tasks.backends.gearman_backend.Task.bulk_log") +@mock.patch( + "archivematica.MCPServer.server.tasks.backends.gearman_backend.MCPGearmanClient" +) +@mock.patch( + "archivematica.MCPServer.server.tasks.GearmanTaskBackend.TASK_BATCH_SIZE", 1 +) +@mock.patch( + "archivematica.MCPServer.server.tasks.backends.gearman_backend.Task.bulk_log" +) def test_gearman_task_submission(bulk_log, mock_client, simple_job, simple_task): backend = GearmanTaskBackend() backend.submit_task(simple_job, simple_task) @@ -80,8 +87,12 @@ def test_gearman_task_submission(bulk_log, mock_client, simple_job, simple_task) assert submit_job_kwargs["max_retries"] == GearmanTaskBackend.MAX_RETRIES -@mock.patch("server.tasks.backends.gearman_backend.MCPGearmanClient") -@mock.patch("server.tasks.backends.gearman_backend.Task.bulk_log") +@mock.patch( + "archivematica.MCPServer.server.tasks.backends.gearman_backend.MCPGearmanClient" +) +@mock.patch( + "archivematica.MCPServer.server.tasks.backends.gearman_backend.Task.bulk_log" +) def test_gearman_task_result_success(bulk_log, mock_client, simple_job, simple_task): backend = GearmanTaskBackend() @@ -127,8 +138,12 @@ def mock_jobs_completed(*args): assert task_result.done is True -@mock.patch("server.tasks.backends.gearman_backend.MCPGearmanClient") -@mock.patch("server.tasks.backends.gearman_backend.Task.bulk_log") +@mock.patch( + "archivematica.MCPServer.server.tasks.backends.gearman_backend.MCPGearmanClient" +) +@mock.patch( + "archivematica.MCPServer.server.tasks.backends.gearman_backend.Task.bulk_log" +) def test_gearman_task_result_error(bulk_log, mock_client, simple_job, simple_task): backend = GearmanTaskBackend() @@ -164,9 +179,13 @@ def mock_jobs_completed(*args): @pytest.mark.parametrize( "reverse_result_order", (False, True), ids=["regular", "reversed"] ) -@mock.patch("server.tasks.backends.gearman_backend.MCPGearmanClient") +@mock.patch( + "archivematica.MCPServer.server.tasks.backends.gearman_backend.MCPGearmanClient" +) @mock.patch.object(GearmanTaskBackend, "TASK_BATCH_SIZE", 2) -@mock.patch("server.tasks.backends.gearman_backend.Task.bulk_log") +@mock.patch( + "archivematica.MCPServer.server.tasks.backends.gearman_backend.Task.bulk_log" +) def test_gearman_multiple_batches( bulk_log, mock_client, simple_job, simple_task, reverse_result_order ): diff --git a/tests/MCPServer/test_integration.py b/tests/MCPServer/test_integration.py index 468bba4739..13555a69bb 100644 --- a/tests/MCPServer/test_integration.py +++ b/tests/MCPServer/test_integration.py @@ -8,20 +8,21 @@ import pytest from django.utils import timezone from lxml import etree -from main import models -from server.jobs import DirectoryClientScriptJob -from server.jobs import FilesClientScriptJob -from server.jobs import GetUnitVarLinkJob -from server.jobs import JobChain -from server.jobs import NextChainDecisionJob -from server.jobs import OutputClientScriptJob -from server.jobs import OutputDecisionJob -from server.jobs import SetUnitVarLinkJob -from server.jobs import UpdateContextDecisionJob -from server.packages import Transfer -from server.queues import PackageQueue -from server.tasks import TaskBackend -from server.workflow import load as load_workflow + +from archivematica.dashboard.main import models +from archivematica.MCPServer.server.jobs import DirectoryClientScriptJob +from archivematica.MCPServer.server.jobs import FilesClientScriptJob +from archivematica.MCPServer.server.jobs import GetUnitVarLinkJob +from archivematica.MCPServer.server.jobs import JobChain +from archivematica.MCPServer.server.jobs import NextChainDecisionJob +from archivematica.MCPServer.server.jobs import OutputClientScriptJob +from archivematica.MCPServer.server.jobs import OutputDecisionJob +from archivematica.MCPServer.server.jobs import SetUnitVarLinkJob +from archivematica.MCPServer.server.jobs import UpdateContextDecisionJob +from archivematica.MCPServer.server.packages import Transfer +from archivematica.MCPServer.server.queues import PackageQueue +from archivematica.MCPServer.server.tasks import TaskBackend +from archivematica.MCPServer.server.workflow import load as load_workflow FIXTURES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures") INTEGRATION_TEST_PATH = os.path.join(FIXTURES_DIR, "workflow-integration-test.json") @@ -94,9 +95,9 @@ def dummy_file_replacements(request): @pytest.mark.django_db(transaction=True) -@mock.patch("server.jobs.decisions.load_processing_xml") -@mock.patch("server.jobs.decisions.load_preconfigured_choice") -@mock.patch("server.jobs.client.get_task_backend") +@mock.patch("archivematica.MCPServer.server.jobs.decisions.load_processing_xml") +@mock.patch("archivematica.MCPServer.server.jobs.decisions.load_preconfigured_choice") +@mock.patch("archivematica.MCPServer.server.jobs.client.get_task_backend") def test_workflow_integration( mock_get_task_backend, mock_load_preconfigured_choice, @@ -114,10 +115,13 @@ def test_workflow_integration( settings.PROCESSING_DIRECTORY = str(tmp_path / "processing") mock_get_task_backend.return_value = echo_backend - with mock.patch.dict( - "server.packages.BASE_REPLACEMENTS", - {r"%processingDirectory%": settings.PROCESSING_DIRECTORY}, - ), mock.patch.object(transfer, "files", return_value=dummy_file_replacements): + with ( + mock.patch.dict( + "archivematica.MCPServer.server.packages.BASE_REPLACEMENTS", + {r"%processingDirectory%": settings.PROCESSING_DIRECTORY}, + ), + mock.patch.object(transfer, "files", return_value=dummy_file_replacements), + ): # Schedule the first job first_workflow_chain = workflow.get_chains()[ "3816f689-65a8-4ad0-ac27-74292a70b093" diff --git a/tests/MCPServer/test_mcp.py b/tests/MCPServer/test_mcp.py index 5c07d8affc..297c6d669c 100644 --- a/tests/MCPServer/test_mcp.py +++ b/tests/MCPServer/test_mcp.py @@ -3,15 +3,17 @@ from unittest import mock import pytest -from server.mcp import main -from server.mcp import watched_dir_handler + +from archivematica.MCPServer.server.mcp import main +from archivematica.MCPServer.server.mcp import watched_dir_handler @pytest.mark.django_db(transaction=True) -@mock.patch("server.packages.models.Transfer.objects.create") -@mock.patch("server.packages.uuid4") +@mock.patch("archivematica.MCPServer.server.packages.models.Transfer.objects.create") +@mock.patch("archivematica.MCPServer.server.packages.uuid4") @mock.patch( - "server.mcp.JobChain", mock.MagicMock(return_value=iter(["some_chain_link"])) + "archivematica.MCPServer.server.mcp.JobChain", + mock.MagicMock(return_value=iter(["some_chain_link"])), ) def test_watched_dir_handler_creates_transfer_if_it_does_not_exist( uuid4, create_mock, tmpdir @@ -51,10 +53,11 @@ def test_watched_dir_handler_creates_transfer_if_it_does_not_exist( @pytest.mark.django_db(transaction=True) -@mock.patch("server.packages.models.Transfer.objects.create") -@mock.patch("server.packages.uuid4") +@mock.patch("archivematica.MCPServer.server.packages.models.Transfer.objects.create") +@mock.patch("archivematica.MCPServer.server.packages.uuid4") @mock.patch( - "server.mcp.JobChain", mock.MagicMock(return_value=iter(["some_chain_link"])) + "archivematica.MCPServer.server.mcp.JobChain", + mock.MagicMock(return_value=iter(["some_chain_link"])), ) def test_watched_dir_handler_creates_transfer_for_file(uuid4, create_mock, tmpdir): """Test that a models.Transfer object exists for a file path.""" @@ -83,12 +86,12 @@ def test_watched_dir_handler_creates_transfer_for_file(uuid4, create_mock, tmpdi create_mock.assert_called_once_with(uuid=transfer_uuid, currentlocation=str(path)) -@mock.patch("server.mcp.metrics") -@mock.patch("server.mcp.Task") -@mock.patch("server.mcp.Job") -@mock.patch("server.mcp.Package") -@mock.patch("server.mcp.shared_dirs") -@mock.patch("server.mcp.load_workflow") +@mock.patch("archivematica.MCPServer.server.mcp.metrics") +@mock.patch("archivematica.MCPServer.server.mcp.Task") +@mock.patch("archivematica.MCPServer.server.mcp.Job") +@mock.patch("archivematica.MCPServer.server.mcp.Package") +@mock.patch("archivematica.MCPServer.server.mcp.shared_dirs") +@mock.patch("archivematica.MCPServer.server.mcp.load_workflow") def test_mcp_main( mock_load_workflow, mock_shared_dirs, diff --git a/tests/MCPServer/test_package.py b/tests/MCPServer/test_package.py index 919ed8b92a..d84a3dde36 100644 --- a/tests/MCPServer/test_package.py +++ b/tests/MCPServer/test_package.py @@ -5,17 +5,20 @@ import pytest from django.core.exceptions import ValidationError -from main import models -from server.packages import DIP -from server.packages import SIP -from server.packages import Package -from server.packages import Transfer -from server.packages import _determine_transfer_paths -from server.packages import _move_to_internal_shared_dir -from server.packages import _pad_destination_filepath_if_it_already_exists -from server.packages import create_package -from server.queues import PackageQueue -from server.workflow import Workflow + +from archivematica.dashboard.main import models +from archivematica.MCPServer.server.packages import DIP +from archivematica.MCPServer.server.packages import SIP +from archivematica.MCPServer.server.packages import Package +from archivematica.MCPServer.server.packages import Transfer +from archivematica.MCPServer.server.packages import _determine_transfer_paths +from archivematica.MCPServer.server.packages import _move_to_internal_shared_dir +from archivematica.MCPServer.server.packages import ( + _pad_destination_filepath_if_it_already_exists, +) +from archivematica.MCPServer.server.packages import create_package +from archivematica.MCPServer.server.queues import PackageQueue +from archivematica.MCPServer.server.workflow import Workflow @pytest.mark.parametrize( @@ -203,9 +206,9 @@ def test_reload_file_list(tmp_path): "transfer_id": transfer_uuid, } models.File.objects.create(**kwargs) - assert ( - _file_count == 2 - ), "Database and file objects were not returned by the generator" + assert _file_count == 2, ( + "Database and file objects were not returned by the generator" + ) assert models.File.objects.filter(transfer_id=str(transfer_uuid)).count() == 2 # Simulate an additional file object being added later on in the transfer @@ -229,9 +232,9 @@ def test_reload_file_list(tmp_path): "transfer_id": transfer_uuid, } models.File.objects.create(**kwargs) - assert ( - _file_count == 3 - ), "Database and file objects were not returned by the generator" + assert _file_count == 3, ( + "Database and file objects were not returned by the generator" + ) assert models.File.objects.filter(transfer_id=str(transfer_uuid)).count() == 3 # Now the database is updated, we will still have the same file count, but diff --git a/tests/MCPServer/test_processing_config.py b/tests/MCPServer/test_processing_config.py index 80d39f9b67..a34e7cb1f2 100644 --- a/tests/MCPServer/test_processing_config.py +++ b/tests/MCPServer/test_processing_config.py @@ -1,30 +1,33 @@ -import os -import pathlib +import importlib.resources from unittest import mock import pytest -from server.processing_config import ChainChoicesField -from server.processing_config import ReplaceDictField -from server.processing_config import SharedChainChoicesField -from server.processing_config import StorageLocationField -from server.processing_config import get_processing_fields -from server.processing_config import processing_configuration_file_exists -from server.processing_config import processing_fields -from server.workflow import load -ASSETS_DIR = ( - pathlib.Path(__file__).parent.parent.parent / "src" / "MCPServer" / "lib" / "assets" +from archivematica.MCPServer.server.processing_config import ChainChoicesField +from archivematica.MCPServer.server.processing_config import ReplaceDictField +from archivematica.MCPServer.server.processing_config import SharedChainChoicesField +from archivematica.MCPServer.server.processing_config import StorageLocationField +from archivematica.MCPServer.server.processing_config import get_processing_fields +from archivematica.MCPServer.server.processing_config import ( + processing_configuration_file_exists, ) +from archivematica.MCPServer.server.processing_config import processing_fields +from archivematica.MCPServer.server.workflow import load @pytest.fixture def _workflow(): - path = os.path.join(ASSETS_DIR, "workflow.json") - with open(path) as fp: + with open( + importlib.resources.files("archivematica.MCPServer") + / "assets" + / "workflow.json" + ) as fp: return load(fp) -@mock.patch("storageService.get_location", return_value=[]) +@mock.patch( + "archivematica.archivematicaCommon.storageService.get_location", return_value=[] +) def test_get_processing_fields(_workflow): fields = get_processing_fields(_workflow) @@ -32,7 +35,7 @@ def test_get_processing_fields(_workflow): @mock.patch( - "server.processing_config.processing_fields", + "archivematica.MCPServer.server.processing_config.processing_fields", new=[ StorageLocationField( link_id="b320ce81-9982-408a-9502-097d0daa48fa", @@ -46,7 +49,7 @@ def test_get_processing_fields(_workflow): ), ], ) -@mock.patch("storageService.get_location") +@mock.patch("archivematica.archivematicaCommon.storageService.get_location") def test_storage_location_field(get_location, _workflow): def mocked_get_location(purpose): return [ @@ -122,7 +125,7 @@ def mocked_get_location(purpose): @mock.patch( - "server.processing_config.processing_fields", + "archivematica.MCPServer.server.processing_config.processing_fields", new=[ ReplaceDictField( link_id="f09847c2-ee51-429a-9478-a860477f6b8d", @@ -197,7 +200,7 @@ def test_replace_dict_field(_workflow): @mock.patch( - "server.processing_config.processing_fields", + "archivematica.MCPServer.server.processing_config.processing_fields", new=[ ChainChoicesField( link_id="eeb23509-57e2-4529-8857-9d62525db048", name="reminder" @@ -317,7 +320,7 @@ def test_chain_choices_field(_workflow): @mock.patch( - "server.processing_config.processing_fields", + "archivematica.MCPServer.server.processing_config.processing_fields", new=[ SharedChainChoicesField( link_id="856d2d65-cd25-49fa-8da9-cabb78292894", @@ -414,7 +417,7 @@ def test_processing_configuration_file_exists_with_existent_file(isfile): assert processing_configuration_file_exists("defaultProcessingMCP.xml") -@mock.patch("server.processing_config.logger") +@mock.patch("archivematica.MCPServer.server.processing_config.logger") @mock.patch("os.path.isfile", return_value=False) def test_processing_configuration_file_exists_with_nonexistent_file(isfile, logger): assert not processing_configuration_file_exists("bogus.xml") diff --git a/tests/MCPServer/test_queues.py b/tests/MCPServer/test_queues.py index 6f67e84779..1ebe581488 100644 --- a/tests/MCPServer/test_queues.py +++ b/tests/MCPServer/test_queues.py @@ -6,13 +6,14 @@ from unittest import mock import pytest -from server.jobs import DecisionJob -from server.jobs import Job -from server.packages import DIP -from server.packages import SIP -from server.packages import Transfer -from server.queues import PackageQueue -from server.workflow import Link + +from archivematica.MCPServer.server.jobs import DecisionJob +from archivematica.MCPServer.server.jobs import Job +from archivematica.MCPServer.server.packages import DIP +from archivematica.MCPServer.server.packages import SIP +from archivematica.MCPServer.server.packages import Transfer +from archivematica.MCPServer.server.queues import PackageQueue +from archivematica.MCPServer.server.workflow import Link def _process_one_job(queue): diff --git a/tests/MCPServer/test_rpc_server.py b/tests/MCPServer/test_rpc_server.py index bd586f437b..4b4e4cfb9c 100644 --- a/tests/MCPServer/test_rpc_server.py +++ b/tests/MCPServer/test_rpc_server.py @@ -1,17 +1,14 @@ -import pathlib +import importlib.resources import threading import uuid from unittest import mock import pytest from django.utils import timezone -from main import models -from server import rpc_server -from server import workflow -ASSETS_DIR = ( - pathlib.Path(__file__).parent.parent.parent / "src" / "MCPServer" / "lib" / "assets" -) +from archivematica.dashboard.main import models +from archivematica.MCPServer.server import rpc_server +from archivematica.MCPServer.server import workflow @pytest.mark.django_db @@ -24,7 +21,11 @@ def test_approve_partial_reingest_handler(): currentstep=models.Job.STATUS_AWAITING_DECISION, ) package_queue = mock.MagicMock() - with open(ASSETS_DIR / "workflow.json") as fp: + with open( + importlib.resources.files("archivematica.MCPServer") + / "assets" + / "workflow.json" + ) as fp: wf = workflow.load(fp) shutdown_event = threading.Event() shutdown_event.set() diff --git a/tests/MCPServer/test_translation.py b/tests/MCPServer/test_translation.py index 565cd4d9ff..0c9b8c422d 100644 --- a/tests/MCPServer/test_translation.py +++ b/tests/MCPServer/test_translation.py @@ -1,10 +1,10 @@ from unittest import mock -from server.translation import UNKNOWN_TRANSLATION_LABEL -from server.translation import TranslationLabel +from archivematica.MCPServer.server.translation import UNKNOWN_TRANSLATION_LABEL +from archivematica.MCPServer.server.translation import TranslationLabel -@mock.patch("server.translation.FALLBACK_LANG", "en") +@mock.patch("archivematica.MCPServer.server.translation.FALLBACK_LANG", "en") def test_translation_label(): tr = TranslationLabel({"en": "cat", "es": "gato"}) assert str(tr) == "cat" @@ -13,11 +13,11 @@ def test_translation_label(): assert tr.get_label(lang="es") == "gato" assert tr.get_label(lang="is", fallback_label="köttur") == "köttur" assert tr.get_label(lang="??") == "cat" - with mock.patch("server.translation.FALLBACK_LANG", "xx"): + with mock.patch("archivematica.MCPServer.server.translation.FALLBACK_LANG", "xx"): assert tr.get_label(lang="yy") == UNKNOWN_TRANSLATION_LABEL -@mock.patch("server.translation.FALLBACK_LANG", "en") +@mock.patch("archivematica.MCPServer.server.translation.FALLBACK_LANG", "en") def test_translation_label_with_prepared_codes(): tr = TranslationLabel({"en": "dog", "pt_BR": "cão"}) assert tr.get_label(lang="en") == "dog" diff --git a/tests/MCPServer/test_workflow.py b/tests/MCPServer/test_workflow.py index d8b318854b..496851277a 100644 --- a/tests/MCPServer/test_workflow.py +++ b/tests/MCPServer/test_workflow.py @@ -1,21 +1,20 @@ -import os +import importlib.resources import pathlib from io import StringIO from unittest import mock import pytest from django.utils.translation import gettext_lazy -from server import translation -from server import workflow -ASSETS_DIR = ( - pathlib.Path(__file__).parent.parent.parent / "src" / "MCPServer" / "lib" / "assets" -) +from archivematica.MCPServer.server import translation +from archivematica.MCPServer.server import workflow + +ASSETS_DIR = importlib.resources.files("archivematica.MCPServer") / "assets" FIXTURES_DIR = pathlib.Path(__file__).parent / "fixtures" @mock.patch( - "server.jobs.Job.STATUSES", + "archivematica.MCPServer.server.jobs.Job.STATUSES", ( (1, gettext_lazy("Uno")), (2, gettext_lazy("Dos")), @@ -42,8 +41,8 @@ def test_load_invalid_json(): @pytest.mark.parametrize( "path", ( - os.path.join(ASSETS_DIR, "workflow.json"), - os.path.join(FIXTURES_DIR, "workflow-sample.json"), + ASSETS_DIR / "workflow.json", + FIXTURES_DIR / "workflow-sample.json", ), ) def test_load_valid_document(path): @@ -100,7 +99,7 @@ def test_load_valid_document(path): def test_link_browse_methods(): - with open(os.path.join(ASSETS_DIR, "workflow.json")) as fp: + with open(ASSETS_DIR / "workflow.json") as fp: wf = workflow.load(fp) ln = wf.get_link("1ba589db-88d1-48cf-bb1a-a5f9d2b17378") assert ln.get_next_link(code="0").id == "087d27be-c719-47d8-9bbb-9a7d8b609c44" @@ -114,7 +113,9 @@ def test_get_schema(): assert schema["$id"] == "https://www.archivematica.org/labs/workflow/schema/v1.json" -@mock.patch("server.workflow._LATEST_SCHEMA", "non-existen-schema") +@mock.patch( + "archivematica.MCPServer.server.workflow._LATEST_SCHEMA", "non-existen-schema" +) def test_get_schema_not_found(): with pytest.raises(IOError): workflow._get_schema() diff --git a/tests/MCPServer/test_workflow_abilities.py b/tests/MCPServer/test_workflow_abilities.py index b5c5bab3c9..f21b24b3e6 100644 --- a/tests/MCPServer/test_workflow_abilities.py +++ b/tests/MCPServer/test_workflow_abilities.py @@ -1,14 +1,9 @@ -import os -import pathlib +import importlib.resources import pytest -from server import workflow -from server.workflow_abilities import choice_is_available - -ASSETS_DIR = ( - pathlib.Path(__file__).parent.parent.parent / "src" / "MCPServer" / "lib" / "assets" -) +from archivematica.MCPServer.server import workflow +from archivematica.MCPServer.server.workflow_abilities import choice_is_available CREATE_SIP_LINK_ID = "bb194013-597c-4e4a-8493-b36d190f8717" SEND_TO_BACKLOG_CHAIN_ID = "7065d256-2f47-4b7d-baec-2c4699626121" @@ -16,8 +11,11 @@ @pytest.fixture def _workflow(): - path = os.path.join(ASSETS_DIR, "workflow.json") - with open(path) as fp: + with open( + importlib.resources.files("archivematica.MCPServer") + / "assets" + / "workflow.json" + ) as fp: return workflow.load(fp) diff --git a/tests/archivematicaCommon/test_appconfig.py b/tests/archivematicaCommon/test_appconfig.py index 3b99461030..9d5b1bd8a9 100644 --- a/tests/archivematicaCommon/test_appconfig.py +++ b/tests/archivematicaCommon/test_appconfig.py @@ -2,10 +2,11 @@ from io import StringIO import pytest -from appconfig import Config -from appconfig import process_search_enabled from django.core.exceptions import ImproperlyConfigured +from archivematica.archivematicaCommon.appconfig import Config +from archivematica.archivematicaCommon.appconfig import process_search_enabled + CONFIG_MAPPING = { "search_enabled": { "section": "Dashboard", @@ -32,7 +33,7 @@ ) def test_mapping_list_config_file(option, value, expect): config = Config(env_prefix="ARCHIVEMATICA_DASHBOARD", attrs=CONFIG_MAPPING) - config.read_defaults(StringIO("[Dashboard]\n" f"{option} = {value}")) + config.read_defaults(StringIO(f"[Dashboard]\n{option} = {value}")) if isinstance(expect, list): assert sorted(config.get("search_enabled")) == sorted(expect) else: diff --git a/tests/archivematicaCommon/test_archivematica_functions.py b/tests/archivematicaCommon/test_archivematica_functions.py index 069c78e2f5..fe99340c56 100644 --- a/tests/archivematicaCommon/test_archivematica_functions.py +++ b/tests/archivematicaCommon/test_archivematica_functions.py @@ -1,9 +1,10 @@ from unittest.mock import patch -import archivematicaFunctions as am import bagit import pytest +from archivematica.archivematicaCommon import archivematicaFunctions as am + def test_find_mets_file_match(tmp_path): metadata_dir = tmp_path / "metadata" @@ -43,7 +44,9 @@ def test_get_bag_size(tmpdir): bag.info["Payload-Oxum"] = size_oxum bag.save() # Assertions - with patch("archivematicaFunctions.os") as mock_os: + with patch( + "archivematica.archivematicaCommon.archivematicaFunctions.os" + ) as mock_os: # Test returned value against expected size = am.get_bag_size(bag, bag_dir.strpath) assert size == int(size_oxum.split(".")[0]) @@ -115,15 +118,25 @@ def test_get_oidc_secondary_providers_ignores_provider_if_client_id_and_secret_a monkeypatch.setenv("OIDC_RP_CLIENT_ID_BAR", "bar-client-id") monkeypatch.setenv("OIDC_RP_CLIENT_SECRET_BAZ", "foo-secret") - assert am.get_oidc_secondary_providers(["FOO", "BAR", "BAZ"]) == { + assert am.get_oidc_secondary_providers( + ["FOO", "BAR", "BAZ"], {"given_name": "first_name", "family_name": "last_name"} + ) == { "FOO": { "OIDC_OP_AUTHORIZATION_ENDPOINT": "", "OIDC_OP_JWKS_ENDPOINT": "", "OIDC_OP_LOGOUT_ENDPOINT": "", "OIDC_OP_TOKEN_ENDPOINT": "", "OIDC_OP_USER_ENDPOINT": "", + "OIDC_OP_SET_ROLES_FROM_CLAIMS": False, + "OIDC_OP_ROLE_CLAIM_PATH": "realm_access.roles", + "OIDC_ACCESS_ATTRIBUTE_MAP": { + "given_name": "first_name", + "family_name": "last_name", + }, "OIDC_RP_CLIENT_ID": "foo-client-id", "OIDC_RP_CLIENT_SECRET": "foo-client-secret", + "OIDC_ROLE_CLAIM_ADMIN": "admin", + "OIDC_ROLE_CLAIM_DEFAULT": "default", } } @@ -136,15 +149,25 @@ def test_get_oidc_secondary_providers_strips_provider_names( monkeypatch.setenv("OIDC_RP_CLIENT_ID_BAR", "bar-client-id") monkeypatch.setenv("OIDC_RP_CLIENT_SECRET_BAR", "bar-client-secret") - assert am.get_oidc_secondary_providers([" FOO", " BAR "]) == { + assert am.get_oidc_secondary_providers( + [" FOO", " BAR "], {"given_name": "first_name", "family_name": "last_name"} + ) == { "FOO": { "OIDC_OP_AUTHORIZATION_ENDPOINT": "", "OIDC_OP_JWKS_ENDPOINT": "", "OIDC_OP_LOGOUT_ENDPOINT": "", "OIDC_OP_TOKEN_ENDPOINT": "", "OIDC_OP_USER_ENDPOINT": "", + "OIDC_OP_SET_ROLES_FROM_CLAIMS": False, + "OIDC_OP_ROLE_CLAIM_PATH": "realm_access.roles", + "OIDC_ACCESS_ATTRIBUTE_MAP": { + "given_name": "first_name", + "family_name": "last_name", + }, "OIDC_RP_CLIENT_ID": "foo-client-id", "OIDC_RP_CLIENT_SECRET": "foo-client-secret", + "OIDC_ROLE_CLAIM_ADMIN": "admin", + "OIDC_ROLE_CLAIM_DEFAULT": "default", }, "BAR": { "OIDC_OP_AUTHORIZATION_ENDPOINT": "", @@ -152,8 +175,16 @@ def test_get_oidc_secondary_providers_strips_provider_names( "OIDC_OP_LOGOUT_ENDPOINT": "", "OIDC_OP_TOKEN_ENDPOINT": "", "OIDC_OP_USER_ENDPOINT": "", + "OIDC_OP_SET_ROLES_FROM_CLAIMS": False, + "OIDC_OP_ROLE_CLAIM_PATH": "realm_access.roles", + "OIDC_ACCESS_ATTRIBUTE_MAP": { + "given_name": "first_name", + "family_name": "last_name", + }, "OIDC_RP_CLIENT_ID": "bar-client-id", "OIDC_RP_CLIENT_SECRET": "bar-client-secret", + "OIDC_ROLE_CLAIM_ADMIN": "admin", + "OIDC_ROLE_CLAIM_DEFAULT": "default", }, } @@ -166,15 +197,25 @@ def test_get_oidc_secondary_providers_capitalizes_provider_names( monkeypatch.setenv("OIDC_RP_CLIENT_ID_BAR", "bar-client-id") monkeypatch.setenv("OIDC_RP_CLIENT_SECRET_BAR", "bar-client-secret") - assert am.get_oidc_secondary_providers(["fOo", "bar"]) == { + assert am.get_oidc_secondary_providers( + ["fOo", "bar"], {"given_name": "first_name", "family_name": "last_name"} + ) == { "FOO": { "OIDC_OP_AUTHORIZATION_ENDPOINT": "", "OIDC_OP_JWKS_ENDPOINT": "", "OIDC_OP_LOGOUT_ENDPOINT": "", "OIDC_OP_TOKEN_ENDPOINT": "", "OIDC_OP_USER_ENDPOINT": "", + "OIDC_OP_SET_ROLES_FROM_CLAIMS": False, + "OIDC_OP_ROLE_CLAIM_PATH": "realm_access.roles", + "OIDC_ACCESS_ATTRIBUTE_MAP": { + "given_name": "first_name", + "family_name": "last_name", + }, "OIDC_RP_CLIENT_ID": "foo-client-id", "OIDC_RP_CLIENT_SECRET": "foo-client-secret", + "OIDC_ROLE_CLAIM_ADMIN": "admin", + "OIDC_ROLE_CLAIM_DEFAULT": "default", }, "BAR": { "OIDC_OP_AUTHORIZATION_ENDPOINT": "", @@ -182,7 +223,15 @@ def test_get_oidc_secondary_providers_capitalizes_provider_names( "OIDC_OP_LOGOUT_ENDPOINT": "", "OIDC_OP_TOKEN_ENDPOINT": "", "OIDC_OP_USER_ENDPOINT": "", + "OIDC_OP_SET_ROLES_FROM_CLAIMS": False, + "OIDC_OP_ROLE_CLAIM_PATH": "realm_access.roles", + "OIDC_ACCESS_ATTRIBUTE_MAP": { + "given_name": "first_name", + "family_name": "last_name", + }, "OIDC_RP_CLIENT_ID": "bar-client-id", "OIDC_RP_CLIENT_SECRET": "bar-client-secret", + "OIDC_ROLE_CLAIM_ADMIN": "admin", + "OIDC_ROLE_CLAIM_DEFAULT": "default", }, } diff --git a/tests/archivematicaCommon/test_bindpid.py b/tests/archivematicaCommon/test_bindpid.py index 9bc10e43cd..c6e92dca3e 100644 --- a/tests/archivematicaCommon/test_bindpid.py +++ b/tests/archivematicaCommon/test_bindpid.py @@ -1,7 +1,8 @@ -import bindpid import pytest from django.test import TestCase +from archivematica.archivematicaCommon import bindpid + VALID_ARG_DICT = { "entity_type": "file", "resolve_url_template_file": "https://access.my.org/access/{{ naming_authority }}/{{ pid }}", diff --git a/tests/archivematicaCommon/test_database_functions.py b/tests/archivematicaCommon/test_database_functions.py index 2d36ba6d2e..edc0619eb4 100644 --- a/tests/archivematicaCommon/test_database_functions.py +++ b/tests/archivematicaCommon/test_database_functions.py @@ -1,11 +1,15 @@ import pathlib import uuid -import databaseFunctions import pytest from django.test import TestCase -from main.models import Event -from main.models import File + +from archivematica.archivematicaCommon import databaseFunctions +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import Directory +from archivematica.dashboard.main.models import Event +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import Identifier AGENTS_FIXTURE = pathlib.Path(__file__).parent / "fixtures" / "agents.json" TEST_DATABASE_FUNCTIONS_FIXTURE = ( @@ -152,3 +156,26 @@ def test_insert_into_event_fetches_correct_agent_from_file(self): ).agents assert agents.get(id=2) assert agents.get(id=5) + + +@pytest.fixture +def sip(db): + sip = SIP.objects.create(uuid="f663fd87-5ce4-4114-886e-4856371cf0d6") + sip.identifiers.add(Identifier.objects.create(value="sip_identifier")) + return sip + + +@pytest.fixture +def directories(db, sip): + # Two directories are created but only one is associated with the SIP + dir1 = Directory.objects.create( + uuid="49fe38a0-c50a-4fdf-9353-04d61057220d", sip=sip + ) + dir1.identifiers.add(Identifier.objects.create(value="dir1")) + dir2 = Directory.objects.create(uuid="58eaa39c-2a0b-47fd-9d81-52fbaa108abc") + dir2.identifiers.add(Identifier.objects.create(value="dir2")) + + +def test_get_sip_identifiers_returns_sip_and_directory_identifiers(sip, directories): + result = databaseFunctions.get_sip_identifiers(sip.uuid) + assert sorted(result) == ["dir1", "sip_identifier"] diff --git a/tests/archivematicaCommon/test_dicts.py b/tests/archivematicaCommon/test_dicts.py index b4af4cba10..4221fdb877 100644 --- a/tests/archivematicaCommon/test_dicts.py +++ b/tests/archivematicaCommon/test_dicts.py @@ -1,10 +1,10 @@ import os import pytest -from dicts import ChoicesDict -from dicts import ReplacementDict -from dicts import setup as setup_dicts -from main import models + +from archivematica.archivematicaCommon.dicts import ReplacementDict +from archivematica.archivematicaCommon.dicts import setup as setup_dicts +from archivematica.dashboard.main import models @pytest.fixture @@ -48,21 +48,6 @@ def with_dicts(): ) -def test_alternate_replacementdict_constructor(): - """ - This constructor allows serialized Python strings to be expanded - into ReplacementDict instances. - """ - - d = {"foo": "bar"} - assert ReplacementDict(d) == ReplacementDict.fromstring(str(d)) - - -def test_alternate_choicesdict_constructor(): - d = {"foo": "bar"} - assert ChoicesDict(d) == ChoicesDict.fromstring(str(d)) - - def test_replacementdict_replace(): d = ReplacementDict({"%PREFIX%": "/usr/local"}) assert d.replace("%PREFIX%/bin/") == ["/usr/local/bin/"] diff --git a/tests/archivematicaCommon/test_elasticsearch_functions.py b/tests/archivematicaCommon/test_elasticsearch_functions.py deleted file mode 100644 index ecda1b5543..0000000000 --- a/tests/archivematicaCommon/test_elasticsearch_functions.py +++ /dev/null @@ -1,1458 +0,0 @@ -import datetime -import os -import pathlib -import uuid -from unittest import mock - -import elasticSearchFunctions -import pytest -from components import helpers -from django.utils.timezone import make_aware -from lxml import etree -from main.models import SIP -from main.models import Directory -from main.models import File -from main.models import Identifier -from main.models import Transfer - -THIS_DIR = os.path.dirname(os.path.abspath(__file__)) - - -@pytest.fixture -def es_client(): - with mock.patch("elasticsearch.transport.Transport.perform_request"): - elasticSearchFunctions.setup("elasticsearch:9200") - return elasticSearchFunctions.get_client() - - -@mock.patch( - "elasticsearch.transport.Transport.perform_request", - side_effect=[ - { - "took": 2, - "timed_out": False, - "_shards": {"total": 5, "successful": 5, "skipped": 0, "failed": 0}, - "hits": { - "total": 1, - "max_score": 0.2876821, - "hits": [ - { - "_index": "aips", - "_type": "_doc", - "_id": "lBsZBWgBn49OAVhMXeO8", - "_score": 0.2876821, - "_source": {"uuid": "b34521a3-1c63-43dd-b901-584416f36c91"}, - } - ], - }, - }, - { - "took": 8, - "timed_out": False, - "total": 1, - "deleted": 1, - "batches": 1, - "version_conflicts": 0, - "noops": 0, - "retries": {"bulk": 0, "search": 0}, - "throttled_millis": 0, - "requests_per_second": -1.0, - "throttled_until_millis": 0, - "failures": [], - }, - { - "took": 0, - "timed_out": False, - "_shards": {"total": 5, "successful": 5, "skipped": 0, "failed": 0}, - "hits": {"total": 0, "max_score": None, "hits": []}, - }, - ], -) -def test_delete_aip(perform_request, es_client): - aip_uuid = "b34521a3-1c63-43dd-b901-584416f36c91" - - # Verify AIP exists - results = es_client.search( - index="aips", - body={"query": {"term": {"uuid": aip_uuid}}}, - _source="uuid", - ) - assert results["hits"]["total"] == 1 - assert results["hits"]["hits"][0]["_source"]["uuid"] == aip_uuid - - # Delete AIP - elasticSearchFunctions.delete_aip(es_client, "b34521a3-1c63-43dd-b901-584416f36c91") - - # Verify AIP gone - results = es_client.search( - index="aips", - body={"query": {"term": {"uuid": aip_uuid}}}, - _source="uuid", - ) - assert results["hits"]["total"] == 0 - - -@mock.patch( - "elasticsearch.transport.Transport.perform_request", - side_effect=[ - { - "took": 1, - "timed_out": False, - "_shards": {"total": 5, "successful": 5, "skipped": 0, "failed": 0}, - "hits": { - "total": 2, - "max_score": 0.2876821, - "hits": [ - { - "_index": "aipfiles", - "_type": "_doc", - "_id": "lRsZBWgBn49OAVhMXuMC", - "_score": 0.2876821, - "_source": {"origin": "1a14043f-68ef-4bfe-a129-e2e4cdbe391b"}, - }, - { - "_index": "aipfiles", - "_type": "_doc", - "_id": "lhsZBWgBn49OAVhMXuMh", - "_score": 0.2876821, - "_source": {"origin": "1a14043f-68ef-4bfe-a129-e2e4cdbe391b"}, - }, - ], - }, - }, - { - "took": 11, - "timed_out": False, - "total": 2, - "deleted": 2, - "batches": 1, - "version_conflicts": 0, - "noops": 0, - "retries": {"bulk": 0, "search": 0}, - "throttled_millis": 0, - "requests_per_second": -1.0, - "throttled_until_millis": 0, - "failures": [], - }, - { - "took": 0, - "timed_out": False, - "_shards": {"total": 5, "successful": 5, "skipped": 0, "failed": 0}, - "hits": {"total": 0, "max_score": None, "hits": []}, - }, - ], -) -def test_delete_aip_files(perform_request, es_client): - aip_uuid = "b34521a3-1c63-43dd-b901-584416f36c91" - - # Verify AIP files exist - results = es_client.search( - index="aipfiles", body={"query": {"term": {"AIPUUID": aip_uuid}}} - ) - assert results["hits"]["total"] == 2 - - # Delete AIP files - elasticSearchFunctions.delete_aip_files(es_client, aip_uuid) - # Verify AIP files gone - results = es_client.search( - index="aipfiles", body={"query": {"term": {"AIPUUID": aip_uuid}}} - ) - assert results["hits"]["total"] == 0 - - assert perform_request.mock_calls == [ - mock.call( - "GET", - "/aipfiles/_search", - params={}, - body={ - "query": {"term": {"AIPUUID": "b34521a3-1c63-43dd-b901-584416f36c91"}} - }, - ), - mock.call( - "POST", - "/aipfiles/_delete_by_query", - params={}, - body={ - "query": {"term": {"AIPUUID": "b34521a3-1c63-43dd-b901-584416f36c91"}} - }, - ), - mock.call( - "GET", - "/aipfiles/_search", - params={}, - body={ - "query": {"term": {"AIPUUID": "b34521a3-1c63-43dd-b901-584416f36c91"}} - }, - ), - ] - - -@mock.patch( - "elasticsearch.transport.Transport.perform_request", - side_effect=[ - { - "took": 1, - "timed_out": False, - "_shards": {"total": 5, "successful": 5, "skipped": 0, "failed": 0}, - "hits": { - "total": 1, - "max_score": 0.6931472, - "hits": [ - { - "_index": "transferfiles", - "_type": "_doc", - "_id": "mBsZBWgBn49OAVhMh-OV", - "_score": 0.6931472, - "_source": { - "accessionid": "", - "status": "backlog", - "sipuuid": "17b168b6-cbba-4f43-8838-a53360238acb", - "tags": [], - "file_extension": "jpg", - "relative_path": "test-17b168b6-cbba-4f43-8838-a53360238acb/objects/Landing_zone.jpg", - "bulk_extractor_reports": [], - "origin": "1a14043f-68ef-4bfe-a129-e2e4cdbe391b", - "size": 1.2982568740844727, - "modification_date": "2018-12-11", - "created": 1546273029.7313669, - "format": [], - "ingestdate": "2018-12-31", - "filename": "Landing_zone.jpg", - "fileuuid": "268421a7-a986-4fa0-95c1-54176e508210", - }, - } - ], - }, - }, - { - "_index": "transferfiles", - "_type": "_doc", - "_id": "mBsZBWgBn49OAVhMh-OV", - "_version": 2, - "result": "updated", - "forced_refresh": True, - "_shards": {"total": 2, "successful": 1, "failed": 0}, - "_seq_no": 2, - "_primary_term": 1, - }, - { - "took": 2, - "timed_out": False, - "_shards": {"total": 5, "successful": 5, "skipped": 0, "failed": 0}, - "hits": { - "total": 1, - "max_score": 0.47000363, - "hits": [ - { - "_index": "transferfiles", - "_type": "_doc", - "_id": "mBsZBWgBn49OAVhMh-OV", - "_score": 0.47000363, - "_source": {"tags": ["test"]}, - } - ], - }, - }, - ], -) -def test_set_get_tags(perform_request, es_client): - file_uuid = "268421a7-a986-4fa0-95c1-54176e508210" - elasticSearchFunctions.set_file_tags(es_client, file_uuid, ["test"]) - assert elasticSearchFunctions.get_file_tags(es_client, file_uuid) == ["test"] - - assert perform_request.mock_calls == [ - mock.call( - "GET", - "/transferfiles/_search", - params={"size": "10000"}, - body={ - "query": {"term": {"fileuuid": "268421a7-a986-4fa0-95c1-54176e508210"}} - }, - ), - mock.call( - "POST", - "/transferfiles/_doc/mBsZBWgBn49OAVhMh-OV/_update", - params={}, - body={"doc": {"tags": ["test"]}}, - ), - mock.call( - "GET", - "/transferfiles/_search", - params={"_source": b"tags"}, - body={ - "query": {"term": {"fileuuid": "268421a7-a986-4fa0-95c1-54176e508210"}} - }, - ), - ] - - -@mock.patch( - "elasticsearch.transport.Transport.perform_request", - side_effect=[ - { - "took": 1, - "timed_out": False, - "_shards": {"total": 5, "successful": 5, "skipped": 0, "failed": 0}, - "hits": {"total": 0, "max_score": None, "hits": []}, - } - ], -) -def test_list_tags_fails_when_file_cant_be_found(perform_request, es_client): - with pytest.raises(elasticSearchFunctions.EmptySearchResultError): - elasticSearchFunctions.get_file_tags(es_client, "no_such_file") - perform_request.assert_called_once_with( - "GET", - "/transferfiles/_search", - params={"_source": b"tags"}, - body={"query": {"term": {"fileuuid": "no_such_file"}}}, - ) - - -@mock.patch( - "elasticsearch.transport.Transport.perform_request", - side_effect=[ - { - "took": 0, - "timed_out": False, - "_shards": {"total": 5, "successful": 5, "skipped": 0, "failed": 0}, - "hits": {"total": 0, "max_score": None, "hits": []}, - } - ], -) -def test_set_tags_fails_when_file_cant_be_found(perform_request, es_client): - with pytest.raises(elasticSearchFunctions.EmptySearchResultError): - elasticSearchFunctions.set_file_tags(es_client, "no_such_file", []) - perform_request.assert_called_once_with( - "GET", - "/transferfiles/_search", - params={"size": "10000"}, - body={"query": {"term": {"fileuuid": "no_such_file"}}}, - ) - - -@pytest.mark.django_db -@mock.patch("elasticSearchFunctions.get_dashboard_uuid") -@mock.patch("elasticSearchFunctions.bulk") -def test_index_mets_file_metadata(bulk, get_dashboard_uuid, es_client): - # Set up mocked functions - get_dashboard_uuid.return_value = "test-uuid" - indexed_data = {} - - def _bulk(client, actions, stats_only=False, *args, **kwargs): - for item in actions: - try: - dmd_section = item["_source"]["METS"]["dmdSec"] - metadata_container = dmd_section["mets:xmlData_dict"] - dc = metadata_container["dcterms:dublincore_dict"] - except (KeyError, IndexError): - dc = None - indexed_data[item["_source"]["filePath"]] = dc - - bulk.side_effect = _bulk - - # This METS file is a cut-down version of the AIP METS produced - # using the SampleTransfers/DemoTransfer - mets_file_path = os.path.join(THIS_DIR, "fixtures", "test_index_metadata-METS.xml") - mets_object_id = "771aa252-7930-4e68-b73e-f91416b1d4a4" - aip_uuid = "f42a260a-9b53-4555-847e-8a4329c81662" - sipName = f"DemoTransfer-{aip_uuid}" - identifiers = [] - elasticSearchFunctions._index_aip_files( - client=es_client, - uuid=aip_uuid, - mets=etree.parse(mets_file_path).getroot(), - name=sipName, - identifiers=identifiers, - ) - - assert bulk.call_count == 1 - - # ES should have indexed 12 files - # - 5 content files - # - 5 checksum and csv files in the metadata directory - # - 2 files generated in the transfer process - assert len(indexed_data) == 12 - - # Metadata should have been indexed only for these content - # files because they are listed in the metadata.csv file - content_files_with_metadata = ( - { - "path": ( - "objects/View_from_lookout_over_Queenstown_" - "towards_the_Remarkables_in_spring.jpg" - ), - "title": ( - "Morning view from lookout over Queenstown " - "towards the Remarkables in spring" - ), - "creator": "Pseudopanax at English Wikipedia", - }, - { - "path": "objects/beihai.tif", - "title": "Beihai, Guanxi, China, 1988", - "creator": ( - "NASA/GSFC/METI/ERSDAC/JAROS and U.S./Japan " "ASTER Science Team" - ), - }, - { - "path": "objects/bird.mp3", - "title": "14000 Caen, France - Bird in my garden", - "creator": "Nicolas Germain", - }, - { - "path": "objects/ocr-image.png", - "title": "OCR image", - "creator": "Tesseract", - }, - ) - for file_metadata in content_files_with_metadata: - dc = indexed_data[file_metadata["path"]] - assert dc["dc:title"] == file_metadata["title"] - assert dc["dc:creator"] == file_metadata["creator"] - - # There is no metadata for this content file because - # it was not listed in the metadata.csv file - assert indexed_data["objects/piiTestDataCreditCardNumbers.txt"] is None - - # Checksum and csv files in the metadata directory - # won't have dublin core metadata indexed - files_in_metadata_directory = ( - "checksum.md5", - "checksum.sha1", - "checksum.sha256", - "metadata.csv", - "rights.csv", - ) - for filename in files_in_metadata_directory: - path = f"objects/metadata/transfers/DemoTransfer-{mets_object_id}/{filename}" - assert indexed_data[path] is None - - # Neither will the generated files during the transfer process - generated_files = ("dc.json", "directory_tree.txt") - for filename in generated_files: - path = f"objects/metadata/transfers/DemoTransfer-{mets_object_id}/{filename}" - assert indexed_data[path] is None - - -@pytest.mark.django_db -@mock.patch("elasticSearchFunctions.bulk") -def test_index_mets_file_metadata_with_utf8(bulk, es_client): - def _bulk(client, actions, stats_only=False, *args, **kwargs): - pass - - bulk.side_effect = _bulk - mets_file_path = os.path.join( - THIS_DIR, "fixtures", "test_index_metadata-METS-utf8.xml" - ) - elasticSearchFunctions._index_aip_files( - client=es_client, - uuid="", - mets=etree.parse(mets_file_path).getroot(), - name="", - identifiers=[], - ) - - -@mock.patch("elasticSearchFunctions.create_indexes_if_needed") -def test_default_setup(create_indexes_if_needed): - elasticSearchFunctions.setup("elasticsearch:9200") - create_indexes_if_needed.assert_called_with( - mock.ANY, ["aips", "aipfiles", "transfers", "transferfiles"] - ) - - -@mock.patch("elasticSearchFunctions.create_indexes_if_needed") -def test_only_aips_setup(create_indexes_if_needed): - elasticSearchFunctions.setup("elasticsearch:9200", enabled=["aips"]) - create_indexes_if_needed.assert_called_with(mock.ANY, ["aips", "aipfiles"]) - - -@mock.patch("elasticSearchFunctions.create_indexes_if_needed") -def test_only_transfers_setup(create_indexes_if_needed): - elasticSearchFunctions.setup("elasticsearch:9200", enabled=["transfers"]) - create_indexes_if_needed.assert_called_with( - mock.ANY, ["transfers", "transferfiles"] - ) - - -@mock.patch("elasticSearchFunctions.create_indexes_if_needed") -def test_no_indexes_setup(create_indexes_if_needed): - elasticSearchFunctions.setup("elasticsearch:9200", enabled=[]) - elasticSearchFunctions.setup("elasticsearch:9200", enabled=["unknown"]) - create_indexes_if_needed.assert_not_called() - - -@mock.patch("elasticsearch.client.indices.IndicesClient.create") -@mock.patch("elasticsearch.client.indices.IndicesClient.exists", return_value=True) -def test_create_indexes_already_created(exists, create, es_client): - elasticSearchFunctions.create_indexes_if_needed( - es_client, ["aips", "aipfiles", "transfers", "transferfiles"] - ) - create.assert_not_called() - - -@mock.patch("elasticsearch.client.indices.IndicesClient.create") -@mock.patch("elasticsearch.client.indices.IndicesClient.exists", return_value=False) -def test_create_indexes_creation_calls(exists, create, es_client): - elasticSearchFunctions.create_indexes_if_needed( - es_client, ["aips", "aipfiles", "transfers", "transferfiles"] - ) - assert create.call_count == 4 - - -@mock.patch("elasticsearch.client.indices.IndicesClient.create") -@mock.patch("elasticsearch.client.indices.IndicesClient.exists", return_value=False) -def test_create_indexes_wrong_index(exists, create, es_client): - elasticSearchFunctions.create_indexes_if_needed( - es_client, ["aips", "aipfiles", "unknown"] - ) - assert create.call_count == 2 - - -fileuuid_premisv3 = ( - { - "filePath": "objects/evelyn_s_photo.jpg", - "FILEUUID": "e9caf37c-93cb-4c37-ab5f-157c7d2611ac", - }, - { - "filePath": "objects/metadata/transfers/evelynphotos-96344c4e-bdaa-4e57-a271-408234de976d/directory_tree.txt", - "FILEUUID": "61e56606-a1d6-456d-9c97-406feaa13b85", - }, -) -fileuuid_premisv2 = ( - { - "filePath": "objects/metadata/transfers/MAPS2015-AM641-44f3ee8e-88fd-424c-9d2b-f35d69b148e1/directory_tree.txt", - "FILEUUID": "44d3aa6d-8bb0-4cfb-bd93-f1121c08916e", - }, - { - "filePath": "objects/LEG1363.01.TIF", - "FILEUUID": "0552c02b-8626-456f-89cc-bc5f3ce8a112", - }, -) -fileuuid_premisv2_no_ns = ( - { - "filePath": "objects/AM68.csv", - "FILEUUID": "fc0e52ca-a688-41c0-a10b-c1d36e21e804", - }, - { - "filePath": "objects/V00154.MPG", - "FILEUUID": "3a6a182a-40a0-4c2b-9752-fc7e91ac1edf", - }, - { - "filePath": "objects/V00158.MPG", - "FILEUUID": "431913ba-4379-4373-8798-cc5f2b9dd769", - }, -) - - -@pytest.mark.django_db -@pytest.mark.parametrize( - "metsfile,fileuuid_dict,aipuuid,aipname", - [ - ( - "test_index_fileuuid_METS_premisv3.xml", - fileuuid_premisv3, - "37abc30b-a258-4389-b1f2-67ccd330bc7e", - "evelynphotos", - ), - ( - "test_index_fileuuid_METS_premisv2.xml", - fileuuid_premisv2, - "9559945a-52e8-4eb4-ac1a-e0e794e758fb", - "MAPS2015-AM641", - ), - ( - "test_index_fileuuid_METS_premisv2_no_ns.xml", - fileuuid_premisv2_no_ns, - "bdcb560d-7ddd-4c13-8040-1e565b4eddff", - "AM68", - ), - ], -) -@mock.patch("elasticSearchFunctions.get_dashboard_uuid") -@mock.patch("elasticSearchFunctions.bulk") -def test_index_aipfile_fileuuid( - bulk, get_dashboard_uuid, metsfile, fileuuid_dict, aipuuid, aipname -): - """Check AIP file uuids are being correctly parsed from METS files. - - Mock _try_to_index() with a function that populates a dict - indexed_data, with the fileuuids that _index_aip_files() obtained - from the METS - """ - - get_dashboard_uuid.return_value = "test-uuid" - - indexed_data = {} - - def _bulk(client, actions, stats_only=False, *args, **kwargs): - for item in actions: - indexed_data[item["_source"]["filePath"]] = item["_source"]["FILEUUID"] - - bulk.side_effect = _bulk - - elasticSearchFunctions._index_aip_files( - client=None, - uuid=aipuuid, - mets=etree.parse(os.path.join(THIS_DIR, "fixtures", metsfile)).getroot(), - name=f"{aipname}-{aipuuid}", - identifiers=[], - ) - - for file_uuid in fileuuid_dict: - assert indexed_data[file_uuid["filePath"]] == file_uuid["FILEUUID"] - - -dmdsec_dconly = { - "filePath": "objects/lion.svg", - "dublincore_dict": { - "dc:language": "English", - "dc:title": "Test Title", - "dc:date": "2019-05-03", - "dc:description": "Test description", - }, -} - - -@pytest.mark.django_db -@pytest.mark.parametrize( - "metsfile,dmdsec_dict", - [ - ("test_index_aipfile_dmdsec_METS_dconly.xml", dmdsec_dconly), - ( - "test_index_aipfile_dmdsec_METS_mixed.xml", - dmdsec_dconly, # non-DC metadata should be ignored without error - ), - ], -) -@mock.patch("elasticSearchFunctions.get_dashboard_uuid") -@mock.patch("elasticSearchFunctions.bulk") -def test_index_aipfile_dmdsec(bulk, get_dashboard_uuid, metsfile, dmdsec_dict): - """Check AIP file dmdSec is correctly parsed from METS files. - - Mock _try_to_index() with a function that populates a dict - indexed_data, with the dmdSec data that _index_aip_files() obtained - from the METS - """ - - get_dashboard_uuid.return_value = "test-uuid" - - indexed_data = {} - - def _bulk(client, actions, stats_only=False, *args, **kwargs): - for item in actions: - try: - dmd_section = item["_source"]["METS"]["dmdSec"] - metadata_container = dmd_section["mets:xmlData_dict"] - dc = metadata_container["dcterms:dublincore_dict"] - except (KeyError, IndexError): - dc = None - indexed_data[item["_source"]["filePath"]] = dc - - bulk.side_effect = _bulk - - elasticSearchFunctions._index_aip_files( - client=None, - uuid="DUMMYUUID", - mets=etree.parse(os.path.join(THIS_DIR, "fixtures", metsfile)).getroot(), - name="{}-{}".format("DUMMYNAME", "DUMMYUUID"), - identifiers=[], - ) - - for key, value in dmdsec_dict["dublincore_dict"].items(): - assert indexed_data[dmdsec_dict["filePath"]][key] == value - - -@pytest.fixture -def sip(db): - sip = SIP.objects.create(uuid="f663fd87-5ce4-4114-886e-4856371cf0d6") - sip.identifiers.add(Identifier.objects.create(value="sip_identifier")) - return sip - - -@pytest.fixture -def directories(db, sip): - # Two directories are created but only one is associated with the SIP - dir1 = Directory.objects.create( - uuid="49fe38a0-c50a-4fdf-9353-04d61057220d", sip=sip - ) - dir1.identifiers.add(Identifier.objects.create(value="dir1")) - dir2 = Directory.objects.create(uuid="58eaa39c-2a0b-47fd-9d81-52fbaa108abc") - dir2.identifiers.add(Identifier.objects.create(value="dir2")) - - -def test_get_sip_identifiers_returns_sip_and_directory_identifiers(sip, directories): - result = elasticSearchFunctions._get_sip_identifiers(sip.uuid) - assert sorted(result) == ["dir1", "sip_identifier"] - - -PHYSICAL_STRUCT_MAP = """ - - - - - - - - - - - - - - - - - - -""" - - -@pytest.fixture -def physical_struct_map(): - return etree.fromstring(PHYSICAL_STRUCT_MAP) - - -def test_get_directories_with_metadata(physical_struct_map): - result = elasticSearchFunctions._get_directories_with_metadata(physical_struct_map) - labels = sorted(directory.attrib["LABEL"] for directory in result) - assert labels == ["Demo-166e916c-0676-4324-8045-bfc628bebcea", "artwork", "objects"] - - -METS = """ - - - - - - UUID - 6cebefad-2b19-4874-b014-094ec35a85aa - - pics_2-6cebefad-2b19-4874-b014-094ec35a85aa - - - - - - - - - Some title - AM - - - - - - - - - - - custom field part 1 - custom field part 2 - custom field 2 - - - - - - - - deleted - - - - - - - - updated custom field part 1 - updated custom field part 2 - updated custom field 2 - - - - - - - - idfield - controlfield 1 - controlfield 2 - - subfield 1 - subfield 2 - - - subfield 3 - - - - - - - - - - - 63140.2 - A. - R. - Chivist - - - - - - -""" - - -@pytest.fixture -def mets(): - return etree.fromstring(METS) - - -@pytest.fixture -def directory(): - result = etree.Element("directory") - result.set("LABEL", "some/path/to/directory") - result.set("DMDID", "dmdSec_1 dmdSec_2 dmdSec_3 dmdSec_4 dmdSec_5 dmdSec_6") - result.set("ADMID", "amdSec_1") - return result - - -@pytest.fixture -def directory_with_no_metadata(): - result = etree.Element("directory") - return result - - -@pytest.fixture -def file_pointer(): - result = etree.Element("file") - result.set("DMDID", "dmdSec_1 dmdSec_2 dmdSec_3 dmdSec_4 dmdSec_5 dmdSec_6") - return result - - -@pytest.fixture -def file_pointer_with_no_metadata(): - result = etree.Element("file") - return result - - -expected_file_metadata = { - "custom_field": ["updated custom field part 1", "updated custom field part 2"], - "custom_field2": "updated custom field 2", - "dc:creator": "AM", - "dc:subject": [None, None, None], - "dc:title": "Some title", - "record_dict": { - "idfield": "idfield", - "controlfield": [ - "controlfield 1", - "controlfield 2", - ], - "datafield_dict": [ - { - "subfield": [ - "subfield 1", - "subfield 2", - ] - }, - { - "subfield": "subfield 3", - }, - ], - }, -} - - -expected_directory_metadata = { - "__DIRECTORY_LABEL__": "some/path/to/directory", - "FIELD_CONTACT_NAME": ["A.", "R.", "Chivist"], - "Payload-Oxum": "63140.2", - **expected_file_metadata, -} - - -@pytest.mark.parametrize( - "element_fixture_name, method_name, expected_metadata", - [ - ("directory", "_get_directory_metadata", expected_directory_metadata), - ("directory_with_no_metadata", "_get_directory_metadata", {}), - ("file_pointer", "_get_file_metadata", expected_file_metadata), - ("file_pointer_with_no_metadata", "_get_file_metadata", {}), - ], -) -def test_get_metadata( - request, mets, element_fixture_name, method_name, expected_metadata -): - assert ( - getattr(elasticSearchFunctions, method_name)( - request.getfixturevalue(element_fixture_name), mets - ) - == expected_metadata - ) - - -def test_index_aip_and_files_logs_error_if_mets_does_not_exist( - es_client, tmp_path, caplog -): - printfn = mock.Mock() - aip_uuid = uuid.uuid4() - aip_stored_path = tmp_path / "aip.7z" - mets_staging_path = tmp_path / "mets.XML" - expected_error_message = f"METS file does not exist at: {mets_staging_path}" - - result = elasticSearchFunctions.index_aip_and_files( - es_client, - str(aip_uuid), - str(aip_stored_path), - str(mets_staging_path), - "aip", - 1024, - printfn=printfn, - ) - assert result == 1 - - assert [r.message for r in caplog.records] == [expected_error_message] - printfn.assert_called_once_with(expected_error_message, file=mock.ANY) - - -@pytest.mark.django_db -@mock.patch("elasticsearch.Elasticsearch.index") -@mock.patch( - "elasticsearch.client.cluster.ClusterClient.health", - return_value={"status": "green"}, -) -@mock.patch("elasticSearchFunctions._index_aip_files") -def test_index_aip_and_files(_index_aip_files, health, index, es_client, tmp_path): - dashboard_uuid = uuid.uuid4() - helpers.set_setting("dashboard_uuid", str(dashboard_uuid)) - printfn = mock.Mock() - aip_name = "aip" - aip_uuid = str(uuid.uuid4()) - aip_stored_path = tmp_path / "aip.7z" - mets_staging_path = pathlib.Path( - THIS_DIR, "fixtures", "test_index_aip_and_files_METS.xml" - ) - expected_file_count = 3 - accession_ids = "accession_ids" - _index_aip_files.return_value = (expected_file_count, accession_ids) - - result = elasticSearchFunctions.index_aip_and_files( - es_client, - str(aip_uuid), - str(aip_stored_path), - str(mets_staging_path), - aip_name, - 1024 * 1024 * 10, - printfn=printfn, - ) - assert result == 0 - - health.assert_called_once() - index.assert_called_once_with( - body={ - "AICID": None, - "accessionids": accession_ids, - "countAIPsinAIC": None, - "created": 1714362668, - "encrypted": False, - "filePath": str(aip_stored_path), - "file_count": expected_file_count, - "identifiers": [], - "isPartOf": "", - "location": "", - "name": aip_name, - "origin": str(dashboard_uuid), - "size": 10.0, - "status": elasticSearchFunctions.STATUS_UPLOADED, - "transferMetadata": [ - { - "__DIRECTORY_LABEL__": "objects", - "dc:title": "Pictures with DublinCore", - "dc:type": "Archival Information Package", - }, - ], - "uuid": str(aip_uuid), - }, - doc_type="_doc", - index="aips", - ) - assert printfn.mock_calls == [ - mock.call(f"AIP UUID: {aip_uuid}"), - mock.call("Indexing AIP files ..."), - mock.call(f"Files indexed: {expected_file_count}"), - mock.call("Indexing AIP ..."), - mock.call("Done."), - ] - - -def test_index_transfer_and_files_logs_error_if_transfer_path_does_not_exist( - es_client, tmp_path, caplog -): - printfn = mock.Mock() - transfer_uuid = uuid.uuid4() - transfer_path = tmp_path / "transfer" - expected_error_message = f"Transfer does not exist at: {transfer_path}" - - result = elasticSearchFunctions.index_transfer_and_files( - es_client, - str(transfer_uuid), - str(transfer_path), - 1024, - printfn=printfn, - ) - assert result == 1 - - assert [r.message for r in caplog.records] == [expected_error_message] - printfn.assert_called_once_with(expected_error_message, file=mock.ANY) - - -@pytest.mark.django_db -@pytest.fixture -def transfer(tmp_path): - transfer_dir = tmp_path / "transfer" - transfer_dir.mkdir() - - (transfer_dir / "processingMCP.xml").touch() - - return Transfer.objects.create( - # The trailing slash is expected by index_transfer_and_files. - currentlocation=f"{transfer_dir}/", - accessionid="accession_id", - ) - - -@pytest.mark.django_db -@pytest.fixture -def transfer_file(transfer): - filename = "file.txt" - (pathlib.Path(transfer.currentlocation) / filename).touch() - - result = File.objects.create( - transfer=transfer, currentlocation=f"%transferDirectory%{filename}".encode() - ) - - # enteredsystem is an auto_now DateTimeField. This resets its value. - dt = make_aware(datetime.datetime(2024, 1, 1)) - result.enteredsystem = dt - result.modificationtime = dt - result.save() - - return result - - -@pytest.mark.django_db -@mock.patch("elasticsearch.Elasticsearch.index") -@mock.patch( - "elasticsearch.client.cluster.ClusterClient.health", - return_value={"status": "green"}, -) -def test_index_transfer_and_files(health, index, es_client, transfer, transfer_file): - dashboard_uuid = uuid.uuid4() - helpers.set_setting("dashboard_uuid", str(dashboard_uuid)) - printfn = mock.Mock() - expected_transfer_name = "transfer" - expected_file_count = 1 - expected_file_name = "file.txt" - expected_date = "2024-01-01" - expected_status = "backlog" - - result = elasticSearchFunctions.index_transfer_and_files( - es_client, - str(transfer.uuid), - str(transfer.currentlocation), - 1024, - printfn=printfn, - ) - assert result == 0 - - assert health.mock_calls == [mock.call(), mock.call()] - assert index.mock_calls == [ - mock.call( - body={ - "filename": expected_file_name, - "relative_path": f"{expected_transfer_name}/{expected_file_name}", - "fileuuid": str(transfer_file.uuid), - "sipuuid": str(transfer.uuid), - "accessionid": transfer.accessionid, - "status": expected_status, - "origin": str(dashboard_uuid), - "ingestdate": expected_date, - "created": mock.ANY, - "modification_date": expected_date, - "size": 0.0, - "tags": [], - "file_extension": "txt", - "bulk_extractor_reports": [], - "format": [], - "pending_deletion": False, - }, - index="transferfiles", - doc_type="_doc", - ), - mock.call( - body={ - "accessionid": transfer.accessionid, - "file_count": expected_file_count, - "ingest_date": expected_date, - "name": expected_transfer_name, - "pending_deletion": False, - "size": 1024, - "status": expected_status, - "uuid": str(transfer.uuid), - }, - doc_type="_doc", - index="transfers", - ), - ] - # Cannot compare using mock_calls here because the use of os.listdir in - # _list_files_in_dir returns files in arbitrary order. - printfn.assert_has_calls( - [ - mock.call(f"Transfer UUID: {transfer.uuid}"), - mock.call("Indexing Transfer files ..."), - mock.call( - f"Indexing {expected_transfer_name}/{expected_file_name} (UUID: {transfer_file.uuid})" - ), - mock.call(f"Skipping indexing {expected_transfer_name}/processingMCP.xml"), - mock.call(f"Files indexed: {expected_file_count}"), - mock.call("Indexing Transfer ..."), - mock.call("Done."), - ], - any_order=True, - ) - - -@mock.patch( - "elasticSearchFunctions.search_all_results", - return_value={ - "hits": { - "hits": [ - { - "_source": { - "filename": "file.txt", - "fileuuid": "f704ab10-d52e-482f-af4a-cc21111f8df4", - }, - } - ], - }, - }, -) -def test_get_transfer_file_info_when_search_returns_a_single_document( - search_all_results, es_client -): - field = "fileuuid" - value = "f704ab10-d52e-482f-af4a-cc21111f8df4" - - result = elasticSearchFunctions.get_transfer_file_info(es_client, field, value) - - assert result == { - "filename": "file.txt", - "fileuuid": "f704ab10-d52e-482f-af4a-cc21111f8df4", - } - - search_all_results.assert_called_once_with( - es_client, - body={"query": {"term": {field: value}}}, - index=elasticSearchFunctions.TRANSFER_FILES_INDEX, - ) - - -@mock.patch( - "elasticSearchFunctions.search_all_results", - return_value={ - "hits": { - "hits": [ - { - "_source": { - "fileuuid": "f704ab10-d52e-482f-af4a-cc21111f8df4", - "filename": "foo.txt", - } - }, - { - "_source": { - "fileuuid": "f704ab10-d52e-482f-af4a-cc21111f8df4", - "filename": "bar.txt", - } - }, - ], - }, - }, -) -def test_get_transfer_file_info_logs_multiple_results(search_all_results, es_client): - field = "fileuuid" - value = "f704ab10-d52e-482f-af4a-cc21111f8df4" - - result = elasticSearchFunctions.get_transfer_file_info(es_client, field, value) - - assert result == {field: value, "filename": "foo.txt"} - - search_all_results.assert_called_once_with( - es_client, - body={"query": {"term": {field: value}}}, - index=elasticSearchFunctions.TRANSFER_FILES_INDEX, - ) - - -@pytest.mark.django_db -@mock.patch("elasticSearchFunctions.get_client") -@mock.patch("elasticSearchFunctions.search_all_results") -def test_remove_backlog_transfer_files(search_all_results, client, transfer): - file_doc_id = "123" - search_all_results.return_value = { - "hits": { - "hits": [ - { - "_id": file_doc_id, - "_source": {"filename": "file.txt", "fileuuid": str(uuid.uuid4())}, - } - ] - } - } - - elasticSearchFunctions.remove_backlog_transfer_files(client, transfer.uuid) - - search_all_results.assert_called_once_with( - client, - body={"query": {"term": {"sipuuid": str(transfer.uuid)}}}, - index=elasticSearchFunctions.TRANSFER_FILES_INDEX, - ) - client.delete.assert_called_once_with( - index=elasticSearchFunctions.TRANSFER_FILES_INDEX, - doc_type=elasticSearchFunctions.DOC_TYPE, - id=file_doc_id, - ) - - -@pytest.mark.django_db -@mock.patch("elasticSearchFunctions.get_client") -@mock.patch("elasticSearchFunctions.search_all_results") -def test_remove_sip_transfer_files(search_all_results, client, transfer, transfer_file): - file_doc_id = "123" - search_all_results.return_value = { - "hits": { - "hits": [ - { - "_id": file_doc_id, - "_source": {"filename": "file.txt", "fileuuid": str(uuid.uuid4())}, - } - ] - } - } - - elasticSearchFunctions.remove_sip_transfer_files(client, transfer.uuid) - - search_all_results.assert_called_once_with( - client, - body={"query": {"term": {"sipuuid": str(transfer.uuid)}}}, - index=elasticSearchFunctions.TRANSFER_FILES_INDEX, - ) - client.delete.assert_called_once_with( - index=elasticSearchFunctions.TRANSFER_FILES_INDEX, - doc_type=elasticSearchFunctions.DOC_TYPE, - id=file_doc_id, - ) - - -@mock.patch("elasticSearchFunctions.get_client") -@mock.patch("elasticSearchFunctions._document_ids_from_field_query", return_value=[]) -def test_mark_aip_stored_logs_error(_document_ids_from_field_query, client, caplog): - aip_uuid = str(uuid.uuid4()) - - elasticSearchFunctions.mark_aip_stored(es_client, aip_uuid) - - _document_ids_from_field_query.assert_called_once_with( - es_client, - elasticSearchFunctions.AIPS_INDEX, - elasticSearchFunctions.ES_FIELD_UUID, - aip_uuid, - ) - assert [r.message for r in caplog.records] == [ - f"Unable to find document with UUID {aip_uuid} in index {elasticSearchFunctions.AIPS_INDEX}" - ] - - -@mock.patch("elasticSearchFunctions.get_client") -@mock.patch("elasticSearchFunctions._document_ids_from_field_query") -def test_mark_aip_stored(_document_ids_from_field_query, client): - aip_uuid = str(uuid.uuid4()) - aip_doc_id = str(uuid.uuid4()) - _document_ids_from_field_query.return_value = [aip_doc_id] - - elasticSearchFunctions.mark_aip_stored(client, aip_uuid) - - client.update.assert_called_once_with( - body={ - "doc": { - elasticSearchFunctions.ES_FIELD_STATUS: elasticSearchFunctions.STATUS_UPLOADED - } - }, - index=elasticSearchFunctions.AIPS_INDEX, - doc_type=elasticSearchFunctions.DOC_TYPE, - id=aip_doc_id, - ) - _document_ids_from_field_query.assert_called_once_with( - client, - elasticSearchFunctions.AIPS_INDEX, - elasticSearchFunctions.ES_FIELD_UUID, - aip_uuid, - ) - - -@pytest.mark.parametrize( - "helper,package_index,files_index,package_uuid_field,field,value", - ( - ( - elasticSearchFunctions.mark_aip_deletion_requested, - elasticSearchFunctions.AIPS_INDEX, - elasticSearchFunctions.AIP_FILES_INDEX, - "AIPUUID", - elasticSearchFunctions.ES_FIELD_STATUS, - elasticSearchFunctions.STATUS_DELETE_REQUESTED, - ), - ( - elasticSearchFunctions.mark_backlog_deletion_requested, - elasticSearchFunctions.TRANSFERS_INDEX, - elasticSearchFunctions.TRANSFER_FILES_INDEX, - "sipuuid", - "pending_deletion", - True, - ), - ( - elasticSearchFunctions.revert_aip_deletion_request, - elasticSearchFunctions.AIPS_INDEX, - elasticSearchFunctions.AIP_FILES_INDEX, - "AIPUUID", - elasticSearchFunctions.ES_FIELD_STATUS, - elasticSearchFunctions.STATUS_UPLOADED, - ), - ( - elasticSearchFunctions.revert_backlog_deletion_request, - elasticSearchFunctions.TRANSFERS_INDEX, - elasticSearchFunctions.TRANSFER_FILES_INDEX, - "sipuuid", - "pending_deletion", - False, - ), - ), - ids=[ - "mark_aip_deletion_requested", - "mark_backlog_deletion_requested", - "revert_aip_deletion_request", - "revert_backlog_deletion_request", - ], -) -@mock.patch("elasticSearchFunctions.get_client") -@mock.patch("elasticSearchFunctions._document_ids_from_field_query") -def test_update_helpers( - _document_ids_from_field_query, - client, - helper, - package_index, - files_index, - package_uuid_field, - field, - value, -): - package_uuid = str(uuid.uuid4()) - package_doc_id = str(uuid.uuid4()) - file_doc_id = str(uuid.uuid4()) - _document_ids_from_field_query.side_effect = [[package_doc_id], [file_doc_id]] - - helper(client, package_uuid) - - assert client.update.mock_calls == [ - mock.call( - body={"doc": {field: value}}, - index=package_index, - doc_type=elasticSearchFunctions.DOC_TYPE, - id=package_doc_id, - ), - mock.call( - body={"doc": {field: value}}, - index=files_index, - doc_type=elasticSearchFunctions.DOC_TYPE, - id=file_doc_id, - ), - ] - assert _document_ids_from_field_query.mock_calls == [ - mock.call( - client, package_index, elasticSearchFunctions.ES_FIELD_UUID, package_uuid - ), - mock.call(client, files_index, package_uuid_field, package_uuid), - ] - - -def test_augment_raw_search_results(): - raw_results = { - "hits": { - "hits": [ - {"_id": "123", "_source": {"filename": "foo.txt"}}, - {"_id": "456", "_source": {"filename": "bar.txt"}}, - ], - }, - } - - result = elasticSearchFunctions.augment_raw_search_results(raw_results) - - assert result == [ - {"document_id": "123", "filename": "foo.txt"}, - {"document_id": "456", "filename": "bar.txt"}, - ] - - -@mock.patch("elasticSearchFunctions.get_client") -def test_try_to_index_fails_with_invalid_maximum_retries(client): - with pytest.raises(ValueError, match="max_tries must be 1 or greater"): - elasticSearchFunctions._try_to_index( - client, {}, elasticSearchFunctions.AIPS_INDEX, max_tries=0 - ) - - -@mock.patch("elasticSearchFunctions.get_client") -def test_try_to_index_retries_after_error(client): - error = Exception("error") - client.index.side_effect = [error, None] - printfn = mock.Mock() - data = {} - index = elasticSearchFunctions.AIPS_INDEX - - elasticSearchFunctions._try_to_index( - client, data, index, wait_between_tries=0, printfn=printfn - ) - - assert client.index.mock_calls == [ - mock.call(body=data, index=index, doc_type=elasticSearchFunctions.DOC_TYPE), - mock.call(body=data, index=index, doc_type=elasticSearchFunctions.DOC_TYPE), - ] - assert printfn.mock_calls == [ - mock.call("ERROR: error trying to index."), - mock.call(error), - ] - - -@mock.patch("elasticSearchFunctions.get_client") -def test_try_to_index_raises_exception_after_retries(client): - error = Exception("error") - client.index.side_effect = [error, None] - printfn = mock.Mock() - data = {} - index = elasticSearchFunctions.AIPS_INDEX - - with pytest.raises(Exception, match="error"): - elasticSearchFunctions._try_to_index( - client, data, index, wait_between_tries=0, max_tries=1, printfn=printfn - ) - - assert client.index.mock_calls == [ - mock.call(body=data, index=index, doc_type=elasticSearchFunctions.DOC_TYPE), - ] - assert printfn.mock_calls == [ - mock.call("ERROR: error trying to index."), - mock.call(error), - ] diff --git a/tests/archivematicaCommon/test_env_configparser.py b/tests/archivematicaCommon/test_env_configparser.py index 287026f167..5bd71f84aa 100644 --- a/tests/archivematicaCommon/test_env_configparser.py +++ b/tests/archivematicaCommon/test_env_configparser.py @@ -4,7 +4,8 @@ import pytest from django.test import TestCase -from env_configparser import EnvConfigParser + +from archivematica.archivematicaCommon.env_configparser import EnvConfigParser class TestConfigReader(TestCase): diff --git a/tests/archivematicaCommon/test_execute_functions.py b/tests/archivematicaCommon/test_execute_functions.py index 0f1fa41b91..fe408e9706 100644 --- a/tests/archivematicaCommon/test_execute_functions.py +++ b/tests/archivematicaCommon/test_execute_functions.py @@ -1,14 +1,15 @@ import pathlib import shlex import tempfile -from typing import Generator +from collections.abc import Generator from unittest.mock import ANY from unittest.mock import Mock from unittest.mock import patch -import executeOrRunSubProcess as execsub import pytest +import archivematica.archivematicaCommon.executeOrRunSubProcess as execsub + def test_capture_output() -> None: """Tests behaviour of capture_output when executing sub processes.""" @@ -75,7 +76,7 @@ def temp_path(tmp_path: pathlib.Path) -> Generator[str, None, None]: tempfile.tempdir = original_tempdir -@patch("executeOrRunSubProcess.launchSubProcess") +@patch("archivematica.archivematicaCommon.executeOrRunSubProcess.launchSubProcess") def test_createAndRunScript_creates_tmpfile_in_custom_dir( launchSubProcess: Mock, temp_path: str ) -> None: diff --git a/tests/archivematicaCommon/test_file_operations.py b/tests/archivematicaCommon/test_file_operations.py index b3b0d3770c..820cd22385 100644 --- a/tests/archivematicaCommon/test_file_operations.py +++ b/tests/archivematicaCommon/test_file_operations.py @@ -3,14 +3,17 @@ import pytest from django.db.models import Q -from fileOperations import FindFileInNormalizatonCSVError -from fileOperations import addAccessionEvent -from fileOperations import findFileInNormalizationCSV -from fileOperations import get_extract_dir_name -from main.models import SIP -from main.models import Event -from main.models import File -from main.models import Transfer + +from archivematica.archivematicaCommon.fileOperations import ( + FindFileInNormalizatonCSVError, +) +from archivematica.archivematicaCommon.fileOperations import addAccessionEvent +from archivematica.archivematicaCommon.fileOperations import findFileInNormalizationCSV +from archivematica.archivematicaCommon.fileOperations import get_extract_dir_name +from archivematica.dashboard.main.models import SIP +from archivematica.dashboard.main.models import Event +from archivematica.dashboard.main.models import File +from archivematica.dashboard.main.models import Transfer @pytest.mark.parametrize( diff --git a/tests/archivematicaCommon/test_gearman_encoder.py b/tests/archivematicaCommon/test_gearman_encoder.py index 6a758ff226..694628c9e2 100644 --- a/tests/archivematicaCommon/test_gearman_encoder.py +++ b/tests/archivematicaCommon/test_gearman_encoder.py @@ -1,7 +1,8 @@ from datetime import datetime from django.utils.timezone import make_aware -from gearman_encoder import JSONDataEncoder + +from archivematica.archivematicaCommon.gearman_encoder import JSONDataEncoder def test_encoder(): diff --git a/tests/archivematicaCommon/test_identifier_functions.py b/tests/archivematicaCommon/test_identifier_functions.py index ac0183772b..af8140b0a8 100644 --- a/tests/archivematicaCommon/test_identifier_functions.py +++ b/tests/archivematicaCommon/test_identifier_functions.py @@ -1,7 +1,7 @@ import os import unittest -import identifier_functions +from archivematica.archivematicaCommon import identifier_functions THIS_DIR = os.path.dirname(os.path.abspath(__file__)) FIXTURES_DIR = os.path.join(THIS_DIR, "fixtures") diff --git a/tests/archivematicaCommon/test_storage_service.py b/tests/archivematicaCommon/test_storage_service.py index 44eba5e4c5..468038b84c 100644 --- a/tests/archivematicaCommon/test_storage_service.py +++ b/tests/archivematicaCommon/test_storage_service.py @@ -8,8 +8,13 @@ import pytest from requests import Response -from storageService import location_description_from_slug -from storageService import retrieve_storage_location_description + +from archivematica.archivematicaCommon.storageService import ( + location_description_from_slug, +) +from archivematica.archivematicaCommon.storageService import ( + retrieve_storage_location_description, +) def mock_response(status_code, content_type, content): @@ -33,7 +38,7 @@ def mock_response(status_code, content_type, content): (0, "", {}), ], ) -@mock.patch("storageService._storage_service_url") +@mock.patch("archivematica.archivematicaCommon.storageService._storage_service_url") @mock.patch("requests.Session.get") def test_location_desc_from_slug( get, _storage_service_url, status_code, content_type, expected_result @@ -76,7 +81,9 @@ def test_location_desc_from_slug( ("/api/v2/location/fd46760b-567f-4c17-a2f4-a05e79074932/", {}, ""), ], ) -@mock.patch("storageService.location_description_from_slug") +@mock.patch( + "archivematica.archivematicaCommon.storageService.location_description_from_slug" +) def test_retrieve_storage_location( location_description_from_slug, slug, return_value, expected_result ): diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000000..9baa83fb43 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,13 @@ +import uuid + +import pytest + +from archivematica.dashboard.components import helpers + + +@pytest.fixture +def dashboard_uuid(db: None) -> uuid.UUID: + result = uuid.uuid4() + helpers.set_setting("dashboard_uuid", str(result)) + + return result diff --git a/tests/dashboard/components/accounts/test_forms.py b/tests/dashboard/components/accounts/test_forms.py new file mode 100644 index 0000000000..204d2e0e4b --- /dev/null +++ b/tests/dashboard/components/accounts/test_forms.py @@ -0,0 +1,87 @@ +import pytest +from django.contrib.auth.models import User + +from archivematica.dashboard.components.accounts.forms import UserChangeForm + + +@pytest.mark.django_db +def test_current_password_is_not_required_when_not_changing_password( + admin_user: User, +) -> None: + # Create a user to edit. + user_to_edit = User.objects.create_user(username="testuser", password="oldpass") + + # Form data without passwords. + form_data = { + "username": "testuser", + "first_name": "Test", + "last_name": "User", + "email": "test@example.com", + "is_active": True, + "is_superuser": False, + } + + form = UserChangeForm( + data=form_data, instance=user_to_edit, requesting_user=admin_user + ) + + # Form should be valid even if current_password is not provided. + assert form.is_valid() + + +@pytest.mark.django_db +def test_current_password_is_required_when_changing_password(admin_user: User) -> None: + # Create a user to edit. + user_to_edit = User.objects.create_user(username="testuser", password="oldpass") + + # Form data with password change request but missing current_password. + form_data = { + "username": "testuser", + "first_name": "Test", + "last_name": "User", + "email": "test@example.com", + "is_active": True, + "is_superuser": False, + "password": "newpass123", + "password_confirmation": "newpass123", + } + + form = UserChangeForm( + data=form_data, instance=user_to_edit, requesting_user=admin_user + ) + + # Form should be invalid due to missing current_password. + assert not form.is_valid() + assert form.errors["current_password"] == [ + "Your current password is required when setting a new password." + ] + + +@pytest.mark.django_db +def test_current_password_is_required_when_only_password_confirmation_provided( + admin_user: User, +) -> None: + # Create a user to edit. + user_to_edit = User.objects.create_user(username="testuser", password="oldpass") + + # Form data with only password_confirmation but missing current_password. + form_data = { + "username": "testuser", + "first_name": "Test", + "last_name": "User", + "email": "test@example.com", + "is_active": True, + "is_superuser": False, + "password": "", # Empty password + "password_confirmation": "newpass123", + } + + form = UserChangeForm( + data=form_data, instance=user_to_edit, requesting_user=admin_user + ) + + # Form should be invalid due to missing current_password. + assert not form.is_valid() + assert form.errors["current_password"] == [ + "Your current password is required when setting a new password." + ] diff --git a/tests/dashboard/components/accounts/test_views.py b/tests/dashboard/components/accounts/test_views.py index 8b42a678dc..0f2dfdfb7f 100644 --- a/tests/dashboard/components/accounts/test_views.py +++ b/tests/dashboard/components/accounts/test_views.py @@ -1,21 +1,20 @@ import hmac import uuid from hashlib import sha1 -from typing import Type from unittest import mock from urllib.parse import parse_qs from urllib.parse import urlparse import pytest import pytest_django -from components import helpers -from components.accounts.views import get_oidc_logout_url from django.contrib.auth.models import User from django.test import Client from django.test import RequestFactory from django.urls import reverse from tastypie.models import ApiKey +from archivematica.dashboard.components.accounts.views import get_oidc_logout_url + def test_get_oidc_logout_url_fails_if_token_is_not_set(rf: RequestFactory) -> None: request = rf.get("/") @@ -55,12 +54,7 @@ def test_get_oidc_logout_url_returns_logout_url( @pytest.fixture -def dashboard_uuid() -> None: - helpers.set_setting("dashboard_uuid", str(uuid.uuid4())) - - -@pytest.fixture -def non_administrative_user(django_user_model: Type[User]) -> User: +def non_administrative_user(django_user_model: type[User]) -> User: return django_user_model.objects.create_user( username="test", password="test", @@ -72,7 +66,7 @@ def non_administrative_user(django_user_model: Type[User]) -> User: @pytest.mark.django_db def test_edit_user_view_denies_access_to_non_admin_users_editing_others( - dashboard_uuid: None, + dashboard_uuid: uuid.UUID, non_administrative_user: User, admin_user: User, client: Client, @@ -96,11 +90,13 @@ def non_administrative_user_apikey(non_administrative_user: User) -> ApiKey: @pytest.mark.django_db def test_edit_user_view_renders_user_profile_fields( - dashboard_uuid: None, + dashboard_uuid: uuid.UUID, non_administrative_user: User, non_administrative_user_apikey: ApiKey, admin_client: Client, ) -> None: + expected_apikey = non_administrative_user_apikey.key + response = admin_client.get( reverse("accounts:edit", kwargs={"id": non_administrative_user.id}), follow=True ) @@ -112,15 +108,19 @@ def test_edit_user_view_renders_user_profile_fields( assert f'name="first_name" value="{non_administrative_user.first_name}"' in content assert f'name="last_name" value="{non_administrative_user.last_name}"' in content assert f'name="email" value="{non_administrative_user.email}"' in content - assert f"{non_administrative_user_apikey.key}" in content + + non_administrative_user_apikey.refresh_from_db() + assert non_administrative_user_apikey.key == expected_apikey @pytest.mark.django_db def test_edit_user_view_updates_user_profile_fields( - dashboard_uuid: None, + dashboard_uuid: uuid.UUID, non_administrative_user: User, admin_client: Client, + admin_user: User, ) -> None: + current_password = "currentpassword" new_username = "newusername" new_password = "newpassword" new_first_name = "bar" @@ -130,10 +130,14 @@ def test_edit_user_view_updates_user_profile_fields( "username": new_username, "password": new_password, "password_confirmation": new_password, + "current_password": current_password, "first_name": new_first_name, "last_name": new_last_name, "email": new_email, } + admin_user.set_password(current_password) + admin_user.save() + admin_client.force_login(admin_user) response = admin_client.post( reverse("accounts:edit", kwargs={"id": non_administrative_user.id}), @@ -143,21 +147,21 @@ def test_edit_user_view_updates_user_profile_fields( assert response.status_code == 200 content = response.content.decode() + non_administrative_user.refresh_from_db() + assert "Saved" in content - assert ( - f'{new_username}' - in content - ) - assert f"{new_first_name} {new_last_name}" in content - assert f"{new_email}" in content + assert f"Edit user {non_administrative_user.username}" in content + assert f'name="username" value="{non_administrative_user.username}"' in content + assert f'name="first_name" value="{non_administrative_user.first_name}"' in content + assert f'name="last_name" value="{non_administrative_user.last_name}"' in content + assert f'name="email" value="{non_administrative_user.email}"' in content - non_administrative_user.refresh_from_db() assert non_administrative_user.check_password(new_password) @pytest.mark.django_db def test_edit_user_view_regenerates_api_key( - dashboard_uuid: None, + dashboard_uuid: uuid.UUID, non_administrative_user: User, non_administrative_user_apikey: ApiKey, admin_client: Client, @@ -180,7 +184,10 @@ def test_edit_user_view_regenerates_api_key( ) assert response.status_code == 200 - assert "Saved" in response.content.decode() + content = response.content.decode() + assert "Saved" in content + assert "Make sure to copy the API key now as you will not be able to see it again." + assert f'value="{expected_key}"' in content non_administrative_user_apikey.refresh_from_db() assert non_administrative_user_apikey.key == expected_key @@ -188,7 +195,7 @@ def test_edit_user_view_regenerates_api_key( @pytest.mark.django_db def test_user_profile_view_allows_users_to_edit_their_profile_fields( - dashboard_uuid: None, + dashboard_uuid: uuid.UUID, non_administrative_user: User, non_administrative_user_apikey: ApiKey, client: Client, @@ -196,6 +203,7 @@ def test_user_profile_view_allows_users_to_edit_their_profile_fields( ) -> None: settings.ALLOW_USER_EDITS = True client.force_login(non_administrative_user) + expected_apikey = non_administrative_user_apikey.key response = client.get( reverse("accounts:profile"), @@ -209,12 +217,14 @@ def test_user_profile_view_allows_users_to_edit_their_profile_fields( assert f'name="first_name" value="{non_administrative_user.first_name}"' in content assert f'name="last_name" value="{non_administrative_user.last_name}"' in content assert f'name="email" value="{non_administrative_user.email}"' in content - assert f"{non_administrative_user_apikey.key}" in content + + non_administrative_user_apikey.refresh_from_db() + assert non_administrative_user_apikey.key == expected_apikey @pytest.mark.django_db def test_user_profile_view_denies_editing_profile_fields_if_setting_disables_it( - dashboard_uuid: None, + dashboard_uuid: uuid.UUID, non_administrative_user: User, non_administrative_user_apikey: ApiKey, client: Client, @@ -222,6 +232,7 @@ def test_user_profile_view_denies_editing_profile_fields_if_setting_disables_it( ) -> None: settings.ALLOW_USER_EDITS = False client.force_login(non_administrative_user) + expected_apikey = non_administrative_user_apikey.key response = client.get( reverse("accounts:profile"), @@ -238,14 +249,16 @@ def test_user_profile_view_denies_editing_profile_fields_if_setting_disables_it( ) assert f"
{non_administrative_user.email}
" in content assert ( - f'
{"yes" if non_administrative_user.is_superuser else "no"}
' in content + f"
{'yes' if non_administrative_user.is_superuser else 'no'}
" in content ) - assert f"{non_administrative_user_apikey.key}" in content + + non_administrative_user_apikey.refresh_from_db() + assert non_administrative_user_apikey.key == expected_apikey @pytest.mark.django_db def test_user_profile_view_regenerates_api_key_if_setting_disables_editing( - dashboard_uuid: None, + dashboard_uuid: uuid.UUID, non_administrative_user: User, non_administrative_user_apikey: ApiKey, client: Client, @@ -274,14 +287,16 @@ def test_user_profile_view_regenerates_api_key_if_setting_disables_editing( ) assert f"
{non_administrative_user.email}
" in content assert ( - f'
{"yes" if non_administrative_user.is_superuser else "no"}
' in content + f"
{'yes' if non_administrative_user.is_superuser else 'no'}
" in content ) - assert f"{expected_key}" in content + + non_administrative_user_apikey.refresh_from_db() + assert non_administrative_user_apikey.key == expected_key @pytest.mark.django_db def test_user_profile_view_does_not_regenerate_api_key_if_not_requested( - dashboard_uuid: None, + dashboard_uuid: uuid.UUID, non_administrative_user: User, non_administrative_user_apikey: ApiKey, client: Client, @@ -289,6 +304,7 @@ def test_user_profile_view_does_not_regenerate_api_key_if_not_requested( ) -> None: settings.ALLOW_USER_EDITS = False client.force_login(non_administrative_user) + expected_apikey = non_administrative_user_apikey.key response = client.post(reverse("accounts:profile"), {}, follow=True) assert response.status_code == 200 @@ -302,6 +318,211 @@ def test_user_profile_view_does_not_regenerate_api_key_if_not_requested( ) assert f"
{non_administrative_user.email}
" in content assert ( - f'
{"yes" if non_administrative_user.is_superuser else "no"}
' in content + f"
{'yes' if non_administrative_user.is_superuser else 'no'}
" in content + ) + + non_administrative_user_apikey.refresh_from_db() + assert non_administrative_user_apikey.key == expected_apikey + + +@pytest.mark.django_db +def test_user_successfully_changes_own_password( + dashboard_uuid: uuid.UUID, + non_administrative_user: User, + client: Client, + settings: pytest_django.fixtures.SettingsWrapper, +) -> None: + """Test that a user can successfully change their own password with correct current_password.""" + settings.ALLOW_USER_EDITS = True + + # Set a known password for the user + old_password = "oldpass123" + non_administrative_user.set_password(old_password) + non_administrative_user.save() + + # Log in with the known password + client.force_login(non_administrative_user) + + # Data to change the user's password + new_password = "newpass456" + data = { + "username": non_administrative_user.username, + "first_name": non_administrative_user.first_name, + "last_name": non_administrative_user.last_name, + "email": non_administrative_user.email, + "current_password": old_password, # Correct current password + "password": new_password, + "password_confirmation": new_password, + } + + response = client.post( + reverse("accounts:profile"), + data, + follow=False, # Don't follow redirects to see the direct response + ) + + # Check if the form was processed correctly + # It might redirect to login or show success message + assert response.status_code in [200, 302] # Success or redirect + + # Verify password was changed regardless of UI behavior + non_administrative_user.refresh_from_db() + assert non_administrative_user.check_password(new_password) + assert not non_administrative_user.check_password(old_password) + + +@pytest.mark.django_db +def test_user_fails_to_change_password_with_incorrect_current_password( + dashboard_uuid: uuid.UUID, + non_administrative_user: User, + client: Client, + settings: pytest_django.fixtures.SettingsWrapper, +) -> None: + """Test that a user fails to change their password with incorrect current_password.""" + settings.ALLOW_USER_EDITS = True + + # Set a known password for the user + old_password = "oldpass123" + non_administrative_user.set_password(old_password) + non_administrative_user.save() + + # Log in with the known password + client.force_login(non_administrative_user) + + # Data to change the user's password with WRONG current password + new_password = "newpass456" + data = { + "username": non_administrative_user.username, + "first_name": non_administrative_user.first_name, + "last_name": non_administrative_user.last_name, + "email": non_administrative_user.email, + "current_password": "wrongpass", # Incorrect current password + "password": new_password, + "password_confirmation": new_password, + } + + response = client.post( + reverse("accounts:profile"), + data, + follow=True, + ) + assert response.status_code == 200 + + content = response.content.decode() + # Should show error message about incorrect current password + assert "Your current password is incorrect." in content + + # Verify password was NOT changed + non_administrative_user.refresh_from_db() + assert non_administrative_user.check_password(old_password) # Still old password + assert not non_administrative_user.check_password( + new_password + ) # New password not set + + +@pytest.mark.django_db +def test_admin_successfully_changes_another_users_password( + dashboard_uuid: uuid.UUID, + non_administrative_user: User, + admin_user: User, + admin_client: Client, +) -> None: + """Test that an admin can successfully change another user's password with admin's current_password.""" + # Set passwords for both users + admin_password = "adminpass123" + user_old_password = "useroldpass" + + admin_user.set_password(admin_password) + admin_user.save() + non_administrative_user.set_password(user_old_password) + non_administrative_user.save() + + # Log in as admin + admin_client.force_login(admin_user) + + # Data to change the other user's password using admin's current password + user_new_password = "usernewpass456" + data = { + "username": non_administrative_user.username, + "first_name": non_administrative_user.first_name, + "last_name": non_administrative_user.last_name, + "email": non_administrative_user.email, + "current_password": admin_password, # Admin's password (not the user's) + "password": user_new_password, + "password_confirmation": user_new_password, + } + + response = admin_client.post( + reverse("accounts:edit", kwargs={"id": non_administrative_user.id}), + data, + follow=False, ) - assert f"{non_administrative_user_apikey.key}" in content + + # Check if the form was processed correctly + assert response.status_code in [200, 302] # Success or redirect + + # Verify the user's password was changed + non_administrative_user.refresh_from_db() + assert non_administrative_user.check_password(user_new_password) + assert not non_administrative_user.check_password(user_old_password) + + # Verify admin password is unchanged + admin_user.refresh_from_db() + assert admin_user.check_password(admin_password) + + +@pytest.mark.django_db +def test_admin_fails_to_change_another_users_password_with_incorrect_admin_password( + dashboard_uuid: uuid.UUID, + non_administrative_user: User, + admin_user: User, + admin_client: Client, +) -> None: + """Test that an admin fails to change another user's password with incorrect admin password.""" + # Set passwords for both users + admin_password = "adminpass123" + user_old_password = "useroldpass" + + admin_user.set_password(admin_password) + admin_user.save() + non_administrative_user.set_password(user_old_password) + non_administrative_user.save() + + # Log in as admin + admin_client.force_login(admin_user) + + # Data to change the other user's password using WRONG admin password + user_new_password = "usernewpass456" + data = { + "username": non_administrative_user.username, + "first_name": non_administrative_user.first_name, + "last_name": non_administrative_user.last_name, + "email": non_administrative_user.email, + "current_password": "wrongadminpass", # Wrong admin password + "password": user_new_password, + "password_confirmation": user_new_password, + } + + response = admin_client.post( + reverse("accounts:edit", kwargs={"id": non_administrative_user.id}), + data, + follow=True, + ) + assert response.status_code == 200 + + content = response.content.decode() + # Should show error message about incorrect current password + assert "Your current password is incorrect." in content + + # Verify the user's password was NOT changed + non_administrative_user.refresh_from_db() + assert non_administrative_user.check_password( + user_old_password + ) # Still old password + assert not non_administrative_user.check_password( + user_new_password + ) # New password not set + + # Verify admin password is unchanged + admin_user.refresh_from_db() + assert admin_user.check_password(admin_password) diff --git a/tests/dashboard/components/administration/test_administration.py b/tests/dashboard/components/administration/test_administration.py index 7778b1913c..e0d4958412 100644 --- a/tests/dashboard/components/administration/test_administration.py +++ b/tests/dashboard/components/administration/test_administration.py @@ -4,26 +4,18 @@ import pytest import pytest_django import requests -from components import helpers from django.contrib.auth.models import User from django.http import HttpResponseNotFound from django.test import Client from django.urls import reverse -from main.models import Report from tastypie.models import ApiKey - -@pytest.fixture -@pytest.mark.django_db -def dashboard_uuid() -> str: - value = str(uuid.uuid4()) - helpers.set_setting("dashboard_uuid", value) - - return value +from archivematica.dashboard.components import helpers +from archivematica.dashboard.main.models import Report @pytest.mark.django_db -def test_admin_set_language(dashboard_uuid: str, admin_client: Client) -> None: +def test_admin_set_language(dashboard_uuid: uuid.UUID, admin_client: Client) -> None: response = admin_client.get(reverse("administration:admin_set_language")) assert response.status_code == 200 @@ -33,7 +25,7 @@ def test_admin_set_language(dashboard_uuid: str, admin_client: Client) -> None: @pytest.mark.django_db -def test_failure_report_delete(dashboard_uuid: str, admin_client: Client) -> None: +def test_failure_report_delete(dashboard_uuid: uuid.UUID, admin_client: Client) -> None: report = Report.objects.create(content="my report") response = admin_client.post( @@ -48,7 +40,7 @@ def test_failure_report_delete(dashboard_uuid: str, admin_client: Client) -> Non @pytest.mark.django_db -def test_failure_report(dashboard_uuid: str, admin_client: Client) -> None: +def test_failure_report(dashboard_uuid: uuid.UUID, admin_client: Client) -> None: report = Report.objects.create(content="my report") response = admin_client.get(reverse("administration:reports_failures_index")) @@ -106,7 +98,7 @@ def checksum_type() -> str: ) def test_general_view_renders_initial_dashboard_settings( get: mock.Mock, - dashboard_uuid: str, + dashboard_uuid: uuid.UUID, site_url: str, storage_service_url: str, storage_service_user: str, @@ -126,10 +118,6 @@ def test_general_view_renders_initial_dashboard_settings( assert ( f'name="storage-storage_service_user" value="{storage_service_user}"' in content ) - assert ( - f'name="storage-storage_service_apikey" value="{storage_service_apikey}"' - in content - ) assert f'