galaxy-commits
Threads by month
- ----- 2026 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- 15302 discussions
commit/galaxy-central: nsoranzo: Fix variable name causing a NameError.
by commits-noreply@bitbucket.org 15 Dec '14
by commits-noreply@bitbucket.org 15 Dec '14
15 Dec '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/733696644b89/
Changeset: 733696644b89
User: nsoranzo
Date: 2014-12-15 15:43:16+00:00
Summary: Fix variable name causing a NameError.
Affected #: 1 file
diff -r 3c0acd32969bc282603607037522a2e16d70d074 -r 733696644b89ba39bbeeaf5908b97f4698d6868c lib/galaxy/managers/folders.py
--- a/lib/galaxy/managers/folders.py
+++ b/lib/galaxy/managers/folders.py
@@ -195,7 +195,7 @@
if ( ( len( encoded_folder_id ) % 16 == 1 ) and encoded_folder_id.startswith( 'F' ) ):
cut_id = encoded_folder_id[ 1: ]
else:
- raise exceptions.MalformedId( 'Malformed folder id ( %s ) specified, unable to decode.' % str( encoded_id ) )
+ raise exceptions.MalformedId( 'Malformed folder id ( %s ) specified, unable to decode.' % str( encoded_folder_id ) )
return cut_id
def decode_folder_id( self, trans, encoded_folder_id ):
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
[galaxyproject/usegalaxy-playbook] fdfbf4: added ga_code for google analytics purposes
by GitHub 15 Dec '14
by GitHub 15 Dec '14
15 Dec '14
Branch: refs/heads/master
Home: https://github.com/galaxyproject/usegalaxy-playbook
Commit: fdfbf4b48b6f86f9a524772c11a0f41a95fdad76
https://github.com/galaxyproject/usegalaxy-playbook/commit/fdfbf4b48b6f86f9…
Author: Marten <emulatorer(a)gmail.com>
Date: 2014-12-12 (Fri, 12 Dec 2014)
Changed paths:
M stage/group_vars/galaxyservers.yml
Log Message:
-----------
added ga_code for google analytics purposes
Commit: 9485f82f52b57445b6e45d63845bc162c7ab155b
https://github.com/galaxyproject/usegalaxy-playbook/commit/9485f82f52b57445…
Author: Marten <emulatorer(a)gmail.com>
Date: 2014-12-12 (Fri, 12 Dec 2014)
Changed paths:
M production/group_vars/galaxyservers.yml
Log Message:
-----------
add ga_code for google analytics purposes
Commit: 8f1785c81d7ee068b564f079dd02f4eaa33c49de
https://github.com/galaxyproject/usegalaxy-playbook/commit/8f1785c81d7ee068…
Author: Marten <emulatorer(a)gmail.com>
Date: 2014-12-12 (Fri, 12 Dec 2014)
Changed paths:
M templates/galaxy/common/static/welcome.html.j2
Log Message:
-----------
add ga script and onclick event sending
to track whether the cycle thing is used
Commit: 33e106b5e2f13eeb88b5c5356b4c49a87a615c62
https://github.com/galaxyproject/usegalaxy-playbook/commit/33e106b5e2f13eeb…
Author: Marten <emulatorer(a)gmail.com>
Date: 2014-12-12 (Fri, 12 Dec 2014)
Changed paths:
M templates/galaxy/common/static/welcome.html.j2
Log Message:
-----------
do not init ga twice
use the one in top.window instead
Commit: 1bbaae7a09fa230a97337d42d5b4cd754d377ba1
https://github.com/galaxyproject/usegalaxy-playbook/commit/1bbaae7a09fa230a…
Author: Marten <emulatorer(a)gmail.com>
Date: 2014-12-15 (Mon, 15 Dec 2014)
Changed paths:
M production/group_vars/galaxyservers.yml
M stage/group_vars/galaxyservers.yml
M templates/galaxy/common/static/welcome.html.j2
Log Message:
-----------
Merge pull request #2 from martenson/master
track how people click on the carousel links
Compare: https://github.com/galaxyproject/usegalaxy-playbook/compare/15b2fa183d58...…
1
0
commit/galaxy-central: dannon: Retab, whitespace in base.less
by commits-noreply@bitbucket.org 15 Dec '14
by commits-noreply@bitbucket.org 15 Dec '14
15 Dec '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/3c0acd32969b/
Changeset: 3c0acd32969b
User: dannon
Date: 2014-12-15 14:56:25+00:00
Summary: Retab, whitespace in base.less
Affected #: 1 file
diff -r 36457cc1ec26c440e7a996ce51eb87ed7c58cc4d -r 3c0acd32969bc282603607037522a2e16d70d074 static/style/src/less/base.less
--- a/static/style/src/less/base.less
+++ b/static/style/src/less/base.less
@@ -1,7 +1,7 @@
// bootstrap.less modified for Galaxy
@import "bootstrap.less";
-// Galaxy specific variables
+// Galaxy specific variables
@import "galaxy_variables.less";
@white: #fff;
@@ -92,23 +92,23 @@
}
#messagebox {
- position:absolute;
+ position:absolute;
top: @navbar-height + @layout_top_padding + 1;
- left:0;
- width:100%;
+ left:0;
+ width:100%;
height: @panel-message-height !important;
- overflow: hidden;
+ overflow: hidden;
border-bottom: solid #999 1px;
font-size: 90%;
}
#inactivebox {
- position:absolute;
+ position:absolute;
top: @navbar-height + @layout_top_padding + 1;
- left:0;
- width:100%;
+ left:0;
+ width:100%;
height: @panel-message-height !important;
- overflow: hidden;
+ overflow: hidden;
border-bottom: solid #999 1px;
font-size: 90%;
}
@@ -116,10 +116,10 @@
// Panels
#left, #left-border, #center, #right-border, #right {
- position: absolute;
+ position: absolute;
top: @navbar-height + @layout_top_padding + 1;
bottom: 0px;
- overflow: hidden;
+ overflow: hidden;
background: #fff;
}
#left, #center, #right, #left-border, #right-border {
@@ -145,7 +145,7 @@
}
#right {
width: 250px;
- right: 0px;
+ right: 0px;
z-index: 200;
border-left: solid @layout-border-color 1px;
}
@@ -277,11 +277,11 @@
}
}
}
-
+
.menu-bg {
background: whiteSmoke top repeat-x;
}
-
+
div.unified-panel-body {
position: absolute;
top: @panel_header_height;
@@ -326,10 +326,10 @@
z-index: 900; // Bootstrap elements start at 1000
position: absolute;
top: 0;
- left: 0;
+ left: 0;
width: 100%;
height: 100%;
-}
+}
// Messages for message box, slightly different style
@@ -345,19 +345,19 @@
background-position: 6px 50%;
}
-.panel-warning-message
+.panel-warning-message
{
background-image: url(warn_small.png);
background-color: @state-warning-bg;
}
-.panel-done-message
+.panel-done-message
{
background-image: url(ok_small.png);
background-color: @state-success-bg;
}
-.panel-info-message
+.panel-info-message
{
background-image: url(info_small.png);
background-color: @state-info-bg;
@@ -367,9 +367,9 @@
#masthead {
- position:absolute;
- top:0;
- left:0;
+ position:absolute;
+ top:0;
+ left:0;
width:100%;
min-width:990px;
padding: 0;
@@ -385,7 +385,7 @@
color: gold;
}
}
-
+
//.dropdown-menu {
// z-index: 15002;
//}
@@ -412,8 +412,8 @@
z-index: 2000;
img {
- display: inline;
- width: 26px;
+ display: inline;
+ width: 26px;
vertical-align: top;
}
a {
@@ -421,7 +421,7 @@
text-decoration: none;
}
}
-
+
.iconbar
{
position : absolute;
@@ -448,7 +448,7 @@
left : 23px;
top : -12px;
}
-
+
.toggle
{
color : gold;
@@ -542,7 +542,7 @@
border-color:#999;
}
-div.metadataHelpBody {
+div.metadataHelpBody {
width:100%;overflow:auto;
}
@@ -553,7 +553,7 @@
padding-bottom: 0.25em;
}
-// Forms
+// Forms
div.toolFormBody div.toolFormTitle {
background: transparent;
@@ -602,7 +602,7 @@
.border-radius(@panel-border-radius);
}
-div.form-title, div.toolFormTitle {
+div.form-title, div.toolFormTitle {
// font-size: @font-size-base * 1.25;
// line-height: @line-height-base * 1.5;
padding: 5px 10px;
@@ -610,7 +610,7 @@
border-bottom: solid @form-border 1px;
}
-div.form-body {
+div.form-body {
padding: 5px 0;
}
@@ -687,7 +687,7 @@
font: inherit;
}
-.form-row {
+.form-row {
select, textarea, input[type="text"], input[type="file"], input[type="password"] {
// -webkit-box-sizing: border-box;
max-width: 90%;
@@ -733,15 +733,15 @@
}
.search-spinner {
- position: absolute;
- display: none;
- right: 6px;
+ position: absolute;
+ display: none;
+ right: 6px;
top: 9px;
}
#search-clear-btn {
- position: absolute;
- right: 6px;
+ position: absolute;
+ right: 6px;
top: 9px;
display: block;
font-size: 1.4em !important;
@@ -753,7 +753,7 @@
}
}
-// Messages
+// Messages
.errormessagelarge, .warningmessagelarge, .donemessagelarge, .infomessagelarge {
.alert();
@@ -786,7 +786,7 @@
background-image: url(info_large.png);
}
-.screencastBox {
+.screencastBox {
padding-left: 10px;
border-color: #AAAA66;
background-color: #FFFFCC;
@@ -891,7 +891,7 @@
line-height: @line-height-base;
}
-table.colored tr.header {
+table.colored tr.header {
background: @table-heading-bg;
color: contrast(@complement-color-3);
background-repeat: repeat-x;
@@ -900,11 +900,11 @@
font-weight: bold;
}
-table.colored tr {
+table.colored tr {
background: @white;
}
-table.colored tr.odd_row {
+table.colored tr.odd_row {
background: @table-bg-accent;
}
@@ -1018,7 +1018,7 @@
height: 18px;
vertical-align: middle;
margin-left: 2px;
-
+
}
.search-box-input {
border: 0;
@@ -1051,7 +1051,7 @@
div.odd_row {
background: @table-bg-accent;
}
-
+
#footer {
display: none;
}
@@ -1199,15 +1199,15 @@
cursor: pointer;
position: relative;
.unselectable;
-
+
// border: solid transparent 1px;
// -moz-border-radius: 0.25em;
// -webkit-border-radius: 0.25em;
// border-radius: 0.25em;
-
+
// padding: 1px 0.25em;
// margin: -1px -0.25em;
-
+
a {
text-decoration: none;
}
@@ -1335,28 +1335,28 @@
background:#aaa;
color:#aaa;
}
-
+
table
{
border-collapse:collapse;
border-top:1px solid #ccc;
border-left:1px solid #ccc;
}
-
+
blockquote {
color:#666;
}
-
+
fieldset {
border-color:#ccc;
border:1px solid #ccc;
}
-
+
th,td {
border-bottom:1px solid #ddd;
border-right:1px solid #ccc;
}
-
+
th,td {
padding:.8em;
}
@@ -1438,7 +1438,7 @@
margin:10px 0;
padding-left:5px;
}
-
+
// ============================================================================ History
@import "sprite-history-states.less";
@import "sprite-history-buttons.less";
@@ -1457,7 +1457,7 @@
@import "ui/pagination.less";
-// ==== Tool menu styles
+// ==== Tool menu styles
.toolMenuContainer
{
@@ -1480,7 +1480,7 @@
}
div.toolSectionDetailsInner
-{
+{
margin-left: 5px;
margin-right: 5px;
}
@@ -1565,9 +1565,9 @@
.toolMenuAndView .toolForm
{
- float: left;
- background-color: white;
- margin: 10px;
+ float: left;
+ background-color: white;
+ margin: 10px;
}
// @import "base_sprites";
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
MiBuZXcgY29tbWl0cyBpbiBnYWxheHktY2VudHJhbDoKCmh0dHBzOi8vYml0YnVja2V0Lm9yZy9n
YWxheHkvZ2FsYXh5LWNlbnRyYWwvY29tbWl0cy84ZWM2OTA1MjYwMGEvCkNoYW5nZXNldDogICA4
ZWM2OTA1MjYwMGEKVXNlcjogICAgICAgIGRhbm5vbgpEYXRlOiAgICAgICAgMjAxNC0xMi0xNSAx
NDozOTo1NSswMDowMApTdW1tYXJ5OiAgICAgQWRkIGRlZmF1bHQgbnVtZXJpY2FsIGNvbHVtbiBo
ZWFkZXJzLCBwZXIgQmo9QzM9QjZybidzIFBSIChzbGk9CmdodGx5IHR3ZWFrZWQpIGh0dHBzOi8v
Yml0YnVja2V0Lm9yZy9nYWxheHkvZ2FsYXh5LWNlbnRyYWwvcHVsbC1yZXF1ZXN0LzYxMT0KL2Fk
ZC1hLWRlZmF1bHQtaGVhZGVyLXRvLXRhYnVsYXItb3V0cHV0Ly4KQWZmZWN0ZWQgIzogIDMgZmls
ZXMKCmRpZmYgLXIgN2U2OGFhM2Q4ZDE2MWZmMmEwNTI2MTAxYWVkZWJjMDI2YjRkNzFjYyAtciA4
ZWM2OTA1MjYwMGEyYzkxNWY4OGQzMT0KODU4ODRhMTg1ZTgyMDg2ZDEgY2xpZW50L2dhbGF4eS9z
Y3JpcHRzL212Yy9kYXRhLmpzCi0tLSBhL2NsaWVudC9nYWxheHkvc2NyaXB0cy9tdmMvZGF0YS5q
cworKysgYi9jbGllbnQvZ2FsYXh5L3NjcmlwdHMvbXZjL2RhdGEuanMKQEAgLTE3Niw2ICsxNzYs
MTAgQEAKICAgICAgICAgICAgIGhlYWRlcl9yb3cgPTNEICQoJzx0ci8+JykuY3NzKCdiYWNrZ3Jv
dW5kLWNvbG9yJywgdGhpcy5oZWFkZXJfPQpjb2xvcikuYXBwZW5kVG8oZGF0YV90YWJsZSk7CiAg
ICAgICAgIGlmIChjb2x1bW5fbmFtZXMpIHsKICAgICAgICAgICAgIGhlYWRlcl9yb3cuYXBwZW5k
KCc8dGg+JyArIGNvbHVtbl9uYW1lcy5qb2luKCc8L3RoPjx0aD4nKSArICc8PQovdGg+Jyk7Cisg
ICAgICAgIH0gZWxzZSB7CisgICAgICAgICAgICBmb3IgKHZhciBqID0zRCAxOyBqIDw9M0QgdGhp
cy5tb2RlbC5nZXRfbWV0YWRhdGEoJ2NvbHVtbnMnKTsgaj0KKyspIHsKKyAgICAgICAgICAgICAg
ICBoZWFkZXJfcm93LmFwcGVuZCgnPHRoPicgKyBqICsgJzwvdGg+Jyk7CisgICAgICAgICAgICB9
CiAgICAgICAgIH0KPTIwCiAgICAgICAgIC8vIFJlbmRlciBmaXJzdCBjaHVuay4KCmRpZmYgLXIg
N2U2OGFhM2Q4ZDE2MWZmMmEwNTI2MTAxYWVkZWJjMDI2YjRkNzFjYyAtciA4ZWM2OTA1MjYwMGEy
YzkxNWY4OGQzMT0KODU4ODRhMTg1ZTgyMDg2ZDEgc3RhdGljL3NjcmlwdHMvbXZjL2RhdGEuanMK
LS0tIGEvc3RhdGljL3NjcmlwdHMvbXZjL2RhdGEuanMKKysrIGIvc3RhdGljL3NjcmlwdHMvbXZj
L2RhdGEuanMKQEAgLTE3Niw2ICsxNzYsMTAgQEAKICAgICAgICAgICAgIGhlYWRlcl9yb3cgPTNE
ICQoJzx0ci8+JykuY3NzKCdiYWNrZ3JvdW5kLWNvbG9yJywgdGhpcy5oZWFkZXJfPQpjb2xvciku
YXBwZW5kVG8oZGF0YV90YWJsZSk7CiAgICAgICAgIGlmIChjb2x1bW5fbmFtZXMpIHsKICAgICAg
ICAgICAgIGhlYWRlcl9yb3cuYXBwZW5kKCc8dGg+JyArIGNvbHVtbl9uYW1lcy5qb2luKCc8L3Ro
Pjx0aD4nKSArICc8PQovdGg+Jyk7CisgICAgICAgIH0gZWxzZSB7CisgICAgICAgICAgICBmb3Ig
KHZhciBqID0zRCAxOyBqIDw9M0QgdGhpcy5tb2RlbC5nZXRfbWV0YWRhdGEoJ2NvbHVtbnMnKTsg
aj0KKyspIHsKKyAgICAgICAgICAgICAgICBoZWFkZXJfcm93LmFwcGVuZCgnPHRoPicgKyBqICsg
JzwvdGg+Jyk7CisgICAgICAgICAgICB9CiAgICAgICAgIH0KPTIwCiAgICAgICAgIC8vIFJlbmRl
ciBmaXJzdCBjaHVuay4KCmRpZmYgLXIgN2U2OGFhM2Q4ZDE2MWZmMmEwNTI2MTAxYWVkZWJjMDI2
YjRkNzFjYyAtciA4ZWM2OTA1MjYwMGEyYzkxNWY4OGQzMT0KODU4ODRhMTg1ZTgyMDg2ZDEgc3Rh
dGljL3NjcmlwdHMvcGFja2VkL212Yy9kYXRhLmpzCi0tLSBhL3N0YXRpYy9zY3JpcHRzL3BhY2tl
ZC9tdmMvZGF0YS5qcworKysgYi9zdGF0aWMvc2NyaXB0cy9wYWNrZWQvbXZjL2RhdGEuanMKQEAg
LTEsMSArMSwxIEBACi1kZWZpbmUoWyJtdmMvdWkvdWktbW9kYWwiLCJtdmMvdWkvdWktZnJhbWVz
IiwibXZjL3VpL2ljb24tYnV0dG9uIl0sZnVuY3Rpbz0KbihrLGosZil7dmFyIGg9M0RCYWNrYm9u
ZS5Nb2RlbC5leHRlbmQoe30pO3ZhciBiPTNEQmFja2JvbmUuTW9kZWwuZXh0ZW5kKHtkPQplZmF1
bHRzOntpZDoiIix0eXBlOiIiLG5hbWU6IiIsaGRhX2xkZGE6ImhkYSIsbWV0YWRhdGE6bnVsbH0s
aW5pdGlhbGl6ZTpmdW49CmN0aW9uKCl7aWYoIXRoaXMuZ2V0KCJtZXRhZGF0YSIpKXt0aGlzLl9z
ZXRfbWV0YWRhdGEoKX10aGlzLm9uKCJjaGFuZ2UiLHRoaT0Kcy5fc2V0X21ldGFkYXRhLHRoaXMp
fSxfc2V0X21ldGFkYXRhOmZ1bmN0aW9uKCl7dmFyIG49M0RuZXcgaCgpO18uZWFjaChfLmtlPQp5
cyh0aGlzLmF0dHJpYnV0ZXMpLGZ1bmN0aW9uKG8pe2lmKG8uaW5kZXhPZigibWV0YWRhdGFfIik9
M0Q9M0Q9M0QwKXt2YXIgcD0KPTNEby5zcGxpdCgibWV0YWRhdGFfIilbMV07bi5zZXQocCx0aGlz
LmF0dHJpYnV0ZXNbb10pO2RlbGV0ZSB0aGlzLmF0dHJpYnV0PQplc1tvXX19LHRoaXMpO3RoaXMu
c2V0KCJtZXRhZGF0YSIsbix7c2lsZW50OnRydWV9KX0sZ2V0X21ldGFkYXRhOmZ1bmN0aW9uKG49
Cil7cmV0dXJuIHRoaXMuYXR0cmlidXRlcy5tZXRhZGF0YS5nZXQobil9LHVybFJvb3Q6Z2FsYXh5
X2NvbmZpZy5yb290KyJhcGkvZD0KYXRhc2V0cyJ9KTt2YXIgaT0zRGIuZXh0ZW5kKHtkZWZhdWx0
czpfLmV4dGVuZCh7fSxiLnByb3RvdHlwZS5kZWZhdWx0cyx7Y2h1PQpua191cmw6bnVsbCxmaXJz
dF9kYXRhX2NodW5rOm51bGwsY2h1bmtfaW5kZXg6LTEsYXRfZW9mOmZhbHNlfSksaW5pdGlhbGl6
ZTo9CmZ1bmN0aW9uKG4pe2IucHJvdG90eXBlLmluaXRpYWxpemUuY2FsbCh0aGlzKTt0aGlzLmF0
dHJpYnV0ZXMuY2h1bmtfaW5kZXg9Cj0zRCh0aGlzLmF0dHJpYnV0ZXMuZmlyc3RfZGF0YV9jaHVu
az8xOjApO3RoaXMuYXR0cmlidXRlcy5jaHVua191cmw9M0RnYWxheD0KeV9jb25maWcucm9vdCsi
ZGF0YXNldC9kaXNwbGF5P2RhdGFzZXRfaWQ9M0QiK3RoaXMuaWQ7dGhpcy5hdHRyaWJ1dGVzLnVy
bF92PQppej0zRGdhbGF4eV9jb25maWcucm9vdCsidmlzdWFsaXphdGlvbiJ9LGdldF9uZXh0X2No
dW5rOmZ1bmN0aW9uKCl7aWYodGhpcy49CmF0dHJpYnV0ZXMuYXRfZW9mKXtyZXR1cm4gbnVsbH12
YXIgbj0zRHRoaXMsbz0zRCQuRGVmZXJyZWQoKTskLmdldEpTT04odGhpcz0KLmF0dHJpYnV0ZXMu
Y2h1bmtfdXJsLHtjaHVuazpuLmF0dHJpYnV0ZXMuY2h1bmtfaW5kZXgrK30pLnN1Y2Nlc3MoZnVu
Y3Rpb24oPQpwKXt2YXIgcTtpZihwLmNrX2RhdGEhPTNEPTNEIiIpe3E9M0RwfWVsc2V7bi5hdHRy
aWJ1dGVzLmF0X2VvZj0zRHRydWU7cT0zRG49CnVsbH1vLnJlc29sdmUocSl9KTtyZXR1cm4gb319
KTt2YXIgZT0zREJhY2tib25lLkNvbGxlY3Rpb24uZXh0ZW5kKHttb2RlbDpifT0KKTt2YXIgYT0z
REJhY2tib25lLlZpZXcuZXh0ZW5kKHtpbml0aWFsaXplOmZ1bmN0aW9uKG4pe3RoaXMucm93X2Nv
dW50PTNEMDt0PQpoaXMubG9hZGluZ19jaHVuaz0zRGZhbHNlO3RoaXMuaGVhZGVyX2NvbG9yPTNE
IiNBQUEiO3RoaXMuZGFya19yb3dfY29sb3I9M0Q9CiIjREREIjtuZXcgZCh7bW9kZWw6bi5tb2Rl
bCwkZWw6dGhpcy4kZWx9KX0sZXhwYW5kX3RvX2NvbnRhaW5lcjpmdW5jdGlvbigpez0KaWYodGhp
cy4kZWwuaGVpZ2h0KCk8dGhpcy5zY3JvbGxfZWx0LmhlaWdodCgpKXt0aGlzLmF0dGVtcHRfdG9f
ZmV0Y2goKX19LGF0PQp0ZW1wdF90b19mZXRjaDpmdW5jdGlvbihvKXt2YXIgbj0zRHRoaXM7aWYo
IXRoaXMubG9hZGluZ19jaHVuayYmdGhpcy5zY3JvbGw9CmVkX3RvX2JvdHRvbSgpKXt0aGlzLmxv
YWRpbmdfY2h1bms9M0R0cnVlO3RoaXMubG9hZGluZ19pbmRpY2F0b3Iuc2hvdygpOyQudz0KaGVu
KG4ubW9kZWwuZ2V0X25leHRfY2h1bmsoKSkudGhlbihmdW5jdGlvbihwKXtpZihwKXtuLl9yZW5k
ZXJDaHVuayhwKTtuLmxvPQphZGluZ19jaHVuaz0zRGZhbHNlfW4ubG9hZGluZ19pbmRpY2F0b3Iu
aGlkZSgpO24uZXhwYW5kX3RvX2NvbnRhaW5lcigpfSl9fSw9CnJlbmRlcjpmdW5jdGlvbigpe3Ro
aXMubG9hZGluZ19pbmRpY2F0b3I9M0QkKCI8ZGl2Lz4iKS5hdHRyKCJpZCIsImxvYWRpbmdfaT0K
bmRpY2F0b3IiKTt0aGlzLiRlbC5hcHBlbmQodGhpcy5sb2FkaW5nX2luZGljYXRvcik7dmFyIHE9
M0QkKCI8dGFibGUvPiIpLmF0PQp0cih7aWQ6ImNvbnRlbnRfdGFibGUiLGNlbGxwYWRkaW5nOjB9
KTt0aGlzLiRlbC5hcHBlbmQocSk7dmFyIG49M0R0aGlzLm1vZGU9CmwuZ2V0X21ldGFkYXRhKCJj
b2x1bW5fbmFtZXMiKSxyPTNEJCgiPHRyLz4iKS5jc3MoImJhY2tncm91bmQtY29sb3IiLHRoaXMu
aD0KZWFkZXJfY29sb3IpLmFwcGVuZFRvKHEpO2lmKG4pe3IuYXBwZW5kKCI8dGg+IituLmpvaW4o
IjwvdGg+PHRoPiIpKyI8L3RoPiIpPQp9dmFyIHA9M0R0aGlzLG89M0R0aGlzLm1vZGVsLmdldCgi
Zmlyc3RfZGF0YV9jaHVuayIpO2lmKG8pe3RoaXMuX3JlbmRlckNodW49Cmsobyl9ZWxzZXskLndo
ZW4ocC5tb2RlbC5nZXRfbmV4dF9jaHVuaygpKS50aGVuKGZ1bmN0aW9uKHMpe3AuX3JlbmRlckNo
dW5rKD0Kcyl9KX10aGlzLnNjcm9sbF9lbHQuc2Nyb2xsKGZ1bmN0aW9uKCl7cC5hdHRlbXB0X3Rv
X2ZldGNoKCl9KX0sc2Nyb2xsZWRfdG9fPQpib3R0b206ZnVuY3Rpb24oKXtyZXR1cm4gZmFsc2V9
LF9yZW5kZXJDZWxsOmZ1bmN0aW9uKHEsbixyKXt2YXIgbz0zRCQoIjx0ZD49CiIpLnRleHQocSk7
dmFyIHA9M0R0aGlzLm1vZGVsLmdldF9tZXRhZGF0YSgiY29sdW1uX3R5cGVzIik7aWYociE9M0Q9
M0R1bmRlZj0KaW5lZCl7by5hdHRyKCJjb2xzcGFuIixyKS5hZGRDbGFzcygic3RyaW5nYWxpZ24i
KX1lbHNle2lmKHApe2lmKG48cC5sZW5ndGgpPQp7aWYocFtuXT0zRD0zRD0zRCJzdHIifHxwW25d
PTNEPTNEPTNEImxpc3QiKXtvLmFkZENsYXNzKCJzdHJpbmdhbGlnbiIpfX19fXI9CmV0dXJuIG99
LF9yZW5kZXJSb3c6ZnVuY3Rpb24obil7dmFyIG89M0RuLnNwbGl0KCJcdCIpLHE9M0QkKCI8dHI+
IikscD0zRHRoaT0Kcy5tb2RlbC5nZXRfbWV0YWRhdGEoImNvbHVtbnMiKTtpZih0aGlzLnJvd19j
b3VudCUyIT0zRD0zRDApe3EuY3NzKCJiYWNrZ3JvPQp1bmQtY29sb3IiLHRoaXMuZGFya19yb3df
Y29sb3IpfWlmKG8ubGVuZ3RoPTNEPTNEPTNEcCl7Xy5lYWNoKG8sZnVuY3Rpb24ocyw9CnIpe3Eu
YXBwZW5kKHRoaXMuX3JlbmRlckNlbGwocyxyKSl9LHRoaXMpfWVsc2V7aWYoby5sZW5ndGg+cCl7
Xy5lYWNoKG8uc2xpYz0KZSgwLHAtMSksZnVuY3Rpb24ocyxyKXtxLmFwcGVuZCh0aGlzLl9yZW5k
ZXJDZWxsKHMscikpfSx0aGlzKTtxLmFwcGVuZCh0aGlzPQouX3JlbmRlckNlbGwoby5zbGljZShw
LTEpLmpvaW4oIlx0IikscC0xKSl9ZWxzZXtpZihwPjUmJm8ubGVuZ3RoPTNEPTNEPTNEcC09CjEp
e18uZWFjaChvLGZ1bmN0aW9uKHMscil7cS5hcHBlbmQodGhpcy5fcmVuZGVyQ2VsbChzLHIpKX0s
dGhpcyk7cS5hcHBlbmQoJD0KKCI8dGQ+IikpfWVsc2V7cS5hcHBlbmQodGhpcy5fcmVuZGVyQ2Vs
bChuLDAscCkpfX19dGhpcy5yb3dfY291bnQrKztyZXR1cm4gPQpxfSxfcmVuZGVyQ2h1bms6ZnVu
Y3Rpb24obil7dmFyIG89M0R0aGlzLiRlbC5maW5kKCJ0YWJsZSIpO18uZWFjaChuLmNrX2RhdGE9
Ci5zcGxpdCgiXG4iKSxmdW5jdGlvbihwLHEpe2lmKHAhPTNEPTNEIiIpe28uYXBwZW5kKHRoaXMu
X3JlbmRlclJvdyhwKSl9fSx0aD0KaXMpfX0pO3ZhciBnPTNEYS5leHRlbmQoe2luaXRpYWxpemU6
ZnVuY3Rpb24obil7YS5wcm90b3R5cGUuaW5pdGlhbGl6ZS5jYWxsPQoodGhpcyxuKTtzY3JvbGxf
ZWx0PTNEXy5maW5kKHRoaXMuJGVsLnBhcmVudHMoKSxmdW5jdGlvbihvKXtyZXR1cm4gJChvKS5j
c3M9Cigib3ZlcmZsb3ciKT0zRD0zRD0zRCJhdXRvIn0pO2lmKCFzY3JvbGxfZWx0KXtzY3JvbGxf
ZWx0PTNEd2luZG93fXRoaXMuc2Nybz0KbGxfZWx0PTNEJChzY3JvbGxfZWx0KX0sc2Nyb2xsZWRf
dG9fYm90dG9tOmZ1bmN0aW9uKCl7cmV0dXJuKHRoaXMuJGVsLmhlaWdoPQp0KCktdGhpcy5zY3Jv
bGxfZWx0LnNjcm9sbFRvcCgpLXRoaXMuc2Nyb2xsX2VsdC5oZWlnaHQoKTw9M0QwKX19KTt2YXIg
bT0zRGE9Ci5leHRlbmQoe2luaXRpYWxpemU6ZnVuY3Rpb24obil7YS5wcm90b3R5cGUuaW5pdGlh
bGl6ZS5jYWxsKHRoaXMsbik7dGhpcy5zYz0Kcm9sbF9lbHQ9M0R0aGlzLiRlbC5jc3Moe3Bvc2l0
aW9uOiJyZWxhdGl2ZSIsb3ZlcmZsb3c6InNjcm9sbCIsaGVpZ2h0OnRoaXMuPQpvcHRpb25zLmhl
aWdodHx8IjUwMHB4In0pfSxzY3JvbGxlZF90b19ib3R0b206ZnVuY3Rpb24oKXtyZXR1cm4gdGhp
cy4kZWwuc2M9CnJvbGxUb3AoKSt0aGlzLiRlbC5pbm5lckhlaWdodCgpPj0zRHRoaXMuZWwuc2Ny
b2xsSGVpZ2h0fX0pO3ZhciBkPTNEQmFja2Jvbj0KZS5WaWV3LmV4dGVuZCh7Y29sOntjaHJvbTpu
dWxsLHN0YXJ0Om51bGwsZW5kOm51bGx9LHVybF92aXo6bnVsbCxkYXRhc2V0X2lkPQo6bnVsbCxn
ZW5vbWVfYnVpbGQ6bnVsbCxmaWxlX2V4dDpudWxsLGluaXRpYWxpemU6ZnVuY3Rpb24ocCl7dmFy
IHM9M0RwYXJlbnQ9Ci5HYWxheHk7aWYocyYmcy5tb2RhbCl7dGhpcy5tb2RhbD0zRHMubW9kYWx9
aWYocyYmcy5mcmFtZSl7dGhpcy5mcmFtZT0zRHMuZj0KcmFtZX1pZighdGhpcy5tb2RhbHx8IXRo
aXMuZnJhbWUpe3JldHVybn12YXIgbz0zRHAubW9kZWw7dmFyIHI9M0RvLmdldCgibWV0PQphZGF0
YSIpO2lmKCFvLmdldCgiZmlsZV9leHQiKSl7cmV0dXJufXRoaXMuZmlsZV9leHQ9M0RvLmdldCgi
ZmlsZV9leHQiKTtpZig9CnRoaXMuZmlsZV9leHQ9M0Q9M0QiYmVkIil7aWYoci5nZXQoImNocm9t
Q29sIikmJnIuZ2V0KCJzdGFydENvbCIpJiZyLmdldCgiZT0KbmRDb2wiKSl7dGhpcy5jb2wuY2hy
b209M0RyLmdldCgiY2hyb21Db2wiKS0xO3RoaXMuY29sLnN0YXJ0PTNEci5nZXQoInN0YXJ0PQpD
b2wiKS0xO3RoaXMuY29sLmVuZD0zRHIuZ2V0KCJlbmRDb2wiKS0xfWVsc2V7Y29uc29sZS5sb2co
IlRhYnVsYXJCdXR0b25UcmE9CmNrc3RlciA6IEJlZC1maWxlIG1ldGFkYXRhIGluY29tcGxldGUu
Iik7cmV0dXJufX1pZih0aGlzLmZpbGVfZXh0PTNEPTNEInZjZj0KIil7ZnVuY3Rpb24gcSh1LHYp
e2Zvcih2YXIgdD0zRDA7dDx2Lmxlbmd0aDt0Kyspe2lmKHZbdF0ubWF0Y2godSkpe3JldHVybiB0
PQp9fXJldHVybiAtMX10aGlzLmNvbC5jaHJvbT0zRHEoIkNocm9tIixyLmdldCgiY29sdW1uX25h
bWVzIikpO3RoaXMuY29sLnN0YXI9CnQ9M0RxKCJQb3MiLHIuZ2V0KCJjb2x1bW5fbmFtZXMiKSk7
dGhpcy5jb2wuZW5kPTNEbnVsbDtpZih0aGlzLmNvbC5jaHJvbT0zRD0KPTNELTF8fHRoaXMuY29s
LnN0YXJ0PTNEPTNELTEpe2NvbnNvbGUubG9nKCJUYWJ1bGFyQnV0dG9uVHJhY2tzdGVyIDogVkNG
LWZpPQpsZSBtZXRhZGF0YSBpbmNvbXBsZXRlLiIpO3JldHVybn19aWYodGhpcy5jb2wuY2hyb209
M0Q9M0Q9M0R1bmRlZmluZWQpe3JldHU9CnJufWlmKG8uaWQpe3RoaXMuZGF0YXNldF9pZD0zRG8u
aWR9ZWxzZXtjb25zb2xlLmxvZygiVGFidWxhckJ1dHRvblRyYWNrc3Rlcj0KIDogRGF0YXNldCBp
ZGVudGlmaWNhdGlvbiBpcyBtaXNzaW5nLiIpO3JldHVybn1pZihvLmdldCgidXJsX3ZpeiIpKXt0
aGlzLnVyPQpsX3Zpej0zRG8uZ2V0KCJ1cmxfdml6Iil9ZWxzZXtjb25zb2xlLmxvZygiVGFidWxh
ckJ1dHRvblRyYWNrc3RlciA6IFVybCBmb3I9CiB2aXN1YWxpemF0aW9uIGNvbnRyb2xsZXIgaXMg
bWlzc2luZy4iKTtyZXR1cm59aWYoby5nZXQoImdlbm9tZV9idWlsZCIpKXt0aD0KaXMuZ2Vub21l
X2J1aWxkPTNEby5nZXQoImdlbm9tZV9idWlsZCIpfXZhciBuPTNEbmV3IGYuSWNvbkJ1dHRvblZp
ZXcoe21vZGVsPQo6bmV3IGYuSWNvbkJ1dHRvbih7dGl0bGU6IlZpc3VhbGl6ZSIsaWNvbl9jbGFz
czoiY2hhcnRfY3VydmUiLGlkOiJidG5fdml6In09Cil9KTt0aGlzLnNldEVsZW1lbnQocC4kZWwp
O3RoaXMuJGVsLmFwcGVuZChuLnJlbmRlcigpLiRlbCk7dGhpcy5oaWRlKCl9LGV2ZT0KbnRzOnsi
bW91c2VvdmVyIHRyIjoic2hvdyIsbW91c2VsZWF2ZToiaGlkZSJ9LHNob3c6ZnVuY3Rpb24ocyl7
ZnVuY3Rpb24gcih4PQope3JldHVybiAhaXNOYU4ocGFyc2VGbG9hdCh4KSkmJmlzRmluaXRlKHgp
fWlmKHRoaXMuY29sLmNocm9tPTNEPTNEPTNEbnVsbCk9CntyZXR1cm59dmFyIHc9M0QkKHMudGFy
Z2V0KS5wYXJlbnQoKTt2YXIgdD0zRHcuY2hpbGRyZW4oKS5lcSh0aGlzLmNvbC5jaHJvbT0KKS5o
dG1sKCk7dmFyIG49M0R3LmNoaWxkcmVuKCkuZXEodGhpcy5jb2wuc3RhcnQpLmh0bWwoKTt2YXIg
cD0zRHRoaXMuY29sLmVuPQpkP3cuY2hpbGRyZW4oKS5lcSh0aGlzLmNvbC5lbmQpLmh0bWwoKTpu
O2lmKCF0Lm1hdGNoKCJeIyIpJiZ0IT0zRD0zRCIiJiZyKG49Cikpe3ZhciB2PTNEe2RhdGFzZXRf
aWQ6dGhpcy5kYXRhc2V0X2lkLGdlbmVfcmVnaW9uOnQrIjoiK24rIi0iK3B9O3ZhciBxPTNEdz0K
Lm9mZnNldCgpO3ZhciBvPTNEcS5sZWZ0LTEwO3ZhciB1PTNEcS50b3AtJCh3aW5kb3cpLnNjcm9s
bFRvcCgpKzM7JCgiI2J0bl92PQppeiIpLmNzcyh7cG9zaXRpb246ImZpeGVkIix0b3A6dSsicHgi
LGxlZnQ6bysicHgifSk7JCgiI2J0bl92aXoiKS5vZmYoImNsaWM9CmsiKTskKCIjYnRuX3ZpeiIp
LmNsaWNrKHRoaXMuY3JlYXRlX3RyYWNrc3Rlcl9hY3Rpb24odGhpcy51cmxfdml6LHYsdGhpcy5n
ZT0Kbm9tZV9idWlsZCkpOyQoIiNidG5fdml6Iikuc2hvdygpfWVsc2V7JCgiI2J0bl92aXoiKS5o
aWRlKCl9fSxoaWRlOmZ1bmN0aW9uPQooKXt0aGlzLiRlbC5maW5kKCIjYnRuX3ZpeiIpLmhpZGUo
KX0sY3JlYXRlX3RyYWNrc3Rlcl9hY3Rpb246ZnVuY3Rpb24obixxLHA9Cil7dmFyIG89M0R0aGlz
O3JldHVybiBmdW5jdGlvbigpe3ZhciByPTNEe307aWYocCl7clsiZi1kYmtleSJdPTNEcH0kLmFq
YXgoez0KdXJsOm4rIi9saXN0X3RyYWNrcz8iKyQucGFyYW0ociksZGF0YVR5cGU6Imh0bWwiLGVy
cm9yOmZ1bmN0aW9uKCl7by5tb2RhbC5zPQpob3coe3RpdGxlOiJTb21ldGhpbmcgd2VudCB3cm9u
ZyEiLGJvZHk6IlVuZm9ydHVuYXRlbHkgd2UgY291bGQgbm90IGFkZCB0aGk9CnMgZGF0YXNldCB0
byB0aGUgdHJhY2sgYnJvd3Nlci4gUGxlYXNlIHRyeSBhZ2FpbiBvciBjb250YWN0IHVzLiIsYnV0
dG9uczp7Qz0KYW5jZWw6ZnVuY3Rpb24oKXtvLm1vZGFsLmhpZGUoKX19fSl9LHN1Y2Nlc3M6ZnVu
Y3Rpb24ocyl7by5tb2RhbC5zaG93KHt0aXRsPQplOiJWaWV3IERhdGEgaW4gYSBOZXcgb3IgU2F2
ZWQgVmlzdWFsaXphdGlvbiIsYnV0dG9uczp7Q2FuY2VsOmZ1bmN0aW9uKCl7by49Cm1vZGFsLmhp
ZGUoKX0sIlZpZXcgaW4gc2F2ZWQgdmlzdWFsaXphdGlvbiI6ZnVuY3Rpb24oKXtvLm1vZGFsLnNo
b3coe3RpdGxlOj0KIkFkZCBEYXRhIHRvIFNhdmVkIFZpc3VhbGl6YXRpb24iLGJvZHk6cyxidXR0
b25zOntDYW5jZWw6ZnVuY3Rpb24oKXtvLm1vZGFsPQouaGlkZSgpfSwiQWRkIHRvIHZpc3VhbGl6
YXRpb24iOmZ1bmN0aW9uKCl7by5tb2RhbC5oaWRlKCk7by5tb2RhbC4kZWwuZmluZCg9CiJpbnB1
dFtuYW1lPTNEaWRdOmNoZWNrZWQiKS5lYWNoKGZ1bmN0aW9uKCl7dmFyIHQ9M0QkKHRoaXMpLnZh
bCgpO3EuaWQ9M0R0Oz0Kby5mcmFtZS5hZGQoe3RpdGxlOiJUcmFja3N0ZXIiLHR5cGU6InVybCIs
Y29udGVudDpuKyIvdHJhY2tzdGVyPyIrJC5wYXJhbShxPQopfSl9KX19fSl9LCJWaWV3IGluIG5l
dyB2aXN1YWxpemF0aW9uIjpmdW5jdGlvbigpe28ubW9kYWwuaGlkZSgpO28uZnJhbWUuYWQ9CmQo
e3RpdGxlOiJUcmFja3N0ZXIiLHR5cGU6InVybCIsY29udGVudDpuKyIvdHJhY2tzdGVyPyIrJC5w
YXJhbShxKX0pfX19KX19KT0KO3JldHVybiBmYWxzZX19fSk7dmFyIGw9M0RmdW5jdGlvbihxLG8s
cixuKXt2YXIgcD0zRG5ldyBvKHttb2RlbDpuZXcgcShyKX0pPQo7cC5yZW5kZXIoKTtpZihuKXtu
LmFwcGVuZChwLiRlbCl9cmV0dXJuIHB9O3ZhciBjPTNEZnVuY3Rpb24ocCl7aWYoIXAubW9kZWw9
Cil7cC5tb2RlbD0zRG5ldyBpKHAuZGF0YXNldF9jb25maWcpfXZhciBvPTNEcC5wYXJlbnRfZWx0
O3ZhciBxPTNEcC5lbWJlZGRlZD0KO2RlbGV0ZSBwLmVtYmVkZGVkO2RlbGV0ZSBwLnBhcmVudF9l
bHQ7ZGVsZXRlIHAuZGF0YXNldF9jb25maWc7dmFyIG49M0QocT9uPQpldyBtKHApOm5ldyBnKHAp
KTtuLnJlbmRlcigpO2lmKG8pe28uYXBwZW5kKG4uJGVsKTtuLmV4cGFuZF90b19jb250YWluZXIo
KX09CnJldHVybiBufTtyZXR1cm57RGF0YXNldDpiLFRhYnVsYXJEYXRhc2V0OmksRGF0YXNldENv
bGxlY3Rpb246ZSxUYWJ1bGFyRGF0YT0Kc2V0Q2h1bmtlZFZpZXc6YSxjcmVhdGVUYWJ1bGFyRGF0
YXNldENodW5rZWRWaWV3OmN9fSk7ClwgTm8gbmV3bGluZSBhdCBlbmQgb2YgZmlsZQorZGVmaW5l
KFsibXZjL3VpL3VpLW1vZGFsIiwibXZjL3VpL3VpLWZyYW1lcyIsIm12Yy91aS9pY29uLWJ1dHRv
biJdLGZ1bmN0aW89Cm4oayxqLGYpe3ZhciBoPTNEQmFja2JvbmUuTW9kZWwuZXh0ZW5kKHt9KTt2
YXIgYj0zREJhY2tib25lLk1vZGVsLmV4dGVuZCh7ZD0KZWZhdWx0czp7aWQ6IiIsdHlwZToiIixu
YW1lOiIiLGhkYV9sZGRhOiJoZGEiLG1ldGFkYXRhOm51bGx9LGluaXRpYWxpemU6ZnVuPQpjdGlv
bigpe2lmKCF0aGlzLmdldCgibWV0YWRhdGEiKSl7dGhpcy5fc2V0X21ldGFkYXRhKCl9dGhpcy5v
bigiY2hhbmdlIix0aGk9CnMuX3NldF9tZXRhZGF0YSx0aGlzKX0sX3NldF9tZXRhZGF0YTpmdW5j
dGlvbigpe3ZhciBuPTNEbmV3IGgoKTtfLmVhY2goXy5rZT0KeXModGhpcy5hdHRyaWJ1dGVzKSxm
dW5jdGlvbihvKXtpZihvLmluZGV4T2YoIm1ldGFkYXRhXyIpPTNEPTNEPTNEMCl7dmFyIHA9Cj0z
RG8uc3BsaXQoIm1ldGFkYXRhXyIpWzFdO24uc2V0KHAsdGhpcy5hdHRyaWJ1dGVzW29dKTtkZWxl
dGUgdGhpcy5hdHRyaWJ1dD0KZXNbb119fSx0aGlzKTt0aGlzLnNldCgibWV0YWRhdGEiLG4se3Np
bGVudDp0cnVlfSl9LGdldF9tZXRhZGF0YTpmdW5jdGlvbihuPQope3JldHVybiB0aGlzLmF0dHJp
YnV0ZXMubWV0YWRhdGEuZ2V0KG4pfSx1cmxSb290OmdhbGF4eV9jb25maWcucm9vdCsiYXBpL2Q9
CmF0YXNldHMifSk7dmFyIGk9M0RiLmV4dGVuZCh7ZGVmYXVsdHM6Xy5leHRlbmQoe30sYi5wcm90
b3R5cGUuZGVmYXVsdHMse2NodT0KbmtfdXJsOm51bGwsZmlyc3RfZGF0YV9jaHVuazpudWxsLGNo
dW5rX2luZGV4Oi0xLGF0X2VvZjpmYWxzZX0pLGluaXRpYWxpemU6PQpmdW5jdGlvbihuKXtiLnBy
b3RvdHlwZS5pbml0aWFsaXplLmNhbGwodGhpcyk7dGhpcy5hdHRyaWJ1dGVzLmNodW5rX2luZGV4
PQo9M0QodGhpcy5hdHRyaWJ1dGVzLmZpcnN0X2RhdGFfY2h1bms/MTowKTt0aGlzLmF0dHJpYnV0
ZXMuY2h1bmtfdXJsPTNEZ2FsYXg9CnlfY29uZmlnLnJvb3QrImRhdGFzZXQvZGlzcGxheT9kYXRh
c2V0X2lkPTNEIit0aGlzLmlkO3RoaXMuYXR0cmlidXRlcy51cmxfdj0KaXo9M0RnYWxheHlfY29u
ZmlnLnJvb3QrInZpc3VhbGl6YXRpb24ifSxnZXRfbmV4dF9jaHVuazpmdW5jdGlvbigpe2lmKHRo
aXMuPQphdHRyaWJ1dGVzLmF0X2VvZil7cmV0dXJuIG51bGx9dmFyIG49M0R0aGlzLG89M0QkLkRl
ZmVycmVkKCk7JC5nZXRKU09OKHRoaXM9Ci5hdHRyaWJ1dGVzLmNodW5rX3VybCx7Y2h1bms6bi5h
dHRyaWJ1dGVzLmNodW5rX2luZGV4Kyt9KS5zdWNjZXNzKGZ1bmN0aW9uKD0KcCl7dmFyIHE7aWYo
cC5ja19kYXRhIT0zRD0zRCIiKXtxPTNEcH1lbHNle24uYXR0cmlidXRlcy5hdF9lb2Y9M0R0cnVl
O3E9M0RuPQp1bGx9by5yZXNvbHZlKHEpfSk7cmV0dXJuIG99fSk7dmFyIGU9M0RCYWNrYm9uZS5D
b2xsZWN0aW9uLmV4dGVuZCh7bW9kZWw6Yn09Cik7dmFyIGE9M0RCYWNrYm9uZS5WaWV3LmV4dGVu
ZCh7aW5pdGlhbGl6ZTpmdW5jdGlvbihuKXt0aGlzLnJvd19jb3VudD0zRDA7dD0KaGlzLmxvYWRp
bmdfY2h1bms9M0RmYWxzZTt0aGlzLmhlYWRlcl9jb2xvcj0zRCIjQUFBIjt0aGlzLmRhcmtfcm93
X2NvbG9yPTNEPQoiI0RERCI7bmV3IGQoe21vZGVsOm4ubW9kZWwsJGVsOnRoaXMuJGVsfSl9LGV4
cGFuZF90b19jb250YWluZXI6ZnVuY3Rpb24oKXs9CmlmKHRoaXMuJGVsLmhlaWdodCgpPHRoaXMu
c2Nyb2xsX2VsdC5oZWlnaHQoKSl7dGhpcy5hdHRlbXB0X3RvX2ZldGNoKCl9fSxhdD0KdGVtcHRf
dG9fZmV0Y2g6ZnVuY3Rpb24obyl7dmFyIG49M0R0aGlzO2lmKCF0aGlzLmxvYWRpbmdfY2h1bmsm
JnRoaXMuc2Nyb2xsPQplZF90b19ib3R0b20oKSl7dGhpcy5sb2FkaW5nX2NodW5rPTNEdHJ1ZTt0
aGlzLmxvYWRpbmdfaW5kaWNhdG9yLnNob3coKTskLnc9CmhlbihuLm1vZGVsLmdldF9uZXh0X2No
dW5rKCkpLnRoZW4oZnVuY3Rpb24ocCl7aWYocCl7bi5fcmVuZGVyQ2h1bmsocCk7bi5sbz0KYWRp
bmdfY2h1bms9M0RmYWxzZX1uLmxvYWRpbmdfaW5kaWNhdG9yLmhpZGUoKTtuLmV4cGFuZF90b19j
b250YWluZXIoKX0pfX0sPQpyZW5kZXI6ZnVuY3Rpb24oKXt0aGlzLmxvYWRpbmdfaW5kaWNhdG9y
PTNEJCgiPGRpdi8+IikuYXR0cigiaWQiLCJsb2FkaW5nX2k9Cm5kaWNhdG9yIik7dGhpcy4kZWwu
YXBwZW5kKHRoaXMubG9hZGluZ19pbmRpY2F0b3IpO3ZhciByPTNEJCgiPHRhYmxlLz4iKS5hdD0K
dHIoe2lkOiJjb250ZW50X3RhYmxlIixjZWxscGFkZGluZzowfSk7dGhpcy4kZWwuYXBwZW5kKHIp
O3ZhciBuPTNEdGhpcy5tb2RlPQpsLmdldF9tZXRhZGF0YSgiY29sdW1uX25hbWVzIikscz0zRCQo
Ijx0ci8+IikuY3NzKCJiYWNrZ3JvdW5kLWNvbG9yIix0aGlzLmg9CmVhZGVyX2NvbG9yKS5hcHBl
bmRUbyhyKTtpZihuKXtzLmFwcGVuZCgiPHRoPiIrbi5qb2luKCI8L3RoPjx0aD4iKSsiPC90aD4i
KT0KfWVsc2V7Zm9yKHZhciBxPTNEMTtxPD0zRHRoaXMubW9kZWwuZ2V0X21ldGFkYXRhKCJjb2x1
bW5zIik7cSsrKXtzLmFwcGVuZCgiPQo8dGg+IitxKyI8L3RoPiIpfX12YXIgcD0zRHRoaXMsbz0z
RHRoaXMubW9kZWwuZ2V0KCJmaXJzdF9kYXRhX2NodW5rIik7aWYobyk9Cnt0aGlzLl9yZW5kZXJD
aHVuayhvKX1lbHNleyQud2hlbihwLm1vZGVsLmdldF9uZXh0X2NodW5rKCkpLnRoZW4oZnVuY3Rp
b24odD0KKXtwLl9yZW5kZXJDaHVuayh0KX0pfXRoaXMuc2Nyb2xsX2VsdC5zY3JvbGwoZnVuY3Rp
b24oKXtwLmF0dGVtcHRfdG9fZmV0Y2goPQopfSl9LHNjcm9sbGVkX3RvX2JvdHRvbTpmdW5jdGlv
bigpe3JldHVybiBmYWxzZX0sX3JlbmRlckNlbGw6ZnVuY3Rpb24ocSxuLHI9Cil7dmFyIG89M0Qk
KCI8dGQ+IikudGV4dChxKTt2YXIgcD0zRHRoaXMubW9kZWwuZ2V0X21ldGFkYXRhKCJjb2x1bW5f
dHlwZXMiKT0KO2lmKHIhPTNEPTNEdW5kZWZpbmVkKXtvLmF0dHIoImNvbHNwYW4iLHIpLmFkZENs
YXNzKCJzdHJpbmdhbGlnbiIpfWVsc2V7aWYoPQpwKXtpZihuPHAubGVuZ3RoKXtpZihwW25dPTNE
PTNEPTNEInN0ciJ8fHBbbl09M0Q9M0Q9M0QibGlzdCIpe28uYWRkQ2xhc3MoInM9CnRyaW5nYWxp
Z24iKX19fX1yZXR1cm4gb30sX3JlbmRlclJvdzpmdW5jdGlvbihuKXt2YXIgbz0zRG4uc3BsaXQo
Ilx0IikscT0zRD0KJCgiPHRyPiIpLHA9M0R0aGlzLm1vZGVsLmdldF9tZXRhZGF0YSgiY29sdW1u
cyIpO2lmKHRoaXMucm93X2NvdW50JTIhPTNEPTNEPQowKXtxLmNzcygiYmFja2dyb3VuZC1jb2xv
ciIsdGhpcy5kYXJrX3Jvd19jb2xvcil9aWYoby5sZW5ndGg9M0Q9M0Q9M0RwKXtfLmU9CmFjaChv
LGZ1bmN0aW9uKHMscil7cS5hcHBlbmQodGhpcy5fcmVuZGVyQ2VsbChzLHIpKX0sdGhpcyl9ZWxz
ZXtpZihvLmxlbmd0aD0KPnApe18uZWFjaChvLnNsaWNlKDAscC0xKSxmdW5jdGlvbihzLHIpe3Eu
YXBwZW5kKHRoaXMuX3JlbmRlckNlbGwocyxyKSl9LHRoPQppcyk7cS5hcHBlbmQodGhpcy5fcmVu
ZGVyQ2VsbChvLnNsaWNlKHAtMSkuam9pbigiXHQiKSxwLTEpKX1lbHNle2lmKHA+NSYmby49Cmxl
bmd0aD0zRD0zRD0zRHAtMSl7Xy5lYWNoKG8sZnVuY3Rpb24ocyxyKXtxLmFwcGVuZCh0aGlzLl9y
ZW5kZXJDZWxsKHMscikpfT0KLHRoaXMpO3EuYXBwZW5kKCQoIjx0ZD4iKSl9ZWxzZXtxLmFwcGVu
ZCh0aGlzLl9yZW5kZXJDZWxsKG4sMCxwKSl9fX10aGlzLnJvPQp3X2NvdW50Kys7cmV0dXJuIHF9
LF9yZW5kZXJDaHVuazpmdW5jdGlvbihuKXt2YXIgbz0zRHRoaXMuJGVsLmZpbmQoInRhYmxlIik9
CjtfLmVhY2gobi5ja19kYXRhLnNwbGl0KCJcbiIpLGZ1bmN0aW9uKHAscSl7aWYocCE9M0Q9M0Qi
Iil7by5hcHBlbmQodGhpcy5fcj0KZW5kZXJSb3cocCkpfX0sdGhpcyl9fSk7dmFyIGc9M0RhLmV4
dGVuZCh7aW5pdGlhbGl6ZTpmdW5jdGlvbihuKXthLnByb3RvdHlwPQplLmluaXRpYWxpemUuY2Fs
bCh0aGlzLG4pO3Njcm9sbF9lbHQ9M0RfLmZpbmQodGhpcy4kZWwucGFyZW50cygpLGZ1bmN0aW9u
KG89Cil7cmV0dXJuICQobykuY3NzKCJvdmVyZmxvdyIpPTNEPTNEPTNEImF1dG8ifSk7aWYoIXNj
cm9sbF9lbHQpe3Njcm9sbF9lbHQ9Cj0zRHdpbmRvd310aGlzLnNjcm9sbF9lbHQ9M0QkKHNjcm9s
bF9lbHQpfSxzY3JvbGxlZF90b19ib3R0b206ZnVuY3Rpb24oKXtyZT0KdHVybih0aGlzLiRlbC5o
ZWlnaHQoKS10aGlzLnNjcm9sbF9lbHQuc2Nyb2xsVG9wKCktdGhpcy5zY3JvbGxfZWx0LmhlaWdo
dCgpPQo8PTNEMCl9fSk7dmFyIG09M0RhLmV4dGVuZCh7aW5pdGlhbGl6ZTpmdW5jdGlvbihuKXth
LnByb3RvdHlwZS5pbml0aWFsaXplLmM9CmFsbCh0aGlzLG4pO3RoaXMuc2Nyb2xsX2VsdD0zRHRo
aXMuJGVsLmNzcyh7cG9zaXRpb246InJlbGF0aXZlIixvdmVyZmxvdzoicz0KY3JvbGwiLGhlaWdo
dDp0aGlzLm9wdGlvbnMuaGVpZ2h0fHwiNTAwcHgifSl9LHNjcm9sbGVkX3RvX2JvdHRvbTpmdW5j
dGlvbigpPQp7cmV0dXJuIHRoaXMuJGVsLnNjcm9sbFRvcCgpK3RoaXMuJGVsLmlubmVySGVpZ2h0
KCk+PTNEdGhpcy5lbC5zY3JvbGxIZWlnaHQ9Cn19KTt2YXIgZD0zREJhY2tib25lLlZpZXcuZXh0
ZW5kKHtjb2w6e2Nocm9tOm51bGwsc3RhcnQ6bnVsbCxlbmQ6bnVsbH0sdXJsXz0Kdml6Om51bGws
ZGF0YXNldF9pZDpudWxsLGdlbm9tZV9idWlsZDpudWxsLGZpbGVfZXh0Om51bGwsaW5pdGlhbGl6
ZTpmdW5jdGlvPQpuKHApe3ZhciBzPTNEcGFyZW50LkdhbGF4eTtpZihzJiZzLm1vZGFsKXt0aGlz
Lm1vZGFsPTNEcy5tb2RhbH1pZihzJiZzLmZyYW09CmUpe3RoaXMuZnJhbWU9M0RzLmZyYW1lfWlm
KCF0aGlzLm1vZGFsfHwhdGhpcy5mcmFtZSl7cmV0dXJufXZhciBvPTNEcC5tb2RlbD0KO3ZhciBy
PTNEby5nZXQoIm1ldGFkYXRhIik7aWYoIW8uZ2V0KCJmaWxlX2V4dCIpKXtyZXR1cm59dGhpcy5m
aWxlX2V4dD0zRG8uPQpnZXQoImZpbGVfZXh0Iik7aWYodGhpcy5maWxlX2V4dD0zRD0zRCJiZWQi
KXtpZihyLmdldCgiY2hyb21Db2wiKSYmci5nZXQoInM9CnRhcnRDb2wiKSYmci5nZXQoImVuZENv
bCIpKXt0aGlzLmNvbC5jaHJvbT0zRHIuZ2V0KCJjaHJvbUNvbCIpLTE7dGhpcy5jb2wucz0KdGFy
dD0zRHIuZ2V0KCJzdGFydENvbCIpLTE7dGhpcy5jb2wuZW5kPTNEci5nZXQoImVuZENvbCIpLTF9
ZWxzZXtjb25zb2xlLmxvPQpnKCJUYWJ1bGFyQnV0dG9uVHJhY2tzdGVyIDogQmVkLWZpbGUgbWV0
YWRhdGEgaW5jb21wbGV0ZS4iKTtyZXR1cm59fWlmKHRoaXM9Ci5maWxlX2V4dD0zRD0zRCJ2Y2Yi
KXtmdW5jdGlvbiBxKHUsdil7Zm9yKHZhciB0PTNEMDt0PHYubGVuZ3RoO3QrKyl7aWYodlt0XT0K
Lm1hdGNoKHUpKXtyZXR1cm4gdH19cmV0dXJuIC0xfXRoaXMuY29sLmNocm9tPTNEcSgiQ2hyb20i
LHIuZ2V0KCJjb2x1bW5fbmFtPQplcyIpKTt0aGlzLmNvbC5zdGFydD0zRHEoIlBvcyIsci5nZXQo
ImNvbHVtbl9uYW1lcyIpKTt0aGlzLmNvbC5lbmQ9M0RudWxsO2k9CmYodGhpcy5jb2wuY2hyb209
M0Q9M0QtMXx8dGhpcy5jb2wuc3RhcnQ9M0Q9M0QtMSl7Y29uc29sZS5sb2coIlRhYnVsYXJCdXR0
bz0KblRyYWNrc3RlciA6IFZDRi1maWxlIG1ldGFkYXRhIGluY29tcGxldGUuIik7cmV0dXJufX1p
Zih0aGlzLmNvbC5jaHJvbT0zRD0KPTNEPTNEdW5kZWZpbmVkKXtyZXR1cm59aWYoby5pZCl7dGhp
cy5kYXRhc2V0X2lkPTNEby5pZH1lbHNle2NvbnNvbGUubG9nKCJUPQphYnVsYXJCdXR0b25UcmFj
a3N0ZXIgOiBEYXRhc2V0IGlkZW50aWZpY2F0aW9uIGlzIG1pc3NpbmcuIik7cmV0dXJufWlmKG8u
Z2U9CnQoInVybF92aXoiKSl7dGhpcy51cmxfdml6PTNEby5nZXQoInVybF92aXoiKX1lbHNle2Nv
bnNvbGUubG9nKCJUYWJ1bGFyQnV0dD0Kb25UcmFja3N0ZXIgOiBVcmwgZm9yIHZpc3VhbGl6YXRp
b24gY29udHJvbGxlciBpcyBtaXNzaW5nLiIpO3JldHVybn1pZihvLmdlPQp0KCJnZW5vbWVfYnVp
bGQiKSl7dGhpcy5nZW5vbWVfYnVpbGQ9M0RvLmdldCgiZ2Vub21lX2J1aWxkIil9dmFyIG49M0Ru
ZXcgZi49Ckljb25CdXR0b25WaWV3KHttb2RlbDpuZXcgZi5JY29uQnV0dG9uKHt0aXRsZToiVmlz
dWFsaXplIixpY29uX2NsYXNzOiJjaGFydD0KX2N1cnZlIixpZDoiYnRuX3ZpeiJ9KX0pO3RoaXMu
c2V0RWxlbWVudChwLiRlbCk7dGhpcy4kZWwuYXBwZW5kKG4ucmVuZGVyKCkuPQokZWwpO3RoaXMu
aGlkZSgpfSxldmVudHM6eyJtb3VzZW92ZXIgdHIiOiJzaG93Iixtb3VzZWxlYXZlOiJoaWRlIn0s
c2hvdzpmdW49CmN0aW9uKHMpe2Z1bmN0aW9uIHIoeCl7cmV0dXJuICFpc05hTihwYXJzZUZsb2F0
KHgpKSYmaXNGaW5pdGUoeCl9aWYodGhpcy5jbz0KbC5jaHJvbT0zRD0zRD0zRG51bGwpe3JldHVy
bn12YXIgdz0zRCQocy50YXJnZXQpLnBhcmVudCgpO3ZhciB0PTNEdy5jaGlsZHJlPQpuKCkuZXEo
dGhpcy5jb2wuY2hyb20pLmh0bWwoKTt2YXIgbj0zRHcuY2hpbGRyZW4oKS5lcSh0aGlzLmNvbC5z
dGFydCkuaHRtbCg9Cik7dmFyIHA9M0R0aGlzLmNvbC5lbmQ/dy5jaGlsZHJlbigpLmVxKHRoaXMu
Y29sLmVuZCkuaHRtbCgpOm47aWYoIXQubWF0Y2goIj0KXiMiKSYmdCE9M0Q9M0QiIiYmcihuKSl7
dmFyIHY9M0R7ZGF0YXNldF9pZDp0aGlzLmRhdGFzZXRfaWQsZ2VuZV9yZWdpb246dCsiPQo6Iitu
KyItIitwfTt2YXIgcT0zRHcub2Zmc2V0KCk7dmFyIG89M0RxLmxlZnQtMTA7dmFyIHU9M0RxLnRv
cC0kKHdpbmRvdykuc2M9CnJvbGxUb3AoKSszOyQoIiNidG5fdml6IikuY3NzKHtwb3NpdGlvbjoi
Zml4ZWQiLHRvcDp1KyJweCIsbGVmdDpvKyJweCJ9KTskKD0KIiNidG5fdml6Iikub2ZmKCJjbGlj
ayIpOyQoIiNidG5fdml6IikuY2xpY2sodGhpcy5jcmVhdGVfdHJhY2tzdGVyX2FjdGlvbih0PQpo
aXMudXJsX3Zpeix2LHRoaXMuZ2Vub21lX2J1aWxkKSk7JCgiI2J0bl92aXoiKS5zaG93KCl9ZWxz
ZXskKCIjYnRuX3ZpeiIpLmg9CmlkZSgpfX0saGlkZTpmdW5jdGlvbigpe3RoaXMuJGVsLmZpbmQo
IiNidG5fdml6IikuaGlkZSgpfSxjcmVhdGVfdHJhY2tzdGVyXz0KYWN0aW9uOmZ1bmN0aW9uKG4s
cSxwKXt2YXIgbz0zRHRoaXM7cmV0dXJuIGZ1bmN0aW9uKCl7dmFyIHI9M0R7fTtpZihwKXtyWyJm
PQotZGJrZXkiXT0zRHB9JC5hamF4KHt1cmw6bisiL2xpc3RfdHJhY2tzPyIrJC5wYXJhbShyKSxk
YXRhVHlwZToiaHRtbCIsZXJyb3I9CjpmdW5jdGlvbigpe28ubW9kYWwuc2hvdyh7dGl0bGU6IlNv
bWV0aGluZyB3ZW50IHdyb25nISIsYm9keToiVW5mb3J0dW5hdGVseT0KIHdlIGNvdWxkIG5vdCBh
ZGQgdGhpcyBkYXRhc2V0IHRvIHRoZSB0cmFjayBicm93c2VyLiBQbGVhc2UgdHJ5IGFnYWluIG9y
IGNvPQpudGFjdCB1cy4iLGJ1dHRvbnM6e0NhbmNlbDpmdW5jdGlvbigpe28ubW9kYWwuaGlkZSgp
fX19KX0sc3VjY2VzczpmdW5jdGlvbig9CnMpe28ubW9kYWwuc2hvdyh7dGl0bGU6IlZpZXcgRGF0
YSBpbiBhIE5ldyBvciBTYXZlZCBWaXN1YWxpemF0aW9uIixidXR0b25zOj0Ke0NhbmNlbDpmdW5j
dGlvbigpe28ubW9kYWwuaGlkZSgpfSwiVmlldyBpbiBzYXZlZCB2aXN1YWxpemF0aW9uIjpmdW5j
dGlvbigpPQp7by5tb2RhbC5zaG93KHt0aXRsZToiQWRkIERhdGEgdG8gU2F2ZWQgVmlzdWFsaXph
dGlvbiIsYm9keTpzLGJ1dHRvbnM6e0NhbmM9CmVsOmZ1bmN0aW9uKCl7by5tb2RhbC5oaWRlKCl9
LCJBZGQgdG8gdmlzdWFsaXphdGlvbiI6ZnVuY3Rpb24oKXtvLm1vZGFsLmhpZD0KZSgpO28ubW9k
YWwuJGVsLmZpbmQoImlucHV0W25hbWU9M0RpZF06Y2hlY2tlZCIpLmVhY2goZnVuY3Rpb24oKXt2
YXIgdD0zRCQoPQp0aGlzKS52YWwoKTtxLmlkPTNEdDtvLmZyYW1lLmFkZCh7dGl0bGU6IlRyYWNr
c3RlciIsdHlwZToidXJsIixjb250ZW50Om4rIi89CnRyYWNrc3Rlcj8iKyQucGFyYW0ocSl9KX0p
fX19KX0sIlZpZXcgaW4gbmV3IHZpc3VhbGl6YXRpb24iOmZ1bmN0aW9uKCl7by5tbz0KZGFsLmhp
ZGUoKTtvLmZyYW1lLmFkZCh7dGl0bGU6IlRyYWNrc3RlciIsdHlwZToidXJsIixjb250ZW50Om4r
Ii90cmFja3N0ZXI/PQoiKyQucGFyYW0ocSl9KX19fSl9fSk7cmV0dXJuIGZhbHNlfX19KTt2YXIg
bD0zRGZ1bmN0aW9uKHEsbyxyLG4pe3ZhciBwPTNEbmU9Cncgbyh7bW9kZWw6bmV3IHEocil9KTtw
LnJlbmRlcigpO2lmKG4pe24uYXBwZW5kKHAuJGVsKX1yZXR1cm4gcH07dmFyIGM9M0RmdT0KbmN0
aW9uKHApe2lmKCFwLm1vZGVsKXtwLm1vZGVsPTNEbmV3IGkocC5kYXRhc2V0X2NvbmZpZyl9dmFy
IG89M0RwLnBhcmVudF9lPQpsdDt2YXIgcT0zRHAuZW1iZWRkZWQ7ZGVsZXRlIHAuZW1iZWRkZWQ7
ZGVsZXRlIHAucGFyZW50X2VsdDtkZWxldGUgcC5kYXRhc2U9CnRfY29uZmlnO3ZhciBuPTNEKHE/
bmV3IG0ocCk6bmV3IGcocCkpO24ucmVuZGVyKCk7aWYobyl7by5hcHBlbmQobi4kZWwpO24uZT0K
eHBhbmRfdG9fY29udGFpbmVyKCl9cmV0dXJuIG59O3JldHVybntEYXRhc2V0OmIsVGFidWxhckRh
dGFzZXQ6aSxEYXRhc2V0Q29sPQpsZWN0aW9uOmUsVGFidWxhckRhdGFzZXRDaHVua2VkVmlldzph
LGNyZWF0ZVRhYnVsYXJEYXRhc2V0Q2h1bmtlZFZpZXc6Y319KTsKXCBObyBuZXdsaW5lIGF0IGVu
ZCBvZiBmaWxlCgoKaHR0cHM6Ly9iaXRidWNrZXQub3JnL2dhbGF4eS9nYWxheHktY2VudHJhbC9j
b21taXRzLzM2NDU3Y2MxZWMyNi8KQ2hhbmdlc2V0OiAgIDM2NDU3Y2MxZWMyNgpVc2VyOiAgICAg
ICAgZGFubm9uCkRhdGU6ICAgICAgICAyMDE0LTEyLTE1IDE0OjQ3OjQ1KzAwOjAwClN1bW1hcnk6
ICAgICBTd2FwIHRhYnVsYXIgcmVuZGVyaW5nIGZyb20gdXNpbmcgaW5saW5lIGNzcyB0byBhcHBs
eWluZyBzdHlsZT0KcyBmb3Igcm93L2hlYWRlci4KQWZmZWN0ZWQgIzogIDUgZmlsZXMKCmRpZmYg
LXIgOGVjNjkwNTI2MDBhMmM5MTVmODhkMzE4NTg4NGExODVlODIwODZkMSAtciAzNjQ1N2NjMWVj
MjZjNDQwZTdhOTk2Yz0KZTUxZWI4N2VkN2M1OGNjNGQgY2xpZW50L2dhbGF4eS9zY3JpcHRzL212
Yy9kYXRhLmpzCi0tLSBhL2NsaWVudC9nYWxheHkvc2NyaXB0cy9tdmMvZGF0YS5qcworKysgYi9j
bGllbnQvZ2FsYXh5L3NjcmlwdHMvbXZjL2RhdGEuanMKQEAgLTEyOCwxMCArMTI4LDYgQEAKICAg
ICAgICAgdGhpcy5yb3dfY291bnQgPTNEIDA7CiAgICAgICAgIHRoaXMubG9hZGluZ19jaHVuayA9
M0QgZmFsc2U7Cj0yMAotICAgICAgICAvLyBDU1MgY29sb3JzIHVzZWQgaW4gdGFibGUuCi0gICAg
ICAgIHRoaXMuaGVhZGVyX2NvbG9yID0zRCAnI0FBQSc7Ci0gICAgICAgIHRoaXMuZGFya19yb3df
Y29sb3IgPTNEICcjREREJzsKLQogICAgICAgICAvLyBsb2FkIHRyYWNrc3RlciBidXR0b24KICAg
ICAgICAgbmV3IFRhYnVsYXJCdXR0b25UcmFja3N0ZXJWaWV3KHsKICAgICAgICAgICAgIG1vZGVs
ICAgOiBvcHRpb25zLm1vZGVsLApAQCAtMTczLDcgKzE2OSw3IEBACiAgICAgICAgIH0pOwogICAg
ICAgICB0aGlzLiRlbC5hcHBlbmQoZGF0YV90YWJsZSk7CiAgICAgICAgIHZhciBjb2x1bW5fbmFt
ZXMgPTNEIHRoaXMubW9kZWwuZ2V0X21ldGFkYXRhKCdjb2x1bW5fbmFtZXMnKSwKLSAgICAgICAg
ICAgIGhlYWRlcl9yb3cgPTNEICQoJzx0ci8+JykuY3NzKCdiYWNrZ3JvdW5kLWNvbG9yJywgdGhp
cy5oZWFkZXJfPQpjb2xvcikuYXBwZW5kVG8oZGF0YV90YWJsZSk7CisgICAgICAgICAgICBoZWFk
ZXJfcm93ID0zRCAkKCc8dHIvPicpLmFwcGVuZFRvKGRhdGFfdGFibGUpOwogICAgICAgICBpZiAo
Y29sdW1uX25hbWVzKSB7CiAgICAgICAgICAgICBoZWFkZXJfcm93LmFwcGVuZCgnPHRoPicgKyBj
b2x1bW5fbmFtZXMuam9pbignPC90aD48dGg+JykgKyAnPD0KL3RoPicpOwogICAgICAgICB9IGVs
c2UgewpAQCAtMjM2LDcgKzIzMiw3IEBACiAgICAgICAgICAgICBudW1fY29sdW1ucyA9M0QgdGhp
cy5tb2RlbC5nZXRfbWV0YWRhdGEoJ2NvbHVtbnMnKTsKPTIwCiAgICAgICAgIGlmICh0aGlzLnJv
d19jb3VudCAlIDIgIT0zRD0zRCAwKSB7Ci0gICAgICAgICAgICByb3cuY3NzKCdiYWNrZ3JvdW5k
LWNvbG9yJywgdGhpcy5kYXJrX3Jvd19jb2xvcik7CisgICAgICAgICAgICByb3cuYWRkQ2xhc3Mo
J2Rhcmtfcm93Jyk7CiAgICAgICAgIH0KPTIwCiAgICAgICAgIGlmIChjZWxscy5sZW5ndGggPTNE
PTNEPTNEIG51bV9jb2x1bW5zKSB7CgpkaWZmIC1yIDhlYzY5MDUyNjAwYTJjOTE1Zjg4ZDMxODU4
ODRhMTg1ZTgyMDg2ZDEgLXIgMzY0NTdjYzFlYzI2YzQ0MGU3YTk5NmM9CmU1MWViODdlZDdjNThj
YzRkIHN0YXRpYy9zY3JpcHRzL212Yy9kYXRhLmpzCi0tLSBhL3N0YXRpYy9zY3JpcHRzL212Yy9k
YXRhLmpzCisrKyBiL3N0YXRpYy9zY3JpcHRzL212Yy9kYXRhLmpzCkBAIC0xMjgsMTAgKzEyOCw2
IEBACiAgICAgICAgIHRoaXMucm93X2NvdW50ID0zRCAwOwogICAgICAgICB0aGlzLmxvYWRpbmdf
Y2h1bmsgPTNEIGZhbHNlOwo9MjAKLSAgICAgICAgLy8gQ1NTIGNvbG9ycyB1c2VkIGluIHRhYmxl
LgotICAgICAgICB0aGlzLmhlYWRlcl9jb2xvciA9M0QgJyNBQUEnOwotICAgICAgICB0aGlzLmRh
cmtfcm93X2NvbG9yID0zRCAnI0RERCc7Ci0KICAgICAgICAgLy8gbG9hZCB0cmFja3N0ZXIgYnV0
dG9uCiAgICAgICAgIG5ldyBUYWJ1bGFyQnV0dG9uVHJhY2tzdGVyVmlldyh7CiAgICAgICAgICAg
ICBtb2RlbCAgIDogb3B0aW9ucy5tb2RlbCwKQEAgLTE3Myw3ICsxNjksNyBAQAogICAgICAgICB9
KTsKICAgICAgICAgdGhpcy4kZWwuYXBwZW5kKGRhdGFfdGFibGUpOwogICAgICAgICB2YXIgY29s
dW1uX25hbWVzID0zRCB0aGlzLm1vZGVsLmdldF9tZXRhZGF0YSgnY29sdW1uX25hbWVzJyksCi0g
ICAgICAgICAgICBoZWFkZXJfcm93ID0zRCAkKCc8dHIvPicpLmNzcygnYmFja2dyb3VuZC1jb2xv
cicsIHRoaXMuaGVhZGVyXz0KY29sb3IpLmFwcGVuZFRvKGRhdGFfdGFibGUpOworICAgICAgICAg
ICAgaGVhZGVyX3JvdyA9M0QgJCgnPHRyLz4nKS5hcHBlbmRUbyhkYXRhX3RhYmxlKTsKICAgICAg
ICAgaWYgKGNvbHVtbl9uYW1lcykgewogICAgICAgICAgICAgaGVhZGVyX3Jvdy5hcHBlbmQoJzx0
aD4nICsgY29sdW1uX25hbWVzLmpvaW4oJzwvdGg+PHRoPicpICsgJzw9Ci90aD4nKTsKICAgICAg
ICAgfSBlbHNlIHsKQEAgLTIzNiw3ICsyMzIsNyBAQAogICAgICAgICAgICAgbnVtX2NvbHVtbnMg
PTNEIHRoaXMubW9kZWwuZ2V0X21ldGFkYXRhKCdjb2x1bW5zJyk7Cj0yMAogICAgICAgICBpZiAo
dGhpcy5yb3dfY291bnQgJSAyICE9M0Q9M0QgMCkgewotICAgICAgICAgICAgcm93LmNzcygnYmFj
a2dyb3VuZC1jb2xvcicsIHRoaXMuZGFya19yb3dfY29sb3IpOworICAgICAgICAgICAgcm93LmFk
ZENsYXNzKCdkYXJrX3JvdycpOwogICAgICAgICB9Cj0yMAogICAgICAgICBpZiAoY2VsbHMubGVu
Z3RoID0zRD0zRD0zRCBudW1fY29sdW1ucykgewoKZGlmZiAtciA4ZWM2OTA1MjYwMGEyYzkxNWY4
OGQzMTg1ODg0YTE4NWU4MjA4NmQxIC1yIDM2NDU3Y2MxZWMyNmM0NDBlN2E5OTZjPQplNTFlYjg3
ZWQ3YzU4Y2M0ZCBzdGF0aWMvc2NyaXB0cy9wYWNrZWQvbXZjL2RhdGEuanMKLS0tIGEvc3RhdGlj
L3NjcmlwdHMvcGFja2VkL212Yy9kYXRhLmpzCisrKyBiL3N0YXRpYy9zY3JpcHRzL3BhY2tlZC9t
dmMvZGF0YS5qcwpAQCAtMSwxICsxLDEgQEAKLWRlZmluZShbIm12Yy91aS91aS1tb2RhbCIsIm12
Yy91aS91aS1mcmFtZXMiLCJtdmMvdWkvaWNvbi1idXR0b24iXSxmdW5jdGlvPQpuKGssaixmKXt2
YXIgaD0zREJhY2tib25lLk1vZGVsLmV4dGVuZCh7fSk7dmFyIGI9M0RCYWNrYm9uZS5Nb2RlbC5l
eHRlbmQoe2Q9CmVmYXVsdHM6e2lkOiIiLHR5cGU6IiIsbmFtZToiIixoZGFfbGRkYToiaGRhIixt
ZXRhZGF0YTpudWxsfSxpbml0aWFsaXplOmZ1bj0KY3Rpb24oKXtpZighdGhpcy5nZXQoIm1ldGFk
YXRhIikpe3RoaXMuX3NldF9tZXRhZGF0YSgpfXRoaXMub24oImNoYW5nZSIsdGhpPQpzLl9zZXRf
bWV0YWRhdGEsdGhpcyl9LF9zZXRfbWV0YWRhdGE6ZnVuY3Rpb24oKXt2YXIgbj0zRG5ldyBoKCk7
Xy5lYWNoKF8ua2U9CnlzKHRoaXMuYXR0cmlidXRlcyksZnVuY3Rpb24obyl7aWYoby5pbmRleE9m
KCJtZXRhZGF0YV8iKT0zRD0zRD0zRDApe3ZhciBwPQo9M0RvLnNwbGl0KCJtZXRhZGF0YV8iKVsx
XTtuLnNldChwLHRoaXMuYXR0cmlidXRlc1tvXSk7ZGVsZXRlIHRoaXMuYXR0cmlidXQ9CmVzW29d
fX0sdGhpcyk7dGhpcy5zZXQoIm1ldGFkYXRhIixuLHtzaWxlbnQ6dHJ1ZX0pfSxnZXRfbWV0YWRh
dGE6ZnVuY3Rpb24obj0KKXtyZXR1cm4gdGhpcy5hdHRyaWJ1dGVzLm1ldGFkYXRhLmdldChuKX0s
dXJsUm9vdDpnYWxheHlfY29uZmlnLnJvb3QrImFwaS9kPQphdGFzZXRzIn0pO3ZhciBpPTNEYi5l
eHRlbmQoe2RlZmF1bHRzOl8uZXh0ZW5kKHt9LGIucHJvdG90eXBlLmRlZmF1bHRzLHtjaHU9Cm5r
X3VybDpudWxsLGZpcnN0X2RhdGFfY2h1bms6bnVsbCxjaHVua19pbmRleDotMSxhdF9lb2Y6ZmFs
c2V9KSxpbml0aWFsaXplOj0KZnVuY3Rpb24obil7Yi5wcm90b3R5cGUuaW5pdGlhbGl6ZS5jYWxs
KHRoaXMpO3RoaXMuYXR0cmlidXRlcy5jaHVua19pbmRleD0KPTNEKHRoaXMuYXR0cmlidXRlcy5m
aXJzdF9kYXRhX2NodW5rPzE6MCk7dGhpcy5hdHRyaWJ1dGVzLmNodW5rX3VybD0zRGdhbGF4PQp5
X2NvbmZpZy5yb290KyJkYXRhc2V0L2Rpc3BsYXk/ZGF0YXNldF9pZD0zRCIrdGhpcy5pZDt0aGlz
LmF0dHJpYnV0ZXMudXJsX3Y9Cml6PTNEZ2FsYXh5X2NvbmZpZy5yb290KyJ2aXN1YWxpemF0aW9u
In0sZ2V0X25leHRfY2h1bms6ZnVuY3Rpb24oKXtpZih0aGlzLj0KYXR0cmlidXRlcy5hdF9lb2Yp
e3JldHVybiBudWxsfXZhciBuPTNEdGhpcyxvPTNEJC5EZWZlcnJlZCgpOyQuZ2V0SlNPTih0aGlz
PQouYXR0cmlidXRlcy5jaHVua191cmwse2NodW5rOm4uYXR0cmlidXRlcy5jaHVua19pbmRleCsr
fSkuc3VjY2VzcyhmdW5jdGlvbig9CnApe3ZhciBxO2lmKHAuY2tfZGF0YSE9M0Q9M0QiIil7cT0z
RHB9ZWxzZXtuLmF0dHJpYnV0ZXMuYXRfZW9mPTNEdHJ1ZTtxPTNEbj0KdWxsfW8ucmVzb2x2ZShx
KX0pO3JldHVybiBvfX0pO3ZhciBlPTNEQmFja2JvbmUuQ29sbGVjdGlvbi5leHRlbmQoe21vZGVs
OmJ9PQopO3ZhciBhPTNEQmFja2JvbmUuVmlldy5leHRlbmQoe2luaXRpYWxpemU6ZnVuY3Rpb24o
bil7dGhpcy5yb3dfY291bnQ9M0QwO3Q9Cmhpcy5sb2FkaW5nX2NodW5rPTNEZmFsc2U7dGhpcy5o
ZWFkZXJfY29sb3I9M0QiI0FBQSI7dGhpcy5kYXJrX3Jvd19jb2xvcj0zRD0KIiNEREQiO25ldyBk
KHttb2RlbDpuLm1vZGVsLCRlbDp0aGlzLiRlbH0pfSxleHBhbmRfdG9fY29udGFpbmVyOmZ1bmN0
aW9uKCl7PQppZih0aGlzLiRlbC5oZWlnaHQoKTx0aGlzLnNjcm9sbF9lbHQuaGVpZ2h0KCkpe3Ro
aXMuYXR0ZW1wdF90b19mZXRjaCgpfX0sYXQ9CnRlbXB0X3RvX2ZldGNoOmZ1bmN0aW9uKG8pe3Zh
ciBuPTNEdGhpcztpZighdGhpcy5sb2FkaW5nX2NodW5rJiZ0aGlzLnNjcm9sbD0KZWRfdG9fYm90
dG9tKCkpe3RoaXMubG9hZGluZ19jaHVuaz0zRHRydWU7dGhpcy5sb2FkaW5nX2luZGljYXRvci5z
aG93KCk7JC53PQpoZW4obi5tb2RlbC5nZXRfbmV4dF9jaHVuaygpKS50aGVuKGZ1bmN0aW9uKHAp
e2lmKHApe24uX3JlbmRlckNodW5rKHApO24ubG89CmFkaW5nX2NodW5rPTNEZmFsc2V9bi5sb2Fk
aW5nX2luZGljYXRvci5oaWRlKCk7bi5leHBhbmRfdG9fY29udGFpbmVyKCl9KX19LD0KcmVuZGVy
OmZ1bmN0aW9uKCl7dGhpcy5sb2FkaW5nX2luZGljYXRvcj0zRCQoIjxkaXYvPiIpLmF0dHIoImlk
IiwibG9hZGluZ19pPQpuZGljYXRvciIpO3RoaXMuJGVsLmFwcGVuZCh0aGlzLmxvYWRpbmdfaW5k
aWNhdG9yKTt2YXIgcj0zRCQoIjx0YWJsZS8+IikuYXQ9CnRyKHtpZDoiY29udGVudF90YWJsZSIs
Y2VsbHBhZGRpbmc6MH0pO3RoaXMuJGVsLmFwcGVuZChyKTt2YXIgbj0zRHRoaXMubW9kZT0KbC5n
ZXRfbWV0YWRhdGEoImNvbHVtbl9uYW1lcyIpLHM9M0QkKCI8dHIvPiIpLmNzcygiYmFja2dyb3Vu
ZC1jb2xvciIsdGhpcy5oPQplYWRlcl9jb2xvcikuYXBwZW5kVG8ocik7aWYobil7cy5hcHBlbmQo
Ijx0aD4iK24uam9pbigiPC90aD48dGg+IikrIjwvdGg+Iik9Cn1lbHNle2Zvcih2YXIgcT0zRDE7
cTw9M0R0aGlzLm1vZGVsLmdldF9tZXRhZGF0YSgiY29sdW1ucyIpO3ErKyl7cy5hcHBlbmQoIj0K
PHRoPiIrcSsiPC90aD4iKX19dmFyIHA9M0R0aGlzLG89M0R0aGlzLm1vZGVsLmdldCgiZmlyc3Rf
ZGF0YV9jaHVuayIpO2lmKG8pPQp7dGhpcy5fcmVuZGVyQ2h1bmsobyl9ZWxzZXskLndoZW4ocC5t
b2RlbC5nZXRfbmV4dF9jaHVuaygpKS50aGVuKGZ1bmN0aW9uKHQ9Cil7cC5fcmVuZGVyQ2h1bmso
dCl9KX10aGlzLnNjcm9sbF9lbHQuc2Nyb2xsKGZ1bmN0aW9uKCl7cC5hdHRlbXB0X3RvX2ZldGNo
KD0KKX0pfSxzY3JvbGxlZF90b19ib3R0b206ZnVuY3Rpb24oKXtyZXR1cm4gZmFsc2V9LF9yZW5k
ZXJDZWxsOmZ1bmN0aW9uKHEsbixyPQope3ZhciBvPTNEJCgiPHRkPiIpLnRleHQocSk7dmFyIHA9
M0R0aGlzLm1vZGVsLmdldF9tZXRhZGF0YSgiY29sdW1uX3R5cGVzIik9CjtpZihyIT0zRD0zRHVu
ZGVmaW5lZCl7by5hdHRyKCJjb2xzcGFuIixyKS5hZGRDbGFzcygic3RyaW5nYWxpZ24iKX1lbHNl
e2lmKD0KcCl7aWYobjxwLmxlbmd0aCl7aWYocFtuXT0zRD0zRD0zRCJzdHIifHxwW25dPTNEPTNE
PTNEImxpc3QiKXtvLmFkZENsYXNzKCJzPQp0cmluZ2FsaWduIil9fX19cmV0dXJuIG99LF9yZW5k
ZXJSb3c6ZnVuY3Rpb24obil7dmFyIG89M0RuLnNwbGl0KCJcdCIpLHE9M0Q9CiQoIjx0cj4iKSxw
PTNEdGhpcy5tb2RlbC5nZXRfbWV0YWRhdGEoImNvbHVtbnMiKTtpZih0aGlzLnJvd19jb3VudCUy
IT0zRD0zRD0KMCl7cS5jc3MoImJhY2tncm91bmQtY29sb3IiLHRoaXMuZGFya19yb3dfY29sb3Ip
fWlmKG8ubGVuZ3RoPTNEPTNEPTNEcCl7Xy5lPQphY2gobyxmdW5jdGlvbihzLHIpe3EuYXBwZW5k
KHRoaXMuX3JlbmRlckNlbGwocyxyKSl9LHRoaXMpfWVsc2V7aWYoby5sZW5ndGg9Cj5wKXtfLmVh
Y2goby5zbGljZSgwLHAtMSksZnVuY3Rpb24ocyxyKXtxLmFwcGVuZCh0aGlzLl9yZW5kZXJDZWxs
KHMscikpfSx0aD0KaXMpO3EuYXBwZW5kKHRoaXMuX3JlbmRlckNlbGwoby5zbGljZShwLTEpLmpv
aW4oIlx0IikscC0xKSl9ZWxzZXtpZihwPjUmJm8uPQpsZW5ndGg9M0Q9M0Q9M0RwLTEpe18uZWFj
aChvLGZ1bmN0aW9uKHMscil7cS5hcHBlbmQodGhpcy5fcmVuZGVyQ2VsbChzLHIpKX09Cix0aGlz
KTtxLmFwcGVuZCgkKCI8dGQ+IikpfWVsc2V7cS5hcHBlbmQodGhpcy5fcmVuZGVyQ2VsbChuLDAs
cCkpfX19dGhpcy5ybz0Kd19jb3VudCsrO3JldHVybiBxfSxfcmVuZGVyQ2h1bms6ZnVuY3Rpb24o
bil7dmFyIG89M0R0aGlzLiRlbC5maW5kKCJ0YWJsZSIpPQo7Xy5lYWNoKG4uY2tfZGF0YS5zcGxp
dCgiXG4iKSxmdW5jdGlvbihwLHEpe2lmKHAhPTNEPTNEIiIpe28uYXBwZW5kKHRoaXMuX3I9CmVu
ZGVyUm93KHApKX19LHRoaXMpfX0pO3ZhciBnPTNEYS5leHRlbmQoe2luaXRpYWxpemU6ZnVuY3Rp
b24obil7YS5wcm90b3R5cD0KZS5pbml0aWFsaXplLmNhbGwodGhpcyxuKTtzY3JvbGxfZWx0PTNE
Xy5maW5kKHRoaXMuJGVsLnBhcmVudHMoKSxmdW5jdGlvbihvPQope3JldHVybiAkKG8pLmNzcygi
b3ZlcmZsb3ciKT0zRD0zRD0zRCJhdXRvIn0pO2lmKCFzY3JvbGxfZWx0KXtzY3JvbGxfZWx0PQo9
M0R3aW5kb3d9dGhpcy5zY3JvbGxfZWx0PTNEJChzY3JvbGxfZWx0KX0sc2Nyb2xsZWRfdG9fYm90
dG9tOmZ1bmN0aW9uKCl7cmU9CnR1cm4odGhpcy4kZWwuaGVpZ2h0KCktdGhpcy5zY3JvbGxfZWx0
LnNjcm9sbFRvcCgpLXRoaXMuc2Nyb2xsX2VsdC5oZWlnaHQoKT0KPD0zRDApfX0pO3ZhciBtPTNE
YS5leHRlbmQoe2luaXRpYWxpemU6ZnVuY3Rpb24obil7YS5wcm90b3R5cGUuaW5pdGlhbGl6ZS5j
PQphbGwodGhpcyxuKTt0aGlzLnNjcm9sbF9lbHQ9M0R0aGlzLiRlbC5jc3Moe3Bvc2l0aW9uOiJy
ZWxhdGl2ZSIsb3ZlcmZsb3c6InM9CmNyb2xsIixoZWlnaHQ6dGhpcy5vcHRpb25zLmhlaWdodHx8
IjUwMHB4In0pfSxzY3JvbGxlZF90b19ib3R0b206ZnVuY3Rpb24oKT0Ke3JldHVybiB0aGlzLiRl
bC5zY3JvbGxUb3AoKSt0aGlzLiRlbC5pbm5lckhlaWdodCgpPj0zRHRoaXMuZWwuc2Nyb2xsSGVp
Z2h0PQp9fSk7dmFyIGQ9M0RCYWNrYm9uZS5WaWV3LmV4dGVuZCh7Y29sOntjaHJvbTpudWxsLHN0
YXJ0Om51bGwsZW5kOm51bGx9LHVybF89CnZpejpudWxsLGRhdGFzZXRfaWQ6bnVsbCxnZW5vbWVf
YnVpbGQ6bnVsbCxmaWxlX2V4dDpudWxsLGluaXRpYWxpemU6ZnVuY3Rpbz0KbihwKXt2YXIgcz0z
RHBhcmVudC5HYWxheHk7aWYocyYmcy5tb2RhbCl7dGhpcy5tb2RhbD0zRHMubW9kYWx9aWYocyYm
cy5mcmFtPQplKXt0aGlzLmZyYW1lPTNEcy5mcmFtZX1pZighdGhpcy5tb2RhbHx8IXRoaXMuZnJh
bWUpe3JldHVybn12YXIgbz0zRHAubW9kZWw9Cjt2YXIgcj0zRG8uZ2V0KCJtZXRhZGF0YSIpO2lm
KCFvLmdldCgiZmlsZV9leHQiKSl7cmV0dXJufXRoaXMuZmlsZV9leHQ9M0RvLj0KZ2V0KCJmaWxl
X2V4dCIpO2lmKHRoaXMuZmlsZV9leHQ9M0Q9M0QiYmVkIil7aWYoci5nZXQoImNocm9tQ29sIikm
JnIuZ2V0KCJzPQp0YXJ0Q29sIikmJnIuZ2V0KCJlbmRDb2wiKSl7dGhpcy5jb2wuY2hyb209M0Ry
LmdldCgiY2hyb21Db2wiKS0xO3RoaXMuY29sLnM9CnRhcnQ9M0RyLmdldCgic3RhcnRDb2wiKS0x
O3RoaXMuY29sLmVuZD0zRHIuZ2V0KCJlbmRDb2wiKS0xfWVsc2V7Y29uc29sZS5sbz0KZygiVGFi
dWxhckJ1dHRvblRyYWNrc3RlciA6IEJlZC1maWxlIG1ldGFkYXRhIGluY29tcGxldGUuIik7cmV0
dXJufX1pZih0aGlzPQouZmlsZV9leHQ9M0Q9M0QidmNmIil7ZnVuY3Rpb24gcSh1LHYpe2Zvcih2
YXIgdD0zRDA7dDx2Lmxlbmd0aDt0Kyspe2lmKHZbdF09Ci5tYXRjaCh1KSl7cmV0dXJuIHR9fXJl
dHVybiAtMX10aGlzLmNvbC5jaHJvbT0zRHEoIkNocm9tIixyLmdldCgiY29sdW1uX25hbT0KZXMi
KSk7dGhpcy5jb2wuc3RhcnQ9M0RxKCJQb3MiLHIuZ2V0KCJjb2x1bW5fbmFtZXMiKSk7dGhpcy5j
b2wuZW5kPTNEbnVsbDtpPQpmKHRoaXMuY29sLmNocm9tPTNEPTNELTF8fHRoaXMuY29sLnN0YXJ0
PTNEPTNELTEpe2NvbnNvbGUubG9nKCJUYWJ1bGFyQnV0dG89Cm5UcmFja3N0ZXIgOiBWQ0YtZmls
ZSBtZXRhZGF0YSBpbmNvbXBsZXRlLiIpO3JldHVybn19aWYodGhpcy5jb2wuY2hyb209M0Q9Cj0z
RD0zRHVuZGVmaW5lZCl7cmV0dXJufWlmKG8uaWQpe3RoaXMuZGF0YXNldF9pZD0zRG8uaWR9ZWxz
ZXtjb25zb2xlLmxvZygiVD0KYWJ1bGFyQnV0dG9uVHJhY2tzdGVyIDogRGF0YXNldCBpZGVudGlm
aWNhdGlvbiBpcyBtaXNzaW5nLiIpO3JldHVybn1pZihvLmdlPQp0KCJ1cmxfdml6Iikpe3RoaXMu
dXJsX3Zpej0zRG8uZ2V0KCJ1cmxfdml6Iil9ZWxzZXtjb25zb2xlLmxvZygiVGFidWxhckJ1dHQ9
Cm9uVHJhY2tzdGVyIDogVXJsIGZvciB2aXN1YWxpemF0aW9uIGNvbnRyb2xsZXIgaXMgbWlzc2lu
Zy4iKTtyZXR1cm59aWYoby5nZT0KdCgiZ2Vub21lX2J1aWxkIikpe3RoaXMuZ2Vub21lX2J1aWxk
PTNEby5nZXQoImdlbm9tZV9idWlsZCIpfXZhciBuPTNEbmV3IGYuPQpJY29uQnV0dG9uVmlldyh7
bW9kZWw6bmV3IGYuSWNvbkJ1dHRvbih7dGl0bGU6IlZpc3VhbGl6ZSIsaWNvbl9jbGFzczoiY2hh
cnQ9Cl9jdXJ2ZSIsaWQ6ImJ0bl92aXoifSl9KTt0aGlzLnNldEVsZW1lbnQocC4kZWwpO3RoaXMu
JGVsLmFwcGVuZChuLnJlbmRlcigpLj0KJGVsKTt0aGlzLmhpZGUoKX0sZXZlbnRzOnsibW91c2Vv
dmVyIHRyIjoic2hvdyIsbW91c2VsZWF2ZToiaGlkZSJ9LHNob3c6ZnVuPQpjdGlvbihzKXtmdW5j
dGlvbiByKHgpe3JldHVybiAhaXNOYU4ocGFyc2VGbG9hdCh4KSkmJmlzRmluaXRlKHgpfWlmKHRo
aXMuY289CmwuY2hyb209M0Q9M0Q9M0RudWxsKXtyZXR1cm59dmFyIHc9M0QkKHMudGFyZ2V0KS5w
YXJlbnQoKTt2YXIgdD0zRHcuY2hpbGRyZT0KbigpLmVxKHRoaXMuY29sLmNocm9tKS5odG1sKCk7
dmFyIG49M0R3LmNoaWxkcmVuKCkuZXEodGhpcy5jb2wuc3RhcnQpLmh0bWwoPQopO3ZhciBwPTNE
dGhpcy5jb2wuZW5kP3cuY2hpbGRyZW4oKS5lcSh0aGlzLmNvbC5lbmQpLmh0bWwoKTpuO2lmKCF0
Lm1hdGNoKCI9Cl4jIikmJnQhPTNEPTNEIiImJnIobikpe3ZhciB2PTNEe2RhdGFzZXRfaWQ6dGhp
cy5kYXRhc2V0X2lkLGdlbmVfcmVnaW9uOnQrIj0KOiIrbisiLSIrcH07dmFyIHE9M0R3Lm9mZnNl
dCgpO3ZhciBvPTNEcS5sZWZ0LTEwO3ZhciB1PTNEcS50b3AtJCh3aW5kb3cpLnNjPQpyb2xsVG9w
KCkrMzskKCIjYnRuX3ZpeiIpLmNzcyh7cG9zaXRpb246ImZpeGVkIix0b3A6dSsicHgiLGxlZnQ6
bysicHgifSk7JCg9CiIjYnRuX3ZpeiIpLm9mZigiY2xpY2siKTskKCIjYnRuX3ZpeiIpLmNsaWNr
KHRoaXMuY3JlYXRlX3RyYWNrc3Rlcl9hY3Rpb24odD0KaGlzLnVybF92aXosdix0aGlzLmdlbm9t
ZV9idWlsZCkpOyQoIiNidG5fdml6Iikuc2hvdygpfWVsc2V7JCgiI2J0bl92aXoiKS5oPQppZGUo
KX19LGhpZGU6ZnVuY3Rpb24oKXt0aGlzLiRlbC5maW5kKCIjYnRuX3ZpeiIpLmhpZGUoKX0sY3Jl
YXRlX3RyYWNrc3Rlcl89CmFjdGlvbjpmdW5jdGlvbihuLHEscCl7dmFyIG89M0R0aGlzO3JldHVy
biBmdW5jdGlvbigpe3ZhciByPTNEe307aWYocCl7clsiZj0KLWRia2V5Il09M0RwfSQuYWpheCh7
dXJsOm4rIi9saXN0X3RyYWNrcz8iKyQucGFyYW0ociksZGF0YVR5cGU6Imh0bWwiLGVycm9yPQo6
ZnVuY3Rpb24oKXtvLm1vZGFsLnNob3coe3RpdGxlOiJTb21ldGhpbmcgd2VudCB3cm9uZyEiLGJv
ZHk6IlVuZm9ydHVuYXRlbHk9CiB3ZSBjb3VsZCBub3QgYWRkIHRoaXMgZGF0YXNldCB0byB0aGUg
dHJhY2sgYnJvd3Nlci4gUGxlYXNlIHRyeSBhZ2FpbiBvciBjbz0KbnRhY3QgdXMuIixidXR0b25z
OntDYW5jZWw6ZnVuY3Rpb24oKXtvLm1vZGFsLmhpZGUoKX19fSl9LHN1Y2Nlc3M6ZnVuY3Rpb24o
PQpzKXtvLm1vZGFsLnNob3coe3RpdGxlOiJWaWV3IERhdGEgaW4gYSBOZXcgb3IgU2F2ZWQgVmlz
dWFsaXphdGlvbiIsYnV0dG9uczo9CntDYW5jZWw6ZnVuY3Rpb24oKXtvLm1vZGFsLmhpZGUoKX0s
IlZpZXcgaW4gc2F2ZWQgdmlzdWFsaXphdGlvbiI6ZnVuY3Rpb24oKT0Ke28ubW9kYWwuc2hvdyh7
dGl0bGU6IkFkZCBEYXRhIHRvIFNhdmVkIFZpc3VhbGl6YXRpb24iLGJvZHk6cyxidXR0b25zOntD
YW5jPQplbDpmdW5jdGlvbigpe28ubW9kYWwuaGlkZSgpfSwiQWRkIHRvIHZpc3VhbGl6YXRpb24i
OmZ1bmN0aW9uKCl7by5tb2RhbC5oaWQ9CmUoKTtvLm1vZGFsLiRlbC5maW5kKCJpbnB1dFtuYW1l
PTNEaWRdOmNoZWNrZWQiKS5lYWNoKGZ1bmN0aW9uKCl7dmFyIHQ9M0QkKD0KdGhpcykudmFsKCk7
cS5pZD0zRHQ7by5mcmFtZS5hZGQoe3RpdGxlOiJUcmFja3N0ZXIiLHR5cGU6InVybCIsY29udGVu
dDpuKyIvPQp0cmFja3N0ZXI/IiskLnBhcmFtKHEpfSl9KX19fSl9LCJWaWV3IGluIG5ldyB2aXN1
YWxpemF0aW9uIjpmdW5jdGlvbigpe28ubW89CmRhbC5oaWRlKCk7by5mcmFtZS5hZGQoe3RpdGxl
OiJUcmFja3N0ZXIiLHR5cGU6InVybCIsY29udGVudDpuKyIvdHJhY2tzdGVyPz0KIiskLnBhcmFt
KHEpfSl9fX0pfX0pO3JldHVybiBmYWxzZX19fSk7dmFyIGw9M0RmdW5jdGlvbihxLG8scixuKXt2
YXIgcD0zRG5lPQp3IG8oe21vZGVsOm5ldyBxKHIpfSk7cC5yZW5kZXIoKTtpZihuKXtuLmFwcGVu
ZChwLiRlbCl9cmV0dXJuIHB9O3ZhciBjPTNEZnU9Cm5jdGlvbihwKXtpZighcC5tb2RlbCl7cC5t
b2RlbD0zRG5ldyBpKHAuZGF0YXNldF9jb25maWcpfXZhciBvPTNEcC5wYXJlbnRfZT0KbHQ7dmFy
IHE9M0RwLmVtYmVkZGVkO2RlbGV0ZSBwLmVtYmVkZGVkO2RlbGV0ZSBwLnBhcmVudF9lbHQ7ZGVs
ZXRlIHAuZGF0YXNlPQp0X2NvbmZpZzt2YXIgbj0zRChxP25ldyBtKHApOm5ldyBnKHApKTtuLnJl
bmRlcigpO2lmKG8pe28uYXBwZW5kKG4uJGVsKTtuLmU9CnhwYW5kX3RvX2NvbnRhaW5lcigpfXJl
dHVybiBufTtyZXR1cm57RGF0YXNldDpiLFRhYnVsYXJEYXRhc2V0OmksRGF0YXNldENvbD0KbGVj
dGlvbjplLFRhYnVsYXJEYXRhc2V0Q2h1bmtlZFZpZXc6YSxjcmVhdGVUYWJ1bGFyRGF0YXNldENo
dW5rZWRWaWV3OmN9fSk7ClwgTm8gbmV3bGluZSBhdCBlbmQgb2YgZmlsZQorZGVmaW5lKFsibXZj
L3VpL3VpLW1vZGFsIiwibXZjL3VpL3VpLWZyYW1lcyIsIm12Yy91aS9pY29uLWJ1dHRvbiJdLGZ1
bmN0aW89Cm4oayxqLGYpe3ZhciBoPTNEQmFja2JvbmUuTW9kZWwuZXh0ZW5kKHt9KTt2YXIgYj0z
REJhY2tib25lLk1vZGVsLmV4dGVuZCh7ZD0KZWZhdWx0czp7aWQ6IiIsdHlwZToiIixuYW1lOiIi
LGhkYV9sZGRhOiJoZGEiLG1ldGFkYXRhOm51bGx9LGluaXRpYWxpemU6ZnVuPQpjdGlvbigpe2lm
KCF0aGlzLmdldCgibWV0YWRhdGEiKSl7dGhpcy5fc2V0X21ldGFkYXRhKCl9dGhpcy5vbigiY2hh
bmdlIix0aGk9CnMuX3NldF9tZXRhZGF0YSx0aGlzKX0sX3NldF9tZXRhZGF0YTpmdW5jdGlvbigp
e3ZhciBuPTNEbmV3IGgoKTtfLmVhY2goXy5rZT0KeXModGhpcy5hdHRyaWJ1dGVzKSxmdW5jdGlv
bihvKXtpZihvLmluZGV4T2YoIm1ldGFkYXRhXyIpPTNEPTNEPTNEMCl7dmFyIHA9Cj0zRG8uc3Bs
aXQoIm1ldGFkYXRhXyIpWzFdO24uc2V0KHAsdGhpcy5hdHRyaWJ1dGVzW29dKTtkZWxldGUgdGhp
cy5hdHRyaWJ1dD0KZXNbb119fSx0aGlzKTt0aGlzLnNldCgibWV0YWRhdGEiLG4se3NpbGVudDp0
cnVlfSl9LGdldF9tZXRhZGF0YTpmdW5jdGlvbihuPQope3JldHVybiB0aGlzLmF0dHJpYnV0ZXMu
bWV0YWRhdGEuZ2V0KG4pfSx1cmxSb290OmdhbGF4eV9jb25maWcucm9vdCsiYXBpL2Q9CmF0YXNl
dHMifSk7dmFyIGk9M0RiLmV4dGVuZCh7ZGVmYXVsdHM6Xy5leHRlbmQoe30sYi5wcm90b3R5cGUu
ZGVmYXVsdHMse2NodT0KbmtfdXJsOm51bGwsZmlyc3RfZGF0YV9jaHVuazpudWxsLGNodW5rX2lu
ZGV4Oi0xLGF0X2VvZjpmYWxzZX0pLGluaXRpYWxpemU6PQpmdW5jdGlvbihuKXtiLnByb3RvdHlw
ZS5pbml0aWFsaXplLmNhbGwodGhpcyk7dGhpcy5hdHRyaWJ1dGVzLmNodW5rX2luZGV4PQo9M0Qo
dGhpcy5hdHRyaWJ1dGVzLmZpcnN0X2RhdGFfY2h1bms/MTowKTt0aGlzLmF0dHJpYnV0ZXMuY2h1
bmtfdXJsPTNEZ2FsYXg9CnlfY29uZmlnLnJvb3QrImRhdGFzZXQvZGlzcGxheT9kYXRhc2V0X2lk
PTNEIit0aGlzLmlkO3RoaXMuYXR0cmlidXRlcy51cmxfdj0KaXo9M0RnYWxheHlfY29uZmlnLnJv
b3QrInZpc3VhbGl6YXRpb24ifSxnZXRfbmV4dF9jaHVuazpmdW5jdGlvbigpe2lmKHRoaXMuPQph
dHRyaWJ1dGVzLmF0X2VvZil7cmV0dXJuIG51bGx9dmFyIG49M0R0aGlzLG89M0QkLkRlZmVycmVk
KCk7JC5nZXRKU09OKHRoaXM9Ci5hdHRyaWJ1dGVzLmNodW5rX3VybCx7Y2h1bms6bi5hdHRyaWJ1
dGVzLmNodW5rX2luZGV4Kyt9KS5zdWNjZXNzKGZ1bmN0aW9uKD0KcCl7dmFyIHE7aWYocC5ja19k
YXRhIT0zRD0zRCIiKXtxPTNEcH1lbHNle24uYXR0cmlidXRlcy5hdF9lb2Y9M0R0cnVlO3E9M0Ru
PQp1bGx9by5yZXNvbHZlKHEpfSk7cmV0dXJuIG99fSk7dmFyIGU9M0RCYWNrYm9uZS5Db2xsZWN0
aW9uLmV4dGVuZCh7bW9kZWw6Yn09Cik7dmFyIGE9M0RCYWNrYm9uZS5WaWV3LmV4dGVuZCh7aW5p
dGlhbGl6ZTpmdW5jdGlvbihuKXt0aGlzLnJvd19jb3VudD0zRDA7dD0KaGlzLmxvYWRpbmdfY2h1
bms9M0RmYWxzZTtuZXcgZCh7bW9kZWw6bi5tb2RlbCwkZWw6dGhpcy4kZWx9KX0sZXhwYW5kX3Rv
X2NvPQpudGFpbmVyOmZ1bmN0aW9uKCl7aWYodGhpcy4kZWwuaGVpZ2h0KCk8dGhpcy5zY3JvbGxf
ZWx0LmhlaWdodCgpKXt0aGlzLmF0dGU9Cm1wdF90b19mZXRjaCgpfX0sYXR0ZW1wdF90b19mZXRj
aDpmdW5jdGlvbihvKXt2YXIgbj0zRHRoaXM7aWYoIXRoaXMubG9hZGluZz0KX2NodW5rJiZ0aGlz
LnNjcm9sbGVkX3RvX2JvdHRvbSgpKXt0aGlzLmxvYWRpbmdfY2h1bms9M0R0cnVlO3RoaXMubG9h
ZGluZ19pPQpuZGljYXRvci5zaG93KCk7JC53aGVuKG4ubW9kZWwuZ2V0X25leHRfY2h1bmsoKSku
dGhlbihmdW5jdGlvbihwKXtpZihwKXtuLl89CnJlbmRlckNodW5rKHApO24ubG9hZGluZ19jaHVu
az0zRGZhbHNlfW4ubG9hZGluZ19pbmRpY2F0b3IuaGlkZSgpO24uZXhwYW5kXz0KdG9fY29udGFp
bmVyKCl9KX19LHJlbmRlcjpmdW5jdGlvbigpe3RoaXMubG9hZGluZ19pbmRpY2F0b3I9M0QkKCI8
ZGl2Lz4iKS5hPQp0dHIoImlkIiwibG9hZGluZ19pbmRpY2F0b3IiKTt0aGlzLiRlbC5hcHBlbmQo
dGhpcy5sb2FkaW5nX2luZGljYXRvcik7dmFyIHI9Cj0zRCQoIjx0YWJsZS8+IikuYXR0cih7aWQ6
ImNvbnRlbnRfdGFibGUiLGNlbGxwYWRkaW5nOjB9KTt0aGlzLiRlbC5hcHBlbmQocj0KKTt2YXIg
bj0zRHRoaXMubW9kZWwuZ2V0X21ldGFkYXRhKCJjb2x1bW5fbmFtZXMiKSxzPTNEJCgiPHRyLz4i
KS5hcHBlbmRUbyhyPQopO2lmKG4pe3MuYXBwZW5kKCI8dGg+IituLmpvaW4oIjwvdGg+PHRoPiIp
KyI8L3RoPiIpfWVsc2V7Zm9yKHZhciBxPTNEMTtxPD0KPTNEdGhpcy5tb2RlbC5nZXRfbWV0YWRh
dGEoImNvbHVtbnMiKTtxKyspe3MuYXBwZW5kKCI8dGg+IitxKyI8L3RoPiIpfX12YXIgPQpwPTNE
dGhpcyxvPTNEdGhpcy5tb2RlbC5nZXQoImZpcnN0X2RhdGFfY2h1bmsiKTtpZihvKXt0aGlzLl9y
ZW5kZXJDaHVuayhvKX09CmVsc2V7JC53aGVuKHAubW9kZWwuZ2V0X25leHRfY2h1bmsoKSkudGhl
bihmdW5jdGlvbih0KXtwLl9yZW5kZXJDaHVuayh0KX0pfT0KdGhpcy5zY3JvbGxfZWx0LnNjcm9s
bChmdW5jdGlvbigpe3AuYXR0ZW1wdF90b19mZXRjaCgpfSl9LHNjcm9sbGVkX3RvX2JvdHRvPQpt
OmZ1bmN0aW9uKCl7cmV0dXJuIGZhbHNlfSxfcmVuZGVyQ2VsbDpmdW5jdGlvbihxLG4scil7dmFy
IG89M0QkKCI8dGQ+IikudGU9Cnh0KHEpO3ZhciBwPTNEdGhpcy5tb2RlbC5nZXRfbWV0YWRhdGEo
ImNvbHVtbl90eXBlcyIpO2lmKHIhPTNEPTNEdW5kZWZpbmVkKT0Ke28uYXR0cigiY29sc3BhbiIs
cikuYWRkQ2xhc3MoInN0cmluZ2FsaWduIil9ZWxzZXtpZihwKXtpZihuPHAubGVuZ3RoKXtpZihw
PQpbbl09M0Q9M0Q9M0Qic3RyInx8cFtuXT0zRD0zRD0zRCJsaXN0Iil7by5hZGRDbGFzcygic3Ry
aW5nYWxpZ24iKX19fX1yZXR1cm49CiBvfSxfcmVuZGVyUm93OmZ1bmN0aW9uKG4pe3ZhciBvPTNE
bi5zcGxpdCgiXHQiKSxxPTNEJCgiPHRyPiIpLHA9M0R0aGlzLm1vZD0KZWwuZ2V0X21ldGFkYXRh
KCJjb2x1bW5zIik7aWYodGhpcy5yb3dfY291bnQlMiE9M0Q9M0QwKXtxLmFkZENsYXNzKCJkYXJr
X3JvPQp3Iil9aWYoby5sZW5ndGg9M0Q9M0Q9M0RwKXtfLmVhY2gobyxmdW5jdGlvbihzLHIpe3Eu
YXBwZW5kKHRoaXMuX3JlbmRlckNlbGw9CihzLHIpKX0sdGhpcyl9ZWxzZXtpZihvLmxlbmd0aD5w
KXtfLmVhY2goby5zbGljZSgwLHAtMSksZnVuY3Rpb24ocyxyKXtxLmFwcD0KZW5kKHRoaXMuX3Jl
bmRlckNlbGwocyxyKSl9LHRoaXMpO3EuYXBwZW5kKHRoaXMuX3JlbmRlckNlbGwoby5zbGljZShw
LTEpLmpvPQppbigiXHQiKSxwLTEpKX1lbHNle2lmKHA+NSYmby5sZW5ndGg9M0Q9M0Q9M0RwLTEp
e18uZWFjaChvLGZ1bmN0aW9uKHMscil7cS49CmFwcGVuZCh0aGlzLl9yZW5kZXJDZWxsKHMscikp
fSx0aGlzKTtxLmFwcGVuZCgkKCI8dGQ+IikpfWVsc2V7cS5hcHBlbmQodGhpcz0KLl9yZW5kZXJD
ZWxsKG4sMCxwKSl9fX10aGlzLnJvd19jb3VudCsrO3JldHVybiBxfSxfcmVuZGVyQ2h1bms6ZnVu
Y3Rpb24obil7PQp2YXIgbz0zRHRoaXMuJGVsLmZpbmQoInRhYmxlIik7Xy5lYWNoKG4uY2tfZGF0
YS5zcGxpdCgiXG4iKSxmdW5jdGlvbihwLHEpe2k9CmYocCE9M0Q9M0QiIil7by5hcHBlbmQodGhp
cy5fcmVuZGVyUm93KHApKX19LHRoaXMpfX0pO3ZhciBnPTNEYS5leHRlbmQoe2luaT0KdGlhbGl6
ZTpmdW5jdGlvbihuKXthLnByb3RvdHlwZS5pbml0aWFsaXplLmNhbGwodGhpcyxuKTtzY3JvbGxf
ZWx0PTNEXy5maW5kPQoodGhpcy4kZWwucGFyZW50cygpLGZ1bmN0aW9uKG8pe3JldHVybiAkKG8p
LmNzcygib3ZlcmZsb3ciKT0zRD0zRD0zRCJhdXRvIn09Cik7aWYoIXNjcm9sbF9lbHQpe3Njcm9s
bF9lbHQ9M0R3aW5kb3d9dGhpcy5zY3JvbGxfZWx0PTNEJChzY3JvbGxfZWx0KX0sc2Nybz0KbGxl
ZF90b19ib3R0b206ZnVuY3Rpb24oKXtyZXR1cm4odGhpcy4kZWwuaGVpZ2h0KCktdGhpcy5zY3Jv
bGxfZWx0LnNjcm9sbFRvPQpwKCktdGhpcy5zY3JvbGxfZWx0LmhlaWdodCgpPD0zRDApfX0pO3Zh
ciBtPTNEYS5leHRlbmQoe2luaXRpYWxpemU6ZnVuY3Rpb249CihuKXthLnByb3RvdHlwZS5pbml0
aWFsaXplLmNhbGwodGhpcyxuKTt0aGlzLnNjcm9sbF9lbHQ9M0R0aGlzLiRlbC5jc3Moe3Bvcz0K
aXRpb246InJlbGF0aXZlIixvdmVyZmxvdzoic2Nyb2xsIixoZWlnaHQ6dGhpcy5vcHRpb25zLmhl
aWdodHx8IjUwMHB4In0pfSxzPQpjcm9sbGVkX3RvX2JvdHRvbTpmdW5jdGlvbigpe3JldHVybiB0
aGlzLiRlbC5zY3JvbGxUb3AoKSt0aGlzLiRlbC5pbm5lckhlaWc9Cmh0KCk+PTNEdGhpcy5lbC5z
Y3JvbGxIZWlnaHR9fSk7dmFyIGQ9M0RCYWNrYm9uZS5WaWV3LmV4dGVuZCh7Y29sOntjaHJvbTpu
dT0KbGwsc3RhcnQ6bnVsbCxlbmQ6bnVsbH0sdXJsX3ZpejpudWxsLGRhdGFzZXRfaWQ6bnVsbCxn
ZW5vbWVfYnVpbGQ6bnVsbCxmaWxlPQpfZXh0Om51bGwsaW5pdGlhbGl6ZTpmdW5jdGlvbihwKXt2
YXIgcz0zRHBhcmVudC5HYWxheHk7aWYocyYmcy5tb2RhbCl7dGhpcy49Cm1vZGFsPTNEcy5tb2Rh
bH1pZihzJiZzLmZyYW1lKXt0aGlzLmZyYW1lPTNEcy5mcmFtZX1pZighdGhpcy5tb2RhbHx8IXRo
aXMuZj0KcmFtZSl7cmV0dXJufXZhciBvPTNEcC5tb2RlbDt2YXIgcj0zRG8uZ2V0KCJtZXRhZGF0
YSIpO2lmKCFvLmdldCgiZmlsZV9leHQiPQopKXtyZXR1cm59dGhpcy5maWxlX2V4dD0zRG8uZ2V0
KCJmaWxlX2V4dCIpO2lmKHRoaXMuZmlsZV9leHQ9M0Q9M0QiYmVkIil7aWY9CihyLmdldCgiY2hy
b21Db2wiKSYmci5nZXQoInN0YXJ0Q29sIikmJnIuZ2V0KCJlbmRDb2wiKSl7dGhpcy5jb2wuY2hy
b209M0RyLj0KZ2V0KCJjaHJvbUNvbCIpLTE7dGhpcy5jb2wuc3RhcnQ9M0RyLmdldCgic3RhcnRD
b2wiKS0xO3RoaXMuY29sLmVuZD0zRHIuZ2V0PQooImVuZENvbCIpLTF9ZWxzZXtjb25zb2xlLmxv
ZygiVGFidWxhckJ1dHRvblRyYWNrc3RlciA6IEJlZC1maWxlIG1ldGFkYXRhIGk9Cm5jb21wbGV0
ZS4iKTtyZXR1cm59fWlmKHRoaXMuZmlsZV9leHQ9M0Q9M0QidmNmIil7ZnVuY3Rpb24gcSh1LHYp
e2Zvcih2YXIgdD0KPTNEMDt0PHYubGVuZ3RoO3QrKyl7aWYodlt0XS5tYXRjaCh1KSl7cmV0dXJu
IHR9fXJldHVybiAtMX10aGlzLmNvbC5jaHJvbT0KPTNEcSgiQ2hyb20iLHIuZ2V0KCJjb2x1bW5f
bmFtZXMiKSk7dGhpcy5jb2wuc3RhcnQ9M0RxKCJQb3MiLHIuZ2V0KCJjb2x1bW5fPQpuYW1lcyIp
KTt0aGlzLmNvbC5lbmQ9M0RudWxsO2lmKHRoaXMuY29sLmNocm9tPTNEPTNELTF8fHRoaXMuY29s
LnN0YXJ0PTNEPQo9M0QtMSl7Y29uc29sZS5sb2coIlRhYnVsYXJCdXR0b25UcmFja3N0ZXIgOiBW
Q0YtZmlsZSBtZXRhZGF0YSBpbmNvbXBsZXRlLiI9Cik7cmV0dXJufX1pZih0aGlzLmNvbC5jaHJv
bT0zRD0zRD0zRHVuZGVmaW5lZCl7cmV0dXJufWlmKG8uaWQpe3RoaXMuZGF0YXNldD0KX2lkPTNE
by5pZH1lbHNle2NvbnNvbGUubG9nKCJUYWJ1bGFyQnV0dG9uVHJhY2tzdGVyIDogRGF0YXNldCBp
ZGVudGlmaWNhdGlvPQpuIGlzIG1pc3NpbmcuIik7cmV0dXJufWlmKG8uZ2V0KCJ1cmxfdml6Iikp
e3RoaXMudXJsX3Zpej0zRG8uZ2V0KCJ1cmxfdml6Iik9Cn1lbHNle2NvbnNvbGUubG9nKCJUYWJ1
bGFyQnV0dG9uVHJhY2tzdGVyIDogVXJsIGZvciB2aXN1YWxpemF0aW9uIGNvbnRyb2xsZT0KciBp
cyBtaXNzaW5nLiIpO3JldHVybn1pZihvLmdldCgiZ2Vub21lX2J1aWxkIikpe3RoaXMuZ2Vub21l
X2J1aWxkPTNEby5nZXQoPQoiZ2Vub21lX2J1aWxkIil9dmFyIG49M0RuZXcgZi5JY29uQnV0dG9u
Vmlldyh7bW9kZWw6bmV3IGYuSWNvbkJ1dHRvbih7dGl0bGU9CjoiVmlzdWFsaXplIixpY29uX2Ns
YXNzOiJjaGFydF9jdXJ2ZSIsaWQ6ImJ0bl92aXoifSl9KTt0aGlzLnNldEVsZW1lbnQocC4kZT0K
bCk7dGhpcy4kZWwuYXBwZW5kKG4ucmVuZGVyKCkuJGVsKTt0aGlzLmhpZGUoKX0sZXZlbnRzOnsi
bW91c2VvdmVyIHRyIjoic2hvPQp3Iixtb3VzZWxlYXZlOiJoaWRlIn0sc2hvdzpmdW5jdGlvbihz
KXtmdW5jdGlvbiByKHgpe3JldHVybiAhaXNOYU4ocGFyc2VGbG89CmF0KHgpKSYmaXNGaW5pdGUo
eCl9aWYodGhpcy5jb2wuY2hyb209M0Q9M0Q9M0RudWxsKXtyZXR1cm59dmFyIHc9M0QkKHMudGFy
Zz0KZXQpLnBhcmVudCgpO3ZhciB0PTNEdy5jaGlsZHJlbigpLmVxKHRoaXMuY29sLmNocm9tKS5o
dG1sKCk7dmFyIG49M0R3LmNoaWxkPQpyZW4oKS5lcSh0aGlzLmNvbC5zdGFydCkuaHRtbCgpO3Zh
ciBwPTNEdGhpcy5jb2wuZW5kP3cuY2hpbGRyZW4oKS5lcSh0aGlzLmM9Cm9sLmVuZCkuaHRtbCgp
Om47aWYoIXQubWF0Y2goIl4jIikmJnQhPTNEPTNEIiImJnIobikpe3ZhciB2PTNEe2RhdGFzZXRf
aWQ6dD0KaGlzLmRhdGFzZXRfaWQsZ2VuZV9yZWdpb246dCsiOiIrbisiLSIrcH07dmFyIHE9M0R3
Lm9mZnNldCgpO3ZhciBvPTNEcS5sZWZ0PQotMTA7dmFyIHU9M0RxLnRvcC0kKHdpbmRvdykuc2Ny
b2xsVG9wKCkrMzskKCIjYnRuX3ZpeiIpLmNzcyh7cG9zaXRpb246ImZpeGU9CmQiLHRvcDp1KyJw
eCIsbGVmdDpvKyJweCJ9KTskKCIjYnRuX3ZpeiIpLm9mZigiY2xpY2siKTskKCIjYnRuX3ZpeiIp
LmNsaWNrKD0KdGhpcy5jcmVhdGVfdHJhY2tzdGVyX2FjdGlvbih0aGlzLnVybF92aXosdix0aGlz
Lmdlbm9tZV9idWlsZCkpOyQoIiNidG5fdml6PQoiKS5zaG93KCl9ZWxzZXskKCIjYnRuX3ZpeiIp
LmhpZGUoKX19LGhpZGU6ZnVuY3Rpb24oKXt0aGlzLiRlbC5maW5kKCIjYnRuX3Y9Cml6IikuaGlk
ZSgpfSxjcmVhdGVfdHJhY2tzdGVyX2FjdGlvbjpmdW5jdGlvbihuLHEscCl7dmFyIG89M0R0aGlz
O3JldHVybiBmdT0KbmN0aW9uKCl7dmFyIHI9M0R7fTtpZihwKXtyWyJmLWRia2V5Il09M0RwfSQu
YWpheCh7dXJsOm4rIi9saXN0X3RyYWNrcz8iKyQuPQpwYXJhbShyKSxkYXRhVHlwZToiaHRtbCIs
ZXJyb3I6ZnVuY3Rpb24oKXtvLm1vZGFsLnNob3coe3RpdGxlOiJTb21ldGhpbmcgd2U9Cm50IHdy
b25nISIsYm9keToiVW5mb3J0dW5hdGVseSB3ZSBjb3VsZCBub3QgYWRkIHRoaXMgZGF0YXNldCB0
byB0aGUgdHJhY2sgYj0Kcm93c2VyLiBQbGVhc2UgdHJ5IGFnYWluIG9yIGNvbnRhY3QgdXMuIixi
dXR0b25zOntDYW5jZWw6ZnVuY3Rpb24oKXtvLm1vZGFsPQouaGlkZSgpfX19KX0sc3VjY2Vzczpm
dW5jdGlvbihzKXtvLm1vZGFsLnNob3coe3RpdGxlOiJWaWV3IERhdGEgaW4gYSBOZXcgb3I9CiBT
YXZlZCBWaXN1YWxpemF0aW9uIixidXR0b25zOntDYW5jZWw6ZnVuY3Rpb24oKXtvLm1vZGFsLmhp
ZGUoKX0sIlZpZXcgaW4gcz0KYXZlZCB2aXN1YWxpemF0aW9uIjpmdW5jdGlvbigpe28ubW9kYWwu
c2hvdyh7dGl0bGU6IkFkZCBEYXRhIHRvIFNhdmVkIFZpc3VhPQpsaXphdGlvbiIsYm9keTpzLGJ1
dHRvbnM6e0NhbmNlbDpmdW5jdGlvbigpe28ubW9kYWwuaGlkZSgpfSwiQWRkIHRvIHZpc3VhbGk9
CnphdGlvbiI6ZnVuY3Rpb24oKXtvLm1vZGFsLmhpZGUoKTtvLm1vZGFsLiRlbC5maW5kKCJpbnB1
dFtuYW1lPTNEaWRdOmNoZWNrZT0KZCIpLmVhY2goZnVuY3Rpb24oKXt2YXIgdD0zRCQodGhpcyku
dmFsKCk7cS5pZD0zRHQ7by5mcmFtZS5hZGQoe3RpdGxlOiJUcmFjPQprc3RlciIsdHlwZToidXJs
Iixjb250ZW50Om4rIi90cmFja3N0ZXI/IiskLnBhcmFtKHEpfSl9KX19fSl9LCJWaWV3IGluIG5l
dyA9CnZpc3VhbGl6YXRpb24iOmZ1bmN0aW9uKCl7by5tb2RhbC5oaWRlKCk7by5mcmFtZS5hZGQo
e3RpdGxlOiJUcmFja3N0ZXIiLHR5cD0KZToidXJsIixjb250ZW50Om4rIi90cmFja3N0ZXI/Iisk
LnBhcmFtKHEpfSl9fX0pfX0pO3JldHVybiBmYWxzZX19fSk7dmFyIGw9Cj0zRGZ1bmN0aW9uKHEs
byxyLG4pe3ZhciBwPTNEbmV3IG8oe21vZGVsOm5ldyBxKHIpfSk7cC5yZW5kZXIoKTtpZihuKXtu
LmFwcD0KZW5kKHAuJGVsKX1yZXR1cm4gcH07dmFyIGM9M0RmdW5jdGlvbihwKXtpZighcC5tb2Rl
bCl7cC5tb2RlbD0zRG5ldyBpKHAuZGF0PQphc2V0X2NvbmZpZyl9dmFyIG89M0RwLnBhcmVudF9l
bHQ7dmFyIHE9M0RwLmVtYmVkZGVkO2RlbGV0ZSBwLmVtYmVkZGVkO2RlbGU9CnRlIHAucGFyZW50
X2VsdDtkZWxldGUgcC5kYXRhc2V0X2NvbmZpZzt2YXIgbj0zRChxP25ldyBtKHApOm5ldyBnKHAp
KTtuLnJlbj0KZGVyKCk7aWYobyl7by5hcHBlbmQobi4kZWwpO24uZXhwYW5kX3RvX2NvbnRhaW5l
cigpfXJldHVybiBufTtyZXR1cm57RGF0YXNlPQp0OmIsVGFidWxhckRhdGFzZXQ6aSxEYXRhc2V0
Q29sbGVjdGlvbjplLFRhYnVsYXJEYXRhc2V0Q2h1bmtlZFZpZXc6YSxjcmVhdGU9ClRhYnVsYXJE
YXRhc2V0Q2h1bmtlZFZpZXc6Y319KTsKXCBObyBuZXdsaW5lIGF0IGVuZCBvZiBmaWxlCgpkaWZm
IC1yIDhlYzY5MDUyNjAwYTJjOTE1Zjg4ZDMxODU4ODRhMTg1ZTgyMDg2ZDEgLXIgMzY0NTdjYzFl
YzI2YzQ0MGU3YTk5NmM9CmU1MWViODdlZDdjNThjYzRkIHN0YXRpYy9zdHlsZS9ibHVlL2Jhc2Uu
Y3NzCi0tLSBhL3N0YXRpYy9zdHlsZS9ibHVlL2Jhc2UuY3NzCisrKyBiL3N0YXRpYy9zdHlsZS9i
bHVlL2Jhc2UuY3NzCkBAIC0yMjM0LDYgKzIyMzQsOCBAQAogI2xvYWRpbmdfaW5kaWNhdG9ye3Bv
c2l0aW9uOmZpeGVkO3JpZ2h0OjEwcHg7dG9wOjEwcHg7aGVpZ2h0OjMycHg7d2lkdGg6MzI9CnB4
O2Rpc3BsYXk6bm9uZTtiYWNrZ3JvdW5kOnVybChsYXJnZXNwaW5uZXIuZ2lmKX0KICNjb250ZW50
X3RhYmxlIHRke3RleHQtYWxpZ246cmlnaHQ7d2hpdGUtc3BhY2U6bm93cmFwO3BhZGRpbmc6MnB4
IDEwcHh9CiAjY29udGVudF90YWJsZSB0ZC5zdHJpbmdhbGlnbnt0ZXh0LWFsaWduOmxlZnR9Cisj
Y29udGVudF90YWJsZSAuZGFya19yb3d7YmFja2dyb3VuZC1jb2xvcjojREREfQorI2NvbnRlbnRf
dGFibGUgdGh7YmFja2dyb3VuZC1jb2xvcjojQUFBfQogLnRvb2xNZW51QW5kVmlldyAudG9vbEZv
cm17ZmxvYXQ6bGVmdDtiYWNrZ3JvdW5kLWNvbG9yOndoaXRlO21hcmdpbjoxMHB4fQogLnRleHQt
YW5kLWF1dG9jb21wbGV0ZS1zZWxlY3R7YmFja2dyb3VuZDpub25lO3Bvc2l0aW9uOnJlbGF0aXZl
O3BhZGRpbmctcmk9CmdodDoxOHB4fS50ZXh0LWFuZC1hdXRvY29tcGxldGUtc2VsZWN0OmFmdGVy
e21hcmdpbi10b3A6NnB4O3Bvc2l0aW9uOmFic29sdT0KdGU7dG9wOjJweDtyaWdodDo2cHg7d2lk
dGg6MTBweDtoZWlnaHQ6MTBweDtkaXNwbGF5OmlubGluZS1ibG9jazt3aWR0aDowO2hlPQppZ2h0
OjA7bWFyZ2luLWxlZnQ6MnB4O3ZlcnRpY2FsLWFsaWduOm1pZGRsZTtib3JkZXItdG9wOjRweCBz
b2xpZCAjMDAwO2JvcmQ9CmVyLXJpZ2h0OjRweCBzb2xpZCB0cmFuc3BhcmVudDtib3JkZXItbGVm
dDo0cHggc29saWQgdHJhbnNwYXJlbnQ7Ym9yZGVyLWJvdD0KdG9tOjAgZG90dGVkO2NvbnRlbnQ6
IiI7ZmlsdGVyOmFscGhhKG9wYWNpdHk9M0Q4MDAwKTtvcGFjaXR5OjgwOy1tcy1maWx0ZXI6PQpw
cm9naWQ6RFhJbWFnZVRyYW5zZm9ybS5NaWNyb3NvZnQuQWxwaGEoT3BhY2l0eT0zRDgwMDApO2Zp
bHRlcjphbHBoYShvcGFjaXQ9Cnk9M0Q4MDAwKX0uYnRuLWRlZmF1bHQgLnRleHQtYW5kLWF1dG9j
b21wbGV0ZS1zZWxlY3Q6YWZ0ZXJ7Ym9yZGVyLXRvcC1jb2xvcj0KOiMzMzN9CiAuYnRuLXByaW1h
cnkgLnRleHQtYW5kLWF1dG9jb21wbGV0ZS1zZWxlY3Q6YWZ0ZXIsLmJ0bi1zdWNjZXNzIC50ZXh0
LWFuZC1hdT0KdG9jb21wbGV0ZS1zZWxlY3Q6YWZ0ZXIsLmJ0bi13YXJuaW5nIC50ZXh0LWFuZC1h
dXRvY29tcGxldGUtc2VsZWN0OmFmdGVyLC5iPQp0bi1kYW5nZXIgLnRleHQtYW5kLWF1dG9jb21w
bGV0ZS1zZWxlY3Q6YWZ0ZXIsLmJ0bi1pbmZvIC50ZXh0LWFuZC1hdXRvY29tcGw9CmV0ZS1zZWxl
Y3Q6YWZ0ZXJ7Ym9yZGVyLXRvcC1jb2xvcjojZmZmfQoKZGlmZiAtciA4ZWM2OTA1MjYwMGEyYzkx
NWY4OGQzMTg1ODg0YTE4NWU4MjA4NmQxIC1yIDM2NDU3Y2MxZWMyNmM0NDBlN2E5OTZjPQplNTFl
Yjg3ZWQ3YzU4Y2M0ZCBzdGF0aWMvc3R5bGUvc3JjL2xlc3MvYmFzZS5sZXNzCi0tLSBhL3N0YXRp
Yy9zdHlsZS9zcmMvbGVzcy9iYXNlLmxlc3MKKysrIGIvc3RhdGljL3N0eWxlL3NyYy9sZXNzL2Jh
c2UubGVzcwpAQCAtMTU1Myw2ICsxNTUzLDE0IEBACiAgICAgdGV4dC1hbGlnbjpsZWZ0OwogfQo9
MjAKKyNjb250ZW50X3RhYmxlIC5kYXJrX3Jvd3sKKyAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjRERE
OworfQorCisjY29udGVudF90YWJsZSB0aHsKKyAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjQUFBOwor
fQorCiAvLyA9M0Q9M0Q9M0Q9M0QgSW50ZWdyYXRlZCB0b29sIGZvcm0gc3R5bGVzCj0yMAogLnRv
b2xNZW51QW5kVmlldyAudG9vbEZvcm0KClJlcG9zaXRvcnkgVVJMOiBodHRwczovL2JpdGJ1Y2tl
dC5vcmcvZ2FsYXh5L2dhbGF4eS1jZW50cmFsLwoKLS0KClRoaXMgaXMgYSBjb21taXQgbm90aWZp
Y2F0aW9uIGZyb20gYml0YnVja2V0Lm9yZy4gWW91IGFyZSByZWNlaXZpbmcKdGhpcyBiZWNhdXNl
IHlvdSBoYXZlIHRoZSBzZXJ2aWNlIGVuYWJsZWQsIGFkZHJlc3NpbmcgdGhlIHJlY2lwaWVudCBv
Zgp0aGlzIGVtYWlsLgo
1
0
commit/galaxy-central: dannon: Organize and deduplicate imports, pep8 in lib/galaxy/config.py
by commits-noreply@bitbucket.org 15 Dec '14
by commits-noreply@bitbucket.org 15 Dec '14
15 Dec '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/7e68aa3d8d16/
Changeset: 7e68aa3d8d16
User: dannon
Date: 2014-12-15 13:28:21+00:00
Summary: Organize and deduplicate imports, pep8 in lib/galaxy/config.py
Affected #: 1 file
diff -r 5223de4eacea84dc2f39ec1ecec33ce45fa84739 -r 7e68aa3d8d161ff2a0526101aedebc026b4d71cc lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -4,23 +4,22 @@
# absolute_import needed for tool_shed package.
from __future__ import absolute_import
+import ConfigParser
+import logging
+import logging.config
import os
import re
import socket
import string
import sys
import tempfile
-import logging
-import logging.config
-import ConfigParser
from datetime import timedelta
+from galaxy import eggs
+from galaxy.util import listify
+from galaxy.util import string_as_bool
+from galaxy.util.dbkeys import GenomeBuilds
from galaxy.web.formatting import expand_pretty_datetime_format
-from galaxy.util import string_as_bool
-from galaxy.util import listify
-from galaxy.util.dbkeys import GenomeBuilds
-from galaxy import eggs
-import ConfigParser
log = logging.getLogger( __name__ )
@@ -442,23 +441,23 @@
Backwards compatibility for config files moved to the config/ dir.
"""
defaults = dict(
- data_manager_config_file = [ 'config/data_manager_conf.xml', 'data_manager_conf.xml', 'config/data_manager_conf.xml.sample' ],
- datatypes_config_file = [ 'config/datatypes_conf.xml', 'datatypes_conf.xml', 'config/datatypes_conf.xml.sample' ],
- external_service_type_config_file = [ 'config/external_service_types_conf.xml', 'external_service_types_conf.xml', 'config/external_service_types_conf.xml.sample' ],
- job_config_file = [ 'config/job_conf.xml', 'job_conf.xml' ],
- job_metrics_config_file = [ 'config/job_metrics_conf.xml', 'job_metrics_conf.xml' ],
- dependency_resolvers_config_file = [ 'config/dependency_resolvers_conf.xml', 'dependency_resolvers_conf.xml' ],
- job_resource_params_file = [ 'config/job_resource_params_conf.xml', 'job_resource_params_conf.xml' ],
- migrated_tools_config = [ 'migrated_tools_conf.xml', 'config/migrated_tools_conf.xml' ],
- object_store_config_file = [ 'config/object_store_conf.xml', 'object_store_conf.xml' ],
- openid_config_file = [ 'config/openid_conf.xml', 'openid_conf.xml', 'config/openid_conf.xml.sample' ],
- shed_data_manager_config_file = [ 'shed_data_manager_conf.xml', 'config/shed_data_manager_conf.xml' ],
- shed_tool_data_table_config = [ 'shed_tool_data_table_conf.xml', 'config/shed_tool_data_table_conf.xml' ],
- tool_sheds_config_file = [ 'config/tool_sheds_conf.xml', 'tool_sheds_conf.xml', 'config/tool_sheds_conf.xml.sample' ],
+ data_manager_config_file=[ 'config/data_manager_conf.xml', 'data_manager_conf.xml', 'config/data_manager_conf.xml.sample' ],
+ datatypes_config_file=[ 'config/datatypes_conf.xml', 'datatypes_conf.xml', 'config/datatypes_conf.xml.sample' ],
+ external_service_type_config_file=[ 'config/external_service_types_conf.xml', 'external_service_types_conf.xml', 'config/external_service_types_conf.xml.sample' ],
+ job_config_file=[ 'config/job_conf.xml', 'job_conf.xml' ],
+ job_metrics_config_file=[ 'config/job_metrics_conf.xml', 'job_metrics_conf.xml' ],
+ dependency_resolvers_config_file=[ 'config/dependency_resolvers_conf.xml', 'dependency_resolvers_conf.xml' ],
+ job_resource_params_file=[ 'config/job_resource_params_conf.xml', 'job_resource_params_conf.xml' ],
+ migrated_tools_config=[ 'migrated_tools_conf.xml', 'config/migrated_tools_conf.xml' ],
+ object_store_config_file=[ 'config/object_store_conf.xml', 'object_store_conf.xml' ],
+ openid_config_file=[ 'config/openid_conf.xml', 'openid_conf.xml', 'config/openid_conf.xml.sample' ],
+ shed_data_manager_config_file=[ 'shed_data_manager_conf.xml', 'config/shed_data_manager_conf.xml' ],
+ shed_tool_data_table_config=[ 'shed_tool_data_table_conf.xml', 'config/shed_tool_data_table_conf.xml' ],
+ tool_sheds_config_file=[ 'config/tool_sheds_conf.xml', 'tool_sheds_conf.xml', 'config/tool_sheds_conf.xml.sample' ],
)
listify_defaults = dict(
- tool_data_table_config_path = [ 'config/tool_data_table_conf.xml', 'tool_data_table_conf.xml', 'config/tool_data_table_conf.xml.sample' ],
+ tool_data_table_config_path=[ 'config/tool_data_table_conf.xml', 'tool_data_table_conf.xml', 'config/tool_data_table_conf.xml.sample' ],
# rationale:
# [0]: user has explicitly created config/tool_conf.xml but did not
# move their existing shed_tool_conf.xml, don't use
@@ -469,7 +468,7 @@
# [0], probably moved their shed_tool_conf.xml as well
# [2]: user has done nothing, use the old files
# [3]: fresh install
- tool_config_file = [ 'config/tool_conf.xml,shed_tool_conf.xml', 'config/tool_conf.xml,config/shed_tool_conf.xml', 'tool_conf.xml,shed_tool_conf.xml', 'config/tool_conf.xml.sample,config/shed_tool_conf.xml' ]
+ tool_config_file=[ 'config/tool_conf.xml,shed_tool_conf.xml', 'config/tool_conf.xml,config/shed_tool_conf.xml', 'tool_conf.xml,shed_tool_conf.xml', 'config/tool_conf.xml.sample,config/shed_tool_conf.xml' ]
)
for var, defaults in defaults.items():
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/5223de4eacea/
Changeset: 5223de4eacea
User: dannon
Date: 2014-12-15 13:25:15+00:00
Summary: Merge.
Affected #: 1 file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/6704c0ee9fec/
Changeset: 6704c0ee9fec
Branch: next-stable
User: dannon
Date: 2014-12-15 13:23:47+00:00
Summary: Bugfix for openid configuration locations, pointed out by Jelle in https://trello.com/c/AwZwAx4l/2289-lib-galaxy-config-py-missing-comma-on-op…
Affected #: 1 file
diff -r 7a027f285f3b441f8ff1fe48dfc5841d8bc41bfa -r 6704c0ee9fec6b0624d739669cb7c66ea08d480a lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -445,7 +445,7 @@
job_resource_params_file = [ 'config/job_resource_params_conf.xml', 'job_resource_params_conf.xml' ],
migrated_tools_config = [ 'migrated_tools_conf.xml', 'config/migrated_tools_conf.xml' ],
object_store_config_file = [ 'config/object_store_conf.xml', 'object_store_conf.xml' ],
- openid_config_file = [ 'config/openid_conf.xml', 'openid_conf.xml' 'config/openid_conf.xml.sample' ],
+ openid_config_file = [ 'config/openid_conf.xml', 'openid_conf.xml', 'config/openid_conf.xml.sample' ],
shed_data_manager_config_file = [ 'shed_data_manager_conf.xml', 'config/shed_data_manager_conf.xml' ],
shed_tool_data_table_config = [ 'shed_tool_data_table_conf.xml', 'config/shed_tool_data_table_conf.xml' ],
tool_sheds_config_file = [ 'config/tool_sheds_conf.xml', 'tool_sheds_conf.xml', 'config/tool_sheds_conf.xml.sample' ],
https://bitbucket.org/galaxy/galaxy-central/commits/6fee59d63b7e/
Changeset: 6fee59d63b7e
User: dannon
Date: 2014-12-15 13:23:47+00:00
Summary: Bugfix for openid configuration locations, pointed out by Jelle in https://trello.com/c/AwZwAx4l/2289-lib-galaxy-config-py-missing-comma-on-op…
Affected #: 1 file
diff -r ad28e42a8af34c81f0ebcbe6f3baef9bdcf80ae8 -r 6fee59d63b7e042851b2a63e4d66ad6dde630e3b lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -451,7 +451,7 @@
job_resource_params_file = [ 'config/job_resource_params_conf.xml', 'job_resource_params_conf.xml' ],
migrated_tools_config = [ 'migrated_tools_conf.xml', 'config/migrated_tools_conf.xml' ],
object_store_config_file = [ 'config/object_store_conf.xml', 'object_store_conf.xml' ],
- openid_config_file = [ 'config/openid_conf.xml', 'openid_conf.xml' 'config/openid_conf.xml.sample' ],
+ openid_config_file = [ 'config/openid_conf.xml', 'openid_conf.xml', 'config/openid_conf.xml.sample' ],
shed_data_manager_config_file = [ 'shed_data_manager_conf.xml', 'config/shed_data_manager_conf.xml' ],
shed_tool_data_table_config = [ 'shed_tool_data_table_conf.xml', 'config/shed_tool_data_table_conf.xml' ],
tool_sheds_config_file = [ 'config/tool_sheds_conf.xml', 'tool_sheds_conf.xml', 'config/tool_sheds_conf.xml.sample' ],
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: dannon: Merged in jjohnson/galaxy-central-7 (pull request #609)
by commits-noreply@bitbucket.org 15 Dec '14
by commits-noreply@bitbucket.org 15 Dec '14
15 Dec '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/ad28e42a8af3/
Changeset: ad28e42a8af3
User: dannon
Date: 2014-12-15 12:43:42+00:00
Summary: Merged in jjohnson/galaxy-central-7 (pull request #609)
Add dataproviders for SQlite datatype
Affected #: 2 files
diff -r ecac8a8679612d419347dcbedc398319418b077e -r ad28e42a8af34c81f0ebcbe6f3baef9bdcf80ae8 lib/galaxy/datatypes/binary.py
--- a/lib/galaxy/datatypes/binary.py
+++ b/lib/galaxy/datatypes/binary.py
@@ -620,6 +620,16 @@
dataset_source = dataproviders.dataset.DatasetDataProvider( dataset )
return dataproviders.dataset.SQliteDataProvider( dataset_source, **settings )
+ @dataproviders.decorators.dataprovider_factory( 'sqlite-table', dataproviders.dataset.SQliteDataTableProvider.settings )
+ def sqlite_datatableprovider( self, dataset, **settings ):
+ dataset_source = dataproviders.dataset.DatasetDataProvider( dataset )
+ return dataproviders.dataset.SQliteDataTableProvider( dataset_source, **settings )
+
+ @dataproviders.decorators.dataprovider_factory( 'sqlite-dict', dataproviders.dataset.SQliteDataDictProvider.settings )
+ def sqlite_datadictprovider( self, dataset, **settings ):
+ dataset_source = dataproviders.dataset.DatasetDataProvider( dataset )
+ return dataproviders.dataset.SQliteDataDictProvider( dataset_source, **settings )
+
Binary.register_sniffable_binary_format("sqlite", "sqlite", SQlite)
diff -r ecac8a8679612d419347dcbedc398319418b077e -r ad28e42a8af34c81f0ebcbe6f3baef9bdcf80ae8 lib/galaxy/datatypes/dataproviders/dataset.py
--- a/lib/galaxy/datatypes/dataproviders/dataset.py
+++ b/lib/galaxy/datatypes/dataproviders/dataset.py
@@ -13,6 +13,7 @@
import external
from galaxy.util import sqlite
import re
+import sys
from galaxy import eggs
eggs.require( 'bx-python' )
@@ -726,3 +727,55 @@
else:
yield
+class SQliteDataTableProvider( base.DataProvider ):
+ """
+ Data provider that uses a sqlite database file as its source.
+ Allows any query to be run and returns the resulting rows as arrays of arrays
+ """
+ settings = {
+ 'query': 'str',
+ 'headers': 'bool',
+ 'limit': 'int'
+ }
+
+ def __init__( self, source, query=None, headers=False, limit=sys.maxint, **kwargs ):
+ self.query = query
+ self.headers = headers
+ self.limit = limit
+ self.connection = sqlite.connect(source.dataset.file_name)
+ super( SQliteDataTableProvider, self ).__init__( source, **kwargs )
+
+ def __iter__( self ):
+ if (self.query is not None) and sqlite.is_read_only_query(self.query):
+ cur = self.connection.cursor()
+ results = cur.execute(self.query)
+ if self.headers:
+ yield [col[0] for col in cur.description]
+ for i,row in enumerate(results):
+ if i >= self.limit:
+ break
+ yield [val for val in row]
+ else:
+ yield
+
+class SQliteDataDictProvider( base.DataProvider ):
+ """
+ Data provider that uses a sqlite database file as its source.
+ Allows any query to be run and returns the resulting rows as arrays of dicts
+ """
+ settings = {
+ 'query': 'str'
+ }
+
+ def __init__( self, source, query=None, **kwargs ):
+ self.query = query
+ self.connection = sqlite.connect(source.dataset.file_name)
+ super( SQliteDataDictProvider, self ).__init__( source, **kwargs )
+
+ def __iter__( self ):
+ if (self.query is not None) and sqlite.is_read_only_query(self.query):
+ cur = self.connection.cursor()
+ for row in cur.execute(self.query):
+ yield [dict((cur.description[i][0], value) for i, value in enumerate(row))]
+ else:
+ yield
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
3 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/1e6035e53de5/
Changeset: 1e6035e53de5
User: jjohnson
Date: 2014-12-12 19:35:40+00:00
Summary: Add SQlite dataproviders: SQliteDataTableProvider - Allows any query to be run and returns the resulting rows as arrays of arrays, SQliteDataDictProvider - Allows any query to be run and returns the resulting rows as arrays of dicts
Affected #: 1 file
diff -r efd0286e7629289c4c583898ebecb32646834c64 -r 1e6035e53de58ce4fe1687ab74512da711d99989 lib/galaxy/datatypes/dataproviders/dataset.py
--- a/lib/galaxy/datatypes/dataproviders/dataset.py
+++ b/lib/galaxy/datatypes/dataproviders/dataset.py
@@ -13,6 +13,7 @@
import external
from galaxy.util import sqlite
import re
+import sys
from galaxy import eggs
eggs.require( 'bx-python' )
@@ -726,3 +727,55 @@
else:
yield
+class SQliteDataTableProvider( base.DataProvider ):
+ """
+ Data provider that uses a sqlite database file as its source.
+ Allows any query to be run and returns the resulting rows as arrays of arrays
+ """
+ settings = {
+ 'query': 'str',
+ 'headers': 'bool',
+ 'limit': 'int'
+ }
+
+ def __init__( self, source, query=None, headers=False, limit=sys.maxint, **kwargs ):
+ self.query = query
+ self.headers = headers
+ self.limit = limit
+ self.connection = sqlite.connect(source.dataset.file_name)
+ super( SQliteDataTableProvider, self ).__init__( source, **kwargs )
+
+ def __iter__( self ):
+ if (self.query is not None) and sqlite.is_read_only_query(self.query):
+ cur = self.connection.cursor()
+ results = cur.execute(self.query)
+ if self.headers:
+ yield [col[0] for col in cur.description]
+ for i,row in enumerate(results):
+ if i >= self.limit:
+ break
+ yield [val for val in row]
+ else:
+ yield
+
+class SQliteDataDictProvider( base.DataProvider ):
+ """
+ Data provider that uses a sqlite database file as its source.
+ Allows any query to be run and returns the resulting rows as arrays of dicts
+ """
+ settings = {
+ 'query': 'str'
+ }
+
+ def __init__( self, source, query=None, **kwargs ):
+ self.query = query
+ self.connection = sqlite.connect(source.dataset.file_name)
+ super( SQliteDataDictProvider, self ).__init__( source, **kwargs )
+
+ def __iter__( self ):
+ if (self.query is not None) and sqlite.is_read_only_query(self.query):
+ cur = self.connection.cursor()
+ for row in cur.execute(self.query):
+ yield [dict((cur.description[i][0], value) for i, value in enumerate(row))]
+ else:
+ yield
https://bitbucket.org/galaxy/galaxy-central/commits/c207054ea196/
Changeset: c207054ea196
User: jjohnson
Date: 2014-12-12 19:41:35+00:00
Summary: Add dataproviders to the SQlite dataype
Affected #: 1 file
diff -r 1e6035e53de58ce4fe1687ab74512da711d99989 -r c207054ea1966bc9d4640a6215192414e8071739 lib/galaxy/datatypes/binary.py
--- a/lib/galaxy/datatypes/binary.py
+++ b/lib/galaxy/datatypes/binary.py
@@ -620,6 +620,16 @@
dataset_source = dataproviders.dataset.DatasetDataProvider( dataset )
return dataproviders.dataset.SQliteDataProvider( dataset_source, **settings )
+ @dataproviders.decorators.dataprovider_factory( 'sqlite-table', dataproviders.dataset.SQliteDataTableProvider.settings )
+ def sqlite_datatableprovider( self, dataset, **settings ):
+ dataset_source = dataproviders.dataset.DatasetDataProvider( dataset )
+ return dataproviders.dataset.SQliteDataTableProvider( dataset_source, **settings )
+
+ @dataproviders.decorators.dataprovider_factory( 'sqlite-dict', dataproviders.dataset.SQliteDataDictProvider.settings )
+ def sqlite_datadictprovider( self, dataset, **settings ):
+ dataset_source = dataproviders.dataset.DatasetDataProvider( dataset )
+ return dataproviders.dataset.SQliteDataDictProvider( dataset_source, **settings )
+
Binary.register_sniffable_binary_format("sqlite", "sqlite", SQlite)
https://bitbucket.org/galaxy/galaxy-central/commits/ad28e42a8af3/
Changeset: ad28e42a8af3
User: dannon
Date: 2014-12-15 12:43:42+00:00
Summary: Merged in jjohnson/galaxy-central-7 (pull request #609)
Add dataproviders for SQlite datatype
Affected #: 2 files
diff -r ecac8a8679612d419347dcbedc398319418b077e -r ad28e42a8af34c81f0ebcbe6f3baef9bdcf80ae8 lib/galaxy/datatypes/binary.py
--- a/lib/galaxy/datatypes/binary.py
+++ b/lib/galaxy/datatypes/binary.py
@@ -620,6 +620,16 @@
dataset_source = dataproviders.dataset.DatasetDataProvider( dataset )
return dataproviders.dataset.SQliteDataProvider( dataset_source, **settings )
+ @dataproviders.decorators.dataprovider_factory( 'sqlite-table', dataproviders.dataset.SQliteDataTableProvider.settings )
+ def sqlite_datatableprovider( self, dataset, **settings ):
+ dataset_source = dataproviders.dataset.DatasetDataProvider( dataset )
+ return dataproviders.dataset.SQliteDataTableProvider( dataset_source, **settings )
+
+ @dataproviders.decorators.dataprovider_factory( 'sqlite-dict', dataproviders.dataset.SQliteDataDictProvider.settings )
+ def sqlite_datadictprovider( self, dataset, **settings ):
+ dataset_source = dataproviders.dataset.DatasetDataProvider( dataset )
+ return dataproviders.dataset.SQliteDataDictProvider( dataset_source, **settings )
+
Binary.register_sniffable_binary_format("sqlite", "sqlite", SQlite)
diff -r ecac8a8679612d419347dcbedc398319418b077e -r ad28e42a8af34c81f0ebcbe6f3baef9bdcf80ae8 lib/galaxy/datatypes/dataproviders/dataset.py
--- a/lib/galaxy/datatypes/dataproviders/dataset.py
+++ b/lib/galaxy/datatypes/dataproviders/dataset.py
@@ -13,6 +13,7 @@
import external
from galaxy.util import sqlite
import re
+import sys
from galaxy import eggs
eggs.require( 'bx-python' )
@@ -726,3 +727,55 @@
else:
yield
+class SQliteDataTableProvider( base.DataProvider ):
+ """
+ Data provider that uses a sqlite database file as its source.
+ Allows any query to be run and returns the resulting rows as arrays of arrays
+ """
+ settings = {
+ 'query': 'str',
+ 'headers': 'bool',
+ 'limit': 'int'
+ }
+
+ def __init__( self, source, query=None, headers=False, limit=sys.maxint, **kwargs ):
+ self.query = query
+ self.headers = headers
+ self.limit = limit
+ self.connection = sqlite.connect(source.dataset.file_name)
+ super( SQliteDataTableProvider, self ).__init__( source, **kwargs )
+
+ def __iter__( self ):
+ if (self.query is not None) and sqlite.is_read_only_query(self.query):
+ cur = self.connection.cursor()
+ results = cur.execute(self.query)
+ if self.headers:
+ yield [col[0] for col in cur.description]
+ for i,row in enumerate(results):
+ if i >= self.limit:
+ break
+ yield [val for val in row]
+ else:
+ yield
+
+class SQliteDataDictProvider( base.DataProvider ):
+ """
+ Data provider that uses a sqlite database file as its source.
+ Allows any query to be run and returns the resulting rows as arrays of dicts
+ """
+ settings = {
+ 'query': 'str'
+ }
+
+ def __init__( self, source, query=None, **kwargs ):
+ self.query = query
+ self.connection = sqlite.connect(source.dataset.file_name)
+ super( SQliteDataDictProvider, self ).__init__( source, **kwargs )
+
+ def __iter__( self ):
+ if (self.query is not None) and sqlite.is_read_only_query(self.query):
+ cur = self.connection.cursor()
+ for row in cur.execute(self.query):
+ yield [dict((cur.description[i][0], value) for i, value in enumerate(row))]
+ else:
+ yield
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
7 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/563a28409672/
Changeset: 563a28409672
User: jmchilton
Date: 2014-12-15 05:20:35+00:00
Summary: Allow multiple tool data table config files...
... use this functionality to implement tests for tool data API.
Affected #: 5 files
diff -r 6363c839528ca8bbbbc60c1b3784c788468d9445 -r 563a28409672988f6b30545ba987017e7da2e14c lib/galaxy/tools/data/__init__.py
--- a/lib/galaxy/tools/data/__init__.py
+++ b/lib/galaxy/tools/data/__init__.py
@@ -31,8 +31,10 @@
# at server startup. If tool shed repositories are installed that contain a valid file named tool_data_table_conf.xml.sample, entries
# from that file are inserted into this dict at the time of installation.
self.data_tables = {}
- if config_filename:
- self.load_from_config_file( config_filename, self.tool_data_path, from_shed_config=False )
+ for single_config_filename in util.listify( config_filename ):
+ if not single_config_filename:
+ continue
+ self.load_from_config_file( single_config_filename, self.tool_data_path, from_shed_config=False )
def __getitem__( self, key ):
return self.data_tables.__getitem__( key )
diff -r 6363c839528ca8bbbbc60c1b3784c788468d9445 -r 563a28409672988f6b30545ba987017e7da2e14c scripts/functional_tests.py
--- a/scripts/functional_tests.py
+++ b/scripts/functional_tests.py
@@ -231,7 +231,16 @@
start_server = 'GALAXY_TEST_EXTERNAL' not in os.environ
tool_data_table_config_path = None
if os.path.exists( 'tool_data_table_conf.test.xml' ):
+ # If explicitly defined tables for test, use those.
tool_data_table_config_path = 'tool_data_table_conf.test.xml'
+ else:
+ # ... otherise find whatever Galaxy would use as the default and
+ # the sample data for fucntional tests to that.
+ default_tool_data_config = 'config/tool_data_table_conf.xml.sample'
+ for tool_data_config in ['config/tool_data_table_conf.xml', 'tool_data_table_conf.xml' ]:
+ if os.path.exists( tool_data_config ):
+ default_tool_data_config = tool_data_config
+ tool_data_table_config_path = '%s,test/functional/tool-data/sample_tool_data_tables.xml' % default_tool_data_config
shed_tool_data_table_config = 'config/shed_tool_data_table_conf.xml'
tool_dependency_dir = os.environ.get( 'GALAXY_TOOL_DEPENDENCY_DIR', None )
use_distributed_object_store = os.environ.get( 'GALAXY_USE_DISTRIBUTED_OBJECT_STORE', False )
diff -r 6363c839528ca8bbbbc60c1b3784c788468d9445 -r 563a28409672988f6b30545ba987017e7da2e14c test/api/test_tool_data.py
--- /dev/null
+++ b/test/api/test_tool_data.py
@@ -0,0 +1,30 @@
+""" Tests for the tool data API.
+"""
+from base import api
+
+import operator
+
+
+class ToolDataApiTestCase( api.ApiTestCase ):
+
+ def test_admin_only( self ):
+ index_response = self._get( "tool_data", admin=False )
+ self._assert_status_code_is( index_response, 403 )
+
+ def test_list(self):
+ index_response = self._get( "tool_data", admin=True )
+ self._assert_status_code_is( index_response, 200 )
+ print index_response.content
+ index = index_response.json()
+ assert "testalpha" in map(operator.itemgetter("name"), index)
+
+ def test_show(self):
+ show_response = self._get( "tool_data/testalpha", admin=True )
+ self._assert_status_code_is( show_response, 200 )
+ print show_response.content
+ data_table = show_response.json()
+ assert data_table["columns"] == ["value", "name", "path"]
+ first_entry = data_table["fields"][0]
+ assert first_entry[0] == "data1"
+ assert first_entry[1] == "data1name"
+ assert first_entry[2].endswith("test/functional/tool-data/data1/entry.txt")
diff -r 6363c839528ca8bbbbc60c1b3784c788468d9445 -r 563a28409672988f6b30545ba987017e7da2e14c test/functional/tool-data/sample_tool_data_tables.xml
--- /dev/null
+++ b/test/functional/tool-data/sample_tool_data_tables.xml
@@ -0,0 +1,12 @@
+<tables>
+ <table name="testalpha" comment_char="#">
+ <columns>value, name, path</columns>
+ <file path="${__HERE__}/testalpha.loc" />
+ </table>
+ <!--
+ <table name="testbeta" comment_char="#">
+ <columns>value, foo, bar, path</columns>
+ <file path="${__HERE__}/testbeta.loc" />
+ </table>
+ -->
+</tables>
diff -r 6363c839528ca8bbbbc60c1b3784c788468d9445 -r 563a28409672988f6b30545ba987017e7da2e14c test/functional/tool-data/testalpha.loc
--- /dev/null
+++ b/test/functional/tool-data/testalpha.loc
@@ -0,0 +1,2 @@
+data1 data1name ${__HERE__}/data1/entry.txt
+data2 data2name ${__HERE__}/data2/entry.txt
https://bitbucket.org/galaxy/galaxy-central/commits/8795d0fe13c4/
Changeset: 8795d0fe13c4
User: jmchilton
Date: 2014-12-15 05:20:35+00:00
Summary: PEP-8 fixes for lib/galaxy/tools/data/__init__.py.
Affected #: 1 file
diff -r 563a28409672988f6b30545ba987017e7da2e14c -r 8795d0fe13c45804ea53f4543c71ee7ca6f41a0c lib/galaxy/tools/data/__init__.py
--- a/lib/galaxy/tools/data/__init__.py
+++ b/lib/galaxy/tools/data/__init__.py
@@ -9,9 +9,7 @@
import logging
import os
import os.path
-import shutil
import string
-import tempfile
from galaxy import util
from galaxy.util.odict import odict
@@ -22,6 +20,7 @@
DEFAULT_TABLE_TYPE = 'tabular'
+
class ToolDataTableManager( object ):
"""Manages a collection of tool data tables"""
@@ -81,7 +80,7 @@
log.debug( "Loaded tool data table '%s'", table.name )
else:
log.debug( "Loading another instance of data table '%s', attempting to merge content.", table.name )
- self.data_tables[ table.name ].merge_tool_data_table( table, allow_duplicates=False ) #only merge content, do not persist to disk, do not allow duplicate rows when merging
+ self.data_tables[ table.name ].merge_tool_data_table( table, allow_duplicates=False ) # only merge content, do not persist to disk, do not allow duplicate rows when merging
# FIXME: This does not account for an entry with the same unique build ID, but a different path.
return table_elems
@@ -128,7 +127,7 @@
"""
if not ( new_elems or remove_elems ):
log.debug( 'ToolDataTableManager.to_xml_file called without any elements to add or remove.' )
- return #no changes provided, no need to persist any changes
+ return # no changes provided, no need to persist any changes
if not new_elems:
new_elems = []
if not remove_elems:
@@ -175,7 +174,7 @@
assert table_type in tool_data_table_types, "Unknown data table type '%s'" % type
return tool_data_table_types[ table_type ]( table_elem, tool_data_path, from_shed_config=from_shed_config, filename=filename )
- def __init__( self, config_element, tool_data_path, from_shed_config = False, filename=None ):
+ def __init__( self, config_element, tool_data_path, from_shed_config=False, filename=None ):
self.name = config_element.get( 'name' )
self.comment_char = config_element.get( 'comment_char' )
self.empty_field_value = config_element.get( 'empty_field_value', '' )
@@ -187,7 +186,7 @@
# increment this variable any time a new entry is added, or when the table is totally reloaded
# This value has no external meaning, and does not represent an abstract version of the underlying data
self._loaded_content_version = 1
- self._load_info = ( [ config_element, tool_data_path ], { 'from_shed_config':from_shed_config } )
+ self._load_info = ( [ config_element, tool_data_path ], { 'from_shed_config': from_shed_config } )
self._merged_load_info = []
def _update_version( self, version=None ):
@@ -252,13 +251,13 @@
type_key = 'tabular'
- def __init__( self, config_element, tool_data_path, from_shed_config = False, filename=None ):
+ def __init__( self, config_element, tool_data_path, from_shed_config=False, filename=None ):
super( TabularToolDataTable, self ).__init__( config_element, tool_data_path, from_shed_config, filename)
self.config_element = config_element
self.data = []
self.configure_and_load( config_element, tool_data_path, from_shed_config)
- def configure_and_load( self, config_element, tool_data_path, from_shed_config = False):
+ def configure_and_load( self, config_element, tool_data_path, from_shed_config=False):
"""
Configure and load table from an XML element.
"""
@@ -323,7 +322,6 @@
else:
log.debug( "Filename '%s' already exists in filenames (%s), not adding", filename, self.filenames.keys() )
-
def merge_tool_data_table( self, other_table, allow_duplicates=True, persist=False, persist_on_error=False, entry_source=None, **kwd ):
assert self.columns == other_table.columns, "Merging tabular data tables with non matching columns is not allowed: %s:%s != %s:%s" % ( self.name, self.columns, other_table.name, other_table.columns )
#merge filename info
@@ -350,7 +348,7 @@
for i, field in enumerate( fields ):
field_name = named_colums[i]
if field_name is None:
- field_name = i #check that this is supposed to be 0 based.
+ field_name = i # check that this is supposed to be 0 based.
field_dict[ field_name ] = field
rval.append( field_dict )
return rval
@@ -508,7 +506,7 @@
data_table_fh = open( filename, 'wb' )
if os.stat( filename )[6] != 0:
# ensure last existing line ends with new line
- data_table_fh.seek( -1, 2 ) #last char in file
+ data_table_fh.seek( -1, 2 ) # last char in file
last_char = data_table_fh.read( 1 )
if last_char not in [ '\n', '\r' ]:
data_table_fh.write( '\n' )
@@ -577,7 +575,7 @@
def to_dict(self, view='collection'):
rval = super(TabularToolDataTable, self).to_dict()
if view == 'element':
- rval['columns'] = sorted(self.columns.keys(), key=lambda x:self.columns[x])
+ rval['columns'] = sorted(self.columns.keys(), key=lambda x: self.columns[x])
rval['fields'] = self.get_fields()
return rval
https://bitbucket.org/galaxy/galaxy-central/commits/6d54a7fd1fe9/
Changeset: 6d54a7fd1fe9
User: jmchilton
Date: 2014-12-15 05:20:35+00:00
Summary: Small improvements to tool data API.
Update tool data API to use newer API decorator for better error handling, create some abstractions for reuse down stream, etc...
Affected #: 1 file
diff -r 8795d0fe13c45804ea53f4543c71ee7ca6f41a0c -r 6d54a7fd1fe9edfdf2f5c2fdb1b5ddb7eebdf59a lib/galaxy/webapps/galaxy/api/tool_data.py
--- a/lib/galaxy/webapps/galaxy/api/tool_data.py
+++ b/lib/galaxy/webapps/galaxy/api/tool_data.py
@@ -1,4 +1,6 @@
+from galaxy import exceptions
from galaxy import web
+from galaxy.web import _future_expose_api as expose_api
from galaxy.web.base.controller import BaseAPIController
@@ -8,21 +10,21 @@
"""
@web.require_admin
- @web.expose_api
+ @expose_api
def index( self, trans, **kwds ):
"""
GET /api/tool_data: returns a list tool_data tables::
"""
- return list( a.to_dict() for a in trans.app.tool_data_tables.data_tables.values() )
+ return list( a.to_dict() for a in self._data_tables.values() )
@web.require_admin
- @web.expose_api
+ @expose_api
def show( self, trans, id, **kwds ):
- return trans.app.tool_data_tables.data_tables[id].to_dict(view='element')
+ return self._data_table(id).to_dict(view='element')
@web.require_admin
- @web.expose_api
+ @expose_api
def delete( self, trans, id, **kwd ):
"""
DELETE /api/tool_data/{id}
@@ -61,3 +63,13 @@
return "Invalid data table item ( %s ) specified. Wrong number of columns (%s given, %s required)." % ( str( values ), str(len(split_values)), str(len(data_table.get_column_name_list())))
return data_table.remove_entry(split_values)
+
+ def _data_table( self, id ):
+ try:
+ return self._data_tables[id]
+ except IndexError:
+ raise exceptions.ObjectNotFound("No such data table %s" % id)
+
+ @property
+ def _data_tables( self ):
+ return self.app.tool_data_tables.data_tables
https://bitbucket.org/galaxy/galaxy-central/commits/edbe286564de/
Changeset: edbe286564de
User: jmchilton
Date: 2014-12-15 05:20:35+00:00
Summary: Implement a detailed break down of data table fields via API.
Originally this approach was laid out by Kyle Ellrott in this pull request (https://bitbucket.org/galaxy/galaxy-central/pull-request/531/add-downloads-…) The changes to galaxy.tools.data are entirely his contribution, I only reworked the API and endpoint slightly and did some stylistic fixes and refactoring.
Affected #: 4 files
diff -r 6d54a7fd1fe9edfdf2f5c2fdb1b5ddb7eebdf59a -r edbe286564de14b2d51656c03443b3e4aa99e3ab lib/galaxy/tools/data/__init__.py
--- a/lib/galaxy/tools/data/__init__.py
+++ b/lib/galaxy/tools/data/__init__.py
@@ -9,7 +9,11 @@
import logging
import os
import os.path
+import re
import string
+import hashlib
+
+from glob import glob
from galaxy import util
from galaxy.util.odict import odict
@@ -340,6 +344,13 @@
def get_fields( self ):
return self.data
+ def get_field(self, value):
+ rval = None
+ for i in self.get_named_fields_list():
+ if i['value'] == value:
+ rval = TabularToolDataField(i)
+ return rval
+
def get_named_fields_list( self ):
rval = []
named_colums = self.get_column_name_list()
@@ -580,6 +591,58 @@
return rval
+class TabularToolDataField(Dictifiable, object):
+
+ dict_collection_visible_keys = []
+
+ def __init__(self, data):
+ self.data = data
+
+ def __getitem__(self, key):
+ return self.data[key]
+
+ def get_base_path(self):
+ return os.path.normpath(os.path.abspath( self.data['path'] ))
+
+ def get_base_dir(self):
+ path = self.get_base_path()
+ if not os.path.isdir(path):
+ path = os.path.dirname(path)
+ return path
+
+ def clean_base_dir(self, path):
+ return re.sub( "^" + self.get_base_dir() + r"/*", "", path )
+
+ def get_files(self):
+ return glob( self.get_base_path() + "*" )
+
+ def get_filesize_map(self, rm_base_dir=False):
+ out = {}
+ for path in self.get_files():
+ if rm_base_dir:
+ out[self.clean_base_dir(path)] = os.path.getsize(path)
+ else:
+ out[path] = os.path.getsize(path)
+ return out
+
+ def get_fingerprint(self):
+ sha1 = hashlib.sha1()
+ fmap = self.get_filesize_map(True)
+ for k in sorted(fmap.keys()):
+ sha1.update(k)
+ sha1.update(str(fmap[k]))
+ return sha1.hexdigest()
+
+ def to_dict(self):
+ rval = super(TabularToolDataField, self).to_dict()
+ rval['name'] = self.data['value']
+ rval['fields'] = self.data
+ rval['base_dir'] = self.get_base_dir(),
+ rval['files'] = self.get_filesize_map(True)
+ rval['fingerprint'] = self.get_fingerprint()
+ return rval
+
+
def expand_here_template(content, here=None):
if here and content:
content = string.Template(content).safe_substitute( { "__HERE__": here })
diff -r 6d54a7fd1fe9edfdf2f5c2fdb1b5ddb7eebdf59a -r edbe286564de14b2d51656c03443b3e4aa99e3ab lib/galaxy/webapps/galaxy/api/tool_data.py
--- a/lib/galaxy/webapps/galaxy/api/tool_data.py
+++ b/lib/galaxy/webapps/galaxy/api/tool_data.py
@@ -64,6 +64,22 @@
return data_table.remove_entry(split_values)
+ @web.require_admin
+ @expose_api
+ def show_field( self, trans, id, value, **kwds ):
+ """
+ GET /api/tool_data/<id>/fields/<value>
+
+ Get information about a partiular field in a tool_data table
+ """
+ return self._data_table_field( id, value ).to_dict()
+
+ def _data_table_field( self, id, value ):
+ out = self._data_table(id).get_field(value)
+ if out is None:
+ raise exceptions.ObjectNotFound("No such field %s in data table %s." % (value, id))
+ return out
+
def _data_table( self, id ):
try:
return self._data_tables[id]
diff -r 6d54a7fd1fe9edfdf2f5c2fdb1b5ddb7eebdf59a -r edbe286564de14b2d51656c03443b3e4aa99e3ab lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -171,6 +171,7 @@
webapp.mapper.resource( 'dataset', 'datasets', path_prefix='/api' )
webapp.mapper.resource( 'tool_data', 'tool_data', path_prefix='/api' )
+ webapp.mapper.connect( '/api/tool_data/{id:.+?}/fields/{value:.+?}', action='show_field', controller="tool_data" )
webapp.mapper.resource( 'dataset_collection', 'dataset_collections', path_prefix='/api/')
webapp.mapper.resource( 'sample', 'samples', path_prefix='/api' )
webapp.mapper.resource( 'request', 'requests', path_prefix='/api' )
diff -r 6d54a7fd1fe9edfdf2f5c2fdb1b5ddb7eebdf59a -r edbe286564de14b2d51656c03443b3e4aa99e3ab test/api/test_tool_data.py
--- a/test/api/test_tool_data.py
+++ b/test/api/test_tool_data.py
@@ -28,3 +28,11 @@
assert first_entry[0] == "data1"
assert first_entry[1] == "data1name"
assert first_entry[2].endswith("test/functional/tool-data/data1/entry.txt")
+
+ def test_show_field(self):
+ show_field_response = self._get( "tool_data/testalpha/fields/data1", admin=True )
+ self._assert_status_code_is( show_field_response, 200 )
+ field = show_field_response.json()
+ self._assert_has_keys( field, "files", "name", "fields", "fingerprint", "base_dir" )
+ files = field[ "files" ]
+ assert len( files ) == 2
https://bitbucket.org/galaxy/galaxy-central/commits/e416c3a2f956/
Changeset: e416c3a2f956
User: jmchilton
Date: 2014-12-15 05:20:35+00:00
Summary: Allow downloading index files via tool data API.
This provides direct access to the files to admins - probably still wise to provide some mechanism to download a copressed archive of these files.
Affected #: 3 files
diff -r edbe286564de14b2d51656c03443b3e4aa99e3ab -r e416c3a2f956a28127eb730076f37ad6ee4b1d8b lib/galaxy/webapps/galaxy/api/tool_data.py
--- a/lib/galaxy/webapps/galaxy/api/tool_data.py
+++ b/lib/galaxy/webapps/galaxy/api/tool_data.py
@@ -1,6 +1,9 @@
+import os
+
from galaxy import exceptions
from galaxy import web
from galaxy.web import _future_expose_api as expose_api
+from galaxy.web import _future_expose_api_raw as expose_api_raw
from galaxy.web.base.controller import BaseAPIController
@@ -74,6 +77,16 @@
"""
return self._data_table_field( id, value ).to_dict()
+ @web.require_admin
+ @expose_api_raw
+ def download_field_file( self, trans, id, value, path, **kwds ):
+ field_value = self._data_table_field( id, value )
+ base_dir = field_value.get_base_dir()
+ full_path = os.path.join( base_dir, path )
+ if full_path not in field_value.get_files():
+ raise exceptions.ObjectNotFound("No such path in data table field.")
+ return open(full_path, "r")
+
def _data_table_field( self, id, value ):
out = self._data_table(id).get_field(value)
if out is None:
diff -r edbe286564de14b2d51656c03443b3e4aa99e3ab -r e416c3a2f956a28127eb730076f37ad6ee4b1d8b lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -171,6 +171,7 @@
webapp.mapper.resource( 'dataset', 'datasets', path_prefix='/api' )
webapp.mapper.resource( 'tool_data', 'tool_data', path_prefix='/api' )
+ webapp.mapper.connect( '/api/tool_data/{id:.+?}/fields/{value:.+?}/files/{path:.+?}', action='download_field_file', controller="tool_data" )
webapp.mapper.connect( '/api/tool_data/{id:.+?}/fields/{value:.+?}', action='show_field', controller="tool_data" )
webapp.mapper.resource( 'dataset_collection', 'dataset_collections', path_prefix='/api/')
webapp.mapper.resource( 'sample', 'samples', path_prefix='/api' )
diff -r edbe286564de14b2d51656c03443b3e4aa99e3ab -r e416c3a2f956a28127eb730076f37ad6ee4b1d8b test/api/test_tool_data.py
--- a/test/api/test_tool_data.py
+++ b/test/api/test_tool_data.py
@@ -36,3 +36,9 @@
self._assert_has_keys( field, "files", "name", "fields", "fingerprint", "base_dir" )
files = field[ "files" ]
assert len( files ) == 2
+
+ def test_download_field_file(self):
+ show_field_response = self._get( "tool_data/testalpha/fields/data1/files/entry.txt", admin=True )
+ self._assert_status_code_is( show_field_response, 200 )
+ content = show_field_response.content
+ assert content == "This is data 1.", content
https://bitbucket.org/galaxy/galaxy-central/commits/66fbbbd58209/
Changeset: 66fbbbd58209
User: jmchilton
Date: 2014-12-15 05:20:35+00:00
Summary: Allow specification of multiple data manager configuration files...
... use this to create API functional tests for data managers.
Affected #: 7 files
diff -r e416c3a2f956a28127eb730076f37ad6ee4b1d8b -r 66fbbbd58209dba224f95c2aaf973ec7c64b7c11 lib/galaxy/tools/data_manager/manager.py
--- a/lib/galaxy/tools/data_manager/manager.py
+++ b/lib/galaxy/tools/data_manager/manager.py
@@ -25,7 +25,10 @@
self.managed_data_tables = odict()
self.tool_path = None
self.filename = xml_filename or self.app.config.data_manager_config_file
- self.load_from_xml( self.filename )
+ for filename in util.listify( self.filename ):
+ if not filename:
+ continue
+ self.load_from_xml( filename )
if self.app.config.shed_data_manager_config_file:
self.load_from_xml( self.app.config.shed_data_manager_config_file, store_tool_path=False, replace_existing=True )
diff -r e416c3a2f956a28127eb730076f37ad6ee4b1d8b -r 66fbbbd58209dba224f95c2aaf973ec7c64b7c11 scripts/functional_tests.py
--- a/scripts/functional_tests.py
+++ b/scripts/functional_tests.py
@@ -241,6 +241,12 @@
if os.path.exists( tool_data_config ):
default_tool_data_config = tool_data_config
tool_data_table_config_path = '%s,test/functional/tool-data/sample_tool_data_tables.xml' % default_tool_data_config
+
+ default_data_manager_config = 'config/data_manager_conf.xml.sample'
+ for data_manager_config in ['config/data_manager_conf.xml', 'data_manager_conf.xml' ]:
+ if os.path.exists( data_manager_config ):
+ default_data_manager_config = data_manager_config
+ data_manager_config_file = "%s,test/functional/tools/sample_data_manager_conf.xml" % default_data_manager_config
shed_tool_data_table_config = 'config/shed_tool_data_table_conf.xml'
tool_dependency_dir = os.environ.get( 'GALAXY_TOOL_DEPENDENCY_DIR', None )
use_distributed_object_store = os.environ.get( 'GALAXY_USE_DISTRIBUTED_OBJECT_STORE', False )
@@ -329,6 +335,7 @@
master_api_key=master_api_key,
use_tasked_jobs=True,
enable_beta_tool_formats=True,
+ data_manager_config_file=data_manager_config_file,
)
if install_database_connection is not None:
kwargs[ 'install_database_connection' ] = install_database_connection
diff -r e416c3a2f956a28127eb730076f37ad6ee4b1d8b -r 66fbbbd58209dba224f95c2aaf973ec7c64b7c11 test/api/test_tool_data.py
--- a/test/api/test_tool_data.py
+++ b/test/api/test_tool_data.py
@@ -1,6 +1,9 @@
""" Tests for the tool data API.
"""
+import json
+
from base import api
+from .helpers import DatasetPopulator
import operator
@@ -42,3 +45,19 @@
self._assert_status_code_is( show_field_response, 200 )
content = show_field_response.content
assert content == "This is data 1.", content
+
+ def test_create_data_with_manager(self):
+ dataset_populator = DatasetPopulator( self.galaxy_interactor )
+ history_id = dataset_populator.new_history()
+ payload = dataset_populator.run_tool_payload(
+ tool_id="data_manager",
+ inputs={"ignored_value": "moo"},
+ history_id=history_id,
+ )
+ create_response = self._post( "tools", data=payload )
+ self._assert_status_code_is( create_response, 200 )
+ dataset_populator.wait_for_history( history_id, assert_ok=True )
+ show_response = self._get( "tool_data/testbeta", admin=True )
+ print show_response.content
+ assert False
+
diff -r e416c3a2f956a28127eb730076f37ad6ee4b1d8b -r 66fbbbd58209dba224f95c2aaf973ec7c64b7c11 test/functional/tool-data/sample_tool_data_tables.xml
--- a/test/functional/tool-data/sample_tool_data_tables.xml
+++ b/test/functional/tool-data/sample_tool_data_tables.xml
@@ -3,10 +3,8 @@
<columns>value, name, path</columns><file path="${__HERE__}/testalpha.loc" /></table>
- <!--
<table name="testbeta" comment_char="#">
- <columns>value, foo, bar, path</columns>
+ <columns>value, path</columns><file path="${__HERE__}/testbeta.loc" /></table>
- --></tables>
diff -r e416c3a2f956a28127eb730076f37ad6ee4b1d8b -r 66fbbbd58209dba224f95c2aaf973ec7c64b7c11 test/functional/tools/data_manager.xml
--- /dev/null
+++ b/test/functional/tools/data_manager.xml
@@ -0,0 +1,16 @@
+<tool id="data_manager" name="Test Data Manager" tool_type="manage_data" version="0.0.1">
+ <configfiles>
+ <configfile name="static_test_data">{"data_tables": {"testbeta": [{"value": "newvalue", "path": "newvalue.txt"}]}}</configfile>
+ </configfiles>
+ <command>
+ mkdir $out_file.files_path ;
+ echo "A new value" > $out_file.files_path/newvalue.txt;
+ cp $static_test_data $out_file
+ </command>
+ <inputs>
+ <param type="text" name="ignored_value" value="" label="Ignored" />
+ </inputs>
+ <outputs>
+ <data name="out_file" format="data_manager_json"/>
+ </outputs>
+</tool>
diff -r e416c3a2f956a28127eb730076f37ad6ee4b1d8b -r 66fbbbd58209dba224f95c2aaf973ec7c64b7c11 test/functional/tools/sample_data_manager_conf.xml
--- /dev/null
+++ b/test/functional/tools/sample_data_manager_conf.xml
@@ -0,0 +1,16 @@
+<data_managers tool_path="test/functional/tools">
+ <data_manager tool_file="data_manager.xml" id="test_data_manager" version="1.0">
+ <data_table name="testbeta">
+ <output>
+ <column name="value" />
+ <column name="path" output_ref="out_file" >
+ <move type="directory" relativize_symlinks="True">
+ <target base="${GALAXY_DATA_MANAGER_DATA_PATH}">testbeta/${value}</target>
+ </move>
+ <value_translation>${GALAXY_DATA_MANAGER_DATA_PATH}/testbeta/${value}/${path}</value_translation>
+ <value_translation type="function">abspath</value_translation>
+ </column>
+ </output>
+ </data_table>
+ </data_manager>
+</data_managers>
https://bitbucket.org/galaxy/galaxy-central/commits/ecac8a867961/
Changeset: ecac8a867961
User: jmchilton
Date: 2014-12-15 05:20:35+00:00
Summary: Implement test case for Pull Request #577.
Affected #: 1 file
diff -r 66fbbbd58209dba224f95c2aaf973ec7c64b7c11 -r ecac8a8679612d419347dcbedc398319418b077e test/api/test_tool_data.py
--- a/test/api/test_tool_data.py
+++ b/test/api/test_tool_data.py
@@ -5,6 +5,8 @@
from base import api
from .helpers import DatasetPopulator
+from requests import delete
+
import operator
@@ -46,7 +48,10 @@
content = show_field_response.content
assert content == "This is data 1.", content
- def test_create_data_with_manager(self):
+ def test_delete_entry(self):
+ show_response = self._get( "tool_data/testbeta", admin=True )
+ original_count = len(show_response.json()["fields"])
+
dataset_populator = DatasetPopulator( self.galaxy_interactor )
history_id = dataset_populator.new_history()
payload = dataset_populator.run_tool_payload(
@@ -58,6 +63,12 @@
self._assert_status_code_is( create_response, 200 )
dataset_populator.wait_for_history( history_id, assert_ok=True )
show_response = self._get( "tool_data/testbeta", admin=True )
- print show_response.content
- assert False
+ updated_fields = show_response.json()["fields"]
+ assert len(updated_fields) == original_count + 1
+ field0 = updated_fields[0]
+ url = self._api_url( "tool_data/testbeta?key=%s" % self.galaxy_interactor.master_api_key )
+ delete( url, data=json.dumps({"values": "\t".join(field0)}) )
+ show_response = self._get( "tool_data/testbeta", admin=True )
+ updated_fields = show_response.json()["fields"]
+ assert len(updated_fields) == original_count
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0