writer 0.8.3rc4__py3-none-any.whl → 1.25.1rc1__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.
- writer/__init__.py +1 -1
- writer/abstract.py +1 -1
- writer/{ai.py → ai/__init__.py} +867 -163
- writer/app_runner.py +596 -241
- writer/app_templates/default/.wf/components-blueprints_blueprint-0-0decp3w5erhvl0nw.jsonl +11 -0
- writer/app_templates/default/.wf/components-blueprints_root.jsonl +1 -0
- writer/app_templates/default/.wf/components-page-0-c0f99a9e-5004-4e75-a6c6-36f17490b134.jsonl +27 -0
- writer/app_templates/default/.wf/components-root.jsonl +1 -0
- writer/app_templates/default/.wf/components-workflows_root.jsonl +1 -0
- writer/app_templates/default/.wf/components-workflows_workflow-0-lfltcky7l1fsm6j2.jsonl +1 -0
- writer/app_templates/default/.wf/metadata.json +3 -0
- writer/app_templates/default/README.md +3 -0
- writer/app_templates/default/main.py +16 -0
- writer/app_templates/default/requirements.txt +1 -0
- writer/app_templates/default/static/README.md +8 -0
- writer/app_templates/default/static/agent_builder_demo.png +0 -0
- writer/app_templates/default/static/favicon.png +0 -0
- writer/app_templates/hello/.wf/components-blueprints_blueprint-0-t84xyhxau9ej3823.jsonl +18 -0
- writer/app_templates/hello/.wf/components-blueprints_root.jsonl +1 -0
- writer/app_templates/hello/.wf/components-page-0-c0f99a9e-5004-4e75-a6c6-36f17490b134.jsonl +15 -0
- writer/app_templates/hello/.wf/components-root.jsonl +1 -0
- writer/app_templates/hello/.wf/metadata.json +3 -0
- writer/app_templates/hello/main.py +16 -0
- writer/app_templates/hello/static/README.md +8 -0
- writer/app_templates/hello/static/favicon.png +0 -0
- writer/app_templates/hello/static/welcome.svg +40 -0
- writer/auth.py +7 -2
- writer/autogen.py +352 -0
- writer/blocks/__init__.py +51 -17
- writer/blocks/addtostatelist.py +10 -9
- writer/blocks/apitrigger.py +45 -0
- writer/blocks/base_block.py +332 -21
- writer/blocks/base_trigger.py +14 -0
- writer/blocks/calleventhandler.py +39 -35
- writer/blocks/changepage.py +48 -0
- writer/blocks/code.py +102 -0
- writer/blocks/crontrigger.py +49 -0
- writer/blocks/foreach.py +70 -53
- writer/blocks/httprequest.py +112 -99
- writer/blocks/ifelse.py +71 -0
- writer/blocks/logmessage.py +34 -39
- writer/blocks/parsejson.py +30 -29
- writer/blocks/returnvalue.py +7 -7
- writer/blocks/runblueprint.py +63 -0
- writer/blocks/setstate.py +43 -33
- writer/blocks/sharedblueprint.py +86 -0
- writer/blocks/uieventtrigger.py +49 -0
- writer/blocks/writeraddchatmessage.py +50 -12
- writer/blocks/writeraddtokg.py +38 -11
- writer/blocks/writeraskkg.py +123 -0
- writer/blocks/writerchat.py +80 -61
- writer/blocks/writerchatreply.py +279 -0
- writer/blocks/writerchatreplywithtoolconfig.py +393 -0
- writer/blocks/writerclassification.py +78 -39
- writer/blocks/writercompletion.py +49 -44
- writer/blocks/writerfileapi.py +85 -0
- writer/blocks/writerinitchat.py +24 -12
- writer/blocks/writerkeyvaluestorage.py +106 -0
- writer/blocks/writernocodeapp.py +35 -37
- writer/blocks/writerparsepdf.py +73 -0
- writer/blocks/writerstructuredoutput.py +105 -0
- writer/blocks/writertoolcalling.py +251 -0
- writer/blocks/writervision.py +141 -0
- writer/blocks/writerwebsearch.py +175 -0
- writer/blueprints.py +839 -0
- writer/command_line.py +52 -16
- writer/core.py +562 -290
- writer/core_ui.py +6 -2
- writer/evaluator.py +98 -46
- writer/journal.py +227 -0
- writer/keyvalue_storage.py +93 -0
- writer/logs.py +277 -0
- writer/serve.py +625 -327
- writer/ss_types.py +101 -12
- writer/static/assets/Arrow.dom-GBJpMYQS.js +1 -0
- writer/static/assets/BaseMarkdown-Wrvby5J8.js +1 -0
- writer/static/assets/BlueprintToolbar-BuXNRxWT.js +1 -0
- writer/static/assets/BlueprintToolbar-wpfX0jo_.css +1 -0
- writer/static/assets/BuilderApp-PTOI76jZ.js +8 -0
- writer/static/assets/BuilderApp-WimUfNZr.css +1 -0
- writer/static/assets/BuilderApplicationSelect-DXzy4e_h.js +7 -0
- writer/static/assets/BuilderApplicationSelect-XaM1D5fv.css +1 -0
- writer/static/assets/BuilderBlueprintLibraryPanel-Ckrhknlh.css +1 -0
- writer/static/assets/BuilderBlueprintLibraryPanel-DBDzhTlc.js +1 -0
- writer/static/assets/BuilderEmbeddedCodeEditor-B0bcjlhk.css +1 -0
- writer/static/assets/BuilderEmbeddedCodeEditor-Dn7eDICN.js +726 -0
- writer/static/assets/BuilderGraphSelect-C-LRsO8W.js +7 -0
- writer/static/assets/BuilderGraphSelect-D7B61d5s.css +1 -0
- writer/static/assets/BuilderInsertionLabel-BhyL9wgn.js +1 -0
- writer/static/assets/BuilderInsertionLabel-_YS5WPfq.css +1 -0
- writer/static/assets/BuilderInsertionOverlay-D2XS0ij9.css +1 -0
- writer/static/assets/BuilderInsertionOverlay-MkAIVruY.js +1 -0
- writer/static/assets/BuilderJournal-A0LcEwGI.js +7 -0
- writer/static/assets/BuilderJournal-DHv3Pvvm.css +1 -0
- writer/static/assets/BuilderModelSelect-CdSo_sih.js +7 -0
- writer/static/assets/BuilderModelSelect-Dc4IPLp2.css +1 -0
- writer/static/assets/BuilderSettings-BDwZBveu.js +16 -0
- writer/static/assets/BuilderSettings-lZkOXEYw.css +1 -0
- writer/static/assets/BuilderSettingsArtifactAPITriggerDetails-3O6jKBXD.js +4 -0
- writer/static/assets/BuilderSettingsArtifactAPITriggerDetails-DnX66iRg.css +1 -0
- writer/static/assets/BuilderSettingsDeploySharedBlueprint-BR_3ptsd.js +1 -0
- writer/static/assets/BuilderSettingsDeploySharedBlueprint-KJTl8gxP.css +1 -0
- writer/static/assets/BuilderSettingsHandlers-CBtEQFSo.js +1 -0
- writer/static/assets/BuilderSettingsHandlers-DJPeASfz.css +1 -0
- writer/static/assets/BuilderSidebarComponentTree-DLltgas5.js +1 -0
- writer/static/assets/BuilderSidebarComponentTree-DYu1F793.css +1 -0
- writer/static/assets/BuilderSidebarToolkit-CApZNTAq.js +7 -0
- writer/static/assets/BuilderSidebarToolkit-CwqbjRv8.css +1 -0
- writer/static/assets/BuilderTemplateEditor-CYSDeWgV.css +1 -0
- writer/static/assets/BuilderTemplateEditor-DnRDRcA0.js +87 -0
- writer/static/assets/BuilderVault-2vGoV0sx.js +1 -0
- writer/static/assets/BuilderVault-Cx6oQSES.css +1 -0
- writer/static/assets/ComponentRenderer-72hqvEvI.css +1 -0
- writer/static/assets/ComponentRenderer-D4Pj1i3s.js +1 -0
- writer/static/assets/SharedCopyClipboardButton-BipJKGtz.css +1 -0
- writer/static/assets/SharedCopyClipboardButton-DNI9kLe6.js +1 -0
- writer/static/assets/WdsCheckbox-DKvpPA4D.css +1 -0
- writer/static/assets/WdsCheckbox-edQcn1cf.js +1 -0
- writer/static/assets/WdsDropdownMenu-CzzPN9Wg.css +1 -0
- writer/static/assets/WdsDropdownMenu-DQnrRBNV.js +1 -0
- writer/static/assets/WdsFieldWrapper-Cmufx5Nj.js +1 -0
- writer/static/assets/WdsFieldWrapper-CsemOh8D.css +1 -0
- writer/static/assets/WdsTabs-DKj7BqI0.css +1 -0
- writer/static/assets/WdsTabs-DcfY_zn5.js +1 -0
- writer/static/assets/abap-D8nrxEjS.js +6 -0
- writer/static/assets/apex-BrXDlLUW.js +6 -0
- writer/static/assets/art-paper-D70v1WMA.svg +180 -0
- writer/static/assets/azcli-CElzELwZ.js +6 -0
- writer/static/assets/bat-CUsyEhik.js +6 -0
- writer/static/assets/bicep-BtxyJn6H.js +7 -0
- writer/static/assets/cameligo-ClBCoF8h.js +6 -0
- writer/static/assets/clojure-B9TqLHAk.js +6 -0
- writer/static/assets/codicon-BA2IlpFX.ttf +0 -0
- writer/static/assets/coffee-DYsfeylR.js +6 -0
- writer/static/assets/cpp-VVGvvgir.js +6 -0
- writer/static/assets/csharp-Z6z2stHy.js +6 -0
- writer/static/assets/csp-DgZoLDI1.js +6 -0
- writer/static/assets/css-KqQ96-gC.js +8 -0
- writer/static/assets/css.worker-DvNUQFd1.js +84 -0
- writer/static/assets/cssMode-BYq4oZGq.js +9 -0
- writer/static/assets/cypher-CYoSlgTu.js +6 -0
- writer/static/assets/dart-BGDl7St1.js +6 -0
- writer/static/assets/dockerfile-CuCtxA7T.js +6 -0
- writer/static/assets/ecl-BCTFAUpS.js +6 -0
- writer/static/assets/editor.worker-BVwmgLrR.js +11 -0
- writer/static/assets/elixir-C7hRTYZ9.js +6 -0
- writer/static/assets/flow9-Bi_qi707.js +6 -0
- writer/static/assets/freemarker2-CnNourkO.js +8 -0
- writer/static/assets/fsharp-CxaaEKKi.js +6 -0
- writer/static/assets/go-DUImKuGY.js +6 -0
- writer/static/assets/graphql-D5sGVkLV.js +6 -0
- writer/static/assets/handlebars-Bm22yapJ.js +6 -0
- writer/static/assets/hcl-zD_CCkZ1.js +6 -0
- writer/static/assets/html-CAKAfoZF.js +6 -0
- writer/static/assets/html.worker-BJMlcbMU.js +458 -0
- writer/static/assets/htmlMode-BGZ97n-V.js +9 -0
- writer/static/assets/index-5u5REPT4.js +16 -0
- writer/static/assets/index-BKNuk68o.css +1 -0
- writer/static/assets/index-BQNXU3IR.js +17 -0
- writer/static/assets/index-BQr1pfrb.js +1 -0
- writer/static/assets/index-DHXAd5Yn.js +4 -0
- writer/static/assets/index-Zki-pfO-.js +8525 -0
- writer/static/assets/index.esm-B1ZQtduY.js +17 -0
- writer/static/assets/ini-8kKHd4ZL.js +6 -0
- writer/static/assets/java-De1axCfe.js +6 -0
- writer/static/assets/javascript-X1f02eyK.js +6 -0
- writer/static/assets/json.worker-BwvX8PuZ.js +42 -0
- writer/static/assets/jsonMode-hT0bNgT8.js +11 -0
- writer/static/assets/julia-D3ApGBxz.js +6 -0
- writer/static/assets/kotlin-GbSrCElU.js +6 -0
- writer/static/assets/less-DNUaDNdz.js +7 -0
- writer/static/assets/lexon-Bg9QKxBu.js +6 -0
- writer/static/assets/liquid-KmCCiJw2.js +6 -0
- writer/static/assets/lua-Crkvc3mc.js +6 -0
- writer/static/assets/m3-DsrzVyM1.js +6 -0
- writer/static/assets/mapbox-gl-C0cyFYYW.js +2329 -0
- writer/static/assets/markdown-CY5IOZuu.js +6 -0
- writer/static/assets/marked.esm-273vDTCT.js +45 -0
- writer/static/assets/mdx-DtRFauUw.js +6 -0
- writer/static/assets/mips-BE8RsGBA.js +6 -0
- writer/static/assets/msdax-N5ajIiFQ.js +6 -0
- writer/static/assets/mysql-DRxbB97D.js +6 -0
- writer/static/assets/objective-c-BHUZy23s.js +6 -0
- writer/static/assets/pascal-BemVzBTY.js +6 -0
- writer/static/assets/pascaligo-BACCcnx_.js +6 -0
- writer/static/assets/pdf-B6-yWJ-Y.js +12 -0
- writer/static/assets/pdf.worker.min-CyUfim15.mjs +21 -0
- writer/static/assets/perl-CuU66Ptk.js +6 -0
- writer/static/assets/pgsql-CQ6TMH2r.js +6 -0
- writer/static/assets/php-BvyzZa65.js +6 -0
- writer/static/assets/pla-DrIuu9u1.js +6 -0
- writer/static/assets/plotly.min-DutuuatZ.js +4030 -0
- writer/static/assets/poppins-latin-300-italic-4WBEAciR.woff +0 -0
- writer/static/assets/poppins-latin-300-italic-EWCPeN2Y.woff2 +0 -0
- writer/static/assets/poppins-latin-300-normal-DCNuMXUj.woff +0 -0
- writer/static/assets/poppins-latin-300-normal-Dku2WoCh.woff2 +0 -0
- writer/static/assets/poppins-latin-400-italic-B4GYq972.woff2 +0 -0
- writer/static/assets/poppins-latin-400-italic-BPejoDS-.woff +0 -0
- writer/static/assets/poppins-latin-400-normal-BOb3E3N0.woff +0 -0
- writer/static/assets/poppins-latin-400-normal-cpxAROuN.woff2 +0 -0
- writer/static/assets/poppins-latin-500-italic-Ce_qjtl5.woff +0 -0
- writer/static/assets/poppins-latin-500-italic-o28Otv0U.woff2 +0 -0
- writer/static/assets/poppins-latin-500-normal-C8OXljZJ.woff2 +0 -0
- writer/static/assets/poppins-latin-500-normal-DGXqpDMm.woff +0 -0
- writer/static/assets/poppins-latin-600-italic-BhOZippK.woff +0 -0
- writer/static/assets/poppins-latin-600-italic-CZ4wqKBi.woff2 +0 -0
- writer/static/assets/poppins-latin-600-normal-BJdTmd5m.woff +0 -0
- writer/static/assets/poppins-latin-600-normal-zEkxB9Mr.woff2 +0 -0
- writer/static/assets/poppins-latin-700-italic-CW91C-LJ.woff +0 -0
- writer/static/assets/poppins-latin-700-italic-RKf6esGj.woff2 +0 -0
- writer/static/assets/poppins-latin-700-normal-BVuQR_eA.woff +0 -0
- writer/static/assets/poppins-latin-700-normal-Qrb0O0WB.woff2 +0 -0
- writer/static/assets/poppins-latin-ext-300-italic-CBzyU4Pf.woff +0 -0
- writer/static/assets/poppins-latin-ext-300-italic-DdDvTq5-.woff2 +0 -0
- writer/static/assets/poppins-latin-ext-300-normal-7Zg2msWE.woff2 +0 -0
- writer/static/assets/poppins-latin-ext-300-normal-C9p7gvmA.woff +0 -0
- writer/static/assets/poppins-latin-ext-400-italic-BiCGV3eO.woff2 +0 -0
- writer/static/assets/poppins-latin-ext-400-italic-gsPYOGqV.woff +0 -0
- writer/static/assets/poppins-latin-ext-400-normal-CIpeJEZw.woff2 +0 -0
- writer/static/assets/poppins-latin-ext-400-normal-Ce_uWq1Z.woff +0 -0
- writer/static/assets/poppins-latin-ext-500-italic-CwrTHwbn.woff2 +0 -0
- writer/static/assets/poppins-latin-ext-500-italic-jdc8Bv4M.woff +0 -0
- writer/static/assets/poppins-latin-ext-500-normal-Bl1-S02S.woff +0 -0
- writer/static/assets/poppins-latin-ext-500-normal-H4Q0z8D2.woff2 +0 -0
- writer/static/assets/poppins-latin-ext-600-italic-BqeDa496.woff2 +0 -0
- writer/static/assets/poppins-latin-ext-600-italic-C7MQPb_A.woff +0 -0
- writer/static/assets/poppins-latin-ext-600-normal-Cn4C8475.woff2 +0 -0
- writer/static/assets/poppins-latin-ext-600-normal-DB6FJURc.woff +0 -0
- writer/static/assets/poppins-latin-ext-700-italic-BAdhB_WS.woff2 +0 -0
- writer/static/assets/poppins-latin-ext-700-italic-WKTwQMp8.woff +0 -0
- writer/static/assets/poppins-latin-ext-700-normal-CE2WFKmF.woff +0 -0
- writer/static/assets/poppins-latin-ext-700-normal-DDaViAzG.woff2 +0 -0
- writer/static/assets/postiats-BR_hrfni.js +6 -0
- writer/static/assets/powerquery-CKDUeRmd.js +6 -0
- writer/static/assets/powershell-Dsa4rhA_.js +6 -0
- writer/static/assets/protobuf-CGsvhooB.js +7 -0
- writer/static/assets/pug-D2p3uOX2.js +6 -0
- writer/static/assets/python-DVhxg746.js +6 -0
- writer/static/assets/qsharp-B7F3HtPF.js +6 -0
- writer/static/assets/r-3aLoi2fs.js +6 -0
- writer/static/assets/razor-DR5Ns_BC.js +6 -0
- writer/static/assets/redis-jqFeRM5s.js +6 -0
- writer/static/assets/redshift-BriwQgXR.js +6 -0
- writer/static/assets/restructuredtext-hbBFZ0w9.js +6 -0
- writer/static/assets/ruby-ByThyB2Q.js +6 -0
- writer/static/assets/rust-DIEZMp5R.js +6 -0
- writer/static/assets/sb-C6Gjjw_x.js +6 -0
- writer/static/assets/scala-DZNw3jJB.js +6 -0
- writer/static/assets/scheme-55eqh71t.js +6 -0
- writer/static/assets/scss-D-OVkc4F.js +8 -0
- writer/static/assets/serialization-DJC7NP0N.js +20 -0
- writer/static/assets/shell-DSpi8_qN.js +6 -0
- writer/static/assets/solidity-BHddiNFS.js +6 -0
- writer/static/assets/sophia-D6taVZFb.js +6 -0
- writer/static/assets/sparql-LA0C7mUc.js +6 -0
- writer/static/assets/sql-C3-3IcFM.js +6 -0
- writer/static/assets/st-C4g7059C.js +6 -0
- writer/static/assets/swift-DNI1vH3h.js +8 -0
- writer/static/assets/systemverilog-DL_FVbcQ.js +6 -0
- writer/static/assets/tcl-DVJXmIwd.js +6 -0
- writer/static/assets/ts.worker-CwG1rUES.js +37021 -0
- writer/static/assets/tsMode-BNUEZzir.js +16 -0
- writer/static/assets/twig-BVWDLtw5.js +6 -0
- writer/static/assets/typescript-CRVt7Hx0.js +6 -0
- writer/static/assets/useBlueprintRun-C00bCxh-.js +1 -0
- writer/static/assets/useKeyValueEditor-nDmI7cTJ.js +1 -0
- writer/static/assets/useListResources-DLkZhRSJ.js +1 -0
- writer/static/assets/vb-Btz91-7U.js +6 -0
- writer/static/assets/vega-embed.module-SNP5iNdJ.js +201 -0
- writer/static/assets/wgsl-D8V_buCG.js +303 -0
- writer/static/assets/xml-C_6-t1tb.js +6 -0
- writer/static/assets/yaml-DIw8G7jk.js +6 -0
- writer/static/components/annotatedtext.svg +4 -0
- writer/static/components/avatar.svg +4 -0
- writer/static/components/blueprints_addtostatelist.svg +4 -0
- writer/static/components/blueprints_apitrigger.svg +4 -0
- writer/static/components/blueprints_calleventhandler.svg +9 -0
- writer/static/components/blueprints_category_Logic.svg +4 -0
- writer/static/components/blueprints_category_Other.svg +4 -0
- writer/static/components/blueprints_category_Triggers.svg +4 -0
- writer/static/components/blueprints_category_Writer.svg +25 -0
- writer/static/components/blueprints_code.svg +9 -0
- writer/static/components/blueprints_crontrigger.svg +6 -0
- writer/static/components/blueprints_foreach.svg +4 -0
- writer/static/components/blueprints_httprequest.svg +11 -0
- writer/static/components/blueprints_logmessage.svg +11 -0
- writer/static/components/blueprints_parsejson.svg +4 -0
- writer/static/components/blueprints_returnvalue.svg +4 -0
- writer/static/components/blueprints_runblueprint.svg +4 -0
- writer/static/components/blueprints_setstate.svg +4 -0
- writer/static/components/blueprints_uieventtrigger.svg +4 -0
- writer/static/components/blueprints_writeraddchatmessage.svg +19 -0
- writer/static/components/blueprints_writeraddtokg.svg +19 -0
- writer/static/components/blueprints_writerchat.svg +11 -0
- writer/static/components/blueprints_writerchatreply.svg +19 -0
- writer/static/components/blueprints_writerclassification.svg +24 -0
- writer/static/components/blueprints_writercompletion.svg +14 -0
- writer/static/components/blueprints_writerinitchat.svg +11 -0
- writer/static/components/blueprints_writernocodeapp.svg +14 -0
- writer/static/components/button.svg +4 -0
- writer/static/components/category_Content.svg +4 -0
- writer/static/components/category_Embed.svg +4 -0
- writer/static/components/category_Input.svg +5 -0
- writer/static/components/category_Layout.svg +9 -0
- writer/static/components/category_Other.svg +4 -0
- writer/static/components/chatbot.svg +4 -0
- writer/static/components/checkboxinput.svg +4 -0
- writer/static/components/colorinput.svg +11 -0
- writer/static/components/column.svg +4 -0
- writer/static/components/columns.svg +4 -0
- writer/static/components/dataframe.svg +4 -0
- writer/static/components/dateinput.svg +4 -0
- writer/static/components/dropdowninput.svg +5 -0
- writer/static/components/fileinput.svg +4 -0
- writer/static/components/googlemaps.svg +4 -0
- writer/static/components/header.svg +4 -0
- writer/static/components/heading.svg +9 -0
- writer/static/components/horizontalstack.svg +4 -0
- writer/static/components/html.svg +9 -0
- writer/static/components/icon.svg +4 -0
- writer/static/components/iframe.svg +4 -0
- writer/static/components/image.svg +11 -0
- writer/static/components/jsonviewer.svg +4 -0
- writer/static/components/link.svg +12 -0
- writer/static/components/mapbox.svg +4 -0
- writer/static/components/message.svg +4 -0
- writer/static/components/metric.svg +4 -0
- writer/static/components/multiselectinput.svg +4 -0
- writer/static/components/numberinput.svg +4 -0
- writer/static/components/page.svg +50 -0
- writer/static/components/pagination.svg +4 -0
- writer/static/components/pdf.svg +4 -0
- writer/static/components/plotlygraph.svg +7 -0
- writer/static/components/progressbar.svg +5 -0
- writer/static/components/radioinput.svg +4 -0
- writer/static/components/rangeinput.svg +4 -0
- writer/static/components/ratinginput.svg +4 -0
- writer/static/components/repeater.svg +4 -0
- writer/static/components/reuse.svg +4 -0
- writer/static/components/section.svg +4 -0
- writer/static/components/selectinput.svg +5 -0
- writer/static/components/separator.svg +4 -0
- writer/static/components/sidebar.svg +4 -0
- writer/static/components/sliderinput.svg +4 -0
- writer/static/components/step.svg +4 -0
- writer/static/components/steps.svg +4 -0
- writer/static/components/switchinput.svg +4 -0
- writer/static/components/tab.svg +4 -0
- writer/static/components/tabs.svg +4 -0
- writer/static/components/tags.svg +11 -0
- writer/static/components/text.svg +4 -0
- writer/static/components/textareainput.svg +11 -0
- writer/static/components/textinput.svg +4 -0
- writer/static/components/timeinput.svg +4 -0
- writer/static/components/timer.svg +4 -0
- writer/static/components/vegalitechart.svg +7 -0
- writer/static/components/videoplayer.svg +11 -0
- writer/static/components/webcamcapture.svg +4 -0
- writer/static/favicon.png +0 -0
- writer/static/index.html +84 -0
- writer/static/status/cancelled.svg +5 -0
- writer/static/status/error.svg +5 -0
- writer/static/status/skipped.svg +4 -0
- writer/static/status/stopped.svg +4 -0
- writer/static/status/success.svg +4 -0
- writer/sync.py +431 -0
- writer/ui.py +2268 -0
- writer/vault.py +48 -0
- writer/wf_project.py +90 -66
- writer-1.25.1rc1.dist-info/METADATA +92 -0
- writer-1.25.1rc1.dist-info/RECORD +382 -0
- {writer-0.8.3rc4.dist-info → writer-1.25.1rc1.dist-info}/WHEEL +1 -1
- writer/blocks/runworkflow.py +0 -59
- writer/workflows.py +0 -183
- writer-0.8.3rc4.dist-info/METADATA +0 -117
- writer-0.8.3rc4.dist-info/RECORD +0 -44
- {writer-0.8.3rc4.dist-info → writer-1.25.1rc1.dist-info}/entry_points.txt +0 -0
- {writer-0.8.3rc4.dist-info → writer-1.25.1rc1.dist-info/licenses}/LICENSE.txt +0 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg width="19" height="19" viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect x="0.516602" y="0.193359" width="18" height="18" rx="4" fill="#333333"/>
|
|
3
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.5163 5.19352H5.51628V13.1935H13.5163V5.19352ZM4.84961 4.52686V13.8602H14.1829V4.52686H4.84961Z" fill="white"/>
|
|
4
|
+
<path d="M10.8496 11.1935V9.19352H11.5163V11.1935H10.8496Z" fill="white"/>
|
|
5
|
+
<path d="M7.51628 11.1935V8.52686H8.18294V11.1935H7.51628Z" fill="white"/>
|
|
6
|
+
<path d="M9.18294 11.1935V7.19352H9.84961L9.84961 11.1935H9.18294Z" fill="white"/>
|
|
7
|
+
</svg>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<svg width="19" height="18" viewBox="0 0 19 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect x="0.815918" width="18" height="18" rx="4" fill="#F5F5F9"/>
|
|
3
|
+
<g clip-path="url(#clip0_4193_236611)">
|
|
4
|
+
<path d="M15.0659 4.5V13.5H4.56592V4.5H15.0659ZM5.23291 5.16699V12.833H14.3989V5.16699H5.23291ZM13.2749 8.97656L7.48291 11.873V6.49512L13.2749 8.97656ZM8.1499 10.7939L11.6909 9.02344L8.1499 7.50586V10.7939Z" fill="black"/>
|
|
5
|
+
</g>
|
|
6
|
+
<defs>
|
|
7
|
+
<clipPath id="clip0_4193_236611">
|
|
8
|
+
<rect width="12" height="12" fill="white" transform="translate(3.81592 3)"/>
|
|
9
|
+
</clipPath>
|
|
10
|
+
</defs>
|
|
11
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="19" height="18" viewBox="0 0 19 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect x="0.815918" width="18" height="18" rx="4" fill="#F5F5F9"/>
|
|
3
|
+
<path d="M11.8159 5.6665C12.184 5.66668 12.4819 5.96541 12.4819 6.3335V8.33252L14.4819 6.3335V11.6665L12.4819 9.6665V11.6665C12.4819 12.0346 12.184 12.3333 11.8159 12.3335H5.81592C5.44773 12.3335 5.14893 12.0347 5.14893 11.6665V6.3335C5.14893 5.96531 5.44773 5.6665 5.81592 5.6665H11.8159ZM5.81592 11.6665H11.8159V9.00049L11.8149 8.99951L11.8159 8.99854V6.3335H5.81592V11.6665Z" fill="black"/>
|
|
4
|
+
</svg>
|
|
Binary file
|
writer/static/index.html
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" prefix="og: https://ogp.me/ns#">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/png" href="./static/favicon.png?2" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>Writer</title>
|
|
8
|
+
<!-- {{ meta }} -->
|
|
9
|
+
<!-- {{ opengraph_tags }} -->
|
|
10
|
+
<style>@layer wf {
|
|
11
|
+
* {
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
margin: 0;
|
|
14
|
+
padding: 0;
|
|
15
|
+
font-family: inherit;
|
|
16
|
+
color: currentColor;
|
|
17
|
+
-webkit-font-smoothing: antialiased;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
body {
|
|
21
|
+
font-size: 13px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
#app {
|
|
25
|
+
isolation: isolate;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#app > * {
|
|
29
|
+
min-height: 100vh;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
#loading_L1 {
|
|
33
|
+
width: 100%;
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
justify-content: center;
|
|
37
|
+
position: relative;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#loading_L2 {
|
|
41
|
+
position: absolute;
|
|
42
|
+
background: conic-gradient(from 180deg at 50% 50%, #FFF 0deg, #A95EF8 360deg);
|
|
43
|
+
width: 113px;
|
|
44
|
+
height: 113px;
|
|
45
|
+
top: calc(50% - 56.5px);
|
|
46
|
+
left: calc(50% - 56.5px);
|
|
47
|
+
border-radius: 50%;
|
|
48
|
+
animation: logoAnimation 1s infinite linear;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#loading_L1 svg {
|
|
52
|
+
top: calc(50% - 49.5px);
|
|
53
|
+
left: calc(50% - 49px);
|
|
54
|
+
position: absolute;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@keyframes logoAnimation {
|
|
58
|
+
0% {
|
|
59
|
+
transform: rotate(0deg);
|
|
60
|
+
}
|
|
61
|
+
100% {
|
|
62
|
+
transform: rotate(360deg);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
</style>
|
|
67
|
+
<script type="module" crossorigin src="./assets/index-Zki-pfO-.js"></script>
|
|
68
|
+
<link rel="stylesheet" crossorigin href="./assets/index-BKNuk68o.css">
|
|
69
|
+
</head>
|
|
70
|
+
<body>
|
|
71
|
+
<div id="app">
|
|
72
|
+
<div id="loading_L1">
|
|
73
|
+
<div id="loading_L2">
|
|
74
|
+
</div>
|
|
75
|
+
<svg width="98" height="99" viewBox="0 0 98 99" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
76
|
+
<rect y="1" width="98" height="98" rx="49" fill="black"/>
|
|
77
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M69.4549 60.0928L76.0021 33.0802H66.897L66.897 33.0802H62.9077L69.4549 60.0928Z" fill="white"/>
|
|
78
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M51.9127 33.0802H42.8076L51.9127 70.6464H55.959L55.959 70.6464H65.0641L55.959 33.0802H51.9128L51.9127 33.0802Z" fill="white"/>
|
|
79
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M35.8587 33.0803H26.7535L26.7537 33.081H22.7073L31.8124 70.6471H40.9176L40.9174 70.6465H44.9638L35.8587 33.0803Z" fill="white"/>
|
|
80
|
+
</svg>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</body>
|
|
84
|
+
</html>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="24" height="24" rx="4" fill="#d2d4d7"/>
|
|
3
|
+
<rect x="3" y="3" width="18" height="18" rx="9" fill="#d2d4d7"/>
|
|
4
|
+
<path d="M9.00002 9L15 15M15 9.00004L9 15" stroke="black" stroke-width="1.08"/>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="24" height="24" rx="4" fill="#FFCFC2"/>
|
|
3
|
+
<rect x="3" y="3" width="18" height="18" rx="9" fill="#FFCFC2"/>
|
|
4
|
+
<path d="M9.00002 9L15 15M15 9.00004L9 15" stroke="black" stroke-width="1.08"/>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="24" height="24" rx="4" fill="#d2d4d7"/>
|
|
3
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5002 14.1L8.4002 12L7.7002 12.7L10.5002 15.5L16.5002 9.49999L15.8002 8.79999L10.5002 14.1V14.1Z" fill="black"/>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect y="0" width="24" height="24" rx="4" fill="#E4E7ED"/>
|
|
3
|
+
<path d="M16.6667 6.5H7.33333C6.59695 6.5 6 7.09695 6 7.83333V17.1667C6 17.903 6.59695 18.5 7.33333 18.5H16.6667C17.403 18.5 18 17.903 18 17.1667V7.83333C18 7.09695 17.403 6.5 16.6667 6.5Z" stroke="#828282" stroke-linecap="round" stroke-linejoin="round"/>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="24" height="24" rx="4" fill="#A9F9E1"/>
|
|
3
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5002 14.1L8.4002 12L7.7002 12.7L10.5002 15.5L16.5002 9.49999L15.8002 8.79999L10.5002 14.1V14.1Z" fill="black"/>
|
|
4
|
+
</svg>
|
writer/sync.py
ADDED
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import asyncio
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
import stat
|
|
6
|
+
import tempfile
|
|
7
|
+
import threading
|
|
8
|
+
import time
|
|
9
|
+
from collections import defaultdict
|
|
10
|
+
from datetime import datetime, timedelta
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from threading import Timer
|
|
13
|
+
from typing import Any, Dict, List, Optional, Set, Union
|
|
14
|
+
|
|
15
|
+
from watchdog.events import FileSystemEvent, FileSystemEventHandler
|
|
16
|
+
from watchdog.observers import Observer
|
|
17
|
+
from watchdog.observers.api import BaseObserver
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class OperationQueues:
|
|
21
|
+
def __init__(self) -> None:
|
|
22
|
+
self.files_to_update: Dict[str, str] = {} # Maps dest paths to their source paths
|
|
23
|
+
self.files_to_delete: Set[str] = set()
|
|
24
|
+
self.dirs_to_create: Set[str] = set()
|
|
25
|
+
self.dirs_to_delete: Set[str] = set()
|
|
26
|
+
self.lock = threading.Lock()
|
|
27
|
+
|
|
28
|
+
def has_pending_operations(self) -> bool:
|
|
29
|
+
with self.lock:
|
|
30
|
+
return (
|
|
31
|
+
len(self.files_to_update) > 0 or
|
|
32
|
+
len(self.files_to_delete) > 0 or
|
|
33
|
+
len(self.dirs_to_create) > 0 or
|
|
34
|
+
len(self.dirs_to_delete) > 0
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def clear_all(self) -> None:
|
|
38
|
+
with self.lock:
|
|
39
|
+
self.files_to_update.clear()
|
|
40
|
+
self.files_to_delete.clear()
|
|
41
|
+
self.dirs_to_create.clear()
|
|
42
|
+
self.dirs_to_delete.clear()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class FolderSyncHandler(FileSystemEventHandler):
|
|
46
|
+
def __init__(self, src_dir: str, dest_dir: str, verbose: bool, operation_queues: OperationQueues) -> None:
|
|
47
|
+
self.src_dir = Path(src_dir).resolve()
|
|
48
|
+
self.dest_dir = Path(dest_dir).resolve()
|
|
49
|
+
self.verbose = verbose
|
|
50
|
+
self.operation_queues = operation_queues
|
|
51
|
+
|
|
52
|
+
def log(self, *args) -> None:
|
|
53
|
+
if self.verbose:
|
|
54
|
+
print(*args)
|
|
55
|
+
|
|
56
|
+
def rel(self, path: str) -> str:
|
|
57
|
+
return str(Path(path).relative_to(self.src_dir))
|
|
58
|
+
|
|
59
|
+
def dest_path(self, src_path: str) -> str:
|
|
60
|
+
rel_path = self.rel(src_path)
|
|
61
|
+
return str(self.dest_dir / rel_path)
|
|
62
|
+
|
|
63
|
+
def on_created(self, event) -> None:
|
|
64
|
+
if event.is_directory:
|
|
65
|
+
with self.operation_queues.lock:
|
|
66
|
+
self.operation_queues.dirs_to_create.add(event.src_path)
|
|
67
|
+
self.log("Queued dir add:", self.rel(event.src_path))
|
|
68
|
+
else:
|
|
69
|
+
dest = self.dest_path(event.src_path)
|
|
70
|
+
with self.operation_queues.lock:
|
|
71
|
+
self.operation_queues.files_to_update[dest] = event.src_path
|
|
72
|
+
self.log("Queued add:", self.rel(event.src_path))
|
|
73
|
+
|
|
74
|
+
def on_modified(self, event) -> None:
|
|
75
|
+
if not event.is_directory:
|
|
76
|
+
dest = self.dest_path(event.src_path)
|
|
77
|
+
with self.operation_queues.lock:
|
|
78
|
+
self.operation_queues.files_to_update[dest] = event.src_path
|
|
79
|
+
self.log("Queued change:", self.rel(event.src_path))
|
|
80
|
+
|
|
81
|
+
def on_deleted(self, event) -> None:
|
|
82
|
+
if event.is_directory:
|
|
83
|
+
with self.operation_queues.lock:
|
|
84
|
+
self.operation_queues.dirs_to_delete.add(event.src_path)
|
|
85
|
+
self.log("Queued dir remove:", self.rel(event.src_path))
|
|
86
|
+
else:
|
|
87
|
+
dest = self.dest_path(event.src_path)
|
|
88
|
+
with self.operation_queues.lock:
|
|
89
|
+
self.operation_queues.files_to_delete.add(event.src_path)
|
|
90
|
+
# Remove from update queue if present
|
|
91
|
+
self.operation_queues.files_to_update.pop(dest, None)
|
|
92
|
+
self.log("Queued remove:", self.rel(event.src_path))
|
|
93
|
+
|
|
94
|
+
def on_moved(self, event) -> None:
|
|
95
|
+
# Handle moves as delete + create
|
|
96
|
+
self.on_deleted(type('MockEvent', (), {'src_path': event.src_path, 'is_directory': event.is_directory})())
|
|
97
|
+
self.on_created(type('MockEvent', (), {'src_path': event.dest_path, 'is_directory': event.is_directory})())
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class FileBuffering:
|
|
101
|
+
def __init__(self, dest_dir: str, *, src_dir: Optional[str] = None, verbose: bool = False, interval: int = 10) -> None:
|
|
102
|
+
self.path = src_dir if src_dir is not None else tempfile.mkdtemp()
|
|
103
|
+
self.src_dir = Path(self.path).resolve()
|
|
104
|
+
self.dest_dir = Path(dest_dir).resolve()
|
|
105
|
+
self.verbose = verbose
|
|
106
|
+
self.sync_interval = interval
|
|
107
|
+
self.operation_queues = OperationQueues()
|
|
108
|
+
self.is_sync_in_progress = False
|
|
109
|
+
self.observer: Optional[BaseObserver] = None
|
|
110
|
+
self.sync_timer: Optional[Timer] = None
|
|
111
|
+
self.running = False
|
|
112
|
+
self.last_comparison_time: datetime = datetime.now()
|
|
113
|
+
self.check_interval = 10
|
|
114
|
+
self.check_timer: Optional[Timer] = None
|
|
115
|
+
|
|
116
|
+
def log(self, *args) -> None:
|
|
117
|
+
if self.verbose:
|
|
118
|
+
print(*args)
|
|
119
|
+
|
|
120
|
+
def rel(self, path: str) -> str:
|
|
121
|
+
return str(Path(path).relative_to(self.src_dir))
|
|
122
|
+
|
|
123
|
+
def dest_path(self, src_path: str) -> str:
|
|
124
|
+
rel_path = self.rel(src_path)
|
|
125
|
+
return str(self.dest_dir / rel_path)
|
|
126
|
+
|
|
127
|
+
def should_copy(self, src: str, dest: str) -> bool:
|
|
128
|
+
try:
|
|
129
|
+
src_path = Path(src)
|
|
130
|
+
dest_path = Path(dest)
|
|
131
|
+
|
|
132
|
+
if not dest_path.exists():
|
|
133
|
+
return True
|
|
134
|
+
|
|
135
|
+
src_stat = src_path.stat()
|
|
136
|
+
dest_stat = dest_path.stat()
|
|
137
|
+
|
|
138
|
+
# Compare size
|
|
139
|
+
if src_stat.st_size != dest_stat.st_size:
|
|
140
|
+
return True
|
|
141
|
+
|
|
142
|
+
# Compare modification time (with 1 second tolerance)
|
|
143
|
+
if src_stat.st_mtime > dest_stat.st_mtime + 1:
|
|
144
|
+
return True
|
|
145
|
+
|
|
146
|
+
# Compare content
|
|
147
|
+
try:
|
|
148
|
+
with open(src, 'rb') as f1, open(dest, 'rb') as f2:
|
|
149
|
+
return f1.read() != f2.read()
|
|
150
|
+
except Exception:
|
|
151
|
+
return True
|
|
152
|
+
|
|
153
|
+
except Exception:
|
|
154
|
+
return True
|
|
155
|
+
|
|
156
|
+
def ensure_dir(self, path: str) -> None:
|
|
157
|
+
Path(path).mkdir(parents=True, exist_ok=True)
|
|
158
|
+
|
|
159
|
+
def copy_file(self, src: str, dest: str) -> None:
|
|
160
|
+
dest_path = Path(dest)
|
|
161
|
+
dest_path.parent.mkdir(parents=True, exist_ok=True)
|
|
162
|
+
shutil.copyfile(src, dest)
|
|
163
|
+
|
|
164
|
+
def remove_path(self, path: str) -> None:
|
|
165
|
+
path_obj = Path(path)
|
|
166
|
+
if path_obj.exists():
|
|
167
|
+
if path_obj.is_dir():
|
|
168
|
+
shutil.rmtree(path_obj)
|
|
169
|
+
else:
|
|
170
|
+
path_obj.unlink()
|
|
171
|
+
|
|
172
|
+
def init(self) -> None:
|
|
173
|
+
"""Initialize by copying all files from destination to source directory."""
|
|
174
|
+
self.log(f"Initializing: copying files from {self.dest_dir} to {self.src_dir}")
|
|
175
|
+
|
|
176
|
+
# Check if destination directory exists
|
|
177
|
+
if not self.dest_dir.exists():
|
|
178
|
+
self.log(f"Destination directory {self.dest_dir} doesn't exist yet. Nothing to copy.")
|
|
179
|
+
return
|
|
180
|
+
|
|
181
|
+
def copy_recursively(relative_path: str = ""):
|
|
182
|
+
full_dest = self.dest_dir / relative_path
|
|
183
|
+
full_src = self.src_dir / relative_path
|
|
184
|
+
|
|
185
|
+
# Ensure the directory exists in source
|
|
186
|
+
self.ensure_dir(str(full_src))
|
|
187
|
+
|
|
188
|
+
try:
|
|
189
|
+
for entry in full_dest.iterdir():
|
|
190
|
+
rel_path = str(Path(relative_path) / entry.name) if relative_path else entry.name
|
|
191
|
+
dest_path = self.dest_dir / rel_path
|
|
192
|
+
src_path = self.src_dir / rel_path
|
|
193
|
+
|
|
194
|
+
if entry.is_dir():
|
|
195
|
+
copy_recursively(rel_path)
|
|
196
|
+
elif entry.is_file():
|
|
197
|
+
self.copy_file(str(dest_path), str(src_path))
|
|
198
|
+
self.log(f"Copied to source: {rel_path}")
|
|
199
|
+
except PermissionError as e:
|
|
200
|
+
print(f"Permission error accessing {full_dest}: {e}")
|
|
201
|
+
except Exception as e:
|
|
202
|
+
print(f"Error processing {full_dest}: {e}")
|
|
203
|
+
|
|
204
|
+
# Execute the recursive copy
|
|
205
|
+
copy_recursively()
|
|
206
|
+
self.log("Initialization complete. All files copied to temporary directory.")
|
|
207
|
+
|
|
208
|
+
def initial_sync(self) -> None:
|
|
209
|
+
self.log("Starting initial sync...")
|
|
210
|
+
|
|
211
|
+
def sync_recursively(relative: str = ""):
|
|
212
|
+
full_src = self.src_dir / relative
|
|
213
|
+
full_dest = self.dest_dir / relative
|
|
214
|
+
|
|
215
|
+
self.ensure_dir(str(full_dest))
|
|
216
|
+
|
|
217
|
+
try:
|
|
218
|
+
for entry in full_src.iterdir():
|
|
219
|
+
rel_path = str(Path(relative) / entry.name) if relative else entry.name
|
|
220
|
+
src_path = self.src_dir / rel_path
|
|
221
|
+
dst_path = self.dest_dir / rel_path
|
|
222
|
+
|
|
223
|
+
if entry.is_dir():
|
|
224
|
+
sync_recursively(rel_path)
|
|
225
|
+
elif entry.is_file():
|
|
226
|
+
if self.should_copy(str(src_path), str(dst_path)):
|
|
227
|
+
self.copy_file(str(src_path), str(dst_path))
|
|
228
|
+
self.log("Copied:", rel_path)
|
|
229
|
+
else:
|
|
230
|
+
self.log("Skipped (up to date):", rel_path)
|
|
231
|
+
except PermissionError as e:
|
|
232
|
+
print(f"Permission error accessing {full_src}: {e}")
|
|
233
|
+
except Exception as e:
|
|
234
|
+
print(f"Error processing {full_src}: {e}")
|
|
235
|
+
|
|
236
|
+
# Run synchronously since it's the initial sync
|
|
237
|
+
sync_recursively()
|
|
238
|
+
|
|
239
|
+
def process_batched_operations(self) -> None:
|
|
240
|
+
if self.is_sync_in_progress:
|
|
241
|
+
return
|
|
242
|
+
if not self.operation_queues.has_pending_operations():
|
|
243
|
+
return
|
|
244
|
+
|
|
245
|
+
self.is_sync_in_progress = True
|
|
246
|
+
self.log(f"Processing batched operations at {datetime.now().isoformat()}")
|
|
247
|
+
|
|
248
|
+
try:
|
|
249
|
+
with self.operation_queues.lock:
|
|
250
|
+
# Process directory creations first
|
|
251
|
+
if self.operation_queues.dirs_to_create:
|
|
252
|
+
self.log(f"Creating {len(self.operation_queues.dirs_to_create)} directories")
|
|
253
|
+
for dir_path in self.operation_queues.dirs_to_create.copy():
|
|
254
|
+
try:
|
|
255
|
+
self.log("Dir add:", self.rel(dir_path))
|
|
256
|
+
to = self.dest_path(dir_path)
|
|
257
|
+
self.ensure_dir(to)
|
|
258
|
+
except Exception as e:
|
|
259
|
+
print(f"Error creating directory {dir_path}: {e}")
|
|
260
|
+
self.operation_queues.dirs_to_create.clear()
|
|
261
|
+
|
|
262
|
+
# Process file updates
|
|
263
|
+
if self.operation_queues.files_to_update:
|
|
264
|
+
self.log(f"Updating {len(self.operation_queues.files_to_update)} files")
|
|
265
|
+
for dest_path, src_path in self.operation_queues.files_to_update.copy().items():
|
|
266
|
+
try:
|
|
267
|
+
if self.should_copy(src_path, dest_path):
|
|
268
|
+
self.log("File update:", self.rel(src_path))
|
|
269
|
+
self.copy_file(src_path, dest_path)
|
|
270
|
+
except Exception as e:
|
|
271
|
+
print(f"Error updating file {src_path} to {dest_path}: {e}")
|
|
272
|
+
self.operation_queues.files_to_update.clear()
|
|
273
|
+
|
|
274
|
+
# Process file deletions
|
|
275
|
+
if self.operation_queues.files_to_delete:
|
|
276
|
+
self.log(f"Deleting {len(self.operation_queues.files_to_delete)} files")
|
|
277
|
+
for file_path in self.operation_queues.files_to_delete.copy():
|
|
278
|
+
try:
|
|
279
|
+
self.log("File remove:", self.rel(file_path))
|
|
280
|
+
to = self.dest_path(file_path)
|
|
281
|
+
self.remove_path(to)
|
|
282
|
+
except Exception as e:
|
|
283
|
+
print(f"Error deleting file {file_path}: {e}")
|
|
284
|
+
self.operation_queues.files_to_delete.clear()
|
|
285
|
+
|
|
286
|
+
# Process directory deletions last
|
|
287
|
+
if self.operation_queues.dirs_to_delete:
|
|
288
|
+
self.log(f"Deleting {len(self.operation_queues.dirs_to_delete)} directories")
|
|
289
|
+
for dir_path in self.operation_queues.dirs_to_delete.copy():
|
|
290
|
+
try:
|
|
291
|
+
self.log("Dir remove:", self.rel(dir_path))
|
|
292
|
+
to = self.dest_path(dir_path)
|
|
293
|
+
self.remove_path(to)
|
|
294
|
+
except Exception as e:
|
|
295
|
+
print(f"Error deleting directory {dir_path}: {e}")
|
|
296
|
+
self.operation_queues.dirs_to_delete.clear()
|
|
297
|
+
|
|
298
|
+
except Exception as error:
|
|
299
|
+
print(f"Error processing batch: {error}")
|
|
300
|
+
finally:
|
|
301
|
+
self.is_sync_in_progress = False
|
|
302
|
+
|
|
303
|
+
# Compare directories after processing
|
|
304
|
+
self.compare_directories(str(self.src_dir), str(self.dest_dir), self.verbose)
|
|
305
|
+
|
|
306
|
+
def schedule_batch_processing(self) -> None:
|
|
307
|
+
if not self.running:
|
|
308
|
+
return
|
|
309
|
+
|
|
310
|
+
self.process_batched_operations()
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
# Schedule next processing
|
|
314
|
+
self.sync_timer = threading.Timer(self.sync_interval, self.schedule_batch_processing)
|
|
315
|
+
if self.sync_timer: # This check is redundant but satisfies mypy
|
|
316
|
+
self.sync_timer.start()
|
|
317
|
+
|
|
318
|
+
def schedule_sanity_check(self) -> None:
|
|
319
|
+
|
|
320
|
+
if self.last_comparison_time < datetime.now() - timedelta(seconds=5):
|
|
321
|
+
self.compare_directories(str(self.src_dir), str(self.dest_dir), self.verbose)
|
|
322
|
+
|
|
323
|
+
self.check_timer = threading.Timer(self.check_interval, self.schedule_sanity_check)
|
|
324
|
+
if self.check_timer: # This check is redundant but satisfies mypy
|
|
325
|
+
self.check_timer.start()
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def get_all_files(self, directory: str, base_dir: str) -> Dict[str, str]:
|
|
330
|
+
files = {}
|
|
331
|
+
dir_path = Path(directory)
|
|
332
|
+
base_path = Path(base_dir)
|
|
333
|
+
|
|
334
|
+
def traverse_dir(current_path: Path):
|
|
335
|
+
try:
|
|
336
|
+
for entry in current_path.iterdir():
|
|
337
|
+
if entry.is_dir():
|
|
338
|
+
traverse_dir(entry)
|
|
339
|
+
elif entry.is_file():
|
|
340
|
+
relative_path = str(entry.relative_to(base_path))
|
|
341
|
+
files[relative_path] = str(entry)
|
|
342
|
+
except PermissionError:
|
|
343
|
+
pass
|
|
344
|
+
|
|
345
|
+
traverse_dir(dir_path)
|
|
346
|
+
return files
|
|
347
|
+
|
|
348
|
+
def files_have_same_content(self, file1: str, file2: str) -> bool:
|
|
349
|
+
try:
|
|
350
|
+
with open(file1, 'rb') as f1, open(file2, 'rb') as f2:
|
|
351
|
+
return f1.read() == f2.read()
|
|
352
|
+
except Exception as e:
|
|
353
|
+
if self.verbose:
|
|
354
|
+
print(f"Error comparing files {file1} and {file2}: {e}")
|
|
355
|
+
return False
|
|
356
|
+
|
|
357
|
+
def compare_directories(self, dir1: str, dir2: str, verbose: bool = False) -> List[Dict[str, str]]:
|
|
358
|
+
divergent_files: List[Dict[str, str]] = []
|
|
359
|
+
self.last_comparison_time = datetime.now()
|
|
360
|
+
|
|
361
|
+
def log_verbose(*args):
|
|
362
|
+
if verbose:
|
|
363
|
+
print(*args)
|
|
364
|
+
|
|
365
|
+
# Get all files from both directories
|
|
366
|
+
dir1_files = self.get_all_files(dir1, dir1)
|
|
367
|
+
dir2_files = self.get_all_files(dir2, dir2)
|
|
368
|
+
|
|
369
|
+
# Compare files that exist in both directories
|
|
370
|
+
for relative_path, full_path1 in dir1_files.items():
|
|
371
|
+
full_path2 = dir2_files.get(relative_path)
|
|
372
|
+
|
|
373
|
+
if not full_path2:
|
|
374
|
+
# File exists in dir1 but not in dir2
|
|
375
|
+
divergent_files.append({'path': relative_path, 'reason': 'missing in target'})
|
|
376
|
+
dest = self.dest_path(full_path1)
|
|
377
|
+
with self.operation_queues.lock:
|
|
378
|
+
self.operation_queues.files_to_update[dest] = full_path1
|
|
379
|
+
log_verbose(f"File only in source: {relative_path}")
|
|
380
|
+
else:
|
|
381
|
+
# File exists in both, compare content
|
|
382
|
+
if not self.files_have_same_content(full_path1, full_path2):
|
|
383
|
+
divergent_files.append({'path': relative_path, 'reason': 'content differs'})
|
|
384
|
+
dest = self.dest_path(full_path1)
|
|
385
|
+
with self.operation_queues.lock:
|
|
386
|
+
self.operation_queues.files_to_update[dest] = full_path1
|
|
387
|
+
log_verbose(f"Content differs: {relative_path}")
|
|
388
|
+
|
|
389
|
+
# Check for files in dir2 that don't exist in dir1
|
|
390
|
+
for relative_path in dir2_files.keys():
|
|
391
|
+
if relative_path not in dir1_files:
|
|
392
|
+
divergent_files.append({'path': relative_path, 'reason': 'missing in source'})
|
|
393
|
+
with self.operation_queues.lock:
|
|
394
|
+
self.operation_queues.files_to_delete.add(str(self.src_dir / relative_path))
|
|
395
|
+
log_verbose(f"File only in target: {relative_path}")
|
|
396
|
+
|
|
397
|
+
return divergent_files
|
|
398
|
+
|
|
399
|
+
def watch_changes(self) -> None:
|
|
400
|
+
"""
|
|
401
|
+
Set up file system monitoring and start the periodic sync process.
|
|
402
|
+
This method returns immediately after setting up the background watchers.
|
|
403
|
+
"""
|
|
404
|
+
handler = FolderSyncHandler(str(self.src_dir), str(self.dest_dir), self.verbose, self.operation_queues)
|
|
405
|
+
self.observer = Observer()
|
|
406
|
+
if self.observer: # This check is redundant but satisfies mypy
|
|
407
|
+
self.observer.schedule(handler, str(self.src_dir), recursive=True)
|
|
408
|
+
self.observer.start()
|
|
409
|
+
|
|
410
|
+
print(f"Watching for changes in {self.src_dir}...")
|
|
411
|
+
print(f"Files will be synchronized every {self.sync_interval} seconds")
|
|
412
|
+
|
|
413
|
+
# Start the batch processing scheduler
|
|
414
|
+
self.running = True
|
|
415
|
+
self.schedule_batch_processing()
|
|
416
|
+
self.schedule_sanity_check()
|
|
417
|
+
|
|
418
|
+
def stop(self) -> None:
|
|
419
|
+
self.running = False
|
|
420
|
+
if self.sync_timer:
|
|
421
|
+
self.sync_timer.cancel()
|
|
422
|
+
self.sync_timer.join()
|
|
423
|
+
self.sync_timer = None
|
|
424
|
+
if self.check_timer:
|
|
425
|
+
self.check_timer.cancel()
|
|
426
|
+
self.check_timer.join()
|
|
427
|
+
self.check_timer = None
|
|
428
|
+
if self.observer:
|
|
429
|
+
self.observer.stop()
|
|
430
|
+
self.observer.join()
|
|
431
|
+
|