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",
|
||||
"dependencies": {
|
||||
"dockerode": "^4.0.3",
|
||||
"fs-extra": "^11.2.0",
|
||||
"dockerode": "^4.0.4",
|
||||
"fs-extra": "^11.3.0",
|
||||
"nanoid": "5.0.9",
|
||||
"nanoid-dictionary": "5.0.0-beta.1",
|
||||
"slugify": "1.6.6",
|
||||
|
@ -258,6 +258,7 @@
|
|||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
|
@ -290,6 +291,7 @@
|
|||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"tweetnacl": "^0.14.3"
|
||||
}
|
||||
|
@ -376,6 +378,7 @@
|
|||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
|
@ -389,9 +392,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/docker-modem": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.5.tgz",
|
||||
"integrity": "sha512-Cxw8uEcvNTRmsQuGqzzfiCnfGgf96tVJItLh8taOX0miTcIBALKH5TckCSuZbpbjP7uhAl81dOL9sxfa6HgCIg==",
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.6.tgz",
|
||||
"integrity": "sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1",
|
||||
"readable-stream": "^3.5.0",
|
||||
|
@ -403,14 +407,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/dockerode": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.3.tgz",
|
||||
"integrity": "sha512-QSXJFcBQNaGZO6U3qWW4B7p8yRIJn/dWmvL2AQWfO/bjptBBO6QYdVkYSYFz9qoivP2jsOHZfmXMAfrK0BMKyg==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.4.tgz",
|
||||
"integrity": "sha512-6GYP/EdzEY50HaOxTVTJ2p+mB5xDHTMJhS+UoGrVyS6VC+iQRh7kZ4FRpUYq6nziby7hPqWhOrFFUFTMUZJJ5w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@balena/dockerignore": "^1.0.2",
|
||||
"@grpc/grpc-js": "^1.11.1",
|
||||
"@grpc/proto-loader": "^0.7.13",
|
||||
"docker-modem": "^5.0.5",
|
||||
"docker-modem": "^5.0.6",
|
||||
"protobufjs": "^7.3.2",
|
||||
"tar-fs": "~2.0.1",
|
||||
"uuid": "^10.0.0"
|
||||
|
@ -466,9 +471,10 @@
|
|||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "11.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
|
||||
"integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
|
||||
"version": "11.3.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
|
||||
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
|
@ -651,12 +657,14 @@
|
|||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"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": {
|
||||
"version": "2.22.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz",
|
||||
"integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
|
@ -804,7 +812,8 @@
|
|||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"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": {
|
||||
"version": "2.0.0",
|
||||
|
@ -847,7 +856,8 @@
|
|||
"node_modules/split-ca": {
|
||||
"version": "1.0.1",
|
||||
"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": {
|
||||
"version": "1.16.0",
|
||||
|
@ -1010,7 +1020,8 @@
|
|||
"node_modules/tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"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": {
|
||||
"version": "6.20.0",
|
||||
|
@ -1411,9 +1422,9 @@
|
|||
}
|
||||
},
|
||||
"docker-modem": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.5.tgz",
|
||||
"integrity": "sha512-Cxw8uEcvNTRmsQuGqzzfiCnfGgf96tVJItLh8taOX0miTcIBALKH5TckCSuZbpbjP7uhAl81dOL9sxfa6HgCIg==",
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.6.tgz",
|
||||
"integrity": "sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==",
|
||||
"requires": {
|
||||
"debug": "^4.1.1",
|
||||
"readable-stream": "^3.5.0",
|
||||
|
@ -1422,14 +1433,14 @@
|
|||
}
|
||||
},
|
||||
"dockerode": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.3.tgz",
|
||||
"integrity": "sha512-QSXJFcBQNaGZO6U3qWW4B7p8yRIJn/dWmvL2AQWfO/bjptBBO6QYdVkYSYFz9qoivP2jsOHZfmXMAfrK0BMKyg==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.4.tgz",
|
||||
"integrity": "sha512-6GYP/EdzEY50HaOxTVTJ2p+mB5xDHTMJhS+UoGrVyS6VC+iQRh7kZ4FRpUYq6nziby7hPqWhOrFFUFTMUZJJ5w==",
|
||||
"requires": {
|
||||
"@balena/dockerignore": "^1.0.2",
|
||||
"@grpc/grpc-js": "^1.11.1",
|
||||
"@grpc/proto-loader": "^0.7.13",
|
||||
"docker-modem": "^5.0.5",
|
||||
"docker-modem": "^5.0.6",
|
||||
"protobufjs": "^7.3.2",
|
||||
"tar-fs": "~2.0.1",
|
||||
"uuid": "^10.0.0"
|
||||
|
@ -1473,9 +1484,9 @@
|
|||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "11.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
|
||||
"integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
|
||||
"version": "11.3.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
|
||||
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
|
||||
"requires": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
"name": "deploy-to-container",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"dockerode": "^4.0.3",
|
||||
"fs-extra": "^11.2.0",
|
||||
"dockerode": "^4.0.4",
|
||||
"fs-extra": "^11.3.0",
|
||||
"nanoid": "5.0.9",
|
||||
"nanoid-dictionary": "5.0.0-beta.1",
|
||||
"slugify": "1.6.6",
|
||||
|
|
407
dev/diff/package-lock.json
generated
407
dev/diff/package-lock.json
generated
|
@ -6,11 +6,11 @@
|
|||
"": {
|
||||
"name": "diff",
|
||||
"dependencies": {
|
||||
"chalk": "^5.3.0",
|
||||
"dockerode": "^4.0.2",
|
||||
"chalk": "^5.4.1",
|
||||
"dockerode": "^4.0.4",
|
||||
"enquirer": "^2.4.1",
|
||||
"extract-zip": "^2.0.1",
|
||||
"fs-extra": "^11.2.0",
|
||||
"fs-extra": "^11.3.0",
|
||||
"got": "^13.0.0",
|
||||
"keypress": "^0.2.1",
|
||||
"listr2": "^6.6.1",
|
||||
|
@ -29,6 +29,37 @@
|
|||
"resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz",
|
||||
"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": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
|
@ -129,6 +160,16 @@
|
|||
"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": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||
|
@ -138,6 +179,70 @@
|
|||
"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": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.3.0.tgz",
|
||||
|
@ -168,8 +273,7 @@
|
|||
"node_modules/@types/node": {
|
||||
"version": "18.6.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.5.tgz",
|
||||
"integrity": "sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw==",
|
||||
"optional": true
|
||||
"integrity": "sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw=="
|
||||
},
|
||||
"node_modules/@types/yauzl": {
|
||||
"version": "2.10.0",
|
||||
|
@ -228,6 +332,7 @@
|
|||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
|
@ -260,6 +365,7 @@
|
|||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"tweetnacl": "^0.14.3"
|
||||
}
|
||||
|
@ -315,9 +421,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
|
||||
"integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
|
||||
"integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||
},
|
||||
|
@ -440,14 +547,14 @@
|
|||
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="
|
||||
},
|
||||
"node_modules/cpu-features": {
|
||||
"version": "0.0.9",
|
||||
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.9.tgz",
|
||||
"integrity": "sha512-AKjgn2rP2yJyfbepsmLfiYcmtNn/2eUvocUyM/09yB0YDiz39HteK/5/T4Onf0pmdYDMgkBoGvRLvEguzyL7wQ==",
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz",
|
||||
"integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"buildcheck": "~0.0.6",
|
||||
"nan": "^2.17.0"
|
||||
"nan": "^2.19.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
|
@ -516,9 +623,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/docker-modem": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.3.tgz",
|
||||
"integrity": "sha512-89zhop5YVhcPEt5FpUFGr3cDyceGhq/F9J+ZndQ4KfqNvfbJpPMfgeixFgUj5OjCYAboElqODxY5Z1EBsSa6sg==",
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.6.tgz",
|
||||
"integrity": "sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1",
|
||||
"readable-stream": "^3.5.0",
|
||||
|
@ -530,13 +638,18 @@
|
|||
}
|
||||
},
|
||||
"node_modules/dockerode": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.2.tgz",
|
||||
"integrity": "sha512-9wM1BVpVMFr2Pw3eJNXrYYt6DT9k0xMcsSCjtPvyQ+xa1iPg/Mo3T/gUcwI0B2cczqCeCYRPF8yFYDwtFXT0+w==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.4.tgz",
|
||||
"integrity": "sha512-6GYP/EdzEY50HaOxTVTJ2p+mB5xDHTMJhS+UoGrVyS6VC+iQRh7kZ4FRpUYq6nziby7hPqWhOrFFUFTMUZJJ5w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@balena/dockerignore": "^1.0.2",
|
||||
"docker-modem": "^5.0.3",
|
||||
"tar-fs": "~2.0.1"
|
||||
"@grpc/grpc-js": "^1.11.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": {
|
||||
"node": ">= 8.0"
|
||||
|
@ -666,9 +779,10 @@
|
|||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "11.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
|
||||
"integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
|
||||
"version": "11.3.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
|
||||
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
|
@ -949,6 +1063,12 @@
|
|||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"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": {
|
||||
"version": "5.0.1",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
|
||||
|
@ -1145,9 +1271,10 @@
|
|||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/nan": {
|
||||
"version": "2.18.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz",
|
||||
"integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==",
|
||||
"version": "2.22.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz",
|
||||
"integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/normalize-url": {
|
||||
|
@ -1230,6 +1357,30 @@
|
|||
"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": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||
|
@ -1349,7 +1500,8 @@
|
|||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"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": {
|
||||
"version": "2.0.0",
|
||||
|
@ -1415,12 +1567,13 @@
|
|||
"node_modules/split-ca": {
|
||||
"version": "1.0.1",
|
||||
"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": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.15.0.tgz",
|
||||
"integrity": "sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==",
|
||||
"version": "1.16.0",
|
||||
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz",
|
||||
"integrity": "sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"asn1": "^0.2.6",
|
||||
|
@ -1430,8 +1583,8 @@
|
|||
"node": ">=10.16.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"cpu-features": "~0.0.9",
|
||||
"nan": "^2.18.0"
|
||||
"cpu-features": "~0.0.10",
|
||||
"nan": "^2.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
|
@ -1578,7 +1731,8 @@
|
|||
"node_modules/tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"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": {
|
||||
"version": "1.4.0",
|
||||
|
@ -1604,6 +1758,19 @@
|
|||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"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": {
|
||||
"version": "2.0.2",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
|
@ -1779,12 +1966,71 @@
|
|||
"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": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
|
||||
"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": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.3.0.tgz",
|
||||
|
@ -1806,8 +2052,7 @@
|
|||
"@types/node": {
|
||||
"version": "18.6.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.5.tgz",
|
||||
"integrity": "sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw==",
|
||||
"optional": true
|
||||
"integrity": "sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw=="
|
||||
},
|
||||
"@types/yauzl": {
|
||||
"version": "2.10.0",
|
||||
|
@ -1909,9 +2154,9 @@
|
|||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
|
||||
"integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w=="
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
|
||||
"integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="
|
||||
},
|
||||
"chownr": {
|
||||
"version": "1.1.4",
|
||||
|
@ -1994,13 +2239,13 @@
|
|||
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="
|
||||
},
|
||||
"cpu-features": {
|
||||
"version": "0.0.9",
|
||||
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.9.tgz",
|
||||
"integrity": "sha512-AKjgn2rP2yJyfbepsmLfiYcmtNn/2eUvocUyM/09yB0YDiz39HteK/5/T4Onf0pmdYDMgkBoGvRLvEguzyL7wQ==",
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz",
|
||||
"integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"buildcheck": "~0.0.6",
|
||||
"nan": "^2.17.0"
|
||||
"nan": "^2.19.0"
|
||||
}
|
||||
},
|
||||
"cross-spawn": {
|
||||
|
@ -2042,9 +2287,9 @@
|
|||
"integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="
|
||||
},
|
||||
"docker-modem": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.3.tgz",
|
||||
"integrity": "sha512-89zhop5YVhcPEt5FpUFGr3cDyceGhq/F9J+ZndQ4KfqNvfbJpPMfgeixFgUj5OjCYAboElqODxY5Z1EBsSa6sg==",
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.6.tgz",
|
||||
"integrity": "sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==",
|
||||
"requires": {
|
||||
"debug": "^4.1.1",
|
||||
"readable-stream": "^3.5.0",
|
||||
|
@ -2053,13 +2298,17 @@
|
|||
}
|
||||
},
|
||||
"dockerode": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.2.tgz",
|
||||
"integrity": "sha512-9wM1BVpVMFr2Pw3eJNXrYYt6DT9k0xMcsSCjtPvyQ+xa1iPg/Mo3T/gUcwI0B2cczqCeCYRPF8yFYDwtFXT0+w==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.4.tgz",
|
||||
"integrity": "sha512-6GYP/EdzEY50HaOxTVTJ2p+mB5xDHTMJhS+UoGrVyS6VC+iQRh7kZ4FRpUYq6nziby7hPqWhOrFFUFTMUZJJ5w==",
|
||||
"requires": {
|
||||
"@balena/dockerignore": "^1.0.2",
|
||||
"docker-modem": "^5.0.3",
|
||||
"tar-fs": "~2.0.1"
|
||||
"@grpc/grpc-js": "^1.11.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": {
|
||||
|
@ -2155,9 +2404,9 @@
|
|||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "11.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
|
||||
"integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
|
||||
"version": "11.3.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
|
||||
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
|
||||
"requires": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
|
@ -2342,6 +2591,11 @@
|
|||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"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": {
|
||||
"version": "5.0.1",
|
||||
"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": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
|
||||
|
@ -2462,9 +2721,9 @@
|
|||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.18.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz",
|
||||
"integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==",
|
||||
"version": "2.22.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz",
|
||||
"integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==",
|
||||
"optional": true
|
||||
},
|
||||
"normalize-url": {
|
||||
|
@ -2517,6 +2776,25 @@
|
|||
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz",
|
||||
"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": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||
|
@ -2636,14 +2914,14 @@
|
|||
"integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ=="
|
||||
},
|
||||
"ssh2": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.15.0.tgz",
|
||||
"integrity": "sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==",
|
||||
"version": "1.16.0",
|
||||
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz",
|
||||
"integrity": "sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==",
|
||||
"requires": {
|
||||
"asn1": "^0.2.6",
|
||||
"bcrypt-pbkdf": "^1.0.2",
|
||||
"cpu-features": "~0.0.9",
|
||||
"nan": "^2.18.0"
|
||||
"cpu-features": "~0.0.10",
|
||||
"nan": "^2.20.0"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
|
@ -2774,6 +3052,11 @@
|
|||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"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": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
"name": "diff",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"chalk": "^5.3.0",
|
||||
"dockerode": "^4.0.2",
|
||||
"chalk": "^5.4.1",
|
||||
"dockerode": "^4.0.4",
|
||||
"enquirer": "^2.4.1",
|
||||
"extract-zip": "^2.0.1",
|
||||
"fs-extra": "^11.2.0",
|
||||
"fs-extra": "^11.3.0",
|
||||
"got": "^13.0.0",
|
||||
"keypress": "^0.2.1",
|
||||
"listr2": "^6.6.1",
|
||||
|
|
|
@ -101,6 +101,7 @@ services:
|
|||
# stop_grace_period: 1m
|
||||
# volumes:
|
||||
# - .:/workspace
|
||||
# - app-assets:/assets
|
||||
|
||||
volumes:
|
||||
postgresdb-data:
|
||||
|
|
|
@ -7,7 +7,7 @@ import factory
|
|||
import factory.fuzzy
|
||||
import datetime
|
||||
|
||||
from typing import Optional # pyflakes:ignore
|
||||
from typing import Any # pyflakes:ignore
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
|
@ -37,13 +37,16 @@ class BaseDocumentFactory(factory.django.DjangoModelFactory):
|
|||
model = Document
|
||||
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)
|
||||
abstract = factory.Faker('paragraph', nb_sentences=5)
|
||||
abstract: Any = factory.Faker('paragraph', nb_sentences=5)
|
||||
rev = '00'
|
||||
std_level_id = None # type: Optional[str]
|
||||
std_level_id: Any = None
|
||||
intended_std_level_id = None
|
||||
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)
|
||||
|
||||
|
||||
|
@ -282,7 +285,7 @@ class DocEventFactory(factory.django.DjangoModelFactory):
|
|||
|
||||
type = 'added_comment'
|
||||
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)
|
||||
|
||||
@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)))
|
||||
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):
|
||||
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',' '))
|
||||
|
||||
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 })
|
||||
self.client.login(username="secretary", password="secretary+password")
|
||||
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/(?P<state>pending|removed|parked)/$', views.admin),
|
||||
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+)/addcomment/$', views.add_comment),
|
||||
url(r'^(?P<id>\d+)/addemail/$', views.add_email),
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
from textwrap import dedent
|
||||
|
||||
from ietf.ipr.mail import process_response_email, UndeliverableIprResponseError
|
||||
from ietf.ipr.models import IprDocRel
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
|
@ -64,30 +63,6 @@ def related_docs(doc, relationship=('replaces', 'obs'), reverse_relationship=("b
|
|||
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):
|
||||
from ietf.api.views import EmailIngestionError # avoid circular import
|
||||
|
|
|
@ -445,35 +445,6 @@ def history(request, id):
|
|||
'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):
|
||||
"""Submit a new IPR Disclosure. If the updates field != None, this disclosure
|
||||
|
|
|
@ -3,10 +3,42 @@
|
|||
# Celery task definitions
|
||||
#
|
||||
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
|
||||
|
||||
|
||||
@shared_task
|
||||
def agenda_data_refresh():
|
||||
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.template import Context, Template
|
||||
from django.utils import timezone
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.text import slugify
|
||||
|
||||
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.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.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 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 get_summary_by_area, get_summary_by_type, get_summary_by_purpose, generate_agenda_data
|
||||
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())
|
||||
#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):
|
||||
# start with no upcoming IETF meetings, just an interim
|
||||
MeetingFactory(
|
||||
|
@ -2082,7 +2125,8 @@ class EditTimeslotsTests(TestCase):
|
|||
@staticmethod
|
||||
def create_bare_meeting(number=120) -> Meeting:
|
||||
"""Create a basic IETF meeting"""
|
||||
return MeetingFactory(
|
||||
# Call create() explicitly so mypy sees the correct type
|
||||
return MeetingFactory.create(
|
||||
type_id='ietf',
|
||||
number=number,
|
||||
date=date_today() + datetime.timedelta(days=10),
|
||||
|
@ -7363,6 +7407,118 @@ class SessionTests(TestCase):
|
|||
self.assertEqual(r.status_code,302)
|
||||
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):
|
||||
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'
|
||||
return path.open('rb')
|
||||
|
||||
def _assertMeetingHostsDisplayed(self, response, meeting):
|
||||
pq = PyQuery(response.content)
|
||||
def _assertMeetingHostsDisplayed(self, pq: PyQuery, meeting):
|
||||
host_divs = pq('div.host-logo')
|
||||
self.assertEqual(len(host_divs), meeting.meetinghosts.count(), 'Should have a logo for every meeting host')
|
||||
self.assertEqual(
|
||||
|
@ -8158,12 +8313,11 @@ class ProceedingsTests(BaseMeetingTestCase):
|
|||
'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"""
|
||||
expected_materials = [
|
||||
m for m in meeting.proceedings_materials.order_by('type__order') if m.active()
|
||||
]
|
||||
pq = PyQuery(response.content)
|
||||
links = pq('div.proceedings-material a')
|
||||
self.assertEqual(len(links), len(expected_materials), 'Should have an entry for each active ProceedingsMaterial')
|
||||
self.assertEqual(
|
||||
|
@ -8172,9 +8326,8 @@ class ProceedingsTests(BaseMeetingTestCase):
|
|||
'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"""
|
||||
pq = PyQuery(response.content)
|
||||
sections = ["plenaries", "gen", "iab", "editorial", "irtf", "training"]
|
||||
for section in sections:
|
||||
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):
|
||||
"""Proceedings should be displayed correctly
|
||||
|
||||
Currently only tests that the view responds with a 200 response code and checks the ProceedingsMaterials
|
||||
at the top of the proceedings. Ought to actually test the display of the individual group/session
|
||||
materials as well.
|
||||
Proceedings contents are tested in detail when testing generate_proceedings_content.
|
||||
"""
|
||||
# number must be >97 (settings.PROCEEDINGS_VERSION_CHANGES)
|
||||
meeting = make_meeting_test_data(meeting=MeetingFactory(type_id='ietf', number='100'))
|
||||
session = Session.objects.filter(meeting=meeting, group__acronym="mars").first()
|
||||
GroupEventFactory(group=session.group,type='status_update')
|
||||
|
@ -8210,16 +8362,72 @@ class ProceedingsTests(BaseMeetingTestCase):
|
|||
self._create_proceedings_materials(meeting)
|
||||
|
||||
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.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:
|
||||
self.assertContains(r, meeting.city)
|
||||
if len(meeting.venue_name) > 0:
|
||||
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
|
||||
pq = PyQuery(r.content)
|
||||
pq = PyQuery(proceedings_content)
|
||||
self.assertNotEqual(
|
||||
pq('a[href="{}"]'.format(
|
||||
urlreverse('ietf.meeting.views.proceedings_overview', kwargs=dict(num=meeting.number)))
|
||||
|
@ -8250,9 +8458,17 @@ class ProceedingsTests(BaseMeetingTestCase):
|
|||
)
|
||||
|
||||
# configurable contents
|
||||
self._assertMeetingHostsDisplayed(r, meeting)
|
||||
self._assertProceedingsMaterialsDisplayed(r, meeting)
|
||||
self._assertGroupSessions(r, meeting)
|
||||
self._assertProceedingsMaterialsDisplayed(pq, meeting)
|
||||
self._assertGroupSessions(pq)
|
||||
|
||||
# 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):
|
||||
"""Session with a name should appear separately in the proceedings"""
|
||||
|
|
|
@ -16,6 +16,7 @@ class AgendaRedirectView(RedirectView):
|
|||
safe_for_all_meeting_types = [
|
||||
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+)/recordings$', views.add_session_recordings),
|
||||
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+)/minutes$', views.upload_session_minutes),
|
||||
|
@ -63,7 +64,8 @@ type_ietf_only_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.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)
|
||||
]
|
||||
|
||||
|
|
|
@ -3,16 +3,19 @@
|
|||
import datetime
|
||||
import itertools
|
||||
import os
|
||||
from hashlib import sha384
|
||||
|
||||
import pytz
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
from django.conf import settings
|
||||
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.template.loader import render_to_string
|
||||
from django.utils import timezone
|
||||
|
@ -23,7 +26,7 @@ import debug # pyflakes:ignore
|
|||
from ietf.dbtemplate.models import DBTemplate
|
||||
from ietf.meeting.models import (Session, SchedulingEvent, TimeSlot,
|
||||
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.group.models import Group
|
||||
from ietf.group.utils import can_manage_materials
|
||||
|
@ -225,12 +228,7 @@ def generate_bluesheet(request, session):
|
|||
'session': session,
|
||||
'data': data,
|
||||
})
|
||||
fd, name = tempfile.mkstemp(suffix=".txt", text=True)
|
||||
os.close(fd)
|
||||
with open(name, "w") as file:
|
||||
file.write(text)
|
||||
with open(name, "br") as file:
|
||||
return save_bluesheet(request, session, file)
|
||||
return save_bluesheet(request, session, ContentFile(text.encode("utf-8"), name="unusednamepartsothereisanextension.txt"))
|
||||
|
||||
|
||||
def finalize(request, meeting):
|
||||
|
@ -853,6 +851,26 @@ def create_recording(session, url, title=None, user=None):
|
|||
|
||||
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):
|
||||
'''
|
||||
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']))
|
||||
attended = Attended.objects.filter(session__in=sessions).values_list('person', flat=True).distinct()
|
||||
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
|
||||
import jsonschema
|
||||
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 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_request
|
||||
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 session_time_for_sorting
|
||||
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 preprocess_meeting_important_dates
|
||||
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.message.utils import infer_message
|
||||
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.text import xslugify
|
||||
from ietf.utils.timezone import datetime_today, date_today
|
||||
from ietf.settings import YOUTUBE_DOMAINS
|
||||
|
||||
from .forms import (InterimMeetingModelForm, InterimAnnounceForm, InterimSessionModelForm,
|
||||
InterimCancelForm, InterimSessionInlineFormSet, RequestMinutesForm,
|
||||
|
@ -2568,6 +2575,89 @@ def add_session_drafts(request, session_id, num):
|
|||
'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):
|
||||
"""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)
|
||||
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 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)
|
||||
|
||||
|
||||
# Early proceedings were hosted on www.ietf.org rather than the datatracker
|
||||
if meeting.proceedings_format_version == 1:
|
||||
return HttpResponseRedirect(settings.PROCEEDINGS_V1_BASE_URL.format(meeting=meeting))
|
||||
|
@ -4141,72 +4148,12 @@ def proceedings(request, num=None):
|
|||
kwargs['num'] = num
|
||||
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()):
|
||||
return render(request, "meeting/proceedings.html", {
|
||||
return render(request, "meeting/proceedings_wrapper.html", {
|
||||
'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(),
|
||||
'meetinghost_logo': {
|
||||
'max_height': settings.MEETINGHOST_LOGO_MAX_DISPLAY_HEIGHT,
|
||||
'max_width': settings.MEETINGHOST_LOGO_MAX_DISPLAY_WIDTH,
|
||||
}
|
||||
'proceedings_content': generate_proceedings_content(meeting),
|
||||
})
|
||||
|
||||
@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']
|
||||
SESSION_COOKIE_SECURE = False
|
||||
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" %}
|
||||
{# 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 %}
|
||||
{# cache for 15 minutes, as long as there's no proceedings activity. takes 4-8 seconds to generate. #}
|
||||
{% load cache %}
|
||||
{% cache 900 ietf_meeting_proceedings meeting.number cache_version %}
|
||||
{% include 'meeting/proceedings/introduction.html' with meeting=meeting only %}
|
||||
<!-- Plenaries -->
|
||||
{% if plenaries %}
|
||||
<h2 class="mt-5" id="plenaries">Plenaries</h2>
|
||||
{% include 'meeting/proceedings/introduction.html' with meeting=meeting only %}
|
||||
<!-- Plenaries -->
|
||||
{% if plenaries %}
|
||||
<h2 class="mt-5" id="plenaries">Plenaries</h2>
|
||||
<table class="table table-sm table-striped tablesorter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" data-sort="group">Group</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 plenaries %}
|
||||
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
<!-- Working groups -->
|
||||
{% for area, meeting_groups, not_meeting_groups in ietf_areas %}
|
||||
<h2 class="mt-5" id="{{ area.acronym }}">
|
||||
{{ area.acronym|upper }} <small class="text-body-secondary">{{ area.name }}</small>
|
||||
</h2>
|
||||
{% if meeting_groups %}
|
||||
<table class="table table-sm table-striped tablesorter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" data-sort="group">Group</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>
|
||||
<tr>
|
||||
<th scope="col" data-sort="group">Group</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 plenaries %}
|
||||
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
||||
{% endfor %}
|
||||
{% for entry in meeting_groups %}
|
||||
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
<!-- Working groups -->
|
||||
{% for area, meeting_groups, not_meeting_groups in ietf_areas %}
|
||||
<h2 class="mt-5" id="{{ area.acronym }}">
|
||||
{{ area.acronym|upper }} <small class="text-body-secondary">{{ area.name }}</small>
|
||||
</h2>
|
||||
{% if meeting_groups %}
|
||||
<table class="table table-sm table-striped tablesorter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" data-sort="group">Group</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 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">
|
||||
{% 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" data-sort="group">Group</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>
|
||||
<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 training %}
|
||||
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=False only %}
|
||||
{% endfor %}
|
||||
{% 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 %}
|
||||
<!-- IAB Sessions -->
|
||||
{% if iab %}
|
||||
<h2 class="mt-5" id="iab">
|
||||
IAB <small class="text-body-secondary">Internet Architecture Board</small>
|
||||
</h2>
|
||||
<table class="table table-sm table-striped tablesorter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" data-sort="group">
|
||||
Group
|
||||
</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 iab %}
|
||||
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
<!-- IRTF Sessions -->
|
||||
{% if irtf.meeting_groups %}
|
||||
<h2 class="mt-5" id="irtf">
|
||||
IRTF <small class="text-body-secondary">Internet Research Task Force</small>
|
||||
</h2>
|
||||
<table class="table table-sm table-striped tablesorter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" data-sort="group">
|
||||
Group
|
||||
</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 %}
|
||||
{% endfor %}
|
||||
<!-- Training Sessions -->
|
||||
{% if training %}
|
||||
<h2 class="mt-5" id="training">Training</h2>
|
||||
<table class="table table-sm table-striped tablesorter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" data-sort="group">Group</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 training %}
|
||||
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=False only %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
<!-- IAB Sessions -->
|
||||
{% if iab %}
|
||||
<h2 class="mt-5" id="iab">
|
||||
IAB <small class="text-body-secondary">Internet Architecture Board</small>
|
||||
</h2>
|
||||
<table class="table table-sm table-striped tablesorter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" data-sort="group">
|
||||
Group
|
||||
</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 iab %}
|
||||
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
<!-- IRTF Sessions -->
|
||||
{% if irtf.meeting_groups %}
|
||||
<h2 class="mt-5" id="irtf">
|
||||
IRTF <small class="text-body-secondary">Internet Research Task Force</small>
|
||||
</h2>
|
||||
<table class="table table-sm table-striped tablesorter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" data-sort="group">
|
||||
Group
|
||||
</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>
|
||||
IRTF groups not meeting:
|
||||
{% for entry in irtf.not_meeting_groups %}
|
||||
|
@ -191,18 +167,18 @@
|
|||
</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>
|
||||
<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 irtf.not_meeting %}{% if entry.has_materials %}
|
||||
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
||||
{% endif %}{% endfor %}
|
||||
{% for entry in irtf.not_meeting %}{% if entry.has_materials %}
|
||||
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
||||
{% endif %}{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
@ -211,35 +187,29 @@
|
|||
<h2 class="mt-5" id="editorial">Editorial Stream</h2>
|
||||
<table class="table table-sm table-striped tablesorter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" data-sort="group">
|
||||
Group
|
||||
</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>
|
||||
<tr>
|
||||
<th scope="col" data-sort="group">
|
||||
Group
|
||||
</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 editorial %}
|
||||
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
||||
{% endfor %}
|
||||
{% for entry in editorial %}
|
||||
{% include "meeting/group_proceedings.html" with entry=entry meeting=meeting show_agenda=True only %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endcache %}
|
||||
{% endblock %}
|
||||
{% block js %}
|
||||
<script src="{% static "ietf/js/list.js" %}">
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
|
|
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>
|
||||
</table>
|
||||
{% 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 %}
|
||||
{% endfor %}
|
||||
|
|
|
@ -111,6 +111,7 @@
|
|||
"ietf/static/images/irtf-logo-card.png",
|
||||
"ietf/static/images/irtf-logo-white.svg",
|
||||
"ietf/static/images/irtf-logo.svg",
|
||||
"ietf/static/js/add_session_recordings.js",
|
||||
"ietf/static/js/agenda_filter.js",
|
||||
"ietf/static/js/agenda_materials.js",
|
||||
"ietf/static/js/complete-review.js",
|
||||
|
|
Loading…
Reference in a new issue