ci: merge main to release (#8503)
This commit is contained in:
commit
70bf2ae4dd
63
dev/deploy-to-container/package-lock.json
generated
63
dev/deploy-to-container/package-lock.json
generated
|
@ -6,8 +6,8 @@
|
||||||
"": {
|
"": {
|
||||||
"name": "deploy-to-container",
|
"name": "deploy-to-container",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dockerode": "^4.0.3",
|
"dockerode": "^4.0.4",
|
||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.3.0",
|
||||||
"nanoid": "5.0.9",
|
"nanoid": "5.0.9",
|
||||||
"nanoid-dictionary": "5.0.0-beta.1",
|
"nanoid-dictionary": "5.0.0-beta.1",
|
||||||
"slugify": "1.6.6",
|
"slugify": "1.6.6",
|
||||||
|
@ -258,6 +258,7 @@
|
||||||
"version": "0.2.6",
|
"version": "0.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safer-buffer": "~2.1.0"
|
"safer-buffer": "~2.1.0"
|
||||||
}
|
}
|
||||||
|
@ -290,6 +291,7 @@
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||||
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
|
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tweetnacl": "^0.14.3"
|
"tweetnacl": "^0.14.3"
|
||||||
}
|
}
|
||||||
|
@ -376,6 +378,7 @@
|
||||||
"version": "4.4.0",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "^2.1.3"
|
"ms": "^2.1.3"
|
||||||
},
|
},
|
||||||
|
@ -389,9 +392,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/docker-modem": {
|
"node_modules/docker-modem": {
|
||||||
"version": "5.0.5",
|
"version": "5.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.6.tgz",
|
||||||
"integrity": "sha512-Cxw8uEcvNTRmsQuGqzzfiCnfGgf96tVJItLh8taOX0miTcIBALKH5TckCSuZbpbjP7uhAl81dOL9sxfa6HgCIg==",
|
"integrity": "sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
"readable-stream": "^3.5.0",
|
"readable-stream": "^3.5.0",
|
||||||
|
@ -403,14 +407,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dockerode": {
|
"node_modules/dockerode": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.4.tgz",
|
||||||
"integrity": "sha512-QSXJFcBQNaGZO6U3qWW4B7p8yRIJn/dWmvL2AQWfO/bjptBBO6QYdVkYSYFz9qoivP2jsOHZfmXMAfrK0BMKyg==",
|
"integrity": "sha512-6GYP/EdzEY50HaOxTVTJ2p+mB5xDHTMJhS+UoGrVyS6VC+iQRh7kZ4FRpUYq6nziby7hPqWhOrFFUFTMUZJJ5w==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@balena/dockerignore": "^1.0.2",
|
"@balena/dockerignore": "^1.0.2",
|
||||||
"@grpc/grpc-js": "^1.11.1",
|
"@grpc/grpc-js": "^1.11.1",
|
||||||
"@grpc/proto-loader": "^0.7.13",
|
"@grpc/proto-loader": "^0.7.13",
|
||||||
"docker-modem": "^5.0.5",
|
"docker-modem": "^5.0.6",
|
||||||
"protobufjs": "^7.3.2",
|
"protobufjs": "^7.3.2",
|
||||||
"tar-fs": "~2.0.1",
|
"tar-fs": "~2.0.1",
|
||||||
"uuid": "^10.0.0"
|
"uuid": "^10.0.0"
|
||||||
|
@ -466,9 +471,10 @@
|
||||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||||
},
|
},
|
||||||
"node_modules/fs-extra": {
|
"node_modules/fs-extra": {
|
||||||
"version": "11.2.0",
|
"version": "11.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
|
||||||
"integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
|
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
"jsonfile": "^6.0.1",
|
"jsonfile": "^6.0.1",
|
||||||
|
@ -651,12 +657,14 @@
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/nan": {
|
"node_modules/nan": {
|
||||||
"version": "2.22.0",
|
"version": "2.22.0",
|
||||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz",
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz",
|
||||||
"integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==",
|
"integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==",
|
||||||
|
"license": "MIT",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
|
@ -804,7 +812,8 @@
|
||||||
"node_modules/safer-buffer": {
|
"node_modules/safer-buffer": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/shebang-command": {
|
"node_modules/shebang-command": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
|
@ -847,7 +856,8 @@
|
||||||
"node_modules/split-ca": {
|
"node_modules/split-ca": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz",
|
||||||
"integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ=="
|
"integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==",
|
||||||
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/ssh2": {
|
"node_modules/ssh2": {
|
||||||
"version": "1.16.0",
|
"version": "1.16.0",
|
||||||
|
@ -1010,7 +1020,8 @@
|
||||||
"node_modules/tweetnacl": {
|
"node_modules/tweetnacl": {
|
||||||
"version": "0.14.5",
|
"version": "0.14.5",
|
||||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||||
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="
|
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
|
||||||
|
"license": "Unlicense"
|
||||||
},
|
},
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "6.20.0",
|
"version": "6.20.0",
|
||||||
|
@ -1411,9 +1422,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"docker-modem": {
|
"docker-modem": {
|
||||||
"version": "5.0.5",
|
"version": "5.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.6.tgz",
|
||||||
"integrity": "sha512-Cxw8uEcvNTRmsQuGqzzfiCnfGgf96tVJItLh8taOX0miTcIBALKH5TckCSuZbpbjP7uhAl81dOL9sxfa6HgCIg==",
|
"integrity": "sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
"readable-stream": "^3.5.0",
|
"readable-stream": "^3.5.0",
|
||||||
|
@ -1422,14 +1433,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dockerode": {
|
"dockerode": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.4.tgz",
|
||||||
"integrity": "sha512-QSXJFcBQNaGZO6U3qWW4B7p8yRIJn/dWmvL2AQWfO/bjptBBO6QYdVkYSYFz9qoivP2jsOHZfmXMAfrK0BMKyg==",
|
"integrity": "sha512-6GYP/EdzEY50HaOxTVTJ2p+mB5xDHTMJhS+UoGrVyS6VC+iQRh7kZ4FRpUYq6nziby7hPqWhOrFFUFTMUZJJ5w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@balena/dockerignore": "^1.0.2",
|
"@balena/dockerignore": "^1.0.2",
|
||||||
"@grpc/grpc-js": "^1.11.1",
|
"@grpc/grpc-js": "^1.11.1",
|
||||||
"@grpc/proto-loader": "^0.7.13",
|
"@grpc/proto-loader": "^0.7.13",
|
||||||
"docker-modem": "^5.0.5",
|
"docker-modem": "^5.0.6",
|
||||||
"protobufjs": "^7.3.2",
|
"protobufjs": "^7.3.2",
|
||||||
"tar-fs": "~2.0.1",
|
"tar-fs": "~2.0.1",
|
||||||
"uuid": "^10.0.0"
|
"uuid": "^10.0.0"
|
||||||
|
@ -1473,9 +1484,9 @@
|
||||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||||
},
|
},
|
||||||
"fs-extra": {
|
"fs-extra": {
|
||||||
"version": "11.2.0",
|
"version": "11.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
|
||||||
"integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
|
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
"jsonfile": "^6.0.1",
|
"jsonfile": "^6.0.1",
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
"name": "deploy-to-container",
|
"name": "deploy-to-container",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dockerode": "^4.0.3",
|
"dockerode": "^4.0.4",
|
||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.3.0",
|
||||||
"nanoid": "5.0.9",
|
"nanoid": "5.0.9",
|
||||||
"nanoid-dictionary": "5.0.0-beta.1",
|
"nanoid-dictionary": "5.0.0-beta.1",
|
||||||
"slugify": "1.6.6",
|
"slugify": "1.6.6",
|
||||||
|
|
407
dev/diff/package-lock.json
generated
407
dev/diff/package-lock.json
generated
|
@ -6,11 +6,11 @@
|
||||||
"": {
|
"": {
|
||||||
"name": "diff",
|
"name": "diff",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^5.3.0",
|
"chalk": "^5.4.1",
|
||||||
"dockerode": "^4.0.2",
|
"dockerode": "^4.0.4",
|
||||||
"enquirer": "^2.4.1",
|
"enquirer": "^2.4.1",
|
||||||
"extract-zip": "^2.0.1",
|
"extract-zip": "^2.0.1",
|
||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.3.0",
|
||||||
"got": "^13.0.0",
|
"got": "^13.0.0",
|
||||||
"keypress": "^0.2.1",
|
"keypress": "^0.2.1",
|
||||||
"listr2": "^6.6.1",
|
"listr2": "^6.6.1",
|
||||||
|
@ -29,6 +29,37 @@
|
||||||
"resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz",
|
||||||
"integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q=="
|
"integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@grpc/grpc-js": {
|
||||||
|
"version": "1.12.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.5.tgz",
|
||||||
|
"integrity": "sha512-d3iiHxdpg5+ZcJ6jnDSOT8Z0O0VMVGy34jAnYLUX8yd36b1qn8f1TwOA/Lc7TsOh03IkPJ38eGI5qD2EjNkoEA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@grpc/proto-loader": "^0.7.13",
|
||||||
|
"@js-sdsl/ordered-map": "^4.4.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@grpc/proto-loader": {
|
||||||
|
"version": "0.7.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz",
|
||||||
|
"integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash.camelcase": "^4.3.0",
|
||||||
|
"long": "^5.0.0",
|
||||||
|
"protobufjs": "^7.2.5",
|
||||||
|
"yargs": "^17.7.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@isaacs/cliui": {
|
"node_modules/@isaacs/cliui": {
|
||||||
"version": "8.0.2",
|
"version": "8.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||||
|
@ -129,6 +160,16 @@
|
||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@js-sdsl/ordered-map": {
|
||||||
|
"version": "4.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz",
|
||||||
|
"integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/js-sdsl"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@pkgjs/parseargs": {
|
"node_modules/@pkgjs/parseargs": {
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||||
|
@ -138,6 +179,70 @@
|
||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@protobufjs/aspromise": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/@protobufjs/base64": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/@protobufjs/codegen": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/@protobufjs/eventemitter": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/@protobufjs/fetch": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"@protobufjs/aspromise": "^1.1.1",
|
||||||
|
"@protobufjs/inquire": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@protobufjs/float": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/@protobufjs/inquire": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/@protobufjs/path": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/@protobufjs/pool": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/@protobufjs/utf8": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/@sindresorhus/is": {
|
"node_modules/@sindresorhus/is": {
|
||||||
"version": "5.3.0",
|
"version": "5.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.3.0.tgz",
|
||||||
|
@ -168,8 +273,7 @@
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "18.6.5",
|
"version": "18.6.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.5.tgz",
|
||||||
"integrity": "sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw==",
|
"integrity": "sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw=="
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@types/yauzl": {
|
"node_modules/@types/yauzl": {
|
||||||
"version": "2.10.0",
|
"version": "2.10.0",
|
||||||
|
@ -228,6 +332,7 @@
|
||||||
"version": "0.2.6",
|
"version": "0.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safer-buffer": "~2.1.0"
|
"safer-buffer": "~2.1.0"
|
||||||
}
|
}
|
||||||
|
@ -260,6 +365,7 @@
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||||
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
|
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tweetnacl": "^0.14.3"
|
"tweetnacl": "^0.14.3"
|
||||||
}
|
}
|
||||||
|
@ -315,9 +421,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/chalk": {
|
"node_modules/chalk": {
|
||||||
"version": "5.3.0",
|
"version": "5.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
|
||||||
"integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
|
"integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||||
},
|
},
|
||||||
|
@ -440,14 +547,14 @@
|
||||||
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="
|
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="
|
||||||
},
|
},
|
||||||
"node_modules/cpu-features": {
|
"node_modules/cpu-features": {
|
||||||
"version": "0.0.9",
|
"version": "0.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz",
|
||||||
"integrity": "sha512-AKjgn2rP2yJyfbepsmLfiYcmtNn/2eUvocUyM/09yB0YDiz39HteK/5/T4Onf0pmdYDMgkBoGvRLvEguzyL7wQ==",
|
"integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"buildcheck": "~0.0.6",
|
"buildcheck": "~0.0.6",
|
||||||
"nan": "^2.17.0"
|
"nan": "^2.19.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
|
@ -516,9 +623,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/docker-modem": {
|
"node_modules/docker-modem": {
|
||||||
"version": "5.0.3",
|
"version": "5.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.6.tgz",
|
||||||
"integrity": "sha512-89zhop5YVhcPEt5FpUFGr3cDyceGhq/F9J+ZndQ4KfqNvfbJpPMfgeixFgUj5OjCYAboElqODxY5Z1EBsSa6sg==",
|
"integrity": "sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
"readable-stream": "^3.5.0",
|
"readable-stream": "^3.5.0",
|
||||||
|
@ -530,13 +638,18 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dockerode": {
|
"node_modules/dockerode": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.4.tgz",
|
||||||
"integrity": "sha512-9wM1BVpVMFr2Pw3eJNXrYYt6DT9k0xMcsSCjtPvyQ+xa1iPg/Mo3T/gUcwI0B2cczqCeCYRPF8yFYDwtFXT0+w==",
|
"integrity": "sha512-6GYP/EdzEY50HaOxTVTJ2p+mB5xDHTMJhS+UoGrVyS6VC+iQRh7kZ4FRpUYq6nziby7hPqWhOrFFUFTMUZJJ5w==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@balena/dockerignore": "^1.0.2",
|
"@balena/dockerignore": "^1.0.2",
|
||||||
"docker-modem": "^5.0.3",
|
"@grpc/grpc-js": "^1.11.1",
|
||||||
"tar-fs": "~2.0.1"
|
"@grpc/proto-loader": "^0.7.13",
|
||||||
|
"docker-modem": "^5.0.6",
|
||||||
|
"protobufjs": "^7.3.2",
|
||||||
|
"tar-fs": "~2.0.1",
|
||||||
|
"uuid": "^10.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8.0"
|
"node": ">= 8.0"
|
||||||
|
@ -666,9 +779,10 @@
|
||||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||||
},
|
},
|
||||||
"node_modules/fs-extra": {
|
"node_modules/fs-extra": {
|
||||||
"version": "11.2.0",
|
"version": "11.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
|
||||||
"integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
|
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
"jsonfile": "^6.0.1",
|
"jsonfile": "^6.0.1",
|
||||||
|
@ -949,6 +1063,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash.camelcase": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/log-update": {
|
"node_modules/log-update": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz",
|
||||||
|
@ -1040,6 +1160,12 @@
|
||||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/long": {
|
||||||
|
"version": "5.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz",
|
||||||
|
"integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/lowercase-keys": {
|
"node_modules/lowercase-keys": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
|
||||||
|
@ -1145,9 +1271,10 @@
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
},
|
},
|
||||||
"node_modules/nan": {
|
"node_modules/nan": {
|
||||||
"version": "2.18.0",
|
"version": "2.22.0",
|
||||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz",
|
||||||
"integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==",
|
"integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==",
|
||||||
|
"license": "MIT",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/normalize-url": {
|
"node_modules/normalize-url": {
|
||||||
|
@ -1230,6 +1357,30 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/protobufjs": {
|
||||||
|
"version": "7.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz",
|
||||||
|
"integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"@protobufjs/aspromise": "^1.1.2",
|
||||||
|
"@protobufjs/base64": "^1.1.2",
|
||||||
|
"@protobufjs/codegen": "^2.0.4",
|
||||||
|
"@protobufjs/eventemitter": "^1.1.0",
|
||||||
|
"@protobufjs/fetch": "^1.1.0",
|
||||||
|
"@protobufjs/float": "^1.0.2",
|
||||||
|
"@protobufjs/inquire": "^1.1.0",
|
||||||
|
"@protobufjs/path": "^1.1.2",
|
||||||
|
"@protobufjs/pool": "^1.1.0",
|
||||||
|
"@protobufjs/utf8": "^1.1.0",
|
||||||
|
"@types/node": ">=13.7.0",
|
||||||
|
"long": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pump": {
|
"node_modules/pump": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||||
|
@ -1349,7 +1500,8 @@
|
||||||
"node_modules/safer-buffer": {
|
"node_modules/safer-buffer": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/shebang-command": {
|
"node_modules/shebang-command": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
|
@ -1415,12 +1567,13 @@
|
||||||
"node_modules/split-ca": {
|
"node_modules/split-ca": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz",
|
||||||
"integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ=="
|
"integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==",
|
||||||
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/ssh2": {
|
"node_modules/ssh2": {
|
||||||
"version": "1.15.0",
|
"version": "1.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz",
|
||||||
"integrity": "sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==",
|
"integrity": "sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asn1": "^0.2.6",
|
"asn1": "^0.2.6",
|
||||||
|
@ -1430,8 +1583,8 @@
|
||||||
"node": ">=10.16.0"
|
"node": ">=10.16.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"cpu-features": "~0.0.9",
|
"cpu-features": "~0.0.10",
|
||||||
"nan": "^2.18.0"
|
"nan": "^2.20.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/string_decoder": {
|
"node_modules/string_decoder": {
|
||||||
|
@ -1578,7 +1731,8 @@
|
||||||
"node_modules/tweetnacl": {
|
"node_modules/tweetnacl": {
|
||||||
"version": "0.14.5",
|
"version": "0.14.5",
|
||||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||||
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="
|
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
|
||||||
|
"license": "Unlicense"
|
||||||
},
|
},
|
||||||
"node_modules/type-fest": {
|
"node_modules/type-fest": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
|
@ -1604,6 +1758,19 @@
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/uuid": {
|
||||||
|
"version": "10.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
|
||||||
|
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
|
||||||
|
"funding": [
|
||||||
|
"https://github.com/sponsors/broofa",
|
||||||
|
"https://github.com/sponsors/ctavan"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist/bin/uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
@ -1713,6 +1880,26 @@
|
||||||
"resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz",
|
||||||
"integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q=="
|
"integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q=="
|
||||||
},
|
},
|
||||||
|
"@grpc/grpc-js": {
|
||||||
|
"version": "1.12.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.5.tgz",
|
||||||
|
"integrity": "sha512-d3iiHxdpg5+ZcJ6jnDSOT8Z0O0VMVGy34jAnYLUX8yd36b1qn8f1TwOA/Lc7TsOh03IkPJ38eGI5qD2EjNkoEA==",
|
||||||
|
"requires": {
|
||||||
|
"@grpc/proto-loader": "^0.7.13",
|
||||||
|
"@js-sdsl/ordered-map": "^4.4.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@grpc/proto-loader": {
|
||||||
|
"version": "0.7.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz",
|
||||||
|
"integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==",
|
||||||
|
"requires": {
|
||||||
|
"lodash.camelcase": "^4.3.0",
|
||||||
|
"long": "^5.0.0",
|
||||||
|
"protobufjs": "^7.2.5",
|
||||||
|
"yargs": "^17.7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@isaacs/cliui": {
|
"@isaacs/cliui": {
|
||||||
"version": "8.0.2",
|
"version": "8.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||||
|
@ -1779,12 +1966,71 @@
|
||||||
"minipass": "^7.0.4"
|
"minipass": "^7.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@js-sdsl/ordered-map": {
|
||||||
|
"version": "4.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz",
|
||||||
|
"integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="
|
||||||
|
},
|
||||||
"@pkgjs/parseargs": {
|
"@pkgjs/parseargs": {
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||||
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
|
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"@protobufjs/aspromise": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="
|
||||||
|
},
|
||||||
|
"@protobufjs/base64": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
|
||||||
|
},
|
||||||
|
"@protobufjs/codegen": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
|
||||||
|
},
|
||||||
|
"@protobufjs/eventemitter": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
|
||||||
|
},
|
||||||
|
"@protobufjs/fetch": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
|
||||||
|
"requires": {
|
||||||
|
"@protobufjs/aspromise": "^1.1.1",
|
||||||
|
"@protobufjs/inquire": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@protobufjs/float": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
|
||||||
|
},
|
||||||
|
"@protobufjs/inquire": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="
|
||||||
|
},
|
||||||
|
"@protobufjs/path": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="
|
||||||
|
},
|
||||||
|
"@protobufjs/pool": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="
|
||||||
|
},
|
||||||
|
"@protobufjs/utf8": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
|
||||||
|
},
|
||||||
"@sindresorhus/is": {
|
"@sindresorhus/is": {
|
||||||
"version": "5.3.0",
|
"version": "5.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.3.0.tgz",
|
||||||
|
@ -1806,8 +2052,7 @@
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "18.6.5",
|
"version": "18.6.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.5.tgz",
|
||||||
"integrity": "sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw==",
|
"integrity": "sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw=="
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"@types/yauzl": {
|
"@types/yauzl": {
|
||||||
"version": "2.10.0",
|
"version": "2.10.0",
|
||||||
|
@ -1909,9 +2154,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"chalk": {
|
"chalk": {
|
||||||
"version": "5.3.0",
|
"version": "5.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
|
||||||
"integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w=="
|
"integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="
|
||||||
},
|
},
|
||||||
"chownr": {
|
"chownr": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
|
@ -1994,13 +2239,13 @@
|
||||||
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="
|
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="
|
||||||
},
|
},
|
||||||
"cpu-features": {
|
"cpu-features": {
|
||||||
"version": "0.0.9",
|
"version": "0.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz",
|
||||||
"integrity": "sha512-AKjgn2rP2yJyfbepsmLfiYcmtNn/2eUvocUyM/09yB0YDiz39HteK/5/T4Onf0pmdYDMgkBoGvRLvEguzyL7wQ==",
|
"integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"buildcheck": "~0.0.6",
|
"buildcheck": "~0.0.6",
|
||||||
"nan": "^2.17.0"
|
"nan": "^2.19.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cross-spawn": {
|
"cross-spawn": {
|
||||||
|
@ -2042,9 +2287,9 @@
|
||||||
"integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="
|
"integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="
|
||||||
},
|
},
|
||||||
"docker-modem": {
|
"docker-modem": {
|
||||||
"version": "5.0.3",
|
"version": "5.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.6.tgz",
|
||||||
"integrity": "sha512-89zhop5YVhcPEt5FpUFGr3cDyceGhq/F9J+ZndQ4KfqNvfbJpPMfgeixFgUj5OjCYAboElqODxY5Z1EBsSa6sg==",
|
"integrity": "sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
"readable-stream": "^3.5.0",
|
"readable-stream": "^3.5.0",
|
||||||
|
@ -2053,13 +2298,17 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dockerode": {
|
"dockerode": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.4.tgz",
|
||||||
"integrity": "sha512-9wM1BVpVMFr2Pw3eJNXrYYt6DT9k0xMcsSCjtPvyQ+xa1iPg/Mo3T/gUcwI0B2cczqCeCYRPF8yFYDwtFXT0+w==",
|
"integrity": "sha512-6GYP/EdzEY50HaOxTVTJ2p+mB5xDHTMJhS+UoGrVyS6VC+iQRh7kZ4FRpUYq6nziby7hPqWhOrFFUFTMUZJJ5w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@balena/dockerignore": "^1.0.2",
|
"@balena/dockerignore": "^1.0.2",
|
||||||
"docker-modem": "^5.0.3",
|
"@grpc/grpc-js": "^1.11.1",
|
||||||
"tar-fs": "~2.0.1"
|
"@grpc/proto-loader": "^0.7.13",
|
||||||
|
"docker-modem": "^5.0.6",
|
||||||
|
"protobufjs": "^7.3.2",
|
||||||
|
"tar-fs": "~2.0.1",
|
||||||
|
"uuid": "^10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eastasianwidth": {
|
"eastasianwidth": {
|
||||||
|
@ -2155,9 +2404,9 @@
|
||||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||||
},
|
},
|
||||||
"fs-extra": {
|
"fs-extra": {
|
||||||
"version": "11.2.0",
|
"version": "11.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
|
||||||
"integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
|
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
"jsonfile": "^6.0.1",
|
"jsonfile": "^6.0.1",
|
||||||
|
@ -2342,6 +2591,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||||
},
|
},
|
||||||
|
"lodash.camelcase": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="
|
||||||
|
},
|
||||||
"log-update": {
|
"log-update": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz",
|
||||||
|
@ -2399,6 +2653,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"long": {
|
||||||
|
"version": "5.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz",
|
||||||
|
"integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg=="
|
||||||
|
},
|
||||||
"lowercase-keys": {
|
"lowercase-keys": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
|
||||||
|
@ -2462,9 +2721,9 @@
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
},
|
},
|
||||||
"nan": {
|
"nan": {
|
||||||
"version": "2.18.0",
|
"version": "2.22.0",
|
||||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz",
|
||||||
"integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==",
|
"integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"normalize-url": {
|
"normalize-url": {
|
||||||
|
@ -2517,6 +2776,25 @@
|
||||||
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz",
|
||||||
"integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ=="
|
"integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ=="
|
||||||
},
|
},
|
||||||
|
"protobufjs": {
|
||||||
|
"version": "7.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz",
|
||||||
|
"integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==",
|
||||||
|
"requires": {
|
||||||
|
"@protobufjs/aspromise": "^1.1.2",
|
||||||
|
"@protobufjs/base64": "^1.1.2",
|
||||||
|
"@protobufjs/codegen": "^2.0.4",
|
||||||
|
"@protobufjs/eventemitter": "^1.1.0",
|
||||||
|
"@protobufjs/fetch": "^1.1.0",
|
||||||
|
"@protobufjs/float": "^1.0.2",
|
||||||
|
"@protobufjs/inquire": "^1.1.0",
|
||||||
|
"@protobufjs/path": "^1.1.2",
|
||||||
|
"@protobufjs/pool": "^1.1.0",
|
||||||
|
"@protobufjs/utf8": "^1.1.0",
|
||||||
|
"@types/node": ">=13.7.0",
|
||||||
|
"long": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"pump": {
|
"pump": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||||
|
@ -2636,14 +2914,14 @@
|
||||||
"integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ=="
|
"integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ=="
|
||||||
},
|
},
|
||||||
"ssh2": {
|
"ssh2": {
|
||||||
"version": "1.15.0",
|
"version": "1.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz",
|
||||||
"integrity": "sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==",
|
"integrity": "sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"asn1": "^0.2.6",
|
"asn1": "^0.2.6",
|
||||||
"bcrypt-pbkdf": "^1.0.2",
|
"bcrypt-pbkdf": "^1.0.2",
|
||||||
"cpu-features": "~0.0.9",
|
"cpu-features": "~0.0.10",
|
||||||
"nan": "^2.18.0"
|
"nan": "^2.20.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"string_decoder": {
|
"string_decoder": {
|
||||||
|
@ -2774,6 +3052,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||||
},
|
},
|
||||||
|
"uuid": {
|
||||||
|
"version": "10.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
|
||||||
|
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="
|
||||||
|
},
|
||||||
"which": {
|
"which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
"name": "diff",
|
"name": "diff",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^5.3.0",
|
"chalk": "^5.4.1",
|
||||||
"dockerode": "^4.0.2",
|
"dockerode": "^4.0.4",
|
||||||
"enquirer": "^2.4.1",
|
"enquirer": "^2.4.1",
|
||||||
"extract-zip": "^2.0.1",
|
"extract-zip": "^2.0.1",
|
||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.3.0",
|
||||||
"got": "^13.0.0",
|
"got": "^13.0.0",
|
||||||
"keypress": "^0.2.1",
|
"keypress": "^0.2.1",
|
||||||
"listr2": "^6.6.1",
|
"listr2": "^6.6.1",
|
||||||
|
|
|
@ -101,6 +101,7 @@ services:
|
||||||
# stop_grace_period: 1m
|
# stop_grace_period: 1m
|
||||||
# volumes:
|
# volumes:
|
||||||
# - .:/workspace
|
# - .:/workspace
|
||||||
|
# - app-assets:/assets
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgresdb-data:
|
postgresdb-data:
|
||||||
|
|
|
@ -7,7 +7,7 @@ import factory
|
||||||
import factory.fuzzy
|
import factory.fuzzy
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from typing import Optional # pyflakes:ignore
|
from typing import Any # pyflakes:ignore
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
@ -37,13 +37,16 @@ class BaseDocumentFactory(factory.django.DjangoModelFactory):
|
||||||
model = Document
|
model = Document
|
||||||
skip_postgeneration_save = True
|
skip_postgeneration_save = True
|
||||||
|
|
||||||
|
# n.b., a few attributes are typed as Any so mypy won't complain when we override in subclasses
|
||||||
title = factory.Faker('sentence',nb_words=5)
|
title = factory.Faker('sentence',nb_words=5)
|
||||||
abstract = factory.Faker('paragraph', nb_sentences=5)
|
abstract: Any = factory.Faker('paragraph', nb_sentences=5)
|
||||||
rev = '00'
|
rev = '00'
|
||||||
std_level_id = None # type: Optional[str]
|
std_level_id: Any = None
|
||||||
intended_std_level_id = None
|
intended_std_level_id = None
|
||||||
time = timezone.now()
|
time = timezone.now()
|
||||||
expires = factory.LazyAttribute(lambda o: o.time+datetime.timedelta(days=settings.INTERNET_DRAFT_DAYS_TO_EXPIRE))
|
expires: Any = factory.LazyAttribute(
|
||||||
|
lambda o: o.time+datetime.timedelta(days=settings.INTERNET_DRAFT_DAYS_TO_EXPIRE)
|
||||||
|
)
|
||||||
pages = factory.fuzzy.FuzzyInteger(2,400)
|
pages = factory.fuzzy.FuzzyInteger(2,400)
|
||||||
|
|
||||||
|
|
||||||
|
@ -282,7 +285,7 @@ class DocEventFactory(factory.django.DjangoModelFactory):
|
||||||
|
|
||||||
type = 'added_comment'
|
type = 'added_comment'
|
||||||
by = factory.SubFactory('ietf.person.factories.PersonFactory')
|
by = factory.SubFactory('ietf.person.factories.PersonFactory')
|
||||||
doc = factory.SubFactory(DocumentFactory)
|
doc: Any = factory.SubFactory(DocumentFactory) # `Any` to appease mypy when a subclass overrides doc
|
||||||
desc = factory.Faker('sentence',nb_words=6)
|
desc = factory.Faker('sentence',nb_words=6)
|
||||||
|
|
||||||
@factory.lazy_attribute
|
@factory.lazy_attribute
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
# Copyright The IETF Trust 2014-2021, All Rights Reserved
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
|
||||||
|
|
||||||
from ietf.ipr.utils import generate_draft_recursive_txt
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
help = ("Generate machine-readable list of IPR disclosures by Internet-Draft name (recursive)")
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
|
||||||
try:
|
|
||||||
generate_draft_recursive_txt()
|
|
||||||
except (ValueError, IOError) as e:
|
|
||||||
raise CommandError(e)
|
|
|
@ -161,12 +161,6 @@ class IprTests(TestCase):
|
||||||
r = self.client.get(urlreverse("ietf.ipr.views.history", kwargs=dict(id=ipr.pk)))
|
r = self.client.get(urlreverse("ietf.ipr.views.history", kwargs=dict(id=ipr.pk)))
|
||||||
self.assertContains(r, ipr.title)
|
self.assertContains(r, ipr.title)
|
||||||
|
|
||||||
def test_iprs_for_drafts(self):
|
|
||||||
draft=WgDraftFactory()
|
|
||||||
ipr = HolderIprDisclosureFactory(docs=[draft,])
|
|
||||||
r = self.client.get(urlreverse("ietf.ipr.views.by_draft_txt"))
|
|
||||||
self.assertContains(r, draft.name)
|
|
||||||
self.assertContains(r, str(ipr.pk))
|
|
||||||
|
|
||||||
def test_about(self):
|
def test_about(self):
|
||||||
r = self.client.get(urlreverse("ietf.ipr.views.about"))
|
r = self.client.get(urlreverse("ietf.ipr.views.about"))
|
||||||
|
@ -732,7 +726,7 @@ I would like to revoke this declaration.
|
||||||
self.assertIn(f'{settings.IDTRACKER_BASE_URL}{urlreverse("ietf.ipr.views.showlist")}', get_payload_text(outbox[1]).replace('\n',' '))
|
self.assertIn(f'{settings.IDTRACKER_BASE_URL}{urlreverse("ietf.ipr.views.showlist")}', get_payload_text(outbox[1]).replace('\n',' '))
|
||||||
|
|
||||||
def send_ipr_email_helper(self) -> tuple[str, IprEvent, HolderIprDisclosure]:
|
def send_ipr_email_helper(self) -> tuple[str, IprEvent, HolderIprDisclosure]:
|
||||||
ipr = HolderIprDisclosureFactory()
|
ipr = HolderIprDisclosureFactory.create() # call create() explicitly so mypy sees correct type
|
||||||
url = urlreverse('ietf.ipr.views.email',kwargs={ "id": ipr.id })
|
url = urlreverse('ietf.ipr.views.email',kwargs={ "id": ipr.id })
|
||||||
self.client.login(username="secretary", password="secretary+password")
|
self.client.login(username="secretary", password="secretary+password")
|
||||||
yesterday = date_today() - datetime.timedelta(1)
|
yesterday = date_today() - datetime.timedelta(1)
|
||||||
|
|
|
@ -12,8 +12,6 @@ urlpatterns = [
|
||||||
url(r'^admin/$', RedirectView.as_view(url=reverse_lazy('ietf.ipr.views.admin',kwargs={'state':'pending'}), permanent=True)),
|
url(r'^admin/$', RedirectView.as_view(url=reverse_lazy('ietf.ipr.views.admin',kwargs={'state':'pending'}), permanent=True)),
|
||||||
url(r'^admin/(?P<state>pending|removed|parked)/$', views.admin),
|
url(r'^admin/(?P<state>pending|removed|parked)/$', views.admin),
|
||||||
url(r'^ajax/search/$', views.ajax_search),
|
url(r'^ajax/search/$', views.ajax_search),
|
||||||
url(r'^by-draft/$', views.by_draft_txt),
|
|
||||||
url(r'^by-draft-recursive/$', views.by_draft_recursive_txt),
|
|
||||||
url(r'^(?P<id>\d+)/$', views.show),
|
url(r'^(?P<id>\d+)/$', views.show),
|
||||||
url(r'^(?P<id>\d+)/addcomment/$', views.add_comment),
|
url(r'^(?P<id>\d+)/addcomment/$', views.add_comment),
|
||||||
url(r'^(?P<id>\d+)/addemail/$', views.add_email),
|
url(r'^(?P<id>\d+)/addemail/$', views.add_email),
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
|
||||||
from ietf.ipr.mail import process_response_email, UndeliverableIprResponseError
|
from ietf.ipr.mail import process_response_email, UndeliverableIprResponseError
|
||||||
from ietf.ipr.models import IprDocRel
|
|
||||||
|
|
||||||
import debug # pyflakes:ignore
|
import debug # pyflakes:ignore
|
||||||
|
|
||||||
|
@ -64,30 +63,6 @@ def related_docs(doc, relationship=('replaces', 'obs'), reverse_relationship=("b
|
||||||
return list(set(results))
|
return list(set(results))
|
||||||
|
|
||||||
|
|
||||||
def generate_draft_recursive_txt():
|
|
||||||
docipr = {}
|
|
||||||
|
|
||||||
for o in IprDocRel.objects.filter(disclosure__state='posted').select_related('document'):
|
|
||||||
doc = o.document
|
|
||||||
name = doc.name
|
|
||||||
related_set = set(doc) | set(doc.all_related_that_doc(('obs', 'replaces')))
|
|
||||||
for related in related_set:
|
|
||||||
name = related.name
|
|
||||||
if name.startswith("rfc"):
|
|
||||||
name = name.upper()
|
|
||||||
if not name in docipr:
|
|
||||||
docipr[name] = []
|
|
||||||
docipr[name].append(o.disclosure_id)
|
|
||||||
|
|
||||||
lines = [ "# Machine-readable list of IPR disclosures by Internet-Draft name" ]
|
|
||||||
for name, iprs in docipr.items():
|
|
||||||
lines.append(name + "\t" + "\t".join(str(ipr_id) for ipr_id in sorted(iprs)))
|
|
||||||
|
|
||||||
data = '\n'.join(lines)
|
|
||||||
filename = '/a/ietfdata/derived/ipr_draft_recursive.txt'
|
|
||||||
with open(filename, 'w') as f:
|
|
||||||
f.write(data)
|
|
||||||
|
|
||||||
|
|
||||||
def ingest_response_email(message: bytes):
|
def ingest_response_email(message: bytes):
|
||||||
from ietf.api.views import EmailIngestionError # avoid circular import
|
from ietf.api.views import EmailIngestionError # avoid circular import
|
||||||
|
|
|
@ -445,35 +445,6 @@ def history(request, id):
|
||||||
'selected_tab_entry':'history'
|
'selected_tab_entry':'history'
|
||||||
})
|
})
|
||||||
|
|
||||||
def by_draft_txt(request):
|
|
||||||
docipr = {}
|
|
||||||
|
|
||||||
for o in IprDocRel.objects.filter(disclosure__state='posted').select_related('document'):
|
|
||||||
name = o.document.name
|
|
||||||
if name.startswith("rfc"):
|
|
||||||
name = name.upper()
|
|
||||||
|
|
||||||
if not name in docipr:
|
|
||||||
docipr[name] = []
|
|
||||||
|
|
||||||
docipr[name].append(o.disclosure_id)
|
|
||||||
|
|
||||||
lines = [ "# Machine-readable list of IPR disclosures by draft name" ]
|
|
||||||
for name, iprs in docipr.items():
|
|
||||||
lines.append(name + "\t" + "\t".join(str(ipr_id) for ipr_id in sorted(iprs)))
|
|
||||||
|
|
||||||
return HttpResponse("\n".join(lines), content_type="text/plain; charset=%s"%settings.DEFAULT_CHARSET)
|
|
||||||
|
|
||||||
def by_draft_recursive_txt(request):
|
|
||||||
"""Returns machine-readable list of IPR disclosures by draft name, recursive.
|
|
||||||
NOTE: this view is expensive and should be removed _after_ tools.ietf.org is retired,
|
|
||||||
including util function and management commands that generate the content for
|
|
||||||
this view."""
|
|
||||||
|
|
||||||
with open('/a/ietfdata/derived/ipr_draft_recursive.txt') as f:
|
|
||||||
content = f.read()
|
|
||||||
return HttpResponse(content, content_type="text/plain; charset=%s"%settings.DEFAULT_CHARSET)
|
|
||||||
|
|
||||||
|
|
||||||
def new(request, _type, updates=None):
|
def new(request, _type, updates=None):
|
||||||
"""Submit a new IPR Disclosure. If the updates field != None, this disclosure
|
"""Submit a new IPR Disclosure. If the updates field != None, this disclosure
|
||||||
|
|
|
@ -3,10 +3,42 @@
|
||||||
# Celery task definitions
|
# Celery task definitions
|
||||||
#
|
#
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from ietf.utils import log
|
||||||
|
from .models import Meeting
|
||||||
|
from .utils import generate_proceedings_content
|
||||||
from .views import generate_agenda_data
|
from .views import generate_agenda_data
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def agenda_data_refresh():
|
def agenda_data_refresh():
|
||||||
generate_agenda_data(force_refresh=True)
|
generate_agenda_data(force_refresh=True)
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def proceedings_content_refresh_task(*, all=False):
|
||||||
|
"""Refresh meeting proceedings cache
|
||||||
|
|
||||||
|
If `all` is `False`, then refreshes the cache for meetings whose numbers modulo
|
||||||
|
24 equal the current hour number (0-23). Scheduling the task once per hour will
|
||||||
|
then result in all proceedings being recomputed daily, with no more than two per
|
||||||
|
hour (now) or a few per hour in the next decade. That keeps the computation time
|
||||||
|
to under a couple minutes on our current production system.
|
||||||
|
|
||||||
|
If `all` is True, refreshes all meetings
|
||||||
|
"""
|
||||||
|
now = timezone.now()
|
||||||
|
|
||||||
|
for meeting in Meeting.objects.filter(type_id="ietf").order_by("number"):
|
||||||
|
if meeting.proceedings_format_version == 1:
|
||||||
|
continue # skip v1 proceedings, they're stored externally
|
||||||
|
num = meeting.get_number() # convert str -> int
|
||||||
|
if num is None:
|
||||||
|
log.log(
|
||||||
|
f"Not refreshing proceedings for meeting {meeting.number}: "
|
||||||
|
f"type is 'ietf' but get_number() returned None"
|
||||||
|
)
|
||||||
|
elif all or (num % 24 == now.hour):
|
||||||
|
log.log(f"Refreshing proceedings for meeting {meeting.number}...")
|
||||||
|
generate_proceedings_content(meeting, force_refresh=True)
|
||||||
|
|
51
ietf/meeting/tests_tasks.py
Normal file
51
ietf/meeting/tests_tasks.py
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
# Copyright The IETF Trust 2025, All Rights Reserved
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from mock import patch, call
|
||||||
|
from ietf.utils.test_utils import TestCase
|
||||||
|
from .factories import MeetingFactory
|
||||||
|
from .tasks import proceedings_content_refresh_task, agenda_data_refresh
|
||||||
|
|
||||||
|
|
||||||
|
class TaskTests(TestCase):
|
||||||
|
@patch("ietf.meeting.tasks.generate_agenda_data")
|
||||||
|
def test_agenda_data_refresh(self, mock_generate):
|
||||||
|
agenda_data_refresh()
|
||||||
|
self.assertTrue(mock_generate.called)
|
||||||
|
self.assertEqual(mock_generate.call_args, call(force_refresh=True))
|
||||||
|
|
||||||
|
@patch("ietf.meeting.tasks.generate_proceedings_content")
|
||||||
|
def test_proceedings_content_refresh_task(self, mock_generate):
|
||||||
|
# Generate a couple of meetings
|
||||||
|
meeting120 = MeetingFactory(type_id="ietf", number="120") # 24 * 5
|
||||||
|
meeting127 = MeetingFactory(type_id="ietf", number="127") # 24 * 5 + 7
|
||||||
|
|
||||||
|
# Times to be returned
|
||||||
|
now_utc = datetime.datetime.now(tz=datetime.timezone.utc)
|
||||||
|
hour_00_utc = now_utc.replace(hour=0)
|
||||||
|
hour_01_utc = now_utc.replace(hour=1)
|
||||||
|
hour_07_utc = now_utc.replace(hour=7)
|
||||||
|
|
||||||
|
# hour 00 - should call meeting with number % 24 == 0
|
||||||
|
with patch("ietf.meeting.tasks.timezone.now", return_value=hour_00_utc):
|
||||||
|
proceedings_content_refresh_task()
|
||||||
|
self.assertEqual(mock_generate.call_count, 1)
|
||||||
|
self.assertEqual(mock_generate.call_args, call(meeting120, force_refresh=True))
|
||||||
|
mock_generate.reset_mock()
|
||||||
|
|
||||||
|
# hour 01 - should call no meetings
|
||||||
|
with patch("ietf.meeting.tasks.timezone.now", return_value=hour_01_utc):
|
||||||
|
proceedings_content_refresh_task()
|
||||||
|
self.assertEqual(mock_generate.call_count, 0)
|
||||||
|
|
||||||
|
# hour 07 - should call meeting with number % 24 == 0
|
||||||
|
with patch("ietf.meeting.tasks.timezone.now", return_value=hour_07_utc):
|
||||||
|
proceedings_content_refresh_task()
|
||||||
|
self.assertEqual(mock_generate.call_count, 1)
|
||||||
|
self.assertEqual(mock_generate.call_args, call(meeting127, force_refresh=True))
|
||||||
|
mock_generate.reset_mock()
|
||||||
|
|
||||||
|
# With all=True, all should be called regardless of time. Reuse hour_01_utc which called none before
|
||||||
|
with patch("ietf.meeting.tasks.timezone.now", return_value=hour_01_utc):
|
||||||
|
proceedings_content_refresh_task(all=True)
|
||||||
|
self.assertEqual(mock_generate.call_count, 2)
|
|
@ -32,6 +32,7 @@ from django.db.models import F, Max
|
||||||
from django.http import QueryDict, FileResponse
|
from django.http import QueryDict, FileResponse
|
||||||
from django.template import Context, Template
|
from django.template import Context, Template
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
|
|
||||||
import debug # pyflakes:ignore
|
import debug # pyflakes:ignore
|
||||||
|
@ -46,9 +47,9 @@ from ietf.meeting.helpers import send_interim_meeting_cancellation_notice, send_
|
||||||
from ietf.meeting.helpers import send_interim_minutes_reminder, populate_important_dates, update_important_dates
|
from ietf.meeting.helpers import send_interim_minutes_reminder, populate_important_dates, update_important_dates
|
||||||
from ietf.meeting.models import Session, TimeSlot, Meeting, SchedTimeSessAssignment, Schedule, SessionPresentation, SlideSubmission, SchedulingEvent, Room, Constraint, ConstraintName
|
from ietf.meeting.models import Session, TimeSlot, Meeting, SchedTimeSessAssignment, Schedule, SessionPresentation, SlideSubmission, SchedulingEvent, Room, Constraint, ConstraintName
|
||||||
from ietf.meeting.test_data import make_meeting_test_data, make_interim_meeting, make_interim_test_data
|
from ietf.meeting.test_data import make_meeting_test_data, make_interim_meeting, make_interim_test_data
|
||||||
from ietf.meeting.utils import condition_slide_order
|
from ietf.meeting.utils import condition_slide_order, generate_proceedings_content
|
||||||
from ietf.meeting.utils import add_event_info_to_session_qs, participants_for_meeting
|
from ietf.meeting.utils import add_event_info_to_session_qs, participants_for_meeting
|
||||||
from ietf.meeting.utils import create_recording, get_next_sequence, bluesheet_data
|
from ietf.meeting.utils import create_recording, delete_recording, get_next_sequence, bluesheet_data
|
||||||
from ietf.meeting.views import session_draft_list, parse_agenda_filter_params, sessions_post_save, agenda_extract_schedule
|
from ietf.meeting.views import session_draft_list, parse_agenda_filter_params, sessions_post_save, agenda_extract_schedule
|
||||||
from ietf.meeting.views import get_summary_by_area, get_summary_by_type, get_summary_by_purpose, generate_agenda_data
|
from ietf.meeting.views import get_summary_by_area, get_summary_by_type, get_summary_by_purpose, generate_agenda_data
|
||||||
from ietf.name.models import SessionStatusName, ImportantDateName, RoleName, ProceedingsMaterialTypeName
|
from ietf.name.models import SessionStatusName, ImportantDateName, RoleName, ProceedingsMaterialTypeName
|
||||||
|
@ -441,6 +442,48 @@ class MeetingTests(BaseMeetingTestCase):
|
||||||
self.assertIn(new_recording_title, links[0].text_content())
|
self.assertIn(new_recording_title, links[0].text_content())
|
||||||
#debug.show("q(f'#notes_and_recordings_{session_pk}')")
|
#debug.show("q(f'#notes_and_recordings_{session_pk}')")
|
||||||
|
|
||||||
|
def test_delete_recordings(self):
|
||||||
|
# No user specified, active recording state
|
||||||
|
sp = SessionPresentationFactory(
|
||||||
|
document__type_id="recording",
|
||||||
|
document__external_url="https://example.com/some-recording",
|
||||||
|
document__states=[("recording", "active")],
|
||||||
|
)
|
||||||
|
doc = sp.document
|
||||||
|
doc.docevent_set.all().delete() # clear this out
|
||||||
|
delete_recording(sp)
|
||||||
|
self.assertFalse(SessionPresentation.objects.filter(pk=sp.pk).exists())
|
||||||
|
self.assertEqual(doc.get_state("recording").slug, "deleted", "recording state updated")
|
||||||
|
self.assertEqual(doc.docevent_set.count(), 1, "one event added")
|
||||||
|
event = doc.docevent_set.first()
|
||||||
|
self.assertEqual(event.type, "changed_state", "event is a changed_state event")
|
||||||
|
self.assertEqual(event.by.name, "(System)", "system user is responsible")
|
||||||
|
|
||||||
|
# Specified user, no recording state
|
||||||
|
sp = SessionPresentationFactory(
|
||||||
|
document__type_id="recording",
|
||||||
|
document__external_url="https://example.com/some-recording",
|
||||||
|
document__states=[],
|
||||||
|
)
|
||||||
|
doc = sp.document
|
||||||
|
doc.docevent_set.all().delete() # clear this out
|
||||||
|
user = PersonFactory() # naming matches the methods - user is a Person, not a User
|
||||||
|
delete_recording(sp, user=user)
|
||||||
|
self.assertFalse(SessionPresentation.objects.filter(pk=sp.pk).exists())
|
||||||
|
self.assertEqual(doc.get_state("recording").slug, "deleted", "recording state updated")
|
||||||
|
self.assertEqual(doc.docevent_set.count(), 1, "one event added")
|
||||||
|
event = doc.docevent_set.first()
|
||||||
|
self.assertEqual(event.type, "changed_state", "event is a changed_state event")
|
||||||
|
self.assertEqual(event.by, user, "user is responsible")
|
||||||
|
|
||||||
|
# Document is not a recording
|
||||||
|
sp = SessionPresentationFactory(
|
||||||
|
document__type_id="draft",
|
||||||
|
document__external_url="https://example.com/some-recording",
|
||||||
|
)
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
delete_recording(sp)
|
||||||
|
|
||||||
def test_agenda_ical_next_meeting_type(self):
|
def test_agenda_ical_next_meeting_type(self):
|
||||||
# start with no upcoming IETF meetings, just an interim
|
# start with no upcoming IETF meetings, just an interim
|
||||||
MeetingFactory(
|
MeetingFactory(
|
||||||
|
@ -2082,7 +2125,8 @@ class EditTimeslotsTests(TestCase):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_bare_meeting(number=120) -> Meeting:
|
def create_bare_meeting(number=120) -> Meeting:
|
||||||
"""Create a basic IETF meeting"""
|
"""Create a basic IETF meeting"""
|
||||||
return MeetingFactory(
|
# Call create() explicitly so mypy sees the correct type
|
||||||
|
return MeetingFactory.create(
|
||||||
type_id='ietf',
|
type_id='ietf',
|
||||||
number=number,
|
number=number,
|
||||||
date=date_today() + datetime.timedelta(days=10),
|
date=date_today() + datetime.timedelta(days=10),
|
||||||
|
@ -7363,6 +7407,118 @@ class SessionTests(TestCase):
|
||||||
self.assertEqual(r.status_code,302)
|
self.assertEqual(r.status_code,302)
|
||||||
self.assertEqual(len(outbox),1)
|
self.assertEqual(len(outbox),1)
|
||||||
|
|
||||||
|
@override_settings(YOUTUBE_DOMAINS=["youtube.com"])
|
||||||
|
def test_add_session_recordings(self):
|
||||||
|
session = SessionFactory(meeting__type_id="ietf")
|
||||||
|
url = urlreverse(
|
||||||
|
"ietf.meeting.views.add_session_recordings",
|
||||||
|
kwargs={"session_id": session.pk, "num": session.meeting.number},
|
||||||
|
)
|
||||||
|
# does not fully validate authorization for non-secretariat users :-(
|
||||||
|
login_testing_unauthorized(self, "secretary", url)
|
||||||
|
r = self.client.get(url)
|
||||||
|
pq = PyQuery(r.content)
|
||||||
|
title_input = pq("input#id_title")
|
||||||
|
self.assertIsNotNone(title_input)
|
||||||
|
self.assertEqual(
|
||||||
|
title_input.attr.value,
|
||||||
|
"Video recording of {acro} for {timestamp}".format(
|
||||||
|
acro=session.group.acronym,
|
||||||
|
timestamp=session.official_timeslotassignment().timeslot.utc_start_time().strftime(
|
||||||
|
"%Y-%m-%d %H:%M"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch("ietf.meeting.views.create_recording") as mock_create:
|
||||||
|
r = self.client.post(
|
||||||
|
url,
|
||||||
|
data={
|
||||||
|
"title": "This is my video title",
|
||||||
|
"url": "",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertFalse(mock_create.called)
|
||||||
|
|
||||||
|
with patch("ietf.meeting.views.create_recording") as mock_create:
|
||||||
|
r = self.client.post(
|
||||||
|
url,
|
||||||
|
data={
|
||||||
|
"title": "This is my video title",
|
||||||
|
"url": "https://yubtub.com/this-is-not-a-youtube-video",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertFalse(mock_create.called)
|
||||||
|
|
||||||
|
with patch("ietf.meeting.views.create_recording") as mock_create:
|
||||||
|
r = self.client.post(
|
||||||
|
url,
|
||||||
|
data={
|
||||||
|
"title": "This is my video title",
|
||||||
|
"url": "https://youtube.com/finally-a-video",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertTrue(mock_create.called)
|
||||||
|
self.assertEqual(
|
||||||
|
mock_create.call_args,
|
||||||
|
call(
|
||||||
|
session,
|
||||||
|
"https://youtube.com/finally-a-video",
|
||||||
|
title="This is my video title",
|
||||||
|
user=Person.objects.get(user__username="secretary"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# CAN delete session presentation for this session
|
||||||
|
sp = SessionPresentationFactory(
|
||||||
|
session=session,
|
||||||
|
document__type_id="recording",
|
||||||
|
document__external_url="https://example.com/some-video",
|
||||||
|
)
|
||||||
|
with patch("ietf.meeting.views.delete_recording") as mock_delete:
|
||||||
|
r = self.client.post(
|
||||||
|
url,
|
||||||
|
data={
|
||||||
|
"delete": str(sp.pk),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
self.assertTrue(mock_delete.called)
|
||||||
|
self.assertEqual(mock_delete.call_args, call(sp))
|
||||||
|
|
||||||
|
# ValueError message from delete_recording does not reach the user
|
||||||
|
sp = SessionPresentationFactory(
|
||||||
|
session=session,
|
||||||
|
document__type_id="recording",
|
||||||
|
document__external_url="https://example.com/some-video",
|
||||||
|
)
|
||||||
|
with patch("ietf.meeting.views.delete_recording", side_effect=ValueError("oh joy!")) as mock_delete:
|
||||||
|
r = self.client.post(
|
||||||
|
url,
|
||||||
|
data={
|
||||||
|
"delete": str(sp.pk),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertTrue(mock_delete.called)
|
||||||
|
self.assertNotContains(r, "oh joy!", status_code=200)
|
||||||
|
|
||||||
|
# CANNOT delete session presentation for a different session
|
||||||
|
sp_for_other_session = SessionPresentationFactory(
|
||||||
|
document__type_id="recording",
|
||||||
|
document__external_url="https://example.com/some-other-video",
|
||||||
|
)
|
||||||
|
with patch("ietf.meeting.views.delete_recording") as mock_delete:
|
||||||
|
r = self.client.post(
|
||||||
|
url,
|
||||||
|
data={
|
||||||
|
"delete": str(sp_for_other_session.pk),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(r.status_code, 404)
|
||||||
|
self.assertFalse(mock_delete.called)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class HasMeetingsTests(TestCase):
|
class HasMeetingsTests(TestCase):
|
||||||
settings_temp_path_overrides = TestCase.settings_temp_path_overrides + ['AGENDA_PATH']
|
settings_temp_path_overrides = TestCase.settings_temp_path_overrides + ['AGENDA_PATH']
|
||||||
|
|
||||||
|
@ -8141,8 +8297,7 @@ class ProceedingsTests(BaseMeetingTestCase):
|
||||||
path = Path(settings.BASE_DIR) / 'meeting/test_procmat.pdf'
|
path = Path(settings.BASE_DIR) / 'meeting/test_procmat.pdf'
|
||||||
return path.open('rb')
|
return path.open('rb')
|
||||||
|
|
||||||
def _assertMeetingHostsDisplayed(self, response, meeting):
|
def _assertMeetingHostsDisplayed(self, pq: PyQuery, meeting):
|
||||||
pq = PyQuery(response.content)
|
|
||||||
host_divs = pq('div.host-logo')
|
host_divs = pq('div.host-logo')
|
||||||
self.assertEqual(len(host_divs), meeting.meetinghosts.count(), 'Should have a logo for every meeting host')
|
self.assertEqual(len(host_divs), meeting.meetinghosts.count(), 'Should have a logo for every meeting host')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -8158,12 +8313,11 @@ class ProceedingsTests(BaseMeetingTestCase):
|
||||||
'Correct image and name for each host should appear in the correct order'
|
'Correct image and name for each host should appear in the correct order'
|
||||||
)
|
)
|
||||||
|
|
||||||
def _assertProceedingsMaterialsDisplayed(self, response, meeting):
|
def _assertProceedingsMaterialsDisplayed(self, pq: PyQuery, meeting):
|
||||||
"""Checks that all (and only) active materials are linked with correct href and title"""
|
"""Checks that all (and only) active materials are linked with correct href and title"""
|
||||||
expected_materials = [
|
expected_materials = [
|
||||||
m for m in meeting.proceedings_materials.order_by('type__order') if m.active()
|
m for m in meeting.proceedings_materials.order_by('type__order') if m.active()
|
||||||
]
|
]
|
||||||
pq = PyQuery(response.content)
|
|
||||||
links = pq('div.proceedings-material a')
|
links = pq('div.proceedings-material a')
|
||||||
self.assertEqual(len(links), len(expected_materials), 'Should have an entry for each active ProceedingsMaterial')
|
self.assertEqual(len(links), len(expected_materials), 'Should have an entry for each active ProceedingsMaterial')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -8172,9 +8326,8 @@ class ProceedingsTests(BaseMeetingTestCase):
|
||||||
'Correct title and link for each ProceedingsMaterial should appear in the correct order'
|
'Correct title and link for each ProceedingsMaterial should appear in the correct order'
|
||||||
)
|
)
|
||||||
|
|
||||||
def _assertGroupSessions(self, response, meeting):
|
def _assertGroupSessions(self, pq: PyQuery):
|
||||||
"""Checks that group/sessions are present"""
|
"""Checks that group/sessions are present"""
|
||||||
pq = PyQuery(response.content)
|
|
||||||
sections = ["plenaries", "gen", "iab", "editorial", "irtf", "training"]
|
sections = ["plenaries", "gen", "iab", "editorial", "irtf", "training"]
|
||||||
for section in sections:
|
for section in sections:
|
||||||
self.assertEqual(len(pq(f"#{section}")), 1, f"{section} section should exists in proceedings")
|
self.assertEqual(len(pq(f"#{section}")), 1, f"{section} section should exists in proceedings")
|
||||||
|
@ -8182,10 +8335,9 @@ class ProceedingsTests(BaseMeetingTestCase):
|
||||||
def test_proceedings(self):
|
def test_proceedings(self):
|
||||||
"""Proceedings should be displayed correctly
|
"""Proceedings should be displayed correctly
|
||||||
|
|
||||||
Currently only tests that the view responds with a 200 response code and checks the ProceedingsMaterials
|
Proceedings contents are tested in detail when testing generate_proceedings_content.
|
||||||
at the top of the proceedings. Ought to actually test the display of the individual group/session
|
|
||||||
materials as well.
|
|
||||||
"""
|
"""
|
||||||
|
# number must be >97 (settings.PROCEEDINGS_VERSION_CHANGES)
|
||||||
meeting = make_meeting_test_data(meeting=MeetingFactory(type_id='ietf', number='100'))
|
meeting = make_meeting_test_data(meeting=MeetingFactory(type_id='ietf', number='100'))
|
||||||
session = Session.objects.filter(meeting=meeting, group__acronym="mars").first()
|
session = Session.objects.filter(meeting=meeting, group__acronym="mars").first()
|
||||||
GroupEventFactory(group=session.group,type='status_update')
|
GroupEventFactory(group=session.group,type='status_update')
|
||||||
|
@ -8210,16 +8362,72 @@ class ProceedingsTests(BaseMeetingTestCase):
|
||||||
self._create_proceedings_materials(meeting)
|
self._create_proceedings_materials(meeting)
|
||||||
|
|
||||||
url = urlreverse("ietf.meeting.views.proceedings", kwargs=dict(num=meeting.number))
|
url = urlreverse("ietf.meeting.views.proceedings", kwargs=dict(num=meeting.number))
|
||||||
r = self.client.get(url)
|
cached_content = mark_safe("<p>Fake proceedings content</p>")
|
||||||
|
with patch("ietf.meeting.views.generate_proceedings_content") as mock_gpc:
|
||||||
|
mock_gpc.return_value = cached_content
|
||||||
|
r = self.client.get(url)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
self.assertIn(cached_content, r.content.decode())
|
||||||
|
self.assertTemplateUsed(r, "meeting/proceedings_wrapper.html")
|
||||||
|
self.assertTemplateNotUsed(r, "meeting/proceedings.html")
|
||||||
|
|
||||||
|
# These are rendered in proceedings_wrapper.html, so test them here
|
||||||
if len(meeting.city) > 0:
|
if len(meeting.city) > 0:
|
||||||
self.assertContains(r, meeting.city)
|
self.assertContains(r, meeting.city)
|
||||||
if len(meeting.venue_name) > 0:
|
if len(meeting.venue_name) > 0:
|
||||||
self.assertContains(r, meeting.venue_name)
|
self.assertContains(r, meeting.venue_name)
|
||||||
|
self._assertMeetingHostsDisplayed(PyQuery(r.content), meeting)
|
||||||
|
|
||||||
|
@patch("ietf.meeting.utils.caches")
|
||||||
|
def test_generate_proceedings_content(self, mock_caches):
|
||||||
|
# number must be >97 (settings.PROCEEDINGS_VERSION_CHANGES)
|
||||||
|
meeting = make_meeting_test_data(meeting=MeetingFactory(type_id='ietf', number='100'))
|
||||||
|
|
||||||
|
# First, check that by default a value in the cache is used without doing any other computation
|
||||||
|
mock_default_cache = mock_caches["default"]
|
||||||
|
mock_default_cache.get.return_value = "a cached value"
|
||||||
|
result = generate_proceedings_content(meeting)
|
||||||
|
self.assertEqual(result, "a cached value")
|
||||||
|
self.assertFalse(mock_default_cache.set.called)
|
||||||
|
self.assertTrue(mock_default_cache.get.called)
|
||||||
|
cache_key = mock_default_cache.get.call_args.args[0]
|
||||||
|
mock_default_cache.get.reset_mock()
|
||||||
|
|
||||||
|
# Now set up for actual computation of the proceedings content.
|
||||||
|
session = Session.objects.filter(meeting=meeting, group__acronym="mars").first()
|
||||||
|
GroupEventFactory(group=session.group,type='status_update')
|
||||||
|
SessionPresentationFactory(document__type_id='recording',session=session)
|
||||||
|
SessionPresentationFactory(document__type_id='recording',session=session,document__title="Audio recording for tests")
|
||||||
|
|
||||||
|
# Add various group sessions
|
||||||
|
groups = []
|
||||||
|
parent_groups = [
|
||||||
|
GroupFactory.create(type_id="area", acronym="gen"),
|
||||||
|
GroupFactory.create(acronym="iab"),
|
||||||
|
GroupFactory.create(acronym="irtf"),
|
||||||
|
]
|
||||||
|
for parent in parent_groups:
|
||||||
|
groups.append(GroupFactory.create(parent=parent))
|
||||||
|
for acronym in ["rsab", "edu"]:
|
||||||
|
groups.append(GroupFactory.create(acronym=acronym))
|
||||||
|
for group in groups:
|
||||||
|
SessionFactory(meeting=meeting, group=group)
|
||||||
|
|
||||||
|
self.write_materials_files(meeting, session)
|
||||||
|
self._create_proceedings_materials(meeting)
|
||||||
|
|
||||||
|
# Now "empty" the mock cache and see that we compute the expected proceedings content.
|
||||||
|
mock_default_cache.get.return_value = None
|
||||||
|
proceedings_content = generate_proceedings_content(meeting)
|
||||||
|
self.assertTrue(mock_default_cache.get.called)
|
||||||
|
self.assertEqual(mock_default_cache.get.call_args.args[0], cache_key, "same cache key each time")
|
||||||
|
self.assertTrue(mock_default_cache.set.called)
|
||||||
|
self.assertEqual(mock_default_cache.set.call_args, call(cache_key, proceedings_content, timeout=86400))
|
||||||
|
mock_default_cache.get.reset_mock()
|
||||||
|
mock_default_cache.set.reset_mock()
|
||||||
|
|
||||||
# standard items on every proceedings
|
# standard items on every proceedings
|
||||||
pq = PyQuery(r.content)
|
pq = PyQuery(proceedings_content)
|
||||||
self.assertNotEqual(
|
self.assertNotEqual(
|
||||||
pq('a[href="{}"]'.format(
|
pq('a[href="{}"]'.format(
|
||||||
urlreverse('ietf.meeting.views.proceedings_overview', kwargs=dict(num=meeting.number)))
|
urlreverse('ietf.meeting.views.proceedings_overview', kwargs=dict(num=meeting.number)))
|
||||||
|
@ -8250,9 +8458,17 @@ class ProceedingsTests(BaseMeetingTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
# configurable contents
|
# configurable contents
|
||||||
self._assertMeetingHostsDisplayed(r, meeting)
|
self._assertProceedingsMaterialsDisplayed(pq, meeting)
|
||||||
self._assertProceedingsMaterialsDisplayed(r, meeting)
|
self._assertGroupSessions(pq)
|
||||||
self._assertGroupSessions(r, meeting)
|
|
||||||
|
# Finally, repeat the first cache test, but now with force_refresh=True. The cached value
|
||||||
|
# should be ignored and we should recompute the proceedings as before.
|
||||||
|
mock_default_cache.get.return_value = "a cached value"
|
||||||
|
result = generate_proceedings_content(meeting, force_refresh=True)
|
||||||
|
self.assertEqual(result, proceedings_content) # should have recomputed the same thing
|
||||||
|
self.assertFalse(mock_default_cache.get.called, "don't bother reading cache when force_refresh is True")
|
||||||
|
self.assertTrue(mock_default_cache.set.called)
|
||||||
|
self.assertEqual(mock_default_cache.set.call_args, call(cache_key, proceedings_content, timeout=86400))
|
||||||
|
|
||||||
def test_named_session(self):
|
def test_named_session(self):
|
||||||
"""Session with a name should appear separately in the proceedings"""
|
"""Session with a name should appear separately in the proceedings"""
|
||||||
|
|
|
@ -16,6 +16,7 @@ class AgendaRedirectView(RedirectView):
|
||||||
safe_for_all_meeting_types = [
|
safe_for_all_meeting_types = [
|
||||||
url(r'^session/(?P<acronym>[-a-z0-9]+)/?$', views.session_details),
|
url(r'^session/(?P<acronym>[-a-z0-9]+)/?$', views.session_details),
|
||||||
url(r'^session/(?P<session_id>\d+)/drafts$', views.add_session_drafts),
|
url(r'^session/(?P<session_id>\d+)/drafts$', views.add_session_drafts),
|
||||||
|
url(r'^session/(?P<session_id>\d+)/recordings$', views.add_session_recordings),
|
||||||
url(r'^session/(?P<session_id>\d+)/attendance$', views.session_attendance),
|
url(r'^session/(?P<session_id>\d+)/attendance$', views.session_attendance),
|
||||||
url(r'^session/(?P<session_id>\d+)/bluesheets$', views.upload_session_bluesheets),
|
url(r'^session/(?P<session_id>\d+)/bluesheets$', views.upload_session_bluesheets),
|
||||||
url(r'^session/(?P<session_id>\d+)/minutes$', views.upload_session_minutes),
|
url(r'^session/(?P<session_id>\d+)/minutes$', views.upload_session_minutes),
|
||||||
|
@ -63,7 +64,8 @@ type_ietf_only_patterns = [
|
||||||
type_interim_patterns = [
|
type_interim_patterns = [
|
||||||
url(r'^agenda/(?P<acronym>[A-Za-z0-9-]+)-drafts.pdf$', views.session_draft_pdf),
|
url(r'^agenda/(?P<acronym>[A-Za-z0-9-]+)-drafts.pdf$', views.session_draft_pdf),
|
||||||
url(r'^agenda/(?P<acronym>[A-Za-z0-9-]+)-drafts.tgz$', views.session_draft_tarfile),
|
url(r'^agenda/(?P<acronym>[A-Za-z0-9-]+)-drafts.tgz$', views.session_draft_tarfile),
|
||||||
url(r'^materials/%(document)s((?P<ext>\.[a-z0-9]+)|/)?$' % settings.URL_REGEXPS, views.materials_document),
|
url(r'^materials/%(document)s(?P<ext>\.[a-z0-9]+)$' % settings.URL_REGEXPS, views.materials_document),
|
||||||
|
url(r'^materials/%(document)s/?$' % settings.URL_REGEXPS, views.materials_document),
|
||||||
url(r'^agenda.json$', views.agenda_json)
|
url(r'^agenda.json$', views.agenda_json)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -3,16 +3,19 @@
|
||||||
import datetime
|
import datetime
|
||||||
import itertools
|
import itertools
|
||||||
import os
|
import os
|
||||||
|
from hashlib import sha384
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.db.models import OuterRef, Subquery, TextField, Q, Value
|
from django.core.cache import caches
|
||||||
|
from django.core.files.base import ContentFile
|
||||||
|
from django.db.models import OuterRef, Subquery, TextField, Q, Value, Max
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
@ -23,7 +26,7 @@ import debug # pyflakes:ignore
|
||||||
from ietf.dbtemplate.models import DBTemplate
|
from ietf.dbtemplate.models import DBTemplate
|
||||||
from ietf.meeting.models import (Session, SchedulingEvent, TimeSlot,
|
from ietf.meeting.models import (Session, SchedulingEvent, TimeSlot,
|
||||||
Constraint, SchedTimeSessAssignment, SessionPresentation, Attended)
|
Constraint, SchedTimeSessAssignment, SessionPresentation, Attended)
|
||||||
from ietf.doc.models import Document, State, NewRevisionDocEvent
|
from ietf.doc.models import Document, State, NewRevisionDocEvent, StateDocEvent
|
||||||
from ietf.doc.models import DocEvent
|
from ietf.doc.models import DocEvent
|
||||||
from ietf.group.models import Group
|
from ietf.group.models import Group
|
||||||
from ietf.group.utils import can_manage_materials
|
from ietf.group.utils import can_manage_materials
|
||||||
|
@ -225,12 +228,7 @@ def generate_bluesheet(request, session):
|
||||||
'session': session,
|
'session': session,
|
||||||
'data': data,
|
'data': data,
|
||||||
})
|
})
|
||||||
fd, name = tempfile.mkstemp(suffix=".txt", text=True)
|
return save_bluesheet(request, session, ContentFile(text.encode("utf-8"), name="unusednamepartsothereisanextension.txt"))
|
||||||
os.close(fd)
|
|
||||||
with open(name, "w") as file:
|
|
||||||
file.write(text)
|
|
||||||
with open(name, "br") as file:
|
|
||||||
return save_bluesheet(request, session, file)
|
|
||||||
|
|
||||||
|
|
||||||
def finalize(request, meeting):
|
def finalize(request, meeting):
|
||||||
|
@ -853,6 +851,26 @@ def create_recording(session, url, title=None, user=None):
|
||||||
|
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
|
def delete_recording(session_presentation, user=None):
|
||||||
|
"""Delete a session recording"""
|
||||||
|
document = session_presentation.document
|
||||||
|
if document.type_id != "recording":
|
||||||
|
raise ValueError(f"Document {document.pk} is not a recording (type_id={document.type_id})")
|
||||||
|
recording_state = document.get_state("recording")
|
||||||
|
deleted_state = State.objects.get(type_id="recording", slug="deleted")
|
||||||
|
if recording_state != deleted_state:
|
||||||
|
# Update the recording state and create a history event
|
||||||
|
document.set_state(deleted_state)
|
||||||
|
StateDocEvent.objects.create(
|
||||||
|
type="changed_state",
|
||||||
|
by=user or Person.objects.get(name="(System)"),
|
||||||
|
doc=document,
|
||||||
|
rev=document.rev,
|
||||||
|
state_type=deleted_state.type,
|
||||||
|
state=deleted_state,
|
||||||
|
)
|
||||||
|
session_presentation.delete()
|
||||||
|
|
||||||
def get_next_sequence(group, meeting, type):
|
def get_next_sequence(group, meeting, type):
|
||||||
'''
|
'''
|
||||||
Returns the next sequence number to use for a document of type = type.
|
Returns the next sequence number to use for a document of type = type.
|
||||||
|
@ -980,3 +998,169 @@ def participants_for_meeting(meeting):
|
||||||
sessions = meeting.session_set.filter(Q(type='plenary') | Q(group__type__in=['wg', 'rg']))
|
sessions = meeting.session_set.filter(Q(type='plenary') | Q(group__type__in=['wg', 'rg']))
|
||||||
attended = Attended.objects.filter(session__in=sessions).values_list('person', flat=True).distinct()
|
attended = Attended.objects.filter(session__in=sessions).values_list('person', flat=True).distinct()
|
||||||
return (checked_in, attended)
|
return (checked_in, attended)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_proceedings_content(meeting, force_refresh=False):
|
||||||
|
"""Render proceedings content for a meeting and update cache
|
||||||
|
|
||||||
|
:meeting: meeting whose proceedings should be rendered
|
||||||
|
:force_refresh: true to force regeneration and cache refresh
|
||||||
|
"""
|
||||||
|
cache = caches["default"]
|
||||||
|
cache_version = Document.objects.filter(session__meeting__number=meeting.number).aggregate(Max('time'))["time__max"]
|
||||||
|
# Include proceedings_final in the bare_key so we'll always reflect that accurately, even at the cost of
|
||||||
|
# a recomputation in the view
|
||||||
|
bare_key = f"proceedings.{meeting.number}.{cache_version}.final={meeting.proceedings_final}"
|
||||||
|
cache_key = sha384(bare_key.encode("utf8")).hexdigest()
|
||||||
|
if not force_refresh:
|
||||||
|
cached_content = cache.get(cache_key, None)
|
||||||
|
if cached_content is not None:
|
||||||
|
return cached_content
|
||||||
|
|
||||||
|
def area_and_group_acronyms_from_session(s):
|
||||||
|
area = s.group_parent_at_the_time()
|
||||||
|
if area == None:
|
||||||
|
area = s.group.parent
|
||||||
|
group = s.group_at_the_time()
|
||||||
|
return (area.acronym, group.acronym)
|
||||||
|
|
||||||
|
schedule = meeting.schedule
|
||||||
|
sessions = (
|
||||||
|
meeting.session_set.with_current_status()
|
||||||
|
.filter(Q(timeslotassignments__schedule__in=[schedule, schedule.base if schedule else None])
|
||||||
|
| Q(current_status='notmeet'))
|
||||||
|
.select_related()
|
||||||
|
.order_by('-current_status')
|
||||||
|
)
|
||||||
|
|
||||||
|
plenaries, _ = organize_proceedings_sessions(
|
||||||
|
sessions.filter(name__icontains='plenary')
|
||||||
|
.exclude(current_status='notmeet')
|
||||||
|
)
|
||||||
|
irtf_meeting, irtf_not_meeting = organize_proceedings_sessions(
|
||||||
|
sessions.filter(group__parent__acronym = 'irtf').order_by('group__acronym')
|
||||||
|
)
|
||||||
|
# per Colin (datatracker #5010) - don't report not meeting rags
|
||||||
|
irtf_not_meeting = [item for item in irtf_not_meeting if item["group"].type_id != "rag"]
|
||||||
|
irtf = {"meeting_groups":irtf_meeting, "not_meeting_groups":irtf_not_meeting}
|
||||||
|
|
||||||
|
training, _ = organize_proceedings_sessions(
|
||||||
|
sessions.filter(group__acronym__in=['edu','iaoc'], type_id__in=['regular', 'other',])
|
||||||
|
.exclude(current_status='notmeet')
|
||||||
|
)
|
||||||
|
iab, _ = organize_proceedings_sessions(
|
||||||
|
sessions.filter(group__parent__acronym = 'iab')
|
||||||
|
.exclude(current_status='notmeet')
|
||||||
|
)
|
||||||
|
editorial, _ = organize_proceedings_sessions(
|
||||||
|
sessions.filter(group__acronym__in=['rsab','rswg'])
|
||||||
|
.exclude(current_status='notmeet')
|
||||||
|
)
|
||||||
|
|
||||||
|
ietf = sessions.filter(group__parent__type__slug = 'area').exclude(group__acronym__in=['edu','iepg','tools'])
|
||||||
|
ietf = list(ietf)
|
||||||
|
ietf.sort(key=lambda s: area_and_group_acronyms_from_session(s))
|
||||||
|
ietf_areas = []
|
||||||
|
for area, area_sessions in itertools.groupby(ietf, key=lambda s: s.group_parent_at_the_time()):
|
||||||
|
meeting_groups, not_meeting_groups = organize_proceedings_sessions(area_sessions)
|
||||||
|
ietf_areas.append((area, meeting_groups, not_meeting_groups))
|
||||||
|
|
||||||
|
with timezone.override(meeting.tz()):
|
||||||
|
rendered_content = render_to_string(
|
||||||
|
"meeting/proceedings.html",
|
||||||
|
{
|
||||||
|
'meeting': meeting,
|
||||||
|
'plenaries': plenaries,
|
||||||
|
'training': training,
|
||||||
|
'irtf': irtf,
|
||||||
|
'iab': iab,
|
||||||
|
'editorial': editorial,
|
||||||
|
'ietf_areas': ietf_areas,
|
||||||
|
'meetinghost_logo': {
|
||||||
|
'max_height': settings.MEETINGHOST_LOGO_MAX_DISPLAY_HEIGHT,
|
||||||
|
'max_width': settings.MEETINGHOST_LOGO_MAX_DISPLAY_WIDTH,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
cache.set(
|
||||||
|
cache_key,
|
||||||
|
rendered_content,
|
||||||
|
timeout=86400, # one day, in seconds
|
||||||
|
)
|
||||||
|
return rendered_content
|
||||||
|
|
||||||
|
|
||||||
|
def organize_proceedings_sessions(sessions):
|
||||||
|
# Collect sessions by Group, then bin by session name (including sessions with blank names).
|
||||||
|
# If all of a group's sessions are 'notmeet', the processed data goes in not_meeting_sessions.
|
||||||
|
# Otherwise, the data goes in meeting_sessions.
|
||||||
|
meeting_groups = []
|
||||||
|
not_meeting_groups = []
|
||||||
|
for group_acronym, group_sessions in itertools.groupby(sessions, key=lambda s: s.group.acronym):
|
||||||
|
by_name = {}
|
||||||
|
is_meeting = False
|
||||||
|
all_canceled = True
|
||||||
|
group = None
|
||||||
|
for s in sorted(
|
||||||
|
group_sessions,
|
||||||
|
key=lambda gs: (
|
||||||
|
gs.official_timeslotassignment().timeslot.time
|
||||||
|
if gs.official_timeslotassignment() else datetime.datetime(datetime.MAXYEAR, 1, 1)
|
||||||
|
),
|
||||||
|
):
|
||||||
|
group = s.group
|
||||||
|
if s.current_status != 'notmeet':
|
||||||
|
is_meeting = True
|
||||||
|
if s.current_status != 'canceled':
|
||||||
|
all_canceled = False
|
||||||
|
by_name.setdefault(s.name, [])
|
||||||
|
if s.current_status != 'notmeet' or s.presentations.exists():
|
||||||
|
by_name[s.name].append(s) # for notmeet, only include sessions with materials
|
||||||
|
for sess_name, ss in by_name.items():
|
||||||
|
session = ss[0] if ss else None
|
||||||
|
def _format_materials(items):
|
||||||
|
"""Format session/material for template
|
||||||
|
|
||||||
|
Input is a list of (session, materials) pairs. The materials value can be a single value or a list.
|
||||||
|
"""
|
||||||
|
material_times = {} # key is material, value is first timestamp it appeared
|
||||||
|
for s, mats in items:
|
||||||
|
tsa = s.official_timeslotassignment()
|
||||||
|
timestamp = tsa.timeslot.time if tsa else None
|
||||||
|
if not isinstance(mats, list):
|
||||||
|
mats = [mats]
|
||||||
|
for mat in mats:
|
||||||
|
if mat and mat not in material_times:
|
||||||
|
material_times[mat] = timestamp
|
||||||
|
n_mats = len(material_times)
|
||||||
|
result = []
|
||||||
|
if n_mats == 1:
|
||||||
|
result.append({'material': list(material_times)[0]}) # no 'time' when only a single material
|
||||||
|
elif n_mats > 1:
|
||||||
|
for mat, timestamp in material_times.items():
|
||||||
|
result.append({'material': mat, 'time': timestamp})
|
||||||
|
return result
|
||||||
|
|
||||||
|
entry = {
|
||||||
|
'group': group,
|
||||||
|
'name': sess_name,
|
||||||
|
'session': session,
|
||||||
|
'canceled': all_canceled,
|
||||||
|
'has_materials': s.presentations.exists(),
|
||||||
|
'agendas': _format_materials((s, s.agenda()) for s in ss),
|
||||||
|
'minutes': _format_materials((s, s.minutes()) for s in ss),
|
||||||
|
'bluesheets': _format_materials((s, s.bluesheets()) for s in ss),
|
||||||
|
'recordings': _format_materials((s, s.recordings()) for s in ss),
|
||||||
|
'meetecho_recordings': _format_materials((s, [s.session_recording_url()]) for s in ss),
|
||||||
|
'chatlogs': _format_materials((s, s.chatlogs()) for s in ss),
|
||||||
|
'slides': _format_materials((s, s.slides()) for s in ss),
|
||||||
|
'drafts': _format_materials((s, s.drafts()) for s in ss),
|
||||||
|
'last_update': session.last_update if hasattr(session, 'last_update') else None
|
||||||
|
}
|
||||||
|
if session and session.meeting.type_id == 'ietf' and not session.meeting.proceedings_final:
|
||||||
|
entry['attendances'] = _format_materials((s, s) for s in ss if Attended.objects.filter(session=s).exists())
|
||||||
|
if is_meeting:
|
||||||
|
meeting_groups.append(entry)
|
||||||
|
else:
|
||||||
|
not_meeting_groups.append(entry)
|
||||||
|
return meeting_groups, not_meeting_groups
|
||||||
|
|
|
@ -20,7 +20,7 @@ from collections import OrderedDict, Counter, deque, defaultdict, namedtuple
|
||||||
from functools import partialmethod
|
from functools import partialmethod
|
||||||
import jsonschema
|
import jsonschema
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from urllib.parse import parse_qs, unquote, urlencode, urlsplit, urlunsplit
|
from urllib.parse import parse_qs, unquote, urlencode, urlsplit, urlunsplit, urlparse
|
||||||
from tempfile import mkstemp
|
from tempfile import mkstemp
|
||||||
from wsgiref.handlers import format_date_time
|
from wsgiref.handlers import format_date_time
|
||||||
|
|
||||||
|
@ -75,7 +75,13 @@ from ietf.meeting.helpers import send_interim_meeting_cancellation_notice, send_
|
||||||
from ietf.meeting.helpers import send_interim_approval
|
from ietf.meeting.helpers import send_interim_approval
|
||||||
from ietf.meeting.helpers import send_interim_approval_request
|
from ietf.meeting.helpers import send_interim_approval_request
|
||||||
from ietf.meeting.helpers import send_interim_announcement_request, sessions_post_cancel
|
from ietf.meeting.helpers import send_interim_announcement_request, sessions_post_cancel
|
||||||
from ietf.meeting.utils import finalize, sort_accept_tuple, condition_slide_order
|
from ietf.meeting.utils import (
|
||||||
|
condition_slide_order,
|
||||||
|
finalize,
|
||||||
|
generate_proceedings_content,
|
||||||
|
organize_proceedings_sessions,
|
||||||
|
sort_accept_tuple,
|
||||||
|
)
|
||||||
from ietf.meeting.utils import add_event_info_to_session_qs
|
from ietf.meeting.utils import add_event_info_to_session_qs
|
||||||
from ietf.meeting.utils import session_time_for_sorting
|
from ietf.meeting.utils import session_time_for_sorting
|
||||||
from ietf.meeting.utils import session_requested_by, SaveMaterialsError
|
from ietf.meeting.utils import session_requested_by, SaveMaterialsError
|
||||||
|
@ -86,7 +92,7 @@ from ietf.meeting.utils import diff_meeting_schedules, prefetch_schedule_diff_ob
|
||||||
from ietf.meeting.utils import swap_meeting_schedule_timeslot_assignments, bulk_create_timeslots
|
from ietf.meeting.utils import swap_meeting_schedule_timeslot_assignments, bulk_create_timeslots
|
||||||
from ietf.meeting.utils import preprocess_meeting_important_dates
|
from ietf.meeting.utils import preprocess_meeting_important_dates
|
||||||
from ietf.meeting.utils import new_doc_for_session, write_doc_for_session
|
from ietf.meeting.utils import new_doc_for_session, write_doc_for_session
|
||||||
from ietf.meeting.utils import get_activity_stats, post_process, create_recording
|
from ietf.meeting.utils import get_activity_stats, post_process, create_recording, delete_recording
|
||||||
from ietf.meeting.utils import participants_for_meeting, generate_bluesheet, bluesheet_data, save_bluesheet
|
from ietf.meeting.utils import participants_for_meeting, generate_bluesheet, bluesheet_data, save_bluesheet
|
||||||
from ietf.message.utils import infer_message
|
from ietf.message.utils import infer_message
|
||||||
from ietf.name.models import SlideSubmissionStatusName, ProceedingsMaterialTypeName, SessionPurposeName
|
from ietf.name.models import SlideSubmissionStatusName, ProceedingsMaterialTypeName, SessionPurposeName
|
||||||
|
@ -103,6 +109,7 @@ from ietf.utils.pdf import pdf_pages
|
||||||
from ietf.utils.response import permission_denied
|
from ietf.utils.response import permission_denied
|
||||||
from ietf.utils.text import xslugify
|
from ietf.utils.text import xslugify
|
||||||
from ietf.utils.timezone import datetime_today, date_today
|
from ietf.utils.timezone import datetime_today, date_today
|
||||||
|
from ietf.settings import YOUTUBE_DOMAINS
|
||||||
|
|
||||||
from .forms import (InterimMeetingModelForm, InterimAnnounceForm, InterimSessionModelForm,
|
from .forms import (InterimMeetingModelForm, InterimAnnounceForm, InterimSessionModelForm,
|
||||||
InterimCancelForm, InterimSessionInlineFormSet, RequestMinutesForm,
|
InterimCancelForm, InterimSessionInlineFormSet, RequestMinutesForm,
|
||||||
|
@ -2568,6 +2575,89 @@ def add_session_drafts(request, session_id, num):
|
||||||
'form': form,
|
'form': form,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
class SessionRecordingsForm(forms.Form):
|
||||||
|
title = forms.CharField(max_length=255)
|
||||||
|
url = forms.URLField(label="URL of the recording (YouTube only)")
|
||||||
|
|
||||||
|
def clean_url(self):
|
||||||
|
url = self.cleaned_data['url']
|
||||||
|
parsed_url = urlparse(url)
|
||||||
|
if parsed_url.hostname not in YOUTUBE_DOMAINS:
|
||||||
|
raise forms.ValidationError("Must be a YouTube URL")
|
||||||
|
return url
|
||||||
|
|
||||||
|
|
||||||
|
def add_session_recordings(request, session_id, num):
|
||||||
|
# num is redundant, but we're dragging it along an artifact of where we are in the current URL structure
|
||||||
|
session = get_object_or_404(Session, pk=session_id)
|
||||||
|
if not session.can_manage_materials(request.user):
|
||||||
|
permission_denied(
|
||||||
|
request, "You don't have permission to manage recordings for this session."
|
||||||
|
)
|
||||||
|
if session.is_material_submission_cutoff() and not has_role(
|
||||||
|
request.user, "Secretariat"
|
||||||
|
):
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
session_number = None
|
||||||
|
official_timeslotassignment = session.official_timeslotassignment()
|
||||||
|
assertion("official_timeslotassignment is not None")
|
||||||
|
initial = {
|
||||||
|
"title": "Video recording of {acronym} for {timestamp}".format(
|
||||||
|
acronym=session.group.acronym,
|
||||||
|
timestamp=official_timeslotassignment.timeslot.utc_start_time().strftime(
|
||||||
|
"%Y-%m-%d %H:%M"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# find session number if WG has more than one session at the meeting
|
||||||
|
sessions = get_sessions(session.meeting.number, session.group.acronym)
|
||||||
|
if len(sessions) > 1:
|
||||||
|
session_number = 1 + sessions.index(session)
|
||||||
|
|
||||||
|
presentations = session.presentations.filter(
|
||||||
|
document__in=session.get_material("recording", only_one=False),
|
||||||
|
).order_by("document__title", "document__external_url")
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
pk_to_delete = request.POST.get("delete", None)
|
||||||
|
if pk_to_delete is not None:
|
||||||
|
session_presentation = get_object_or_404(presentations, pk=pk_to_delete)
|
||||||
|
try:
|
||||||
|
delete_recording(session_presentation)
|
||||||
|
except ValueError as err:
|
||||||
|
log(f"Error deleting recording from session {session.pk}: {err}")
|
||||||
|
messages.error(
|
||||||
|
request,
|
||||||
|
"Unable to delete this recording. Please contact the secretariat for assistance.",
|
||||||
|
)
|
||||||
|
form = SessionRecordingsForm(initial=initial)
|
||||||
|
else:
|
||||||
|
form = SessionRecordingsForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
title = form.cleaned_data["title"]
|
||||||
|
url = form.cleaned_data["url"]
|
||||||
|
create_recording(session, url, title=title, user=request.user.person)
|
||||||
|
return redirect(
|
||||||
|
"ietf.meeting.views.session_details",
|
||||||
|
num=session.meeting.number,
|
||||||
|
acronym=session.group.acronym,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
form = SessionRecordingsForm(initial=initial)
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"meeting/add_session_recordings.html",
|
||||||
|
{
|
||||||
|
"session": session,
|
||||||
|
"session_number": session_number,
|
||||||
|
"already_linked": presentations,
|
||||||
|
"form": form,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def session_attendance(request, session_id, num):
|
def session_attendance(request, session_id, num):
|
||||||
"""Session attendance view
|
"""Session attendance view
|
||||||
|
@ -4044,93 +4134,10 @@ def upcoming_json(request):
|
||||||
response = HttpResponse(json.dumps(data, indent=2, sort_keys=False), content_type='application/json;charset=%s'%settings.DEFAULT_CHARSET)
|
response = HttpResponse(json.dumps(data, indent=2, sort_keys=False), content_type='application/json;charset=%s'%settings.DEFAULT_CHARSET)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def organize_proceedings_sessions(sessions):
|
|
||||||
# Collect sessions by Group, then bin by session name (including sessions with blank names).
|
|
||||||
# If all of a group's sessions are 'notmeet', the processed data goes in not_meeting_sessions.
|
|
||||||
# Otherwise, the data goes in meeting_sessions.
|
|
||||||
meeting_groups = []
|
|
||||||
not_meeting_groups = []
|
|
||||||
for group_acronym, group_sessions in itertools.groupby(sessions, key=lambda s: s.group.acronym):
|
|
||||||
by_name = {}
|
|
||||||
is_meeting = False
|
|
||||||
all_canceled = True
|
|
||||||
group = None
|
|
||||||
for s in sorted(
|
|
||||||
group_sessions,
|
|
||||||
key=lambda gs: (
|
|
||||||
gs.official_timeslotassignment().timeslot.time
|
|
||||||
if gs.official_timeslotassignment() else datetime.datetime(datetime.MAXYEAR, 1, 1)
|
|
||||||
),
|
|
||||||
):
|
|
||||||
group = s.group
|
|
||||||
if s.current_status != 'notmeet':
|
|
||||||
is_meeting = True
|
|
||||||
if s.current_status != 'canceled':
|
|
||||||
all_canceled = False
|
|
||||||
by_name.setdefault(s.name, [])
|
|
||||||
if s.current_status != 'notmeet' or s.presentations.exists():
|
|
||||||
by_name[s.name].append(s) # for notmeet, only include sessions with materials
|
|
||||||
for sess_name, ss in by_name.items():
|
|
||||||
session = ss[0] if ss else None
|
|
||||||
def _format_materials(items):
|
|
||||||
"""Format session/material for template
|
|
||||||
|
|
||||||
Input is a list of (session, materials) pairs. The materials value can be a single value or a list.
|
|
||||||
"""
|
|
||||||
material_times = {} # key is material, value is first timestamp it appeared
|
|
||||||
for s, mats in items:
|
|
||||||
tsa = s.official_timeslotassignment()
|
|
||||||
timestamp = tsa.timeslot.time if tsa else None
|
|
||||||
if not isinstance(mats, list):
|
|
||||||
mats = [mats]
|
|
||||||
for mat in mats:
|
|
||||||
if mat and mat not in material_times:
|
|
||||||
material_times[mat] = timestamp
|
|
||||||
n_mats = len(material_times)
|
|
||||||
result = []
|
|
||||||
if n_mats == 1:
|
|
||||||
result.append({'material': list(material_times)[0]}) # no 'time' when only a single material
|
|
||||||
elif n_mats > 1:
|
|
||||||
for mat, timestamp in material_times.items():
|
|
||||||
result.append({'material': mat, 'time': timestamp})
|
|
||||||
return result
|
|
||||||
|
|
||||||
entry = {
|
|
||||||
'group': group,
|
|
||||||
'name': sess_name,
|
|
||||||
'session': session,
|
|
||||||
'canceled': all_canceled,
|
|
||||||
'has_materials': s.presentations.exists(),
|
|
||||||
'agendas': _format_materials((s, s.agenda()) for s in ss),
|
|
||||||
'minutes': _format_materials((s, s.minutes()) for s in ss),
|
|
||||||
'bluesheets': _format_materials((s, s.bluesheets()) for s in ss),
|
|
||||||
'recordings': _format_materials((s, s.recordings()) for s in ss),
|
|
||||||
'meetecho_recordings': _format_materials((s, [s.session_recording_url()]) for s in ss),
|
|
||||||
'chatlogs': _format_materials((s, s.chatlogs()) for s in ss),
|
|
||||||
'slides': _format_materials((s, s.slides()) for s in ss),
|
|
||||||
'drafts': _format_materials((s, s.drafts()) for s in ss),
|
|
||||||
'last_update': session.last_update if hasattr(session, 'last_update') else None
|
|
||||||
}
|
|
||||||
if session and session.meeting.type_id == 'ietf' and not session.meeting.proceedings_final:
|
|
||||||
entry['attendances'] = _format_materials((s, s) for s in ss if Attended.objects.filter(session=s).exists())
|
|
||||||
if is_meeting:
|
|
||||||
meeting_groups.append(entry)
|
|
||||||
else:
|
|
||||||
not_meeting_groups.append(entry)
|
|
||||||
return meeting_groups, not_meeting_groups
|
|
||||||
|
|
||||||
|
|
||||||
def proceedings(request, num=None):
|
def proceedings(request, num=None):
|
||||||
|
|
||||||
def area_and_group_acronyms_from_session(s):
|
|
||||||
area = s.group_parent_at_the_time()
|
|
||||||
if area == None:
|
|
||||||
area = s.group.parent
|
|
||||||
group = s.group_at_the_time()
|
|
||||||
return (area.acronym, group.acronym)
|
|
||||||
|
|
||||||
meeting = get_meeting(num)
|
meeting = get_meeting(num)
|
||||||
|
|
||||||
# Early proceedings were hosted on www.ietf.org rather than the datatracker
|
# Early proceedings were hosted on www.ietf.org rather than the datatracker
|
||||||
if meeting.proceedings_format_version == 1:
|
if meeting.proceedings_format_version == 1:
|
||||||
return HttpResponseRedirect(settings.PROCEEDINGS_V1_BASE_URL.format(meeting=meeting))
|
return HttpResponseRedirect(settings.PROCEEDINGS_V1_BASE_URL.format(meeting=meeting))
|
||||||
|
@ -4141,72 +4148,12 @@ def proceedings(request, num=None):
|
||||||
kwargs['num'] = num
|
kwargs['num'] = num
|
||||||
return redirect('ietf.meeting.views.materials', **kwargs)
|
return redirect('ietf.meeting.views.materials', **kwargs)
|
||||||
|
|
||||||
begin_date = meeting.get_submission_start_date()
|
|
||||||
cut_off_date = meeting.get_submission_cut_off_date()
|
|
||||||
cor_cut_off_date = meeting.get_submission_correction_date()
|
|
||||||
today_utc = date_today(datetime.timezone.utc)
|
|
||||||
|
|
||||||
schedule = get_schedule(meeting, None)
|
|
||||||
sessions = (
|
|
||||||
meeting.session_set.with_current_status()
|
|
||||||
.filter(Q(timeslotassignments__schedule__in=[schedule, schedule.base if schedule else None])
|
|
||||||
| Q(current_status='notmeet'))
|
|
||||||
.select_related()
|
|
||||||
.order_by('-current_status')
|
|
||||||
)
|
|
||||||
|
|
||||||
plenaries, _ = organize_proceedings_sessions(
|
|
||||||
sessions.filter(name__icontains='plenary')
|
|
||||||
.exclude(current_status='notmeet')
|
|
||||||
)
|
|
||||||
irtf_meeting, irtf_not_meeting = organize_proceedings_sessions(
|
|
||||||
sessions.filter(group__parent__acronym = 'irtf').order_by('group__acronym')
|
|
||||||
)
|
|
||||||
# per Colin (datatracker #5010) - don't report not meeting rags
|
|
||||||
irtf_not_meeting = [item for item in irtf_not_meeting if item["group"].type_id != "rag"]
|
|
||||||
irtf = {"meeting_groups":irtf_meeting, "not_meeting_groups":irtf_not_meeting}
|
|
||||||
|
|
||||||
training, _ = organize_proceedings_sessions(
|
|
||||||
sessions.filter(group__acronym__in=['edu','iaoc'], type_id__in=['regular', 'other',])
|
|
||||||
.exclude(current_status='notmeet')
|
|
||||||
)
|
|
||||||
iab, _ = organize_proceedings_sessions(
|
|
||||||
sessions.filter(group__parent__acronym = 'iab')
|
|
||||||
.exclude(current_status='notmeet')
|
|
||||||
)
|
|
||||||
editorial, _ = organize_proceedings_sessions(
|
|
||||||
sessions.filter(group__acronym__in=['rsab','rswg'])
|
|
||||||
.exclude(current_status='notmeet')
|
|
||||||
)
|
|
||||||
|
|
||||||
ietf = sessions.filter(group__parent__type__slug = 'area').exclude(group__acronym__in=['edu','iepg','tools'])
|
|
||||||
ietf = list(ietf)
|
|
||||||
ietf.sort(key=lambda s: area_and_group_acronyms_from_session(s))
|
|
||||||
ietf_areas = []
|
|
||||||
for area, area_sessions in itertools.groupby(ietf, key=lambda s: s.group_parent_at_the_time()):
|
|
||||||
meeting_groups, not_meeting_groups = organize_proceedings_sessions(area_sessions)
|
|
||||||
ietf_areas.append((area, meeting_groups, not_meeting_groups))
|
|
||||||
|
|
||||||
cache_version = Document.objects.filter(session__meeting__number=meeting.number).aggregate(Max('time'))["time__max"]
|
|
||||||
|
|
||||||
with timezone.override(meeting.tz()):
|
with timezone.override(meeting.tz()):
|
||||||
return render(request, "meeting/proceedings.html", {
|
return render(request, "meeting/proceedings_wrapper.html", {
|
||||||
'meeting': meeting,
|
'meeting': meeting,
|
||||||
'plenaries': plenaries,
|
|
||||||
'training': training,
|
|
||||||
'irtf': irtf,
|
|
||||||
'iab': iab,
|
|
||||||
'editorial': editorial,
|
|
||||||
'ietf_areas': ietf_areas,
|
|
||||||
'cut_off_date': cut_off_date,
|
|
||||||
'cor_cut_off_date': cor_cut_off_date,
|
|
||||||
'submission_started': today_utc > begin_date,
|
|
||||||
'cache_version': cache_version,
|
|
||||||
'attendance': meeting.get_attendance(),
|
'attendance': meeting.get_attendance(),
|
||||||
'meetinghost_logo': {
|
'proceedings_content': generate_proceedings_content(meeting),
|
||||||
'max_height': settings.MEETINGHOST_LOGO_MAX_DISPLAY_HEIGHT,
|
|
||||||
'max_width': settings.MEETINGHOST_LOGO_MAX_DISPLAY_WIDTH,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
@role_required('Secretariat')
|
@role_required('Secretariat')
|
||||||
|
|
|
@ -1397,3 +1397,6 @@ if SERVER_MODE != 'production':
|
||||||
CSRF_TRUSTED_ORIGINS += ['http://localhost:8000', 'http://127.0.0.1:8000', 'http://[::1]:8000']
|
CSRF_TRUSTED_ORIGINS += ['http://localhost:8000', 'http://127.0.0.1:8000', 'http://[::1]:8000']
|
||||||
SESSION_COOKIE_SECURE = False
|
SESSION_COOKIE_SECURE = False
|
||||||
SESSION_COOKIE_SAMESITE = 'Lax'
|
SESSION_COOKIE_SAMESITE = 'Lax'
|
||||||
|
|
||||||
|
|
||||||
|
YOUTUBE_DOMAINS = ['www.youtube.com', 'youtube.com', 'youtu.be', 'm.youtube.com', 'youtube-nocookie.com', 'www.youtube-nocookie.com']
|
||||||
|
|
30
ietf/static/js/add_session_recordings.js
Normal file
30
ietf/static/js/add_session_recordings.js
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright The IETF Trust 2024-2025, All Rights Reserved
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const form = document.getElementById('delete_recordings_form')
|
||||||
|
const dialog = document.getElementById('delete_confirm_dialog')
|
||||||
|
const dialog_link = document.getElementById('delete_confirm_link')
|
||||||
|
const dialog_submit = document.getElementById('delete_confirm_submit')
|
||||||
|
const dialog_cancel = document.getElementById('delete_confirm_cancel')
|
||||||
|
|
||||||
|
dialog.style.maxWidth = '30vw'
|
||||||
|
|
||||||
|
form.addEventListener('submit', (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
dialog_submit.value = e.submitter.value
|
||||||
|
const recording_link = e.submitter.closest('tr').querySelector('a')
|
||||||
|
dialog_link.setAttribute('href', recording_link.getAttribute('href'))
|
||||||
|
dialog_link.textContent = recording_link.textContent
|
||||||
|
dialog.showModal()
|
||||||
|
})
|
||||||
|
|
||||||
|
dialog_cancel.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
dialog.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (dialog.open && e.key === 'Escape') {
|
||||||
|
dialog.close()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
78
ietf/templates/meeting/add_session_recordings.html
Normal file
78
ietf/templates/meeting/add_session_recordings.html
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||||
|
{% load origin static django_bootstrap5 %}
|
||||||
|
{% block title %}Add I-Ds to {{ session.meeting }} : {{ session.group.acronym }}{% endblock %}
|
||||||
|
{% block pagehead %}{{ form.media.css }}{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
{% origin %}
|
||||||
|
<h1>
|
||||||
|
Add Recordings to {{ session.meeting }}
|
||||||
|
{% if session_number %}: Session {{ session_number }}{% endif %}
|
||||||
|
<br>
|
||||||
|
<small class="text-body-secondary">{{ session.group.acronym }}
|
||||||
|
{% if session.name %}: {{ session.name }}{% endif %}
|
||||||
|
</small>
|
||||||
|
</h1>
|
||||||
|
{% if session.is_material_submission_cutoff %}
|
||||||
|
<div class="alert alert-warning my-3">
|
||||||
|
The deadline for submission corrections has passed. This may affect published proceedings.
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if already_linked|length > 0 %}
|
||||||
|
<h2 class="mt-5">Recordings already linked to this session</h2>
|
||||||
|
<form method="post" id="delete_recordings_form">
|
||||||
|
{% csrf_token %}
|
||||||
|
<table class="table table-sm table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Title</th>
|
||||||
|
<th scope="col">URL</th>
|
||||||
|
<th scope="col">Delete</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for sp in already_linked %}{% with recording_doc=sp.document %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ recording_doc.title }}</td>
|
||||||
|
<td><a href="{{ recording_doc.external_url }}">{{ recording_doc.external_url }}</a></td>
|
||||||
|
<td>
|
||||||
|
<button type="submit"
|
||||||
|
aria-label="Delete {{ recording_doc.title }}"
|
||||||
|
class="btn btn-danger"
|
||||||
|
name="delete"
|
||||||
|
value="{{ sp.pk }}">
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endwith %}{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
<dialog id="delete_confirm_dialog">
|
||||||
|
<p>Really delete the link to <a href="#" id="delete_confirm_link">(default)</a>?</p>
|
||||||
|
<form method="post" class="d-flex justify-content-between">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button class="btn btn-secondary" type="button" id="delete_confirm_cancel">Cancel</button>
|
||||||
|
<button class="btn btn-danger" type="submit" name="delete" id="delete_confirm_submit">Delete</button>
|
||||||
|
</form>
|
||||||
|
</dialog>
|
||||||
|
<h2 class="mt-5">Add a recording to this session</h2>
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% bootstrap_form form %}
|
||||||
|
<button class="btn btn-{% if session.is_material_submission_cutoff %}warning{% else %}primary{% endif %}"
|
||||||
|
type="submit">
|
||||||
|
Add recording
|
||||||
|
</button>
|
||||||
|
<a class="btn btn-secondary float-end"
|
||||||
|
href="{% url 'ietf.meeting.views.session_details' num=session.meeting.number acronym=session.group.acronym %}">
|
||||||
|
Back
|
||||||
|
</a>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
{% block js %}
|
||||||
|
{{ form.media.js }}
|
||||||
|
<script src="{% static 'ietf/js/add_session_recordings.js' %}"></script>
|
||||||
|
{% endblock %}
|
|
@ -1,184 +1,160 @@
|
||||||
{% extends "base.html" %}
|
{% include 'meeting/proceedings/introduction.html' with meeting=meeting only %}
|
||||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
<!-- Plenaries -->
|
||||||
{% load origin %}
|
{% if plenaries %}
|
||||||
{% load ietf_filters static %}
|
<h2 class="mt-5" id="plenaries">Plenaries</h2>
|
||||||
{% block pagehead %}
|
<table class="table table-sm table-striped tablesorter">
|
||||||
<link rel="stylesheet" href="{% static "ietf/css/list.css" %}">
|
<thead>
|
||||||
{% endblock %}
|
<tr>
|
||||||
{% block title %}
|
<th scope="col" data-sort="group">Group</th>
|
||||||
IETF {{ meeting.number }}
|
<th scope="col" data-sort="artifacts">Artifacts</th>
|
||||||
{% if not meeting.proceedings_final %}Draft{% endif %}
|
<th scope="col" data-sort="recordings">Recordings</th>
|
||||||
Proceedings
|
<th scope="col" data-sort="slides">Slides</th>
|
||||||
{% endblock %}
|
<th scope="col" data-sort="drafts">Internet-Drafts</th>
|
||||||
{% block content %}
|
</tr>
|
||||||
{% origin %}
|
</thead>
|
||||||
{% include 'meeting/proceedings/title.html' with meeting=meeting attendance=attendance only %}
|
<tbody>
|
||||||
{% if user|has_role:"Secretariat" and not meeting.proceedings_final %}
|
{% for entry in plenaries %}
|
||||||
<a class="btn btn-warning finalize-button"
|
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
||||||
href="{% url 'ietf.meeting.views.finalize_proceedings' num=meeting.number %}">
|
{% endfor %}
|
||||||
Finalize proceedings
|
</tbody>
|
||||||
</a>
|
</table>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{# cache for 15 minutes, as long as there's no proceedings activity. takes 4-8 seconds to generate. #}
|
<!-- Working groups -->
|
||||||
{% load cache %}
|
{% for area, meeting_groups, not_meeting_groups in ietf_areas %}
|
||||||
{% cache 900 ietf_meeting_proceedings meeting.number cache_version %}
|
<h2 class="mt-5" id="{{ area.acronym }}">
|
||||||
{% include 'meeting/proceedings/introduction.html' with meeting=meeting only %}
|
{{ area.acronym|upper }} <small class="text-body-secondary">{{ area.name }}</small>
|
||||||
<!-- Plenaries -->
|
</h2>
|
||||||
{% if plenaries %}
|
{% if meeting_groups %}
|
||||||
<h2 class="mt-5" id="plenaries">Plenaries</h2>
|
|
||||||
<table class="table table-sm table-striped tablesorter">
|
<table class="table table-sm table-striped tablesorter">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" data-sort="group">Group</th>
|
<th scope="col" data-sort="group">Group</th>
|
||||||
<th scope="col" data-sort="artifacts">Artifacts</th>
|
<th scope="col" data-sort="artifacts">Artifacts</th>
|
||||||
<th scope="col" data-sort="recordings">Recordings</th>
|
<th scope="col" data-sort="recordings">Recordings</th>
|
||||||
<th scope="col" data-sort="slides">Slides</th>
|
<th scope="col" data-sort="slides">Slides</th>
|
||||||
<th scope="col" data-sort="drafts">Internet-Drafts</th>
|
<th scope="col" data-sort="drafts">Internet-Drafts</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for entry in plenaries %}
|
{% for entry in meeting_groups %}
|
||||||
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<!-- Working groups -->
|
{% if not_meeting_groups %}
|
||||||
{% for area, meeting_groups, not_meeting_groups in ietf_areas %}
|
<p>
|
||||||
<h2 class="mt-5" id="{{ area.acronym }}">
|
{{ area.name }} groups not meeting:
|
||||||
{{ area.acronym|upper }} <small class="text-body-secondary">{{ area.name }}</small>
|
{% for entry in not_meeting_groups %}
|
||||||
</h2>
|
{% if entry.name == "" %}{# do not show named sessions in this list #}
|
||||||
{% if meeting_groups %}
|
<a href="{% url 'ietf.group.views.group_home' acronym=entry.group.acronym %}">
|
||||||
<table class="table table-sm table-striped tablesorter">
|
{{ entry.group.acronym }}
|
||||||
<thead>
|
</a>{% if not forloop.last %},{% endif %}
|
||||||
<tr>
|
{% endif %}
|
||||||
<th scope="col" data-sort="group">Group</th>
|
{% endfor %}
|
||||||
<th scope="col" data-sort="artifacts">Artifacts</th>
|
</p>
|
||||||
<th scope="col" data-sort="recordings">Recordings</th>
|
<table class="table table-sm table-striped">
|
||||||
<th scope="col" data-sort="slides">Slides</th>
|
|
||||||
<th scope="col" data-sort="drafts">Internet-Drafts</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for entry in meeting_groups %}
|
|
||||||
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{% endif %}
|
|
||||||
{% if not_meeting_groups %}
|
|
||||||
<p>
|
|
||||||
{{ area.name }} groups not meeting:
|
|
||||||
{% for entry in not_meeting_groups %}
|
|
||||||
{% if entry.name == "" %}{# do not show named sessions in this list #}
|
|
||||||
<a href="{% url 'ietf.group.views.group_home' acronym=entry.group.acronym %}">
|
|
||||||
{{ entry.group.acronym }}
|
|
||||||
</a>{% if not forloop.last %},{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</p>
|
|
||||||
<table class="table table-sm table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col"></th>
|
|
||||||
<th scope="col"></th>
|
|
||||||
<th scope="col"></th>
|
|
||||||
<th scope="col"></th>
|
|
||||||
<th scope="col"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for entry in not_meeting_groups %}{% if entry.has_materials %}
|
|
||||||
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
|
||||||
{% endif %}{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
<!-- Training Sessions -->
|
|
||||||
{% if training %}
|
|
||||||
<h2 class="mt-5" id="training">Training</h2>
|
|
||||||
<table class="table table-sm table-striped tablesorter">
|
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" data-sort="group">Group</th>
|
<th scope="col"></th>
|
||||||
<th scope="col" data-sort="artifacts">Artifacts</th>
|
<th scope="col"></th>
|
||||||
<th scope="col" data-sort="recordings">Recordings</th>
|
<th scope="col"></th>
|
||||||
<th scope="col" data-sort="slides">Slides</th>
|
<th scope="col"></th>
|
||||||
<th scope="col" data-sort="drafts">Internet-Drafts</th>
|
<th scope="col"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for entry in training %}
|
{% for entry in not_meeting_groups %}{% if entry.has_materials %}
|
||||||
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=False only %}
|
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
||||||
{% endfor %}
|
{% endif %}{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<!-- IAB Sessions -->
|
{% endfor %}
|
||||||
{% if iab %}
|
<!-- Training Sessions -->
|
||||||
<h2 class="mt-5" id="iab">
|
{% if training %}
|
||||||
IAB <small class="text-body-secondary">Internet Architecture Board</small>
|
<h2 class="mt-5" id="training">Training</h2>
|
||||||
</h2>
|
<table class="table table-sm table-striped tablesorter">
|
||||||
<table class="table table-sm table-striped tablesorter">
|
<thead>
|
||||||
<thead>
|
<tr>
|
||||||
<tr>
|
<th scope="col" data-sort="group">Group</th>
|
||||||
<th scope="col" data-sort="group">
|
<th scope="col" data-sort="artifacts">Artifacts</th>
|
||||||
Group
|
<th scope="col" data-sort="recordings">Recordings</th>
|
||||||
</th>
|
<th scope="col" data-sort="slides">Slides</th>
|
||||||
<th scope="col" data-sort="artifacts">
|
<th scope="col" data-sort="drafts">Internet-Drafts</th>
|
||||||
Artifacts
|
</tr>
|
||||||
</th>
|
</thead>
|
||||||
<th scope="col" data-sort="recordings">
|
<tbody>
|
||||||
Recordings
|
{% for entry in training %}
|
||||||
</th>
|
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=False only %}
|
||||||
<th scope="col" data-sort="slides">
|
{% endfor %}
|
||||||
Slides
|
</tbody>
|
||||||
</th>
|
</table>
|
||||||
<th scope="col" data-sort="drafts">
|
{% endif %}
|
||||||
Internet-Drafts
|
<!-- IAB Sessions -->
|
||||||
</th>
|
{% if iab %}
|
||||||
</tr>
|
<h2 class="mt-5" id="iab">
|
||||||
</thead>
|
IAB <small class="text-body-secondary">Internet Architecture Board</small>
|
||||||
<tbody>
|
</h2>
|
||||||
{% for entry in iab %}
|
<table class="table table-sm table-striped tablesorter">
|
||||||
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
<thead>
|
||||||
{% endfor %}
|
<tr>
|
||||||
</tbody>
|
<th scope="col" data-sort="group">
|
||||||
</table>
|
Group
|
||||||
{% endif %}
|
</th>
|
||||||
<!-- IRTF Sessions -->
|
<th scope="col" data-sort="artifacts">
|
||||||
{% if irtf.meeting_groups %}
|
Artifacts
|
||||||
<h2 class="mt-5" id="irtf">
|
</th>
|
||||||
IRTF <small class="text-body-secondary">Internet Research Task Force</small>
|
<th scope="col" data-sort="recordings">
|
||||||
</h2>
|
Recordings
|
||||||
<table class="table table-sm table-striped tablesorter">
|
</th>
|
||||||
<thead>
|
<th scope="col" data-sort="slides">
|
||||||
<tr>
|
Slides
|
||||||
<th scope="col" data-sort="group">
|
</th>
|
||||||
Group
|
<th scope="col" data-sort="drafts">
|
||||||
</th>
|
Internet-Drafts
|
||||||
<th scope="col" data-sort="artifacts">
|
</th>
|
||||||
Artifacts
|
</tr>
|
||||||
</th>
|
</thead>
|
||||||
<th scope="col" data-sort="recordings">
|
<tbody>
|
||||||
Recordings
|
{% for entry in iab %}
|
||||||
</th>
|
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
||||||
<th scope="col" data-sort="slides">
|
{% endfor %}
|
||||||
Slides
|
</tbody>
|
||||||
</th>
|
</table>
|
||||||
<th scope="col" data-sort="drafts">
|
{% endif %}
|
||||||
Internet-Drafts
|
<!-- IRTF Sessions -->
|
||||||
</th>
|
{% if irtf.meeting_groups %}
|
||||||
</tr>
|
<h2 class="mt-5" id="irtf">
|
||||||
</thead>
|
IRTF <small class="text-body-secondary">Internet Research Task Force</small>
|
||||||
<tbody>
|
</h2>
|
||||||
{% for entry in irtf.meeting_groups %}
|
<table class="table table-sm table-striped tablesorter">
|
||||||
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
<thead>
|
||||||
{% endfor %}
|
<tr>
|
||||||
</tbody>
|
<th scope="col" data-sort="group">
|
||||||
</table>
|
Group
|
||||||
{% if irtf.not_meeting_groups %}
|
</th>
|
||||||
|
<th scope="col" data-sort="artifacts">
|
||||||
|
Artifacts
|
||||||
|
</th>
|
||||||
|
<th scope="col" data-sort="recordings">
|
||||||
|
Recordings
|
||||||
|
</th>
|
||||||
|
<th scope="col" data-sort="slides">
|
||||||
|
Slides
|
||||||
|
</th>
|
||||||
|
<th scope="col" data-sort="drafts">
|
||||||
|
Internet-Drafts
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for entry in irtf.meeting_groups %}
|
||||||
|
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% if irtf.not_meeting_groups %}
|
||||||
<p>
|
<p>
|
||||||
IRTF groups not meeting:
|
IRTF groups not meeting:
|
||||||
{% for entry in irtf.not_meeting_groups %}
|
{% for entry in irtf.not_meeting_groups %}
|
||||||
|
@ -191,18 +167,18 @@
|
||||||
</p>
|
</p>
|
||||||
<table class="table table-sm table-striped">
|
<table class="table table-sm table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col"></th>
|
<th scope="col"></th>
|
||||||
<th scope="col"></th>
|
<th scope="col"></th>
|
||||||
<th scope="col"></th>
|
<th scope="col"></th>
|
||||||
<th scope="col"></th>
|
<th scope="col"></th>
|
||||||
<th scope="col"></th>
|
<th scope="col"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for entry in irtf.not_meeting %}{% if entry.has_materials %}
|
{% for entry in irtf.not_meeting %}{% if entry.has_materials %}
|
||||||
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
||||||
{% endif %}{% endfor %}
|
{% endif %}{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -211,35 +187,29 @@
|
||||||
<h2 class="mt-5" id="editorial">Editorial Stream</h2>
|
<h2 class="mt-5" id="editorial">Editorial Stream</h2>
|
||||||
<table class="table table-sm table-striped tablesorter">
|
<table class="table table-sm table-striped tablesorter">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" data-sort="group">
|
<th scope="col" data-sort="group">
|
||||||
Group
|
Group
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" data-sort="artifacts">
|
<th scope="col" data-sort="artifacts">
|
||||||
Artifacts
|
Artifacts
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" data-sort="recordings">
|
<th scope="col" data-sort="recordings">
|
||||||
Recordings
|
Recordings
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" data-sort="slides">
|
<th scope="col" data-sort="slides">
|
||||||
Slides
|
Slides
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" data-sort="drafts">
|
<th scope="col" data-sort="drafts">
|
||||||
Internet-Drafts
|
Internet-Drafts
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for entry in editorial %}
|
{% for entry in editorial %}
|
||||||
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endcache %}
|
|
||||||
{% endblock %}
|
|
||||||
{% block js %}
|
|
||||||
<script src="{% static "ietf/js/list.js" %}">
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
27
ietf/templates/meeting/proceedings_wrapper.html
Normal file
27
ietf/templates/meeting/proceedings_wrapper.html
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||||
|
{% load origin %}
|
||||||
|
{% load ietf_filters static %}
|
||||||
|
{% block pagehead %}
|
||||||
|
<link rel="stylesheet" href="{% static "ietf/css/list.css" %}">
|
||||||
|
{% endblock %}
|
||||||
|
{% block title %}
|
||||||
|
IETF {{ meeting.number }}
|
||||||
|
{% if not meeting.proceedings_final %}Draft{% endif %}
|
||||||
|
Proceedings
|
||||||
|
{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
{% origin %}
|
||||||
|
{% include 'meeting/proceedings/title.html' with meeting=meeting attendance=attendance only %}
|
||||||
|
{% if user|has_role:"Secretariat" and not meeting.proceedings_final %}
|
||||||
|
<a class="btn btn-warning finalize-button"
|
||||||
|
href="{% url 'ietf.meeting.views.finalize_proceedings' num=meeting.number %}">
|
||||||
|
Finalize proceedings
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{{ proceedings_content }}
|
||||||
|
{% endblock %}
|
||||||
|
{% block js %}
|
||||||
|
<script src="{% static "ietf/js/list.js" %}">
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -368,5 +368,13 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if can_manage_materials %}
|
||||||
|
<a class="btn btn-primary"
|
||||||
|
href="{% url 'ietf.meeting.views.add_session_recordings' session_id=session.pk num=session.meeting.number %}">
|
||||||
|
Link additional recordings to session
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endwith %}{% endwith %}
|
{% endwith %}{% endwith %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -111,6 +111,7 @@
|
||||||
"ietf/static/images/irtf-logo-card.png",
|
"ietf/static/images/irtf-logo-card.png",
|
||||||
"ietf/static/images/irtf-logo-white.svg",
|
"ietf/static/images/irtf-logo-white.svg",
|
||||||
"ietf/static/images/irtf-logo.svg",
|
"ietf/static/images/irtf-logo.svg",
|
||||||
|
"ietf/static/js/add_session_recordings.js",
|
||||||
"ietf/static/js/agenda_filter.js",
|
"ietf/static/js/agenda_filter.js",
|
||||||
"ietf/static/js/agenda_materials.js",
|
"ietf/static/js/agenda_materials.js",
|
||||||
"ietf/static/js/complete-review.js",
|
"ietf/static/js/complete-review.js",
|
||||||
|
|
Loading…
Reference in a new issue