Merge branch 'master' into merge-requests/37

This commit is contained in:
Gregor Kleen 2021-05-10 10:16:01 +02:00
commit 330a2fd974
62 changed files with 817 additions and 350 deletions

2
.envrc Normal file
View File

@ -0,0 +1,2 @@
use flake
dotenv_if_exists .develop.env

4
.gitignore vendored
View File

@ -44,4 +44,6 @@ tunnel.log
/sessions /sessions
/changelog.json /changelog.json
/.current-version /.current-version
/.current-changelog.md /.current-changelog.md
**/.direnv
.develop.env

View File

@ -7,14 +7,6 @@ workflow:
default: default:
image: image:
name: fpco/stack-build:lts-16.31 name: fpco/stack-build:lts-16.31
cache: &global_cache
key: default
paths:
- .npm
- node_modules
- .stack
- .stack-work
- .well-known-cache
variables: variables:
STACK_ROOT: "${CI_PROJECT_DIR}/.stack" STACK_ROOT: "${CI_PROJECT_DIR}/.stack"
@ -43,6 +35,13 @@ stages:
# - deploy # - deploy
npm install: npm install:
cache:
- &npm-cache
key: default-npm
paths:
- .npm
- node_modules
stage: setup stage: setup
script: script:
- ./.npmrc.gup - ./.npmrc.gup
@ -69,6 +68,12 @@ npm install:
interruptible: true interruptible: true
frontend:build: frontend:build:
cache:
- &frontend-cache
key: default-frontend
paths:
- .well-known-cache
stage: frontend:build stage: frontend:build
script: script:
- npm run frontend:build - npm run frontend:build
@ -98,6 +103,13 @@ frontend:lint:
interruptible: true interruptible: true
yesod:build:dev: yesod:build:dev:
cache:
- &stack-dev-cache
key: default-stack-dev
paths:
- .stack
- .stack-work
stage: yesod:build stage: yesod:build
script: script:
- stack build --test --copy-bins --local-bin-path $(pwd)/bin --fast --flag uniworx:-library-only --flag uniworx:dev --flag uniworx:pedantic --no-strip --no-run-tests - stack build --test --copy-bins --local-bin-path $(pwd)/bin --fast --flag uniworx:-library-only --flag uniworx:dev --flag uniworx:pedantic --no-strip --no-run-tests
@ -124,7 +136,7 @@ yesod:build:dev:
paths: paths:
- bin/ - bin/
name: "${CI_JOB_NAME}-${CI_COMMIT_SHORT_SHA}" name: "${CI_JOB_NAME}-${CI_COMMIT_SHORT_SHA}"
expire_in: "1 week" expire_in: "1 day"
rules: rules:
- if: $CI_COMMIT_REF_NAME =~ /(^v[0-9].*)|((^|\/)profile($|\/))/ - if: $CI_COMMIT_REF_NAME =~ /(^v[0-9].*)|((^|\/)profile($|\/))/
@ -136,6 +148,13 @@ yesod:build:dev:
interruptible: true interruptible: true
yesod:build: yesod:build:
cache:
- &stack-cache
key: default-stack
paths:
- .stack
- .stack-work
stage: yesod:build stage: yesod:build
script: script:
- stack build --test --copy-bins --local-bin-path $(pwd)/bin --flag uniworx:-library-only --flag uniworx:-dev --flag uniworx:pedantic --no-strip --no-run-tests - stack build --test --copy-bins --local-bin-path $(pwd)/bin --flag uniworx:-library-only --flag uniworx:-dev --flag uniworx:pedantic --no-strip --no-run-tests
@ -151,7 +170,7 @@ yesod:build:
paths: paths:
- bin/ - bin/
name: "${CI_JOB_NAME}-${CI_COMMIT_SHORT_SHA}" name: "${CI_JOB_NAME}-${CI_COMMIT_SHORT_SHA}"
expire_in: "1 week" expire_in: "1 day"
rules: rules:
- if: $CI_COMMIT_REF_NAME =~ /^v[0-9].*/ - if: $CI_COMMIT_REF_NAME =~ /^v[0-9].*/
@ -165,8 +184,11 @@ yesod:build:
yesod:build:profile: yesod:build:profile:
cache: cache:
<<: *global_cache - &stack-profile-cache
policy: pull key: default-stack-profile
paths:
- .stack
- .stack-work
stage: yesod:build stage: yesod:build
script: script:
@ -181,7 +203,7 @@ yesod:build:profile:
paths: paths:
- bin/ - bin/
name: "${CI_JOB_NAME}-${CI_COMMIT_SHORT_SHA}" name: "${CI_JOB_NAME}-${CI_COMMIT_SHORT_SHA}"
expire_in: "1 week" expire_in: "1 day"
rules: rules:
- if: $CI_COMMIT_REF_NAME =~ /(^|\/)profile($|\/)/ - if: $CI_COMMIT_REF_NAME =~ /(^|\/)profile($|\/)/
@ -195,7 +217,6 @@ yesod:build:profile:
yesod:test:yesod: yesod:test:yesod:
stage: test stage: test
cache: {}
services: &test-services services: &test-services
- name: postgres:10.10 - name: postgres:10.10
@ -224,7 +245,6 @@ yesod:test:yesod:
yesod:test:yesod:dev: yesod:test:yesod:dev:
stage: test stage: test
cache: {}
services: *test-services services: *test-services
@ -271,14 +291,13 @@ yesod:test:hlint:
before_script: *haskell before_script: *haskell
script: script:
- stack install hlint - stack install hlint
- stack exec -- hlint --cc src > gl-code-quality-report.json - stack exec -- hlint --cc src > gl-code-quality-report.json || jq . gl-code-quality-report.json
- jq . gl-code-quality-report.json
artifacts: artifacts:
paths: paths:
- gl-code-quality-report.json - gl-code-quality-report.json
name: "${CI_JOB_NAME}-${CI_COMMIT_SHORT_SHA}" name: "${CI_JOB_NAME}-${CI_COMMIT_SHORT_SHA}"
expire_in: "1 week" expire_in: "1 day"
reports: reports:
codequality: gl-code-quality-report.json codequality: gl-code-quality-report.json
@ -307,14 +326,13 @@ yesod:test:hlint:dev:
before_script: *haskell before_script: *haskell
script: script:
- stack install hlint - stack install hlint
- stack exec -- hlint --cc src > gl-code-quality-report.json - stack exec -- hlint --cc src > gl-code-quality-report.json || jq . gl-code-quality-report.json
- jq . gl-code-quality-report.json
artifacts: artifacts:
paths: paths:
- gl-code-quality-report.json - gl-code-quality-report.json
name: "${CI_JOB_NAME}-${CI_COMMIT_SHORT_SHA}" name: "${CI_JOB_NAME}-${CI_COMMIT_SHORT_SHA}"
expire_in: "1 week" expire_in: "1 day"
reports: reports:
codequality: gl-code-quality-report.json codequality: gl-code-quality-report.json
@ -323,9 +341,6 @@ yesod:test:hlint:dev:
interruptible: true interruptible: true
frontend:test: frontend:test:
cache:
<<: *global_cache
policy: pull
stage: test stage: test
script: script:
- npm run frontend:test - npm run frontend:test
@ -346,7 +361,6 @@ frontend:test:
interruptible: true interruptible: true
parse-changelog: parse-changelog:
cache: {}
stage: prepare release stage: prepare release
needs: needs:
- job: npm install - job: npm install
@ -375,7 +389,6 @@ parse-changelog:
interruptible: true interruptible: true
upload: upload:
cache: {}
variables: variables:
GIT_STRATEGY: none GIT_STRATEGY: none
stage: upload packages stage: upload packages
@ -413,7 +426,6 @@ upload:
curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file bin/uniworx-wflint ${PACKAGE_REGISTRY_URL}/${VERSION}/uniworx-wflint curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file bin/uniworx-wflint ${PACKAGE_REGISTRY_URL}/${VERSION}/uniworx-wflint
release: release:
cache: {}
variables: variables:
GIT_STRATEGY: none GIT_STRATEGY: none
stage: release stage: release

View File

@ -2,6 +2,31 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [25.10.5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v25.10.4...v25.10.5) (2021-05-07)
### Bug Fixes
* update imprint & add instructions for help ([eec9a39](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/eec9a3974fc4cde5cc70ab650d018667ce044a92))
## [25.10.4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v25.10.3...v25.10.4) (2021-05-06)
### Bug Fixes
* **workflow-workflow-list:** restore default sorting ([454a917](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/454a91702bdbbed7e473ef94a603bcea2e716406))
## [25.10.3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v25.10.2...v25.10.3) (2021-05-05)
### Bug Fixes
* restore workflowWorkflowList columns ([e55c6d7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e55c6d795fd724bdb732e22d13c96d6b67ea7da1))
## [25.10.2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v25.10.1...v25.10.2) (2021-05-04)
## [25.10.1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v25.10.0...v25.10.1) (2021-05-04)
## [25.10.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v25.9.3...v25.10.0) (2021-04-15) ## [25.10.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v25.9.3...v25.10.0) (2021-04-15)

44
flake.lock Normal file
View File

@ -0,0 +1,44 @@
{
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1619345332,
"narHash": "sha256-qHnQkEp1uklKTpx3MvKtY6xzgcqXDsz5nLilbbuL+3A=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "2ebf2558e5bf978c7fb8ea927dfaed8fefab2e28",
"type": "github"
},
"original": {
"owner": "numtide",
"ref": "master",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1620323686,
"narHash": "sha256-+gfcE3YTGl+Osc8HzOUXSFO8/0PAK4J8ZxCXZ4hjXHI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "dfacb8329b2236688b9a1e705116203a213b283a",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "master",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

27
flake.nix Normal file
View File

@ -0,0 +1,27 @@
{
inputs = {
nixpkgs = {
type = "github";
owner = "NixOS";
repo = "nixpkgs";
ref = "master";
};
flake-utils = {
type = "github";
owner = "numtide";
repo = "flake-utils";
ref = "master";
};
};
outputs = { nixpkgs, flake-utils, ... }: flake-utils.lib.eachDefaultSystem
(system:
let pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
};
in {
devShell = import ./shell.nix { inherit pkgs; };
}
);
}

View File

@ -1,10 +1,8 @@
{ nixpkgs ? import <nixpkgs> import (
}: let
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
import ((nixpkgs {}).fetchFromGitHub { in fetchTarball {
owner = "NixOS"; url = "https://api.github.com/repos/NixOS/nixpkgs/tarball/${lock.nodes.nixpkgs.locked.rev}";
repo = "nixpkgs"; sha256 = lock.nodes.nixpkgs.locked.narHash;
rev = "a7a1447e5d40a9ad90983d33e151f5474eddeed9"; }
sha256 = "1zb8wgsq9grrsdcz81y08h45rj8i5r8ckjhg2cv1cqmam4dczcrf"; )
fetchSubmodules = true;
})

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "uni2work", "name": "uni2work",
"version": "25.10.0", "version": "25.10.5",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "uni2work", "name": "uni2work",
"version": "25.10.0", "version": "25.10.5",
"description": "", "description": "",
"keywords": [], "keywords": [],
"author": "", "author": "",

View File

@ -1,5 +1,5 @@
name: uniworx name: uniworx
version: 25.10.0 version: 25.10.5
dependencies: dependencies:
- base - base
- yesod - yesod

230
shell.nix
View File

