alchemist-nrel 0.2.1__py3-none-any.whl → 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- alchemist_core/__init__.py +14 -7
- alchemist_core/acquisition/botorch_acquisition.py +14 -6
- alchemist_core/audit_log.py +594 -0
- alchemist_core/data/experiment_manager.py +69 -5
- alchemist_core/models/botorch_model.py +6 -4
- alchemist_core/models/sklearn_model.py +44 -6
- alchemist_core/session.py +600 -8
- alchemist_core/utils/doe.py +200 -0
- {alchemist_nrel-0.2.1.dist-info → alchemist_nrel-0.3.0.dist-info}/METADATA +57 -40
- alchemist_nrel-0.3.0.dist-info/RECORD +66 -0
- {alchemist_nrel-0.2.1.dist-info → alchemist_nrel-0.3.0.dist-info}/entry_points.txt +1 -0
- {alchemist_nrel-0.2.1.dist-info → alchemist_nrel-0.3.0.dist-info}/top_level.txt +1 -0
- api/main.py +19 -3
- api/models/requests.py +71 -0
- api/models/responses.py +144 -0
- api/routers/experiments.py +117 -5
- api/routers/sessions.py +329 -10
- api/routers/visualizations.py +10 -5
- api/services/session_store.py +210 -54
- api/static/NEW_ICON.ico +0 -0
- api/static/NEW_ICON.png +0 -0
- api/static/NEW_LOGO_DARK.png +0 -0
- api/static/NEW_LOGO_LIGHT.png +0 -0
- api/static/assets/api-vcoXEqyq.js +1 -0
- api/static/assets/index-C0_glioA.js +4084 -0
- api/static/assets/index-CB4V1LI5.css +1 -0
- api/static/index.html +14 -0
- api/static/vite.svg +1 -0
- run_api.py +55 -0
- ui/gpr_panel.py +7 -2
- ui/notifications.py +197 -10
- ui/ui.py +1117 -68
- ui/variables_setup.py +47 -2
- ui/visualizations.py +60 -3
- alchemist_nrel-0.2.1.dist-info/RECORD +0 -54
- {alchemist_nrel-0.2.1.dist-info → alchemist_nrel-0.3.0.dist-info}/WHEEL +0 -0
- {alchemist_nrel-0.2.1.dist-info → alchemist_nrel-0.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{--background: 0 0% 98%;--foreground: 222.2 84% 4.9%;--card: 0 0% 100%;--card-foreground: 222.2 84% 4.9%;--popover: 0 0% 100%;--popover-foreground: 222.2 84% 4.9%;--primary: 217 91% 50%;--primary-foreground: 0 0% 100%;--secondary: 214 32% 88%;--secondary-foreground: 222.2 47.4% 11.2%;--muted: 210 40% 93%;--muted-foreground: 215.4 16.3% 40%;--accent: 210 40% 93%;--accent-foreground: 222.2 47.4% 11.2%;--destructive: 0 84.2% 50%;--destructive-foreground: 0 0% 100%;--border: 214.3 31.8% 85%;--input: 214.3 31.8% 85%;--ring: 217 91% 50%;--radius: .5rem}.dark{--background: 220 13% 13%;--foreground: 210 40% 98%;--card: 220 13% 16%;--card-foreground: 210 40% 98%;--popover: 220 13% 16%;--popover-foreground: 210 40% 98%;--primary: 211 100% 65%;--primary-foreground: 220 13% 13%;--secondary: 217.2 32.6% 20%;--secondary-foreground: 210 40% 98%;--muted: 220 13% 20%;--muted-foreground: 215 15% 65%;--accent: 220 13% 20%;--accent-foreground: 210 40% 98%;--destructive: 0 70% 55%;--destructive-foreground: 210 40% 98%;--border: 220 13% 24%;--input: 220 13% 24%;--ring: 211 100% 65%}*{border-color:hsl(var(--border))}body{background-color:hsl(var(--background));color:hsl(var(--foreground));margin:0;min-height:100vh;font-feature-settings:"kern" 1,"liga" 1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code,.tabular-nums{font-variant-numeric:tabular-nums;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.container{width:100%}@media(min-width:640px){.container{max-width:640px}}@media(min-width:768px){.container{max-width:768px}}@media(min-width:1024px){.container{max-width:1024px}}@media(min-width:1280px){.container{max-width:1280px}}@media(min-width:1536px){.container{max-width:1536px}}.visible{visibility:visible}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.bottom-0{bottom:0}.left-0{left:0}.right-0{right:0}.top-0{top:0}.z-50{z-index:50}.col-span-1{grid-column:span 1 / span 1}.col-span-2{grid-column:span 2 / span 2}.col-span-3{grid-column:span 3 / span 3}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-8{margin-top:2rem;margin-bottom:2rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-4{margin-left:1rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-0\.5{height:.125rem}.h-12{height:3rem}.h-2{height:.5rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-8{height:2rem}.h-96{height:24rem}.h-auto{height:auto}.h-full{height:100%}.h-screen{height:100vh}.max-h-48{max-height:12rem}.max-h-64{max-height:16rem}.max-h-\[120px\]{max-height:120px}.max-h-\[450px\]{max-height:450px}.max-h-\[85vh\]{max-height:85vh}.max-h-\[90vh\]{max-height:90vh}.min-h-0{min-height:0px}.min-h-\[550px\]{min-height:550px}.min-h-screen{min-height:100vh}.w-12{width:3rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-8{width:2rem}.w-80{width:20rem}.w-\[320px\]{width:320px}.w-\[580px\]{width:580px}.w-full{width:100%}.max-w-2xl{max-width:42rem}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-xl{max-width:36rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.resize-y{resize:vertical}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.gap-y-1\.5{row-gap:.375rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-2\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.625rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.625rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-amber-500\/20{border-color:#f59e0b33}.border-blue-500\/30{border-color:#3b82f64d}.border-border{border-color:hsl(var(--border))}.border-border\/50{border-color:hsl(var(--border) / .5)}.border-destructive\/30{border-color:hsl(var(--destructive) / .3)}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.border-green-500\/20{border-color:#22c55e33}.border-input{border-color:hsl(var(--input))}.border-muted{border-color:hsl(var(--muted))}.border-muted-foreground\/20{border-color:hsl(var(--muted-foreground) / .2)}.border-primary{border-color:hsl(var(--primary))}.bg-amber-500\/10{background-color:#f59e0b1a}.bg-background{background-color:hsl(var(--background))}.bg-black\/50{background-color:#00000080}.bg-blue-500\/5{background-color:#3b82f60d}.bg-card{background-color:hsl(var(--card))}.bg-green-500\/10{background-color:#22c55e1a}.bg-green-600{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity, 1))}.bg-muted{background-color:hsl(var(--muted))}.bg-muted\/20{background-color:hsl(var(--muted) / .2)}.bg-muted\/30{background-color:hsl(var(--muted) / .3)}.bg-muted\/50{background-color:hsl(var(--muted) / .5)}.bg-primary{background-color:hsl(var(--primary))}.bg-primary\/10{background-color:hsl(var(--primary) / .1)}.bg-primary\/5{background-color:hsl(var(--primary) / .05)}.bg-secondary{background-color:hsl(var(--secondary))}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pb-1\.5{padding-bottom:.375rem}.pb-2{padding-bottom:.5rem}.pr-2{padding-right:.5rem}.pt-2{padding-top:.5rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-6xl{font-size:3.75rem;line-height:1}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-tight{line-height:1.25}.tracking-wide{letter-spacing:.025em}.text-amber-600{--tw-text-opacity: 1;color:rgb(217 119 6 / var(--tw-text-opacity, 1))}.text-amber-700{--tw-text-opacity: 1;color:rgb(180 83 9 / var(--tw-text-opacity, 1))}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity, 1))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-destructive{color:hsl(var(--destructive))}.text-foreground{color:hsl(var(--foreground))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity, 1))}.text-green-700{--tw-text-opacity: 1;color:rgb(21 128 61 / var(--tw-text-opacity, 1))}.text-muted-foreground{color:hsl(var(--muted-foreground))}.text-muted-foreground\/60{color:hsl(var(--muted-foreground) / .6)}.text-orange-600{--tw-text-opacity: 1;color:rgb(234 88 12 / var(--tw-text-opacity, 1))}.text-primary{color:hsl(var(--primary))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-purple-500{--tw-text-opacity: 1;color:rgb(168 85 247 / var(--tw-text-opacity, 1))}.text-secondary-foreground{color:hsl(var(--secondary-foreground))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.text-yellow-600{--tw-text-opacity: 1;color:rgb(202 138 4 / var(--tw-text-opacity, 1))}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:hsl(var(--muted-foreground) / .3);border-radius:4px}::-webkit-scrollbar-thumb:hover{background:hsl(var(--muted-foreground) / .5)}*{scrollbar-width:thin;scrollbar-color:hsl(var(--muted-foreground) / .3) transparent}.placeholder\:text-muted-foreground::-moz-placeholder{color:hsl(var(--muted-foreground))}.placeholder\:text-muted-foreground::placeholder{color:hsl(var(--muted-foreground))}.hover\:bg-accent:hover{background-color:hsl(var(--accent))}.hover\:bg-accent\/50:hover{background-color:hsl(var(--accent) / .5)}.hover\:bg-destructive\/10:hover{background-color:hsl(var(--destructive) / .1)}.hover\:bg-green-700:hover{--tw-bg-opacity: 1;background-color:rgb(21 128 61 / var(--tw-bg-opacity, 1))}.hover\:bg-muted\/5:hover{background-color:hsl(var(--muted) / .05)}.hover\:bg-muted\/50:hover{background-color:hsl(var(--muted) / .5)}.hover\:bg-muted\/80:hover{background-color:hsl(var(--muted) / .8)}.hover\:bg-primary\/90:hover{background-color:hsl(var(--primary) / .9)}.hover\:bg-secondary\/80:hover{background-color:hsl(var(--secondary) / .8)}.hover\:bg-secondary\/90:hover{background-color:hsl(var(--secondary) / .9)}.hover\:text-destructive\/80:hover{color:hsl(var(--destructive) / .8)}.hover\:text-foreground:hover{color:hsl(var(--foreground))}.hover\:text-primary\/80:hover{color:hsl(var(--primary) / .8)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-primary\/50:focus{--tw-ring-color: hsl(var(--primary) / .5)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.dark\:text-amber-500:is(.dark *){--tw-text-opacity: 1;color:rgb(245 158 11 / var(--tw-text-opacity, 1))}.dark\:text-green-500:is(.dark *){--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}@media(min-width:768px){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media(min-width:1024px){.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}
|
api/static/index.html
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en" class="dark">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/NEW_ICON.png" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>ALchemist - Active Learning Toolkit</title>
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-C0_glioA.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CB4V1LI5.css">
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="root"></div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
api/static/vite.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
run_api.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Startup script for ALchemist FastAPI server.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python run_api.py # Development mode with auto-reload
|
|
6
|
+
python run_api.py --production # Production mode (no reload)
|
|
7
|
+
python run_api.py --dev # Explicitly start in development mode
|
|
8
|
+
alchemist-web # Entry point (production mode by default)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def main():
|
|
12
|
+
"""Entry point for alchemist-web command."""
|
|
13
|
+
import uvicorn
|
|
14
|
+
import sys
|
|
15
|
+
|
|
16
|
+
# For the alchemist-web entry point, default to production mode
|
|
17
|
+
# Only use dev mode if explicitly requested
|
|
18
|
+
is_script_call = any(arg.endswith('run_api.py') for arg in sys.argv)
|
|
19
|
+
|
|
20
|
+
if is_script_call:
|
|
21
|
+
# Called as: python run_api.py
|
|
22
|
+
# Default to dev mode unless --production flag is present
|
|
23
|
+
production = "--production" in sys.argv or "--prod" in sys.argv
|
|
24
|
+
else:
|
|
25
|
+
# Called as: alchemist-web
|
|
26
|
+
# Default to production mode unless --dev flag is present
|
|
27
|
+
production = "--dev" not in sys.argv and "--development" not in sys.argv
|
|
28
|
+
|
|
29
|
+
if production:
|
|
30
|
+
print("Starting ALchemist API in PRODUCTION mode...")
|
|
31
|
+
print("Access the web UI at: http://localhost:8000")
|
|
32
|
+
# Run the API server in production mode
|
|
33
|
+
uvicorn.run(
|
|
34
|
+
"api.main:app",
|
|
35
|
+
host="0.0.0.0",
|
|
36
|
+
port=8000,
|
|
37
|
+
reload=False,
|
|
38
|
+
log_level="warning",
|
|
39
|
+
workers=1 # Increase to 4 for multi-core production
|
|
40
|
+
)
|
|
41
|
+
else:
|
|
42
|
+
print("Starting ALchemist API in DEVELOPMENT mode...")
|
|
43
|
+
print("API docs: http://localhost:8000/api/docs")
|
|
44
|
+
# Run the API server in development mode
|
|
45
|
+
uvicorn.run(
|
|
46
|
+
"api.main:app",
|
|
47
|
+
host="0.0.0.0",
|
|
48
|
+
port=8000,
|
|
49
|
+
reload=True,
|
|
50
|
+
log_level="info"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
if __name__ == "__main__":
|
|
55
|
+
main()
|
ui/gpr_panel.py
CHANGED
|
@@ -444,10 +444,15 @@ class GaussianProcessPanel(ctk.CTkFrame):
|
|
|
444
444
|
|
|
445
445
|
# Get categorical dimensions
|
|
446
446
|
categorical_variables = self.main_app.search_space_manager.get_categorical_variables()
|
|
447
|
+
|
|
448
|
+
# Get feature columns (excluding metadata)
|
|
449
|
+
metadata_cols = {'Output', 'Noise', 'Iteration', 'Reason'}
|
|
450
|
+
feature_cols = [col for col in self.main_app.exp_df.columns if col not in metadata_cols]
|
|
451
|
+
|
|
447
452
|
cat_dims = [
|
|
448
|
-
|
|
453
|
+
feature_cols.index(var)
|
|
449
454
|
for var in categorical_variables
|
|
450
|
-
if var in
|
|
455
|
+
if var in feature_cols
|
|
451
456
|
]
|
|
452
457
|
|
|
453
458
|
# Train using session API
|
ui/notifications.py
CHANGED
|
@@ -16,7 +16,16 @@ class ResultNotificationWindow:
|
|
|
16
16
|
"""
|
|
17
17
|
self.window = ctk.CTkToplevel(parent)
|
|
18
18
|
self.window.title("Suggested Next Experiment")
|
|
19
|
-
|
|
19
|
+
# Increase height to avoid bottom content being cut off on smaller displays
|
|
20
|
+
# Use a taller default and enforce a reasonable minimum size
|
|
21
|
+
screen_h = self.window.winfo_screenheight()
|
|
22
|
+
default_h = min(750, int(screen_h * 0.65))
|
|
23
|
+
self.window.geometry(f"600x{default_h}")
|
|
24
|
+
# Ensure window cannot be resized smaller than a usable size
|
|
25
|
+
try:
|
|
26
|
+
self.window.minsize(560, 600)
|
|
27
|
+
except Exception:
|
|
28
|
+
pass
|
|
20
29
|
self.window.lift()
|
|
21
30
|
self.window.focus_force()
|
|
22
31
|
self.window.grab_set()
|
|
@@ -39,16 +48,23 @@ class ResultNotificationWindow:
|
|
|
39
48
|
# Fill strategy info tab
|
|
40
49
|
self._create_strategy_info_tab(result_data)
|
|
41
50
|
|
|
42
|
-
# Add
|
|
51
|
+
# Add action buttons
|
|
43
52
|
button_frame = ctk.CTkFrame(self.window)
|
|
44
53
|
button_frame.pack(fill="x", padx=10, pady=10)
|
|
45
54
|
|
|
46
|
-
|
|
55
|
+
# Store references for logging
|
|
56
|
+
self.result_data = result_data
|
|
57
|
+
self.model_data = model_data
|
|
58
|
+
self.parent = parent
|
|
59
|
+
|
|
60
|
+
log_button = ctk.CTkButton(
|
|
47
61
|
button_frame,
|
|
48
|
-
text="
|
|
49
|
-
command=
|
|
62
|
+
text="📝 Log to Audit Trail",
|
|
63
|
+
command=self._log_to_audit_trail,
|
|
64
|
+
fg_color="green",
|
|
65
|
+
hover_color="darkgreen"
|
|
50
66
|
)
|
|
51
|
-
|
|
67
|
+
log_button.pack(side="left", padx=10, pady=5)
|
|
52
68
|
|
|
53
69
|
close_button = ctk.CTkButton(
|
|
54
70
|
button_frame,
|
|
@@ -146,7 +162,7 @@ class ResultNotificationWindow:
|
|
|
146
162
|
).pack(pady=5)
|
|
147
163
|
|
|
148
164
|
# Create scrollable frame for coordinates
|
|
149
|
-
coords_scroll = ctk.CTkScrollableFrame(coords_frame, height=
|
|
165
|
+
coords_scroll = ctk.CTkScrollableFrame(coords_frame, height=220)
|
|
150
166
|
coords_scroll.pack(fill="x", padx=10, pady=5)
|
|
151
167
|
|
|
152
168
|
# Add each coordinate
|
|
@@ -232,7 +248,7 @@ class ResultNotificationWindow:
|
|
|
232
248
|
).pack(pady=5)
|
|
233
249
|
|
|
234
250
|
# Create scrollable frame for model details
|
|
235
|
-
model_scroll = ctk.CTkScrollableFrame(model_frame, height=
|
|
251
|
+
model_scroll = ctk.CTkScrollableFrame(model_frame, height=520)
|
|
236
252
|
model_scroll.pack(fill="both", expand=True, padx=10, pady=5)
|
|
237
253
|
|
|
238
254
|
# Basic model info
|
|
@@ -342,7 +358,7 @@ class ResultNotificationWindow:
|
|
|
342
358
|
).pack(pady=5)
|
|
343
359
|
|
|
344
360
|
# Create scrollable frame for strategy details
|
|
345
|
-
strategy_scroll = ctk.CTkScrollableFrame(strategy_frame, height=
|
|
361
|
+
strategy_scroll = ctk.CTkScrollableFrame(strategy_frame, height=520)
|
|
346
362
|
strategy_scroll.pack(fill="both", expand=True, padx=10, pady=5)
|
|
347
363
|
|
|
348
364
|
# Strategy type
|
|
@@ -479,6 +495,112 @@ class ResultNotificationWindow:
|
|
|
479
495
|
|
|
480
496
|
except Exception as e:
|
|
481
497
|
print(f"Error exporting results: {e}")
|
|
498
|
+
|
|
499
|
+
def _log_to_audit_trail(self):
|
|
500
|
+
"""Log the complete optimization decision (data + model + acquisition) to audit trail."""
|
|
501
|
+
# Show notes dialog
|
|
502
|
+
notes_dialog = AuditNotesDialog(self.window)
|
|
503
|
+
self.window.wait_window(notes_dialog)
|
|
504
|
+
|
|
505
|
+
# Check if user cancelled
|
|
506
|
+
if notes_dialog.result is None:
|
|
507
|
+
print("Audit logging cancelled by user")
|
|
508
|
+
return
|
|
509
|
+
|
|
510
|
+
notes = notes_dialog.result
|
|
511
|
+
|
|
512
|
+
# Get the main app from parent chain
|
|
513
|
+
main_app = self._get_main_app()
|
|
514
|
+
if main_app is None:
|
|
515
|
+
print("Error: Could not find main app to log audit trail")
|
|
516
|
+
return
|
|
517
|
+
|
|
518
|
+
# Get next point(s) from result data
|
|
519
|
+
next_point_df = self.result_data.get('point_df')
|
|
520
|
+
|
|
521
|
+
# Create strategy info dict
|
|
522
|
+
strategy_type = self.result_data.get('strategy_type', 'Unknown')
|
|
523
|
+
strategy_info = {
|
|
524
|
+
'type': strategy_type,
|
|
525
|
+
'params': self.result_data.get('strategy_params'),
|
|
526
|
+
'maximize': self.result_data.get('maximize'),
|
|
527
|
+
'description': self.result_data.get('strategy_description'),
|
|
528
|
+
'notes': notes # Add user notes
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
# Store pending suggestions in main app for use with Add Point dialog
|
|
532
|
+
if next_point_df is not None and len(next_point_df) > 0:
|
|
533
|
+
# Convert DataFrame to list of dicts and add metadata
|
|
534
|
+
pending = next_point_df.to_dict('records')
|
|
535
|
+
# Attach authoritative Iteration from the session so the Add Point
|
|
536
|
+
# dialog displays the correct iteration (session.lock_acquisition
|
|
537
|
+
# may have already incremented it).
|
|
538
|
+
sess_iter = int(getattr(main_app.session.experiment_manager, '_current_iteration',
|
|
539
|
+
getattr(main_app.experiment_manager, '_current_iteration', 0)))
|
|
540
|
+
for suggestion in pending:
|
|
541
|
+
suggestion['_reason'] = strategy_type # Tag with acquisition strategy name
|
|
542
|
+
suggestion['Iteration'] = int(suggestion.get('Iteration', sess_iter))
|
|
543
|
+
|
|
544
|
+
# Sync UI experiment manager iteration to session so iteration
|
|
545
|
+
# shown in other UI places is consistent.
|
|
546
|
+
try:
|
|
547
|
+
main_app.experiment_manager._current_iteration = sess_iter
|
|
548
|
+
except Exception:
|
|
549
|
+
pass
|
|
550
|
+
|
|
551
|
+
main_app.pending_suggestions = pending
|
|
552
|
+
main_app.current_suggestion_index = 0
|
|
553
|
+
|
|
554
|
+
batch_size = len(pending)
|
|
555
|
+
if batch_size > 1:
|
|
556
|
+
print(f"✓ Stored {batch_size} pending suggestions from {strategy_type}")
|
|
557
|
+
else:
|
|
558
|
+
print(f"✓ Stored 1 pending suggestion from {strategy_type}")
|
|
559
|
+
|
|
560
|
+
# Call the main app's logging function
|
|
561
|
+
success = main_app.log_optimization_to_audit(next_point_df, strategy_info)
|
|
562
|
+
|
|
563
|
+
if success:
|
|
564
|
+
print("✓ Complete optimization decision logged to audit trail")
|
|
565
|
+
# Show brief success message and close window
|
|
566
|
+
import tkinter as tk
|
|
567
|
+
msg = "Optimization decision logged successfully!\n\n" \
|
|
568
|
+
"• Data snapshot recorded\n" \
|
|
569
|
+
"• Model parameters recorded\n" \
|
|
570
|
+
"• Acquisition strategy recorded\n"
|
|
571
|
+
|
|
572
|
+
if next_point_df is not None and len(next_point_df) > 0:
|
|
573
|
+
batch_size = len(next_point_df)
|
|
574
|
+
if batch_size > 1:
|
|
575
|
+
msg += f"\n{batch_size} suggestions are now pending.\n" \
|
|
576
|
+
f"Use 'Add Point' to enter results for each suggestion."
|
|
577
|
+
else:
|
|
578
|
+
msg += f"\n1 suggestion is now pending.\n" \
|
|
579
|
+
f"Use 'Add Point' to enter the result."
|
|
580
|
+
|
|
581
|
+
tk.messagebox.showinfo("Logged to Audit Trail", msg, parent=self.window)
|
|
582
|
+
self.window.destroy()
|
|
583
|
+
else:
|
|
584
|
+
import tkinter as tk
|
|
585
|
+
tk.messagebox.showerror("Logging Failed",
|
|
586
|
+
"Failed to log optimization decision to audit trail.\n"
|
|
587
|
+
"Check the console for details.",
|
|
588
|
+
parent=self.window)
|
|
589
|
+
|
|
590
|
+
def _get_main_app(self):
|
|
591
|
+
"""Walk up the widget tree to find the main app."""
|
|
592
|
+
widget = self.parent
|
|
593
|
+
while widget is not None:
|
|
594
|
+
if hasattr(widget, 'log_optimization_to_audit'):
|
|
595
|
+
return widget
|
|
596
|
+
# Try to get parent for CustomTkinter widgets
|
|
597
|
+
if hasattr(widget, 'master'):
|
|
598
|
+
widget = widget.master
|
|
599
|
+
elif hasattr(widget, 'main_app'):
|
|
600
|
+
widget = widget.main_app
|
|
601
|
+
else:
|
|
602
|
+
break
|
|
603
|
+
return None
|
|
482
604
|
|
|
483
605
|
|
|
484
606
|
class CalibrationWarningDialog:
|
|
@@ -495,7 +617,8 @@ class CalibrationWarningDialog:
|
|
|
495
617
|
"""
|
|
496
618
|
self.window = ctk.CTkToplevel(parent)
|
|
497
619
|
self.window.title("⚠ Model Calibration Warning")
|
|
498
|
-
|
|
620
|
+
# Slightly taller to ensure recommendations aren't clipped
|
|
621
|
+
self.window.geometry("600x480")
|
|
499
622
|
self.window.lift()
|
|
500
623
|
self.window.focus_force()
|
|
501
624
|
self.window.grab_set()
|
|
@@ -652,3 +775,67 @@ def show_calibration_warning(parent, calibration_factor, backend="scikit-learn")
|
|
|
652
775
|
CalibrationWarningDialog(parent, calibration_factor, backend)
|
|
653
776
|
return True
|
|
654
777
|
return False
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
# ============================================================
|
|
781
|
+
# Audit Notes Dialog
|
|
782
|
+
# ============================================================
|
|
783
|
+
|
|
784
|
+
class AuditNotesDialog(ctk.CTkToplevel):
|
|
785
|
+
"""Simple dialog for entering optional notes when logging to audit trail."""
|
|
786
|
+
|
|
787
|
+
def __init__(self, parent):
|
|
788
|
+
super().__init__(parent)
|
|
789
|
+
self.result = None # Will be None (cancelled) or notes string (confirmed)
|
|
790
|
+
|
|
791
|
+
self.title("Add Notes to Audit Log")
|
|
792
|
+
self.geometry("450x250")
|
|
793
|
+
|
|
794
|
+
# Message
|
|
795
|
+
msg_label = ctk.CTkLabel(
|
|
796
|
+
self,
|
|
797
|
+
text="Add optional notes to this audit log entry:",
|
|
798
|
+
font=('Arial', 12)
|
|
799
|
+
)
|
|
800
|
+
msg_label.pack(pady=(20, 10))
|
|
801
|
+
|
|
802
|
+
# Notes field
|
|
803
|
+
self.notes_text = ctk.CTkTextbox(self, width=400, height=100)
|
|
804
|
+
self.notes_text.pack(pady=5, padx=20)
|
|
805
|
+
self.notes_text.focus()
|
|
806
|
+
|
|
807
|
+
# Buttons
|
|
808
|
+
button_frame = ctk.CTkFrame(self, fg_color="transparent")
|
|
809
|
+
button_frame.pack(pady=20)
|
|
810
|
+
|
|
811
|
+
confirm_btn = ctk.CTkButton(
|
|
812
|
+
button_frame,
|
|
813
|
+
text="Log to Audit Trail",
|
|
814
|
+
command=self.confirm,
|
|
815
|
+
fg_color="green",
|
|
816
|
+
hover_color="darkgreen",
|
|
817
|
+
width=150
|
|
818
|
+
)
|
|
819
|
+
confirm_btn.pack(side='left', padx=5)
|
|
820
|
+
|
|
821
|
+
cancel_btn = ctk.CTkButton(
|
|
822
|
+
button_frame,
|
|
823
|
+
text="Cancel",
|
|
824
|
+
command=self.cancel,
|
|
825
|
+
width=100
|
|
826
|
+
)
|
|
827
|
+
cancel_btn.pack(side='left', padx=5)
|
|
828
|
+
|
|
829
|
+
# Make modal
|
|
830
|
+
self.transient(parent)
|
|
831
|
+
self.grab_set()
|
|
832
|
+
|
|
833
|
+
def confirm(self):
|
|
834
|
+
"""Confirm and return notes."""
|
|
835
|
+
self.result = self.notes_text.get("1.0", "end-1c").strip()
|
|
836
|
+
self.destroy()
|
|
837
|
+
|
|
838
|
+
def cancel(self):
|
|
839
|
+
"""Cancel."""
|
|
840
|
+
self.result = None
|
|
841
|
+
self.destroy()
|