From dbf4d9d6858bb97d5c5d1ffd70e14d2e3cac18f4 Mon Sep 17 00:00:00 2001 From: Miguel Date: Fri, 7 Feb 2025 23:08:39 +0100 Subject: [PATCH] Funcionando --- backend/app.py | 79 ++++++- backend/core/__init__.py | 0 .../core/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 154 bytes .../directory_handler.cpython-310.pyc | Bin 0 -> 833 bytes .../profile_manager.cpython-310.pyc | Bin 0 -> 3793 bytes .../script_manager.cpython-310.pyc | Bin 0 -> 3382 bytes .../workdir_config.cpython-310.pyc | Bin 0 -> 2895 bytes backend/core/script_manager.py | 112 +++++++++ backend/script_groups/__init__.py | 0 backend/script_groups/base_script.py | 61 +++++ .../script_groups/example_group/__init__.py | 0 .../__pycache__/x1.cpython-310.pyc | Bin 0 -> 1640 bytes .../__pycache__/x2.cpython-310.pyc | Bin 0 -> 1640 bytes backend/script_groups/example_group/x1.py | 49 ++++ backend/script_groups/example_group/x2.py | 57 +++++ data/profiles.json | 26 +++ files.txt | Bin 0 -> 1582 bytes frontend/static/css/style.css | 220 +++++++++++------- frontend/static/js/main.js | 50 +++- frontend/static/js/profile.js | 77 +++++- frontend/static/js/scripts.js | 145 ++++++++++++ frontend/static/js/workdir_config.js | 66 +++--- frontend/templates/index.html | 86 ++++--- 23 files changed, 855 insertions(+), 173 deletions(-) create mode 100644 backend/core/__init__.py create mode 100644 backend/core/__pycache__/__init__.cpython-310.pyc create mode 100644 backend/core/__pycache__/directory_handler.cpython-310.pyc create mode 100644 backend/core/__pycache__/profile_manager.cpython-310.pyc create mode 100644 backend/core/__pycache__/script_manager.cpython-310.pyc create mode 100644 backend/core/__pycache__/workdir_config.cpython-310.pyc create mode 100644 backend/core/script_manager.py create mode 100644 backend/script_groups/__init__.py create mode 100644 backend/script_groups/base_script.py create mode 100644 backend/script_groups/example_group/__init__.py create mode 100644 backend/script_groups/example_group/__pycache__/x1.cpython-310.pyc create mode 100644 backend/script_groups/example_group/__pycache__/x2.cpython-310.pyc create mode 100644 backend/script_groups/example_group/x1.py create mode 100644 backend/script_groups/example_group/x2.py create mode 100644 data/profiles.json create mode 100644 files.txt create mode 100644 frontend/static/js/scripts.js diff --git a/backend/app.py b/backend/app.py index e7e59c5..c295683 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1,21 +1,35 @@ # backend/app.py +import os +import sys +from pathlib import Path + +# Add the backend directory to Python path +backend_dir = Path(__file__).parent +if str(backend_dir) not in sys.path: + sys.path.append(str(backend_dir)) + from flask import Flask, render_template, jsonify, request, send_from_directory from core.directory_handler import select_directory -from pathlib import Path +from core.script_manager import ScriptManager from core.profile_manager import ProfileManager app = Flask(__name__, template_folder='../frontend/templates', static_folder='../frontend/static') -# Initialize profile manager -profile_manager = ProfileManager(Path('../data')) +# Initialize managers +data_dir = Path(__file__).parent.parent / 'data' +script_groups_dir = Path(__file__).parent / 'script_groups' + +profile_manager = ProfileManager(data_dir) +script_manager = ScriptManager(script_groups_dir) @app.route('/') def index(): """Render main page""" return render_template('index.html') +# Profile endpoints @app.route('/api/profiles', methods=['GET']) def get_profiles(): """Get all profiles""" @@ -58,6 +72,7 @@ def delete_profile(profile_id): except Exception as e: return jsonify({"error": str(e)}), 400 +# Directory handling endpoints @app.route('/api/select-directory', methods=['GET']) def handle_select_directory(): """Handle directory selection""" @@ -66,5 +81,61 @@ def handle_select_directory(): return jsonify(result), 400 return jsonify(result) +# Script management endpoints +@app.route('/api/scripts', methods=['GET']) +def get_scripts(): + """Get all available script groups""" + try: + groups = script_manager.discover_groups() + return jsonify(groups) + except Exception as e: + return jsonify({"error": str(e)}), 500 + +@app.route('/api/scripts///run', methods=['POST']) +def run_script(group_id, script_id): + """Execute a specific script""" + data = request.json + work_dir = data.get('work_dir') + profile = data.get('profile') + + if not work_dir: + return jsonify({"error": "Work directory not specified"}), 400 + if not profile: + return jsonify({"error": "Profile not specified"}), 400 + + try: + result = script_manager.execute_script(group_id, script_id, work_dir, profile) + return jsonify(result) + except Exception as e: + return jsonify({"error": str(e)}), 500 + +# Work directory configuration endpoints +@app.route('/api/workdir-config/', methods=['GET']) +def get_workdir_config(work_dir): + """Get work directory configuration""" + from core.workdir_config import WorkDirConfigManager + config_manager = WorkDirConfigManager(work_dir) + return jsonify(config_manager.get_config()) + +@app.route('/api/workdir-config//group/', methods=['GET']) +def get_group_config(work_dir, group_id): + """Get group configuration from work directory""" + from core.workdir_config import WorkDirConfigManager + config_manager = WorkDirConfigManager(work_dir) + return jsonify(config_manager.get_group_config(group_id)) + +@app.route('/api/workdir-config//group/', methods=['PUT']) +def update_group_config(work_dir, group_id): + """Update group configuration in work directory""" + from core.workdir_config import WorkDirConfigManager + config_manager = WorkDirConfigManager(work_dir) + + try: + settings = request.json + config_manager.update_group_config(group_id, settings) + return jsonify({"status": "success"}) + except Exception as e: + return jsonify({"error": str(e)}), 400 + if __name__ == '__main__': - app.run(debug=True, port=5000) \ No newline at end of file + app.run(debug=True, port=5000) diff --git a/backend/core/__init__.py b/backend/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/core/__pycache__/__init__.cpython-310.pyc b/backend/core/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9000c4eed57b8df376531358487170042e112e72 GIT binary patch literal 154 zcmd1j<>g`kf_ROqRHA&}CMlemV?qFK$;%+t(6dTT2oXm5XhKl(-q`Q?L6LyYXAQw1ZMQ@Fx{oFNvdU=-%z3S+7pIEy$v z2OfN(OIU!}pF7|b8!ia6oo&#CpAoFb=+GIgLVRnZ!$OposB|W8^ z`B%skrfZHV$>RBoBt>fv^fcwth|+2^OKYKUCv}CGA1^H`o*H2%rn){8JRPG7tK#`i zdzGJdv>B$aZdfkhG@~xG(khWBvXU0L@D9g(r3-~I+RS(M^?w%&zMtK4)DCm{ewfEo zX$RaCQ{&alTSYxwcj~CFO@y#Z#K{=DoYcbP5f`;Jdghw1Pf9WNgSY^ZEO5+dZC&#p z0f-;OY$LbiBf4vv!vWr{gn2Zc zxjR?t9j*^!WUn8hi&+eeGN6z^XbTPERFZ}3VS?Jw6 literal 0 HcmV?d00001 diff --git a/backend/core/__pycache__/profile_manager.cpython-310.pyc b/backend/core/__pycache__/profile_manager.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a11e478cf877775510ff929ade8e412588933a9b GIT binary patch literal 3793 zcmZ`*VQ<^U8NNGG6fMiL;@GjBCMlbs1=Jzg3JgQnxoxsI?KXH$u%rtncpw-ZWz(ie z^+?6FAP>cyufcvmTfqLX5BV$m3HPNR1`OyQ*oOdH_q<0+v6M^#k9WuSjxV3*dEfWa zg@qc!Z|D2J@4j<^v47Iw{LjMRHm>Y$wsgOB z+P0hvD_tRLa{ig!c4R%QhHhBxigkwB1=+ytTsd1SXBTA?v-9#DxfH_QSYDLN&noQ& zST@WK#@0Mpk(Xe-C|5%lPmBG>Pi?s-*YUI|FTYwf^)_3+C9lBZf?5CntQ>h&UW3&V zS;6nOrH9c)c>&;F*Nq1%?M89<`ydH+Lp8isbTY4#Cf#UvpaO!{>lap;uF9|*4B~va zxLI_(as$nVmEC@R^XI!=^ZVDQElXE=sSM-xd>;1tA+{Q*uw4!Mk$(^#wHz%XsqG}_ zh1xw#)qyV~rR#Cr^RqC|qhvSJwT=owsPuze*9Lte*Yt_zfb2Ws9LLnB>n_oJ4b6y; zn2%QSw-|aDor*K+S(4%N#rAMv{AIWHvovYhY6;8qWY#BabiA}-?T&jbq3tY; zyJpo3rdF}|<)-{-2cR5qBlSUhIZwwcXNEY1@51HgHRSf>Aq&AuAhb9|KdH<<8MIXUAAKyqH&|GqS#6--#2-1eL-*;*XhweWZPC{l$7%-Vgbq z@owMUpOaPm-RI)`nqzo-S{sQo>@=dxFgIM?0xc75dR>+FJVO{)FDrw*d-KB=$OvK5 zNhQ>@t>ys0aP6K_sq%-@@(u2ts_w9(*e-2Gbocd1erHE#3a)mg1f>3B*h zl)vdRN_iihu~MK4acZ5hQ~Qj813}S5Y~wC)=U~DbzI)fisaHZUM%6ov@;#5zah3f&RzV0!F#h9yqT?SgZWfCAvmaN7)TzWL!Ulis!GrR z7NnDXQB)6v&xK==Tl)wf3J2*tM*tv*_&A6pfI=AEmqoWh^a!`&eI?sgjtIl zW@kLq&D}8fv3L?1xABUMQhP%T-<~0K)>hX_nfQ^M;*-`-oUUU~NSqR$ae0|s&iY{| z>PDTh-z`x%Y~eL8MBy!gH|z2|xlz7&6VHvebuqmB25(C+*IxjL%E(@)dTDR4FI!YM_aS7gV5iO*GQNp(ceqr>c@iFu0aAqIq$+v6L6&=a zBA7rRsReAJNJwfCO{=PFeaDlpVB%BgQO8jWYC0*na~%b4+jras@JGlh-ZE8ZO!f7hl&f|As6& zy-SBAHU3e5!kfgvAjyF7LJr189Q=raA24AZ2(>P4)KcOP3{CTfL;|izq$6;BikfR` z-vd~- z-BhGdP)`u2ucgQUuID0!v6l0dXy4a0-^Xct5L3VI`(F-%xO`IeeVKNAzla$EOs!KxV$~KhvN=~MLQzo_Rdm`c ze5X95KBQ)Ynmg2NQge@*S0811=7>TClR`D4*mRlAg(W!tIIIY6|3_2=cmCa2xKz2c zvg}^2UbC;;@2xI16|HO4=m3`Q#nBUG+`NHF?c_&&NZPntIBC2xPMV}ZOW)*+b~g${ TKB01z(Yb(51#37nEs6gDvC51D literal 0 HcmV?d00001 diff --git a/backend/core/__pycache__/script_manager.cpython-310.pyc b/backend/core/__pycache__/script_manager.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3962c1ffab2b0d4e106e163759b37f2e12b7cff1 GIT binary patch literal 3382 zcmaJ^TW=f372cVhy>LlU7t0qpuFUiyEsI1*Yxfevb?wG>fW}b*2(1uS!D7W3$xAP} z^z6!#h^1jfdF@M|3d9D;0)6OTn%6wF4@IAiI?i`?Dccf^5}31dpEKut-#No{ZZ5F! z`|H2|vAerqS>I9P&7Y0NO_XdMLRyj~R-bhkqdHG`$1cC3Bk;8or|)*$zSr^ke#h_E zI^ds=N~#1~P1f4wb?xE*(MBksjnZtPowL*suw z%;R(rC0&~O=C6UqO_c2K5X$N>X?3_{P>3tB!=)|7H=^T6N4d&VzN+o;6>G)9h$}se z1kzVfNYzh_3R#l@MjNvJ*3pKX!|0rBDqqf{ETAmD-U0i)I%_A8OY#h6H{~)ed)CZt zbagz;KZ^#@uF|9Ts!G<%%I=novu^rG>BCkukS%qfx_fkWvsjIXyE@$)W}%FAm*dLE zKdMvt7$qZL7Ob#BxCdvl^)}N!`r6!xOeH&I7nZ8X|`1|O24|o+7J#3!*~$qVOXd0Ns)k!&Q}1ZR@KGV_xtHpIZ8I!F%>ZNH%5J<-(dc1)s99 zUDz056SuIZfaOE4V7NISUA#TBXDdpQV;fgCG%Rd;r74UT=Sst*#*ItU8Zn6a$^=;+ z={(zy^ZTY5;|ZDzGDmT|n6>gB{-=+oL9| z#>h@%Q3!pG>{K@J)Txx>Z@U6`gPlD4+?9 z`fyY``16M!eDoC%HadSt>r{gj)8dH@-Z*lVn$+4;HfAq0ngl< z_|&2Ru*JvD*hNpRaHmXPE!W)0ZGOMv!fd5Y-G>AMBw zq$#nN6f4$bergR$9kAa5fj?gaNtO_B&P>@6lg?AI38ZWSF&l_W`SRf-P{IQx7W1>x zEf^@_72XL-u(9{pqxsMrH6lgZA6y*@^;58p$^ZpfAwUbEX#ehX6zpZySM>k(l$@P<6u{o%Ty4goMyCm53}?eY4O{ zv9E}p=8$}ICr^VoQdxnqQQi{1iUwx&wz94N6Zy` z740VT|A)SM^TjF?96We&Iu@VeuJLE_UYgoWxmX&1Kh+N?9T;z@Q*vY1ffmQVIaFTp z=uQvxaeVR9mfZk`+JN8R9T`BzRk2v3@ z^Plk}_BAW~BWqkQ>XZ4x27@^91x#5u+y_${V9K2Iz^Vq=uKvz zl*ICwUL`PIPd1LC{DBvci=>|KW%?5=YcEy#-&F^AYXZ)HijqB5(oDaPas2@a!jArs z#Lr3mg2WXPS4sSm#IH#Fn#6BNkV+Mde~Vgsu7WWIK}~NPeTT$d5(I7iI}$fZ+#*4_ zLjN8Dx|WZ^x1riJtGA&Ft2v)zentu{+diHU9HG1cV80FE2<5!V5bC+hmcFMDe>$w6 z+d-x@`~%6;S| tZ6uUFyb28HL0Zb+fRXBSYG_zyZNRI>m8 literal 0 HcmV?d00001 diff --git a/backend/core/__pycache__/workdir_config.cpython-310.pyc b/backend/core/__pycache__/workdir_config.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..450317b236da44b0e8c91ce2add77ad7b1f88347 GIT binary patch literal 2895 zcmai0-ER{|5Z~SVwBwk>gp{PTaVb<>X#?~_Y7wodLV_yMfC?z873p-`uET+S=kA>o za&oFl;T83zZ?sbU&N6}@u&z#fy&@Ds2yi7W86S^gG`s{AAX zFsEkL68Z32B$tx78Et$Q#KDG;`GumB)tIgZ$>SV)}y$-1)J(Tk|N9!8HqGE zE-hYE#50w%DP#(|svJrIpm9IQRJq$h0Cm5*A(L*$PeqnR@kXj#wB>`XkO6|j2m9ET zpr!L5GSZ{Fv`2hs4=AT^6X-+C!3nmS*V0f%oy;$eyZ9hYV$YChKu&^C6UBm$4tPwN zsc1Fz$}=LTK;gv_zr21+Cfn#xx_(<*OV@8CVbB^3*2KN_dqMb6#C$zWq*%vu!?B0o z>;PkYKZ>Kw_ou;RYJw=!DVo1C4&ue3uCk@bx-t$a=no$xJEntn|5xaM(`1kKiCoFw z&lrw4MTR{weyQ%z%-Az~v`^OnGj5v1B)bMBYXHtAk}hmPlHl(LXlr0;&*{U8Niu7D znUWnwNj{y~-0HEXSoQ4zjy|jB;qjP5c$=7VOdI0ZY2SZUvC=NyIl8NSq0aXEgnT_mMSyph*+5zTxH@e zRa$-&3Z13O>c}Y0)U037&gY^TbX!A402z*&D(P*i#}B7M9KS$=+eI;R~GM$IU!7ge^d zzauMP%VY9Acqs!C;8~Gl@&Zg0ZXWV*3bUj*W5K@9KvS1VmqD34JJ!L&dz3r8IYMOA z@Ieo8tiAjMIs-2aPJ6~Cm6eF}ps#Zb-gA&+&`0l$KK_S5|BZa{uD+)ZaRoA`{$>bP z2N=jj_^M~>I#XcqDNKw7;&^c#j$rX6DC>~B41To2sx&`07LhTVj|AKTYZ?lK_;tV` zb@m|LFx;C&o`giwKzxN7P=tm+48izbw38!=^M7C%nh^3L`i_XmODGO8;keYMM*z^r z#fpPuDAOu6U%ml=F`HMtV|e?ia-0GsxxcOPRzRV zm3gaH)|y@k3r=>w72PZTpXd@{XWJdP0}q@7BUscGv_0;_%Dqx}`8ED?(A>dNJwq$B G#{L6>{m4-O literal 0 HcmV?d00001 diff --git a/backend/core/script_manager.py b/backend/core/script_manager.py new file mode 100644 index 0000000..cf6f0fa --- /dev/null +++ b/backend/core/script_manager.py @@ -0,0 +1,112 @@ +# backend/core/script_manager.py +from pathlib import Path +import importlib.util +import inspect +from typing import Dict, List, Any, Optional +import json + +class ScriptManager: + """Manages script discovery and execution""" + + def __init__(self, script_groups_dir: Path): + self.script_groups_dir = script_groups_dir + + def discover_groups(self) -> List[Dict[str, Any]]: + """Discover all script groups""" + groups = [] + + for group_dir in self.script_groups_dir.iterdir(): + if group_dir.is_dir() and not group_dir.name.startswith('_'): + group_info = self._analyze_group(group_dir) + if group_info: + groups.append(group_info) + + return groups + + def _analyze_group(self, group_dir: Path) -> Optional[Dict[str, Any]]: + """Analyze a script group directory""" + scripts = [] + + for script_file in group_dir.glob('x[0-9].py'): + try: + script_info = self._analyze_script(script_file) + if script_info: + scripts.append(script_info) + except Exception as e: + print(f"Error analyzing script {script_file}: {e}") + + if scripts: + return { + "id": group_dir.name, + "name": group_dir.name.replace('_', ' ').title(), + "scripts": sorted(scripts, key=lambda x: x['id']) + } + return None + + def _analyze_script(self, script_file: Path) -> Optional[Dict[str, Any]]: + """Analyze a single script file""" + try: + # Import script module + spec = importlib.util.spec_from_file_location( + script_file.stem, + script_file + ) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + # Find script class + script_class = None + for name, obj in inspect.getmembers(module): + if (inspect.isclass(obj) and + obj.__module__ == module.__name__ and + hasattr(obj, 'run')): + script_class = obj + break + + if script_class: + return { + "id": script_file.stem, + "name": script_class.__doc__.split('\n')[0].strip() if script_class.__doc__ else script_file.stem, + "description": inspect.getdoc(script_class), + "file": str(script_file.relative_to(self.script_groups_dir)) + } + + except Exception as e: + print(f"Error loading script {script_file}: {e}") + + return None + + def execute_script(self, group_id: str, script_id: str, work_dir: str, + profile: Dict[str, Any]) -> Dict[str, Any]: + """Execute a specific script""" + script_file = self.script_groups_dir / group_id / f"{script_id}.py" + + if not script_file.exists(): + raise ValueError(f"Script {script_id} not found in group {group_id}") + + try: + # Import script module + spec = importlib.util.spec_from_file_location(script_id, script_file) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + # Find and instantiate script class + script_class = None + for name, obj in inspect.getmembers(module): + if (inspect.isclass(obj) and + obj.__module__ == module.__name__ and + hasattr(obj, 'run')): + script_class = obj + break + + if not script_class: + raise ValueError(f"No valid script class found in {script_id}") + + script = script_class() + return script.run(work_dir, profile) + + except Exception as e: + return { + "status": "error", + "error": str(e) + } diff --git a/backend/script_groups/__init__.py b/backend/script_groups/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/script_groups/base_script.py b/backend/script_groups/base_script.py new file mode 100644 index 0000000..2a34b9a --- /dev/null +++ b/backend/script_groups/base_script.py @@ -0,0 +1,61 @@ +# backend/script_groups/base_script.py +from typing import Dict, Any +from pathlib import Path +import json + +class BaseScript: + """Base class for all scripts""" + + def run(self, work_dir: str, profile: Dict[str, Any]) -> Dict[str, Any]: + """ + Execute the script + + Args: + work_dir (str): Working directory path + profile (Dict[str, Any]): Current profile configuration + + Returns: + Dict[str, Any]: Execution results + """ + raise NotImplementedError("Script must implement run method") + + def get_config(self, work_dir: str, group_id: str) -> Dict[str, Any]: + """Get group configuration from work directory""" + config_file = Path(work_dir) / "script_config.json" + + if config_file.exists(): + try: + with open(config_file, 'r', encoding='utf-8') as f: + config = json.load(f) + return config.get("group_settings", {}).get(group_id, {}) + except Exception as e: + print(f"Error loading config: {e}") + + return {} + + def save_config(self, work_dir: str, group_id: str, settings: Dict[str, Any]): + """Save group configuration to work directory""" + config_file = Path(work_dir) / "script_config.json" + + try: + # Load existing config or create new + if config_file.exists(): + with open(config_file, 'r', encoding='utf-8') as f: + config = json.load(f) + else: + config = { + "version": "1.0", + "group_settings": {} + } + + # Update settings + if "group_settings" not in config: + config["group_settings"] = {} + config["group_settings"][group_id] = settings + + # Save config + with open(config_file, 'w', encoding='utf-8') as f: + json.dump(config, f, indent=4) + + except Exception as e: + print(f"Error saving config: {e}") diff --git a/backend/script_groups/example_group/__init__.py b/backend/script_groups/example_group/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/script_groups/example_group/__pycache__/x1.cpython-310.pyc b/backend/script_groups/example_group/__pycache__/x1.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c1e701b09126f6799a931ca445668e32464cf1d9 GIT binary patch literal 1640 zcmZ`(&5ImG6n|A+{V_c=o4Ce&3DT$t<6%({l&~zKF{1>N|nJF6SPf_`1~>b-iee((3H?Ds>!@#pj3C%Fgk zhh3Z>3>Tl{r#?Z!fs-7jWJm~b%AK4JDgIgROxchT_!_v&y<^~>AV<#7<<1Ta{AUzh z2gHP5Bueb0GMnjVsDW+Nu{(*rpW4;=fr+$1KfsBAA>n|5$k5?bFzyJKGvSWCCKwxf z+{ZA1>E6n6@ntzLw2;d*jBqro$m&&;717O1id2{Ku-UwwDXpTU;8ALwR5U)b)AvPm zP|Al{F^Tw@PqcR!iG>!0%F3cSzy#JPMhoN5WNF=vCxxENB6ZH)r-yTkvel{QD3)wNH>)~FVJeS6p!i)1>>MLdz^d}exLk>+zQEP_f}bW(!i zY*^Y@`n`#tx{6`~72FMKQjwqGJ0fXCex>_#2cCko@D8Y<8y^|MpAy-@b)$T-&2NnE z$nww@UX5D%snP8+P4ZWZU9mUXOVWp;;3L)0v&~P9UQ2hh_;_P>Xu{1Ld6SmY+153i zn9cGkEZ;jvz707qHa2q`LT%yX%>J3Z-LhAd|MJNAYM!P-DNMerOP%C#!$NcMAF9PI zJD1)qWGRo9e#_I)knlCQEPk%d$bRpm_`$-}AJLlB2o=^ADvFXkVc!6Oee7Ip8)r#x zL`$zdbGyaSkzI5*4yIt-CVHc9#hTsfY)(WW7BjhJJ5*Wp=_3XnU3(e6T)zANSe&^H z9OEgS=(#eCCps}+IoGo}#uHMOazLdmxO^MMpl8B~&@>3p?F#Y|)G8b@h~r!pn-v9xcE^qW)K2-5OR zu3HV0WvMYiq^EJ)x-k2bZ`oZUT15KRzJbNT!rNwV9!pAkv`3i3$R+1G4QN2t$z?+S zq8_D`u+vK{q`_(MVy#E1N7he+NA%_L!hfLK9{47R<06@gI5uG%PfI?>28vBDj`!zD z{%XgMV_v3lENwk(#;KMYHZNP6bHb)6ElcdRl@$Rna@}oo18>;f!#6r^yFglM%Xd)V k^M=oKp6xYoH@4@_Y(g~m4>sHMwk*+AfTBkN(j%AX-%1LnlK=n! literal 0 HcmV?d00001 diff --git a/backend/script_groups/example_group/__pycache__/x2.cpython-310.pyc b/backend/script_groups/example_group/__pycache__/x2.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8a20ed30929a2c5fe619077e15e9cb0b10ab4aff GIT binary patch literal 1640 zcmZWpOKxcZAkF5YJDc)YR#&K^JcI?V=oD5fm>&VCF96RUn`S|4RwvS+tzkWUa!$s(K zBRn1igguz@IS_^zPSFqt7$aseJGBC?t1kl(8;LWRXT=**%G5 z5_e@~qnrHi7wwI5aUQZnXtxmgAW6BVf~!(wu?<1~I~u?kaQ9)#J&e%g2Ze6%94T}L z88GVwX7;rOI(v@J$YOOCD`n)`2Hu_GIj(kV&U>kYoZ&>{#-F2`<{ZGjCNq1AZdy~! z+_^O)Q*zU09&5}k5vipqzQWAkLNiA#PaW30vVQE$sOp@pOzkOUE!KWvV{{CcOyIi$ zKJ(s1vOj{$Hs4n;+0xWz%NvMwHqcLq!Jn11T1jglt;61HmGnADZ@iSA{M?wiYy&*G zZ1dVaMn~X7t&V@eXy#!wX|pZ0HuWIF?Q828QSioK=UeY+v5xQv_1-+V1sF{Yc}b?Y zxQNo8rJXnzTxzG3(JAlQ+EKZRQr(Cyq9lz@;C9@Cif81A55L^)iWP`LG=WUKZk0&)nP?et9sYuhjR7I(@!-dF2&k|3Jy)7WUbxjLdssvyI zIKntDGZi!qyMQiOx=4g7qcp6Ms(=(r#()V(`v$nM3RrZE)-(=nXZc7s5}DVJPWz`^ zf$?mRoN60*O%5aVwI+GNGfgV+2rGw$ZhUkZ^TNDRZA&G34NW9Z2f}m%0euSY(4enr zZ7gWksfE>J!~?Nuj$r;95<9^CeZY46haw-tOV4G$?me>qSsq8}LOA9p{gWs@=NaqE zsyEd)F8lu}fd1usJH=SrqRdPs$pau3ro_kO-g7+cVRGkFAA@}7JM_=iS__+R6>mRS z=F~$qEpqSM-kn7)Y+>(iiCPxLtM}e_*283}t`*a$cEHU+&(&TSX3>y`q4vXYn6nb< zG1RRv{01&$vEzmz%i}N<&rIr2$FUOc09yg@h`eod4iJN)G()O?HMzI| literal 0 HcmV?d00001 diff --git a/backend/script_groups/example_group/x1.py b/backend/script_groups/example_group/x1.py new file mode 100644 index 0000000..3dc0f84 --- /dev/null +++ b/backend/script_groups/example_group/x1.py @@ -0,0 +1,49 @@ +# backend/script_groups/example_group/x1.py +from ..base_script import BaseScript +import os +from pathlib import Path + +class FileCounter(BaseScript): + """ + Count Files in Directory + Lists and counts files in the working directory by extension + """ + + def run(self, work_dir: str, profile: dict) -> dict: + try: + # Get configuration if any + config = self.get_config(work_dir, "example_group") + exclude_dirs = config.get("exclude_dirs", []) + + # Initialize counters + extension_counts = {} + total_files = 0 + + # Walk through directory + for root, dirs, files in os.walk(work_dir): + # Skip excluded directories + dirs[:] = [d for d in dirs if d not in exclude_dirs] + + for file in files: + total_files += 1 + ext = Path(file).suffix.lower() or 'no extension' + extension_counts[ext] = extension_counts.get(ext, 0) + 1 + + return { + "status": "success", + "data": { + "total_files": total_files, + "extension_counts": extension_counts + }, + "output": f"Found {total_files} files\n" + "\n".join( + f"{ext}: {count} files" + for ext, count in sorted(extension_counts.items()) + ) + } + + except Exception as e: + return { + "status": "error", + "error": str(e) + } + diff --git a/backend/script_groups/example_group/x2.py b/backend/script_groups/example_group/x2.py new file mode 100644 index 0000000..f2dfbef --- /dev/null +++ b/backend/script_groups/example_group/x2.py @@ -0,0 +1,57 @@ +# backend/script_groups/example_group/x2.py +from ..base_script import BaseScript +import psutil +import json +from datetime import datetime + +class SystemInfo(BaseScript): + """ + System Information + Collects and displays basic system information + """ + + def run(self, work_dir: str, profile: dict) -> dict: + try: + # Collect system information + info = { + "cpu": { + "cores": psutil.cpu_count(), + "usage": psutil.cpu_percent(interval=1), + }, + "memory": { + "total": psutil.virtual_memory().total, + "available": psutil.virtual_memory().available, + "percent": psutil.virtual_memory().percent, + }, + "disk": { + "total": psutil.disk_usage(work_dir).total, + "free": psutil.disk_usage(work_dir).free, + "percent": psutil.disk_usage(work_dir).percent, + }, + "timestamp": datetime.now().isoformat() + } + + # Save to work directory if configured + config = self.get_config(work_dir, "example_group") + if config.get("save_system_info", False): + output_file = Path(work_dir) / "system_info.json" + with open(output_file, 'w') as f: + json.dump(info, f, indent=2) + + # Format output + output = f"""System Information: +CPU: {info['cpu']['cores']} cores ({info['cpu']['usage']}% usage) +Memory: {info['memory']['percent']}% used +Disk: {info['disk']['percent']}% used""" + + return { + "status": "success", + "data": info, + "output": output + } + + except Exception as e: + return { + "status": "error", + "error": str(e) + } diff --git a/data/profiles.json b/data/profiles.json new file mode 100644 index 0000000..ea2b923 --- /dev/null +++ b/data/profiles.json @@ -0,0 +1,26 @@ +{ + "default": { + "id": "default", + "name": "Default Profile", + "work_dir": "", + "llm_settings": { + "model": "gpt-4", + "temperature": 0.7, + "api_key": "" + }, + "created_at": "2025-02-07T12:47:49.766608", + "updated_at": "2025-02-07T12:47:49.766608" + }, + "1": { + "id": "1", + "name": "Base", + "work_dir": "D:/Proyectos/AutoCAD", + "llm_settings": { + "api_key": "333333333333", + "model": "gpt-4", + "temperature": 0.7 + }, + "created_at": "2025-02-07T13:00:43.541932", + "updated_at": "2025-02-07T13:01:40.473406" + } +} \ No newline at end of file diff --git a/files.txt b/files.txt new file mode 100644 index 0000000000000000000000000000000000000000..dd78c0bc6e8fbc591be2f9e5054a13d46f8da7f2 GIT binary patch literal 1582 zcmb7E?M{S144mI4zJvdbB)*RcLEsSYgUf*t5}#e27AGvYIoA-b3EP=Yr``MdyvKt- z&nQr0j~XXbxWx@VaOaO=fis_s?-V@`F=8#z;X=;vOnizJTF)slc=m)GFH~bX4#Zxr z)D*{=Ssiv{GDMD};|ny2_@2FAh(1%p|IzQRdr!UANlvbg>P=0Ppgkv|Wq#|m^8`f9 z&3RVKZ+t$Rg?VEhq`P5EiUT&&XsB-Uh z2X0j9d^*=el~=OnJo{~iGWFdUCwD$0XDZ5ci$RR)!}^V#D__xWvfYl^p%ld0+jLyG z-I_>Bz3aC>wa>7UJhB { - await loadProfiles(); - updateWorkDirDisplay(); + try { + await loadProfiles(); + await loadScriptGroups(); + updateWorkDirDisplay(); + } catch (error) { + console.error('Initialization error:', error); + showError('Failed to initialize application'); + } }); // API functions @@ -70,13 +77,18 @@ async function selectProfile(profileId) { async function changeProfile() { const select = document.getElementById('profileSelect'); - await selectProfile(select.value); + if (select.value) { + await selectProfile(select.value); + await loadScriptGroups(); // Reload scripts when profile changes + } } // Work directory functions function updateWorkDirDisplay() { const input = document.getElementById('workDirPath'); - input.value = currentProfile?.work_dir || ''; + if (input && currentProfile) { + input.value = currentProfile.work_dir || ''; + } } async function selectWorkDir() { @@ -101,17 +113,39 @@ async function selectWorkDir() { // Output functions function showError(message) { const output = document.getElementById('outputArea'); - output.innerHTML += `\nError: ${message}`; + const timestamp = new Date().toLocaleTimeString(); + output.innerHTML += `\n[${timestamp}] ERROR: ${message}`; output.scrollTop = output.scrollHeight; } function showSuccess(message) { const output = document.getElementById('outputArea'); - output.innerHTML += `\nSuccess: ${message}`; + const timestamp = new Date().toLocaleTimeString(); + output.innerHTML += `\n[${timestamp}] SUCCESS: ${message}`; output.scrollTop = output.scrollHeight; } function clearOutput() { const output = document.getElementById('outputArea'); output.innerHTML = ''; -} \ No newline at end of file +} + +// Modal helper functions +function closeModal(button) { + const modal = button.closest('.modal'); + if (modal) { + modal.remove(); + } +} + +// Global error handler +window.addEventListener('unhandledrejection', function(event) { + console.error('Unhandled promise rejection:', event.reason); + showError('An unexpected error occurred'); +}); + +// Export functions for use in other modules +window.showError = showError; +window.showSuccess = showSuccess; +window.closeModal = closeModal; +window.currentProfile = currentProfile; \ No newline at end of file diff --git a/frontend/static/js/profile.js b/frontend/static/js/profile.js index 3893e11..07e4417 100644 --- a/frontend/static/js/profile.js +++ b/frontend/static/js/profile.js @@ -1,4 +1,65 @@ -# frontend/static/js/profile.js +// frontend/static/js/profile.js + +// Profile functions +async function loadProfiles() { + try { + const profiles = await apiRequest('/profiles'); + updateProfileSelector(profiles); + + // Select first profile if none selected + if (!currentProfile) { + const defaultProfile = profiles.find(p => p.id === 'default') || profiles[0]; + if (defaultProfile) { + await selectProfile(defaultProfile.id); + } + } + } catch (error) { + showError('Failed to load profiles'); + } +} + +function updateProfileSelector(profiles) { + const select = document.getElementById('profileSelect'); + select.innerHTML = profiles.map(profile => ` + + `).join(''); +} + +async function selectProfile(profileId) { + try { + currentProfile = await apiRequest(`/profiles/${profileId}`); + updateWorkDirDisplay(); + } catch (error) { + showError('Failed to load profile'); + } +} + +async function changeProfile() { + const select = document.getElementById('profileSelect'); + await selectProfile(select.value); +} + +async function selectWorkDir() { + try { + const response = await apiRequest('/select-directory'); + if (response.path) { + await apiRequest(`/profiles/${currentProfile.id}`, { + method: 'PUT', + body: JSON.stringify({ + ...currentProfile, + work_dir: response.path + }) + }); + await selectProfile(currentProfile.id); + showSuccess('Work directory updated successfully'); + } + } catch (error) { + showError('Failed to update work directory'); + } +} + // Profile editor modal let editingProfile = null; @@ -27,7 +88,7 @@ function showProfileEditor(profile = null) {
+ value="${profile?.work_dir || ''}" readonly>
@@ -48,7 +109,7 @@ function showProfileEditor(profile = null) { min="0" max="2" step="0.1">
- +
@@ -58,14 +119,6 @@ function showProfileEditor(profile = null) { document.body.appendChild(modal); } -function closeProfileEditor() { - const modal = document.querySelector('.modal'); - if (modal) { - modal.remove(); - } - editingProfile = null; -} - async function saveProfile(event) { event.preventDefault(); const form = event.target; @@ -96,7 +149,7 @@ async function saveProfile(event) { } await loadProfiles(); - closeProfileEditor(); + closeModal(event.target); showSuccess(`Profile ${editingProfile ? 'updated' : 'created'} successfully`); } catch (error) { showError(`Failed to ${editingProfile ? 'update' : 'create'} profile`); diff --git a/frontend/static/js/scripts.js b/frontend/static/js/scripts.js new file mode 100644 index 0000000..1b61ff0 --- /dev/null +++ b/frontend/static/js/scripts.js @@ -0,0 +1,145 @@ +// frontend/static/js/scripts.js + +// Script groups state +let scriptGroups = []; + +// Load script groups when page loads +document.addEventListener('DOMContentLoaded', async () => { + await loadScriptGroups(); +}); + +// Load script groups from API +async function loadScriptGroups() { + try { + scriptGroups = await apiRequest('/scripts'); + updateScriptGroupsDisplay(); + } catch (error) { + showError('Failed to load script groups'); + } +} + +// Update script groups display +function updateScriptGroupsDisplay() { + const container = document.getElementById('scriptGroups'); + + if (!scriptGroups.length) { + container.innerHTML = '

No script groups available

'; + return; + } + + container.innerHTML = scriptGroups.map(group => ` +
+
+

${group.name}

+ +
+
+ ${group.scripts.map(script => ` +
+
+

${script.name}

+

${script.description || 'No description available'}

+
+
+ +
+
+ `).join('')} +
+
+ `).join(''); +} + +// Run a script +async function runScript(groupId, scriptId) { + if (!currentProfile?.work_dir) { + showError('Please select a work directory first'); + return; + } + + try { + const result = await apiRequest(`/scripts/${groupId}/${scriptId}/run`, { + method: 'POST', + body: JSON.stringify({ + work_dir: currentProfile.work_dir, + profile: currentProfile + }) + }); + + if (result.status === 'error') { + showError(result.error); + } else { + showSuccess(`Script ${scriptId} executed successfully`); + if (result.output) { + const output = document.getElementById('outputArea'); + output.innerHTML += `\n[${new Date().toLocaleTimeString()}] ${result.output}`; + output.scrollTop = output.scrollHeight; + } + } + } catch (error) { + showError(`Failed to run script: ${error.message}`); + } +} + +// Configure script group +async function configureGroup(groupId) { + if (!currentProfile?.work_dir) { + showError('Please select a work directory first'); + return; + } + + try { + const config = await getGroupConfig(groupId); + showGroupConfigEditor(groupId, config); + } catch (error) { + showError('Failed to load group configuration'); + } +} + +// Show group configuration editor +function showGroupConfigEditor(groupId, config) { + const group = scriptGroups.find(g => g.id === groupId); + if (!group) return; + + const modal = document.createElement('div'); + modal.className = 'modal active'; + + modal.innerHTML = ` + + `; + + document.body.appendChild(modal); +} + +// Save group configuration +async function saveGroupConfig(event, groupId) { + event.preventDefault(); + + try { + const configText = document.getElementById('configData').value; + const config = JSON.parse(configText); + + await updateGroupConfig(groupId, config); + closeModal(event.target); + showSuccess('Group configuration updated successfully'); + } catch (error) { + showError(`Failed to save configuration: ${error.message}`); + } +} \ No newline at end of file diff --git a/frontend/static/js/workdir_config.js b/frontend/static/js/workdir_config.js index e496ad3..6a8bb6c 100644 --- a/frontend/static/js/workdir_config.js +++ b/frontend/static/js/workdir_config.js @@ -1,4 +1,5 @@ -# frontend/static/js/workdir_config.js +// frontend/static/js/workdir_config.js + async function getWorkDirConfig() { if (!currentProfile?.work_dir) { showError('No work directory selected'); @@ -51,44 +52,43 @@ async function updateGroupConfig(groupId, settings) { } } -function showWorkDirConfig() { +async function showWorkDirConfig() { if (!currentProfile?.work_dir) { showError('No work directory selected'); return; } - getWorkDirConfig().then(config => { - if (config) { - const modal = document.createElement('div'); - modal.className = 'modal active'; - - modal.innerHTML = ` - -{% endblock %} - -{% block extra_js %} - -{% endblock %} \ No newline at end of file + + + + + + + \ No newline at end of file