@ -1,12 +1,8 @@
{ nixpkgs ? import ./nixpkgs.nix {} }: { pkgs ? (import ./nixpkgs.nix).pkgs }:
let let
inherit (nixpkgs {}) pkgs;
# haskellPackages = import ./stackage.nix { inherit nixpkgs; };
haskellPackages = pkgs.haskellPackages; haskellPackages = pkgs.haskellPackages;
drv = haskellPackages.callPackage ./uniworx.nix {};
postgresSchema = pkgs.writeText "schema.sql" '' postgresSchema = pkgs.writeText "schema.sql" ''
CREATE USER uniworx WITH SUPERUSER; CREATE USER uniworx WITH SUPERUSER;
CREATE DATABASE uniworx_test; CREATE DATABASE uniworx_test;
@ -19,129 +15,167 @@ let
local all all trust local all all trust
''; '';
override = oldAttrs: { develop = pkgs.writeScriptBin "develop" ''
nativeBuildInputs = oldAttrs.nativeBuildInputs ++ (with pkgs; [ nodejs-14_x postgresql_12 openldap google-chrome exiftool memcached minio minio-client ]) ++ (with pkgs.haskellPackages; [ stack yesod-bin hlint cabal-install weeder profiteur ]); #!${pkgs.zsh}/bin/zsh
shellHook = ''
export PROMPT_INFO="${oldAttrs.name}"
export EDITOR=emacsclient cleanup() {
set +e -x
type cleanup_postgres &>/dev/null && cleanup_postgres
type cleanup_widget_memcached &>/dev/null && cleanup_widget_memcached
type cleanup_session_memcached &>/dev/null && cleanup_session_memcached
type cleanup_cache_memcached &>/dev/null && cleanup_cache_memcached
type cleanup_minio &>/dev/null && cleanup_minio
cleanup() { [ -f "''${basePath}/.develop.env" ] && rm -vf "''${basePath}/.develop.env"
set +e -x set +x
type cleanup_postgres &>/dev/null && cleanup_postgres }
type cleanup_widget_memcached &>/dev/null && cleanup_widget_memcached
type cleanup_session_memcached &>/dev/null && cleanup_session_memcached trap cleanup EXIT
type cleanup_cache_memcached &>/dev/null && cleanup_cache_memcached
type cleanup_minio &>/dev/null && cleanup_minio basePath=$(pwd)
set +x
echo "" > ''${basePath}/.develop.env
export PORT_OFFSET=$(((16#$(whoami | sha256sum | head -c 16)) % 1000))
if [[ -z "$PGHOST" ]]; then
set -xe
pgDir=$(mktemp -d --tmpdir=''${XDG_RUNTIME_DIR} postgresql.XXXXXX)
pgSockDir=$(mktemp -d --tmpdir=''${XDG_RUNTIME_DIR} postgresql.sock.XXXXXX)
pgLogFile=$(mktemp --tmpdir=''${XDG_RUNTIME_DIR} postgresql.XXXXXX.log)
initdb --no-locale -D ''${pgDir}
pg_ctl start -D ''${pgDir} -l ''${pgLogFile} -w -o "-k ''${pgSockDir} -c listen_addresses=''' -c hba_file='${postgresHba}' -c unix_socket_permissions=0700 -c max_connections=9990 -c shared_preload_libraries=pg_stat_statements -c auto_explain.log_min_duration=100ms"
psql -h ''${pgSockDir} -f ${postgresSchema} postgres
printf "Postgres logfile is %s\nPostgres socket directory is %s\n" ''${pgLogFile} ''${pgSockDir}
export PGHOST=''${pgSockDir}
export PGLOG=''${pgLogFile}
cleanup_postgres() {
set +e -x
pg_ctl stop -D ''${pgDir}
rm -rvf ''${pgDir} ''${pgSockDir} ''${pgLogFile}
set +x
} }
trap cleanup EXIT set +xe
fi
if [[ -z "$PGHOST" ]]; then if [[ -z "$WIDGET_MEMCACHED_HOST" ]]; then
set -xe set -xe
pgDir=$(mktemp -d) memcached -l localhost -p $(($PORT_OFFSET + 11211)) &>/dev/null &
pgSockDir=$(mktemp -d) widget_memcached_pid=$!
pgLogFile=$(mktemp)
initdb --no-locale -D ''${pgDir}
pg_ctl start -D ''${pgDir} -l ''${pgLogFile} -w -o "-k ''${pgSockDir} -c listen_addresses=''' -c hba_file='${postgresHba}' -c unix_socket_permissions=0700 -c max_connections=9990 -c shared_preload_libraries=pg_stat_statements -c auto_explain.log_min_duration=100ms"
export PGHOST=''${pgSockDir} PGLOG=''${pgLogFile}
psql -f ${postgresSchema} postgres
printf "Postgres logfile is %s\nPostgres socket directory is %s\n" ''${pgLogFile} ''${pgSockDir}
cleanup_postgres() { export WIDGET_MEMCACHED_HOST=localhost
set +e -x export WIDGET_MEMCACHED_PORT=$(($PORT_OFFSET + 11211))
pg_ctl stop -D ''${pgDir}
rm -rvf ''${pgDir} ''${pgSockDir} ''${pgLogFile}
set +x
}
set +xe cleanup_widget_memcached() {
fi [[ -n "$widget_memcached_pid" ]] && kill $widget_memcached_pid
}
if [[ -z "$WIDGET_MEMCACHED_HOST" ]]; then set +xe
set -xe fi
memcached -l localhost -p 11211 &>/dev/null & if [[ -z "$SESSION_MEMCACHED_HOST" ]]; then
widget_memcached_pid=$? set -xe
cleanup_widget_memcached() { memcached -l localhost -p $(($PORT_OFFSET + 11212)) &>/dev/null &
[[ -n "$widget_memcached_pid" ]] && kill $widget_memcached_pid session_memcached_pid=$!
}
export WIDGET_MEMCACHED_HOST=localhost WIDGET_MEMCACHED_PORT=11211 export SESSION_MEMCACHED_HOST=localhost
export SESSION_MEMCACHED_PORT=$(($PORT_OFFSET + 11212))
set +xe cleanup_session_memcached() {
fi [[ -n "$session_memcached_pid" ]] && kill $session_memcached_pid
}
if [[ -z "$SESSION_MEMCACHED_HOST" ]]; then set +xe
set -xe fi
memcached -l localhost -p 11212 &>/dev/null & if [[ -z "$MEMCACHED_HOST" ]]; then
session_memcached_pid=$? set -xe
cleanup_session_memcached() { memcached -l localhost -p $(($PORT_OFFSET + 11213)) &>/dev/null &
[[ -n "$session_memcached_pid" ]] && kill $session_memcached_pid memcached_pid=$!
}
export SESSION_MEMCACHED_HOST=localhost SESSION_MEMCACHED_PORT=11212 export MEMCACHED_HOST=localhost
export MEMCACHED_PORT=$(($PORT_OFFSET + 11212))
set +xe cleanup_session_memcached() {
fi [[ -n "$memcached_pid" ]] && kill $memcached_pid
}
if [[ -z "$MEMCACHED_HOST" ]]; then set +xe
set -xe fi
memcached -l localhost -p 11213 &>/dev/null & if [[ -z "$UPLOAD_S3_HOST" ]]; then
memcached_pid=$? set -xe
cleanup_session_memcached() { cleanup_minio() {
[[ -n "$memcached_pid" ]] && kill $memcached_pid [[ -n "$minio_pid" ]] && kill $minio_pid
} [[ -n "''${MINIO_DIR}" ]] && rm -rvf ''${MINIO_DIR}
[[ -n "''${MINIO_LOGFILE}" ]] && rm -rvf ''${MINIO_LOGFILE}
}
export MEMCACHED_HOST=localhost MEMCACHED_PORT=11212 export MINIO_DIR=$(mktemp -d --tmpdir=''${XDG_RUNTIME_DIR} minio.XXXXXX)
export MINIO_LOGFILE=$(mktemp --tmpdir=''${XDG_RUNTIME_DIR} minio.XXXXXX.log)
export MINIO_ACCESS_KEY=$(${pkgs.pwgen}/bin/pwgen -s 16 1)
export MINIO_SECRET_KEY=$(${pkgs.pwgen}/bin/pwgen -s 32 1)
set +xe minio server --address localhost:$(($PORT_OFFSET + 9000)) ''${MINIO_DIR} &>''${MINIO_LOGFILE} &
fi minio_pid=$!
if [[ -z "$UPLOAD_S3_HOST" ]]; then export UPLOAD_S3_HOST=localhost
set -xe export UPLOAD_S3_PORT=$(($PORT_OFFSET + 9000))
export UPLOAD_S3_SSL=false
export UPLOAD_S3_KEY_ID=''${MINIO_ACCESS_KEY}
export UPLOAD_S3_KEY=''${MINIO_SECRET_KEY}
cleanup_minio() { sleep 1
[[ -n "$minio_pid" ]] && kill $minio_pid
[[ -n "$minio_dir" ]] && rm -rvf ''${minio_dir}
[[ -n "MINIO_LOGFILE" ]] && rm -rvf ''${MINIO_LOGFILE}
}
export MINIO_DIR=$(mktemp -d) set +xe
export MINIO_LOGFILE=$(mktemp --tmpdir minio.XXXXXX.log) fi
export MINIO_ACCESS_KEY=$(${pkgs.pwgen}/bin/pwgen -s 16 1)
export MINIO_SECRET_KEY=$(${pkgs.pwgen}/bin/pwgen -s 32 1)
minio server --address localhost:9000 ''${MINIO_DIR} &>''${MINIO_LOGFILE} & set -xe
minio_pid=$?
sleep 1 cat >> ''${basePath}/.develop.env <<EOF
PORT_OFFSET=''${PORT_OFFSET}
export UPLOAD_S3_HOST=localhost UPLOAD_S3_PORT=9000 UPLOAD_S3_SSL=false UPLOAD_S3_KEY_ID=''${MINIO_ACCESS_KEY} UPLOAD_S3_KEY=''${MINIO_SECRET_KEY} PGHOST=''${pgSockDir}
PGLOG=''${pgLogFile}
set +xe WIDGET_MEMCACHED_HOST=localhost
fi WIDGET_MEMCACHED_PORT=$(($PORT_OFFSET + 11211))
if [ -n "$ZSH_VERSION" ]; then SESSION_MEMCACHED_HOST=localhost
autoload -U +X compinit && compinit SESSION_MEMCACHED_PORT=$(($PORT_OFFSET + 11212))
autoload -U +X bashcompinit && bashcompinit
fi
eval "$(stack --bash-completion-script stack)"
${oldAttrs.shellHook} MEMCACHED_HOST=localhost
''; MEMCACHED_PORT=$(($PORT_OFFSET + 11212))
};
dummy = pkgs.stdenv.mkDerivation { MINIO_DIR=''${MINIO_DIR}
name = "interactive-uniworx-environment"; MINIO_LOGFILE=''${MINIO_LOGFILE}
shellHook = ""; UPLOAD_S3_HOST=localhost
}; UPLOAD_S3_PORT=$(($PORT_OFFSET + 9000))
in pkgs.stdenv.lib.overrideDerivation dummy override UPLOAD_S3_SSL=false
#pkgs.stdenv.lib.overrideDerivation drv.env override UPLOAD_S3_KEY_ID=''${MINIO_ACCESS_KEY}
UPLOAD_S3_KEY=''${MINIO_SECRET_KEY}
EOF
set +xe
if [ -n "$ZSH_VERSION" ]; then
autoload -U +X compinit && compinit
autoload -U +X bashcompinit && bashcompinit
fi
eval "$(stack --bash-completion-script stack)"
$(getent passwd $USER | cut -d: -f 7)
'';
in pkgs.mkShell {
name = "uni2work";
nativeBuildInputs = [develop] ++ (with pkgs; [ nodejs-14_x postgresql_12 openldap google-chrome exiftool memcached minio minio-client ]) ++ (with pkgs.haskellPackages; [ stack yesod-bin hlint cabal-install weeder profiteur ]);
}

View File

@ -88,6 +88,10 @@ instance (CI.FoldCase s, PathPiece s) => PathPiece (CI s) where
fromPathPiece = fmap CI.mk . fromPathPiece fromPathPiece = fmap CI.mk . fromPathPiece
toPathPiece = toPathPiece . CI.original toPathPiece = toPathPiece . CI.original
instance PathPiece [CI Char] where
fromPathPiece = fmap (map CI.mk . (unpack :: Text -> [Char])) . fromPathPiece
toPathPiece = toPathPiece . (pack :: [Char] -> Text) . map CI.original
instance ToHttpApiData s => ToHttpApiData (CI s) where instance ToHttpApiData s => ToHttpApiData (CI s) where
toUrlPiece = toUrlPiece . CI.original toUrlPiece = toUrlPiece . CI.original
toEncodedUrlPiece = toEncodedUrlPiece . CI.original toEncodedUrlPiece = toEncodedUrlPiece . CI.original

View File

@ -1967,7 +1967,9 @@ wouldHaveWriteAccessToIff assumptions route = and2M (not <$> hasWriteAccessTo ro
evalWorkflowRoleFor' :: forall m backend. evalWorkflowRoleFor' :: forall m backend.
( HasCallStack ( HasCallStack
, MonadHandler m, HandlerSite m ~ UniWorX
, MonadAP (ReaderT backend m), MonadIO m , MonadAP (ReaderT backend m), MonadIO m
, MonadThrow m
, BackendCompatible SqlReadBackend backend , BackendCompatible SqlReadBackend backend
) )
=> (forall m'. MonadAP m' => AuthTagsEval m') => (forall m'. MonadAP m' => AuthTagsEval m')
@ -2006,7 +2008,8 @@ evalWorkflowRoleFor' eval mAuthId mwwId wRole route isWrite = do
WorkflowRolePayloadReference{..} -> orDefault . exceptT return return $ do WorkflowRolePayloadReference{..} -> orDefault . exceptT return return $ do
uid <- maybeExceptT AuthenticationRequired $ return mAuthId uid <- maybeExceptT AuthenticationRequired $ return mAuthId
wwId <- maybeMExceptT (unauthorizedI MsgWorkflowRoleNoPayload) $ return mwwId wwId <- maybeMExceptT (unauthorizedI MsgWorkflowRoleNoPayload) $ return mwwId
WorkflowWorkflow{..} <- maybeMExceptT (unauthorizedI MsgWorkflowRoleNoSuchWorkflowWorkflow) . lift . withReaderT (projectBackend @SqlReadBackend) $ get wwId Entity _ WorkflowWorkflow{..} <- maybeMExceptT (unauthorizedI MsgWorkflowRoleNoSuchWorkflowWorkflow) . lift $ getWorkflowWorkflowState wwId
-- WorkflowWorkflow{..} <- maybeMExceptT (unauthorizedI MsgWorkflowRoleNoSuchWorkflowWorkflow) . lift . withReaderT (projectBackend @SqlReadBackend) $ get wwId
let uids = maybe Set.empty getLast . foldMap (fmap Last) . workflowStatePayload workflowRolePayloadLabel $ _DBWorkflowState # workflowWorkflowState let uids = maybe Set.empty getLast . foldMap (fmap Last) . workflowStatePayload workflowRolePayloadLabel $ _DBWorkflowState # workflowWorkflowState
unless (uid `Set.member` uids) $ unless (uid `Set.member` uids) $
throwE =<< unauthorizedI MsgWorkflowRoleUserMismatch throwE =<< unauthorizedI MsgWorkflowRoleUserMismatch
@ -2015,6 +2018,8 @@ evalWorkflowRoleFor' eval mAuthId mwwId wRole route isWrite = do
evalWorkflowRoleFor :: ( HasCallStack evalWorkflowRoleFor :: ( HasCallStack
, MonadAP (ReaderT backend m), MonadIO m , MonadAP (ReaderT backend m), MonadIO m
, MonadHandler m, HandlerSite m ~ UniWorX
, MonadThrow m
, BackendCompatible SqlReadBackend backend , BackendCompatible SqlReadBackend backend
) )
=> Maybe UserId => Maybe UserId
@ -2038,8 +2043,9 @@ evalWorkflowRoleFor mAuthId mwwId wRole route isWrite = do
hasWorkflowRole :: ( HasCallStack hasWorkflowRole :: ( HasCallStack
, MonadAP (ReaderT backend m) , MonadAP (ReaderT backend m)
, BackendCompatible SqlReadBackend backend
, MonadHandler m, HandlerSite m ~ UniWorX , MonadHandler m, HandlerSite m ~ UniWorX
, MonadThrow m
, BackendCompatible SqlReadBackend backend
) )
=> Maybe WorkflowWorkflowId => Maybe WorkflowWorkflowId
-> WorkflowRole UserId -> WorkflowRole UserId
@ -2065,7 +2071,7 @@ mayViewWorkflowAction' :: forall backend m fileid.
-> WorkflowAction fileid UserId -> WorkflowAction fileid UserId
-> WriterT (Set AuthTag) (ReaderT backend m) Bool -> WriterT (Set AuthTag) (ReaderT backend m) Bool
mayViewWorkflowAction' eval mAuthId wwId WorkflowAction{..} = hoist (withReaderT $ projectBackend @SqlReadBackend) . maybeT (return False) $ do mayViewWorkflowAction' eval mAuthId wwId WorkflowAction{..} = hoist (withReaderT $ projectBackend @SqlReadBackend) . maybeT (return False) $ do
WorkflowWorkflow{..} <- MaybeT . lift $ get wwId Entity _ WorkflowWorkflow{..} <- MaybeT . lift $ getWorkflowWorkflowState wwId
rScope <- hoist lift . toRouteWorkflowScope $ _DBWorkflowScope # workflowWorkflowScope rScope <- hoist lift . toRouteWorkflowScope $ _DBWorkflowScope # workflowWorkflowScope
cID <- catchMaybeT (Proxy @CryptoIDError) . lift . lift $ encrypt wwId cID <- catchMaybeT (Proxy @CryptoIDError) . lift . lift $ encrypt wwId
WorkflowGraph{..} <- lift . lift $ getSharedIdWorkflowGraph workflowWorkflowGraph WorkflowGraph{..} <- lift . lift $ getSharedIdWorkflowGraph workflowWorkflowGraph

View File

@ -326,7 +326,7 @@ postAdminFeaturesR = do
dbtSQLQuery :: E.SqlExpr (Entity StudyDegree) -> E.SqlQuery (E.SqlExpr (Entity StudyDegree)) dbtSQLQuery :: E.SqlExpr (Entity StudyDegree) -> E.SqlQuery (E.SqlExpr (Entity StudyDegree))
dbtSQLQuery = return dbtSQLQuery = return
dbtRowKey = (E.^. StudyDegreeKey) dbtRowKey = (E.^. StudyDegreeKey)
dbtProj = return dbtProj = dbtProjId
dbtColonnade = formColonnade $ mconcat dbtColonnade = formColonnade $ mconcat
[ sortable (Just "key") (i18nCell MsgGenericKey) (numCell . view (_dbrOutput . _entityVal . _studyDegreeKey)) [ sortable (Just "key") (i18nCell MsgGenericKey) (numCell . view (_dbrOutput . _entityVal . _studyDegreeKey))
, sortable (Just "name") (i18nCell MsgTableDegreeName) (textInputCell _1 (_dbrOutput . _entityVal . _studyDegreeName) (_dbrOutput . _entityKey)) , sortable (Just "name") (i18nCell MsgTableDegreeName) (textInputCell _1 (_dbrOutput . _entityVal . _studyDegreeName) (_dbrOutput . _entityKey))
@ -356,7 +356,7 @@ postAdminFeaturesR = do
dbtSQLQuery :: E.SqlExpr (Entity StudyTerms) -> E.SqlQuery (E.SqlExpr (Entity StudyTerms)) dbtSQLQuery :: E.SqlExpr (Entity StudyTerms) -> E.SqlQuery (E.SqlExpr (Entity StudyTerms))
dbtSQLQuery = return dbtSQLQuery = return
dbtRowKey = (E.^. StudyTermsKey) dbtRowKey = (E.^. StudyTermsKey)
dbtProj field@(view _dbrOutput -> Entity fId _) = do dbtProj = dbtProjSimple $ \field@(Entity fId _) -> do
fieldSchools <- fmap (setOf $ folded . _Value) . E.select . E.from $ \school -> do fieldSchools <- fmap (setOf $ folded . _Value) . E.select . E.from $ \school -> do
E.where_ . E.exists . E.from $ \schoolTerms -> E.where_ . E.exists . E.from $ \schoolTerms ->
E.where_ $ schoolTerms E.^. SchoolTermsSchool E.==. school E.^. SchoolId E.where_ $ schoolTerms E.^. SchoolTermsSchool E.==. school E.^. SchoolId
@ -368,7 +368,7 @@ postAdminFeaturesR = do
E.where_ $ subTerms E.^. StudySubTermsChild E.==. E.val fId E.where_ $ subTerms E.^. StudySubTermsChild E.==. E.val fId
E.&&. subTerms E.^. StudySubTermsParent E.==. terms E.^. StudyTermsId E.&&. subTerms E.^. StudySubTermsParent E.==. terms E.^. StudyTermsId
return terms return terms
return $ field & _dbrOutput %~ (, fieldParents, fieldSchools) return (field, fieldParents, fieldSchools)
dbtColonnade = formColonnade $ mconcat dbtColonnade = formColonnade $ mconcat
[ sortable (Just "key") (i18nCell MsgGenericKey) (maybe mempty numCell . preview (_dbrOutput . _1 . _entityVal . _studyTermsKey)) [ sortable (Just "key") (i18nCell MsgGenericKey) (maybe mempty numCell . preview (_dbrOutput . _1 . _entityVal . _studyTermsKey))
, sortable Nothing (i18nCell MsgStudySubTermsParentKey) (parentsCell _4 (_dbrOutput . _2 . to (Set.map entityKey)) _dbrKey') , sortable Nothing (i18nCell MsgStudySubTermsParentKey) (parentsCell _4 (_dbrOutput . _2 . to (Set.map entityKey)) _dbrKey')
@ -416,7 +416,7 @@ postAdminFeaturesR = do
dbtSQLQuery :: E.SqlExpr (Entity StudyTermNameCandidate) -> E.SqlQuery ( E.SqlExpr (Entity StudyTermNameCandidate)) dbtSQLQuery :: E.SqlExpr (Entity StudyTermNameCandidate) -> E.SqlQuery ( E.SqlExpr (Entity StudyTermNameCandidate))
dbtSQLQuery = return dbtSQLQuery = return
dbtRowKey = (E.^. StudyTermNameCandidateId) dbtRowKey = (E.^. StudyTermNameCandidateId)
dbtProj = return dbtProj = dbtProjId
dbtColonnade = dbColonnade $ mconcat dbtColonnade = dbColonnade $ mconcat
[ sortable (Just "key") (i18nCell MsgStudyTermsKey) (numCell . view (_dbrOutput . _entityVal . _studyTermNameCandidateKey)) [ sortable (Just "key") (i18nCell MsgStudyTermsKey) (numCell . view (_dbrOutput . _entityVal . _studyTermNameCandidateKey))
, sortable (Just "name") (i18nCell MsgStudyTermsName) (textCell . view (_dbrOutput . _entityVal . _studyTermNameCandidateName)) , sortable (Just "name") (i18nCell MsgStudyTermsName) (textCell . view (_dbrOutput . _entityVal . _studyTermNameCandidateName))
@ -459,7 +459,7 @@ postAdminFeaturesR = do
E.on $ parent E.?. StudyTermsKey E.==. E.just (candidate E.^. StudySubTermParentCandidateParent) E.on $ parent E.?. StudyTermsKey E.==. E.just (candidate E.^. StudySubTermParentCandidateParent)
return (candidate, parent, child) return (candidate, parent, child)
dbtRowKey = queryCandidate >>> (E.^. StudySubTermParentCandidateId) dbtRowKey = queryCandidate >>> (E.^. StudySubTermParentCandidateId)
dbtProj = return dbtProj = dbtProjId
dbtColonnade = dbColonnade $ mconcat dbtColonnade = dbColonnade $ mconcat
[ sortable (Just "child") (i18nCell MsgStudySubTermsChildKey) (numCell . view (_dbrOutput . _1 . _entityVal . _studySubTermParentCandidateKey)) [ sortable (Just "child") (i18nCell MsgStudySubTermsChildKey) (numCell . view (_dbrOutput . _1 . _entityVal . _studySubTermParentCandidateKey))
, sortable (Just "child-name") (i18nCell MsgStudySubTermsChildName) (maybe mempty i18nCell . preview (_dbrOutput . _3 . _Just . _entityVal . _studyTermsName . _Just)) , sortable (Just "child-name") (i18nCell MsgStudySubTermsChildName) (maybe mempty i18nCell . preview (_dbrOutput . _3 . _Just . _entityVal . _studyTermsName . _Just))
@ -502,7 +502,7 @@ postAdminFeaturesR = do
E.on $ sterm E.?. StudyTermsKey E.==. E.just (candidate E.^. StudyTermStandaloneCandidateKey) E.on $ sterm E.?. StudyTermsKey E.==. E.just (candidate E.^. StudyTermStandaloneCandidateKey)
return (candidate, sterm) return (candidate, sterm)
dbtRowKey = queryCandidate >>> (E.^. StudyTermStandaloneCandidateId) dbtRowKey = queryCandidate >>> (E.^. StudyTermStandaloneCandidateId)
dbtProj = return dbtProj = dbtProjId
dbtColonnade = formColonnade $ mconcat dbtColonnade = formColonnade $ mconcat
[ sortable (Just "key") (i18nCell MsgStudyTermsKey) (numCell . view (_dbrOutput . _1 . _entityVal . _studyTermStandaloneCandidateKey)) [ sortable (Just "key") (i18nCell MsgStudyTermsKey) (numCell . view (_dbrOutput . _1 . _entityVal . _studyTermStandaloneCandidateKey))
, sortable (Just "name") (i18nCell MsgStudyTermsName) (maybe mempty i18nCell . preview (_dbrOutput . _2 . _Just . _entityVal . _studyTermsName . _Just)) , sortable (Just "name") (i18nCell MsgStudyTermsName) (maybe mempty i18nCell . preview (_dbrOutput . _2 . _Just . _entityVal . _studyTermsName . _Just))

View File

@ -77,8 +77,10 @@ getAllocationListR = do
<*> view (queryAvailable muid ata now) <*> view (queryAvailable muid ata now)
<*> view (maybe (to . const $ E.val 0) (queryApplied ata now) muid) <*> view (maybe (to . const $ E.val 0) (queryApplied ata now) muid)
dbtProj :: DBRow _ -> DB AllocationTableData dbtProj :: _ AllocationTableData
dbtProj = return . over (_dbrOutput . _2) (fromIntegral . E.unValue) . over (_dbrOutput . _3) (fromIntegral . E.unValue) dbtProj = dbtProjId
<&> _dbrOutput . _2 %~ fromIntegral . E.unValue
<&> _dbrOutput . _3 %~ fromIntegral . E.unValue
dbtRowKey = view $ queryAllocation . to (E.^. AllocationId) dbtRowKey = view $ queryAllocation . to (E.^. AllocationId)

View File

@ -187,7 +187,7 @@ postAUsersR tid ssh ash = do
, assigned , assigned
, vetoed) , vetoed)
dbtRowKey = views queryAllocationUser (E.^. AllocationUserId) dbtRowKey = views queryAllocationUser (E.^. AllocationUserId)
dbtProj = runReaderT $ (asks . set _dbrOutput) <=< magnify _dbrOutput $ do dbtProj = dbtProjSimple . runReaderT $ do
feats <- lift . allocationUserStudyFeatures aId =<< views _1 entityKey feats <- lift . allocationUserStudyFeatures aId =<< views _1 entityKey
(,,,,,) (,,,,,)
<$> view _1 <*> pure feats <*> view _2 <*> view (_3 . _Value) <*> view (_4 . _Value) <*> view (_5 . _Value) <$> view _1 <*> pure feats <*> view _2 <*> view (_3 . _Value) <*> view (_4 . _Value) <*> view (_5 . _Value)

View File

@ -271,8 +271,8 @@ postCApplicationsR tid ssh csh = do
, E.not_ . E.isNothing $ courseParticipant E.?. CourseParticipantId , E.not_ . E.isNothing $ courseParticipant E.?. CourseParticipantId
) )
dbtProj :: DBRow _ -> DB CourseApplicationsTableData dbtProj :: _ CourseApplicationsTableData
dbtProj = traverse $ \(application, user, E.Value hasFiles, allocation, E.Value isParticipant) -> do dbtProj = dbtProjSimple $ \(application, user, E.Value hasFiles, allocation, E.Value isParticipant) -> do
feats <- courseUserStudyFeatures (application ^. _entityVal . _courseApplicationCourse) (user ^. _entityKey) feats <- courseUserStudyFeatures (application ^. _entityVal . _courseApplicationCourse) (user ^. _entityKey)
return (application, user, hasFiles, allocation, isParticipant, feats) return (application, user, hasFiles, allocation, isParticipant, feats)

View File

@ -90,8 +90,8 @@ makeCourseTable whereClause colChoices psValidator = do
return user return user
isEditorQuery course user = E.where_ $ mayEditCourse' muid ata course isEditorQuery course user = E.where_ $ mayEditCourse' muid ata course
E.&&. E.just (user E.^. UserId) E.==. E.val muid E.&&. E.just (user E.^. UserId) E.==. E.val muid
dbtProj :: DBRow _ -> DB CourseTableData dbtProj :: _ CourseTableData
dbtProj = traverse $ \(course, E.Value participants, E.Value registered, school) -> do dbtProj = dbtProjSimple $ \(course, E.Value participants, E.Value registered, school) -> do
lecturerList <- E.select $ E.from $ lecturerQuery $ E.val $ entityKey course lecturerList <- E.select $ E.from $ lecturerQuery $ E.val $ entityKey course
courseAlloc <- getBy (UniqueAllocationCourse $ entityKey course) courseAlloc <- getBy (UniqueAllocationCourse $ entityKey course)
>>= traverse (getJustEntity . allocationCourseAllocation . entityVal) >>= traverse (getJustEntity . allocationCourseAllocation . entityVal)

View File

@ -169,7 +169,7 @@ getCShowR tid ssh csh = do
E.||. E.not_ (tutorial E.^. TutorialRoomHidden) E.||. E.not_ (tutorial E.^. TutorialRoomHidden)
return (tutorial, showRoom) return (tutorial, showRoom)
dbtRowKey = (E.^. TutorialId) dbtRowKey = (E.^. TutorialId)
dbtProj = traverse $ return . over _2 E.unValue dbtProj = over (_dbrOutput . _2) E.unValue <$> dbtProjId
dbtColonnade = dbColonnade $ mconcat dbtColonnade = dbColonnade $ mconcat
[ sortable (Just "type") (i18nCell MsgTableTutorialType) $ \(view $ resultTutorial . _entityVal -> Tutorial{..}) -> textCell $ CI.original tutorialType [ sortable (Just "type") (i18nCell MsgTableTutorialType) $ \(view $ resultTutorial . _entityVal -> Tutorial{..}) -> textCell $ CI.original tutorialType
, sortable (Just "name") (i18nCell MsgTableTutorialName) $ \(view $ resultTutorial . _entityVal -> Tutorial{..}) -> indicatorCell <> anchorCell (CTutorialR tid ssh csh tutorialName TUsersR) [whamlet|#{tutorialName}|] , sortable (Just "name") (i18nCell MsgTableTutorialName) $ \(view $ resultTutorial . _entityVal -> Tutorial{..}) -> indicatorCell <> anchorCell (CTutorialR tid ssh csh tutorialName TUsersR) [whamlet|#{tutorialName}|]

View File

@ -306,7 +306,7 @@ courseUserExamsSection (Entity cid Course{..}) (Entity uid _) = do
] ]
return exam return exam
dbtRowKey = (E.^. ExamId) dbtRowKey = (E.^. ExamId)
dbtProj = traverse $ \exam@(Entity eId _) -> do dbtProj = dbtProjSimple $ \exam@(Entity eId _) -> do
registration <- getBy $ UniqueExamRegistration eId uid registration <- getBy $ UniqueExamRegistration eId uid
occurrence <- runMaybeT $ do occurrence <- runMaybeT $ do
Entity _ ExamRegistration{..} <- hoistMaybe registration Entity _ ExamRegistration{..} <- hoistMaybe registration
@ -444,7 +444,7 @@ courseUserTutorialsSection (Entity cid Course{..}) (Entity uid _) = do
E.&&. tutorialParticipant E.^. TutorialParticipantUser E.==. E.val uid E.&&. tutorialParticipant E.^. TutorialParticipantUser E.==. E.val uid
return (tutorial, tutorialParticipant) return (tutorial, tutorialParticipant)
dbtRowKey (_ `E.InnerJoin` tutorialParticipant) = tutorialParticipant E.^. TutorialParticipantId dbtRowKey (_ `E.InnerJoin` tutorialParticipant) = tutorialParticipant E.^. TutorialParticipantId
dbtProj = traverse $ \(tutorial, tutorialParticipant) -> do dbtProj = dbtProjSimple $ \(tutorial, tutorialParticipant) -> do
tutors <- E.select . E.from $ \(tutor `E.InnerJoin` user) -> do tutors <- E.select . E.from $ \(tutor `E.InnerJoin` user) -> do
E.on $ tutor E.^. TutorUser E.==. user E.^. UserId E.on $ tutor E.^. TutorUser E.==. user E.^. UserId
E.where_ $ tutor E.^. TutorTutorial E.==. E.val (tutorial ^. _entityKey) E.where_ $ tutor E.^. TutorTutorial E.==. E.val (tutorial ^. _entityKey)

View File

@ -375,7 +375,7 @@ makeCourseUserTable cid acts restrict colChoices psValidator csvColumns = do
dbtStyle = def { dbsFilterLayout = defaultDBSFilterLayout } dbtStyle = def { dbsFilterLayout = defaultDBSFilterLayout }
dbtSQLQuery q = userTableQuery cid q <* E.where_ (restrict q) dbtSQLQuery q = userTableQuery cid q <* E.where_ (restrict q)
dbtRowKey = queryUser >>> (E.^. UserId) dbtRowKey = queryUser >>> (E.^. UserId)
dbtProj = traverse $ \(user, participant, E.Value userNoteId, subGroup) -> do dbtProj = dbtProjSimple $ \(user, participant, E.Value userNoteId, subGroup) -> do
tuts'' <- selectList [ TutorialParticipantUser ==. entityKey user, TutorialParticipantTutorial <-. map entityKey tutorials ] [] tuts'' <- selectList [ TutorialParticipantUser ==. entityKey user, TutorialParticipantTutorial <-. map entityKey tutorials ] []
exams' <- selectList [ ExamRegistrationUser ==. entityKey user ] [] exams' <- selectList [ ExamRegistrationUser ==. entityKey user ] []
subs' <- E.select . E.from $ \(sheet `E.LeftOuterJoin` (submission `E.InnerJoin` submissionUser)) -> do subs' <- E.select . E.from $ \(sheet `E.LeftOuterJoin` (submission `E.InnerJoin` submissionUser)) -> do

View File

@ -29,7 +29,7 @@ mkExamTable (Entity cid Course{..}) = do
E.where_ $ exam E.^. ExamCourse E.==. E.val cid E.where_ $ exam E.^. ExamCourse E.==. E.val cid
return exam return exam
dbtRowKey = (E.^. ExamId) dbtRowKey = (E.^. ExamId)
dbtProj = return dbtProj = dbtProjFilteredPostId
dbtColonnade = dbColonnade . mconcat $ catMaybes dbtColonnade = dbColonnade . mconcat $ catMaybes
[ Just . sortable (Just "name") (i18nCell MsgTableExamName) $ \DBRow{ dbrOutput = Entity _ Exam{..} } -> indicatorCell <> anchorCell (CExamR tid ssh csh examName EShowR) examName [ Just . sortable (Just "name") (i18nCell MsgTableExamName) $ \DBRow{ dbrOutput = Entity _ Exam{..} } -> indicatorCell <> anchorCell (CExamR tid ssh csh examName EShowR) examName
, (<$ guard mayCreate) . sortable (Just "visible") (i18nCell MsgExamVisibleFrom) $ \DBRow { dbrOutput = Entity _ Exam{..} } -> maybe mempty (dateTimeCellVisible now) examVisibleFrom , (<$ guard mayCreate) . sortable (Just "visible") (i18nCell MsgExamVisibleFrom) $ \DBRow { dbrOutput = Entity _ Exam{..} } -> maybe mempty (dateTimeCellVisible now) examVisibleFrom
@ -61,7 +61,7 @@ mkExamTable (Entity cid Course{..}) = do
E.where_ $ reg E.^. ExamRegistrationExam E.==. exam E.^. ExamId E.where_ $ reg E.^. ExamRegistrationExam E.==. exam E.^. ExamId
) )
] ]
dbtFilter = singletonMap "may-read" . FilterProjected $ dbtFilter = singletonMap "may-read" . mkFilterProjectedPost $
\(Any b) DBRow{ dbrOutput = Entity _ Exam{..} } \(Any b) DBRow{ dbrOutput = Entity _ Exam{..} }
-> (== b) <$> hasReadAccessTo (CExamR tid ssh csh examName EShowR) :: DB Bool -> (== b) <$> hasReadAccessTo (CExamR tid ssh csh examName EShowR) :: DB Bool
dbtFilterUI = const mempty dbtFilterUI = const mempty

View File

@ -454,7 +454,7 @@ postEUsersR tid ssh csh examn = do
return (examRegistration, user, occurrence, examBonus', examResult, courseUserNote) return (examRegistration, user, occurrence, examBonus', examResult, courseUserNote)
dbtRowKey = queryExamRegistration >>> (E.^. ExamRegistrationId) dbtRowKey = queryExamRegistration >>> (E.^. ExamRegistrationId)
dbtProj = runReaderT $ (asks . set _dbrOutput) <=< magnify _dbrOutput $ dbtProj = dbtProjSimple . runReaderT $
(,,,,,,,) (,,,,,,,)
<$> view _1 <*> view _2 <*> view _3 <*> view _4 <*> view _5 <$> view _1 <*> view _2 <*> view _3 <*> view _4 <*> view _5
<*> getExamParts <*> getExamParts

View File

@ -302,8 +302,8 @@ postEGradesR tid ssh csh examn = do
return (examResult, user, occurrence, examRegistration, isSynced) return (examResult, user, occurrence, examRegistration, isSynced)
dbtRowKey = views queryExamResult (E.^. ExamResultId) dbtRowKey = views queryExamResult (E.^. ExamResultId)
dbtProj :: DBRow _ -> DB ExamUserTableData dbtProj :: _ ExamUserTableData
dbtProj = runReaderT $ (asks . set _dbrOutput) <=< magnify _dbrOutput $ dbtProj = dbtProjSimple . runReaderT $
(,,,,,,) (,,,,,,)
<$> view _1 <*> view _2 <*> view _3 <*> view _4 <*> view (_5 . _Value) <$> view _1 <*> view _2 <*> view _3 <*> view _4 <*> view (_5 . _Value)
<*> getSynchronised <*> getSynchronised

View File

@ -18,6 +18,22 @@ import qualified Colonnade
import qualified Data.Conduit.Combinators as C import qualified Data.Conduit.Combinators as C
data ExamsTableFilterProj = ExamsTableFilterProj
{ etProjFilterMayAccess :: Maybe Bool
, etProjFilterHasResults :: Maybe Bool
, etProjFilterIsSynced :: Maybe Bool
}
instance Default ExamsTableFilterProj where
def = ExamsTableFilterProj
{ etProjFilterMayAccess = Nothing
, etProjFilterHasResults = Nothing
, etProjFilterIsSynced = Nothing
}
makeLenses_ ''ExamsTableFilterProj
type ExamsTableExpr = ( E.SqlExpr (Maybe (Entity Exam)) type ExamsTableExpr = ( E.SqlExpr (Maybe (Entity Exam))
`E.InnerJoin` E.SqlExpr (Maybe (Entity Course)) `E.InnerJoin` E.SqlExpr (Maybe (Entity Course))
`E.InnerJoin` E.SqlExpr (Maybe (Entity School)) `E.InnerJoin` E.SqlExpr (Maybe (Entity School))
@ -101,12 +117,33 @@ getEOExamsR = do
return (exam, course, school, externalExam) return (exam, course, school, externalExam)
dbtRowKey = views ($(multifocusG 2) queryExam queryExternalExam) (bimap (E.?. ExamId) (E.?. ExternalExamId)) dbtRowKey = views ($(multifocusG 2) queryExam queryExternalExam) (bimap (E.?. ExamId) (E.?. ExternalExamId))
dbtProj :: DBRow _ -> DB ExamsTableData -- [ singletonMap "may-access" . FilterProjected $ \(Any b) r -> (== b) <$> if
dbtProj = runReaderT $ (asks . set _dbrOutput) <=< magnify _dbrOutput $ do -- | Just exam <- r ^? resultExam . _entityVal
exam <- view _1 -- , Just course <- r ^? resultCourse . _entityVal
course <- view _2 -- -> hasReadAccessTo . urlRoute $ examLink course exam
school <- view _3 -- | Just eexam <- r ^? resultExternalExam . _entityVal
externalExam <- view _4 -- -> hasReadAccessTo . urlRoute $ externalExamLink eexam :: DB Bool
-- | otherwise
-- -> return $ error "Got neither exam nor externalExam in result"
-- , singletonMap "has-results" . FilterProjected $ \(Any b) r -> (return $ b == (r ^. resultResults > 0) :: DB Bool)
-- , singletonMap "is-synced" . FilterProjected $ \(Any b) r -> (return $ b == (r ^. resultSynchronised >= r ^. resultResults) :: DB Bool)
-- ]
dbtProj :: _ ExamsTableData
dbtProj = (views _dbtProjRow . set _dbrOutput) =<< do -- dbtProjSimple . runReaderT $ do
exam <- view $ _dbtProjRow . _dbrOutput . _1
course <- view $ _dbtProjRow . _dbrOutput . _2
school <- view $ _dbtProjRow . _dbrOutput . _3
externalExam <- view $ _dbtProjRow . _dbrOutput . _4
forMM_ (view $ _dbtProjFilter . _etProjFilterMayAccess) $ \b -> if
| Just (Entity _ exam') <- exam
, Just (Entity _ course') <- course
-> guardM . lift . lift . fmap (== b) . hasReadAccessTo . urlRoute $ examLink course' exam'
| Just (Entity _ eexam) <- externalExam
-> guardM . lift . lift . fmap (== b) . hasReadAccessTo . urlRoute $ externalExamLink eexam
| otherwise
-> error "Got neither exam nor externalExam in result"
let let
getExamResults = for_ exam $ \(Entity examId _) -> E.selectSource . E.from $ \examResult -> do getExamResults = for_ exam $ \(Entity examId _) -> E.selectSource . E.from $ \examResult -> do
@ -119,7 +156,12 @@ getEOExamsR = do
return $ ExternalExam.resultIsSynced (E.val uid) externalExamResult return $ ExternalExam.resultIsSynced (E.val uid) externalExamResult
getResults = getExamResults >> getExternalExamResults getResults = getExamResults >> getExternalExamResults
foldResult (E.Value isSynced) = (Sum 1, guardMonoid isSynced $ Sum 1) foldResult (E.Value isSynced) = (Sum 1, guardMonoid isSynced $ Sum 1)
(Sum resultCount, Sum syncedCount) <- lift . runConduit $ getResults .| C.foldMap foldResult (Sum resultCount, Sum syncedCount) <- lift . lift . runConduit $ getResults .| C.foldMap foldResult
forMM_ (view $ _dbtProjFilter . _etProjFilterHasResults) $ \b ->
guard $ b == (resultCount > 0)
forMM_ (view $ _dbtProjFilter . _etProjFilterIsSynced) $ \b ->
guard $ b == (syncedCount >= resultCount)
case (exam, course, school, externalExam) of case (exam, course, school, externalExam) of
(Just exam', Just course', Just school', Nothing) -> return (Just exam', Just course', Just school', Nothing) -> return
@ -189,16 +231,9 @@ getEOExamsR = do
] ]
dbtFilter = mconcat dbtFilter = mconcat
[ singletonMap "may-access" . FilterProjected $ \(Any b) r -> (== b) <$> if [ singletonMap "may-access" . FilterProjected $ (_etProjFilterMayAccess ?~) . getAny
| Just exam <- r ^? resultExam . _entityVal , singletonMap "has-results" . FilterProjected $ (_etProjFilterHasResults ?~) . getAny
, Just course <- r ^? resultCourse . _entityVal , singletonMap "is-synced" . FilterProjected $ (_etProjFilterIsSynced ?~) . getAny
-> hasReadAccessTo . urlRoute $ examLink course exam
| Just eexam <- r ^? resultExternalExam . _entityVal
-> hasReadAccessTo . urlRoute $ externalExamLink eexam :: DB Bool
| otherwise
-> return $ error "Got neither exam nor externalExam in result"
, singletonMap "has-results" . FilterProjected $ \(Any b) r -> (return $ b == (r ^. resultResults > 0) :: DB Bool)
, singletonMap "is-synced" . FilterProjected $ \(Any b) r -> (return $ b == (r ^. resultSynchronised >= r ^. resultResults) :: DB Bool)
] ]
dbtFilterUI = mconcat dbtFilterUI = mconcat
[ flip (prismAForm $ singletonFilter "is-synced" . maybePrism _PathPiece) $ aopt (boolField . Just $ SomeMessage MsgBoolIrrelevant) (fslI MsgExamSynchronised) [ flip (prismAForm $ singletonFilter "is-synced" . maybePrism _PathPiece) $ aopt (boolField . Just $ SomeMessage MsgBoolIrrelevant) (fslI MsgExamSynchronised)

View File

@ -46,7 +46,7 @@ getEExamListR = do
return (eexam, school) return (eexam, school)
dbtRowKey = queryEExam >>> (E.^. ExternalExamId) dbtRowKey = queryEExam >>> (E.^. ExternalExamId)
dbtProj = return dbtProj = dbtProjFilteredPostId
dbtColonnade = widgetColonnade $ mconcat dbtColonnade = widgetColonnade $ mconcat
[ sortable (Just "term") (i18nCell MsgTableTerm) $ \(view resultEExam -> Entity _ ExternalExam{..}) -> i18nCell . ShortTermIdentifier $ unTermKey externalExamTerm [ sortable (Just "term") (i18nCell MsgTableTerm) $ \(view resultEExam -> Entity _ ExternalExam{..}) -> i18nCell . ShortTermIdentifier $ unTermKey externalExamTerm
, sortable (Just "school") (i18nCell MsgTableSchool) $ \(view resultSchool -> Entity _ School{..}) -> i18nCell schoolName , sortable (Just "school") (i18nCell MsgTableSchool) $ \(view resultSchool -> Entity _ School{..}) -> i18nCell schoolName
@ -60,7 +60,7 @@ getEExamListR = do
, ("name", SortColumn $ queryEExam >>> (E.^. ExternalExamExamName)) , ("name", SortColumn $ queryEExam >>> (E.^. ExternalExamExamName))
] ]
dbtFilter = mconcat dbtFilter = mconcat
[ singletonMap "may-access" . FilterProjected $ \(Any b) (view resultEExam -> Entity _ ExternalExam{..}) -> (==b) <$> [ singletonMap "may-access" . mkFilterProjectedPost $ \(Any b) (view resultEExam -> Entity _ ExternalExam{..}) -> (==b) <$>
hasReadAccessTo (EExamR externalExamTerm externalExamSchool externalExamCourseName externalExamExamName EEShowR) :: DB Bool hasReadAccessTo (EExamR externalExamTerm externalExamSchool externalExamCourseName externalExamExamName EEShowR) :: DB Bool
] ]
dbtFilterUI = const mempty dbtFilterUI = const mempty

View File

@ -51,6 +51,7 @@ helpForm mReferer mUid = renderWForm FormStandard $ do
hfReferer' <- wopt routeField (fslI MsgHelpProblemPage & inputReadonly) (Just <$> mReferer) hfReferer' <- wopt routeField (fslI MsgHelpProblemPage & inputReadonly) (Just <$> mReferer)
hfUserId' <- multiActionW identActions (fslI MsgHelpAnswer) (HIUser <$ mUid) hfUserId' <- multiActionW identActions (fslI MsgHelpAnswer) (HIUser <$ mUid)
hfSubject' <- wopt textField (fslpI MsgHelpSubject $ mr MsgHelpSubject) Nothing hfSubject' <- wopt textField (fslpI MsgHelpSubject $ mr MsgHelpSubject) Nothing
wformMessage =<< messageWidget Info $(i18nWidgetFile "help-instructions")
hfRequest' <- case sessErr of hfRequest' <- case sessErr of
Nothing -> fmap Just <$> wreq htmlField (fslpI MsgHelpRequest $ mr MsgHelpRequest) Nothing Nothing -> fmap Just <$> wreq htmlField (fslpI MsgHelpRequest $ mr MsgHelpRequest) Nothing
Just _ -> wopt htmlField (fslpI MsgHelpRequest $ mr MsgHelpRequest) Nothing Just _ -> wopt htmlField (fslpI MsgHelpRequest $ mr MsgHelpRequest) Nothing

View File

@ -111,7 +111,7 @@ getMaterialListR tid ssh csh = do
E.where_ $ materialFile E.^. MaterialFileMaterial E.==. material E.^. MaterialId E.where_ $ materialFile E.^. MaterialFileMaterial E.==. material E.^. MaterialId
return (material, filesNum) return (material, filesNum)
, dbtRowKey = (E.^. MaterialId) , dbtRowKey = (E.^. MaterialId)
, dbtProj = return , dbtProj = dbtProjFilteredPostId
, dbtColonnade = widgetColonnade $ mconcat , dbtColonnade = widgetColonnade $ mconcat
[ -- dbRow, [ -- dbRow,
sortable (Just "type") (i18nCell MsgMaterialType) sortable (Just "type") (i18nCell MsgMaterialType)
@ -138,7 +138,7 @@ getMaterialListR tid ssh csh = do
, ( "last-edit" , SortColumn (E.^. MaterialLastEdit) ) , ( "last-edit" , SortColumn (E.^. MaterialLastEdit) )
] ]
, dbtFilter = mconcat , dbtFilter = mconcat
[ singletonMap "may-access" . FilterProjected $ \(Any b) dbr [ singletonMap "may-access" . mkFilterProjectedPost $ \(Any b) dbr
-> (== b) <$> hasReadAccessTo (matLink . materialName $ row2material dbr) :: DB Bool -> (== b) <$> hasReadAccessTo (matLink . materialName $ row2material dbr) :: DB Bool
] ]
, dbtFilterUI = mempty , dbtFilterUI = mempty
@ -237,7 +237,7 @@ getMShowR tid ssh csh mnm = do
in anchorCellM matLink wgt in anchorCellM matLink wgt
, materialModDateCol (view $ _dbrOutput . _entityVal . to (E.Value . materialFileModified)) , materialModDateCol (view $ _dbrOutput . _entityVal . to (E.Value . materialFileModified))
] ]
, dbtProj = return , dbtProj = dbtProjId
, dbtStyle = def , dbtStyle = def
, dbtParams = def , dbtParams = def
, dbtFilter = mempty , dbtFilter = mempty

View File

@ -161,7 +161,7 @@ newsUpcomingSheets uid = do
{ dbtSQLQuery = tableData { dbtSQLQuery = tableData
, dbtRowKey = \((_ `E.InnerJoin` _ `E.InnerJoin` sheet) `E.LeftOuterJoin` _) -> sheet E.^. SheetId , dbtRowKey = \((_ `E.InnerJoin` _ `E.InnerJoin` sheet) `E.LeftOuterJoin` _) -> sheet E.^. SheetId
, dbtColonnade = colonnade , dbtColonnade = colonnade
, dbtProj = return , dbtProj = dbtProjFilteredPostId
, dbtSorting = Map.fromList , dbtSorting = Map.fromList
[ ( "term" [ ( "term"
, SortColumn $ \(_ `E.InnerJoin` course `E.InnerJoin` _ `E.LeftOuterJoin` _) -> course E.^. CourseTerm , SortColumn $ \(_ `E.InnerJoin` course `E.InnerJoin` _ `E.LeftOuterJoin` _) -> course E.^. CourseTerm
@ -183,7 +183,7 @@ newsUpcomingSheets uid = do
) )
] ]
, dbtFilter = mconcat , dbtFilter = mconcat
[ singletonMap "may-access" . FilterProjected $ \(Any b) DBRow{..} -> [ singletonMap "may-access" . mkFilterProjectedPost $ \(Any b) DBRow{..} ->
let (E.Value tid, E.Value ssh, E.Value csh, E.Value shn, _, _) = dbrOutput :: ( E.Value (Key Term) let (E.Value tid, E.Value ssh, E.Value csh, E.Value shn, _, _) = dbrOutput :: ( E.Value (Key Term)
, E.Value SchoolId , E.Value SchoolId
, E.Value CourseShorthand , E.Value CourseShorthand
@ -252,7 +252,7 @@ newsUpcomingExams uid = do
E.||. E.maybe E.false E.not_ (occurrence E.?. ExamOccurrenceRoomHidden) E.||. E.maybe E.false E.not_ (occurrence E.?. ExamOccurrenceRoomHidden)
return (course, exam, register, occurrence, showRoom) return (course, exam, register, occurrence, showRoom)
dbtRowKey = queryExam >>> (E.^. ExamId) dbtRowKey = queryExam >>> (E.^. ExamId)
dbtProj = return dbtProj = dbtProjFilteredPostId
dbtColonnade = dbColonnade $ mconcat dbtColonnade = dbColonnade $ mconcat
[ sortable (Just "term") (i18nCell MsgTableTerm) $ \DBRow{ dbrOutput = view lensCourse -> Entity _ Course{..} } -> [ sortable (Just "term") (i18nCell MsgTableTerm) $ \DBRow{ dbrOutput = view lensCourse -> Entity _ Course{..} } ->
msgCell courseTerm msgCell courseTerm
@ -323,7 +323,7 @@ newsUpcomingExams uid = do
)) ))
] ]
dbtFilter = mconcat dbtFilter = mconcat
[ singletonMap "may-access" . FilterProjected $ \(Any b) DBRow{..} -> [ singletonMap "may-access" . mkFilterProjectedPost $ \(Any b) DBRow{..} ->
let Entity _ Exam{..} = view lensExam dbrOutput let Entity _ Exam{..} = view lensExam dbrOutput
Entity _ Course{..} = view lensCourse dbrOutput Entity _ Course{..} = view lensCourse dbrOutput
in (==b) <$> hasReadAccessTo (CExamR courseTerm courseSchool courseShorthand examName EShowR) :: DB Bool in (==b) <$> hasReadAccessTo (CExamR courseTerm courseSchool courseShorthand examName EShowR) :: DB Bool

View File

@ -504,7 +504,7 @@ mkOwnedCoursesTable =
, course E.^. CourseShorthand , course E.^. CourseShorthand
) )
dbtRowKey (course `E.InnerJoin` _) = course E.^. CourseId dbtRowKey (course `E.InnerJoin` _) = course E.^. CourseId
dbtProj = return . (_dbrOutput %~ (\(E.Value tid, E.Value ssh, E.Value csh) -> (tid,ssh,csh))) dbtProj = dbtProjId <&> _dbrOutput %~ (\(E.Value tid, E.Value ssh, E.Value csh) -> (tid,ssh,csh))
dbtColonnade = mconcat dbtColonnade = mconcat
[ sortable (Just "term") (i18nCell MsgTableTerm & cellAttrs .~ [("priority","0")]) $ do [ sortable (Just "term") (i18nCell MsgTableTerm & cellAttrs .~ [("priority","0")]) $ do
@ -556,7 +556,7 @@ mkEnrolledCoursesTable =
E.&&. participant E.^. CourseParticipantState E.==. E.val CourseParticipantActive E.&&. participant E.^. CourseParticipantState E.==. E.val CourseParticipantActive
return (course, participant E.^. CourseParticipantRegistration) return (course, participant E.^. CourseParticipantRegistration)
, dbtRowKey = \(course `E.InnerJoin` _) -> course E.^. CourseId , dbtRowKey = \(course `E.InnerJoin` _) -> course E.^. CourseId
, dbtProj = \x -> return $ x & _dbrOutput . _2 %~ E.unValue , dbtProj = dbtProjId <&> _dbrOutput . _2 %~ E.unValue
, dbtColonnade = mconcat , dbtColonnade = mconcat
[ sortable (Just "term") (i18nCell MsgTableTerm) $ [ sortable (Just "term") (i18nCell MsgTableTerm) $
termCell <$> view (_dbrOutput . _1 . _entityVal . _courseTerm) termCell <$> view (_dbrOutput . _1 . _entityVal . _courseTerm)
@ -620,10 +620,10 @@ mkSubmissionTable =
E.&&. subEdit E.^. SubmissionEditUser E.==. E.val (Just uid) E.&&. subEdit E.^. SubmissionEditUser E.==. E.val (Just uid)
return . E.max_ $ subEdit E.^. SubmissionEditTime return . E.max_ $ subEdit E.^. SubmissionEditTime
dbtProj x = return $ x dbtProj = dbtProjId
& _dbrOutput . _1 %~ (\(E.Value tid, E.Value ssh, E.Value csh) -> (tid,ssh,csh)) <&> _dbrOutput . _1 %~ (\(E.Value tid, E.Value ssh, E.Value csh) -> (tid,ssh,csh))
& _dbrOutput . _2 %~ E.unValue <&> _dbrOutput . _2 %~ E.unValue
& _dbrOutput . _4 %~ E.unValue <&> _dbrOutput . _4 %~ E.unValue
dbtColonnade = mconcat dbtColonnade = mconcat
[ sortable (Just "term") (i18nCell MsgTableTerm) $ [ sortable (Just "term") (i18nCell MsgTableTerm) $
@ -697,8 +697,8 @@ mkSubmissionGroupTable =
return (crse, sgroup) return (crse, sgroup)
dbtRowKey (_ `E.InnerJoin` sgroup `E.InnerJoin` _) = sgroup E.^. SubmissionGroupId dbtRowKey (_ `E.InnerJoin` sgroup `E.InnerJoin` _) = sgroup E.^. SubmissionGroupId
dbtProj x = return $ x dbtProj = dbtProjId
& _dbrOutput . _1 %~ $(E.unValueN 3) <&> _dbrOutput . _1 %~ $(E.unValueN 3)
dbtColonnade = mconcat dbtColonnade = mconcat
[ sortable (Just "term") (i18nCell MsgTableTerm) $ [ sortable (Just "term") (i18nCell MsgTableTerm) $
@ -764,9 +764,9 @@ mkCorrectionsTable =
return (crse, sheet E.^. SheetName, corrector, (corrsAssigned uid sheet, corrsCorrected uid sheet)) return (crse, sheet E.^. SheetName, corrector, (corrsAssigned uid sheet, corrsCorrected uid sheet))
dbtRowKey (_ `E.InnerJoin` sheet `E.InnerJoin` _) = sheet E.^. SheetId dbtRowKey (_ `E.InnerJoin` sheet `E.InnerJoin` _) = sheet E.^. SheetId
dbtProj x = return $ x dbtProj = dbtProjId
& _dbrOutput . _1 %~ (\(E.Value tid, E.Value ssh, E.Value csh) -> (tid,ssh,csh)) <&> _dbrOutput . _1 %~ (\(E.Value tid, E.Value ssh, E.Value csh) -> (tid,ssh,csh))
& _dbrOutput . _2 %~ E.unValue <&> _dbrOutput . _2 %~ E.unValue
dbtColonnade = mconcat dbtColonnade = mconcat
[ sortable (Just "term") (i18nCell MsgTableTerm) $ [ sortable (Just "term") (i18nCell MsgTableTerm) $

View File

@ -19,8 +19,7 @@ getSchoolListR = do
dbtSQLQuery :: E.SqlExpr (Entity School) -> E.SqlQuery _ dbtSQLQuery :: E.SqlExpr (Entity School) -> E.SqlQuery _
dbtSQLQuery = return dbtSQLQuery = return
dbtProj :: DBRow _ -> DB (DBRow (Entity School)) dbtProj = dbtProjId
dbtProj = return
dbtRowKey = (E.^. SchoolId) dbtRowKey = (E.^. SchoolId)

View File

@ -129,7 +129,7 @@ getSheetListR tid ssh csh = do
) )
return (sheet, lastSheetEdit sheet, submission, existFiles) return (sheet, lastSheetEdit sheet, submission, existFiles)
, dbtRowKey = \(sheet `E.LeftOuterJoin` _) -> sheet E.^. SheetId , dbtRowKey = \(sheet `E.LeftOuterJoin` _) -> sheet E.^. SheetId
, dbtProj = return , dbtProj = dbtProjFilteredPostId
, dbtSorting = Map.fromList , dbtSorting = Map.fromList
[ ( "name" [ ( "name"
, SortColumn $ \(sheet `E.LeftOuterJoin` _) -> sheet E.^. SheetName , SortColumn $ \(sheet `E.LeftOuterJoin` _) -> sheet E.^. SheetName
@ -157,11 +157,11 @@ getSheetListR tid ssh csh = do
-- ) -- )
] ]
, dbtFilter = mconcat , dbtFilter = mconcat
[ singletonMap "may-access" . FilterProjected $ \(Any b) DBRow{..} -> [ singletonMap "may-access" . mkFilterProjectedPost $ \(Any b) DBRow{..} ->
let (Entity _ Sheet{..}, _, _, _) = dbrOutput :: (Entity Sheet, E.Value (Maybe UTCTime), Maybe (Entity Submission), (E.Value Bool, E.Value Bool, E.Value Bool, E.Value Bool)) let (Entity _ Sheet{..}, _, _, _) = dbrOutput :: (Entity Sheet, E.Value (Maybe UTCTime), Maybe (Entity Submission), (E.Value Bool, E.Value Bool, E.Value Bool, E.Value Bool))
in (== b) <$> sheetFilter sheetName :: DB Bool in (== b) <$> sheetFilter sheetName :: DB Bool
, singletonMap "rated" . FilterColumn $ \(Any b) -> (E.==. E.val b) . E.isJust . (E.?. SubmissionRatingTime) . querySubmission , singletonMap "rated" . FilterColumn $ \(Any b) -> (E.==. E.val b) . E.isJust . (E.?. SubmissionRatingTime) . querySubmission
, singletonMap "is-exam" . FilterProjected $ \(Any b) DBRow{..} -> , singletonMap "is-exam" . mkFilterProjectedPost $ \(Any b) DBRow{..} ->
let (Entity _ Sheet{..}, _, _, _) = dbrOutput :: (Entity Sheet, E.Value (Maybe UTCTime), Maybe (Entity Submission), (E.Value Bool, E.Value Bool, E.Value Bool, E.Value Bool)) let (Entity _ Sheet{..}, _, _, _) = dbrOutput :: (Entity Sheet, E.Value (Maybe UTCTime), Maybe (Entity Submission), (E.Value Bool, E.Value Bool, E.Value Bool, E.Value Bool))
in return $ is _ExamPartPoints sheetType == b :: DB Bool in return $ is _ExamPartPoints sheetType == b :: DB Bool
] ]

View File

@ -71,12 +71,11 @@ getSShowR tid ssh csh shn = do
{ dbtSQLQuery = fileData { dbtSQLQuery = fileData
, dbtRowKey = \(sheetFile `E.FullOuterJoin` psFile) -> (sheetFile E.?. SheetFileId, psFile E.?. PersonalisedSheetFileId) , dbtRowKey = \(sheetFile `E.FullOuterJoin` psFile) -> (sheetFile E.?. SheetFileId, psFile E.?. PersonalisedSheetFileId)
, dbtColonnade = colonnadeFiles , dbtColonnade = colonnadeFiles
, dbtProj = return . dbrOutput :: DBRow _ -> DB (E.Value FilePath, E.Value UTCTime, E.Value SheetFileType, E.Value (Maybe FileContentReference)) , dbtProj = (dbrOutput :: _ -> (E.Value FilePath, E.Value UTCTime, E.Value SheetFileType, E.Value (Maybe FileContentReference))) <$> dbtProjFilteredPostId
, dbtStyle = def , dbtStyle = def
, dbtFilter = mconcat , dbtFilter = mconcat
[ singletonMap "may-access" . FilterProjected $ \(Any b) r -> [ singletonMap "may-access" . mkFilterProjectedPost $ \(Any b) DBRow{ dbrOutput = (E.Value fName, _ :: E.Value UTCTime, E.Value fType, _ :: E.Value (Maybe FileContentReference)) } ->
let (E.Value fName, _, E.Value fType, _) = r :: (E.Value FilePath, E.Value UTCTime, E.Value SheetFileType, E.Value (Maybe FileContentReference)) (==b) <$> hasReadAccessTo (CSheetR tid ssh csh shn $ SFileR fType fName) :: DB Bool
in (==b) <$> hasReadAccessTo (CSheetR tid ssh csh shn $ SFileR fType fName) :: DB Bool
] ]
, dbtFilterUI = mempty , dbtFilterUI = mempty
, dbtIdent = "files" :: Text , dbtIdent = "files" :: Text

View File

@ -545,7 +545,7 @@ submissionHelper tid ssh csh shn mcid = do
{ dbtSQLQuery = submissionFiles smid { dbtSQLQuery = submissionFiles smid
, dbtRowKey = \(sf1 `E.FullOuterJoin` sf2) -> (sf1 E.?. SubmissionFileId, sf2 E.?. SubmissionFileId) , dbtRowKey = \(sf1 `E.FullOuterJoin` sf2) -> (sf1 E.?. SubmissionFileId, sf2 E.?. SubmissionFileId)
, dbtColonnade = colonnadeFiles cid , dbtColonnade = colonnadeFiles cid
, dbtProj = return . dbrOutput , dbtProj = dbrOutput <$> dbtProjId
, dbtStyle = def , dbtStyle = def
, dbtIdent = "files" :: Text , dbtIdent = "files" :: Text
, dbtSorting = Map.fromList , dbtSorting = Map.fromList

View File

@ -37,6 +37,17 @@ import Database.Persist.Sql (updateWhereCount)
import Data.List (genericLength) import Data.List (genericLength)
newtype CorrectionTableFilterProj = CorrectionTableFilterProj
{ corrProjFilterSubmission :: Maybe (Set [CI Char])
}
instance Default CorrectionTableFilterProj where
def = CorrectionTableFilterProj
{ corrProjFilterSubmission = Nothing
}
makeLenses_ ''CorrectionTableFilterProj
type CorrectionTableExpr = (E.SqlExpr (Entity Course) `E.InnerJoin` E.SqlExpr (Entity Sheet) `E.InnerJoin` E.SqlExpr (Entity Submission)) `E.LeftOuterJoin` E.SqlExpr (Maybe (Entity User)) type CorrectionTableExpr = (E.SqlExpr (Entity Course) `E.InnerJoin` E.SqlExpr (Entity Sheet) `E.InnerJoin` E.SqlExpr (Entity Submission)) `E.LeftOuterJoin` E.SqlExpr (Maybe (Entity User))
type CorrectionTableWhere = CorrectionTableExpr -> E.SqlExpr (E.Value Bool) type CorrectionTableWhere = CorrectionTableExpr -> E.SqlExpr (E.Value Bool)
type CorrectionTableData = DBRow (Entity Submission, Entity Sheet, (CourseName, CourseShorthand, Key Term, Key School), Maybe (Entity User), Maybe UTCTime, Map UserId (User, Maybe Pseudonym, Maybe SubmissionGroupName), CryptoFileNameSubmission, Bool {- Access to non-anonymous submission data -}) type CorrectionTableData = DBRow (Entity Submission, Entity Sheet, (CourseName, CourseShorthand, Key Term, Key School), Maybe (Entity User), Maybe UTCTime, Map UserId (User, Maybe Pseudonym, Maybe SubmissionGroupName), CryptoFileNameSubmission, Bool {- Access to non-anonymous submission data -})
@ -225,9 +236,14 @@ makeCorrectionsTable whereClause dbtColonnade dbtFilterUI psValidator dbtParams
) )
in (submission, sheet, crse, corrector, lastEditQuery submission) in (submission, sheet, crse, corrector, lastEditQuery submission)
) )
dbtProj :: DBRow _ -> DB CorrectionTableData dbtProj = (views _dbtProjRow . set _dbrOutput) =<< do
dbtProj = traverse $ \(submission@(Entity sId _), sheet@(Entity shId Sheet{..}), (E.Value courseName, E.Value courseShorthand, E.Value courseTerm, E.Value courseSchool), mCorrector, E.Value mbLastEdit) -> do (submission@(Entity sId _), sheet@(Entity shId Sheet{..}), (E.Value courseName, E.Value courseShorthand, E.Value courseTerm, E.Value courseSchool), mCorrector, E.Value mbLastEdit) <- view $ _dbtProjRow . _dbrOutput
submittors <- E.select . E.from $ \((submissionUser `E.InnerJoin` user) `E.LeftOuterJoin` pseudonym) -> do cid <- encrypt sId
forMM_ (view $ _dbtProjFilter . _corrProjFilterSubmission) $ \criteria ->
let haystack = map CI.mk . unpack $ toPathPiece cid
in guard $ any (`isInfixOf` haystack) criteria
submittors <- lift . lift . E.select . E.from $ \((submissionUser `E.InnerJoin` user) `E.LeftOuterJoin` pseudonym) -> do
E.on $ pseudonym E.?. SheetPseudonymUser E.==. E.just (user E.^. UserId) E.on $ pseudonym E.?. SheetPseudonymUser E.==. E.just (user E.^. UserId)
E.&&. pseudonym E.?. SheetPseudonymSheet E.==. E.just (E.val shId) E.&&. pseudonym E.?. SheetPseudonymSheet E.==. E.just (E.val shId)
E.on $ submissionUser E.^. SubmissionUserUser E.==. user E.^. UserId E.on $ submissionUser E.^. SubmissionUserUser E.==. user E.^. UserId
@ -238,13 +254,14 @@ makeCorrectionsTable whereClause dbtColonnade dbtFilterUI psValidator dbtParams
E.where_ $ submissionGroup E.^. SubmissionGroupCourse E.==. E.val sheetCourse E.where_ $ submissionGroup E.^. SubmissionGroupCourse E.==. E.val sheetCourse
E.where_ $ submissionGroupUser E.^. SubmissionGroupUserUser E.==. user E.^. UserId E.where_ $ submissionGroupUser E.^. SubmissionGroupUserUser E.==. user E.^. UserId
return . E.just $ submissionGroup E.^. SubmissionGroupName return . E.just $ submissionGroup E.^. SubmissionGroupName
return (user, pseudonym E.?. SheetPseudonymPseudonym, submissionGroup') return (user, pseudonym E.?. SheetPseudonymPseudonym, submissionGroup')
let let
submittorMap = List.foldr (\(Entity userId user, E.Value pseudo, E.Value sGroup) -> Map.insert userId (user, pseudo, sGroup)) Map.empty submittors submittorMap = List.foldr (\(Entity userId user, E.Value pseudo, E.Value sGroup) -> Map.insert userId (user, pseudo, sGroup)) Map.empty submittors
nonAnonymousAccess <- or2M nonAnonymousAccess <- lift . lift $ or2M
(return $ not sheetAnonymousCorrection) (return $ not sheetAnonymousCorrection)
(hasReadAccessTo $ CourseR courseTerm courseSchool courseShorthand CCorrectionsR) (hasReadAccessTo $ CourseR courseTerm courseSchool courseShorthand CCorrectionsR)
cid <- encrypt sId
return (submission, sheet, (courseName, courseShorthand, courseTerm, courseSchool), mCorrector, mbLastEdit, submittorMap, cid, nonAnonymousAccess) return (submission, sheet, (courseName, courseShorthand, courseTerm, courseSchool), mCorrector, mbLastEdit, submittorMap, cid, nonAnonymousAccess)
dbTable psValidator DBTable dbTable psValidator DBTable
{ dbtSQLQuery { dbtSQLQuery
@ -397,10 +414,11 @@ makeCorrectionsTable whereClause dbtColonnade dbtFilterUI psValidator dbtParams
Just needle -> E.maybe (E.val False :: E.SqlExpr (E.Value Bool)) (E.isInfixOf $ E.val needle) (submission E.^. SubmissionRatingComment) Just needle -> E.maybe (E.val False :: E.SqlExpr (E.Value Bool)) (E.isInfixOf $ E.val needle) (submission E.^. SubmissionRatingComment)
) )
, ( "submission" , ( "submission"
, FilterProjected $ \(DBRow{..} :: CorrectionTableData) (criteria :: Set Text) -> , FilterProjected (_corrProjFilterSubmission ?~)
let cid = map CI.mk . unpack . toPathPiece $ dbrOutput ^. _7 -- , FilterProjected $ \(DBRow{..} :: CorrectionTableData) (criteria :: Set Text) ->
criteria' = map CI.mk . unpack <$> Set.toList criteria -- let cid = map CI.mk . unpack . toPathPiece $ dbrOutput ^. _7
in any (`isInfixOf` cid) criteria' -- criteria' = map CI.mk . unpack <$> Set.toList criteria
-- in any (`isInfixOf` cid) criteria'
) )
] ]
, dbtFilterUI = fromMaybe mempty dbtFilterUI , dbtFilterUI = fromMaybe mempty dbtFilterUI
@ -662,7 +680,7 @@ postCorrectionsR = do
courses <- selectList [] [Asc CourseShorthand] >>= filterM (\(Entity _ Course{..}) -> (== Authorized) <$> evalAccessCorrector courseTerm courseSchool courseShorthand) courses <- selectList [] [Asc CourseShorthand] >>= filterM (\(Entity _ Course{..}) -> (== Authorized) <$> evalAccessCorrector courseTerm courseSchool courseShorthand)
optionsPairs $ map (id &&& id) $ nub $ map (CI.original . courseShorthand . entityVal) courses optionsPairs $ map (id &&& id) $ nub $ map (CI.original . courseShorthand . entityVal) courses
termOptions = runDB $ do termOptions = runDB $ do
courses <- selectList [] [Asc CourseTerm] >>= filterM (\(Entity _ Course{..}) -> (== Authorized) <$> evalAccessCorrector courseTerm courseSchool courseShorthand) courses <- selectList [] [Desc CourseTerm] >>= filterM (\(Entity _ Course{..}) -> (== Authorized) <$> evalAccessCorrector courseTerm courseSchool courseShorthand)
optionsPairs $ map (id &&& id) $ nub $ map (termToText . unTermKey . courseTerm . entityVal) courses optionsPairs $ map (id &&& id) $ nub $ map (termToText . unTermKey . courseTerm . entityVal) courses
schoolOptions = runDB $ do schoolOptions = runDB $ do
courses <- selectList [] [Asc CourseSchool] >>= filterM (\(Entity _ Course{..}) -> (== Authorized) <$> evalAccessCorrector courseTerm courseSchool courseShorthand) courses <- selectList [] [Asc CourseSchool] >>= filterM (\(Entity _ Course{..}) -> (== Authorized) <$> evalAccessCorrector courseTerm courseSchool courseShorthand)
@ -671,8 +689,8 @@ postCorrectionsR = do
psValidator = def psValidator = def
& restrictCorrector & restrictCorrector
& restrictAnonymous & restrictAnonymous
& defaultSorting [SortAscBy "israted", SortDescBy "ratingTime", SortAscBy "assignedtime" ] & defaultSorting [SortDescBy "ratingtime", SortAscBy "assignedtime" ]
-- & defaultFilter (Map.fromList [("israted",[toPathPiece False])]) -- DEPENDS ON ISSUE #371 UNCOMMENT THEN & defaultFilter (singletonMap "israted" [toPathPiece False])
correctionsR whereClause colonnade filterUI psValidator $ Map.fromList correctionsR whereClause colonnade filterUI psValidator $ Map.fromList
[ downloadAction [ downloadAction
] ]

View File

@ -184,12 +184,8 @@ postMessageListR = do
Just SystemMessageTranslation{..} -> (systemMessageTranslationSummary, systemMessageTranslationContent) Just SystemMessageTranslation{..} -> (systemMessageTranslationSummary, systemMessageTranslationContent)
in cell . toWidget $ fromMaybe content summary in cell . toWidget $ fromMaybe content summary
] ]
dbtProj DBRow{ dbrOutput = smE@(Entity smId _), .. } = do dbtProj = dbtProjSimple $ \smE@(Entity smId _) ->
smT <- (>>= view _2) <$> getSystemMessage smId (smE, ) . (>>= view _2) <$> getSystemMessage smId
return DBRow
{ dbrOutput = (smE, smT)
, ..
}
psValidator = def :: PSValidator (MForm Handler) (FormResult (Last ActionSystemMessageData, DBFormResult CryptoUUIDSystemMessage Bool MessageListData)) psValidator = def :: PSValidator (MForm Handler) (FormResult (Last ActionSystemMessageData, DBFormResult CryptoUUIDSystemMessage Bool MessageListData))
(tableRes', tableView) <- runDB $ dbTable psValidator DBTable (tableRes', tableView) <- runDB $ dbTable psValidator DBTable
{ dbtSQLQuery { dbtSQLQuery

View File

@ -76,7 +76,7 @@ getTermShowR = do
E.where_ $ term E.^. TermId E.==. course E.^. CourseTerm E.where_ $ term E.^. TermId E.==. course E.^. CourseTerm
E.&&. mayViewCourse muid ata now course Nothing E.&&. mayViewCourse muid ata now course Nothing
dbtRowKey = (E.^. TermId) dbtRowKey = (E.^. TermId)
dbtProj = return . dbrOutput dbtProj = dbrOutput <$> dbtProjId
dbtColonnade = widgetColonnade $ mconcat dbtColonnade = widgetColonnade $ mconcat
[ sortable (Just "term-id") (i18nCell MsgTermShort) $ \(Entity tid _, _) [ sortable (Just "term-id") (i18nCell MsgTermShort) $ \(Entity tid _, _)
-> cell $ do -> cell $ do

View File

@ -38,7 +38,7 @@ getCTutorialListR tid ssh csh = do
E.||. E.not_ (tutorial E.^. TutorialRoomHidden) E.||. E.not_ (tutorial E.^. TutorialRoomHidden)
return (tutorial, participants, showRoom) return (tutorial, participants, showRoom)
dbtRowKey = (E.^. TutorialId) dbtRowKey = (E.^. TutorialId)
dbtProj = return . over (_dbrOutput . _2) E.unValue . over (_dbrOutput . _3) E.unValue dbtProj = over (_dbrOutput . _2) E.unValue . over (_dbrOutput . _3) E.unValue <$> dbtProjId
dbtColonnade = dbColonnade $ mconcat dbtColonnade = dbColonnade $ mconcat
[ sortable (Just "type") (i18nCell MsgTableTutorialType) $ \(view $ resultTutorial . _entityVal -> Tutorial{..}) -> textCell $ CI.original tutorialType [ sortable (Just "type") (i18nCell MsgTableTutorialType) $ \(view $ resultTutorial . _entityVal -> Tutorial{..}) -> textCell $ CI.original tutorialType
, sortable (Just "name") (i18nCell MsgTableTutorialName) $ \(view $ resultTutorial . _entityVal -> Tutorial{..}) -> anchorCell (CTutorialR tid ssh csh tutorialName TUsersR) [whamlet|#{tutorialName}|] , sortable (Just "name") (i18nCell MsgTableTutorialName) $ \(view $ resultTutorial . _entityVal -> Tutorial{..}) -> anchorCell (CTutorialR tid ssh csh tutorialName TUsersR) [whamlet|#{tutorialName}|]

View File

@ -143,7 +143,7 @@ postUsersR = do
{ dbtSQLQuery = return :: E.SqlExpr (Entity User) -> E.SqlQuery (E.SqlExpr (Entity User)) { dbtSQLQuery = return :: E.SqlExpr (Entity User) -> E.SqlQuery (E.SqlExpr (Entity User))
, dbtRowKey = (E.^. UserId) , dbtRowKey = (E.^. UserId)
, dbtColonnade , dbtColonnade
, dbtProj = return , dbtProj = dbtProjId
, dbtSorting = Map.fromList , dbtSorting = Map.fromList
[ ( "name" [ ( "name"
, SortColumn $ \user -> user E.^. UserSurname , SortColumn $ \user -> user E.^. UserSurname

View File

@ -215,8 +215,8 @@ makeExternalExamUsersTable mode (Entity eeId ExternalExam{..}) = do
return (result, user, isSynced) return (result, user, isSynced)
dbtRowKey = views queryResult (E.^. ExternalExamResultId) dbtRowKey = views queryResult (E.^. ExternalExamResultId)
dbtProj :: DBRow _ -> DB ExternalExamUserTableData dbtProj :: _ ExternalExamUserTableData
dbtProj = runReaderT $ (asks . set _dbrOutput) <=< magnify _dbrOutput $ dbtProj = dbtProjSimple . runReaderT $
(,,,,) (,,,,)
<$> view _1 <*> view _2 <*> view (_3 . _Value) <$> view _1 <*> view _2 <*> view (_3 . _Value)
<*> getSynchronised <*> getSynchronised

View File

@ -70,10 +70,10 @@ type OpticSortColumn' focus
type OpticSortColumn val = OpticSortColumn' (E.SqlExpr (E.Value val)) type OpticSortColumn val = OpticSortColumn' (E.SqlExpr (E.Value val))
type OpticFilterColumn' t inp focus type OpticFilterColumn' t inp focus
= forall r' filterMap. = forall fs filterMap.
( IsMap filterMap ( IsMap filterMap
, ContainerKey filterMap ~ FilterKey , ContainerKey filterMap ~ FilterKey
, MapValue filterMap ~ FilterColumn t r' , MapValue filterMap ~ FilterColumn t fs
, IsFilterColumn t (t -> inp -> E.SqlExpr (E.Value Bool)) , IsFilterColumn t (t -> inp -> E.SqlExpr (E.Value Bool))
) )
=> (forall focus'. Getting focus' t focus) => (forall focus'. Getting focus' t focus)
@ -500,37 +500,37 @@ defaultSortingByName =
defaultSorting [SortAscBy "user-name"] -- new way, working with single sorter defaultSorting [SortAscBy "user-name"] -- new way, working with single sorter
-- | Alias for sortUserName for consistency -- | Alias for sortUserName for consistency
fltrUserNameLink :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d) => (a -> E.SqlExpr (Entity User)) -> (d, FilterColumn t r') fltrUserNameLink :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d) => (a -> E.SqlExpr (Entity User)) -> (d, FilterColumn t fs)
fltrUserNameLink = fltrUserName fltrUserNameLink = fltrUserName
fltrUserName :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d) fltrUserName :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d)
=> (a -> E.SqlExpr (Entity User)) => (a -> E.SqlExpr (Entity User))
-> (d, FilterColumn t r') -> (d, FilterColumn t fs)
fltrUserName queryUser = ( "user-name", FilterColumn $ mkContainsFilter queryName ) fltrUserName queryUser = ( "user-name", FilterColumn $ mkContainsFilter queryName )
where where
queryName = queryUser >>> (E.^. UserDisplayName) queryName = queryUser >>> (E.^. UserDisplayName)
fltrUserNameExact :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d) fltrUserNameExact :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d)
=> (a -> E.SqlExpr (Entity User)) => (a -> E.SqlExpr (Entity User))
-> (d, FilterColumn t r') -> (d, FilterColumn t fs)
fltrUserNameExact queryUser = ( "user-name", FilterColumn $ mkExactFilter queryName ) fltrUserNameExact queryUser = ( "user-name", FilterColumn $ mkExactFilter queryName )
where where
queryName = queryUser >>> (E.^. UserDisplayName) queryName = queryUser >>> (E.^. UserDisplayName)
fltrUserSurname :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d) fltrUserSurname :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d)
=> (a -> E.SqlExpr (Entity User)) => (a -> E.SqlExpr (Entity User))
-> (d, FilterColumn t r') -> (d, FilterColumn t fs)
fltrUserSurname queryUser = ( "user-surname", FilterColumn $ mkContainsFilter $ queryUser >>> (E.^. UserSurname)) fltrUserSurname queryUser = ( "user-surname", FilterColumn $ mkContainsFilter $ queryUser >>> (E.^. UserSurname))
fltrUserDisplayName :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d) fltrUserDisplayName :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d)
=> (a -> E.SqlExpr (Entity User)) => (a -> E.SqlExpr (Entity User))
-> (d, FilterColumn t r') -> (d, FilterColumn t fs)
fltrUserDisplayName queryUser = ( "user-display-name", FilterColumn $ mkContainsFilter $ queryUser >>> (E.^. UserDisplayName)) fltrUserDisplayName queryUser = ( "user-display-name", FilterColumn $ mkContainsFilter $ queryUser >>> (E.^. UserDisplayName))
-- | Search all names, i.e. DisplayName, Surname, EMail -- | Search all names, i.e. DisplayName, Surname, EMail
fltrUserNameEmail :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d) fltrUserNameEmail :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d)
=> (a -> E.SqlExpr (Entity User)) => (a -> E.SqlExpr (Entity User))
-> (d, FilterColumn t r') -> (d, FilterColumn t fs)
fltrUserNameEmail queryUser = ( "user-name-email", FilterColumn $ anyFilter fltrUserNameEmail queryUser = ( "user-name-email", FilterColumn $ anyFilter
[ mkContainsFilter $ queryUser >>> (E.^. UserDisplayName) [ mkContainsFilter $ queryUser >>> (E.^. UserDisplayName)
, mkContainsFilter $ queryUser >>> (E.^. UserSurname) , mkContainsFilter $ queryUser >>> (E.^. UserSurname)
@ -579,7 +579,7 @@ fltrUserMatriclenr :: ( IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bo
, IsString d , IsString d
) )
=> (a -> E.SqlExpr (Entity User)) => (a -> E.SqlExpr (Entity User))
-> (d, FilterColumn t r') -> (d, FilterColumn t fs)
fltrUserMatriclenr queryUser = ("user-matriclenumber", FilterColumn . mkContainsFilterWith Just $ queryUser >>> (E.^. UserMatrikelnummer)) fltrUserMatriclenr queryUser = ("user-matriclenumber", FilterColumn . mkContainsFilterWith Just $ queryUser >>> (E.^. UserMatrikelnummer))
fltrUserMatriclenrUI :: Maybe (Map FilterKey [Text]) -> AForm (YesodDB UniWorX) (Map FilterKey [Text]) fltrUserMatriclenrUI :: Maybe (Map FilterKey [Text]) -> AForm (YesodDB UniWorX) (Map FilterKey [Text])
@ -599,7 +599,7 @@ fltrUserEmail :: ( IsFilterColumn t (a -> Set (CI Text) -> E.SqlExpr (E.Value Bo
, IsString d , IsString d
) )
=> (a -> E.SqlExpr (Entity User)) => (a -> E.SqlExpr (Entity User))
-> (d, FilterColumn t r') -> (d, FilterColumn t fs)
fltrUserEmail queryUser = ("user-email", FilterColumn . mkContainsFilter $ queryUser >>> (E.^. UserEmail)) fltrUserEmail queryUser = ("user-email", FilterColumn . mkContainsFilter $ queryUser >>> (E.^. UserEmail))
fltrUserEmailUI :: Maybe (Map FilterKey [Text]) -> AForm (YesodDB UniWorX) (Map FilterKey [Text]) fltrUserEmailUI :: Maybe (Map FilterKey [Text]) -> AForm (YesodDB UniWorX) (Map FilterKey [Text])
@ -724,7 +724,7 @@ fltrFeaturesSemester :: ( IsFilterColumn t (a -> Set Int -> E.SqlExpr (E.Value B
, IsString d , IsString d
) )
=> (a -> E.SqlExpr (Maybe (Entity StudyFeatures))) => (a -> E.SqlExpr (Maybe (Entity StudyFeatures)))
-> (d, FilterColumn t r') -> (d, FilterColumn t fs)
fltrFeaturesSemester queryFeatures = ("features-semester", FilterColumn . mkExactFilterWith Just $ queryFeatures >>> (E.?. StudyFeaturesSemester)) fltrFeaturesSemester queryFeatures = ("features-semester", FilterColumn . mkExactFilterWith Just $ queryFeatures >>> (E.?. StudyFeaturesSemester))
fltrFeaturesSemesterUI :: Maybe (Map FilterKey [Text]) -> AForm (YesodDB UniWorX) (Map FilterKey [Text]) fltrFeaturesSemesterUI :: Maybe (Map FilterKey [Text]) -> AForm (YesodDB UniWorX) (Map FilterKey [Text])
@ -742,7 +742,7 @@ fltrField :: ( IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool))
, IsString d , IsString d
) )
=> (a -> E.SqlExpr (Maybe (Entity StudyTerms))) => (a -> E.SqlExpr (Maybe (Entity StudyTerms)))
-> (d, FilterColumn t r') -> (d, FilterColumn t fs)
fltrField queryFeatures = ( "terms" fltrField queryFeatures = ( "terms"
, FilterColumn $ anyFilter , FilterColumn $ anyFilter
[ mkContainsFilterWith Just $ queryFeatures >>> E.joinV . (E.?. StudyTermsName) [ mkContainsFilterWith Just $ queryFeatures >>> E.joinV . (E.?. StudyTermsName)
@ -763,10 +763,10 @@ sortDegreeShort :: IsString d => (t -> E.SqlExpr (Maybe (Entity StudyDegree))) -
sortDegreeShort queryTerms = ("degree-short", SortColumn $ queryTerms >>> (E.?. StudyDegreeShorthand)) sortDegreeShort queryTerms = ("degree-short", SortColumn $ queryTerms >>> (E.?. StudyDegreeShorthand))
fltrDegree :: ( IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)) fltrDegree :: ( IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool))
, IsString d , IsString d
) )
=> (a -> E.SqlExpr (Maybe (Entity StudyDegree))) => (a -> E.SqlExpr (Maybe (Entity StudyDegree)))
-> (d, FilterColumn t r') -> (d, FilterColumn t fs)
fltrDegree queryFeatures = ( "degree" fltrDegree queryFeatures = ( "degree"
, FilterColumn $ anyFilter , FilterColumn $ anyFilter
[ mkContainsFilterWith Just $ queryFeatures >>> E.joinV . (E.?. StudyDegreeName) [ mkContainsFilterWith Just $ queryFeatures >>> E.joinV . (E.?. StudyDegreeName)

View File

@ -1,4 +1,5 @@
{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
module Handler.Utils.Table.Pagination module Handler.Utils.Table.Pagination
( module Handler.Utils.Table.Pagination.Types ( module Handler.Utils.Table.Pagination.Types
@ -7,14 +8,19 @@ module Handler.Utils.Table.Pagination
, SortColumn(..), SortDirection(..) , SortColumn(..), SortDirection(..)
, SortingSetting(..) , SortingSetting(..)
, pattern SortAscBy, pattern SortDescBy , pattern SortAscBy, pattern SortDescBy
, FilterColumn(..), IsFilterColumn , FilterColumn(..), IsFilterColumn, IsFilterProjected
, mkFilterProjectedPost
, DBTProjFilterPost(..)
, DBRow(..), _dbrOutput, _dbrCount , DBRow(..), _dbrOutput, _dbrCount
, DBStyle(..), defaultDBSFilterLayout, DBEmptyStyle(..) , DBStyle(..), defaultDBSFilterLayout, DBEmptyStyle(..)
, module Handler.Utils.Table.Pagination.CsvColumnExplanations , module Handler.Utils.Table.Pagination.CsvColumnExplanations
, DBCsvActionMode(..) , DBCsvActionMode(..)
, DBCsvDiff(..), _DBCsvDiffNew, _DBCsvDiffExisting, _DBCsvDiffMissing, _dbCsvOldKey, _dbCsvOld, _dbCsvNewKey, _dbCsvNew , DBCsvDiff(..), _DBCsvDiffNew, _DBCsvDiffExisting, _DBCsvDiffMissing, _dbCsvOldKey, _dbCsvOld, _dbCsvNewKey, _dbCsvNew
, DBTCsvEncode(..), DBTCsvDecode(..), DBTExtraRep(..) , DBTCsvEncode(..), DBTCsvDecode(..), DBTExtraRep(..)
, DBTProjCtx(..), _dbtProjFilter, _dbtProjRow, _dbtProjRow'
, DBTable(..), DBFilterUI, IsDBTable(..), DBCell(..) , DBTable(..), DBFilterUI, IsDBTable(..), DBCell(..)
, dbtProjId, dbtProjSimple
, dbtProjFilteredPostId, dbtProjFilteredPostSimple
, noCsvEncode, simpleCsvEncode, simpleCsvEncodeM , noCsvEncode, simpleCsvEncode, simpleCsvEncodeM
, withCsvExtraRep , withCsvExtraRep
, singletonFilter , singletonFilter
@ -200,16 +206,50 @@ pattern SortDescBy :: SortingKey -> SortingSetting
pattern SortDescBy key = SortingSetting key SortDesc pattern SortDescBy key = SortingSetting key SortDesc
data FilterColumn t r' = forall a. IsFilterColumn t a => FilterColumn a type DBTableKey k' = (Show k', ToJSON k', FromJSON k', Ord k', Binary k', Typeable k')
| forall a. IsFilterProjected r' a => FilterProjected a data DBRow r = forall k'. DBTableKey k' => DBRow
{ dbrKey :: k'
, dbrOutput :: r
, dbrCount :: Int64
}
filterColumn :: FilterColumn t r' -> Maybe ([Text] -> t -> E.SqlExpr (E.Value Bool)) makeLenses_ ''DBRow
instance Functor DBRow where
fmap f DBRow{..} = DBRow{ dbrOutput = f dbrOutput, .. }
instance Foldable DBRow where
foldMap f DBRow{..} = f dbrOutput
instance Traversable DBRow where
traverse f DBRow{..} = DBRow <$> pure dbrKey <*> f dbrOutput <*> pure dbrCount
newtype DBTProjFilterPost r' = DBTProjFilterPost { unDBTProjFilterPost :: r' -> DB Bool }
instance Default (DBTProjFilterPost r') where
def = mempty
instance Semigroup (DBTProjFilterPost r') where
DBTProjFilterPost f <> DBTProjFilterPost g = DBTProjFilterPost $ \r' -> f r' `and2M` g r'
instance Monoid (DBTProjFilterPost r') where
mempty = DBTProjFilterPost . const $ return True
data FilterColumn t fs = forall a. IsFilterColumn t a => FilterColumn a
| forall a. IsFilterProjected fs a => FilterProjected a
filterColumn :: FilterColumn t fs -> Maybe ([Text] -> t -> E.SqlExpr (E.Value Bool))
filterColumn (FilterColumn f) = Just $ filterColumn' f filterColumn (FilterColumn f) = Just $ filterColumn' f
filterColumn _ = Nothing filterColumn _ = Nothing
filterProjected :: FilterColumn t r' -> r' -> [Text] -> DB Bool filterProjected :: FilterColumn t fs -> [Text] -> (fs -> fs)
filterProjected (FilterProjected f) = flip $ filterProjected' f filterProjected (FilterProjected f) = filterProjected' f
filterProjected _ = \_ _ -> return True filterProjected _ = const id
mkFilterProjectedPost :: forall r' a t. IsFilterProjectedPost r' a => a -> FilterColumn t (DBTProjFilterPost r')
mkFilterProjectedPost fin = FilterProjected $ \(ts :: [Text]) -> (<> filterProjectedPost' @r' fin ts)
class IsFilterColumn t a where class IsFilterColumn t a where
filterColumn' :: a -> [Text] -> t -> E.SqlExpr (E.Value Bool) filterColumn' :: a -> [Text] -> t -> E.SqlExpr (E.Value Bool)
@ -223,21 +263,33 @@ instance IsFilterColumn t cont => IsFilterColumn t (t -> cont) where
instance {-# OVERLAPPABLE #-} (PathPiece (Element l), IsFilterColumn t cont, MonoPointed l, Monoid l) => IsFilterColumn t (l -> cont) where instance {-# OVERLAPPABLE #-} (PathPiece (Element l), IsFilterColumn t cont, MonoPointed l, Monoid l) => IsFilterColumn t (l -> cont) where
filterColumn' cont is' = filterColumn' (cont $ is' ^. mono' _PathPiece) is' filterColumn' cont is' = filterColumn' (cont $ is' ^. mono' _PathPiece) is'
class IsFilterProjected r' a where class IsFilterProjected fs a where
filterProjected' :: a -> [Text] -> r' -> DB Bool filterProjected' :: a -> [Text] -> (fs -> fs)
instance IsFilterProjected r' (ReaderT SqlBackend (HandlerFor UniWorX) Bool) where instance IsFilterProjected fs (fs -> fs) where
filterProjected' fin _ _ = fin filterProjected' fin _ = fin
instance IsFilterProjected r' Bool where instance {-# OVERLAPPABLE #-} (PathPiece (Element l), IsFilterProjected fs cont, MonoPointed l, Monoid l) => IsFilterProjected fs (l -> cont) where
filterProjected' fin _ _ = return fin
instance IsFilterProjected r' cont => IsFilterProjected r' (r' -> cont) where
filterProjected' cont is' r = filterProjected' (cont r) is' r
instance {-# OVERLAPPABLE #-} (PathPiece (Element l), IsFilterProjected r' cont, MonoPointed l, Monoid l) => IsFilterProjected r' (l -> cont) where
filterProjected' cont is' = filterProjected' (cont $ is' ^. mono' _PathPiece) is' filterProjected' cont is' = filterProjected' (cont $ is' ^. mono' _PathPiece) is'
class IsFilterProjectedPost r' a where
filterProjectedPost' :: a -> [Text] -> DBTProjFilterPost r'
instance IsFilterProjectedPost r' Bool where
filterProjectedPost' fin _ = DBTProjFilterPost . const $ return fin
instance IsFilterProjectedPost r' (ReaderT SqlBackend (HandlerFor UniWorX) Bool) where
filterProjectedPost' fin _ = DBTProjFilterPost $ const fin
instance IsFilterProjectedPost r' (DBTProjFilterPost r') where
filterProjectedPost' fin _ = fin
instance IsFilterProjectedPost r' cont => IsFilterProjectedPost r' (r' -> cont) where
filterProjectedPost' cont is' = DBTProjFilterPost $ \r' -> let DBTProjFilterPost cont' = filterProjectedPost' (cont r') is' in cont' r'
instance {-# OVERLAPPABLE #-} (PathPiece (Element l), IsFilterProjectedPost r' cont, MonoPointed l, Monoid l) => IsFilterProjectedPost r' (l -> cont) where
filterProjectedPost' cont is' = filterProjectedPost' (cont $ is' ^. mono' _PathPiece) is'
data PagesizeLimit = PagesizeLimit !Int64 | PagesizeAll data PagesizeLimit = PagesizeLimit !Int64 | PagesizeAll
deriving (Eq, Ord, Read, Show, Generic) deriving (Eq, Ord, Read, Show, Generic)
@ -431,24 +483,17 @@ makeLenses_ ''DBCsvException
instance (Typeable k', Show k') => Exception (DBCsvException k') instance (Typeable k', Show k') => Exception (DBCsvException k')
type DBTableKey k' = (Show k', ToJSON k', FromJSON k', Ord k', Binary k', Typeable k') data DBTProjCtx fs r = DBTProjCtx
data DBRow r = forall k'. DBTableKey k' => DBRow { dbtProjFilter :: fs
{ dbrKey :: k' , dbtProjRow :: DBRow r
, dbrOutput :: r
, dbrCount :: Int64
} }
makeLenses_ ''DBRow makeLenses_ ''DBTProjCtx
instance Functor DBRow where _dbtProjRow' :: Lens' (DBTProjCtx () r) (DBRow r)
fmap f DBRow{..} = DBRow{ dbrOutput = f dbrOutput, .. } _dbtProjRow' = _dbtProjRow
instance Foldable DBRow where
foldMap f DBRow{..} = f dbrOutput
instance Traversable DBRow where
traverse f DBRow{..} = DBRow <$> pure dbrKey <*> f dbrOutput <*> pure dbrCount
newtype PSValidator m x = PSValidator { runPSValidator :: DBTable m x -> Maybe PaginationInput -> ([SomeMessage UniWorX], PaginationSettings) } newtype PSValidator m x = PSValidator { runPSValidator :: DBTable m x -> Maybe PaginationInput -> ([SomeMessage UniWorX], PaginationSettings) }
@ -579,7 +624,7 @@ singletonFilter key = prism' fromInner (fmap Just . fromOuter)
fromInner = maybe Map.empty $ Map.singleton key . pure fromInner = maybe Map.empty $ Map.singleton key . pure
fromOuter = Map.lookup key >=> listToMaybe fromOuter = Map.lookup key >=> listToMaybe
data DBTCsvEncode r' k' csv = forall exportData filename sheetName. data DBTCsvEncode r' k' csv = forall exportData filename sheetName.
( ToNamedRecord csv, CsvColumnsExplained csv ( ToNamedRecord csv, CsvColumnsExplained csv
, DBTableKey k' , DBTableKey k'
@ -628,19 +673,20 @@ data DBTCsvDecode r' k' csv = forall route csvAction csvActionClass csvException
, dbtCsvRenderException :: csvException -> DB Text , dbtCsvRenderException :: csvException -> DB Text
} }
data DBTable m x = forall a r r' h i t k k' csv colonnade (p :: Pillar). data DBTable m x = forall a r r' h i t k k' csv colonnade (p :: Pillar) fs.
( ToSortable h, Functor h ( ToSortable h, Functor h
, E.SqlSelect a r, E.SqlIn k k', DBTableKey k' , E.SqlSelect a r, E.SqlIn k k', DBTableKey k'
, PathPiece i, Eq i , PathPiece i, Eq i
, E.From t , E.From t
, AsCornice h p r' (DBCell m x) colonnade , AsCornice h p r' (DBCell m x) colonnade
, Default fs
) => DBTable ) => DBTable
{ dbtSQLQuery :: t -> E.SqlQuery a { dbtSQLQuery :: t -> E.SqlQuery a
, dbtRowKey :: t -> k -- ^ required for table forms; always same key for repeated requests. For joins: return unique tuples. , dbtRowKey :: t -> k -- ^ required for table forms; always same key for repeated requests. For joins: return unique tuples.
, dbtProj :: DBRow r -> DB r' , dbtProj :: ReaderT (DBTProjCtx fs r) (MaybeT (ReaderT SqlBackend (HandlerFor UniWorX))) r'
, dbtColonnade :: colonnade , dbtColonnade :: colonnade
, dbtSorting :: Map SortingKey (SortColumn t r') , dbtSorting :: Map SortingKey (SortColumn t r')
, dbtFilter :: Map FilterKey (FilterColumn t r') , dbtFilter :: Map FilterKey (FilterColumn t fs)
, dbtFilterUI :: DBFilterUI , dbtFilterUI :: DBFilterUI
, dbtStyle :: DBStyle r' , dbtStyle :: DBStyle r'
, dbtParams :: DBParams m x , dbtParams :: DBParams m x
@ -652,6 +698,50 @@ data DBTable m x = forall a r r' h i t k k' csv colonnade (p :: Pillar).
type DBFilterUI = Maybe (Map FilterKey [Text]) -> AForm DB (Map FilterKey [Text]) type DBFilterUI = Maybe (Map FilterKey [Text]) -> AForm DB (Map FilterKey [Text])
dbtProjId' :: forall fs r r'.
DBRow r ~ r'
=> ReaderT (DBTProjCtx fs r) (MaybeT (ReaderT SqlBackend (HandlerFor UniWorX))) r'
dbtProjId' = view _dbtProjRow
dbtProjId :: forall fs r r'.
( fs ~ (), DBRow r ~ r' )
=> ReaderT (DBTProjCtx fs r) (MaybeT (ReaderT SqlBackend (HandlerFor UniWorX))) r'
dbtProjId = dbtProjId'
dbtProjSimple' :: forall fs r r' r''.
DBRow r'' ~ r'
=> (r -> DB r'')
-> ReaderT (DBTProjCtx fs r) (MaybeT (ReaderT SqlBackend (HandlerFor UniWorX))) r'
dbtProjSimple' cont = (views _dbtProjRow . set _dbrOutput) <=< (hoist lift . magnify (_dbtProjRow . _dbrOutput)) $ lift . cont =<< ask
dbtProjSimple :: forall fs r r' r''.
( fs ~ (), DBRow r'' ~ r' )
=> (r -> DB r'')
-> ReaderT (DBTProjCtx fs r) (MaybeT (ReaderT SqlBackend (HandlerFor UniWorX))) r'
dbtProjSimple = dbtProjSimple'
withFilteredPost :: forall fs r r'.
fs ~ DBTProjFilterPost r'
=> ReaderT (DBTProjCtx fs r) (MaybeT (ReaderT SqlBackend (HandlerFor UniWorX))) r'
-> ReaderT (DBTProjCtx fs r) (MaybeT (ReaderT SqlBackend (HandlerFor UniWorX))) r'
withFilteredPost proj = do
r' <- proj
p <- views _dbtProjFilter unDBTProjFilterPost
guardM . lift . lift $ p r'
return r'
dbtProjFilteredPostId :: forall fs r r'.
( fs ~ DBTProjFilterPost r', DBRow r ~ r' )
=> ReaderT (DBTProjCtx fs r) (MaybeT (ReaderT SqlBackend (HandlerFor UniWorX))) r'
dbtProjFilteredPostId = withFilteredPost dbtProjId'
dbtProjFilteredPostSimple :: forall fs r r' r''.
( fs ~ DBTProjFilterPost r', DBRow r'' ~ r' )
=> (r -> DB r'')
-> ReaderT (DBTProjCtx fs r) (MaybeT (ReaderT SqlBackend (HandlerFor UniWorX))) r'
dbtProjFilteredPostSimple = withFilteredPost . dbtProjSimple'
noCsvEncode :: Maybe (DBTCsvEncode r' k' Void) noCsvEncode :: Maybe (DBTCsvEncode r' k' Void)
noCsvEncode = Nothing noCsvEncode = Nothing
@ -1115,7 +1205,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
| otherwise | otherwise
= id = id
allFilterProjected r' = lift $ getAll <$> foldMapM (\(f, args) -> All <$> filterProjected f r' args) psFilter' dbtProjFilter = ala Endo foldMap (psFilter' <&> \(f, args) -> filterProjected f args) def
sortProjected sortProjected
| is _Just previousKeys | is _Just previousKeys
@ -1130,7 +1220,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
adjustOrder SortDesc EQ = EQ adjustOrder SortDesc EQ = EQ
adjustOrder SortDesc GT = LT adjustOrder SortDesc GT = LT
(currentKeys, rows) <- fmap (unzip . sortProjected) . mapMaybeM' (assertMM allFilterProjected . lift . dbtProj) . map (\(E.Value dbrCount, dbrKey, dbrOutput) -> (dbrKey, DBRow{..})) $ reproduceSorting rows' (currentKeys, rows) <- fmap (unzip . sortProjected) . mapMaybeM' (\dbtProjRow -> runReaderT dbtProj DBTProjCtx{..}) . map (\(E.Value dbrCount, dbrKey, dbrOutput) -> (dbrKey, DBRow{..})) $ reproduceSorting rows'
csvExample <- runMaybeT $ do csvExample <- runMaybeT $ do
DBTCsvEncode{..} <- hoistMaybe dbtCsvEncode DBTCsvEncode{..} <- hoistMaybe dbtCsvEncode

View File

@ -73,6 +73,7 @@ workflowEdgeForm :: ( MonadHandler m
, MonadHandler m' , MonadHandler m'
, HandlerSite m' ~ UniWorX , HandlerSite m' ~ UniWorX
, MonadUnliftIO m' , MonadUnliftIO m'
, MonadThrow m'
) )
=> Either WorkflowInstanceId WorkflowWorkflowId => Either WorkflowInstanceId WorkflowWorkflowId
-> Maybe WorkflowEdgeForm -> Maybe WorkflowEdgeForm
@ -80,7 +81,7 @@ workflowEdgeForm :: ( MonadHandler m
workflowEdgeForm mwwId mPrev = runMaybeT $ do workflowEdgeForm mwwId mPrev = runMaybeT $ do
MsgRenderer mr <- getMsgRenderer MsgRenderer mr <- getMsgRenderer
ctx' <- bitraverse (MaybeT . getEntity) (MaybeT . getEntity) mwwId ctx' <- bitraverse (MaybeT . getEntity) (MaybeT . getWorkflowWorkflowState) mwwId
let (scope, sharedGraphId) = case ctx' of let (scope, sharedGraphId) = case ctx' of
Left (Entity _ WorkflowInstance{..}) -> ( _DBWorkflowScope # workflowInstanceScope Left (Entity _ WorkflowInstance{..}) -> ( _DBWorkflowScope # workflowInstanceScope
, workflowInstanceGraph , workflowInstanceGraph

View File

@ -97,4 +97,3 @@ sourceWorkflowActionInfos wwId wState = do
let authCheck WorkflowActionInfo{..} let authCheck WorkflowActionInfo{..}
= mayViewWorkflowAction mAuthId wwId waiAction = mayViewWorkflowAction mAuthId wwId waiAction
yieldMany (workflowActionInfos wState) .| C.filterM authCheck yieldMany (workflowActionInfos wState) .| C.filterM authCheck

View File

@ -66,7 +66,7 @@ getAdminWorkflowDefinitionListR = do
return (workflowDefinition, workflowInstanceCount, workflowCount) return (workflowDefinition, workflowInstanceCount, workflowCount)
dbtRowKey = (E.^. WorkflowDefinitionId) dbtRowKey = (E.^. WorkflowDefinitionId)
dbtProj = runReaderT $ (asks . set _dbrOutput) <=< magnify _dbrOutput $ do dbtProj = dbtProjFilteredPostSimple . runReaderT $ do
wd@(Entity wdId _) <- view _1 wd@(Entity wdId _) <- view _1
descLangs <- lift . E.select . E.from $ \workflowDefinitionDescription -> do descLangs <- lift . E.select . E.from $ \workflowDefinitionDescription -> do
E.where_ $ workflowDefinitionDescription E.^. WorkflowDefinitionDescriptionDefinition E.==. E.val wdId E.where_ $ workflowDefinitionDescription E.^. WorkflowDefinitionDescriptionDefinition E.==. E.val wdId
@ -116,8 +116,8 @@ getAdminWorkflowDefinitionListR = do
dbtFilter = mconcat dbtFilter = mconcat
[ singletonMap "name" . FilterColumn $ E.mkContainsFilter (E.^. WorkflowDefinitionName) [ singletonMap "name" . FilterColumn $ E.mkContainsFilter (E.^. WorkflowDefinitionName)
, singletonMap "scope" . FilterColumn $ E.mkExactFilter (E.^. WorkflowDefinitionScope) , singletonMap "scope" . FilterColumn $ E.mkExactFilter (E.^. WorkflowDefinitionScope)
, singletonMap "title" . FilterProjected $ \(ts :: Set Text) (view $ resultDescription . _entityVal . _workflowDefinitionDescriptionTitle -> t) -> oany ((flip isInfixOf `on` CI.foldCase) t) ts , singletonMap "title" . mkFilterProjectedPost $ \(ts :: Set Text) (view $ resultDescription . _entityVal . _workflowDefinitionDescriptionTitle -> t) -> oany ((flip isInfixOf `on` CI.foldCase) t) ts
, singletonMap "instance-title" . FilterProjected $ \(ts :: Set Text) (view $ resultInstanceDescription . _entityVal . _workflowDefinitionInstanceDescriptionTitle -> t) -> oany ((flip isInfixOf `on` CI.foldCase) t) ts , singletonMap "instance-title" . mkFilterProjectedPost $ \(ts :: Set Text) (view $ resultInstanceDescription . _entityVal . _workflowDefinitionInstanceDescriptionTitle -> t) -> oany ((flip isInfixOf `on` CI.foldCase) t) ts
] ]
dbtFilterUI mPrev = mconcat dbtFilterUI mPrev = mconcat
[ prismAForm (singletonFilter "name") mPrev $ aopt textField (fslI MsgWorkflowDefinitionName) [ prismAForm (singletonFilter "name") mPrev $ aopt textField (fslI MsgWorkflowDefinitionName)

View File

@ -75,11 +75,8 @@ getAdminWorkflowInstanceListR = do
return (workflowInstance, workflowCount) return (workflowInstance, workflowCount)
dbtRowKey = (E.^. WorkflowInstanceId) dbtRowKey = (E.^. WorkflowInstanceId)
dbtProj = runReaderT $ (asks . set _dbrOutput) <=< magnify _dbrOutput $ do dbtProj = dbtProjFilteredPostSimple $ \(wi@(Entity wiId _), E.Value iCount) ->
wi@(Entity wiId _) <- view _1 (wi, , iCount) <$> selectWorkflowInstanceDescription wiId
desc <- lift $ selectWorkflowInstanceDescription wiId
(wi, desc,)
<$> view (_2 . _Value)
dbtColonnade :: Colonnade Sortable WorkflowInstanceData _ dbtColonnade :: Colonnade Sortable WorkflowInstanceData _
dbtColonnade = mconcat dbtColonnade = mconcat
[ sortable (Just "name") (i18nCell MsgWorkflowInstanceName) . anchorEdit $ views (resultWorkflowInstance . _entityVal . _workflowInstanceName) i18n [ sortable (Just "name") (i18nCell MsgWorkflowInstanceName) . anchorEdit $ views (resultWorkflowInstance . _entityVal . _workflowInstanceName) i18n
@ -105,7 +102,7 @@ getAdminWorkflowInstanceListR = do
dbtFilter = mconcat dbtFilter = mconcat
[ singletonMap "name" . FilterColumn $ E.mkContainsFilter (E.^. WorkflowInstanceName) [ singletonMap "name" . FilterColumn $ E.mkContainsFilter (E.^. WorkflowInstanceName)
, singletonMap "scope" . FilterColumn $ E.mkExactFilter (E.^. WorkflowInstanceScope) , singletonMap "scope" . FilterColumn $ E.mkExactFilter (E.^. WorkflowInstanceScope)
, singletonMap "title" . FilterProjected $ \(ts :: Set Text) (view $ resultDescription . _entityVal . _workflowInstanceDescriptionTitle -> t) -> oany ((flip isInfixOf `on` CI.foldCase) t) ts , singletonMap "title" . mkFilterProjectedPost $ \(ts :: Set Text) (view $ resultDescription . _entityVal . _workflowInstanceDescriptionTitle -> t) -> oany ((flip isInfixOf `on` CI.foldCase) t) ts
] ]
dbtFilterUI mPrev = mconcat dbtFilterUI mPrev = mconcat
[ prismAForm (singletonFilter "name") mPrev $ aopt textField (fslI MsgWorkflowInstanceName) [ prismAForm (singletonFilter "name") mPrev $ aopt textField (fslI MsgWorkflowInstanceName)

View File

@ -41,6 +41,24 @@ import qualified Control.Monad.State.Class as State
import qualified Data.RFC5051 as RFC5051 import qualified Data.RFC5051 as RFC5051
data WorkflowWorkflowListFilterProj = WorkflowWorkflowListFilterProj
{ wwProjFilterMayAccess :: Maybe Bool
, wwProjFilterWorkflowWorkflow :: Maybe [[CI Char]]
, wwProjFilterCurrentState :: Maybe [[CI Char]]
, wwProjFilterFinal :: Maybe Bool
}
instance Default WorkflowWorkflowListFilterProj where
def = WorkflowWorkflowListFilterProj
{ wwProjFilterMayAccess = Nothing
, wwProjFilterWorkflowWorkflow = Nothing
, wwProjFilterCurrentState = Nothing
, wwProjFilterFinal = Nothing
}
makeLenses_ ''WorkflowWorkflowListFilterProj
getGlobalWorkflowWorkflowListR :: Handler Html getGlobalWorkflowWorkflowListR :: Handler Html
getGlobalWorkflowWorkflowListR = workflowWorkflowListR WSGlobal getGlobalWorkflowWorkflowListR = workflowWorkflowListR WSGlobal
@ -231,15 +249,24 @@ workflowWorkflowList (title, heading) WWListColumns{..} sqlPred = do
lift <=< asks $ E.where_ . sqlPred lift <=< asks $ E.where_ . sqlPred
return (workflowWorkflow, workflowInstance) return (workflowWorkflow, workflowInstance)
dbtRowKey = views queryWorkflowWorkflow (E.^. WorkflowWorkflowId) dbtRowKey = views queryWorkflowWorkflow (E.^. WorkflowWorkflowId)
dbtProj = runReaderT $ (asks . set _dbrOutput) <=< magnify _dbrOutput $ do dbtProj = (views _dbtProjRow . set _dbrOutput) =<< do
ww@(Entity wwId WorkflowWorkflow{..}) <- view _1 ww@(Entity wwId WorkflowWorkflow{..}) <- view $ _dbtProjRow . _dbrOutput . _1
mwi <- view _2 mwi <- view $ _dbtProjRow . _dbrOutput . _2
wiDesc <- lift . runMaybeT $ do
cID <- encrypt wwId
forMM_ (view $ _dbtProjFilter . _wwProjFilterWorkflowWorkflow) $ \criteria ->
let haystack = map CI.mk . unpack $ toPathPiece cID
in guard $ any (`isInfixOf` haystack) criteria
rScope <- lift . lift . runMaybeT . toRouteWorkflowScope $ _DBWorkflowScope # workflowWorkflowScope
forMM_ (view $ _dbtProjFilter . _wwProjFilterMayAccess) $ \needle -> do
rScope' <- hoistMaybe rScope
guardM . lift . lift $ (== needle) . is _Authorized <$> evalAccess (_WorkflowScopeRoute # (rScope', WorkflowWorkflowR cID WWWorkflowR)) False
wiDesc <- lift . lift . $cachedHereBinary (entityKey <$> mwi) . runMaybeT $ do
Entity wiId _ <- hoistMaybe mwi Entity wiId _ <- hoistMaybe mwi
MaybeT $ selectWorkflowInstanceDescription wiId MaybeT $ selectWorkflowInstanceDescription wiId
cID <- encrypt wwId WorkflowGraph{..} <- lift . lift . getSharedIdWorkflowGraph $ ww ^. _entityVal . _workflowWorkflowGraph
rScope <- lift . runMaybeT . toRouteWorkflowScope $ _DBWorkflowScope # workflowWorkflowScope
WorkflowGraph{..} <- lift . getSharedIdWorkflowGraph $ ww ^. _entityVal . _workflowWorkflowGraph
let hasWorkflowRole' :: WorkflowRole UserId -> DB Bool let hasWorkflowRole' :: WorkflowRole UserId -> DB Bool
hasWorkflowRole' role = maybeT (return False) $ do hasWorkflowRole' role = maybeT (return False) $ do
rScope' <- hoistMaybe rScope rScope' <- hoistMaybe rScope
@ -247,7 +274,7 @@ workflowWorkflowList (title, heading) WWListColumns{..} sqlPred = do
lift . $cachedHereBinary (wwId, role) $ is _Authorized <$> hasWorkflowRole (Just wwId) role canonRoute False lift . $cachedHereBinary (wwId, role) $ is _Authorized <$> hasWorkflowRole (Just wwId) role canonRoute False
let let
goAction p w = lift . go $ ww ^? _entityVal . _workflowWorkflowState . from _DBWorkflowState . p goAction p w = lift . lift . go $ ww ^? _entityVal . _workflowWorkflowState . from _DBWorkflowState . p
where where
go Nothing = return Nothing go Nothing = return Nothing
go (Just (act, newSt)) = maybeT (go $ newSt ^? _nullable . p) $ do go (Just (act, newSt)) = maybeT (go $ newSt ^? _nullable . p) $ do
@ -264,7 +291,7 @@ workflowWorkflowList (title, heading) WWListColumns{..} sqlPred = do
| otherwise -> maybeT (return WHIAHidden) $ do | otherwise -> maybeT (return WHIAHidden) $ do
viewActors <- hoistMaybe $ preview _wgeViewActor =<< mVia viewActors <- hoistMaybe $ preview _wgeViewActor =<< mVia
guardM . lift $ anyM (otoList viewActors) hasWorkflowRole' guardM . lift $ anyM (otoList viewActors) hasWorkflowRole'
resUser <- lift $ traverse getEntity wpUser' resUser <- lift . for wpUser' $ \uid -> $cachedHereBinary uid $ getEntity uid
return $ case resUser of return $ case resUser of
Nothing -> WHIAOther Nothing Nothing -> WHIAOther Nothing
Just Nothing -> WHIAGone Just Nothing -> WHIAGone
@ -280,7 +307,7 @@ workflowWorkflowList (title, heading) WWListColumns{..} sqlPred = do
<*> pure actFinal <*> pure actFinal
lastAct <- descAction $ re _nullable . _Snoc . swapped lastAct <- descAction $ re _nullable . _Snoc . swapped
persons' <- lift . flip (execStateT @_ @(Set UserId, Map WorkflowPayloadLabel (Set UserId))) mempty . forM_ (ww ^.. _entityVal . _workflowWorkflowState . from _DBWorkflowState . re _nullable . folded) $ \act -> maybeT_ . forM_ (join $ wpUser act) $ \wpUser' -> do persons' <- lift . lift . flip (execStateT @_ @(Set UserId, Map WorkflowPayloadLabel (Set UserId))) mempty . forM_ (ww ^.. _entityVal . _workflowWorkflowState . from _DBWorkflowState . re _nullable . folded) $ \act -> maybeT_ . forM_ (join $ wpUser act) $ \wpUser' -> do
let mVia = Map.lookup (wpVia act) . wgnEdges =<< Map.lookup (wpTo act) wgNodes let mVia = Map.lookup (wpVia act) . wgnEdges =<< Map.lookup (wpTo act) wgNodes
guardM . lift . lift $ mayViewWorkflowAction mAuthId wwId act guardM . lift . lift $ mayViewWorkflowAction mAuthId wwId act
lift . maybeT_ . hoist (zoom _1) $ do lift . maybeT_ . hoist (zoom _1) $ do
@ -296,12 +323,12 @@ workflowWorkflowList (title, heading) WWListColumns{..} sqlPred = do
guardM . lift . lift $ anyM (otoList wpvViewers) hasWorkflowRole' guardM . lift . lift $ anyM (otoList wpvViewers) hasWorkflowRole'
at pLbl ?= users at pLbl ?= users
persons <- lift . mapMaybeM (MaybeT . getEntity) . toList $ view _1 persons' <> view (_2 . folded) persons' persons <- lift . lift . mapMaybeM (MaybeT . getEntity) . toList $ view _1 persons' <> view (_2 . folded) persons'
return (cID, rScope, ww, mwi, wiDesc, lastAct, persons) return (cID, rScope, ww, mwi, wiDesc, lastAct, persons)
dbtColonnade :: Colonnade Sortable _ _ dbtColonnade :: Colonnade Sortable _ _
dbtColonnade = mconcat -- TODO: columns dbtColonnade = mconcat -- TODO: columns
[ sortable (Just "workflow-workflow") (i18nCell MsgWorkflowWorkflowListNumber) . (addCellClass ("cryptoid" :: Text) .) . anchorWorkflowWorkflow . views resultWorkflowWorkflowId $ toWidget . toPathPiece [ sortable (Just "workflow-workflow") (i18nCell MsgWorkflowWorkflowListNumber) . (addCellClass ("cryptoid" :: Text) .) . anchorWorkflowWorkflow . views resultWorkflowWorkflowId $ toWidget . (toPathPiece :: CryptoFileNameWorkflowWorkflow -> Text)
, guardMonoid wwListColumnScope . sortable (Just "scope") (i18nCell MsgWorkflowWorkflowListScope) $ \x -> foldMap (\t -> anchorWorkflowScope (const $ i18n t :: _ -> Widget) x) $ view resultRouteScope x , guardMonoid wwListColumnScope . sortable (Just "scope") (i18nCell MsgWorkflowWorkflowListScope) $ \x -> foldMap (\t -> anchorWorkflowScope (const $ i18n t :: _ -> Widget) x) $ view resultRouteScope x
, guardMonoid wwListColumnInstance . sortable (Just "instance") (i18nCell MsgWorkflowWorkflowListInstance) $ \x -> foldMap (\t -> anchorWorkflowInstance (const t) x) $ preview resultWorkflowInstanceTitle x , guardMonoid wwListColumnInstance . sortable (Just "instance") (i18nCell MsgWorkflowWorkflowListInstance) $ \x -> foldMap (\t -> anchorWorkflowInstance (const t) x) $ preview resultWorkflowInstanceTitle x
, sortable Nothing (i18nCell MsgWorkflowWorkflowListPersons) $ \x -> , sortable Nothing (i18nCell MsgWorkflowWorkflowListPersons) $ \x ->
@ -352,23 +379,37 @@ workflowWorkflowList (title, heading) WWListColumns{..} sqlPred = do
, singletonMap "final" . SortProjected . comparing $ \x -> guardOnM (has (resultLastAction . _Just . actionTo . _Just) x) (x ^? resultLastAction . _Just . actionFinal . _Just) , singletonMap "final" . SortProjected . comparing $ \x -> guardOnM (has (resultLastAction . _Just . actionTo . _Just) x) (x ^? resultLastAction . _Just . actionFinal . _Just)
] ]
dbtFilter = mconcat dbtFilter = mconcat
[ singletonMap "workflow-workflow" . FilterProjected $ \x (criteria :: Set Text) -> [ singletonMap "workflow-workflow" . FilterProjected $ \(criteria :: Set Text) ->
let cid = map CI.mk . unpack . toPathPiece $ x ^. resultWorkflowWorkflowId
criteria' = map CI.mk . unpack <$> Set.toList criteria
in any (`isInfixOf` cid) criteria'
, singletonMap "may-access" . FilterProjected $ \x (Any b) -> fmap (== b) . maybeT (return False) $ do
let cID = x ^. resultWorkflowWorkflowId
rScope <- hoistMaybe $ x ^. resultRouteScope
lift . lift $ is _Authorized <$> evalAccess (_WorkflowScopeRoute # (rScope, WorkflowWorkflowR cID WWWorkflowR)) False :: MaybeT (YesodDB UniWorX) Bool
, singletonMap "current-state" . FilterProjected $ \x (criteria :: Set Text) ->
let criteria' = map CI.mk . unpack <$> Set.toList criteria let criteria' = map CI.mk . unpack <$> Set.toList criteria
in maybe False (\cSt -> any (`isInfixOf` cSt) criteria') $ x ^? resultLastAction . _Just . actionTo . _Just . to (map CI.mk . unpack) in _wwProjFilterWorkflowWorkflow ?~ criteria'
, singletonMap "final" . FilterProjected $ \x (criterion :: Monoid.Last Bool) -> case Monoid.getLast criterion of , singletonMap "current-state" . FilterProjected $ \(criteria :: Set Text) -> -- TODO
Nothing -> True let criteria' = map CI.mk . unpack <$> Set.toList criteria
Just needle -> let val = has (resultLastAction . _Just . actionTo . _Just) x in _wwProjFilterCurrentState ?~ criteria'
&& has (resultLastAction . _Just . actionFinal . _Just) x , singletonMap "final" . FilterProjected $ \(criterion :: Monoid.Last Bool) -> case Monoid.getLast criterion of -- TODO
in needle == val Nothing -> id
Just needle -> _wwProjFilterFinal ?~ needle
, singletonMap "may-access" . FilterProjected $ \(Any criterion) -> _wwProjFilterMayAccess ?~ criterion
] ]
-- [ singletonMap "workflow-workflow" . FilterProjected $ \x (criteria :: Set Text) ->
-- let cid = map CI.mk . unpack . toPathPiece $ x ^. resultWorkflowWorkflowId
-- criteria' = map CI.mk . unpack <$> Set.toList criteria
-- in any (`isInfixOf` cid) criteria'
-- ,
-- , singletonMap "may-access" . FilterPreProjected $ \(x :: DBRow (Entity WorkflowWorkflow, Maybe (Entity WorkflowInstance))) (Any b) -> fmap (== b) . maybeT (return False) $ do
-- let Entity wwId WorkflowWorkflow{..} = x ^. _dbrOutput . _1
-- cID <- encrypt wwId
-- rScope <- toRouteWorkflowScope $ _DBWorkflowScope # workflowWorkflowScope
-- lift $ is _Authorized <$> evalAccess (_WorkflowScopeRoute # (rScope, WorkflowWorkflowR cID WWWorkflowR)) False :: MaybeT (YesodDB UniWorX) Bool
-- , singletonMap "current-state" . FilterProjected $ \x (criteria :: Set Text) ->
-- let criteria' = map CI.mk . unpack <$> Set.toList criteria
-- in maybe False (\cSt -> any (`isInfixOf` cSt) criteria') $ x ^? resultLastAction . _Just . actionTo . _Just . to (map CI.mk . unpack)
-- , singletonMap "final" . FilterProjected $ \x (criterion :: Monoid.Last Bool) -> case Monoid.getLast criterion of
-- Nothing -> True
-- Just needle -> let val = has (resultLastAction . _Just . actionTo . _Just) x
-- && has (resultLastAction . _Just . actionFinal . _Just) x
-- in needle == val
-- ]
dbtFilterUI = mconcat dbtFilterUI = mconcat
[ flip (prismAForm $ singletonFilter "workflow-workflow") $ aopt textField (fslI MsgWorkflowWorkflowListNumber) [ flip (prismAForm $ singletonFilter "workflow-workflow") $ aopt textField (fslI MsgWorkflowWorkflowListNumber)
, flip (prismAForm $ singletonFilter "current-state") $ aopt textField (fslI MsgWorkflowWorkflowListCurrentState) , flip (prismAForm $ singletonFilter "current-state") $ aopt textField (fslI MsgWorkflowWorkflowListCurrentState)

View File

@ -363,15 +363,15 @@ derivePersistField "CorrectorState"
showCompactCorrectorLoad :: Load -> CorrectorState -> Text showCompactCorrectorLoad :: Load -> CorrectorState -> Text
showCompactCorrectorLoad load CorrectorMissing = "[" <> showCompactCorrectorLoad load CorrectorNormal <> "]" showCompactCorrectorLoad load CorrectorMissing = "[" <> showCompactCorrectorLoad load CorrectorNormal <> "]"
showCompactCorrectorLoad load CorrectorExcused = "{" <> showCompactCorrectorLoad load CorrectorNormal <> "}" showCompactCorrectorLoad load CorrectorExcused = "{" <> showCompactCorrectorLoad load CorrectorNormal <> "}"
showCompactCorrectorLoad Load{..} CorrectorNormal | byProportion == 0 = tutorialText showCompactCorrectorLoad Load{..} CorrectorNormal | byProportion == 0 = fromMaybe mempty tutorialText
| otherwise = proportionText <> " + " <> tutorialText | otherwise = maybe id (\tt pt -> pt <> " + " <> tt) tutorialText proportionText
where where
proportionText = let propDbl :: Double proportionText = let propDbl :: Double
propDbl = fromRational byProportion propDbl = fromRational byProportion
in tshow $ roundToDigits 2 propDbl in tshow $ roundToDigits 2 propDbl
tutorialText = case byTutorial of Nothing -> mempty tutorialText = byTutorial <&> \case
Just True -> "(T)" True -> "(T)"
Just False -> "T" False -> "T"
instance Csv.ToField (SheetType epid, Maybe Points) where instance Csv.ToField (SheetType epid, Maybe Points) where
toField (_, Nothing) = mempty toField (_, Nothing) = mempty

View File

@ -67,6 +67,8 @@ import Data.List (inits)
import Data.RFC5051 (compareUnicode) import Data.RFC5051 (compareUnicode)
import qualified Data.Binary as Binary
----- WORKFLOW GRAPH ----- ----- WORKFLOW GRAPH -----
@ -474,7 +476,7 @@ instance (Ord fileid, Ord userid, Typeable fileid, Typeable userid) => Ord (Work
(WFPBool{}, _) -> LT (WFPBool{}, _) -> LT
(WFPDay{}, WFPText{}) -> GT (WFPDay{}, WFPText{}) -> GT
(WFPDay{}, WFPNumber{}) -> GT (WFPDay{}, WFPNumber{}) -> GT
(WFPDay{}, WFPDay{}) -> GT (WFPDay{}, WFPBool{}) -> GT
(WFPDay{}, _) -> LT (WFPDay{}, _) -> LT
(WFPFile{}, WFPText{}) -> GT (WFPFile{}, WFPText{}) -> GT
(WFPFile{}, WFPNumber{}) -> GT (WFPFile{}, WFPNumber{}) -> GT
@ -488,6 +490,7 @@ workflowPayloadSort
(fileid -> fileid -> Ordering) (fileid -> fileid -> Ordering)
-> (userid -> userid -> Ordering) -> (userid -> userid -> Ordering)
-> (WorkflowFieldPayloadW fileid userid -> WorkflowFieldPayloadW fileid userid -> Ordering) -> (WorkflowFieldPayloadW fileid userid -> WorkflowFieldPayloadW fileid userid -> Ordering)
-- ^ @workflowPayloadSort compare compare /= compare@
workflowPayloadSort ordFiles ordUsers (WorkflowFieldPayloadW a) (WorkflowFieldPayloadW b) = case (a, b) of workflowPayloadSort ordFiles ordUsers (WorkflowFieldPayloadW a) (WorkflowFieldPayloadW b) = case (a, b) of
(WFPText a', WFPText b' ) -> compareUnicode a' b' (WFPText a', WFPText b' ) -> compareUnicode a' b'
(WFPText{}, _ ) -> LT (WFPText{}, _ ) -> LT
@ -545,7 +548,7 @@ _WorkflowFieldPayloadW = prism' WorkflowFieldPayloadW $ \(WorkflowFieldPayloadW
data WorkflowFieldPayload' = WFPText' | WFPNumber' | WFPBool' | WFPDay' | WFPFile' | WFPUser' data WorkflowFieldPayload' = WFPText' | WFPNumber' | WFPBool' | WFPDay' | WFPFile' | WFPUser'
deriving (Eq, Ord, Enum, Bounded, Show, Read, Data, Generic, Typeable) deriving (Eq, Ord, Enum, Bounded, Show, Read, Data, Generic, Typeable)
deriving anyclass (Universe, Finite, NFData) deriving anyclass (Universe, Finite, NFData, Binary)
type IsWorkflowFieldPayload' fileid userid payload = IsWorkflowFieldPayload fileid fileid userid userid payload payload type IsWorkflowFieldPayload' fileid userid payload = IsWorkflowFieldPayload fileid fileid userid userid payload payload
@ -1127,6 +1130,24 @@ instance (Binary termid, Binary schoolid, Binary courseid) => Binary (WorkflowSc
instance Binary userid => Binary (WorkflowRole userid) instance Binary userid => Binary (WorkflowRole userid)
instance (Binary fileid, Binary userid, Typeable fileid, Typeable userid) => Binary (WorkflowAction fileid userid)
instance (Binary fileid, Binary userid, Typeable fileid, Typeable userid) => Binary (WorkflowFieldPayloadW fileid userid) where
get = Binary.get >>= \case
WFPText' -> WorkflowFieldPayloadW . WFPText <$> Binary.get
WFPNumber' -> WorkflowFieldPayloadW . WFPNumber <$> Binary.get
WFPBool' -> WorkflowFieldPayloadW . WFPBool <$> Binary.get
WFPDay' -> WorkflowFieldPayloadW . WFPDay <$> Binary.get
WFPFile' -> WorkflowFieldPayloadW . WFPFile <$> Binary.get
WFPUser' -> WorkflowFieldPayloadW . WFPUser <$> Binary.get
put = \case
WorkflowFieldPayloadW (WFPText t ) -> Binary.put WFPText' >> Binary.put t
WorkflowFieldPayloadW (WFPNumber n ) -> Binary.put WFPNumber' >> Binary.put n
WorkflowFieldPayloadW (WFPBool b ) -> Binary.put WFPBool' >> Binary.put b
WorkflowFieldPayloadW (WFPDay d ) -> Binary.put WFPDay' >> Binary.put d
WorkflowFieldPayloadW (WFPFile fid) -> Binary.put WFPFile' >> Binary.put fid
WorkflowFieldPayloadW (WFPUser uid) -> Binary.put WFPUser' >> Binary.put uid
----- TH Jail ----- ----- TH Jail -----
makeWrapped ''WorkflowGraphReference makeWrapped ''WorkflowGraphReference

View File

@ -7,6 +7,7 @@ import ClassyPrelude.Yesod hiding (foldlM, Proxy, handle, catch, bracket)
-- import Data.Double.Conversion.Text -- faster implementation for textPercent? -- import Data.Double.Conversion.Text -- faster implementation for textPercent?
import qualified Data.Foldable as Fold import qualified Data.Foldable as Fold
import qualified Data.Traversable as Trav
import Data.Foldable as Utils (foldlM, foldrM) import Data.Foldable as Utils (foldlM, foldrM)
import Data.Monoid (First, Sum(..), Endo) import Data.Monoid (First, Sum(..), Endo)
import Data.Proxy import Data.Proxy
@ -975,6 +976,20 @@ sortOnM :: (Ord b, Monad m)
-> m [a] -> m [a]
sortOnM f = fmap (map snd . sortBy (comparing fst)) . mapM (\x -> (\y -> y `seq` (y, x)) <$> f x) sortOnM f = fmap (map snd . sortBy (comparing fst)) . mapM (\x -> (\y -> y `seq` (y, x)) <$> f x)
-- Stolen from Agda...
mapMM :: (Traversable t, Monad m) => (a -> m b) -> m (t a) -> m (t b)
mapMM f mxs = Trav.mapM f =<< mxs
forMM :: (Traversable t, Monad m) => m (t a) -> (a -> m b) -> m (t b)
forMM = flip mapMM
mapMM_ :: (Foldable t, Monad m) => (a -> m ()) -> m (t a) -> m ()
mapMM_ f mxs = Fold.mapM_ f =<< mxs
forMM_ :: (Foldable t, Monad m) => m (t a) -> (a -> m ()) -> m ()
forMM_ = flip mapMM_
-------------- --------------
-- Foldable -- -- Foldable --
-------------- --------------

View File

@ -12,9 +12,12 @@ module Utils.Workflow
, selectWorkflowInstanceDescription , selectWorkflowInstanceDescription
, SharedWorkflowGraphException(..), getSharedDBWorkflowGraph, getSharedIdWorkflowGraph , SharedWorkflowGraphException(..), getSharedDBWorkflowGraph, getSharedIdWorkflowGraph
, insertSharedWorkflowGraph , insertSharedWorkflowGraph
, getWorkflowWorkflowState', getWorkflowWorkflowState
, WorkflowWorkflowStateParseException(..)
) where ) where
import Import.NoFoundation import Import.NoFoundation
import Foundation.Type
import qualified Data.CryptoID.Class.ImplicitNamespace as I import qualified Data.CryptoID.Class.ImplicitNamespace as I
import qualified Crypto.MAC.KMAC as Crypto import qualified Crypto.MAC.KMAC as Crypto
@ -25,8 +28,11 @@ import qualified Crypto.Hash as Crypto
import Language.Haskell.TH (nameBase) import Language.Haskell.TH (nameBase)
import qualified Data.Aeson as Aeson import qualified Data.Aeson as Aeson
import Handler.Utils.Memcached
import qualified Database.Esqueleto as E import qualified Database.Esqueleto as E
import qualified Database.Esqueleto.Utils as E import qualified Database.Esqueleto.Utils as E
import qualified Database.Esqueleto.Internal.Internal as E
{-# ANN module ("HLint: ignore Use newtype instead of data" :: String) #-} {-# ANN module ("HLint: ignore Use newtype instead of data" :: String) #-}
@ -49,19 +55,19 @@ _DBWorkflowScope = iso toScope' toScope
& over (typesCustom @WorkflowChildren @(WorkflowScope TermId SchoolShorthand SqlBackendKey) @(WorkflowScope TermId SchoolId SqlBackendKey)) SchoolKey & over (typesCustom @WorkflowChildren @(WorkflowScope TermId SchoolShorthand SqlBackendKey) @(WorkflowScope TermId SchoolId SqlBackendKey)) SchoolKey
& over (typesCustom @WorkflowChildren @(WorkflowScope TermId SchoolId SqlBackendKey) @(WorkflowScope TermId SchoolId CourseId) @SqlBackendKey @CourseId) (review _SqlKey) & over (typesCustom @WorkflowChildren @(WorkflowScope TermId SchoolId SqlBackendKey) @(WorkflowScope TermId SchoolId CourseId) @SqlBackendKey @CourseId) (review _SqlKey)
fromRouteWorkflowScope :: ( MonadIO m fromRouteWorkflowScope :: ( MonadHandler m
, BackendCompatible SqlReadBackend backend , BackendCompatible SqlReadBackend backend
) )
=> RouteWorkflowScope => RouteWorkflowScope
-> MaybeT (ReaderT backend m) IdWorkflowScope -> MaybeT (ReaderT backend m) IdWorkflowScope
fromRouteWorkflowScope rScope = hoist (withReaderT $ projectBackend @SqlReadBackend) . forOf (typesCustom @WorkflowChildren @(WorkflowScope TermId SchoolId (TermId, SchoolId, CourseShorthand)) @(WorkflowScope TermId SchoolId CourseId) @(TermId, SchoolId, CourseShorthand) @CourseId) rScope $ \(tid, ssh, csh) -> MaybeT . getKeyBy $ TermSchoolCourseShort tid ssh csh fromRouteWorkflowScope rScope = $cachedHereBinary rScope . hoist (withReaderT $ projectBackend @SqlReadBackend) . forOf (typesCustom @WorkflowChildren @(WorkflowScope TermId SchoolId (TermId, SchoolId, CourseShorthand)) @(WorkflowScope TermId SchoolId CourseId) @(TermId, SchoolId, CourseShorthand) @CourseId) rScope $ \(tid, ssh, csh) -> MaybeT . getKeyBy $ TermSchoolCourseShort tid ssh csh
toRouteWorkflowScope :: ( MonadIO m toRouteWorkflowScope :: ( MonadHandler m
, BackendCompatible SqlReadBackend backend , BackendCompatible SqlReadBackend backend
) )
=> IdWorkflowScope => IdWorkflowScope
-> MaybeT (ReaderT backend m) RouteWorkflowScope -> MaybeT (ReaderT backend m) RouteWorkflowScope
toRouteWorkflowScope scope = hoist (withReaderT $ projectBackend @SqlReadBackend) . forOf (typesCustom @WorkflowChildren @(WorkflowScope TermId SchoolId CourseId) @(WorkflowScope TermId SchoolId (TermId, SchoolId, CourseShorthand)) @CourseId @(TermId, SchoolId, CourseShorthand)) scope $ \cId -> MaybeT (get cId) <&> \Course{..} -> (courseTerm, courseSchool, courseShorthand) toRouteWorkflowScope scope = $cachedHereBinary scope . hoist (withReaderT $ projectBackend @SqlReadBackend) . forOf (typesCustom @WorkflowChildren @(WorkflowScope TermId SchoolId CourseId) @(WorkflowScope TermId SchoolId (TermId, SchoolId, CourseShorthand)) @CourseId @(TermId, SchoolId, CourseShorthand)) scope $ \cId -> MaybeT (get cId) <&> \Course{..} -> (courseTerm, courseSchool, courseShorthand)
type IdWorkflowGraph = WorkflowGraph FileReference UserId type IdWorkflowGraph = WorkflowGraph FileReference UserId
@ -168,3 +174,51 @@ insertSharedWorkflowGraph graph = withReaderT (projectBackend @SqlBackend) $
where where
swgId = WorkflowGraphReference . Crypto.hashlazy $ Aeson.encode graph swgId = WorkflowGraphReference . Crypto.hashlazy $ Aeson.encode graph
swgId' = SharedWorkflowGraphKey swgId swgId' = SharedWorkflowGraphKey swgId
newtype WorkflowWorkflowStateParse = WorkflowWorkflowStateParse PersistValue
deriving stock (Eq, Ord, Read, Show, Generic, Typeable)
deriving anyclass (Binary)
newtype WorkflowWorkflowStateParseException = WorkflowWorkflowStateParseException Text
deriving stock (Show, Generic, Typeable)
deriving anyclass (Exception)
getWorkflowWorkflowState' :: forall backend m.
( MonadHandler m, HandlerSite m ~ UniWorX
, BackendCompatible SqlReadBackend backend
, MonadThrow m
)
=> WorkflowWorkflowId
-> Maybe WorkflowWorkflow
-> ReaderT backend m (Maybe (Entity WorkflowWorkflow))
getWorkflowWorkflowState' wwId Nothing = withReaderT (projectBackend @SqlBackend . projectBackend @SqlReadBackend) . runMaybeT $ do
res <- MaybeT . E.selectMaybe . E.from $ \workflowWorkflow -> do
E.where_ $ workflowWorkflow E.^. WorkflowWorkflowId E.==. E.val wwId
return
( workflowWorkflow E.^. WorkflowWorkflowInstance
, workflowWorkflow E.^. WorkflowWorkflowScope
, workflowWorkflow E.^. WorkflowWorkflowGraph
, E.veryUnsafeCoerceSqlExprValue $ workflowWorkflow E.^. WorkflowWorkflowState
)
let
( E.Value workflowWorkflowInstance
, E.Value workflowWorkflowScope
, E.Value workflowWorkflowGraph
, E.Value (wwState :: PersistValue) -- Don't parse
) = res
wwState' <- memcachedBy Nothing (WorkflowWorkflowStateParse wwState) . return $ fromPersistValue wwState
case wwState' of
Left err -> lift . throwM $ WorkflowWorkflowStateParseException err
Right workflowWorkflowState -> return $ Entity wwId WorkflowWorkflow{..}
getWorkflowWorkflowState' wwId (Just ww@WorkflowWorkflow{..}) = Just (Entity wwId ww) <$ do
memcachedBySet Nothing (WorkflowWorkflowStateParse $ toPersistValue workflowWorkflowState) workflowWorkflowState
getWorkflowWorkflowState :: forall backend m.
( MonadHandler m, HandlerSite m ~ UniWorX
, BackendCompatible SqlReadBackend backend
, MonadThrow m
)
=> WorkflowWorkflowId
-> ReaderT backend m (Maybe (Entity WorkflowWorkflow))
getWorkflowWorkflowState = flip getWorkflowWorkflowState' Nothing

View File

@ -1,4 +1,4 @@
{ ghc, nixpkgs ? import ./nixpkgs.nix {} }: { ghc, nixpkgs ? import ./nixpkgs.nix }:
let let
# haskellPackages = import ./stackage.nix { inherit nixpkgs; }; # haskellPackages = import ./stackage.nix { inherit nixpkgs; };

View File

@ -24,7 +24,7 @@ extra-deps:
commit: b7071df50bad3a251a544b984e4bf98fa09b8fae commit: b7071df50bad3a251a544b984e4bf98fa09b8fae
- git: git@gitlab2.rz.ifi.lmu.de:uni2work/conduit-resumablesink.git - git: git@gitlab2.rz.ifi.lmu.de:uni2work/conduit-resumablesink.git
commit: cbea6159c2975d42f948525e03e12fc390da53c5 commit: cbea6159c2975d42f948525e03e12fc390da53c5
- git: git://github.com/jtdaugherty/HaskellNet.git - git: https://github.com/jtdaugherty/HaskellNet.git
commit: 5aa1f3b009253b02c4822005ac59ee208a10a347 commit: 5aa1f3b009253b02c4822005ac59ee208a10a347
- git: git@gitlab2.rz.ifi.lmu.de:uni2work/HaskellNet-SSL.git - git: git@gitlab2.rz.ifi.lmu.de:uni2work/HaskellNet-SSL.git
commit: 40393c938111ac78232dc2c7eec5edb4a22d03e8 commit: 40393c938111ac78232dc2c7eec5edb4a22d03e8

View File

@ -40,13 +40,13 @@ packages:
- completed: - completed:
name: HaskellNet name: HaskellNet
version: 0.5.1 version: 0.5.1
git: git://github.com/jtdaugherty/HaskellNet.git git: https://github.com/jtdaugherty/HaskellNet.git
pantry-tree: pantry-tree:
size: 4011 size: 4011
sha256: 921b437ef18ccb04f889301c407263d6b5b72c5864803a000b1e61328988ce70 sha256: 921b437ef18ccb04f889301c407263d6b5b72c5864803a000b1e61328988ce70
commit: 5aa1f3b009253b02c4822005ac59ee208a10a347 commit: 5aa1f3b009253b02c4822005ac59ee208a10a347
original: original:
git: git://github.com/jtdaugherty/HaskellNet.git git: https://github.com/jtdaugherty/HaskellNet.git
commit: 5aa1f3b009253b02c4822005ac59ee208a10a347 commit: 5aa1f3b009253b02c4822005ac59ee208a10a347
- completed: - completed:
name: HaskellNet-SSL name: HaskellNet-SSL

View File

@ -1,4 +1,4 @@
{ nixpkgs ? import ./nixpkgs.nix {} { nixpkgs ? import ./nixpkgs.nix
, snapshot ? "lts-13.21" , snapshot ? "lts-13.21"
}: }:

View File

@ -16,6 +16,7 @@ export COOKIES_SECURE=${COOKIES_SECURE:-false}
export ALLOW_DEPRECATED=${ALLOW_DEPRECATED:-true} export ALLOW_DEPRECATED=${ALLOW_DEPRECATED:-true}
export ENCRYPT_ERRORS=${ENCRYPT_ERRORS:-false} export ENCRYPT_ERRORS=${ENCRYPT_ERRORS:-false}
export RIBBON=${RIBBON:-${__HOST:-localhost}} export RIBBON=${RIBBON:-${__HOST:-localhost}}
export APPROOT=${APPROOT:-http://localhost:$((${PORT_OFFSET:-0} + 3000))}
unset HOST unset HOST
move-back() { move-back() {
@ -31,10 +32,10 @@ fi
if ! [ -z "$(which yesod)" ] if ! [ -z "$(which yesod)" ]
then then
yesod devel $@ yesod devel -p $((${PORT_OFFSET:-0} + 3000)) -q $((${PORT_OFFSET:-0} + 3443)) $@
elif ! [ -z "$(which stack)" ] elif ! [ -z "$(which stack)" ]
then then
stack exec -- yesod devel $@ stack exec -- yesod devel -p $((${PORT_OFFSET:-0} + 3000)) -q $((${PORT_OFFSET:-0} + 3443)) $@
else else
exit 1 exit 1
fi fi

View File

@ -0,0 +1,15 @@
$newline never
<p>
Bitte bedenken Sie beim Stellen Ihrer Anfrage, dass das #
Uni2work-Kernteam aktuell aus Sarah Vaupel und Gregor Kleen besteht #
und zwei Personen nicht hinreichend sind um in allen Fällen eine #
zeitnahe Bearbeitung Ihres Anliegens zu garantieren.
<p>
Falls sich Ihr Anliegen auf eine konkrete Veranstaltung bezieht, #
ziehen Sie bitte auch in Betracht (insbesondere bei zeitkritischen #
Anliegen wie z.B. Abgaben) sich direkt an die Kursverwalter zu #
wenden.

View File

@ -0,0 +1,15 @@
$newline never
<p>
When formulating your request please consider that the Uni2work core #
team currently consists of Sarah Vaupel and Gregor Kleen and that #
two people are not enough to guarantee a timely answer in all cases.
<p>
If your request is related to a specific course, please also #
consider contacting the relevant course administrators as well. #
Especially if your request is time sensitive (e.g. submitting for an #
exercise sheet).

View File

@ -3,11 +3,12 @@ $newline never
<h4>Inhalt <h4>Inhalt
<ul style="list-style-type: none"> <ul style="list-style-type: none">
<li>Gregor Kleen <li>Gregor Kleen & Sarah Vaupel
<li>Oettingenstraße 67 <li>Oettingenstraße 67
<li>D-80538 München <li>D-80538 München
<li>E-Mail: ^{mailtoHtml "gregor.kleen@tcs.ifi.lmu.de"} <li>E-Mail: ^{mailtoHtml "uni2work@ifi.lmu.de"}
<li>Telefon: +49 (0) 89 / 2180 - 9139 <li>Telefon (Gregor Kleen): +49 (0) 89 / 2180 - 9139
<li>Telefon (Sarah Vaupel): —
<h4>Jugendschutz <h4>Jugendschutz
<ul style="list-style-type: none"> <ul style="list-style-type: none">

View File

@ -3,11 +3,12 @@ $newline never
<h4>Contents <h4>Contents
<ul style="list-style-type: none"> <ul style="list-style-type: none">
<li>Gregor Kleen <li>Gregor Kleen & Sarah Vaupel
<li>Oettingenstraße 67 <li>Oettingenstraße 67
<li>D-80538 München (Germany) <li>D-80538 München (Germany)
<li>E-Mail: ^{mailtoHtml "gregor.kleen@tcs.ifi.lmu.de"} <li>E-Mail: ^{mailtoHtml "uni2work@ifi.lmu.de"}
<li>Telefon: +49 (0) 89 / 2180 - 9139 <li>Telefon (Gregor Kleen): +49 (0) 89 / 2180 - 9139
<li>Telefon (Sarah Vaupel): —
<h4>Youth Protection <h4>Youth Protection
<ul style="list-style-type: none"> <ul style="list-style-type: none">

View File

@ -160,3 +160,5 @@ spec = do
[ eqLaws, ordLaws, jsonLaws ] [ eqLaws, ordLaws, jsonLaws ]
lawsCheckHspec (Proxy @WorkflowScope') lawsCheckHspec (Proxy @WorkflowScope')
[ eqLaws, ordLaws, boundedEnumLaws, showLaws, showReadLaws, universeLaws, finiteLaws, pathPieceLaws, jsonLaws, persistFieldLaws, binaryLaws ] [ eqLaws, ordLaws, boundedEnumLaws, showLaws, showReadLaws, universeLaws, finiteLaws, pathPieceLaws, jsonLaws, persistFieldLaws, binaryLaws ]
lawsCheckHspec (Proxy @(WorkflowFieldPayloadW FileReference SqlBackendKey))
[ eqLaws, ordLaws, showLaws, jsonLaws, binaryLaws ]

View File

@ -523,6 +523,16 @@ spec = do
toPathPiece ExamCloseSeparate `shouldBe` "separate" toPathPiece ExamCloseSeparate `shouldBe` "separate"
toPathPiece (ExamCloseOnFinished False) `shouldBe` "on-finished" toPathPiece (ExamCloseOnFinished False) `shouldBe` "on-finished"
toPathPiece (ExamCloseOnFinished True) `shouldBe` "on-finished-hidden" toPathPiece (ExamCloseOnFinished True) `shouldBe` "on-finished-hidden"
describe "CompactCorrectorLoad" $ do
it "matches expectations" . example $ do
showCompactCorrectorLoad Load{ byTutorial = Just False, byProportion = 0 } CorrectorNormal `shouldBe` "T"
showCompactCorrectorLoad Load{ byTutorial = Just True, byProportion = 0 } CorrectorNormal `shouldBe` "(T)"
showCompactCorrectorLoad Load{ byTutorial = Nothing, byProportion = 1 } CorrectorNormal `shouldBe` "1.0"
showCompactCorrectorLoad Load{ byTutorial = Just False, byProportion = 1 } CorrectorNormal `shouldBe` "1.0 + T"
showCompactCorrectorLoad Load{ byTutorial = Just True, byProportion = 1 } CorrectorNormal `shouldBe` "1.0 + (T)"
showCompactCorrectorLoad Load{ byTutorial = Nothing, byProportion = 0 } CorrectorNormal `shouldBe` ""
showCompactCorrectorLoad Load{ byTutorial = Nothing, byProportion = 1 } CorrectorMissing `shouldBe` "[1.0]"
showCompactCorrectorLoad Load{ byTutorial = Nothing, byProportion = 1 } CorrectorExcused `shouldBe` "{1.0}"
termExample :: (TermIdentifier, Text) -> Expectation termExample :: (TermIdentifier, Text) -> Expectation
termExample (term, encoded) = example $ do termExample (term, encoded) = example $ do